com.mongodb.internal.connection.tlschannel.TlsChannel Maven / Gradle / Ivy
Show all versions of mongo-java-driver Show documentation
/*
* Copyright 2008-present MongoDB, Inc.
*
* Licensed 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.
*
* Original Work: MIT License, Copyright (c) [2015-2018] all contributors
* https://github.com/marianobarrios/tls-channel
*/
package com.mongodb.internal.connection.tlschannel;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.function.Consumer;
/**
* A ByteChannel interface to a TLS (Transport Layer Security) connection.
*
*
* Instances that implement this interface delegate all cryptographic operations
* to the standard Java TLS implementation: SSLEngine; effectively hiding it
* behind an easy-to-use streaming API, that allows to securitize JVM
* applications with minimal added complexity.
*
*
* In other words, an interface that allows the programmer to have TLS using the
* same standard socket API used for plaintext, just like OpenSSL does for C,
* only for Java.
*
*
* Note that this is an API adapter, not a cryptographic implementation: with
* the exception of a few bytesProduced of parsing at the beginning of the connection,
* to look for the SNI, the whole protocol implementation is done by the
* SSLEngine. Both the SSLContext and SSLEngine are supplied by the client;
* these classes are the ones responsible for protocol configuration, including
* hostname validation, client-side authentication, etc.
*
*
* A TLS channel is created by using one of its subclasses. They will
* take an existing {@link ByteChannel} (typically, but not necessarily, a
* {@link SocketChannel}) and a {@link SSLEngine}.
*
*
* It should be noted that this interface extends {@link ByteChannel} as a
* design compromise, but it does not follow its interface completely. In
* particular, in case of underlying non-blocking channels, when it is not
* possible to complete an operation, no zero is returned, but an
* {@link WouldBlockException}. This divergence from the base interface is
* needed because both a read
and a write
operation
* can run out of both bytesProduced for reading and buffer space for writing, as a
* handshake (a bidirectional operation) can happen at any moment. The user
* would use a {@link Selector} to wait for the expected condition
* of the underlying channel, and should know which operation to
* register.
*
*
* On top of that, operations can also fail to complete due to asynchronous
* tasks; this is communicated using a {@link NeedsTaskException}. This behavior
* is controlled by the {@link #getRunTasks()} attribute. This allows the user
* to execute CPU-intensive tasks out of the selector loop.
*/
public interface TlsChannel extends ByteChannel, GatheringByteChannel, ScatteringByteChannel {
/**
* Return a reference to the underlying {@link ByteChannel}.
*/
ByteChannel getUnderlying();
/**
* Return a reference to the {@link SSLEngine} used.
*
* @return the engine reference if present, or null
if unknown
* (that can happen in server-side channels before the SNI is
* parsed).
*/
SSLEngine getSslEngine();
/**
* Return the callback function to be executed when the TLS session is
* established (or re-established).
*
* @see TlsChannelBuilder#withSessionInitCallback(Consumer)
*/
Consumer getSessionInitCallback();
/**
* Return the {@link BufferAllocator} to use for unencrypted data. Actually, a decorating subclass is returned,
* which contains allocation statistics for this channel.
*
* @see TlsChannelBuilder#withPlainBufferAllocator(BufferAllocator)
* @see TrackingAllocator
*/
TrackingAllocator getPlainBufferAllocator();
/**
* Return the {@link BufferAllocator} to use for encrypted data. Actually, a decorating subclass is returned,
* which contains allocation statistics for this channel.
*
* @see TlsChannelBuilder#withEncryptedBufferAllocator(BufferAllocator)
* @see TrackingAllocator
*/
TrackingAllocator getEncryptedBufferAllocator();
/**
* Return whether CPU-intensive tasks are run or not.
*
* @see TlsChannelBuilder#withRunTasks(boolean)
*/
boolean getRunTasks();
/**
* Reads a sequence of bytesProduced from this channel into the given buffer.
*
*
* An attempt is made to read up to r bytesProduced from the channel, where
* r is the number of bytesProduced remaining in the buffer, that is,
* dst.remaining(), at the moment this method is invoked.
*
*
* Suppose that a byte sequence of length n is read, where 0
* <= n <= r.
* This byte sequence will be transferred into the buffer so that the first
* byte in the sequence is at index p and the last byte is at index
* p + n - 1,
* where p is the buffer's position at the moment this method is
* invoked. Upon return the buffer's position will be equal to p
* + n; its limit will not have changed.
*
*
* A read operation might not fill the buffer, and in fact it might not read
* any bytesProduced at all. Whether or not it does so depends upon the nature and
* state of the underlying channel. It is guaranteed, however, that if a
* channel is in blocking mode and there is at least one byte remaining in
* the buffer then this method will block until at least one byte is read.
* On the other hand, if the underlying channel is in non-blocking mode then
* a {@link WouldBlockException} may be thrown. Note that this also includes
* the possibility of a {@link NeedsWriteException}, due to the fact that,
* during a TLS handshake, bytesProduced need to be written to the underlying
* channel. In any case, after a {@link WouldBlockException}, the operation
* should be retried when the underlying channel is ready (for reading or
* writing, depending on the subclass).
*
*
* If the channel is configured to not run tasks and one is due to run, a
* {@link NeedsTaskException} will be thrown. In this case the operation
* should be retried after the task is run.
*
*
* This method may be invoked at any time. If another thread has already
* initiated a read or handshaking operation upon this channel, however,
* then an invocation of this method will block until the first operation is
* complete.
*
* @param dst The buffer into which bytesProduced are to be transferred
* @return The number of bytesProduced read, or -1 if the channel has
* reached end-of-stream; contrary to the behavior specified in
* {@link ByteChannel}, this method never returns 0, but throws
* {@link WouldBlockException}
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation
* cannot be completed immediately
* @throws NeedsTaskException if the channel is not configured to run tasks automatically
* and a task needs to be executed to complete the operation
* @throws SSLException if the {@link SSLEngine} throws a SSLException
* @throws IOException if the underlying channel throws an IOException
*/
int read(ByteBuffer dst) throws IOException;
/**
* Writes a sequence of bytesProduced to this channel from the given buffer.
*
*
* An attempt is made to write up to r bytesProduced to the channel, where
* r is the number of bytesProduced remaining in the buffer, that is,
* src.remaining(), at the moment this method is invoked.
*
*
* Suppose that a byte sequence of length n is written, where
* 0 <= n <=
* r. This byte sequence will be transferred from the buffer starting
* at index p, where p is the buffer's position at the moment
* this method is invoked; the index of the last byte written will be
* p + n - 1.
* Upon return the buffer's position will be equal to p
* + n; its limit will not have changed.
*
*
* If the underlying channel is in blocking mode, a write operation will
* return only after writing all of the r requested bytesProduced. On the
* other hand, if it is in non-blocking mode, this operation may write only
* some of the bytesProduced or possibly none at all, in this case a
* {@link WouldBlockException} will be thrown. Note that this also includes
* the possibility of a {@link NeedsReadException}, due to the fact that,
* during a TLS handshake, bytes need to be read from the underlying channel.
* In any case, after a {@link WouldBlockException}, the operation should be
* retried when the underlying channel is ready (for reading or writing,
* depending on the subclass).
*
*
* If the channel is configured to not run tasks and one is due to run, a
* {@link NeedsTaskException} will be thrown. In this case the operation
* should be retried after the task is run.
*
*
* This method may be invoked at any time. If another thread has already
* initiated a write or handshaking operation upon this channel, however,
* then an invocation of this method will block until the first operation is
* complete.
*
* @param src The buffer from which bytesProduced are to be retrieved
* @return The number of bytesProduced written, contrary to the behavior specified
* in {@link ByteChannel}, this method never returns 0, but throws
* {@link WouldBlockException}
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation
* cannot be completed immediately
* @throws NeedsTaskException if the channel is not configured to run tasks automatically
* and a task needs to be executed to complete the operation
* @throws SSLException if the {@link SSLEngine} throws a SSLException
* @throws IOException if the underlying channel throws an IOException
*/
int write(ByteBuffer src) throws IOException;
/**
* Initiates a handshake (initial or renegotiation) on this channel. This
* method is not needed for the initial handshake, as the
* read()
and write()
methods will implicitly do
* the initial handshake if needed.
*
*
* This method may block if the underlying channel if in blocking mode.
*
*
* Note that renegotiation is a problematic feature of the TLS protocol,
* that should only be initiated at quiet point of the protocol.
*
*
* This method may block if the underlying channel is in blocking mode,
* otherwise a {@link WouldBlockException} can be thrown. In this case the
* operation should be retried when the underlying channel is ready (for
* reading or writing, depending on the subclass).
*
*
* If the channel is configured to not run tasks and one is due to run, a
* {@link NeedsTaskException} will be thrown, with a reference to the task.
* In this case the operation should be retried after the task is run.
*
*
* This method may be invoked at any time. If another thread has already
* initiated a read, write, or handshaking operation upon this channel,
* however, then an invocation of this method will block until the first
* operation is complete.
*
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation
* cannot be completed immediately
* @throws NeedsTaskException if the channel is not configured to run tasks automatically
* and a task needs to be executed to complete the operation
* @throws SSLException if the {@link SSLEngine} throws a SSLException
* @throws IOException if the underlying channel throws an IOException
*/
void renegotiate() throws IOException;
/**
* Forces the initial TLS handshake. Calling this method is usually not
* needed, as a handshake will happen automatically when doing the first
* read()
or write()
operation. Calling this
* method after the initial handshake has been done has no effect.
*
*
* This method may block if the underlying channel is in blocking mode,
* otherwise a {@link WouldBlockException} can be thrown. In this case the
* operation should be retried when the underlying channel is ready (for
* reading or writing, depending on the subclass).
*
*
* If the channel is configured to not run tasks and one is due to run, a
* {@link NeedsTaskException} will be thrown, with a reference to the task.
* In this case the operation should be retried after the task is run.
*
*
* This method may be invoked at any time. If another thread has already
* initiated a read, write, or handshaking operation upon this channel,
* however, then an invocation of this method will block until the first
* operation is complete.
*
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation
* cannot be completed immediately
* @throws NeedsTaskException if the channel is not configured to run tasks automatically
* and a task needs to be executed to complete the operation
* @throws SSLException if the {@link SSLEngine} throws a SSLException
* @throws IOException if the underlying channel throws an IOException
*/
void handshake() throws IOException;
/**
* Writes a sequence of bytesProduced to this channel from a subsequence of the
* given buffers.
*
*
* See {@link GatheringByteChannel#write(ByteBuffer[], int, int)} for more
* details of the meaning of this signature.
*
*
* This method behaves slightly different than the interface specification,
* with respect to non-blocking responses, see {@link #write(ByteBuffer)}
* for more details.
*
* @param srcs The buffers from which bytesProduced are to be retrieved
* @param offset The offset within the buffer array of the first buffer from
* which bytesProduced are to be retrieved; must be non-negative and no
* larger than srcs.length
* @param length The maximum number of buffers to be accessed; must be
* non-negative and no larger than srcs.length
* - offset
* @return The number of bytesProduced written, contrary to the behavior specified
* in {@link ByteChannel}, this method never returns 0, but throws
* {@link WouldBlockException}
* @throws IndexOutOfBoundsException If the preconditions on the offset and
* length parameters do not hold
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation
* cannot be completed immediately
* @throws NeedsTaskException if the channel is not configured to run tasks automatically
* and a task needs to be executed to complete the operation
* @throws SSLException if the {@link SSLEngine} throws a SSLException
* @throws IOException if the underlying channel throws an IOException
*/
long write(ByteBuffer[] srcs, int offset, int length) throws IOException;
/**
* Writes a sequence of bytesProduced to this channel from the given buffers.
*
*
* An invocation of this method of the form c.write(srcs) behaves
* in exactly the same manner as the invocation
*
*
*
*
* c.write(srcs, 0, srcs.length);
*
*
*
*
* This method behaves slightly different than the interface specification,
* with respect to non-blocking responses, see {@link #write(ByteBuffer)}
* for more details.
*
* @param srcs The buffers from which bytesProduced are to be retrieved
* @return The number of bytesProduced written, contrary to the behavior specified
* in {@link ByteChannel}, this method never returns 0, but throws
* {@link WouldBlockException}
* @throws IndexOutOfBoundsException If the preconditions on the offset and
* length parameters do not hold
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation
* cannot be completed immediately
* @throws NeedsTaskException if the channel is not configured to run tasks automatically
* and a task needs to be executed to complete the operation
* @throws SSLException if the {@link SSLEngine} throws a SSLException
* @throws IOException if the underlying channel throws an IOExceptions
*/
long write(ByteBuffer[] srcs) throws IOException;
/**
* Reads a sequence of bytesProduced from this channel into a subsequence of the
* given buffers.
*
*
* See {@link ScatteringByteChannel#read(ByteBuffer[], int, int)} for more
* details of the meaning of this signature.
*
*
* This method behaves slightly different than the interface specification,
* with respect to non-blocking responses, see {@link #read(ByteBuffer)} for
* more details.
*
* @param dsts The buffers into which bytesProduced are to be transferred
* @param offset The offset within the buffer array of the first buffer into
* which bytesProduced are to be transferred; must be non-negative and no
* larger than dsts.length
* @param length The maximum number of buffers to be accessed; must be
* non-negative and no larger than dsts.length
* - offset
* @return The number of bytesProduced read, or -1 if the channel has
* reached end-of-stream; contrary to the behavior specified in
* {@link ByteChannel}, this method never returns 0, but throws
* {@link WouldBlockException}
* @throws IndexOutOfBoundsException If the preconditions on the offset and
* length parameters do not hold
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation
* cannot be completed immediately
* @throws NeedsTaskException if the channel is not configured to run tasks automatically
* and a task needs to be executed to complete the operation
* @throws SSLException if the {@link SSLEngine} throws a SSLException
* @throws IOException if the underlying channel throws an IOException
*/
long read(ByteBuffer[] dsts, int offset, int length) throws IOException;
/**
* Reads a sequence of bytesProduced from this channel into the given buffers.
*
*
* An invocation of this method of the form c.read(dsts) behaves in
* exactly the same manner as the invocation
*
*
*
*
* c.read(dsts, 0, dsts.length);
*
*
*
*
*
* This method behaves slightly different than the interface specification,
* with respect to non-blocking responses, see {@link #read(ByteBuffer)} for
* more details.
*
* @param dsts The buffers into which bytesProduced are to be transferred
* @return The number of bytesProduced read, or -1 if the channel has
* reached end-of-stream; contrary to the behavior specified in
* {@link ByteChannel}, this method never returns 0, but throws
* {@link WouldBlockException}
* @throws IndexOutOfBoundsException If the preconditions on the offset and
* length parameters do not hold
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation
* cannot be completed immediately
* @throws NeedsTaskException if the channel is not configured to run tasks automatically
* and a task needs to be executed to complete the operation
* @throws SSLException if the {@link SSLEngine} throws a SSLException
* @throws IOException if the underlying channel throws an IOException
*/
long read(ByteBuffer[] dsts) throws IOException;
/**
* Closes the underlying channel. This method first does some form of TLS
* close if not already done. The exact behavior can be configured using the
* {@link TlsChannelBuilder#withWaitForCloseConfirmation}.
*
*
* The default behavior mimics what happens in a normal (that is, non
* layered) {@link javax.net.ssl.SSLSocket#close()}.
*
*
* For finer control of the TLS close, use {@link #shutdown()}
*
* @throws IOException if the underlying channel throws an IOException during close.
* Exceptions thrown during any previous TLS close are not
* propagated.
*/
void close() throws IOException;
/**
*
Shuts down the TLS connection. This method emulates the behavior of OpenSSL's SSL_shutdown().
*
* The shutdown procedure consists of two steps: the sending of the "close notify" shutdown alert and the
* reception of the peer's "close notify". According to the TLS standard, it is acceptable for an application to
* only send its shutdown alert and then close the underlying connection without waiting for the peer's response.
* When the underlying connection shall be used for more communications, the complete shutdown procedure
* (bidirectional "close notify" alerts) must be performed, so that the peers stay synchronized.
*
* This class supports both uni- and bidirectional shutdown by its 2 step behavior, using this method.
*
* When this is the first party to send the "close notify" alert, this method will only send the alert, set the
* {@link #shutdownSent()} flag and return false
. If a unidirectional shutdown is enough, this first
* call is sufficient. In order to complete the bidirectional shutdown handshake, This method must be called again.
* The second call will wait for the peer's "close notify" shutdown alert. On success, the second call will return
* true
.
*
* If the peer already sent the "close notify" alert and it was already processed implicitly inside a read
* operation, the {@link #shutdownReceived()} flag is already set. This method will then send the "close notify"
* alert, set the {@link #shutdownSent()} flag and immediately return true
. It is therefore recommended
* to check the return value of this method and call it again, if the bidirectional shutdown is not yet
* complete.
*
* If the underlying channel is blocking, this method will only return once the handshake step has been finished
* or an error occurred.
*
* If the underlying channel is non-blocking, this method may throw {@link WouldBlockException} if the
* underlying channel could not support the continuation of the handshake. The calling process then must repeat the
* call after taking appropriate action (like waiting in a selector in case of a {@link SocketChannel}).
*
* Note that despite not being mandated by the specification, a proper TLS close is important to prevent
* truncation attacks, which consists, essentially, of an adversary introducing TCP FIN segments to trick on party
* to ignore the final bytes of a secure stream. For more details, see the
* original paper.
*
* @return whether the closing is finished.
* @throws IOException if the underlying channel throws an IOException
* @throws WouldBlockException if the channel is in non-blocking mode and the IO operation cannot be completed
* immediately
* @see TlsChannelBuilder#withWaitForCloseConfirmation(boolean)
*/
boolean shutdown() throws IOException;
/**
* Return whether this side of the connection has already received the close
* notification.
*
* @return true
if the close notification was received
* @see #shutdown()
*/
boolean shutdownReceived();
/**
* Return whether this side of the connection has already sent the close
* notification.
*
* @return true
if the close notification was sent
* @see #shutdown()
*/
boolean shutdownSent();
}