1 /* 2 * Copyright (c) 2003, 2007, 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 javax.net.ssl.*; 29 import java.io.IOException; 30 import java.nio.ByteBuffer; 31 import java.util.LinkedList; 32 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 33 import sun.misc.HexDumpEncoder; 34 35 /** 36 * A class to help abstract away SSLEngine writing synchronization. 37 */ 38 final class EngineWriter { 39 40 /* 41 * Outgoing handshake Data waiting for a ride is stored here. 42 * Normal application data is written directly into the outbound 43 * buffer, but handshake data can be written out at any time, 44 * so we have buffer it somewhere. 45 * 46 * When wrap is called, we first check to see if there is 47 * any data waiting, then if we're in a data transfer state, 48 * we try to write app data. 49 * 50 * This will contain either ByteBuffers, or the marker 51 * HandshakeStatus.FINISHED to signify that a handshake just completed. 52 */ 53 private LinkedList<Object> outboundList; 54 55 private boolean outboundClosed = false; 56 57 /* Class and subclass dynamic debugging support */ 58 private static final Debug debug = Debug.getInstance("ssl"); 59 60 EngineWriter() { 61 outboundList = new LinkedList<Object>(); 62 } 63 64 /* 65 * Upper levels assured us we had room for at least one packet of data. 66 * As per the SSLEngine spec, we only return one SSL packets worth of 67 * data. 68 */ 69 private HandshakeStatus getOutboundData(ByteBuffer dstBB) { 70 71 Object msg = outboundList.removeFirst(); 72 assert(msg instanceof ByteBuffer); 73 74 ByteBuffer bbIn = (ByteBuffer) msg; 75 assert(dstBB.remaining() >= bbIn.remaining()); 76 77 dstBB.put(bbIn); 78 79 /* 80 * If we have more data in the queue, it's either 81 * a finished message, or an indication that we need 82 * to call wrap again. 83 */ 84 if (hasOutboundDataInternal()) { 85 msg = outboundList.getFirst(); 86 if (msg == HandshakeStatus.FINISHED) { 87 outboundList.removeFirst(); // consume the message 88 return HandshakeStatus.FINISHED; 89 } else { 90 return HandshakeStatus.NEED_WRAP; 91 } 92 } else { 93 return null; 94 } 95 } 96 97 /* 98 * Properly orders the output of the data written to the wrap call. 99 * This is only handshake data, application data goes through the 100 * other writeRecord. 101 */ 102 synchronized void writeRecord(EngineOutputRecord outputRecord, 103 MAC writeMAC, 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(writeMAC, 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, MAC writeMAC, 156 CipherBox writeCipher) throws IOException { 157 158 /* 159 * If we have data ready to go, output this first before 160 * trying to consume app data. 161 */ 162 if (hasOutboundDataInternal()) { 163 HandshakeStatus hss = getOutboundData(ea.netData); 164 165 if (debug != null && Debug.isOn("packet")) { 166 /* 167 * We could have put the dump in 168 * OutputRecord.write(OutputStream), but let's actually 169 * output when it's actually output by the SSLEngine. 170 */ 171 dumpPacket(ea, true); 172 } 173 174 return hss; 175 } 176 177 /* 178 * If we are closed, no more app data can be output. 179 * Only existing handshake data (above) can be obtained. 180 */ 181 if (outboundClosed) { 182 throw new IOException("The write side was already closed"); 183 } 184 185 outputRecord.write(ea, writeMAC, writeCipher); 186 187 if (debug != null && Debug.isOn("packet")) { 188 dumpPacket(ea, false); 189 } 190 191 /* 192 * No way new outbound handshake data got here if we're 193 * locked properly. 194 * 195 * We don't have any status we can return. 196 */ 197 return null; 198 } 199 200 /* 201 * We already hold "this" lock, this is the callback from the 202 * outputRecord.write() above. We already know this 203 * writer can accept more data (outboundClosed == false), 204 * and the closure is sync'd. 205 */ 206 void putOutboundData(ByteBuffer bytes) { 207 outboundList.addLast(bytes); 208 } 209 210 /* 211 * This is for the really rare case that someone is writing from 212 * the *InputRecord* before we know what to do with it. 213 */ 214 synchronized void putOutboundDataSync(ByteBuffer bytes) 215 throws IOException { 216 217 if (outboundClosed) { 218 throw new IOException("Write side already closed"); 219 } 220 221 outboundList.addLast(bytes); 222 } 223 224 /* 225 * Non-synch'd version of this method, called by internals 226 */ 227 private boolean hasOutboundDataInternal() { 228 return (outboundList.size() != 0); 229 } 230 231 synchronized boolean hasOutboundData() { 232 return hasOutboundDataInternal(); 233 } 234 235 synchronized boolean isOutboundDone() { 236 return outboundClosed && !hasOutboundDataInternal(); 237 } 238 239 synchronized void closeOutbound() { 240 outboundClosed = true; 241 } 242 243 } 244