]> code.delx.au - offlineimap/commitdiff
Check all resolved addresses [deb #413030]
authorJohn Goerzen <jgoerzen@complete.org>
Thu, 8 Mar 2007 01:59:32 +0000 (02:59 +0100)
committerJohn Goerzen <jgoerzen@complete.org>
Thu, 8 Mar 2007 01:59:32 +0000 (02:59 +0100)
From: Mark Brown <broonie@sirena.org.uk>
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

index 2bbe92e8843db06abf238564a559047cd7dd4270..a4bdc22d6b81273afb25f036987e72e95758dcb8 100644 (file)
@@ -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 <apeeters@lashout.net> 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: