From 82d5d5e675ca44c35dd1b1295c065d3b34d339f2 Mon Sep 17 00:00:00 2001 From: John Goerzen Date: Thu, 8 Mar 2007 02:59:32 +0100 Subject: [PATCH] Check all resolved addresses [deb #413030] From: Mark Brown Currently offlineimap will attempt to connect to the first address returned by addrinfo() for the remote system and will fail if that fails even if another result would have worked. This is particularly common when the remote system supports both IPv4 and IPv6 - a laptop may in some environments have no routable IPv6 connectivity so if the IPv6 address is returned first the connect will fail even though IPv4 would have worked. This is actually a bug in imaplib, a copy of which is included in offlineimap. This patch fixes the problem by looping over all the results returned by getaddrinfo(). Unfortunately it mangles the error reporting slightly since I couldn't work out how to raise an appropriate exception, though given that that that was a Python backtrace there was work to do there anyway. Note that I have only tested the SSL case. --- offlineimap/imaplib.py | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/offlineimap/imaplib.py b/offlineimap/imaplib.py index 2bbe92e..a4bdc22 100644 --- a/offlineimap/imaplib.py +++ b/offlineimap/imaplib.py @@ -218,15 +218,26 @@ class IMAP4: """ self.host = host self.port = port - #This connects to the first ip found ipv4/ipv6 - #Added by Adriaan Peeters based on a socket - #example from the python documentation: - #http://www.python.org/doc/lib/socket-example.html res = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM) - af, socktype, proto, canonname, sa = res[0] self.sock = socket.socket(af, socktype, proto) - self.sock.connect(sa) + + # Try each address returned by getaddrinfo in turn until we + # manage to connect to one. + # Try all the addresses in turn until we connect() + last_error = 0 + for remote in res: + af, socktype, proto, canonname, sa = remote + self.sock = socket.socket(af, socktype, proto) + last_error = self.sock.connect_ex(sa) + print af + if last_error == 0: + break + else: + self.sock.close() + if last_error != 0: + # FIXME + raise socket.error(last_error) self.file = self.sock.makefile('rb') def read(self, size): @@ -1132,9 +1143,20 @@ class IMAP4_SSL(IMAP4): #http://www.python.org/doc/lib/socket-example.html res = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM) - af, socktype, proto, canonname, sa = res[0] - self.sock = socket.socket(af, socktype, proto) - self.sock.connect(sa) + # Try all the addresses in turn until we connect() + last_error = 0 + for remote in res: + af, socktype, proto, canonname, sa = remote + self.sock = socket.socket(af, socktype, proto) + last_error = self.sock.connect_ex(sa) + print af + if last_error == 0: + break + else: + self.sock.close() + if last_error != 0: + # FIXME + raise socket.error(last_error) if sys.version_info[0] <= 2 and sys.version_info[1] <= 2: self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile) else: