handle proxy errors. fixes #799 (socks5 proxies should now be supported. tests needed)

This commit is contained in:
Yann Leboulanger 2007-02-07 22:05:52 +00:00
parent 56bcc2e1fa
commit cba44a43a1
3 changed files with 65 additions and 20 deletions

View File

@ -368,6 +368,15 @@ class Connection(ConnectionHandlers):
self._hosts = [i for i in result_array] self._hosts = [i for i in result_array]
self.connect_to_next_host() self.connect_to_next_host()
def on_proxy_failure(self, reason):
log.debug('Connection to proxy failed')
self.time_to_reconnect = None
self.on_connect_failure = None
self.disconnect(on_purpose = True)
self.dispatch('STATUS', 'offline')
self.dispatch('CONNECTION_LOST',
(_('Connection to proxy failed'), reason))
def connect_to_next_host(self, retry = False): def connect_to_next_host(self, retry = False):
if len(self._hosts): if len(self._hosts):
if self.last_connection: if self.last_connection:
@ -377,10 +386,12 @@ class Connection(ConnectionHandlers):
if gajim.verbose: if gajim.verbose:
con = common.xmpp.NonBlockingClient(self._hostname, caller = self, con = common.xmpp.NonBlockingClient(self._hostname, caller = self,
on_connect = self.on_connect_success, on_connect = self.on_connect_success,
on_proxy_failure = self.on_proxy_failure,
on_connect_failure = self.connect_to_next_host) on_connect_failure = self.connect_to_next_host)
else: else:
con = common.xmpp.NonBlockingClient(self._hostname, debug = [], caller = self, con = common.xmpp.NonBlockingClient(self._hostname, debug = [], caller = self,
on_connect = self.on_connect_success, on_connect = self.on_connect_success,
on_proxy_failure = self.on_proxy_failure,
on_connect_failure = self.connect_to_next_host) on_connect_failure = self.connect_to_next_host)
self.last_connection = con self.last_connection = con
# increase default timeout for server responses # increase default timeout for server responses
@ -397,7 +408,6 @@ class Connection(ConnectionHandlers):
log.info("Connecting to %s: [%s:%d]", self.name, host['host'], host['port']) log.info("Connecting to %s: [%s:%d]", self.name, host['host'], host['port'])
con.connect((host['host'], host['port']), proxy = self._proxy, con.connect((host['host'], host['port']), proxy = self._proxy,
secure = self._secure) secure = self._secure)
return
else: else:
if not retry and self.retrycount == 0: if not retry and self.retrycount == 0:
log.debug("Out of hosts, giving up connecting to %s", self.name) log.debug("Out of hosts, giving up connecting to %s", self.name)

View File

@ -32,7 +32,7 @@ from client import *
class NBCommonClient(CommonClient): class NBCommonClient(CommonClient):
''' Base for Client and Component classes.''' ''' Base for Client and Component classes.'''
def __init__(self, server, port=5222, debug=['always', 'nodebuilder'], caller=None, def __init__(self, server, port=5222, debug=['always', 'nodebuilder'], caller=None,
on_connect=None, on_connect_failure=None): on_connect=None, on_proxy_failure=None, on_connect_failure=None):
''' Caches server name and (optionally) port to connect to. "debug" parameter specifies ''' Caches server name and (optionally) port to connect to. "debug" parameter specifies
the debug IDs that will go into debug output. You can either specifiy an "include" the debug IDs that will go into debug output. You can either specifiy an "include"
or "exclude" list. The latter is done via adding "always" pseudo-ID to the list. or "exclude" list. The latter is done via adding "always" pseudo-ID to the list.
@ -65,6 +65,7 @@ class NBCommonClient(CommonClient):
self.idlequeue = None self.idlequeue = None
self.socket = None self.socket = None
self.on_connect = on_connect self.on_connect = on_connect
self.on_proxy_failure = on_proxy_failure
self.on_connect_failure = on_connect_failure self.on_connect_failure = on_connect_failure
def set_idlequeue(self, idlequeue): def set_idlequeue(self, idlequeue):
@ -108,14 +109,17 @@ class NBCommonClient(CommonClient):
if proxy.has_key('type'): if proxy.has_key('type'):
type_ = proxy['type'] type_ = proxy['type']
if type_ == 'socks5': if type_ == 'socks5':
self.socket = transports_nb.NBSOCKS5PROXYsocket(self._on_connected, self.socket = transports_nb.NBSOCKS5PROXYsocket(
self._on_connected, self._on_proxy_failure,
self._on_connected_failure, proxy, server) self._on_connected_failure, proxy, server)
elif type_ == 'http': elif type_ == 'http':
self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected, self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected,
self._on_connected_failure, proxy, server) self._on_proxy_failure, self._on_connected_failure, proxy,
server)
else: else:
self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected, self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected,
self._on_connected_failure, proxy, server) self._on_proxy_failure, self._on_connected_failure, proxy,
server)
else: else:
self.connected = 'tcp' self.connected = 'tcp'
self.socket = transports_nb.NonBlockingTcp(self._on_connected, self.socket = transports_nb.NonBlockingTcp(self._on_connected,
@ -127,6 +131,10 @@ class NBCommonClient(CommonClient):
self.on_stream_start = on_stream_start self.on_stream_start = on_stream_start
self.onreceive(self._on_receive_document_attrs) self.onreceive(self._on_receive_document_attrs)
def _on_proxy_failure(self, reason):
if self.on_proxy_failure:
self.on_proxy_failure(reason)
def _on_connected_failure(self, retry = None): def _on_connected_failure(self, retry = None):
if self.socket: if self.socket:
self.socket.disconnect() self.socket.disconnect()

View File

@ -374,7 +374,10 @@ class NonBlockingTcp(PlugIn, IdleObject):
self.state = -2 self.state = -2
self.sendqueue = None self.sendqueue = None
self.remove_timeout() self.remove_timeout()
self._owner.disconnected() try:
self._owner.disconnected()
except:
pass
self.idlequeue.unplug_idle(self.fd) self.idlequeue.unplug_idle(self.fd)
sock = getattr(self, '_sock', None) sock = getattr(self, '_sock', None)
if sock: if sock:
@ -609,7 +612,10 @@ class NonBlockingTcp(PlugIn, IdleObject):
def getName(self): def getName(self):
''' Return the server's name, or 'getHost()' if not available.''' ''' Return the server's name, or 'getHost()' if not available.'''
retval = None retval = None
retval = gattr(self._owner, 'name') try:
retval = gattr(self._owner, 'name')
except:
pass
if retval: return retval if retval: return retval
return self.getHost() return self.getHost()
@ -834,12 +840,13 @@ class NBHTTPPROXYsocket(NonBlockingTcp):
(optionally) simple authentication (using login and password). (optionally) simple authentication (using login and password).
''' '''
def __init__(self, on_connect =None, on_connect_failure = None,proxy = None,server = None,use_srv=True): def __init__(self, on_connect =None, on_proxy_failure=None, on_connect_failure = None,proxy = None,server = None,use_srv=True):
''' Caches proxy and target addresses. ''' Caches proxy and target addresses.
'proxy' argument is a dictionary with mandatory keys 'host' and 'port' (proxy address) 'proxy' argument is a dictionary with mandatory keys 'host' and 'port' (proxy address)
and optional keys 'user' and 'password' to use for authentication. and optional keys 'user' and 'password' to use for authentication.
'server' argument is a tuple of host and port - just like TCPsocket uses. ''' 'server' argument is a tuple of host and port - just like TCPsocket uses. '''
self.on_connect_proxy = on_connect self.on_connect_proxy = on_connect
self.on_proxy_failure = on_proxy_failure
self.on_connect_failure = on_connect_failure self.on_connect_failure = on_connect_failure
NonBlockingTcp.__init__(self, self._on_tcp_connect, on_connect_failure, server, use_srv) NonBlockingTcp.__init__(self, self._on_tcp_connect, on_connect_failure, server, use_srv)
self.DBG_LINE=DBG_CONNECT_PROXY self.DBG_LINE=DBG_CONNECT_PROXY
@ -881,10 +888,12 @@ class NBHTTPPROXYsocket(NonBlockingTcp):
except: except:
log.error("_on_headers_sent:", exc_info=True) log.error("_on_headers_sent:", exc_info=True)
#traceback.print_exc() #traceback.print_exc()
raise error('Invalid proxy reply') self.on_proxy_failure('Invalid proxy reply')
return
if code <> '200': if code <> '200':
self.DEBUG('Invalid proxy reply: %s %s %s' % (proto, code, desc),'error') self.DEBUG('Invalid proxy reply: %s %s %s' % (proto, code, desc),'error')
self._owner.disconnected() self._owner.disconnected()
self.on_proxy_failure('Invalid proxy reply')
return return
if len(reply) != 2: if len(reply) != 2:
pass pass
@ -893,9 +902,11 @@ class NBHTTPPROXYsocket(NonBlockingTcp):
def _on_proxy_auth(self, reply): def _on_proxy_auth(self, reply):
if self.reply.find('\n\n') == -1: if self.reply.find('\n\n') == -1:
if reply is None: if reply is None:
return self.on_proxy_failure('Proxy authentification failed')
return
if reply.find('\n\n') == -1: if reply.find('\n\n') == -1:
self.reply += reply.replace('\r', '') self.reply += reply.replace('\r', '')
self.on_proxy_failure('Proxy authentification failed')
return return
self.DEBUG('Authentification successfull. Jabber server contacted.','ok') self.DEBUG('Authentification successfull. Jabber server contacted.','ok')
if self.on_connect_proxy: if self.on_connect_proxy:
@ -910,14 +921,15 @@ class NBSOCKS5PROXYsocket(NonBlockingTcp):
redefines only connect method. Allows to use SOCKS5 proxies with redefines only connect method. Allows to use SOCKS5 proxies with
(optionally) simple authentication (only USERNAME/PASSWORD auth). (optionally) simple authentication (only USERNAME/PASSWORD auth).
''' '''
def __init__(self, on_connect = None, on_connect_failure = None, def __init__(self, on_connect = None, on_proxy_failure = None,
proxy = None, server = None, use_srv = True): on_connect_failure = None, proxy = None, server = None, use_srv = True):
''' Caches proxy and target addresses. ''' Caches proxy and target addresses.
'proxy' argument is a dictionary with mandatory keys 'host' and 'port' 'proxy' argument is a dictionary with mandatory keys 'host' and 'port'
(proxy address) and optional keys 'user' and 'password' to use for (proxy address) and optional keys 'user' and 'password' to use for
authentication. 'server' argument is a tuple of host and port - authentication. 'server' argument is a tuple of host and port -
just like TCPsocket uses. ''' just like TCPsocket uses. '''
self.on_connect_proxy = on_connect self.on_connect_proxy = on_connect
self.on_proxy_failure = on_proxy_failure
self.on_connect_failure = on_connect_failure self.on_connect_failure = on_connect_failure
NonBlockingTcp.__init__(self, self._on_tcp_connect, on_connect_failure, NonBlockingTcp.__init__(self, self._on_tcp_connect, on_connect_failure,
server, use_srv) server, use_srv)
@ -952,23 +964,31 @@ class NBSOCKS5PROXYsocket(NonBlockingTcp):
if reply is None: if reply is None:
return return
if len(reply) != 2: if len(reply) != 2:
raise error('Invalid proxy reply') self.on_proxy_failure('Invalid proxy reply')
return
if reply[0] != '\x05': if reply[0] != '\x05':
self.DEBUG('Invalid proxy reply', 'error') self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected() self._owner.disconnected()
self.on_proxy_failure('Invalid proxy reply')
return return
if reply[1] == '\x00': if reply[1] == '\x00':
return self._on_proxy_auth('\x01\x00') return self._on_proxy_auth('\x01\x00')
elif reply[1] == '\x02': elif reply[1] == '\x02':
# TODO: Do authentification to_send = '\x01' + chr(len(self.proxy['user'])) + self.proxy['user'] +\
chr(len(self.proxy['password'])) + self.proxy['password']
self.onreceive(self._on_proxy_auth) self.onreceive(self._on_proxy_auth)
self.send(to_send)
else: else:
if reply[1] == '\xff': if reply[1] == '\xff':
self.DEBUG('Authentification to proxy impossible: no acceptable ' self.DEBUG('Authentification to proxy impossible: no acceptable '
'auth method', 'error') 'auth method', 'error')
else: self._owner.disconnected()
self.DEBUG('Invalid proxy reply', 'error') self.on_proxy_failure('Authentification to proxy impossible: no '
'acceptable authentification method')
return
self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected() self._owner.disconnected()
self.on_proxy_failure('Invalid proxy reply')
return return
def _on_proxy_auth(self, reply): def _on_proxy_auth(self, reply):
@ -977,14 +997,17 @@ class NBSOCKS5PROXYsocket(NonBlockingTcp):
if len(reply) != 2: if len(reply) != 2:
self.DEBUG('Invalid proxy reply', 'error') self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected() self._owner.disconnected()
raise error('Invalid proxy reply') self.on_proxy_failure('Invalid proxy reply')
return
if reply[0] != '\x01': if reply[0] != '\x01':
self.DEBUG('Invalid proxy reply', 'error') self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected() self._owner.disconnected()
raise error('Invalid proxy reply') self.on_proxy_failure('Invalid proxy reply')
return
if reply[1] != '\x00': if reply[1] != '\x00':
self.DEBUG('Authentification to proxy failed', 'error') self.DEBUG('Authentification to proxy failed', 'error')
self._owner.disconnected() self._owner.disconnected()
self.on_proxy_failure('Authentification to proxy failed')
return return
self.DEBUG('Authentification successfull. Jabber server contacted.','ok') self.DEBUG('Authentification successfull. Jabber server contacted.','ok')
# Request connection # Request connection
@ -1014,11 +1037,13 @@ class NBSOCKS5PROXYsocket(NonBlockingTcp):
if len(reply) < 10: if len(reply) < 10:
self.DEBUG('Invalid proxy reply', 'error') self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected() self._owner.disconnected()
raise error('Invalid proxy reply') self.on_proxy_failure('Invalid proxy reply')
return
if reply[0] != '\x05': if reply[0] != '\x05':
self.DEBUG('Invalid proxy reply', 'error') self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected() self._owner.disconnected()
raise error('Invalid proxy reply') self.on_proxy_failure('Invalid proxy reply')
return
if reply[1] != "\x00": if reply[1] != "\x00":
# Connection failed # Connection failed
self._owner.disconnected() self._owner.disconnected()
@ -1036,6 +1061,7 @@ class NBSOCKS5PROXYsocket(NonBlockingTcp):
else: else:
txt = 'Invalid proxy reply' txt = 'Invalid proxy reply'
self.DEBUG(txt, 'error') self.DEBUG(txt, 'error')
self.on_proxy_failure(txt)
return return
# Get the bound address/port # Get the bound address/port
elif reply[3] == "\x01": elif reply[3] == "\x01":
@ -1045,6 +1071,7 @@ class NBSOCKS5PROXYsocket(NonBlockingTcp):
else: else:
self.DEBUG('Invalid proxy reply', 'error') self.DEBUG('Invalid proxy reply', 'error')
self._owner.disconnected() self._owner.disconnected()
self.on_proxy_failure('Invalid proxy reply')
return return
if self.on_connect_proxy: if self.on_connect_proxy: