While testing a a SFTP client of mine using libcurl + libssh2.18, I
discovered a bug when maintaining a persistent connection with the VanDyke
VShell Server (Windows based SFTP server).
My code would open a SFTP session to the VShell server, and poll a directory
every 60 seconds. My client would then download files of any interest. The
entire time the libcurl easy handle would keep the SFTP session open the
server. The VShell server would periodically (by default every 60 minutes
and at most every 10 minutes) send a KEXINIT message while my code was
"sleeping" between tasks.
libssh2 would only be able to detect this received KEXINIT message while it
executed another action for libcurl (for example libssh2_sftp_open_handle).
While waiting for a reply for the original action, the libssh2 code would
begin the key re-exchange. However, as everything was being executed in
non-blocking mode, libssh2 would return from the key re-exchange context to
the original SFTP context before the needed KEX_DH_GEX_GROUP message came
from the VShell. The SFTP context code would continue to wait for a SFTP
message reply, which would never come until the key re-exchange was
In some cases libcurl would time out, disconnect, reconnect and everything
would be okay until the next key re-exchange. At the worst, I sometimes
observed at a customer site that execution would be stuck indefinitely
waiting for a file that wouldn't come.
Please find attached a set of patches I've made that has at the very least
mitigated the situation. The ultimate bug seemed to be that in non-blocking
mode, libssh2 would start the key re-exchange, return EAGAIN when no data
was available, and then lose track that the key re-exchange was occuring.
In transport.c, I changed libssh2_packet_read so that if it detected a key
re-exchange was generally active, but not explictly active at that time, it
would redirect into libssh2_kex_exchange. Later on, when fullpacket returns
with PACKET_EAGAIN, I check the packAdd_state to see if adding the packet
fully succeded. This was necessary so I could reset the packet read state,
which was needed for the key re-exchange conversation to be read.
For the file kex.c, I've changed libssh2_kex_exchange to set the
LIBSSH2_KEX_ACTIVE bit upon function entrance, and to unset it at function
exit. I needed this to avoid a endless libssh2_packet_read ->
libssh2_kex_exchange -> ... -> libssh2_packet_read -> libssh2_kex_exchange
In libssh2_priv.h, I've added the LIBSSH2_KEX_ACTIVE flag.
In packet.c, I had to reset the readPack, fullpacket, and packAdd states so
the key re-exchange conversation could proceed. Adding a packet would also
clear out the packAdd_key_state object, which would create a core dump
during the key re-exchange. For all calls to kex_exchange, I swapped
packAdd_key_state with startup_key_state and everything seemed to be fine.
This fixed the key re-exchange problems for me. I'm now able to maintain a
persistent connection with the VShell without disconnects or endless loops.
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
Received on 2008-06-26
libssh2-devel mailing list