/* * Begin the shutdown process. * <P> * Close out the SSLEngine if not already done so, then * wrap our outgoing close_notify message and try to send it on. * <P> * Return true when we're done passing the shutdown messsages. */ boolean shutdown() throws IOException { if (!shutdown) { sslEngine.closeOutbound(); shutdown = true; } if (outNetBB.hasRemaining() && tryFlush(outNetBB)) { return false; } /* * By RFC 2616, we can "fire and forget" our close_notify * message, so that's what we'll do here. */ outNetBB.clear(); SSLEngineResult result = sslEngine.wrap(hsBB, outNetBB); if (result.getStatus() != Status.CLOSED) { throw new SSLException("Improper close state"); } outNetBB.flip(); /* * We won't wait for a select here, but if this doesn't work, * we'll cycle back through on the next select. */ if (outNetBB.hasRemaining()) { tryFlush(outNetBB); } return (!outNetBB.hasRemaining() && (result.getHandshakeStatus() != HandshakeStatus.NEED_WRAP)); }
/* * Flush any remaining data. * <P> * Return true when the fileChannelBB and outNetBB are empty. */ boolean dataFlush() throws IOException { boolean fileFlushed = true; if ((fileChannelBB != null) && fileChannelBB.hasRemaining()) { doWrite(fileChannelBB); fileFlushed = !fileChannelBB.hasRemaining(); } else if (outNetBB.hasRemaining()) { tryFlush(outNetBB); } return (fileFlushed && !outNetBB.hasRemaining()); }
/* * Try to flush out any existing outbound data, then try to wrap * anything new contained in the src buffer. * <P> * Return the number of bytes actually consumed from the buffer, * but the data may actually be still sitting in the output buffer, * waiting to be flushed. */ private int doWrite(ByteBuffer src) throws IOException { int retValue = 0; if (outNetBB.hasRemaining() && !tryFlush(outNetBB)) { return retValue; } /* * The data buffer is empty, we can reuse the entire buffer. */ outNetBB.clear(); SSLEngineResult result = sslEngine.wrap(src, outNetBB); retValue = result.bytesConsumed(); outNetBB.flip(); switch (result.getStatus()) { case OK: if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { doTasks(); } break; default: throw new IOException("sslEngine error during data write: " + result.getStatus()); } /* * Try to flush the data, regardless of whether or not * it's been selected. Odds of a write buffer being full * is less than a read buffer being empty. */ if (outNetBB.hasRemaining()) { tryFlush(outNetBB); } return retValue; }
/* * Perform any handshaking processing. * <P> * If a SelectionKey is passed, register for selectable * operations. * <P> * In the blocking case, our caller will keep calling us until * we finish the handshake. Our reads/writes will block as expected. * <P> * In the non-blocking case, we just received the selection notification * that this channel is ready for whatever the operation is, so give * it a try. * <P> * return: * true when handshake is done. * false while handshake is in progress */ boolean doHandshake(SelectionKey sk) throws IOException { SSLEngineResult result; if (initialHSComplete) { return initialHSComplete; } /* * Flush out the outgoing buffer, if there's anything left in * it. */ if (outNetBB.hasRemaining()) { if (!tryFlush(outNetBB)) { return false; } // See if we need to switch from write to read mode. switch (initialHSStatus) { /* * Is this the last buffer? */ case FINISHED: initialHSComplete = true; // Fall-through to reregister need for a Read. case NEED_UNWRAP: if (sk != null) { sk.interestOps(SelectionKey.OP_READ); } break; } return initialHSComplete; } switch (initialHSStatus) { case NEED_UNWRAP: if (sc.read(inNetBB) == -1) { sslEngine.closeInbound(); return initialHSComplete; } needIO: while (initialHSStatus == HandshakeStatus.NEED_UNWRAP) { resizeRequestBB(); // expected room for unwrap inNetBB.flip(); result = sslEngine.unwrap(inNetBB, requestBB); inNetBB.compact(); initialHSStatus = result.getHandshakeStatus(); switch (result.getStatus()) { case OK: switch (initialHSStatus) { case NOT_HANDSHAKING: throw new IOException("Not handshaking during initial handshake"); case NEED_TASK: initialHSStatus = doTasks(); break; case FINISHED: initialHSComplete = true; break needIO; } break; case BUFFER_UNDERFLOW: // Resize buffer if needed. netBBSize = sslEngine.getSession().getPacketBufferSize(); if (netBBSize > inNetBB.capacity()) { resizeResponseBB(); } /* * Need to go reread the Channel for more data. */ if (sk != null) { sk.interestOps(SelectionKey.OP_READ); } break needIO; case BUFFER_OVERFLOW: // Reset the application buffer size. appBBSize = sslEngine.getSession().getApplicationBufferSize(); break; default: // CLOSED: throw new IOException("Received" + result.getStatus() + "during initial handshaking"); } } // "needIO" block. /* * Just transitioned from read to write. */ if (initialHSStatus != HandshakeStatus.NEED_WRAP) { break; } // Fall through and fill the write buffers. case NEED_WRAP: /* * The flush above guarantees the out buffer to be empty */ outNetBB.clear(); result = sslEngine.wrap(hsBB, outNetBB); outNetBB.flip(); initialHSStatus = result.getHandshakeStatus(); switch (result.getStatus()) { case OK: if (initialHSStatus == HandshakeStatus.NEED_TASK) { initialHSStatus = doTasks(); } if (sk != null) { sk.interestOps(SelectionKey.OP_WRITE); } break; default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED: throw new IOException("Received" + result.getStatus() + "during initial handshaking"); } break; default: // NOT_HANDSHAKING/NEED_TASK/FINISHED throw new RuntimeException("Invalid Handshaking State" + initialHSStatus); } // switch return initialHSComplete; }
/* * Writes bb to the SocketChannel. * <P> * Returns true when the ByteBuffer has no remaining data. */ private boolean tryFlush(ByteBuffer bb) throws IOException { super.write(bb); return !bb.hasRemaining(); }