Check all resolved addresses [deb #413030]

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.
This commit is contained in:
John Goerzen 2007-03-08 02:59:32 +01:00
parent ca3a306ecc
commit 82d5d5e675

View File

@ -218,15 +218,26 @@ class IMAP4:
""" """
self.host = host self.host = host
self.port = port 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, res = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
socket.SOCK_STREAM) socket.SOCK_STREAM)
af, socktype, proto, canonname, sa = res[0]
self.sock = socket.socket(af, socktype, proto) 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') self.file = self.sock.makefile('rb')
def read(self, size): def read(self, size):
@ -1132,9 +1143,20 @@ class IMAP4_SSL(IMAP4):
#http://www.python.org/doc/lib/socket-example.html #http://www.python.org/doc/lib/socket-example.html
res = socket.getaddrinfo(host, port, socket.AF_UNSPEC, res = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
socket.SOCK_STREAM) socket.SOCK_STREAM)
af, socktype, proto, canonname, sa = res[0] # Try all the addresses in turn until we connect()
self.sock = socket.socket(af, socktype, proto) last_error = 0
self.sock.connect(sa) 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: if sys.version_info[0] <= 2 and sys.version_info[1] <= 2:
self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile) self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
else: else: