Changeset 9870

Show
Ignore:
Timestamp:
07/01/08 01:02:12 (2 months ago)
Author:
tomk
Message:

moved TLS and SSL classes from transports_nb to new tls_nb module, fixed HTTP CONNECT proxy transport

Location:
branches/bosh_support/src/common/xmpp
Files:
1 added
3 modified

Legend:

Unmodified
Added
Removed
  • branches/bosh_support/src/common/xmpp/client_nb.py

    r9867 r9870  
    2626import random 
    2727 
    28 import transports_nb, dispatcher_nb, auth_nb, roster_nb, protocol 
     28import transports_nb, tls_nb, dispatcher_nb, auth_nb, roster_nb, protocol 
    2929from client import * 
    3030 
     
    185185                                                bosh_port = tcp_port) 
    186186                        else: 
    187                                         self.socket = transports_nb.NBHTTPProxySocket( 
    188                                                 on_disconnect=self.on_disconnect, 
    189                                                 proxy_creds=(None, None), 
    190                                                 xmpp_server=(self.Server, self.Port)) 
     187                                # HTTP CONNECT to proxy from environment variables 
     188                                self.socket = transports_nb.NBHTTPProxySocket( 
     189                                        on_disconnect=self.on_disconnect, 
     190                                        proxy_creds=(None, None), 
     191                                        xmpp_server=(self.Server, self.Port)) 
    191192                else:  
    192193                        self._on_tcp_failure = self._on_connect_failure 
     
    433434                                return 
    434435                        # otherwise start TLS    
    435                         transports_nb.NonBlockingTLS().PlugIn( 
     436                        tls_nb.NonBlockingTLS().PlugIn( 
    436437                                self, 
    437438                                on_tls_success=lambda: self._xmpp_connect(socket_type='tls'), 
  • branches/bosh_support/src/common/xmpp/client.py

    r9776 r9870  
    7171                        owner.__dict__[method.__name__]=method 
    7272                owner.__dict__[self.__class__.__name__]=self 
    73                 if self.__class__.__dict__.has_key('plugin'): return self.plugin(owner) 
     73                # following will not work for classes inheriting plugin() 
     74                #if self.__class__.__dict__.has_key('plugin'): return self.plugin(owner) 
     75                if hasattr(self,'plugin'): return self.plugin(owner) 
    7476  
    7577        def PlugOut(self): 
  • branches/bosh_support/src/common/xmpp/transports_nb.py

    r9867 r9870  
    1515##   GNU General Public License for more details. 
    1616 
    17 import socket,select,base64,dispatcher_nb 
    18 import struct 
     17import socket,base64 
     18 
    1919from simplexml import ustr 
    2020from client import PlugIn 
     
    2828 
    2929import traceback 
    30 import threading 
    3130 
    3231import logging 
     
    5352        proto, host, path = grouped[1], grouped[3], grouped[4] 
    5453        return proto, host, path 
    55                  
    56  
    57  
    58  
    59 # I don't need to load gajim.py just because of few TLS variables, so I changed 
    60 # %s/common\.gajim\.DATA_DIR/\'\.\.\/data\'/c 
    61 # %s/common\.gajim\.MY_CACERTS/\'\%s\/\.gajim\/cacerts\.pem\' \% os\.environ\[\'HOME\'\]/c 
    62  
    63 # To change it back do: 
    64 # %s/\'\.\.\/data\'/common\.gajim\.DATA_DIR/c 
    65 # %s/\'%s\/\.gajim\/cacerts\.pem\'\ %\ os\.environ\[\'HOME\'\]/common\.gajim\.MY_CACERTS/c 
    66 # TODO: make the paths configurable - as constructor parameters or sth 
    67  
    68  
    69 # import common.gajim 
    7054 
    7155# timeout to connect to the server socket, it doesn't include auth  
     
    131115 
    132116        def plugin(self, owner): 
     117                print 'plugin called' 
    133118                owner.Connection=self 
    134                 print 'plugin called' 
    135119                self.idlequeue = owner.idlequeue 
    136120 
     
    373357                                self.on_receive = None 
    374358                        return 
     359                log.debug('setting onreceive on %s' % recv_handler) 
    375360                self.on_receive = recv_handler 
    376361 
     
    407392                        # FIXME: is this needed??  
    408393                        if errnum != 0: 
    409                                 self.DEBUG(self.DBG, errstr, 'error') 
    410394                                log.error("CConnection to %s lost: %s %s" % (self.server, errnum, errstr)) 
    411                                 if not errors_only and self.state in [CONNECTING, CONNECTED]: 
    412                                         self.pollend(retry=True) 
     395                                self.disconnect() 
    413396                                return 
    414397                        received = '' 
     
    416399                # we have received some bytes, stop the timeout! 
    417400                self.renew_send_timeout() 
     401                # pass received data to owner 
     402                #self. 
    418403                if self.on_receive: 
    419404                        self._raise_event(DATA_RECEIVED, received) 
    420405                        self._on_receive(received) 
    421406                else: 
    422                         # This should never happen, so we need the debug 
     407                        # This should never happen, so we need the debug. (If there is no handler 
     408                        # on receive spacified, data are passed to Dispatcher.ProcessNonBlocking) 
    423409                        log.error('SOCKET Unhandled data received: %s' % received) 
    424410                        self.disconnect() 
     
    426412        def _on_receive(self, data): 
    427413                # Overriding this method allows modifying received data before it is passed 
    428                 # to callback.  
     414                # to owner's callback.  
     415                log.debug('About to call on_receive which is %s' % self.on_receive) 
    429416                self.on_receive(data) 
    430417 
    431418 
    432 class NonBlockingHttpBOSH(NonBlockingTcp): 
    433         ''' 
    434         Socket wrapper that makes HTTP message out of send data and peels-off  
    435         HTTP headers from incoming messages 
    436         ''' 
    437  
    438         def __init__(self, bosh_uri, bosh_port, on_disconnect): 
    439                 self.bosh_protocol, self.bosh_host, self.bosh_path = self.urisplit(bosh_uri) 
    440                 if self.bosh_protocol is None: 
    441                         self.bosh_protocol = 'http' 
    442                 if self.bosh_path == '': 
    443                         bosh_path = '/' 
    444                 self.bosh_port = bosh_port 
    445                  
    446         def send(self, raw_data, now=False): 
    447  
    448                 NonBlockingTcp.send( 
    449                         self, 
    450                         self.build_http_message(raw_data), 
    451                         now) 
    452  
    453         def _on_receive(self,data): 
    454                 '''Preceeds pass of received data to Client class. Gets rid of HTTP headers 
    455                 and checks them.''' 
    456                 statusline, headers, httpbody = self.parse_http_message(data) 
    457                 if statusline[1] != '200': 
    458                         log.error('HTTP Error: %s %s' % (statusline[1], statusline[2])) 
    459                         self.disconnect() 
    460                 self.on_receive(httpbody) 
    461          
    462                  
    463         def build_http_message(self, httpbody): 
    464                 ''' 
    465                 Builds bosh http message with given body. 
    466                 Values for headers and status line fields are taken from class variables. 
    467                 )   
    468                 ''' 
    469                 headers = ['POST %s HTTP/1.1' % self.bosh_path, 
    470                         'Host: %s:%s' % (self.bosh_host, self.bosh_port), 
    471                         'Content-Type: text/xml; charset=utf-8', 
    472                         'Content-Length: %s' % len(str(httpbody)), 
    473                         '\r\n'] 
    474                 headers = '\r\n'.join(headers) 
    475                 return('%s%s\r\n' % (headers, httpbody)) 
    476  
    477         def parse_http_message(self, message): 
    478                 ''' 
    479                 splits http message to tuple ( 
    480                   statusline - list of e.g. ['HTTP/1.1', '200', 'OK'], 
    481                   headers - dictionary of headers e.g. {'Content-Length': '604', 
    482                             'Content-Type': 'text/xml; charset=utf-8'}, 
    483                   httpbody - string with http body 
    484                 )   
    485                 ''' 
    486                 message = message.replace('\r','') 
    487                 (header, httpbody) = message.split('\n\n') 
    488                 header = header.split('\n') 
    489                 statusline = header[0].split(' ') 
    490                 header = header[1:] 
    491                 headers = {} 
    492                 for dummy in header: 
    493                         row = dummy.split(' ',1) 
    494                         headers[row[0][:-1]] = row[1] 
    495                 return (statusline, headers, httpbody) 
    496  
    497  
    498 USE_PYOPENSSL = False 
    499  
    500 try: 
    501         #raise ImportError("Manually disabled PyOpenSSL") 
    502         import OpenSSL.SSL 
    503         import OpenSSL.crypto 
    504         USE_PYOPENSSL = True 
    505         log.info("PyOpenSSL loaded") 
    506 except ImportError: 
    507         log.debug("Import of PyOpenSSL failed:", exc_info=True) 
    508  
    509         # FIXME: Remove these prints before release, replace with a warning dialog. 
    510         print >> sys.stderr, "=" * 79 
    511         print >> sys.stderr, "PyOpenSSL not found, falling back to Python builtin SSL objects (insecure)." 
    512         print >> sys.stderr, "=" * 79 
    513  
    514  
    515 def torf(cond, tv, fv): 
    516         if cond: return tv 
    517         return fv 
    518  
    519 def gattr(obj, attr, default=None): 
    520         try: 
    521                 return getattr(obj, attr) 
    522         except: 
    523                 return default 
    524  
    525 class SSLWrapper: 
    526         class Error(IOError): 
    527                 def __init__(self, sock=None, exc=None, errno=None, strerror=None, peer=None): 
    528                         self.parent = IOError 
    529  
    530                         errno = errno or gattr(exc, 'errno') 
    531                         strerror = strerror or gattr(exc, 'strerror') or gattr(exc, 'args') 
    532                         if not isinstance(strerror, basestring): strerror = repr(strerror) 
    533  
    534                         self.sock = sock 
    535                         self.exc = exc 
    536                         self.peer = peer 
    537                         self.exc_name = None 
    538                         self.exc_args = None 
    539                         self.exc_str = None 
    540                         self.exc_repr = None 
    541  
    542                         if self.exc is not None: 
    543                                 self.exc_name = str(self.exc.__class__) 
    544                                 self.exc_args = gattr(self.exc, 'args') 
    545                                 self.exc_str = str(self.exc) 
    546                                 self.exc_repr = repr(self.exc) 
    547                                 if not errno: 
    548                                         try: 
    549                                                 if isinstance(exc, OpenSSL.SSL.SysCallError): 
    550                                                         if self.exc_args[0] > 0: 
    551                                                                 errno = self.exc_args[0] 
    552                                                         strerror = self.exc_args[1] 
    553                                         except: pass 
    554  
    555                         self.parent.__init__(self, errno, strerror) 
    556  
    557                         if self.peer is None and sock is not None: 
    558                                 try: 
    559                                         ppeer = self.sock.getpeername() 
    560                                         if len(ppeer) == 2 and isinstance(ppeer[0], basestring) \ 
    561                                         and isinstance(ppeer[1], int): 
    562                                                 self.peer = ppeer 
    563                                 except: pass 
    564  
    565                 def __str__(self): 
    566                         s = str(self.__class__) 
    567                         if self.peer: s += " for %s:%d" % self.peer 
    568                         if self.errno is not None: s += ": [Errno: %d]" % self.errno 
    569                         if self.strerror: s += " (%s)" % self.strerror 
    570                         if self.exc_name: 
    571                                 s += ", Caused by %s" % self.exc_name 
    572                                 if self.exc_str: 
    573                                         if self.strerror: s += "(%s)" % self.exc_str 
    574                                         else: s += "(%s)" % str(self.exc_args) 
    575                         return s 
    576  
    577         def __init__(self, sslobj, sock=None): 
    578                 self.sslobj = sslobj 
    579                 self.sock = sock 
    580                 log.debug("%s.__init__ called with %s", self.__class__, sslobj) 
    581  
    582         def recv(self, data, flags=None): 
    583                 """ Receive wrapper for SSL object 
    584  
    585                 We can return None out of this function to signal that no data is 
    586                 available right now. Better than an exception, which differs 
    587                 depending on which SSL lib we're using. Unfortunately returning '' 
    588                 can indicate that the socket has been closed, so to be sure, we avoid 
    589                 this by returning None. """ 
    590  
    591                 raise NotImplementedException() 
    592  
    593         def send(self, data, flags=None, now = False): 
    594                 raise NotImplementedException() 
    595  
    596 class PyOpenSSLWrapper(SSLWrapper): 
    597         '''Wrapper class for PyOpenSSL's recv() and send() methods''' 
    598  
    599         def __init__(self, *args): 
    600                 self.parent = SSLWrapper 
    601                 self.parent.__init__(self, *args) 
    602  
    603         def is_numtoolarge(self, e): 
    604                 t = ('asn1 encoding routines', 'a2d_ASN1_OBJECT', 'first num too large') 
    605                 return isinstance(e.args, (list, tuple)) and len(e.args) == 1 and \ 
    606                 isinstance(e.args[0], (list, tuple)) and len(e.args[0]) == 2 and \ 
    607                 e.args[0][0] == e.args[0][1] == t 
    608  
    609         def recv(self, bufsize, flags=None): 
    610                 retval = None 
    611                 try: 
    612                         if flags is None: retval = self.sslobj.recv(bufsize) 
    613                         else:             retval = self.sslobj.recv(bufsize, flags) 
    614                 except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: 
    615                         pass 
    616                         # log.debug("Recv: " + repr(e)) 
    617                 except OpenSSL.SSL.SysCallError, e: 
    618                         log.debug("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True) 
    619                         #traceback.print_exc() 
    620                         raise SSLWrapper.Error(self.sock or self.sslobj, e) 
    621                 except OpenSSL.SSL.Error, e: 
    622                         if self.is_numtoolarge(e): 
    623                                 # warn, but ignore this exception 
    624                                 log.warning("Recv: OpenSSL: asn1enc: first num too large (ignored)") 
    625                         else: 
    626                                 log.debug("Recv: Caught OpenSSL.SSL.Error:", exc_info=True) 
    627                                 #traceback.print_exc() 
    628                                 #print "Current Stack:" 
    629                                 #traceback.print_stack() 
    630                                 raise SSLWrapper.Error(self.sock or self.sslobj, e) 
    631                 return retval 
    632  
    633         def send(self, data, flags=None, now = False): 
    634                 try: 
    635                         if flags is None: return self.sslobj.send(data) 
    636                         else:             return self.sslobj.send(data, flags) 
    637                 except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: 
    638                         #log.debug("Send: " + repr(e)) 
    639                         time.sleep(0.1) # prevent 100% CPU usage 
    640                 except OpenSSL.SSL.SysCallError, e: 
    641                         log.error("Send: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True) 
    642                         #traceback.print_exc() 
    643                         raise SSLWrapper.Error(self.sock or self.sslobj, e) 
    644                 except OpenSSL.SSL.Error, e: 
    645                         if self.is_numtoolarge(e): 
    646                                 # warn, but ignore this exception 
    647                                 log.warning("Send: OpenSSL: asn1enc: first num too large (ignored)") 
    648                         else: 
    649                                 log.error("Send: Caught OpenSSL.SSL.Error:", exc_info=True) 
    650                                 #traceback.print_exc() 
    651                                 #print "Current Stack:" 
    652                                 #traceback.print_stack() 
    653                                 raise SSLWrapper.Error(self.sock or self.sslobj, e) 
    654                 return 0 
    655  
    656 class StdlibSSLWrapper(SSLWrapper): 
    657         '''Wrapper class for Python's socket.ssl read() and write() methods''' 
    658  
    659         def __init__(self, *args): 
    660                 self.parent = SSLWrapper 
    661                 self.parent.__init__(self, *args) 
    662  
    663         def recv(self, bufsize, flags=None): 
    664                 # we simply ignore flags since ssl object doesn't support it 
    665                 try: 
    666                         return self.sslobj.read(bufsize) 
    667                 except socket.sslerror, e: 
    668                         #log.debug("Recv: Caught socket.sslerror:", exc_info=True) 
    669                         #traceback.print_exc() 
    670                         if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): 
    671                                 raise SSLWrapper.Error(self.sock or self.sslobj, e) 
    672                 return None 
    673  
    674         def send(self, data, flags=None, now = False): 
    675                 # we simply ignore flags since ssl object doesn't support it 
    676                 try: 
    677                         return self.sslobj.write(data) 
    678                 except socket.sslerror, e: 
    679                         #log.debug("Send: Caught socket.sslerror:", exc_info=True) 
    680                         #traceback.print_exc() 
    681                         if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): 
    682                                 raise SSLWrapper.Error(self.sock or self.sslobj, e) 
    683                 return 0 
    684  
    685  
    686 class NonBlockingTLS(PlugIn): 
    687         ''' TLS connection used to encrypts already estabilished tcp connection.''' 
    688  
    689         # from ssl.h (partial extract) 
    690         ssl_h_bits = {  "SSL_ST_CONNECT": 0x1000, "SSL_ST_ACCEPT": 0x2000,  
    691                         "SSL_CB_LOOP": 0x01, "SSL_CB_EXIT": 0x02,  
    692                         "SSL_CB_READ": 0x04, "SSL_CB_WRITE": 0x08,  
    693                         "SSL_CB_ALERT": 0x4000,  
    694                         "SSL_CB_HANDSHAKE_START": 0x10, "SSL_CB_HANDSHAKE_DONE": 0x20} 
    695  
    696         def PlugIn(self, owner, on_tls_success, on_tls_failure, now=0): 
    697                 ''' If the 'now' argument is true then starts using encryption immidiatedly. 
    698                         If 'now' in false then starts encryption as soon as TLS feature is 
    699                         declared by the server (if it were already declared - it is ok). 
    700                 ''' 
    701                 if owner.__dict__.has_key('NonBlockingTLS'):  
    702                         return  # Already enabled. 
    703                 PlugIn.PlugIn(self, owner) 
    704                 DBG_LINE='NonBlockingTLS' 
    705                 self.on_tls_success = on_tls_success 
    706                 self.on_tls_faliure = on_tls_failure 
    707                 if now: 
    708                         try: 
    709                                 res = self._startSSL() 
    710                         except Exception, e: 
    711                                 log.error("PlugIn: while trying _startSSL():", exc_info=True) 
    712                                 #traceback.print_exc() 
    713                                 self._owner.socket.pollend() 
    714                                 return 
    715                         on_tls_success() 
    716                         return res 
    717                 if self._owner.Dispatcher.Stream.features: 
    718                         try:  
    719                                 self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features) 
    720                         except NodeProcessed:  
    721                                 pass 
    722                 else:  
    723                         self._owner.RegisterHandlerOnce('features',self.FeaturesHandler, xmlns=NS_STREAMS) 
    724                 self.starttls = None 
    725                  
    726         def plugout(self,now=0): 
    727                 ''' Unregisters TLS handler's from owner's dispatcher. Take note that encription 
    728                         can not be stopped once started. You can only break the connection and start over.''' 
    729                 # if dispatcher is not plugged we cannot (un)register handlers 
    730                 if self._owner.__dict__.has_key('Dispatcher'): 
    731                         self._owner.UnregisterHandler('features', self.FeaturesHandler,xmlns=NS_STREAMS) 
    732                         self._owner.Dispatcher.PlugOut() 
    733                 self._owner = None 
    734  
    735         def FeaturesHandler(self, conn, feats): 
    736                 ''' Used to analyse server <features/> tag for TLS support. 
    737                         If TLS is supported starts the encryption negotiation. Used internally ''' 
    738                 if not feats.getTag('starttls', namespace=NS_TLS): 
    739                         self.DEBUG("TLS unsupported by remote server.", 'warn') 
    740                         self.on_tls_failure("TLS unsupported by remote server.") 
    741                         return 
    742                 self.DEBUG("TLS supported by remote server. Requesting TLS start.", 'ok') 
    743                 self._owner.RegisterHandlerOnce('proceed', self.StartTLSHandler, xmlns=NS_TLS) 
    744                 self._owner.RegisterHandlerOnce('failure', self.StartTLSHandler, xmlns=NS_TLS) 
    745                 self._owner.send('<starttls xmlns="%s"/>' % NS_TLS) 
    746                 raise NodeProcessed 
    747  
    748         def _dumpX509(self, cert, stream=sys.stderr): 
    749                 print >> stream, "Digest (SHA-1):", cert.digest("sha1") 
    750                 print >> stream, "Digest (MD5):", cert.digest("md5") 
    751                 print >> stream, "Serial #:", cert.get_serial_number() 
    752                 print >> stream, "Version:", cert.get_version() 
    753                 print >> stream, "Expired:", torf(cert.has_expired(), "Yes", "No") 
    754                 print >> stream, "Subject:" 
    755                 self._dumpX509Name(cert.get_subject(), stream) 
    756                 print >> stream, "Issuer:" 
    757                 self._dumpX509Name(cert.get_issuer(), stream) 
    758                 self._dumpPKey(cert.get_pubkey(), stream) 
    759  
    760         def _dumpX509Name(self, name, stream=sys.stderr): 
    761                 print >> stream, "X509Name:", str(name) 
    762  
    763         def _dumpPKey(self, pkey, stream=sys.stderr): 
    764                 typedict = {OpenSSL.crypto.TYPE_RSA: "RSA", OpenSSL.crypto.TYPE_DSA: "DSA"} 
    765                 print >> stream, "PKey bits:", pkey.bits() 
    766                 print >> stream, "PKey type: %s (%d)" % (typedict.get(pkey.type(), "Unknown"), pkey.type()) 
    767  
    768         def _startSSL(self): 
    769                 ''' Immidiatedly switch socket to TLS mode. Used internally.''' 
    770                 log.debug("_startSSL called") 
    771                 if USE_PYOPENSSL: return self._startSSL_pyOpenSSL() 
    772                 return self._startSSL_stdlib() 
    773  
    774         def _startSSL_pyOpenSSL(self): 
    775                 #log.debug("_startSSL_pyOpenSSL called, thread id: %s", str(thread.get_ident())) 
    776                 log.debug("_startSSL_pyOpenSSL called") 
    777                 tcpsock = self._owner.Connection 
    778                 # FIXME: should method be configurable? 
    779                 tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) 
    780                 #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) 
    781                 tcpsock.ssl_errnum = 0 
    782                 tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback) 
    783                 cacerts = os.path.join('../data', 'other', 'cacerts.pem') 
    784                 try: 
    785                         tcpsock._sslContext.load_verify_locations(cacerts) 
    786                 except: 
    787                         log.warning('Unable to load SSL certificats from file %s' % \ 
    788                                 os.path.abspath(cacerts)) 
    789                 # load users certs 
    790                 if os.path.isfile('%s/.gajim/cacerts.pem' % os.environ['HOME']): 
    791                         store = tcpsock._sslContext.get_cert_store() 
    792                         f = open('%s/.gajim/cacerts.pem' % os.environ['HOME']) 
    793                         lines = f.readlines() 
    794                         i = 0 
    795                         begin = -1 
    796                         for line in lines: 
    797                                 if 'BEGIN CERTIFICATE' in line: 
    798                                         begin = i 
    799                                 elif 'END CERTIFICATE' in line and begin > -1: 
    800                                         cert = ''.join(lines[begin:i+2]) 
    801                                         try: 
    802                                                 X509cert = OpenSSL.crypto.load_certificate( 
    803                                                         OpenSSL.crypto.FILETYPE_PEM, cert) 
    804                                                 store.add_cert(X509cert) 
    805                                         except OpenSSL.crypto.Error, exception_obj: 
    806                                                 log.warning('Unable to load a certificate from file %s: %s' %\ 
    807                                                         ('%s/.gajim/cacerts.pem' % os.environ['HOME'], exception_obj.args[0][0][2])) 
    808                                         except: 
    809                                                 log.warning( 
    810                                                         'Unknown error while loading certificate from file %s' % \ 
    811                                                         '%s/.gajim/cacerts.pem' % os.environ['HOME']) 
    812                                         begin = -1 
    813                                 i += 1 
    814                 tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock) 
    815                 tcpsock._sslObj.set_connect_state() # set to client mode 
    816  
    817                 wrapper = PyOpenSSLWrapper(tcpsock._sslObj) 
    818                 tcpsock._recv = wrapper.recv 
    819                 tcpsock._send = wrapper.send 
    820  
    821                 log.debug("Initiating handshake...") 
    822                 # FIXME: Figure out why _connect_success is called before the 
    823                 # SSL handshake is completed in STARTTLS mode. See #2838. 
    824                 tcpsock._sslObj.setblocking(True) 
    825                 try: 
    826                         self.starttls='in progress' 
    827                         tcpsock._sslObj.do_handshake() 
    828                 except: 
    829                         log.error('Error while TLS handshake: ', exc_info=True) 
    830                         self.on_tls_failure('Error while TLS Handshake') 
    831                         return 
    832                 tcpsock._sslObj.setblocking(False) 
    833                 log.debug("Synchronous handshake completed") 
    834                 #log.debug("Async handshake started...") 
    835  
    836                 # fake it, for now 
    837                 self.starttls='success' 
    838  
    839         def _startSSL_stdlib(self): 
    840                 log.debug("_startSSL_stdlib called") 
    841                 tcpsock=self._owner.Connection 
    842                 tcpsock._sock.setblocking(True) 
    843                 tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None) 
    844                 tcpsock._sock.setblocking(False) 
    845                 tcpsock._sslIssuer = tcpsock._sslObj.issuer() 
    846                 tcpsock._sslServer = tcpsock._sslObj.server() 
    847                 wrapper = StdlibSSLWrapper(tcpsock._sslObj, tcpsock._sock) 
    848                 tcpsock._recv = wrapper.recv 
    849                 tcpsock._send = wrapper.send 
    850                 self.starttls='success' 
    851  
    852         def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok): 
    853                 # Exceptions can't propagate up through this callback, so print them here. 
    854                 try: 
    855                         self._owner.Connection.ssl_fingerprint_sha1 = cert.digest('sha1') 
    856                         if errnum == 0: 
    857                                 return True 
    858                         self._owner.Connection.ssl_errnum = errnum 
    859                         self._owner.Connection.ssl_cert_pem = OpenSSL.crypto.dump_certificate( 
    860                                 OpenSSL.crypto.FILETYPE_PEM, cert) 
    861                         return True 
    862                 except: 
    863                         log.error("Exception caught in _ssl_info_callback:", exc_info=True) 
    864                         traceback.print_exc() # Make sure something is printed, even if log is disabled. 
    865  
    866         def StartTLSHandler(self, conn, starttls): 
    867                 ''' Handle server reply if TLS is allowed to process. Behaves accordingly. 
    868                         Used internally.''' 
    869                 if starttls.getNamespace() <> NS_TLS:  
    87