ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/tomcat/util/net/SecureNioChannel.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/tomcat/util/net/SecureNioChannel.java
Status
scanned
Type
file
Name
SecureNioChannel.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
39601 (38.7 KB)
MD5
f089256e912874a9421d94149a707e9c
SHA1
e85c3f964fea85679d12e493ab54d07a264e8284
SHA256
e038cc73d36facbe90a7edb0e6166a9ebcc9bbd30e63c58b0dc5aebaa30c1c0c
SHA512

      
    
SHA1_git
1855cbc10fc407c192fe28552edb461e89d54d98
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
SecureNioChannel.java | 38.7 KB |

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.tomcat.util.net; import java.io.EOFException; import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.Status; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.ByteBufferUtils; import org.apache.tomcat.util.net.NioEndpoint.NioSocketWrapper; import org.apache.tomcat.util.net.TLSClientHelloExtractor.ExtractorResult; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; import org.apache.tomcat.util.net.openssl.ciphers.Group; import org.apache.tomcat.util.net.openssl.ciphers.SignatureScheme; import org.apache.tomcat.util.res.StringManager; /** * Implementation of a secure socket channel */ public class SecureNioChannel extends NioChannel { private static final Log log = LogFactory.getLog(SecureNioChannel.class); private static final StringManager sm = StringManager.getManager(SecureNioChannel.class); // Value determined by observation of what the SSL Engine requested in // various scenarios private static final int DEFAULT_NET_BUFFER_SIZE = 16921; private final NioEndpoint endpoint; protected ByteBuffer netInBuffer; protected ByteBuffer netOutBuffer; protected SSLEngine sslEngine; protected boolean sniComplete = false; protected boolean handshakeComplete = false; protected boolean needHandshakeWrap = false; protected HandshakeStatus handshakeStatus; // gets set by handshake protected boolean closed = false; protected boolean closing = false; private final Map<String,List<String>> additionalTlsAttributes = new HashMap<>(); public SecureNioChannel(SocketBufferHandler bufHandler, NioEndpoint endpoint) { super(bufHandler); // Create the network buffers (these hold the encrypted data). if (endpoint.getSocketProperties().getDirectSslBuffer()) { netInBuffer = ByteBuffer.allocateDirect(DEFAULT_NET_BUFFER_SIZE); netOutBuffer = ByteBuffer.allocateDirect(DEFAULT_NET_BUFFER_SIZE); } else { netInBuffer = ByteBuffer.allocate(DEFAULT_NET_BUFFER_SIZE); netOutBuffer = ByteBuffer.allocate(DEFAULT_NET_BUFFER_SIZE); } this.endpoint = endpoint; } @Override public void reset(SocketChannel channel, NioSocketWrapper socketWrapper) throws IOException { super.reset(channel, socketWrapper); sslEngine = null; sniComplete = false; handshakeComplete = false; closed = false; closing = false; netInBuffer.clear(); } @Override public void free() { super.free(); if (endpoint.getSocketProperties().getDirectSslBuffer()) { ByteBufferUtils.cleanDirectBuffer(netInBuffer); ByteBufferUtils.cleanDirectBuffer(netOutBuffer); } } // =========================================================================================== // NIO SSL METHODS // =========================================================================================== /** * Flushes the buffer to the network, non-blocking * * @param buf ByteBuffer * * @return boolean true if the buffer has been emptied out, false otherwise * * @throws IOException An IO error occurred writing data */ protected boolean flush(ByteBuffer buf) throws IOException { int remaining = buf.remaining(); if (remaining > 0) { return (sc.write(buf) >= remaining); } else { return true; } } /** * Performs SSL handshake, non-blocking, but performs NEED_TASK on the same thread. Hence, you should never call * this method using your Acceptor thread, as you would slow down your system significantly. If the return value * from this method is positive, the selection key should be registered interestOps given by the return value. * * @param read boolean - true if the underlying channel is readable * @param write boolean - true if the underlying channel is writable * * @return 0 if handshake is complete, -1 if an error (other than an IOException) occurred, otherwise it returns a * SelectionKey interestOps value * * @throws IOException If an I/O error occurs during the handshake or if the handshake fails during wrapping or * unwrapping */ @Override public int handshake(boolean read, boolean write) throws IOException { if (handshakeComplete) { return 0; // we have done our initial handshake } if (!sniComplete) { int sniResult = processSNI(); if (sniResult == 0) { sniComplete = true; } else { return sniResult; } } if (!flush(netOutBuffer)) { return SelectionKey.OP_WRITE; // we still have data to write } SSLEngineResult handshake; while (!handshakeComplete) { switch (handshakeStatus) { case NOT_HANDSHAKING: // should never happen throw new IOException(sm.getString("channel.nio.ssl.notHandshaking")); case FINISHED: if (endpoint.hasNegotiableProtocols()) { if (sslEngine instanceof SSLUtil.ProtocolInfo) { socketWrapper .setNegotiatedProtocol(((SSLUtil.ProtocolInfo) sslEngine).getNegotiatedProtocol()); } else { socketWrapper.setNegotiatedProtocol(sslEngine.getApplicationProtocol()); } } // we are complete if we have delivered the last package handshakeComplete = !netOutBuffer.hasRemaining(); // return 0 if we are complete, otherwise we still have data to write return handshakeComplete ? 0 : SelectionKey.OP_WRITE; case NEED_WRAP: // perform the wrap function try { handshake = handshakeWrap(write); } catch (SSLException e) { handshake = handshakeWrap(write); throw e; } if (handshake.getStatus() == Status.OK) { if (handshakeStatus == HandshakeStatus.NEED_TASK) { handshakeStatus = tasks(); } } else if (handshake.getStatus() == Status.CLOSED) { flush(netOutBuffer); return -1; } else { // wrap should always work with our buffers throw new IOException( sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshake.getStatus())); } if (handshakeStatus != HandshakeStatus.NEED_UNWRAP || (!flush(netOutBuffer))) { // should actually return OP_READ if we have NEED_UNWRAP return SelectionKey.OP_WRITE; } // fall down to NEED_UNWRAP on the same call, will result in a // BUFFER_UNDERFLOW if it needs data // $FALL-THROUGH$ case NEED_UNWRAP: // perform the unwrap function handshake = handshakeUnwrap(read); if (handshake.getStatus() == Status.OK) { if (handshakeStatus == HandshakeStatus.NEED_TASK) { handshakeStatus = tasks(); } } else if (handshake.getStatus() == Status.BUFFER_UNDERFLOW) { // read more data, register again for OP_READ return SelectionKey.OP_READ; } else { throw new IOException( sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshake.getStatus())); } break; case NEED_TASK: handshakeStatus = tasks(); break; default: throw new IllegalStateException(sm.getString("channel.nio.ssl.invalidStatus", handshakeStatus)); } } // Handshake is complete if this point is reached return 0; } /* * Peeks at the initial network bytes to determine if the SNI extension is present and, if it is, what host name has * been requested. Based on the provided host name, configure the SSLEngine for this connection. * * @return 0 if SNI processing is complete, -1 if an error (other than an IOException) occurred, otherwise it * returns a SelectionKey interestOps value * * @throws IOException If an I/O error occurs during the SNI processing */ protected int processSNI() throws IOException { // Read some data into the network input buffer so we can peek at it. int bytesRead = sc.read(netInBuffer); if (bytesRead == -1) { // Reached end of stream before SNI could be processed. return -1; } TLSClientHelloExtractor extractor = new TLSClientHelloExtractor(netInBuffer); while (extractor.getResult() == ExtractorResult.UNDERFLOW && netInBuffer.capacity() < endpoint.getSniParseLimit()) { // extractor needed more data to process but netInBuffer was full so // expand the buffer and read some more data. int newLimit = Math.min(netInBuffer.capacity() * 2, endpoint.getSniParseLimit()); log.info(sm.getString("channel.nio.ssl.expandNetInBuffer", Integer.toString(newLimit))); netInBuffer = ByteBufferUtils.expand(netInBuffer, newLimit); if (sc.read(netInBuffer) < 0) { return -1; } extractor = new TLSClientHelloExtractor(netInBuffer); } String hostName = null; List<Cipher> clientRequestedCiphers = null; List<String> clientRequestedApplicationProtocols = null; List<Group> clientSupportedGroups = null; List<SignatureScheme> clientSignatureSchemes = null; switch (extractor.getResult()) { case COMPLETE: hostName = extractor.getSNIValue(); socketWrapper.setSniHostName(hostName); clientRequestedApplicationProtocols = extractor.getClientRequestedApplicationProtocols(); //$FALL-THROUGH$ to set the client requested ciphers case NOT_PRESENT: clientRequestedCiphers = extractor.getClientRequestedCiphers(); clientSupportedGroups = extractor.getClientSupportedGroups(); clientSignatureSchemes = extractor.getClientSignatureSchemes(); break; case NEED_READ: return SelectionKey.OP_READ; case UNDERFLOW: // Unable to buffer enough data to read SNI extension data if (log.isDebugEnabled()) { log.debug(sm.getString("channel.nio.ssl.sniDefault")); } hostName = endpoint.getDefaultSSLHostConfigName(); clientRequestedCiphers = Collections.emptyList(); break; case NON_SECURE: netOutBuffer.clear(); netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); netOutBuffer.flip(); flushOutbound(); throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); } if (log.isTraceEnabled()) { log.trace(sm.getString("channel.nio.ssl.sniHostName", sc, hostName)); } createSSLEngine(hostName, clientRequestedCiphers, clientRequestedApplicationProtocols, extractor.getClientRequestedProtocols(), clientSupportedGroups, clientSignatureSchemes); // Populate additional TLS attributes obtained from the handshake that // aren't available from the session additionalTlsAttributes.put(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY, extractor.getClientRequestedProtocols()); additionalTlsAttributes.put(SSLSupport.REQUESTED_CIPHERS_KEY, extractor.getClientRequestedCipherNames()); // Ensure the application buffers (which have to be created earlier) are // big enough. getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); if (netOutBuffer.capacity() < sslEngine.getSession().getApplicationBufferSize()) { // Info for now as we may need to increase DEFAULT_NET_BUFFER_SIZE log.info(sm.getString("channel.nio.ssl.expandNetOutBuffer", Integer.toString(sslEngine.getSession().getApplicationBufferSize()))); } netInBuffer = ByteBufferUtils.expand(netInBuffer, sslEngine.getSession().getPacketBufferSize()); netOutBuffer = ByteBufferUtils.expand(netOutBuffer, sslEngine.getSession().getPacketBufferSize()); // Set limit and position to expected values netOutBuffer.position(0); netOutBuffer.limit(0); // Initiate handshake sslEngine.beginHandshake(); handshakeStatus = sslEngine.getHandshakeStatus(); return 0; } /** * Force a blocking handshake to take place for this key. This requires that both network and application buffers * have been emptied out prior to this call taking place, or a IOException will be thrown. * * @param timeout - timeout in milliseconds for each socket operation * * @throws IOException - if an IO exception occurs or if application or network buffers contain data * @throws SocketTimeoutException - if a socket operation timed out */ @SuppressWarnings("null") // key cannot be null public void rehandshake(long timeout) throws IOException { // validate the network buffers are empty if (netInBuffer.position() > 0 && netInBuffer.position() < netInBuffer.limit()) { throw new IOException(sm.getString("channel.nio.ssl.netInputNotEmpty")); } if (netOutBuffer.position() > 0 && netOutBuffer.position() < netOutBuffer.limit()) { throw new IOException(sm.getString("channel.nio.ssl.netOutputNotEmpty")); } if (!getBufHandler().isReadBufferEmpty()) { throw new IOException(sm.getString("channel.nio.ssl.appInputNotEmpty")); } if (!getBufHandler().isWriteBufferEmpty()) { throw new IOException(sm.getString("channel.nio.ssl.appOutputNotEmpty")); } handshakeComplete = false; boolean isReadable = false; boolean isWritable = false; boolean handshaking = true; Selector selector = null; SelectionKey key = null; try { sslEngine.beginHandshake(); handshakeStatus = sslEngine.getHandshakeStatus(); while (handshaking) { int hsStatus = this.handshake(isReadable, isWritable); switch (hsStatus) { case -1: throw new EOFException(sm.getString("channel.nio.ssl.eofDuringHandshake")); case 0: handshaking = false; break; default: long now = System.currentTimeMillis(); if (selector == null) { selector = Selector.open(); key = getIOChannel().register(selector, hsStatus); } else { key.interestOps(hsStatus); // null warning suppressed } int keyCount = selector.select(timeout); if (keyCount == 0 && ((System.currentTimeMillis() - now) >= timeout)) { throw new SocketTimeoutException(sm.getString("channel.nio.ssl.timeoutDuringHandshake")); } isReadable = key.isReadable(); isWritable = key.isWritable(); } } } catch (IOException ioe) { closeSilently(); throw ioe; } catch (Exception e) { closeSilently(); throw new IOException(e); } finally { if (key != null) { try { key.cancel(); } catch (Exception ignore) { // Ignore } } if (selector != null) { try { selector.close(); } catch (Exception ignore) { // Ignore } } } } /** * Executes all the tasks needed on the same thread. * * @return the status */ protected SSLEngineResult.HandshakeStatus tasks() { Runnable r; while ((r = sslEngine.getDelegatedTask()) != null) { r.run(); } return sslEngine.getHandshakeStatus(); } /** * Performs the WRAP function * * @param doWrite boolean * * @return the result * * @throws IOException An IO error occurred */ protected SSLEngineResult handshakeWrap(boolean doWrite) throws IOException { // this should never be called with a network buffer that contains data // so we can clear it here. netOutBuffer.clear(); // perform the wrap getBufHandler().configureWriteBufferForRead(); SSLEngineResult result = sslEngine.wrap(getBufHandler().getWriteBuffer(), netOutBuffer); // prepare the results to be written netOutBuffer.flip(); // set the status handshakeStatus = result.getHandshakeStatus(); // optimization, if we do have a writable channel, write it now if (doWrite) { flush(netOutBuffer); } return result; } /** * Perform handshake unwrap * * @param doread boolean * * @return the result * * @throws IOException An IO error occurred */ protected SSLEngineResult handshakeUnwrap(boolean doread) throws IOException { if (doread) { // if we have data to read, read it int read = sc.read(netInBuffer); if (read == -1) { throw new IOException(sm.getString("channel.nio.ssl.eofDuringHandshake")); } } SSLEngineResult result; boolean cont; // loop while we can perform pure SSLEngine data do { // prepare the buffer with the incoming data netInBuffer.flip(); // call unwrap getBufHandler().configureReadBufferForWrite(); result = sslEngine.unwrap(netInBuffer, getBufHandler().getReadBuffer()); /* * ByteBuffer.compact() is an optional method but netInBuffer is created from either ByteBuffer.allocate() * or ByteBuffer.allocateDirect() and the ByteBuffers returned by those methods do implement compact(). The * ByteBuffer must be in 'read from' mode when compact() is called and will be in 'write to' mode * afterwards. */ netInBuffer.compact(); // read in the status handshakeStatus = result.getHandshakeStatus(); if (result.getStatus() == SSLEngineResult.Status.OK && result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { // execute tasks if we need to handshakeStatus = tasks(); } // perform another unwrap? cont = result.getStatus() == SSLEngineResult.Status.OK && handshakeStatus == HandshakeStatus.NEED_UNWRAP; } while (cont); return result; } public SSLSupport getSSLSupport() { if (sslEngine != null) { SSLSession session = sslEngine.getSession(); return endpoint.getSslImplementation().getSSLSupport(session, additionalTlsAttributes); } return null; } /** * Sends an SSL close message, will not physically close the connection here. <br> * To close the connection, you could do something like * * <pre> * <code> * close(); * while (isOpen() &amp;&amp; !myTimeoutFunction()) Thread.sleep(25); * if ( isOpen() ) close(true); //forces a close if you timed out * </code> * </pre> * * @throws IOException if an I/O error occurs * @throws IOException if there is data on the outgoing network buffer, and we are unable to flush it */ @Override public void close() throws IOException { if (closing) { return; } closing = true; if (sslEngine == null) { netOutBuffer.clear(); closed = true; return; } sslEngine.closeOutbound(); if (!flush(netOutBuffer)) { throw new IOException(sm.getString("channel.nio.ssl.remainingDataDuringClose")); } // prep the buffer for the close message netOutBuffer.clear(); // perform the close, since we called sslEngine.closeOutbound SSLEngineResult handshake = sslEngine.wrap(getEmptyBuf(), netOutBuffer); // we should be in a close state if (handshake.getStatus() != SSLEngineResult.Status.CLOSED) { throw new IOException(sm.getString("channel.nio.ssl.invalidCloseState")); } // prepare the buffer for writing netOutBuffer.flip(); // if there is data to be written flush(netOutBuffer); // is the channel closed? closed = (!netOutBuffer.hasRemaining() && (handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP)); } @Override public void close(boolean force) throws IOException { try { close(); } finally { if (force || closed) { closed = true; sc.close(); } } } private void closeSilently() { try { close(true); } catch (IOException ioe) { // This is expected - swallowing the exception is the reason this // method exists. Log at debug in case someone is interested. if (log.isDebugEnabled()) { log.debug(sm.getString("channel.nio.ssl.closeSilentError"), ioe); } } } /** * Reads a sequence of bytes from this channel into the given buffer. * * @param dst The buffer into which bytes are to be transferred * * @return The number of bytes read, possibly zero, or <code>-1</code> if the channel has reached end-of-stream * * @throws IOException If some other I/O error occurs * @throws IllegalStateException if the handshake was not completed */ @Override public int read(ByteBuffer dst) throws IOException { // are we in the middle of closing or closed? if (closing || closed) { return -1; } // did we finish our handshake? if (!handshakeComplete) { throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake")); } // read from the network int netread = sc.read(netInBuffer); // did we reach EOF? if so send EOF up one layer. if (netread == -1) { return -1; } // the data read int read = 0; // the SSL engine result SSLEngineResult unwrap; do { // prepare the buffer netInBuffer.flip(); // unwrap the data unwrap = sslEngine.unwrap(netInBuffer, dst); // compact the buffer netInBuffer.compact(); if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { // we did receive some data, add it to our total read += unwrap.bytesProduced(); // perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { if (getOutboundRemaining() == 0) { handshakeWrap(true); } else if (needHandshakeWrap) { throw new IOException(sm.getString("channel.nio.ssl.handshakeWrapPending")); } else { needHandshakeWrap = true; } } // if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { break; } } else if (unwrap.getStatus() == Status.BUFFER_OVERFLOW) { if (read > 0) { // Buffer overflow can happen if we have read data. Return // so the destination buffer can be emptied before another // read is attempted break; } else { // The SSL session has increased the required buffer size // since the buffer was created. if (dst == getBufHandler().getReadBuffer()) { // This is the normal case for this code getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); dst = getBufHandler().getReadBuffer(); } else if (getAppReadBufHandler() != null && dst == getAppReadBufHandler().getByteBuffer()) { getAppReadBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); dst = getAppReadBufHandler().getByteBuffer(); } else { // Can't expand the buffer as there is no way to signal // to the caller that the buffer has been replaced. throw new IOException(sm.getString("channel.nio.ssl.unwrapFailResize", unwrap.getStatus())); } } } else if (unwrap.getStatus() == Status.CLOSED && netInBuffer.position() == 0 && read > 0) { // Clean TLS close on input side but there is application data // to process. Can't tell if the client closed the connection // mid-request or if the client is performing a half-close after // a complete request. Assume it is a half-close and allow // processing to continue. If the connection has been closed // mid-request then the next attempt to read will trigger an // EOF. } else { // Something else went wrong throw new IOException(sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus())); } } while (netInBuffer.position() != 0); // continue unwrapping as long as the input buffer has stuff return read; } @Override public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { // are we in the middle of closing or closed? if (closing || closed) { return -1; } // did we finish our handshake? if (!handshakeComplete) { throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake")); } // read from the network int netread = sc.read(netInBuffer); // did we reach EOF? if so send EOF up one layer. if (netread == -1) { return -1; } // the data read int read = 0; // the SSL engine result SSLEngineResult unwrap; OverflowState overflowState = OverflowState.NONE; do { if (overflowState == OverflowState.PROCESSING) { overflowState = OverflowState.DONE; } // prepare the buffer netInBuffer.flip(); // unwrap the data unwrap = sslEngine.unwrap(netInBuffer, dsts, offset, length); // compact the buffer netInBuffer.compact(); if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { // we did receive some data, add it to our total read += unwrap.bytesProduced(); if (overflowState == OverflowState.DONE) { // Remove the data read into the overflow buffer read -= getBufHandler().getReadBuffer().position(); } // perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { if (getOutboundRemaining() == 0) { handshakeWrap(true); } else if (needHandshakeWrap) { throw new IOException(sm.getString("channel.nio.ssl.handshakeWrapPending")); } else { needHandshakeWrap = true; } } // if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { break; } } else if (unwrap.getStatus() == Status.BUFFER_OVERFLOW) { if (read > 0) { // Buffer overflow can happen if we have read data. Return // so the destination buffer can be emptied before another // read is attempted break; } else { ByteBuffer readBuffer = getBufHandler().getReadBuffer(); boolean found = false; boolean resized = true; for (int i = 0; i < length; i++) { // The SSL session has increased the required buffer size // since the buffer was created. if (dsts[offset + i] == getBufHandler().getReadBuffer()) { getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); if (dsts[offset + i] == getBufHandler().getReadBuffer()) { resized = false; } dsts[offset + i] = getBufHandler().getReadBuffer(); found = true; } else if (getAppReadBufHandler() != null && dsts[offset + i] == getAppReadBufHandler().getByteBuffer()) { getAppReadBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); if (dsts[offset + i] == getAppReadBufHandler().getByteBuffer()) { resized = false; } dsts[offset + i] = getAppReadBufHandler().getByteBuffer(); found = true; } } if (found) { if (!resized) { throw new IOException(sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus())); } } else { // Add the main read buffer in the destinations and try again ByteBuffer[] dsts2 = new ByteBuffer[dsts.length + 1]; int dstOffset = 0; for (int i = 0; i < dsts.length + 1; i++) { if (i == offset + length) { dsts2[i] = readBuffer; dstOffset = -1; } else { dsts2[i] = dsts[i + dstOffset]; } } dsts = dsts2; length++; getBufHandler().configureReadBufferForWrite(); overflowState = OverflowState.PROCESSING; } } } else { // Something else went wrong throw new IOException(sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus())); } } while ((netInBuffer.position() != 0 || overflowState == OverflowState.PROCESSING) && overflowState != OverflowState.DONE); return read; } /** * Writes a sequence of bytes to this channel from the given buffer. * * @param src The buffer from which bytes are to be retrieved * * @return The number of bytes written, possibly zero * * @throws IOException If some other I/O error occurs */ @Override public int write(ByteBuffer src) throws IOException { checkInterruptStatus(); if (src == this.netOutBuffer) { return sc.write(src); } else { // Are we closing or closed? if (closing || closed) { throw new IOException(sm.getString("channel.nio.ssl.closing")); } if (!flush(netOutBuffer)) { // We haven't emptied out the buffer yet return 0; } if (!src.hasRemaining()) { // Nothing left to write return 0; } // The data buffer is empty, we can reuse the entire buffer. netOutBuffer.clear(); SSLEngineResult result = sslEngine.wrap(src, netOutBuffer); // Call to wrap() will have included any required handshake data needHandshakeWrap = false; // The number of bytes written int written = result.bytesConsumed(); netOutBuffer.flip(); if (result.getStatus() == Status.OK) { if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } } else { throw new IOException(sm.getString("channel.nio.ssl.wrapFail", result.getStatus())); } // Force a flush flush(netOutBuffer); return written; } } @Override public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { checkInterruptStatus(); // Are we closing or closed? if (closing || closed) { throw new IOException(sm.getString("channel.nio.ssl.closing")); } if (!flush(netOutBuffer)) { // We haven't emptied out the buffer yet return 0; } // The data buffer is empty, we can reuse the entire buffer. netOutBuffer.clear(); SSLEngineResult result = sslEngine.wrap(srcs, offset, length, netOutBuffer); // The number of bytes written int written = result.bytesConsumed(); netOutBuffer.flip(); if (result.getStatus() == Status.OK) { if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } } else { throw new IOException(sm.getString("channel.nio.ssl.wrapFail", result.getStatus())); } // Force a flush flush(netOutBuffer); return written; } @Override public int getOutboundRemaining() { return netOutBuffer.remaining(); } @Override public boolean flushOutbound() throws IOException { int remaining = netOutBuffer.remaining(); flush(netOutBuffer); int remaining2 = netOutBuffer.remaining(); return remaining2 < remaining; } @Override public boolean isHandshakeComplete() { return handshakeComplete; } @Override public boolean isClosing() { return closing; } public SSLEngine getSslEngine() { return sslEngine; } public ByteBuffer getEmptyBuf() { return emptyBuf; } protected void createSSLEngine(String hostName, List<Cipher> clientRequestedCiphers, List<String> clientRequestedApplicationProtocols, List<String> clientRequestedProtocols, List<Group> clientSupportedGroups, List<SignatureScheme> clientSignatureSchemes) { sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers, clientRequestedApplicationProtocols, clientRequestedProtocols, clientSupportedGroups, clientSignatureSchemes); } private enum OverflowState { NONE, PROCESSING, DONE } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
3.5
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9