summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--paramiko/transport.py9
-rw-r--r--sites/www/changelog.rst5
-rw-r--r--tests/test_transport.py17
3 files changed, 31 insertions, 0 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py
index 36da3043..6047fb99 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -295,6 +295,8 @@ class Transport (threading.Thread, ClosingContextManager):
self.global_response = None # response Message from an arbitrary global request
self.completion_event = None # user-defined event callbacks
self.banner_timeout = 15 # how long (seconds) to wait for the SSH banner
+ self.handshake_timeout = 15 # how long (seconds) to wait for the handshake to finish after SSH banner sent.
+
# server mode:
self.server_mode = False
@@ -1582,6 +1584,12 @@ class Transport (threading.Thread, ClosingContextManager):
try:
self.packetizer.write_all(b(self.local_version + '\r\n'))
self._check_banner()
+ # The above is actually very much part of the handshake, but sometimes the banner can be read
+ # but the machine is not responding, for example when the remote ssh daemon is loaded in to memory
+ # but we can not read from the disk/spawn a new shell.
+ # Make sure we can specify a timeout for the initial handshake.
+ # Re-use the banner timeout for now.
+ self.packetizer.start_handshake(self.handshake_timeout)
self._send_kex_init()
self._expect_packet(MSG_KEXINIT)
@@ -1631,6 +1639,7 @@ class Transport (threading.Thread, ClosingContextManager):
msg.add_byte(cMSG_UNIMPLEMENTED)
msg.add_int(m.seqno)
self._send_message(msg)
+ self.packetizer.complete_handshake()
except SSHException as e:
self._log(ERROR, 'Exception: ' + str(e))
self._log(ERROR, util.tb_strings())
diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst
index 6520dde4..f9900327 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -2,6 +2,11 @@
Changelog
=========
+* :bug:`62` Add timeout for handshake completion.
+ This adds a mechanism for timing out a connection if the ssh handshake
+ never completes.
+ Credit to ``@dacut`` for initial report and patch and to Olle Lundberg for
+ re-implementation.
* :bug:`402` Check to see if an SSH agent is actually present before trying to
forward it to the remote end. This replaces what was usually a useless
``TypeError`` with a human-readable ``AuthenticationError``. Credit to Ken
diff --git a/tests/test_transport.py b/tests/test_transport.py
index 5cf9a867..3c8ad81e 100644
--- a/tests/test_transport.py
+++ b/tests/test_transport.py
@@ -792,3 +792,20 @@ class TransportTest(unittest.TestCase):
(None, DEFAULT_WINDOW_SIZE),
(2**32, MAX_WINDOW_SIZE)]:
self.assertEqual(self.tc._sanitize_window_size(val), correct)
+
+ def test_L_handshake_timeout(self):
+ """
+ verify that we can get a hanshake timeout.
+ """
+ host_key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ public_host_key = RSAKey(data=host_key.asbytes())
+ self.ts.add_server_key(host_key)
+ event = threading.Event()
+ server = NullServer()
+ self.assertTrue(not event.is_set())
+ self.tc.handshake_timeout = 0.000000000001
+ self.ts.start_server(event, server)
+ self.assertRaises(EOFError, self.tc.connect,
+ hostkey=public_host_key,
+ username='slowdive',
+ password='pygmalion')