From 3aefee470029eebdc088033c0b3c202c54067789 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Wed, 16 Apr 2008 11:02:01 +0000 Subject: [PATCH] try all IPs of a given host in case first one fails. Fixes #2958 --- src/common/xmpp/transports_nb.py | 106 +++++++++++++++++-------------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/src/common/xmpp/transports_nb.py b/src/common/xmpp/transports_nb.py index 64e5323d2..83f199a4a 100644 --- a/src/common/xmpp/transports_nb.py +++ b/src/common/xmpp/transports_nb.py @@ -274,6 +274,7 @@ class NonBlockingTcp(PlugIn, IdleObject): # This prevents replug of same object with the same flags self.writable = True self.readable = False + self.ais = None def plugin(self, owner): ''' Fire up connection. Return non-empty string on success. @@ -296,46 +297,34 @@ class NonBlockingTcp(PlugIn, IdleObject): if self.on_timeout: self.on_timeout() self.renew_send_timeout() - + def connect(self,server=None, proxy = None, secure = None): - ''' Try to establish connection. Returns True/False on success/failure. ''' + ''' Try to establish connection. ''' if not server: server=self._server else: self._server = server self.printed_error = False self.state = 0 - success = False try: - for ai in socket.getaddrinfo(server[0],server[1],socket.AF_UNSPEC,socket.SOCK_STREAM): - try: - self._sock=socket.socket(*ai[:3]) - self._sock.setblocking(False) - self._server=ai[4] - success = True - break - except: - if sys.exc_value[0] == errno.EINPROGRESS: - success = True - break - #for all errors, we try other addresses - continue + self.set_timeout(CONNECT_TIMEOUT_SECONDS) + if len(server) == 2 and type(server[0]) in (str, unicode) and not \ + self.ais: + # FIXME: blocks here + self.ais = socket.getaddrinfo(server[0],server[1],socket.AF_UNSPEC,socket.SOCK_STREAM) + log.info('Found IPs: %s', self.ais) + else: + self.ais = (server,) + self.connect_to_next_ip() + return except socket.gaierror, e: - log.info("Lookup failure for %s: %s[%s]", self.getName(), e[1], repr(e[0]), exc_info=True) + log.info('Lookup failure for %s: %s[%s]', self.getName(), e[1], repr(e[0]), exc_info=True) except: - log.error("Exception trying to connect to %s:", self.getName(), exc_info=True) + log.error('Exception trying to connect to %s:', self.getName(), exc_info=True) - if not success: - if self.on_connect_failure: - self.on_connect_failure() - return False + if self.on_connect_failure: + self.on_connect_failure() - self.fd = self._sock.fileno() - self.idlequeue.plug_idle(self, True, False) - self.set_timeout(CONNECT_TIMEOUT_SECONDS) - self._do_connect() - return True - def _plug_idle(self): readable = self.state != 0 if self.sendqueue or self.sendbuff: @@ -347,8 +336,9 @@ class NonBlockingTcp(PlugIn, IdleObject): def pollout(self): if self.state == 0: - return self._do_connect() - return self._do_send() + self.connect_to_next_ip() + return + self._do_send() def plugout(self): ''' Disconnect from the remote server and unregister self.disconnected method from @@ -538,19 +528,22 @@ class NonBlockingTcp(PlugIn, IdleObject): return return True - def _do_connect(self): + def connect_to_next_ip(self): if self.state != 0: return - self._sock.setblocking(False) - self._send = self._sock.send - self._recv = self._sock.recv - errnum = 0 + if len(self.ais) == 0: + if self.on_connect_failure: + self.on_connect_failure() + return + ai = self.ais.pop(0) + log.info('Trying to connect to %s:%s', ai[4][0], ai[4][1]) try: - self._sock.connect(self._server) + self._sock = socket.socket(*ai[:3]) + self._server=ai[4] except socket.error, e: - errnum = e[0] - - # Ignore "Socket already connected". + errnum, errstr = e + + # Ignore "Socket already connected". # FIXME: This happens when we switch an already # connected socket to SSL (STARTTLS). Instead of # ignoring the error, the socket should only be @@ -559,28 +552,45 @@ class NonBlockingTcp(PlugIn, IdleObject): # 10035 - winsock equivalent of EINPROGRESS if errnum not in (errno.EINPROGRESS, 10035) + workaround: - log.error("_do_connect:", exc_info=True) + log.error('Could not connect to %s: %s [%s]', ai[4][0], errnum, + errstr, exc_info=True) #traceback.print_exc() + self.connect_to_next_ip() + return + self.fd = self._sock.fileno() + self.idlequeue.plug_idle(self, True, False) + self._send = self._sock.send + self._recv = self._sock.recv + self._do_connect() + + def _do_connect(self): + errnum = 0 + + try: + self._sock.connect(self._server) + self._sock.setblocking(False) + except Exception, ee: + (errnum, errstr) = ee # in progress, or would block - if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): + if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): + self.state = 1 return # 10056 - already connected, only on win32 # code 'WS*' is not available on GNU, so we use its numeric value - elif errnum not in (0, 10056, errno.EISCONN): - self.remove_timeout() - if self.on_connect_failure: - self.on_connect_failure() + elif errnum not in (0, 10056, errno.EISCONN): + log.error('Could not connect to %s: %s [%s]', self._server[0], errnum, + errstr) + self.connect_to_next_ip() return self.remove_timeout() self._owner.Connection=self self.state = 1 - + self._sock.setblocking(False) self._plug_idle() if self.on_connect: self.on_connect() - self.on_connect = None - return True + self.on_connect = None def send(self, raw_data, now = False): '''Append raw_data to the queue of messages to be send.