Crossfire JXClient, Trunk
ChannelIOSecure.java
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
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  *
15  * - Neither the name of Oracle nor the names of its
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 
32 /*
33  * This source code is provided to illustrate the usage of a given feature
34  * or technique and has been deliberately simplified. Additional steps
35  * required for a production-quality application, such as security checks,
36  * input validation and proper error handling, might not be present in
37  * this sample code.
38  */
39 
40 
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 
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() {
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:
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()) {
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) {
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" +
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()) {
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()) {
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  */
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()) {
597  fileFlushed = !fileChannelBB.hasRemaining();
598  } else if (outNetBB.hasRemaining()) {
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()) {
641  }
642 
643  return (!outNetBB.hasRemaining() &&
644  (result.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
645  }
646 
647  /*
648  * close() is not overridden
649  */
650 }
ChannelIOSecure.doWrite
int doWrite(ByteBuffer src)
Definition: ChannelIOSecure.java:508
ChannelIOSecure.doHandshake
boolean doHandshake(SelectionKey sk)
Definition: ChannelIOSecure.java:256
ChannelIOSecure.appBBSize
int appBBSize
Definition: ChannelIOSecure.java:108
ChannelIOSecure.dataFlush
boolean dataFlush()
Definition: ChannelIOSecure.java:592
result
the functions do not always return the right values for PostScript fonts There are still some bugs around the error handling Most of these problems will usually get fixed when some parameters are or the screen is refreshed Many fonts on Solaris fails to retrieve outlines and as the result
Definition: README.txt:145
ChannelIOSecure.outNetBB
ByteBuffer outNetBB
Definition: ChannelIOSecure.java:121
ChannelIOSecure.read
int read()
Definition: ChannelIOSecure.java:431
ChannelIOSecure.inNetBB
ByteBuffer inNetBB
Definition: ChannelIOSecure.java:120
ChannelIOSecure.initialHSStatus
HandshakeStatus initialHSStatus
Definition: ChannelIOSecure.java:144
ChannelIOSecure.doTasks
SSLEngineResult.HandshakeStatus doTasks()
Definition: ChannelIOSecure.java:408
ChannelIOSecure
Definition: ChannelIOSecure.java:104
ChannelIOSecure.shutdown
boolean shutdown
Definition: ChannelIOSecure.java:151
ChannelIOSecure.transferTo
long transferTo(FileChannel fc, long pos, long len)
Definition: ChannelIOSecure.java:562
ChannelIOSecure.resizeResponseBB
void resizeResponseBB()
Definition: ChannelIOSecure.java:212
ChannelIO.requestBB
ByteBuffer requestBB
Definition: ChannelIO.java:65
ChannelIOSecure.resizeRequestBB
void resizeRequestBB()
Definition: ChannelIOSecure.java:205
ChannelIOSecure.getInstance
static ChannelIOSecure getInstance(SocketChannel sc, boolean blocking, SSLContext sslc)
Definition: ChannelIOSecure.java:187
ChannelIOSecure.tryFlush
boolean tryFlush(ByteBuffer bb)
Definition: ChannelIOSecure.java:224
ChannelIOSecure.write
int write(ByteBuffer src)
Definition: ChannelIOSecure.java:491
ChannelIO.sc
SocketChannel sc
Definition: ChannelIO.java:58
ChannelIOSecure.fileChannelBB
ByteBuffer fileChannelBB
Definition: ChannelIOSecure.java:133
ChannelIOSecure.doHandshake
boolean doHandshake()
Definition: ChannelIOSecure.java:235
ChannelIOSecure.initialHSComplete
boolean initialHSComplete
Definition: ChannelIOSecure.java:145
ChannelIO
Definition: ChannelIO.java:56
ChannelIOSecure.ChannelIOSecure
ChannelIOSecure(SocketChannel sc, boolean blocking, SSLContext sslc)
Definition: ChannelIOSecure.java:156
ChannelIOSecure.sslEngine
SSLEngine sslEngine
Definition: ChannelIOSecure.java:106
ChannelIOSecure.netBBSize
int netBBSize
Definition: ChannelIOSecure.java:109
ChannelIOSecure.shutdown
boolean shutdown()
Definition: ChannelIOSecure.java:613
ChannelIOSecure.hsBB
static ByteBuffer hsBB
Definition: ChannelIOSecure.java:128