fix zeroconf to be ablt to use IPv6. Fixes #3683

This commit is contained in:
Yann Leboulanger 2008-03-21 16:55:39 +00:00
parent 5b55264c92
commit 92690da36b
2 changed files with 93 additions and 47 deletions

View File

@ -30,6 +30,9 @@ import socket
import errno import errno
import sys import sys
import logging
log = logging.getLogger('gajim.c.z.client_zeroconf')
from common.zeroconf import roster_zeroconf from common.zeroconf import roster_zeroconf
MAX_BUFF_LEN = 65536 MAX_BUFF_LEN = 65536
@ -109,7 +112,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, host, port, conn_holder, stanzaqueue = [], to = None, on_ok=None, on_not_ok=None):
self._owner = self self._owner = self
self.Namespace = 'jabber:client' self.Namespace = 'jabber:client'
self.defaultNamespace = self.Namespace self.defaultNamespace = self.Namespace
@ -120,6 +123,8 @@ class P2PClient(IdleObject):
self.stanzaqueue = stanzaqueue self.stanzaqueue = stanzaqueue
self.to = to self.to = to
self.Server = host self.Server = host
self.on_ok = on_ok
self.on_not_ok = on_not_ok
self.DBG = 'client' self.DBG = 'client'
self.Connection = None self.Connection = None
if gajim.verbose: if gajim.verbose:
@ -137,6 +142,11 @@ class P2PClient(IdleObject):
self.sock_type = TYPE_CLIENT self.sock_type = TYPE_CLIENT
self.fd = -1 self.fd = -1
conn = P2PConnection('', _sock, host, port, self._caller, self.on_connect, self) conn = P2PConnection('', _sock, host, port, self._caller, self.on_connect, self)
if not self.conn_holder:
# An error occured, disconnect() has been called
if on_not_ok:
on_not_ok('Connection to host could not be established.')
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, port, self.to)
@ -145,14 +155,14 @@ class P2PClient(IdleObject):
stanza, is_message = val stanza, is_message = val
if is_message: if is_message:
if self.fd == -1: if self.fd == -1:
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \ if on_not_ok:
_('Connection to host could not be established'), None, None]) on_not_ok('Connection to host could not be established.')
return
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
self.conn_holder.number_of_awaiting_messages[self.fd]+=1
else: else:
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd): self.conn_holder.number_of_awaiting_messages[self.fd]=1
self.conn_holder.number_of_awaiting_messages[self.fd]+=1
else:
self.conn_holder.number_of_awaiting_messages[self.fd]=1
def add_stanza(self, stanza, is_message = False): def add_stanza(self, stanza, is_message = False):
if self.Connection: if self.Connection:
if self.Connection.state == -1: if self.Connection.state == -1:
@ -177,6 +187,8 @@ class P2PClient(IdleObject):
self.Connection.PlugIn(self) self.Connection.PlugIn(self)
dispatcher_nb.Dispatcher().PlugIn(self) dispatcher_nb.Dispatcher().PlugIn(self)
self._register_handlers() self._register_handlers()
if self.on_ok:
self.on_ok()
def StreamInit(self): def StreamInit(self):
''' Send an initial stream header. ''' ''' Send an initial stream header. '''
@ -202,10 +214,10 @@ class P2PClient(IdleObject):
def _check_stream_start(self, ns, tag, attrs): def _check_stream_start(self, ns, tag, attrs):
if ns<>NS_STREAMS or tag<>'stream': if ns<>NS_STREAMS or tag<>'stream':
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
_('Connection to host could not be established: Incorrect answer from server.'), None, None])
self.Connection.DEBUG('Incorrect stream start: (%s,%s).Terminating! ' % (tag, ns), 'error') self.Connection.DEBUG('Incorrect stream start: (%s,%s).Terminating! ' % (tag, ns), 'error')
self.Connection.disconnect() self.Connection.disconnect()
if self.on_not_ok:
self.on_not_ok('Connection to host could not be established: Incorrect answer from server.')
return return
if self.sock_type == TYPE_SERVER: if self.sock_type == TYPE_SERVER:
if attrs.has_key('from'): if attrs.has_key('from'):
@ -227,9 +239,6 @@ class P2PClient(IdleObject):
def on_disconnect(self): def on_disconnect(self):
if self.conn_holder: if self.conn_holder:
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd): if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
if self.conn_holder.number_of_awaiting_messages[self.fd] > 0:
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
_('Connection to host could not be established'), None, None])
del self.conn_holder.number_of_awaiting_messages[self.fd] del self.conn_holder.number_of_awaiting_messages[self.fd]
self.conn_holder.remove_connection(self.sock_hash) self.conn_holder.remove_connection(self.sock_hash)
if self.__dict__.has_key('Dispatcher'): if self.__dict__.has_key('Dispatcher'):
@ -302,13 +311,35 @@ class P2PConnection(IdleObject, PlugIn):
self.on_connect(self) self.on_connect(self)
else: else:
self.state = 0 self.state = 0
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try:
self.ais = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
except socket.gaierror, e:
log.info("Lookup failure for %s: %s[%s]", host, e[1], repr(e[0]), exc_info=True)
else:
self.connect_to_next_ip()
def connect_to_next_ip(self):
if len(self.ais) == 0:
log.error('Connection failure to %s', self.host, exc_info=True)
self.disconnect()
return
ai = self.ais.pop(0)
log.info('Trying to connect to %s through %s:%s', self.host, ai[4][0],
ai[4][1], exc_info=True)
try:
self._sock = socket.socket(*ai[:3])
self._sock.setblocking(False) self._sock.setblocking(False)
self.fd = self._sock.fileno() self._server=ai[4]
gajim.idlequeue.plug_idle(self, True, False) except:
self.set_timeout(CONNECT_TIMEOUT_SECONDS) if sys.exc_value[0] != errno.EINPROGRESS:
self.do_connect() #for all errors, we try other addresses
self.connect_to_next_ip()
return
self.fd = self._sock.fileno()
gajim.idlequeue.plug_idle(self, True, False)
self.set_timeout(CONNECT_TIMEOUT_SECONDS)
self.do_connect()
def set_timeout(self, timeout): def set_timeout(self, timeout):
gajim.idlequeue.remove_timeout(self.fd) gajim.idlequeue.remove_timeout(self.fd)
if self.state >= 0: if self.state >= 0:
@ -370,7 +401,7 @@ class P2PConnection(IdleObject, PlugIn):
def do_connect(self): def do_connect(self):
errnum = 0 errnum = 0
try: try:
self._sock.connect((self.host, self.port)) self._sock.connect(self._server)
self._sock.setblocking(False) self._sock.setblocking(False)
except Exception, ee: except Exception, ee:
(errnum, errstr) = ee (errnum, errstr) = ee
@ -378,13 +409,15 @@ class P2PConnection(IdleObject, PlugIn):
return return
# win32 needs this # win32 needs this
elif errnum not in (0, 10056, errno.EISCONN) or self.state != 0: elif errnum not in (0, 10056, errno.EISCONN) or self.state != 0:
self.disconnect() log.error('Could not connect to %s: %s [%s]', self.host, errnum,
return None errstr)
self.connect_to_next_ip()
return
else: # socket is already connected else: # socket is already connected
self._sock.setblocking(False) self._sock.setblocking(False)
self.state = 1 # connected self.state = 1 # connected
# we are connected
self.on_connect(self) self.on_connect(self)
return 1 # we are connected
def pollout(self): def pollout(self):
@ -642,28 +675,33 @@ class ClientZeroconf:
return self.roster.getRoster() return self.roster.getRoster()
return {} return {}
def send(self, stanza, is_message = False, now = False): def send(self, stanza, is_message = False, now = False, on_ok=None,
on_not_ok=None):
stanza.setFrom(self.roster.zeroconf.name) stanza.setFrom(self.roster.zeroconf.name)
to = stanza.getTo() to = stanza.getTo()
try: try:
item = self.roster[to] item = self.roster[to]
except KeyError: except KeyError:
self.caller.dispatch('MSGERROR', [unicode(to), '-1', _('Contact is offline. Your message could not be sent.'), None, None]) # Contact offline
return False return -1
# look for hashed connections # look for hashed connections
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]]
if conn.add_stanza(stanza, is_message): if conn.add_stanza(stanza, is_message):
return if on_ok:
on_ok()
return 0
if item['address'] in self.ip_to_hash: if item['address'] in self.ip_to_hash:
hash = self.ip_to_hash[item['address']] hash = self.ip_to_hash[item['address']]
if self.hash_to_port[hash] == item['port']: if self.hash_to_port[hash] == item['port']:
conn = self.connections[hash] conn = self.connections[hash]
if conn.add_stanza(stanza, is_message): if conn.add_stanza(stanza, is_message):
return if on_ok:
on_ok()
return 0
# otherwise open new connection # otherwise open new connection
P2PClient(None, item['address'], item['port'], self, [(stanza, is_message)], to) P2PClient(None, item['address'], item['port'], self, [(stanza, is_message)], to, on_ok=on_ok, on_not_ok=on_not_ok)

View File

@ -435,24 +435,32 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
if session.enable_encryption: if session.enable_encryption:
msg_iq = session.encrypt_stanza(msg_iq) msg_iq = session.encrypt_stanza(msg_iq)
if not self.connection.send(msg_iq, msg != None): def on_send_ok():
return no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')
ji = gajim.get_jid_without_resource(jid)
if session.is_loggable() and self.name not in no_log_for and\
ji not in no_log_for:
log_msg = msg
if subject:
log_msg = _('Subject: %s\n%s') % (subject, msg)
if log_msg:
if type == 'chat':
kind = 'chat_msg_sent'
else:
kind = 'single_msg_sent'
gajim.logger.write(kind, jid, log_msg)
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for') self.dispatch('MSGSENT', (jid, msg, keyID))
ji = gajim.get_jid_without_resource(jid)
if session.is_loggable() and self.name not in no_log_for and\
ji not in no_log_for:
log_msg = msg
if subject:
log_msg = _('Subject: %s\n%s') % (subject, msg)
if log_msg:
if type == 'chat':
kind = 'chat_msg_sent'
else:
kind = 'single_msg_sent'
gajim.logger.write(kind, jid, log_msg)
self.dispatch('MSGSENT', (jid, msg, keyID)) def on_send_not_ok(reason):
reason += ' ' + _('Your message could not be sent.')
self.dispatch('MSGERROR', [jid, '-1', reason, None, None])
ret = self.connection.send(msg_iq, msg != None, on_ok=on_send_ok,
on_not_ok=on_send_not_ok)
if ret == -1:
# Contact Offline
self.dispatch('MSGERROR', [jid, '-1', _('Contact is offline. Your message could not be sent.'), None, None])
def send_stanza(self, stanza): def send_stanza(self, stanza):
# send a stanza untouched # send a stanza untouched