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 } |
|