Ticket #1969: diverse-connection.patch

File diverse-connection.patch, 12.8 kB (added by sgala, 10 months ago)

Current version of ipv6 patch

  • src/common/xmpp/transports_nb.py

    old new  
    253253                self.printed_error = False 
    254254                 
    255255                #  0 - not connected 
    256                 #  1 - connected 
     256                #  1 - connecting 
     257                #  2 - connected 
    257258                # -1 - about to disconnect (when we wait for final events to complete) 
    258259                # -2 - disconnected 
    259260                self.state = 0 
     
    274275                # This prevents replug of same object with the same flags 
    275276                self.writable = True 
    276277                self.readable = False 
     278                self.ais = None 
    277279         
    278280        def plugin(self, owner): 
    279281                ''' Fire up connection. Return non-empty string on success. 
     
    307309                self.state = 0 
    308310                success = False 
    309311                try: 
    310                         for ai in socket.getaddrinfo(server[0],server[1],socket.AF_UNSPEC,socket.SOCK_STREAM): 
    311                                 try: 
    312                                         self._sock=socket.socket(*ai[:3]) 
    313                                         self._sock.setblocking(False) 
    314                                         self._server=ai[4] 
    315                                         success = True 
    316                                         break 
    317                                 except: 
    318                                         if sys.exc_value[0] == errno.EINPROGRESS: 
    319                                                 success = True 
    320                                                 break 
    321                                         #for all errors, we try other addresses 
    322                                         continue 
     312                        self.set_timeout(CONNECT_TIMEOUT_SECONDS) 
     313                        if len(server) == 2 and type(server[0]) in (str, unicode) and not self.ais: 
     314                                # FIXME: blocks here 
     315                                print "blocking getaddrinfo", server 
     316                                self.ais = socket.getaddrinfo(server[0],server[1],socket.AF_UNSPEC,socket.SOCK_STREAM) 
     317                                #print "self.ais=", self.ais  
     318                        else: 
     319                                self.ais = (server,) 
     320                        return self._do_connect() 
    323321                except socket.gaierror, e: 
    324322                        log.info("Lookup failure for %s: %s[%s]", self.getName(), e[1], repr(e[0]), exc_info=True) 
    325323                except: 
     
    328326                if not success: 
    329327                        if self.on_connect_failure: 
    330328                                self.on_connect_failure() 
    331                         return False 
    332  
    333                 self.fd = self._sock.fileno() 
    334                 self.idlequeue.plug_idle(self, True, False) 
    335                 self.set_timeout(CONNECT_TIMEOUT_SECONDS) 
    336                 self._do_connect() 
    337                 return True 
     329                return success 
    338330         
    339331        def _plug_idle(self): 
    340332                readable = self.state != 0 
     
    540532 
    541533        def _do_connect(self): 
    542534                if self.state != 0: 
     535                        print "do_connect while connected" 
    543536                        return 
    544                 self._sock.setblocking(False) 
    545                 self._send = self._sock.send 
    546                 self._recv = self._sock.recv 
    547                 errnum = 0 
    548                 try: 
    549                         self._sock.connect(self._server) 
    550                 except socket.error, e: 
    551                         errnum = e[0] 
     537                for ai in self.ais: 
     538                        success = False 
     539                        try: 
     540                                self._sock=socket.socket(*ai[:3]) 
     541                                self._sock.setblocking(False) 
     542                                self._server=ai[4] 
     543                                self.fd = self._sock.fileno() 
     544                                self.idlequeue.plug_idle(self, True, False) 
     545                                self._send = self._sock.send 
     546                                self._recv = self._sock.recv 
     547                                errnum = 0 
     548 
     549                                self._sock.connect(self._server) 
     550                                print "connected" 
     551                                success = True 
     552                                self.state = 1 
     553                                break 
     554                        except socket.error, e: 
     555                                #print "exception1",sys.exc_value 
     556                                errnum = e[0] 
    552557 
    553                         # Ignore "Socket already connected".  
    554                         # FIXME: This happens when we switch an already 
    555                         # connected socket to SSL (STARTTLS). Instead of 
    556                         # ignoring the error, the socket should only be 
    557                         # connected to once. See #2846 and #3396. 
    558                         workaround = (errno.EALREADY, 10056, 56) 
    559  
    560                         # 10035 - winsock equivalent of EINPROGRESS 
    561                         if errnum not in (errno.EINPROGRESS, 10035) + workaround: 
    562                                 log.error("_do_connect:", exc_info=True) 
    563                                 #traceback.print_exc() 
    564                 # in progress, or would block 
    565                 if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK):  
    566                         return 
    567                 # 10056  - already connected, only on win32 
    568                 # code 'WS*' is not available on GNU, so we use its numeric value 
    569                 elif errnum not in (0, 10056, errno.EISCONN):  
    570                         self.remove_timeout() 
     558                                # Ignore "Socket already connected".  
     559                                # FIXME: This happens when we switch an already 
     560                                # connected socket to SSL (STARTTLS). Instead of 
     561                                # ignoring the error, the socket should only be 
     562                                # connected to once. See #2846 and #3396. 
     563                                workaround = (errno.EALREADY, 10056, 56) 
     564 
     565                                # 10035 - winsock equivalent of EINPROGRESS 
     566                                if errnum not in (errno.EINPROGRESS, 10035) + workaround: 
     567                                        log.error("_do_connect:", exc_info=True) 
     568                                        #traceback.print_exc() 
     569                                # in progress, or would block 
     570                                if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): 
     571                                        success = True 
     572                                        self.state = 1 
     573                                        break 
     574                                # 10056  - already connected, only on win32 
     575                                # code 'WS*' is not available on GNU, so we use its numeric value 
     576                                elif errnum not in (0, 10056, errno.EISCONN):  
     577                                        continue 
     578 
     579                self.remove_timeout() 
     580                if not success: 
    571581                        if self.on_connect_failure: 
    572582                                self.on_connect_failure() 
    573583                        return 
    574                 self.remove_timeout() 
    575584                self._owner.Connection=self 
    576585                self.state = 1 
    577                  
    578586                self._sock.setblocking(False) 
    579587                self._plug_idle() 
    580588                if self.on_connect: 
    581589                        self.on_connect() 
    582                         self.on_connect = None 
     590                self.on_connect = None 
    583591                return True 
    584592 
    585593        def send(self, raw_data, now = False): 
     
    741749                log.debug("_startSSL_pyOpenSSL called") 
    742750                tcpsock = self._owner.Connection 
    743751                # FIXME: should method be configurable? 
    744                 tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) 
    745                 #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) 
     752                #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) 
     753                tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) 
    746754                tcpsock.ssl_errnum = 0 
    747755                tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback) 
    748756                cacerts = os.path.join(gajim.DATA_DIR, 'other', 'cacerts.pem') 
  • src/common/connection.py

    old new  
    481481                # SRV resolver 
    482482                self._proxy = proxy 
    483483                self._secure = secur 
    484                 self._hosts = [ {'host': h, 'port': p, 'prio': 10, 'weight': 10} ] 
    485484                self._hostname = hostname 
     485                def _on_resolve(host, result_array): 
     486                        # SRV query returned at least one valid result, we put it in hosts dict 
     487                        if len(result_array) != 0: 
     488                                self._hosts = [i for i in result_array] 
     489                        else: 
     490                                # TODO: Make async query for non SVR servers and protect exceptions in getaddrinfo 
     491                                self._hosts = [ {'host': h, 'port': p, 'prio': 10, 'weight': 10, 'ai': ai} for ai in socket.getaddrinfo(h,p,socket.AF_UNSPEC,socket.SOCK_STREAM) ] 
     492                        self.connect_to_next_host() 
    486493                if use_srv: 
    487494                        # add request for srv query to the resolve, on result '_on_resolve' 
    488495                        # will be called 
    489496                        gajim.resolver.resolve('_xmpp-client._tcp.' + helpers.idn_to_ascii(h), 
    490                                 self._on_resolve) 
     497                                _on_resolve) 
    491498                else: 
    492                         self._on_resolve('', []) 
     499                        gajim.resolver.resolve(helpers.idn_to_ascii(h), _on_resolve) 
    493500 
    494         def _on_resolve(self, host, result_array): 
    495                 # SRV query returned at least one valid result, we put it in hosts dict 
    496                 if len(result_array) != 0: 
    497                         self._hosts = [i for i in result_array] 
    498                 self.connect_to_next_host() 
    499501 
    500502        def on_proxy_failure(self, reason): 
    501503                log.debug('Connection to proxy failed') 
     
    512514                                self.last_connection.socket.disconnect() 
    513515                                self.last_connection = None 
    514516                                self.connection = None 
     517                        debug = [] 
    515518                        if gajim.verbose: 
    516519                                con = common.xmpp.NonBlockingClient(self._hostname, caller = self, 
    517520                                        on_connect = self.on_connect_success, 
     
    535538                                con.RegisterDisconnectHandler(self._on_new_account) 
    536539 
    537540                        log.info("Connecting to %s: [%s:%d]", self.name, host['host'], host['port']) 
    538                         con.connect((host['host'], host['port']), proxy = self._proxy, 
    539                                 secure = self._secure) 
     541                        if 'ai' in host: 
     542                                con.connect(host['ai'], proxy = self._proxy, 
     543                                        secure = self._secure) 
     544                        else: 
     545                                con.connect((host['host'],host['port']), proxy = self._proxy, 
     546                                        secure = self._secure) 
    540547                else: 
    541548                        if not retry and self.retrycount == 0: 
    542549                                log.debug("Out of hosts, giving up connecting to %s", self.name) 
  • src/common/nslookup.py

    old new  
    2020import sys 
    2121import os 
    2222import re 
     23import socket 
    2324 
    2425from xmpp.idlequeue import * 
    2526 
     
    9697                                                prop_value = prop_value[:-1] 
    9798                                        current_host['host'] = prop_value 
    9899                                if len(current_host) == 4: 
    99                                         hosts.append(current_host) 
    100                                         current_host = None 
     100                                        try: 
     101                                                for ai in socket.getaddrinfo(current_host['host'],current_host['port'],socket.AF_UNSPEC,socket.SOCK_STREAM): 
     102                                                        hosts.append(current_host) 
     103                                                        hosts[-1]['ai'] = ai 
     104                                        finally: 
     105                                                current_host = None 
    101106                return hosts 
    102107         
    103108        def _parse_srv_result_posix(self, fqdn, result): 
     
    129134                                        port = int(port) 
    130135                                except ValueError: 
    131136                                        continue 
    132                                 hosts.append({'host': host, 'port': port,'weight': weight, 
    133                                                 'prio': prio}) 
     137                                try: 
     138                                        for ai in socket.getaddrinfo(host,port,socket.AF_UNSPEC,socket.SOCK_STREAM): 
     139                                                hosts.append({'host': host, 'port': port,'weight': weight, 
     140                                                                        'prio': prio, 'ai':ai}) 
     141                                except: 
     142                                        print "GAI failed", host, port 
    134143                return hosts 
    135144         
    136145        def _on_ready(self, host, result): 
     
    290299                if self.result_handler: 
    291300                        self.result_handler(self.host, self.result) 
    292301                self.result_handler = None 
     302 
     303class GetAddrInfo(IdleCommand): 
     304        def __init__(self, on_result, host='_xmpp-client', port=5222, type = 'srv'): 
     305                IdleCommand.__init__(self, on_result) 
     306                self.commandtimeout = 10  
     307                self.host = host.lower() 
     308                self.port = port 
     309                self.type = type.lower() 
     310                if not host_pattern.match(self.host): 
     311                        # invalid host name 
     312                        print >> sys.stderr, 'Invalid host: %s' % self.host 
     313                        self.canexecute = False 
     314                        return 
     315                if not ns_type_pattern.match(self.type): 
     316                        print >> sys.stderr, 'Invalid querytype: %s' % self.type 
     317                        self.canexecute = False 
     318                        return 
     319        def start(self): 
     320                if not self.canexecute: 
     321                        self.result = None 
     322                        self._return_result() 
     323                        return 
     324                def do_work(): 
     325                        print "in do_work" 
     326                        try: 
     327                          self.result = socket.getaddrinfo(self.host, 
     328                                                self.port, 
     329                                                socket.AF_UNSPEC, 
     330                                                socket.SOCK_STREAM) 
     331                        except: 
     332                                print "Error", sys.exc_info() 
     333                        finally: 
     334                                print "Ended" 
     335                        print "after do_work" 
     336                        self.end() 
     337                print "before start_new_thread" 
     338                import threading 
     339                t = threading.Thread(target=do_work) 
     340                print t 
     341                t.start() 
     342                print "the thread is running" 
     343        def pollin(self): 
     344                if self.result != None: 
     345                        self.end() 
     346        def end(self): 
     347                #self.idlequeue.unplug_idle(self.fd) 
     348                print "in self.end", self.result 
     349 
     350 
     351class AIResolver: 
     352        def __init__(self, idlequeue): 
     353                self.idlequeue = idlequeue 
     354                # dict {host : list of addrinfo records} 
     355                self.resolved_hosts = {}  
     356                # dict {host : list of callbacks} 
     357                self.handlers = {} 
     358        def start_resolve(self, host,port): 
     359                ''' spawn new nslookup process and start waiting for results ''' 
     360                ns = GetAddrInfo(self._on_ready, host,port) 
     361                ns.set_idlequeue(self.idlequeue) 
     362                ns.commandtimeout = 10 
     363                ns.start() 
     364        def resolve(self, host, port, on_ready): 
     365                if host == '': # None means localhost 
     366                        # empty host, return empty list of srv records 
     367                        on_ready([]) 
     368                        return 
     369                if host in self.resolved_hosts: 
     370                        # host is already resolved, return cached values 
     371                        on_ready(host, self.resolved_hosts[host]) 
     372                        return 
     373                if host in self.handlers: 
     374                        # host is about to be resolved by another connection, 
     375                        # attach our callback (FIXME: synced on self.handlers) 
     376                        self.handlers[host].append(on_ready) 
     377                else: 
     378                        # host has never been resolved, start now 
     379                        self.handlers[host] = [on_ready] 
     380                        self.start_resolve(host,port) 
     381        def _on_ready(self, host, result): 
     382                print "AIResolver:", host, result 
     383                # practically it is impossible to be the opposite, but who knows :) 
     384                if not host in self.resolved_hosts: 
     385                        self.resolved_hosts[host] = result_list 
     386                if host in self.handlers: 
     387                        for callback in self.handlers[host]: 
     388                                callback(host, result_list) 
     389                        del self.handlers[host] 
     390         
     391 
     392 
    293393         
    294394# below lines is on how to use API and assist in testing 
    295395if __name__ == '__main__': 
     
    301401        import gobject 
    302402        import gtk 
    303403         
    304         resolver = Resolver(idlequeue) 
     404        #resolver = Resolver(idlequeue) 
     405        resolver = AIResolver(idlequeue) 
    305406         
    306407        def clicked(widget): 
    307408                global resolver 
    308409                host = text_view.get_text() 
     410                port = 5222 
    309411                def on_result(host, result_array): 
    310412                        print 'Result:\n' + repr(result_array) 
    311                 resolver.resolve(host, on_result) 
     413                resolver.resolve(host, port, on_result) 
    312414        win = gtk.Window() 
    313415        win.set_border_width(6) 
    314416        text_view = gtk.Entry() 
     
    321423        but.connect('clicked', clicked) 
    322424        win.add(hbox) 
    323425        win.show_all() 
    324         gobject.timeout_add(200, idlequeue.process) 
     426        win.connect('destroy', lambda w: gtk.main_quit()) 
     427        gobject.timeout_add(500, idlequeue.process) 
    325428        gtk.main()