From cc4f9d5679278ce41cd5480fab3f5e71dba163ed Mon Sep 17 00:00:00 2001 From: Matthew Booth Date: Fri, 16 Mar 2012 16:29:00 +0100 Subject: [PATCH] transport_send: Finish in-progress key exchange before sending data _libssh2_channel_write() first reads outstanding packets before writing new data. If it reads a key exchange request, it will immediately start key re-exchange, which will require sending a response. If the output socket is full, this will result in a return from _libssh2_transport_read() of LIBSSH2_ERROR_EAGAIN. In order not to block a write because there is no data to read, this error is explicitly ignored and the code continues marshalling a packet for sending. When it is sent, the remote end immediately drops the connection because it was expecting a continuation of the key exchange, but got a data packet. This change adds the same check for key exchange to _libssh2_transport_send() that is in _libssh2_transport_read(). This ensures that key exchange is completed before any data packet is sent. --- src/transport.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/transport.c b/src/transport.c index 057dcf5..95b9a3a 100644 --- a/src/transport.c +++ b/src/transport.c @@ -296,7 +296,7 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session) * is done! */ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the" - " key re-exchange"); + " key re-exchange from _libssh2_transport_read"); rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state); if (rc) return rc; @@ -687,6 +687,24 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session, const unsigned char *orgdata = data; size_t orgdata_len = data_len; + /* + * If the last read operation was interrupted in the middle of a key + * exchange, we must complete that key exchange before continuing to write + * further data. + * + * See the similar block in _libssh2_transport_read for more details. + */ + if (session->state & LIBSSH2_STATE_EXCHANGING_KEYS && + !(session->state & LIBSSH2_STATE_KEX_ACTIVE)) { + /* Don't write any new packets if we're still in the middle of a key + * exchange. */ + _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the" + " key re-exchange from _libssh2_transport_send"); + rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state); + if (rc) + return rc; + } + debugdump(session, "libssh2_transport_write plain", data, data_len); if(data2) debugdump(session, "libssh2_transport_write plain2", data2, data2_len);