link-local: try to connect to each collected address in turn

This commit is contained in:
Fabian Pietsch 2015-04-06 16:33:10 +02:00
parent 0dcbe70635
commit 25240888d5
2 changed files with 60 additions and 31 deletions

View file

@ -99,10 +99,11 @@ class ZeroconfListener(IdleObject):
ipaddr = sock[1][0] ipaddr = sock[1][0]
for jid in self.conn_holder.getRoster().keys(): for jid in self.conn_holder.getRoster().keys():
entry = self.conn_holder.getRoster().getItem(jid) entry = self.conn_holder.getRoster().getItem(jid)
if (entry['address'] == ipaddr): for address in entry['addresses']:
from_jid = jid if (address['address'] == ipaddr):
break from_jid = jid
P2PClient(sock[0], ipaddr, sock[1][1], self.conn_holder, [], from_jid) break
P2PClient(sock[0], [{'host': ipaddr, 'address': ipaddr, 'port': sock[1][1]}], self.conn_holder, [], from_jid)
def disconnect(self, message=''): def disconnect(self, message=''):
""" """
@ -128,7 +129,7 @@ class ZeroconfListener(IdleObject):
return _sock return _sock
class P2PClient(IdleObject): class P2PClient(IdleObject):
def __init__(self, _sock, host, port, conn_holder, stanzaqueue, to=None, def __init__(self, _sock, addresses, conn_holder, stanzaqueue, to=None,
on_ok=None, on_not_ok=None): on_ok=None, on_not_ok=None):
self._owner = self self._owner = self
self.Namespace = 'jabber:client' self.Namespace = 'jabber:client'
@ -140,7 +141,7 @@ class P2PClient(IdleObject):
self.conn_holder = conn_holder self.conn_holder = conn_holder
self.stanzaqueue = stanzaqueue self.stanzaqueue = stanzaqueue
self.to = to self.to = to
self.Server = host #self.Server = addresses[0]['host']
self.on_ok = on_ok self.on_ok = on_ok
self.on_not_ok = on_not_ok self.on_not_ok = on_not_ok
self.Connection = None self.Connection = None
@ -150,8 +151,9 @@ class P2PClient(IdleObject):
else: else:
self.sock_type = TYPE_CLIENT self.sock_type = TYPE_CLIENT
self.fd = -1 self.fd = -1
conn = P2PConnection('', _sock, host, port, self._caller, conn = P2PConnection('', _sock, addresses, self._caller,
self.on_connect, self) self.on_connect, self)
self.Server = conn.host # set Server to the last host name / address tried
if not self.conn_holder: if not self.conn_holder:
# An error occured, disconnect() has been called # An error occured, disconnect() has been called
if on_not_ok: if on_not_ok:
@ -159,7 +161,7 @@ class P2PClient(IdleObject):
return return
self.sock_hash = conn._sock.__hash__ self.sock_hash = conn._sock.__hash__
self.fd = conn.fd self.fd = conn.fd
self.conn_holder.add_connection(self, self.Server, port, self.to) self.conn_holder.add_connection(self, self.Server, conn.port, self.to)
# count messages in queue # count messages in queue
for val in self.stanzaqueue: for val in self.stanzaqueue:
stanza, is_message = val stanza, is_message = val
@ -328,7 +330,7 @@ class P2PClient(IdleObject):
nbxmpp.NS_JINGLE) nbxmpp.NS_JINGLE)
class P2PConnection(IdleObject, PlugIn): class P2PConnection(IdleObject, PlugIn):
def __init__(self, sock_hash, _sock, host=None, port=None, caller=None, def __init__(self, sock_hash, _sock, addresses=None, caller=None,
on_connect=None, client=None): on_connect=None, client=None):
IdleObject.__init__(self) IdleObject.__init__(self)
self._owner = client self._owner = client
@ -338,7 +340,7 @@ class P2PConnection(IdleObject, PlugIn):
self.buff_is_message = False self.buff_is_message = False
self._sock = _sock self._sock = _sock
self.sock_hash = None self.sock_hash = None
self.host, self.port = host, port self.addresses = addresses
self.on_connect = on_connect self.on_connect = on_connect
self.client = client self.client = client
self.writable = False self.writable = False
@ -346,6 +348,8 @@ class P2PConnection(IdleObject, PlugIn):
self._exported_methods = [self.send, self.disconnect, self.onreceive] self._exported_methods = [self.send, self.disconnect, self.onreceive]
self.on_receive = None self.on_receive = None
if _sock: if _sock:
self.host = addresses[0]['host']
self.port = addresses[0]['port']
self._sock = _sock self._sock = _sock
self.state = 1 self.state = 1
self._sock.setblocking(False) self._sock.setblocking(False)
@ -353,17 +357,26 @@ class P2PConnection(IdleObject, PlugIn):
self.on_connect(self) self.on_connect(self)
else: else:
self.state = 0 self.state = 0
try: self.addresses_ = self.addresses
self.ais = socket.getaddrinfo(host, port, socket.AF_UNSPEC, self.get_next_addrinfo()
socket.SOCK_STREAM)
except socket.gaierror as e: def get_next_addrinfo(self):
log.info('Lookup failure for %s: %s', host, str(e), address = self.addresses_.pop(0)
exc_info=True) self.host = address['host']
else: self.port = address['port']
self.connect_to_next_ip() try:
self.ais = socket.getaddrinfo(address['host'], address['port'], socket.AF_UNSPEC,
socket.SOCK_STREAM)
except socket.gaierror as e:
log.info('Lookup failure for %s: %s[%s]', host, e[1],
repr(e[0]), exc_info=True)
if len(self.addresses_) > 0: return self.get_next_addrinfo()
else:
self.connect_to_next_ip()
def connect_to_next_ip(self): def connect_to_next_ip(self):
if len(self.ais) == 0: if len(self.ais) == 0:
if len(self.addresses_) > 0: return self.get_next_addrinfo()
log.error('Connection failure to %s', str(self.host), exc_info=True) log.error('Connection failure to %s', str(self.host), exc_info=True)
self.disconnect() self.disconnect()
return return
@ -748,9 +761,13 @@ class ClientZeroconf:
on_ok(id_) on_ok(id_)
return return
if item['address'] in self.ip_to_hash: the_address = None
hash_ = self.ip_to_hash[item['address']] for address in item['addresses']:
if self.hash_to_port[hash_] == item['port']: if address['address'] in self.ip_to_hash:
the_address = address
if the_address and the_address['address'] in self.ip_to_hash:
hash_ = self.ip_to_hash[the_address['address']]
if self.hash_to_port[hash_] == the_address['port']:
conn = self.connections[hash_] conn = self.connections[hash_]
id_ = stanza.getID() or '' id_ = stanza.getID() or ''
if conn.add_stanza(stanza, is_message): if conn.add_stanza(stanza, is_message):
@ -761,7 +778,10 @@ class ClientZeroconf:
# otherwise open new connection # otherwise open new connection
if not stanza.getID(): if not stanza.getID():
stanza.setID('zero') stanza.setID('zero')
P2PClient(None, item['address'], item['port'], self, addresses_ = []
for address in item['addresses']:
addresses_ += [{'host': address['address'], 'address': address['address'], 'port': address['port']}]
P2PClient(None, addresses_, self,
[(stanza, is_message)], to, on_ok=on_ok, on_not_ok=on_not_ok) [(stanza, is_message)], to, on_ok=on_ok, on_not_ok=on_not_ok)
def getAnID(self): def getAnID(self):
@ -807,10 +827,15 @@ class ClientZeroconf:
conn = None conn = None
if to in self.recipient_to_hash: if to in self.recipient_to_hash:
conn = self.connections[self.recipient_to_hash[to]] conn = self.connections[self.recipient_to_hash[to]]
elif item and item['address'] in self.ip_to_hash: elif item:
hash_ = self.ip_to_hash[item['address']] the_address = None
if self.hash_to_port[hash_] == item['port']: for address in item['addresses']:
conn = self.connections[hash_] if address['address'] in self.ip_to_hash:
the_address = address
if the_address and the_address['address'] in self.ip_to_hash:
hash_ = self.ip_to_hash[the_address['address']]
if self.hash_to_port[hash_] == the_address['port']:
conn = self.connections[hash_]
if func: if func:
conn.Dispatcher.on_responses[_waitid] = (func, args) conn.Dispatcher.on_responses[_waitid] = (func, args)
conn.onreceive(conn.Dispatcher._WaitForData) conn.onreceive(conn.Dispatcher._WaitForData)

View file

@ -56,8 +56,14 @@ class Roster:
if not contact: if not contact:
return return
resolved_info = contact[zeroconf.C_RESOLVED_INFO] addresses = []
host, aprotocol, address, port = resolved_info[0][zeroconf.C_RI_HOST:zeroconf.C_RI_PORT+1] i = 0
for ri in contact[zeroconf.C_RESOLVED_INFO]:
addresses += [{}]
addresses[i]['host'] = ri[zeroconf.C_RI_HOST]
addresses[i]['address'] = ri[zeroconf.C_RI_ADDRESS]
addresses[i]['port'] = ri[zeroconf.C_RI_PORT]
i += 1
txt = contact[zeroconf.C_TXT] txt = contact[zeroconf.C_TXT]
self._data[jid]={} self._data[jid]={}
@ -65,9 +71,7 @@ class Roster:
self._data[jid]['subscription'] = 'both' self._data[jid]['subscription'] = 'both'
self._data[jid]['groups'] = [] self._data[jid]['groups'] = []
self._data[jid]['resources'] = {} self._data[jid]['resources'] = {}
self._data[jid]['address'] = address self._data[jid]['addresses'] = addresses
self._data[jid]['host'] = host
self._data[jid]['port'] = port
txt_dict = self.zeroconf.txt_array_to_dict(txt) txt_dict = self.zeroconf.txt_array_to_dict(txt)
status = txt_dict.get('status', '') status = txt_dict.get('status', '')
if not status: if not status: