Class KeyExchangeMessageHandler
- java.lang.Object
-
- org.apache.sshd.common.session.helpers.KeyExchangeMessageHandler
-
public class KeyExchangeMessageHandler extends Object
Manages SSH message sending during a key exchange. RFC 4253 specifies that during a key exchange, no high-level messages are to be sent, but a receiver must be able to deal with messages "in flight" until the peer'sSshConstants#SSH_MSG_KEX_INIT
message is received.Apache MINA sshd queues up high-level messages that threads try to send while a key exchange is ongoing, and sends them once the key exchange is done. Sending queued messages may make the peer re-trigger a new key exchange, in which case sending queued messages stops and is resumed at the end of the new key exchange.
- See Also:
- RFC 4253
-
-
Field Summary
Fields Modifier and Type Field Description protected ExecutorService
flushRunner
AnExecutorService
used to flush the queue asynchronously.protected AtomicBoolean
kexFlushed
Indicates that all pending packets have been flushed.protected AtomicReference<DefaultKeyExchangeFuture>
kexFlushedFuture
Nevernull
.protected ReentrantReadWriteLock
lock
We need the flushing thread to have priority over writing threads.protected org.slf4j.Logger
log
TheLogger
to use.protected Queue<PendingWriteFuture>
pendingPackets
Queues up high-level packets written during an ongoing key exchange.protected AbstractSession
session
TheAbstractSession
thisKeyExchangeMessageHandler
belongs to.protected AtomicBoolean
shutDown
Indicates that the handler has been shut down.
-
Constructor Summary
Constructors Constructor Description KeyExchangeMessageHandler(AbstractSession session, org.slf4j.Logger log)
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description protected PendingWriteFuture
enqueuePendingPacket(int cmd, Buffer buffer)
Enqueues a packet to be written once a running key exchange terminates.protected void
flushQueue(DefaultKeyExchangeFuture flushDone)
Flushes all packets enqueued while a key exchange was ongoing.DefaultKeyExchangeFuture
initNewKeyExchange()
Initializes the state for a new key exchange.protected boolean
isBlockAllowed(int cmd)
Tells whether the calling thread may be blocked inwritePacket(Buffer, long, TimeUnit)
.void
shutdown()
Pretends all pending packets had been written.AbstractMap.SimpleImmutableEntry<Integer,DefaultKeyExchangeFuture>
terminateKeyExchange()
To be called when the key exchange is done.void
updateState(Runnable update)
<V> V
updateState(Supplier<V> update)
protected IoWriteFuture
writeOrEnqueue(int cmd, Buffer buffer, long timeout, TimeUnit unit)
Writes an SSH packet.IoWriteFuture
writePacket(Buffer buffer, long timeout, TimeUnit unit)
Writes a packet.
-
-
-
Field Detail
-
lock
protected final ReentrantReadWriteLock lock
We need the flushing thread to have priority over writing threads. So we use a lock that favors writers over readers, and any state updates and the flushing thread are writers, while writePacket() is a reader.
-
flushRunner
protected final ExecutorService flushRunner
AnExecutorService
used to flush the queue asynchronously.- See Also:
flushQueue(DefaultKeyExchangeFuture)
-
session
protected final AbstractSession session
TheAbstractSession
thisKeyExchangeMessageHandler
belongs to.
-
log
protected final org.slf4j.Logger log
TheLogger
to use.
-
pendingPackets
protected final Queue<PendingWriteFuture> pendingPackets
Queues up high-level packets written during an ongoing key exchange.
-
kexFlushed
protected final AtomicBoolean kexFlushed
Indicates that all pending packets have been flushed. Set totrue
by the flushing thread, or at the end of KEX if there are no packets to be flushed. Set tofalse
when a new KEX starts. Initiallytrue
.
-
shutDown
protected final AtomicBoolean shutDown
Indicates that the handler has been shut down.
-
kexFlushedFuture
protected final AtomicReference<DefaultKeyExchangeFuture> kexFlushedFuture
Nevernull
. Used to block some threads when writing packets while pending packets are still being flushed at the end of a KEX to avoid overrunning the flushing thread. Always set, initially fulfilled. At the beginning of a KEX a new future is installed, which is fulfilled at the end of the KEX once there are no more pending packets to be flushed.
-
-
Constructor Detail
-
KeyExchangeMessageHandler
public KeyExchangeMessageHandler(AbstractSession session, org.slf4j.Logger log)
- Parameters:
session
-AbstractSession
the new instance belongs tolog
-Logger
to use for writing log messages
-
-
Method Detail
-
updateState
public void updateState(Runnable update)
-
updateState
public <V> V updateState(Supplier<V> update)
-
initNewKeyExchange
public DefaultKeyExchangeFuture initNewKeyExchange()
Initializes the state for a new key exchange.#allPacketsFlushed()
will befalse
, and a new future to be fulfilled when all queued packets will be flushed once the key exchange is done is set. The currently set future from an earlier key exchange is returned. The returned future may or may not be fulfilled; if it isn't, there are still left-over pending packets to write from the previous key exchange, which will be written once the new key exchange flushes pending packets.- Returns:
- the previous
DefaultKeyExchangeFuture
indicating whether all pending packets were flushed.
-
terminateKeyExchange
public AbstractMap.SimpleImmutableEntry<Integer,DefaultKeyExchangeFuture> terminateKeyExchange()
To be called when the key exchange is done. If there are any pending packets, returns a future that will be fulfilled whenflushQueue(DefaultKeyExchangeFuture)
with that future as argument has flushed all pending packets, if there are any.- Returns:
- the current
DefaultKeyExchangeFuture
and the number of currently pending packets
-
shutdown
public void shutdown()
Pretends all pending packets had been written. To be called when theAbstractSession
closes.
-
writePacket
public IoWriteFuture writePacket(Buffer buffer, long timeout, TimeUnit unit) throws IOException
Writes a packet. If a key exchange is ongoing, only low-level messages are written directly; all other messages are queued and will be written onceflushQueue(DefaultKeyExchangeFuture)
is called when the key exchange is done. Packets written while there are still pending packets to be flushed will either be queued, too, or the calling thread will be blocked with the given timeout until all packets have been flushed. Whether a write will be blocked is determined byisBlockAllowed(int)
.If a packet was written, a key exchange may be triggered via
AbstractSession.checkRekey()
.If
timeout <= 0
orunit == null
, a time-out of "forever" is assumed. Note that a timeout applies only if the calling thread is blocked.- Parameters:
buffer
- packet to writetimeout
- number ofTimeUnit
s to wait at most if the calling thread is blockedunit
-TimeUnit
oftimeout
- Returns:
- an
IoWriteFuture
that will be fulfilled once the packet has indeed been written. - Throws:
IOException
- if an error occurs
-
writeOrEnqueue
protected IoWriteFuture writeOrEnqueue(int cmd, Buffer buffer, long timeout, TimeUnit unit) throws IOException
Writes an SSH packet. If no KEX is ongoing and there are no pending packets queued to be written after KEX, the buffer is written directly. Otherwise, the write is enqueued or the calling thread is blocked until all pending packets have been written, depending on the result ofisBlockAllowed(int)
. If the calling thread holds the monitor of the session'sAbstractCloseable.getFutureLock()
, it is never blocked and the write is queued.If
timeout <= 0
orunit == null
, a time-out of "forever" is assumed. Note that a timeout applies only if the calling thread is blocked.- Parameters:
cmd
- SSH command from the bufferbuffer
-Buffer
containing the packet to writetimeout
- number ofTimeUnit
s to wait at most if the calling thread is blockedunit
-TimeUnit
oftimeout
- Returns:
- an
IoWriteFuture
that will be fulfilled once the packet has indeed been written. - Throws:
IOException
- if an error occurs
-
isBlockAllowed
protected boolean isBlockAllowed(int cmd)
Tells whether the calling thread may be blocked inwritePacket(Buffer, long, TimeUnit)
. This implementation blocks writes of channel data packets unless written by aninternal thread
.Typically an internal thread is one of the reading threads of Apache MINA sshd handling an SSH protocol message: it's holding the
AbstractSession.decodeLock
; blocking it would mean we couldn't handle any other incoming message, not even disconnections or another key exchange triggered by having lots of data queued.- Parameters:
cmd
- SSH command of the buffer to be written- Returns:
true
if the thread may be blocked;false
if the packet written must be queued without blocking the thread
-
enqueuePendingPacket
protected PendingWriteFuture enqueuePendingPacket(int cmd, Buffer buffer)
Enqueues a packet to be written once a running key exchange terminates.- Parameters:
cmd
- the SSH command from the bufferbuffer
- theBuffer
containing the packet to be sent- Returns:
- the enqueued
PendingWriteFuture
-
flushQueue
protected void flushQueue(DefaultKeyExchangeFuture flushDone)
Flushes all packets enqueued while a key exchange was ongoing. If writing the pending packets triggers a new key exchange, flushing is stopped and is to be resumed by another call to this method when the new key exchange is done.- Parameters:
flushDone
- the future obtained from#getFlushedFuture()
; will be fulfilled once all pending packets have been written
-
-