author | emc |
Wed, 05 Nov 2014 08:37:04 -0500 | |
changeset 27386 | 784414cffd9a |
parent 25859 | 3317bb8137f4 |
child 28059 | e576535359cc |
permissions | -rw-r--r-- |
2 | 1 |
/* |
14342
8435a30053c1
7197491: update copyright year to match last edit in jdk8 jdk repository
alanb
parents:
10292
diff
changeset
|
2 |
* Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. |
2 | 3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
|
5 |
* modification, are permitted provided that the following conditions |
|
6 |
* are met: |
|
7 |
* |
|
8 |
* - Redistributions of source code must retain the above copyright |
|
9 |
* notice, this list of conditions and the following disclaimer. |
|
10 |
* |
|
11 |
* - Redistributions in binary form must reproduce the above copyright |
|
12 |
* notice, this list of conditions and the following disclaimer in the |
|
13 |
* documentation and/or other materials provided with the distribution. |
|
14 |
* |
|
5506 | 15 |
* - Neither the name of Oracle nor the names of its |
2 | 16 |
* contributors may be used to endorse or promote products derived |
17 |
* from this software without specific prior written permission. |
|
18 |
* |
|
19 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
|
20 |
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|
21 |
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
22 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
23 |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
24 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
25 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
26 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
27 |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
28 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
29 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 |
*/ |
|
31 |
||
10292
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
32 |
/* |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
33 |
* This source code is provided to illustrate the usage of a given feature |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
34 |
* or technique and has been deliberately simplified. Additional steps |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
35 |
* required for a production-quality application, such as security checks, |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
36 |
* input validation and proper error handling, might not be present in |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
37 |
* this sample code. |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
38 |
*/ |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
39 |
|
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
40 |
|
2 | 41 |
import java.io.*; |
42 |
import java.nio.*; |
|
43 |
import java.nio.channels.*; |
|
44 |
import javax.net.ssl.*; |
|
45 |
import javax.net.ssl.SSLEngineResult.*; |
|
46 |
||
47 |
/** |
|
48 |
* A helper class which performs I/O using the SSLEngine API. |
|
49 |
* <P> |
|
50 |
* Each connection has a SocketChannel and a SSLEngine that is |
|
51 |
* used through the lifetime of the Channel. We allocate byte buffers |
|
52 |
* for use as the outbound and inbound network buffers. |
|
53 |
* |
|
54 |
* <PRE> |
|
55 |
* Application Data |
|
56 |
* src requestBB |
|
57 |
* | ^ |
|
58 |
* | | | |
|
59 |
* v | | |
|
60 |
* +----+-----|-----+----+ |
|
61 |
* | | | |
|
62 |
* | SSL|Engine | |
|
63 |
* wrap() | | | unwrap() |
|
64 |
* | OUTBOUND | INBOUND | |
|
65 |
* | | | |
|
66 |
* +----+-----|-----+----+ |
|
67 |
* | | ^ |
|
68 |
* | | | |
|
69 |
* v | |
|
70 |
* outNetBB inNetBB |
|
71 |
* Net data |
|
72 |
* </PRE> |
|
73 |
* |
|
74 |
* These buffers handle all of the intermediary data for the SSL |
|
75 |
* connection. To make things easy, we'll require outNetBB be |
|
76 |
* completely flushed before trying to wrap any more data, but we |
|
77 |
* could certainly remove that restriction by using larger buffers. |
|
78 |
* <P> |
|
79 |
* There are many, many ways to handle compute and I/O strategies. |
|
80 |
* What follows is a relatively simple one. The reader is encouraged |
|
81 |
* to develop the strategy that best fits the application. |
|
82 |
* <P> |
|
83 |
* In most of the non-blocking operations in this class, we let the |
|
84 |
* Selector tell us when we're ready to attempt an I/O operation (by the |
|
85 |
* application repeatedly calling our methods). Another option would be |
|
86 |
* to attempt the operation and return from the method when no forward |
|
87 |
* progress can be made. |
|
88 |
* <P> |
|
89 |
* There's lots of room for enhancements and improvement in this example. |
|
90 |
* <P> |
|
91 |
* We're checking for SSL/TLS end-of-stream truncation attacks via |
|
92 |
* sslEngine.closeInbound(). When you reach the end of a input stream |
|
93 |
* via a read() returning -1 or an IOException, we call |
|
94 |
* sslEngine.closeInbound() to signal to the sslEngine that no more |
|
95 |
* input will be available. If the peer's close_notify message has not |
|
96 |
* yet been received, this could indicate a trucation attack, in which |
|
97 |
* an attacker is trying to prematurely close the connection. The |
|
98 |
* closeInbound() will throw an exception if this condition were |
|
99 |
* present. |
|
100 |
* |
|
101 |
* @author Brad R. Wetmore |
|
102 |
* @author Mark Reinhold |
|
103 |
*/ |
|
104 |
class ChannelIOSecure extends ChannelIO { |
|
105 |
||
106 |
private SSLEngine sslEngine = null; |
|
107 |
||
108 |
private int appBBSize; |
|
109 |
private int netBBSize; |
|
110 |
||
111 |
/* |
|
112 |
* All I/O goes through these buffers. |
|
113 |
* <P> |
|
114 |
* It might be nice to use a cache of ByteBuffers so we're |
|
115 |
* not alloc/dealloc'ing ByteBuffer's for each new SSLEngine. |
|
116 |
* <P> |
|
117 |
* We use our superclass' requestBB for our application input buffer. |
|
118 |
* Outbound application data is supplied to us by our callers. |
|
119 |
*/ |
|
120 |
private ByteBuffer inNetBB; |
|
121 |
private ByteBuffer outNetBB; |
|
122 |
||
123 |
/* |
|
124 |
* An empty ByteBuffer for use when one isn't available, say |
|
125 |
* as a source buffer during initial handshake wraps or for close |
|
126 |
* operations. |
|
127 |
*/ |
|
128 |
private static ByteBuffer hsBB = ByteBuffer.allocate(0); |
|
129 |
||
130 |
/* |
|
131 |
* The FileChannel we're currently transferTo'ing (reading). |
|
132 |
*/ |
|
133 |
private ByteBuffer fileChannelBB = null; |
|
134 |
||
135 |
/* |
|
136 |
* During our initial handshake, keep track of the next |
|
137 |
* SSLEngine operation that needs to occur: |
|
138 |
* |
|
139 |
* NEED_WRAP/NEED_UNWRAP |
|
140 |
* |
|
141 |
* Once the initial handshake has completed, we can short circuit |
|
142 |
* handshake checks with initialHSComplete. |
|
143 |
*/ |
|
144 |
private HandshakeStatus initialHSStatus; |
|
145 |
private boolean initialHSComplete; |
|
146 |
||
147 |
/* |
|
148 |
* We have received the shutdown request by our caller, and have |
|
149 |
* closed our outbound side. |
|
150 |
*/ |
|
151 |
private boolean shutdown = false; |
|
152 |
||
153 |
/* |
|
154 |
* Constructor for a secure ChannelIO variant. |
|
155 |
*/ |
|
156 |
protected ChannelIOSecure(SocketChannel sc, boolean blocking, |
|
157 |
SSLContext sslc) throws IOException { |
|
158 |
super(sc, blocking); |
|
159 |
||
160 |
/* |
|
161 |
* We're a server, so no need to use host/port variant. |
|
162 |
* |
|
163 |
* The first call for a server is a NEED_UNWRAP. |
|
164 |
*/ |
|
165 |
sslEngine = sslc.createSSLEngine(); |
|
166 |
sslEngine.setUseClientMode(false); |
|
167 |
initialHSStatus = HandshakeStatus.NEED_UNWRAP; |
|
168 |
initialHSComplete = false; |
|
169 |
||
170 |
// Create a buffer using the normal expected packet size we'll |
|
171 |
// be getting. This may change, depending on the peer's |
|
172 |
// SSL implementation. |
|
173 |
netBBSize = sslEngine.getSession().getPacketBufferSize(); |
|
174 |
inNetBB = ByteBuffer.allocate(netBBSize); |
|
175 |
outNetBB = ByteBuffer.allocate(netBBSize); |
|
176 |
outNetBB.position(0); |
|
177 |
outNetBB.limit(0); |
|
178 |
} |
|
179 |
||
180 |
/* |
|
181 |
* Static factory method for creating a secure ChannelIO object. |
|
182 |
* <P> |
|
183 |
* We need to allocate different sized application data buffers |
|
184 |
* based on whether we're secure or not. We can't determine |
|
185 |
* this until our sslEngine is created. |
|
186 |
*/ |
|
187 |
static ChannelIOSecure getInstance(SocketChannel sc, boolean blocking, |
|
188 |
SSLContext sslc) throws IOException { |
|
189 |
||
190 |
ChannelIOSecure cio = new ChannelIOSecure(sc, blocking, sslc); |
|
191 |
||
192 |
// Create a buffer using the normal expected application size we'll |
|
193 |
// be getting. This may change, depending on the peer's |
|
194 |
// SSL implementation. |
|
195 |
cio.appBBSize = cio.sslEngine.getSession().getApplicationBufferSize(); |
|
196 |
cio.requestBB = ByteBuffer.allocate(cio.appBBSize); |
|
197 |
||
198 |
return cio; |
|
199 |
} |
|
200 |
||
201 |
/* |
|
202 |
* Calls up to the superclass to adjust the buffer size |
|
203 |
* by an appropriate increment. |
|
204 |
*/ |
|
205 |
protected void resizeRequestBB() { |
|
206 |
resizeRequestBB(appBBSize); |
|
207 |
} |
|
208 |
||
209 |
/* |
|
210 |
* Adjust the inbount network buffer to an appropriate size. |
|
211 |
*/ |
|
212 |
private void resizeResponseBB() { |
|
213 |
ByteBuffer bb = ByteBuffer.allocate(netBBSize); |
|
214 |
inNetBB.flip(); |
|
215 |
bb.put(inNetBB); |
|
216 |
inNetBB = bb; |
|
217 |
} |
|
218 |
||
219 |
/* |
|
220 |
* Writes bb to the SocketChannel. |
|
221 |
* <P> |
|
222 |
* Returns true when the ByteBuffer has no remaining data. |
|
223 |
*/ |
|
224 |
private boolean tryFlush(ByteBuffer bb) throws IOException { |
|
225 |
super.write(bb); |
|
226 |
return !bb.hasRemaining(); |
|
227 |
} |
|
228 |
||
229 |
/* |
|
230 |
* Perform any handshaking processing. |
|
231 |
* <P> |
|
232 |
* This variant is for Servers without SelectionKeys (e.g. |
|
233 |
* blocking). |
|
234 |
*/ |
|
235 |
boolean doHandshake() throws IOException { |
|
236 |
return doHandshake(null); |
|
237 |
} |
|
238 |
||
239 |
/* |
|
240 |
* Perform any handshaking processing. |
|
241 |
* <P> |
|
242 |
* If a SelectionKey is passed, register for selectable |
|
243 |
* operations. |
|
244 |
* <P> |
|
245 |
* In the blocking case, our caller will keep calling us until |
|
246 |
* we finish the handshake. Our reads/writes will block as expected. |
|
247 |
* <P> |
|
248 |
* In the non-blocking case, we just received the selection notification |
|
249 |
* that this channel is ready for whatever the operation is, so give |
|
250 |
* it a try. |
|
251 |
* <P> |
|
252 |
* return: |
|
253 |
* true when handshake is done. |
|
254 |
* false while handshake is in progress |
|
255 |
*/ |
|
256 |
boolean doHandshake(SelectionKey sk) throws IOException { |
|
257 |
||
258 |
SSLEngineResult result; |
|
259 |
||
260 |
if (initialHSComplete) { |
|
261 |
return initialHSComplete; |
|
262 |
} |
|
263 |
||
264 |
/* |
|
265 |
* Flush out the outgoing buffer, if there's anything left in |
|
266 |
* it. |
|
267 |
*/ |
|
268 |
if (outNetBB.hasRemaining()) { |
|
269 |
||
270 |
if (!tryFlush(outNetBB)) { |
|
271 |
return false; |
|
272 |
} |
|
273 |
||
274 |
// See if we need to switch from write to read mode. |
|
275 |
||
276 |
switch (initialHSStatus) { |
|
277 |
||
278 |
/* |
|
279 |
* Is this the last buffer? |
|
280 |
*/ |
|
281 |
case FINISHED: |
|
282 |
initialHSComplete = true; |
|
283 |
// Fall-through to reregister need for a Read. |
|
284 |
||
285 |
case NEED_UNWRAP: |
|
286 |
if (sk != null) { |
|
287 |
sk.interestOps(SelectionKey.OP_READ); |
|
288 |
} |
|
289 |
break; |
|
290 |
} |
|
291 |
||
292 |
return initialHSComplete; |
|
293 |
} |
|
294 |
||
295 |
||
296 |
switch (initialHSStatus) { |
|
297 |
||
298 |
case NEED_UNWRAP: |
|
299 |
if (sc.read(inNetBB) == -1) { |
|
300 |
sslEngine.closeInbound(); |
|
301 |
return initialHSComplete; |
|
302 |
} |
|
303 |
||
304 |
needIO: |
|
305 |
while (initialHSStatus == HandshakeStatus.NEED_UNWRAP) { |
|
306 |
resizeRequestBB(); // expected room for unwrap |
|
307 |
inNetBB.flip(); |
|
308 |
result = sslEngine.unwrap(inNetBB, requestBB); |
|
309 |
inNetBB.compact(); |
|
310 |
||
311 |
initialHSStatus = result.getHandshakeStatus(); |
|
312 |
||
313 |
switch (result.getStatus()) { |
|
314 |
||
315 |
case OK: |
|
316 |
switch (initialHSStatus) { |
|
317 |
case NOT_HANDSHAKING: |
|
318 |
throw new IOException( |
|
319 |
"Not handshaking during initial handshake"); |
|
320 |
||
321 |
case NEED_TASK: |
|
322 |
initialHSStatus = doTasks(); |
|
323 |
break; |
|
324 |
||
325 |
case FINISHED: |
|
326 |
initialHSComplete = true; |
|
327 |
break needIO; |
|
328 |
} |
|
329 |
||
330 |
break; |
|
331 |
||
332 |
case BUFFER_UNDERFLOW: |
|
333 |
// Resize buffer if needed. |
|
334 |
netBBSize = sslEngine.getSession().getPacketBufferSize(); |
|
335 |
if (netBBSize > inNetBB.capacity()) { |
|
336 |
resizeResponseBB(); |
|
337 |
} |
|
338 |
||
339 |
/* |
|
340 |
* Need to go reread the Channel for more data. |
|
341 |
*/ |
|
342 |
if (sk != null) { |
|
343 |
sk.interestOps(SelectionKey.OP_READ); |
|
344 |
} |
|
345 |
break needIO; |
|
346 |
||
347 |
case BUFFER_OVERFLOW: |
|
348 |
// Reset the application buffer size. |
|
349 |
appBBSize = |
|
350 |
sslEngine.getSession().getApplicationBufferSize(); |
|
351 |
break; |
|
352 |
||
353 |
default: //CLOSED: |
|
354 |
throw new IOException("Received" + result.getStatus() + |
|
355 |
"during initial handshaking"); |
|
356 |
} |
|
357 |
} // "needIO" block. |
|
358 |
||
359 |
/* |
|
360 |
* Just transitioned from read to write. |
|
361 |
*/ |
|
362 |
if (initialHSStatus != HandshakeStatus.NEED_WRAP) { |
|
363 |
break; |
|
364 |
} |
|
365 |
||
366 |
// Fall through and fill the write buffers. |
|
367 |
||
368 |
case NEED_WRAP: |
|
369 |
/* |
|
370 |
* The flush above guarantees the out buffer to be empty |
|
371 |
*/ |
|
372 |
outNetBB.clear(); |
|
373 |
result = sslEngine.wrap(hsBB, outNetBB); |
|
374 |
outNetBB.flip(); |
|
375 |
||
376 |
initialHSStatus = result.getHandshakeStatus(); |
|
377 |
||
378 |
switch (result.getStatus()) { |
|
379 |
case OK: |
|
380 |
||
381 |
if (initialHSStatus == HandshakeStatus.NEED_TASK) { |
|
382 |
initialHSStatus = doTasks(); |
|
383 |
} |
|
384 |
||
385 |
if (sk != null) { |
|
386 |
sk.interestOps(SelectionKey.OP_WRITE); |
|
387 |
} |
|
388 |
||
389 |
break; |
|
390 |
||
391 |
default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED: |
|
392 |
throw new IOException("Received" + result.getStatus() + |
|
393 |
"during initial handshaking"); |
|
394 |
} |
|
395 |
break; |
|
396 |
||
397 |
default: // NOT_HANDSHAKING/NEED_TASK/FINISHED |
|
398 |
throw new RuntimeException("Invalid Handshaking State" + |
|
399 |
initialHSStatus); |
|
400 |
} // switch |
|
401 |
||
402 |
return initialHSComplete; |
|
403 |
} |
|
404 |
||
405 |
/* |
|
406 |
* Do all the outstanding handshake tasks in the current Thread. |
|
407 |
*/ |
|
408 |
private SSLEngineResult.HandshakeStatus doTasks() { |
|
409 |
||
410 |
Runnable runnable; |
|
411 |
||
412 |
/* |
|
413 |
* We could run this in a separate thread, but |
|
414 |
* do in the current for now. |
|
415 |
*/ |
|
416 |
while ((runnable = sslEngine.getDelegatedTask()) != null) { |
|
417 |
runnable.run(); |
|
418 |
} |
|
419 |
return sslEngine.getHandshakeStatus(); |
|
420 |
} |
|
421 |
||
422 |
/* |
|
423 |
* Read the channel for more information, then unwrap the |
|
424 |
* (hopefully application) data we get. |
|
425 |
* <P> |
|
426 |
* If we run out of data, we'll return to our caller (possibly using |
|
427 |
* a Selector) to get notification that more is available. |
|
428 |
* <P> |
|
429 |
* Each call to this method will perform at most one underlying read(). |
|
430 |
*/ |
|
431 |
int read() throws IOException { |
|
432 |
SSLEngineResult result; |
|
433 |
||
434 |
if (!initialHSComplete) { |
|
435 |
throw new IllegalStateException(); |
|
436 |
} |
|
437 |
||
438 |
int pos = requestBB.position(); |
|
439 |
||
440 |
if (sc.read(inNetBB) == -1) { |
|
441 |
sslEngine.closeInbound(); // probably throws exception |
|
442 |
return -1; |
|
443 |
} |
|
444 |
||
445 |
do { |
|
446 |
resizeRequestBB(); // expected room for unwrap |
|
447 |
inNetBB.flip(); |
|
448 |
result = sslEngine.unwrap(inNetBB, requestBB); |
|
449 |
inNetBB.compact(); |
|
450 |
||
451 |
/* |
|
452 |
* Could check here for a renegotation, but we're only |
|
453 |
* doing a simple read/write, and won't have enough state |
|
454 |
* transitions to do a complete handshake, so ignore that |
|
455 |
* possibility. |
|
456 |
*/ |
|
457 |
switch (result.getStatus()) { |
|
458 |
||
459 |
case BUFFER_OVERFLOW: |
|
460 |
// Reset the application buffer size. |
|
461 |
appBBSize = sslEngine.getSession().getApplicationBufferSize(); |
|
462 |
break; |
|
463 |
||
464 |
case BUFFER_UNDERFLOW: |
|
465 |
// Resize buffer if needed. |
|
466 |
netBBSize = sslEngine.getSession().getPacketBufferSize(); |
|
467 |
if (netBBSize > inNetBB.capacity()) { |
|
468 |
resizeResponseBB(); |
|
469 |
||
470 |
break; // break, next read will support larger buffer. |
|
471 |
} |
|
472 |
case OK: |
|
473 |
if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { |
|
474 |
doTasks(); |
|
475 |
} |
|
476 |
break; |
|
477 |
||
478 |
default: |
|
479 |
throw new IOException("sslEngine error during data read: " + |
|
480 |
result.getStatus()); |
|
481 |
} |
|
482 |
} while ((inNetBB.position() != 0) && |
|
483 |
result.getStatus() != Status.BUFFER_UNDERFLOW); |
|
484 |
||
485 |
return (requestBB.position() - pos); |
|
486 |
} |
|
487 |
||
488 |
/* |
|
489 |
* Try to write out as much as possible from the src buffer. |
|
490 |
*/ |
|
491 |
int write(ByteBuffer src) throws IOException { |
|
492 |
||
493 |
if (!initialHSComplete) { |
|
494 |
throw new IllegalStateException(); |
|
495 |
} |
|
496 |
||
497 |
return doWrite(src); |
|
498 |
} |
|
499 |
||
500 |
/* |
|
501 |
* Try to flush out any existing outbound data, then try to wrap |
|
502 |
* anything new contained in the src buffer. |
|
503 |
* <P> |
|
504 |
* Return the number of bytes actually consumed from the buffer, |
|
505 |
* but the data may actually be still sitting in the output buffer, |
|
506 |
* waiting to be flushed. |
|
507 |
*/ |
|
508 |
private int doWrite(ByteBuffer src) throws IOException { |
|
509 |
int retValue = 0; |
|
510 |
||
511 |
if (outNetBB.hasRemaining() && !tryFlush(outNetBB)) { |
|
512 |
return retValue; |
|
513 |
} |
|
514 |
||
515 |
/* |
|
516 |
* The data buffer is empty, we can reuse the entire buffer. |
|
517 |
*/ |
|
518 |
outNetBB.clear(); |
|
519 |
||
520 |
SSLEngineResult result = sslEngine.wrap(src, outNetBB); |
|
521 |
retValue = result.bytesConsumed(); |
|
522 |
||
523 |
outNetBB.flip(); |
|
524 |
||
525 |
switch (result.getStatus()) { |
|
526 |
||
527 |
case OK: |
|
528 |
if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { |
|
529 |
doTasks(); |
|
530 |
} |
|
531 |
break; |
|
532 |
||
533 |
default: |
|
534 |
throw new IOException("sslEngine error during data write: " + |
|
535 |
result.getStatus()); |
|
536 |
} |
|
537 |
||
538 |
/* |
|
539 |
* Try to flush the data, regardless of whether or not |
|
540 |
* it's been selected. Odds of a write buffer being full |
|
541 |
* is less than a read buffer being empty. |
|
542 |
*/ |
|
543 |
if (outNetBB.hasRemaining()) { |
|
544 |
tryFlush(outNetBB); |
|
545 |
} |
|
546 |
||
547 |
return retValue; |
|
548 |
} |
|
549 |
||
550 |
/* |
|
551 |
* Perform a FileChannel.TransferTo on the socket channel. |
|
552 |
* <P> |
|
553 |
* We have to copy the data into an intermediary app ByteBuffer |
|
554 |
* first, then send it through the SSLEngine. |
|
555 |
* <P> |
|
556 |
* We return the number of bytes actually read out of the |
|
557 |
* filechannel. However, the data may actually be stuck |
|
558 |
* in the fileChannelBB or the outNetBB. The caller |
|
559 |
* is responsible for making sure to call dataFlush() |
|
560 |
* before shutting down. |
|
561 |
*/ |
|
562 |
long transferTo(FileChannel fc, long pos, long len) throws IOException { |
|
563 |
||
564 |
if (!initialHSComplete) { |
|
565 |
throw new IllegalStateException(); |
|
566 |
} |
|
567 |
||
568 |
if (fileChannelBB == null) { |
|
569 |
fileChannelBB = ByteBuffer.allocate(appBBSize); |
|
570 |
fileChannelBB.limit(0); |
|
571 |
} |
|
572 |
||
573 |
fileChannelBB.compact(); |
|
574 |
int fileRead = fc.read(fileChannelBB); |
|
575 |
fileChannelBB.flip(); |
|
576 |
||
577 |
/* |
|
578 |
* We ignore the return value here, we return the |
|
579 |
* number of bytes actually consumed from the the file. |
|
580 |
* We'll flush the output buffer before we start shutting down. |
|
581 |
*/ |
|
582 |
doWrite(fileChannelBB); |
|
583 |
||
584 |
return fileRead; |
|
585 |
} |
|
586 |
||
587 |
/* |
|
588 |
* Flush any remaining data. |
|
589 |
* <P> |
|
590 |
* Return true when the fileChannelBB and outNetBB are empty. |
|
591 |
*/ |
|
592 |
boolean dataFlush() throws IOException { |
|
593 |
boolean fileFlushed = true; |
|
594 |
||
595 |
if ((fileChannelBB != null) && fileChannelBB.hasRemaining()) { |
|
596 |
doWrite(fileChannelBB); |
|
597 |
fileFlushed = !fileChannelBB.hasRemaining(); |
|
598 |
} else if (outNetBB.hasRemaining()) { |
|
599 |
tryFlush(outNetBB); |
|
600 |
} |
|
601 |
||
602 |
return (fileFlushed && !outNetBB.hasRemaining()); |
|
603 |
} |
|
604 |
||
605 |
/* |
|
606 |
* Begin the shutdown process. |
|
607 |
* <P> |
|
608 |
* Close out the SSLEngine if not already done so, then |
|
609 |
* wrap our outgoing close_notify message and try to send it on. |
|
610 |
* <P> |
|
611 |
* Return true when we're done passing the shutdown messsages. |
|
612 |
*/ |
|
613 |
boolean shutdown() throws IOException { |
|
614 |
||
615 |
if (!shutdown) { |
|
616 |
sslEngine.closeOutbound(); |
|
617 |
shutdown = true; |
|
618 |
} |
|
619 |
||
620 |
if (outNetBB.hasRemaining() && tryFlush(outNetBB)) { |
|
621 |
return false; |
|
622 |
} |
|
623 |
||
624 |
/* |
|
625 |
* By RFC 2616, we can "fire and forget" our close_notify |
|
626 |
* message, so that's what we'll do here. |
|
627 |
*/ |
|
628 |
outNetBB.clear(); |
|
629 |
SSLEngineResult result = sslEngine.wrap(hsBB, outNetBB); |
|
630 |
if (result.getStatus() != Status.CLOSED) { |
|
631 |
throw new SSLException("Improper close state"); |
|
632 |
} |
|
633 |
outNetBB.flip(); |
|
634 |
||
635 |
/* |
|
636 |
* We won't wait for a select here, but if this doesn't work, |
|
637 |
* we'll cycle back through on the next select. |
|
638 |
*/ |
|
639 |
if (outNetBB.hasRemaining()) { |
|
640 |
tryFlush(outNetBB); |
|
641 |
} |
|
642 |
||
643 |
return (!outNetBB.hasRemaining() && |
|
644 |
(result.getHandshakeStatus() != HandshakeStatus.NEED_WRAP)); |
|
645 |
} |
|
646 |
||
647 |
/* |
|
648 |
* close() is not overridden |
|
649 |
*/ |
|
650 |
} |