2
|
1 |
/*
|
5506
|
2 |
* Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
|
2
|
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
|
5506
|
7 |
* published by the Free Software Foundation. Oracle designates this
|
2
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
5506
|
9 |
* by Oracle in the LICENSE file that accompanied this code.
|
2
|
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 |
*
|
5506
|
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.
|
2
|
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 |
}
|