001: /*
002: * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.ssl;
027:
028: import javax.net.ssl.*;
029: import java.io.IOException;
030: import java.nio.ByteBuffer;
031: import java.util.LinkedList;
032: import javax.net.ssl.SSLEngineResult.HandshakeStatus;
033: import sun.misc.HexDumpEncoder;
034:
035: /**
036: * A class to help abstract away SSLEngine writing synchronization.
037: */
038: final class EngineWriter {
039:
040: /*
041: * Outgoing handshake Data waiting for a ride is stored here.
042: * Normal application data is written directly into the outbound
043: * buffer, but handshake data can be written out at any time,
044: * so we have buffer it somewhere.
045: *
046: * When wrap is called, we first check to see if there is
047: * any data waiting, then if we're in a data transfer state,
048: * we try to write app data.
049: *
050: * This will contain either ByteBuffers, or the marker
051: * HandshakeStatus.FINISHED to signify that a handshake just completed.
052: */
053: private LinkedList<Object> outboundList;
054:
055: private boolean outboundClosed = false;
056:
057: /* Class and subclass dynamic debugging support */
058: private static final Debug debug = Debug.getInstance("ssl");
059:
060: EngineWriter() {
061: outboundList = new LinkedList<Object>();
062: }
063:
064: /*
065: * Upper levels assured us we had room for at least one packet of data.
066: * As per the SSLEngine spec, we only return one SSL packets worth of
067: * data.
068: */
069: private HandshakeStatus getOutboundData(ByteBuffer dstBB) {
070:
071: Object msg = outboundList.removeFirst();
072: assert (msg instanceof ByteBuffer);
073:
074: ByteBuffer bbIn = (ByteBuffer) msg;
075: assert (dstBB.remaining() >= bbIn.remaining());
076:
077: dstBB.put(bbIn);
078:
079: /*
080: * If we have more data in the queue, it's either
081: * a finished message, or an indication that we need
082: * to call wrap again.
083: */
084: if (hasOutboundDataInternal()) {
085: msg = outboundList.getFirst();
086: if (msg == HandshakeStatus.FINISHED) {
087: outboundList.removeFirst(); // consume the message
088: return HandshakeStatus.FINISHED;
089: } else {
090: return HandshakeStatus.NEED_WRAP;
091: }
092: } else {
093: return null;
094: }
095: }
096:
097: /*
098: * Properly orders the output of the data written to the wrap call.
099: * 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" + (hsData ? "" : " (bb)")
139: + "]: length = " + bb.remaining());
140: hd.encodeBuffer(bb, System.out);
141: } catch (IOException e) {
142: }
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: MAC writeMAC, 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: }
|