summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTorsten Landschoff <torsten.landschoff@dynamore.de>2011-08-12 11:25:36 +0200
committerTorsten Landschoff <torsten@landschoff.net>2013-10-24 01:47:24 +0200
commit27b800bf3f1c0ee7063221538d2913cec7c048c1 (patch)
tree8ad47e324f30278394a412464f963200e77a881b
parenta9a5f69c1affae60fd3c390927e0389325f719a1 (diff)
Issue #22: Try IPv4 as well as IPv6 when port is not open on IPv6.
With this change, paramiko tries the next address family when the connection gets refused or the target port is unreachable.
-rw-r--r--paramiko/client.py53
1 files changed, 39 insertions, 14 deletions
diff --git a/paramiko/client.py b/paramiko/client.py
index c5a2d1ac..da08ad0a 100644
--- a/paramiko/client.py
+++ b/paramiko/client.py
@@ -25,6 +25,7 @@ import getpass
import os
import socket
import warnings
+from errno import ECONNREFUSED, EHOSTUNREACH
from paramiko.agent import Agent
from paramiko.common import *
@@ -231,6 +232,29 @@ class SSHClient (object):
"""
self._policy = policy
+ def _families_and_addresses(self, hostname, port):
+ """
+ Yield pairs of address families and addresses to try for connecting.
+
+ @param hostname: the server to connect to
+ @type hostname: str
+ @param port: the server port to connect to
+ @type port: int
+ @rtype: generator
+ """
+ guess = True
+ addrinfos = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
+ for (family, socktype, proto, canonname, sockaddr) in addrinfos:
+ if socktype == socket.SOCK_STREAM:
+ yield family, sockaddr
+ guess = False
+
+ # some OS like AIX don't indicate SOCK_STREAM support, so just guess. :(
+ # We only do this if we did not get a single result marked as socktype == SOCK_STREAM.
+ if guess:
+ for family, _, _, _, sockaddr in addrinfos:
+ yield family, sockaddr
+
def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None,
key_filename=None, timeout=None, allow_agent=True, look_for_keys=True,
compress=False, sock=None):
@@ -288,21 +312,22 @@ class SSHClient (object):
@raise socket.error: if a socket error occurred while connecting
"""
if not sock:
- for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
- if socktype == socket.SOCK_STREAM:
- af = family
- addr = sockaddr
- break
- else:
- # some OS like AIX don't indicate SOCK_STREAM support, so just guess. :(
- af, _, _, _, addr = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
- sock = socket.socket(af, socket.SOCK_STREAM)
- if timeout is not None:
+ for af, addr in self._families_and_addresses(hostname, port):
try:
- sock.settimeout(timeout)
- except:
- pass
- retry_on_signal(lambda: sock.connect(addr))
+ sock = socket.socket(af, socket.SOCK_STREAM)
+ if timeout is not None:
+ try:
+ sock.settimeout(timeout)
+ except:
+ pass
+ retry_on_signal(lambda: sock.connect(addr))
+ # Break out of the loop on success
+ break
+ except socket.error, e:
+ # If the port is not open on IPv6 for example, we may still try IPv4.
+ # Likewise if the host is not reachable using that address family.
+ if e.errno not in (ECONNREFUSED, EHOSTUNREACH):
+ raise
t = self._transport = Transport(sock)
t.use_compression(compress=compress)