1 /* 2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.ssl; 27 28 import java.io.IOException; 29 import java.nio.ByteBuffer; 30 import java.util.LinkedList; 31 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 32 import sun.misc.HexDumpEncoder; 33 34 /** 35 * A class to help abstract away SSLEngine writing synchronization. 36 */ 37 final class EngineWriter { 38 39 /* 40 * Outgoing handshake Data waiting for a ride is stored here. 41 * Normal application data is written directly into the outbound 42 * buffer, but handshake data can be written out at any time, 43 * so we have buffer it somewhere. 44 * 45 * When wrap is called, we first check to see if there is 46 * any data waiting, then if we're in a data transfer state, 47 * we try to write app data. 48 * 49 * This will contain either ByteBuffers, or the marker 50 * HandshakeStatus.FINISHED to signify that a handshake just completed. 51 */ 52 private LinkedList<Object> outboundList; 53 54 private boolean outboundClosed = false; 55 56 /* Class and subclass dynamic debugging support */ 57 private static final Debug debug = Debug.getInstance("ssl"); 58 59 EngineWriter() { 60 outboundList = new LinkedList<Object>(); 61 } 62 63 /* 64 * Upper levels assured us we had room for at least one packet of data. 65 * As per the SSLEngine spec, we only return one SSL packets worth of 66 * data. 67 */ 68 private HandshakeStatus getOutboundData(ByteBuffer dstBB) { 69 70 Object msg = outboundList.removeFirst(); 71 assert(msg instanceof ByteBuffer); 72 73 ByteBuffer bbIn = (ByteBuffer) msg; 74 assert(dstBB.remaining() >= bbIn.remaining()); 75 76 dstBB.put(bbIn); 77 78 /* 79 * If we have more data in the queue, it's either 80 * a finished message, or an indication that we need 81 * to call wrap again. 82 */ 83 if (hasOutboundDataInternal()) { 84 msg = outboundList.getFirst(); 85 if (msg == HandshakeStatus.FINISHED) { 86 outboundList.removeFirst(); // consume the message 87 return HandshakeStatus.FINISHED; 88 } else { 89 return HandshakeStatus.NEED_WRAP; 90 } 91 } else { 92 return null; 93 } 94 } 95 96 /* 97 * Properly orders the output of the data written to the wrap call. 98 * This is only handshake data, application data goes through the 99 * other writeRecord. 100 */ 101 synchronized void writeRecord(EngineOutputRecord outputRecord, 102 Authenticator authenticator, 103 CipherBox writeCipher) throws IOException { 104 105 /* 106 * Only output if we're still open. 107 */ 108 if (outboundClosed) { 109 throw new IOException("writer side was already closed."); 110 } 111 112 outputRecord.write(authenticator, writeCipher); 113 114 /* 115 * Did our handshakers notify that we just sent the 116 * Finished message? 117 * 118 * Add an "I'm finished" message to the queue. 119 */ 120 if (outputRecord.isFinishedMsg()) { 121 outboundList.addLast(HandshakeStatus.FINISHED); 122 } 123 } 124 125 /* 126 * Output the packet info. 127 */ 128 private void dumpPacket(EngineArgs ea, boolean hsData) { 129 try { 130 HexDumpEncoder hd = new HexDumpEncoder(); 131 132 ByteBuffer bb = ea.netData.duplicate(); 133 134 int pos = bb.position(); 135 bb.position(pos - ea.deltaNet()); 136 bb.limit(pos); 137 138 System.out.println("[Raw write" + 139 (hsData ? "" : " (bb)") + "]: length = " + 140 bb.remaining()); 141 hd.encodeBuffer(bb, System.out); 142 } catch (IOException e) { } 143 } 144 145 /* 146 * Properly orders the output of the data written to the wrap call. 147 * Only app data goes through here, handshake data goes through 148 * the other writeRecord. 149 * 150 * Shouldn't expect to have an IOException here. 151 * 152 * Return any determined status. 153 */ 154 synchronized HandshakeStatus writeRecord( 155 EngineOutputRecord outputRecord, EngineArgs ea, 156 Authenticator authenticator, 157 CipherBox writeCipher) throws IOException { 158 159 /* 160 * If we have data ready to go, output this first before 161 * trying to consume app data. 162 */ 163 if (hasOutboundDataInternal()) { 164 HandshakeStatus hss = getOutboundData(ea.netData); 165 166 if (debug != null && Debug.isOn("packet")) { 167 /* 168 * We could have put the dump in 169 * OutputRecord.write(OutputStream), but let's actually 170 * output when it's actually output by the SSLEngine. 171 */ 172 dumpPacket(ea, true); 173 } 174 175 return hss; 176 } 177 178 /* 179 * If we are closed, no more app data can be output. 180 * Only existing handshake data (above) can be obtained. 181 */ 182 if (outboundClosed) { 183 throw new IOException("The write side was already closed"); 184 } 185 186 outputRecord.write(ea, authenticator, writeCipher); 187 188 if (debug != null && Debug.isOn("packet")) { 189 dumpPacket(ea, false); 190 } 191 192 /* 193 * No way new outbound handshake data got here if we're 194 * locked properly. 195 * 196 * We don't have any status we can return. 197 */ 198 return null; 199 } 200 201 /* 202 * We already hold "this" lock, this is the callback from the 203 * outputRecord.write() above. We already know this 204 * writer can accept more data (outboundClosed == false), 205 * and the closure is sync'd. 206 */ 207 void putOutboundData(ByteBuffer bytes) { 208 outboundList.addLast(bytes); 209 } 210 211 /* 212 * This is for the really rare case that someone is writing from 213 * the *InputRecord* before we know what to do with it. 214 */ 215 synchronized void putOutboundDataSync(ByteBuffer bytes) 216 throws IOException { 217 218 if (outboundClosed) { 219 throw new IOException("Write side already closed"); 220 } 221 222 outboundList.addLast(bytes); 223 } 224 225 /* 226 * Non-synch'd version of this method, called by internals 227 */ 228 private boolean hasOutboundDataInternal() { 229 return (outboundList.size() != 0); 230 } 231 232 synchronized boolean hasOutboundData() { 233 return hasOutboundDataInternal(); 234 } 235 236 synchronized boolean isOutboundDone() { 237 return outboundClosed && !hasOutboundDataInternal(); 238 } 239 240 synchronized void closeOutbound() { 241 outboundClosed = true; 242 } 243 244 }