- In verbose mode, print encodings. (Especially for Windows users who don't have Python) (gajim.py)
 - Attempt at fixing traceback when getting user's home directory in Windows. See #2812. (c/configpaths.py)
 - Show 'error' icon next to account while waiting for reconnect. Fixes #2786. (c/connection_handlers.py, c/gajim.py, c/connection.py)
[PyOpenSSL]
 - Fix 100% CPU usage and hanging connection when server closes connection on us. (c/x/transports_nb.py)
 - Fix 'hanging' connection when server closes the connection on us before we can open the XML stream. (Disconnect handler didn't get called.) (c/x/client_nb.py)
 - Change prints to logger calls, various enhancements to debug printing, reduce spam (c/x/transports_nb.py)
 - this → self (c/x/transports_nb.py)
 - Call _do_receive() once to collect error message from socket, when error flag is raised in scheduler. (c/x/transports_nb.py)
This commit is contained in:
junglecow 2006-12-20 20:40:08 +00:00
parent 8c78a14c3c
commit fddb000a89
2 changed files with 183 additions and 171 deletions

View File

@ -123,8 +123,12 @@ class NBCommonClient(CommonClient):
self.on_connect_failure(retry) self.on_connect_failure(retry)
def _on_connected(self): def _on_connected(self):
# connect succeeded, so no need of this callback anymore # FIXME: why was this needed? Please note that we're working
self.on_connect_failure = None # in nonblocking mode, and this handler is actually called
# as soon as connection is initiated, NOT when connection
# succeeds, as the name suggests.
# # connect succeeded, so no need of this callback anymore
# self.on_connect_failure = None
self.connected = 'tcp' self.connected = 'tcp'
if self._Ssl: if self._Ssl:
transports_nb.NonBlockingTLS().PlugIn(self, now=1) transports_nb.NonBlockingTLS().PlugIn(self, now=1)

View File

@ -46,12 +46,12 @@ try:
import OpenSSL.SSL import OpenSSL.SSL
import OpenSSL.crypto import OpenSSL.crypto
USE_PYOPENSSL = True USE_PYOPENSSL = True
print "PyOpenSSL loaded." log.info("PyOpenSSL loaded")
except ImportError: except ImportError:
# FIXME: Remove these prints before release, replace with a warning dialog. # FIXME: Remove these prints before release, replace with a warning dialog.
print "=" * 79 print >> sys.stderr, "=" * 79
print "PyOpenSSL not found, falling back to Python builtin SSL objects (insecure)." print >> sys.stderr, "PyOpenSSL not found, falling back to Python builtin SSL objects (insecure)."
print "=" * 79 print >> sys.stderr, "=" * 79
# timeout to connect to the server socket, it doesn't include auth # timeout to connect to the server socket, it doesn't include auth
CONNECT_TIMEOUT_SECONDS = 30 CONNECT_TIMEOUT_SECONDS = 30
@ -76,169 +76,162 @@ def gattr(obj, attr, default=None):
class SSLWrapper: class SSLWrapper:
class Error(IOError): class Error(IOError):
def __init__(this, sock=None, exc=None, errno=None, strerror=None, peer=None): def __init__(self, sock=None, exc=None, errno=None, strerror=None, peer=None):
this.parent = IOError self.parent = IOError
errno = errno or gattr(exc, 'errno') errno = errno or gattr(exc, 'errno')
strerror = strerror or gattr(exc, 'strerror') or gattr(exc, 'args') strerror = strerror or gattr(exc, 'strerror') or gattr(exc, 'args')
if not isinstance(strerror, basestring): strerror = repr(strerror) if not isinstance(strerror, basestring): strerror = repr(strerror)
this.sock = sock self.sock = sock
this.exc = exc self.exc = exc
this.peer = peer self.peer = peer
this.exc_name = None self.exc_name = None
this.exc_args = None self.exc_args = None
this.exc_str = None self.exc_str = None
this.exc_repr = None self.exc_repr = None
if this.exc is not None: if self.exc is not None:
this.exc_name = str(this.exc.__class__) self.exc_name = str(self.exc.__class__)
this.exc_args = gattr(this.exc, 'args') self.exc_args = gattr(self.exc, 'args')
this.exc_str = str(this.exc) self.exc_str = str(self.exc)
this.exc_repr = repr(this.exc) self.exc_repr = repr(self.exc)
if not errno: if not errno:
try: try:
if isinstance(exc, OpenSSL.SSL.SysCallError): if isinstance(exc, OpenSSL.SSL.SysCallError):
if this.exc_args[0] > 0: if self.exc_args[0] > 0:
errno = this.exc_args[0] errno = self.exc_args[0]
strerror = this.exc_args[1] strerror = self.exc_args[1]
except: traceback.print_exc() # FIXME: pass except: pass
this.parent.__init__(this, errno, strerror) self.parent.__init__(self, errno, strerror)
if this.peer is None and sock is not None: if self.peer is None and sock is not None:
try: try:
ppeer = this.sock.getpeername() ppeer = self.sock.getpeername()
if len(ppeer) == 2 and isinstance(ppeer[0], basestring) \ if len(ppeer) == 2 and isinstance(ppeer[0], basestring) \
and isinstance(ppeer[1], int): and isinstance(ppeer[1], int):
this.peer = ppeer self.peer = ppeer
except: pass except: pass
def __str__(this): def __str__(self):
s = str(this.__class__) s = str(self.__class__)
if this.peer: s += " for %s:%d" % this.peer if self.peer: s += " for %s:%d" % self.peer
if this.errno is not None: s += ": [Errno: %d]" % this.errno if self.errno is not None: s += ": [Errno: %d]" % self.errno
if this.strerror: s += " (%s)" % this.strerror if self.strerror: s += " (%s)" % self.strerror
if this.exc_name: if self.exc_name:
s += ", Caused by %s" % this.exc_name s += ", Caused by %s" % self.exc_name
if this.exc_str: if self.exc_str:
if this.strerror: s += "(%s)" % this.exc_str if self.strerror: s += "(%s)" % self.exc_str
else: s += "(%s)" % str(this.exc_args) else: s += "(%s)" % str(self.exc_args)
return s return s
def __init__(this, sslobj, sock=None): def __init__(self, sslobj, sock=None):
this.sslobj = sslobj self.sslobj = sslobj
this.sock = sock self.sock = sock
print "init called with", sslobj log.debug("%s.__init__ called with %s", self.__class__, sslobj)
# We can return None out of this function to signal that no data is def recv(self, data, flags=None):
# available right now. Better than an exception, which differs """ Receive wrapper for SSL object
# depending on which SSL lib we're using. Unfortunately returning ''
# can indicate that the socket has been closed, so to be sure, we avoid We can return None out of this function to signal that no data is
# this. available right now. Better than an exception, which differs
depending on which SSL lib we're using. Unfortunately returning ''
can indicate that the socket has been closed, so to be sure, we avoid
this by returning None. """
def recv(this, data, flags=None):
raise NotImplementedException() raise NotImplementedException()
def send(this, data, flags=None): def send(self, data, flags=None):
raise NotImplementedException() raise NotImplementedException()
class PyOpenSSLWrapper(SSLWrapper): class PyOpenSSLWrapper(SSLWrapper):
'''Wrapper class for PyOpenSSL's recv() and send() methods''' '''Wrapper class for PyOpenSSL's recv() and send() methods'''
def __init__(this, *args): def __init__(self, *args):
this.parent = SSLWrapper self.parent = SSLWrapper
this.parent.__init__(this, *args) self.parent.__init__(self, *args)
def is_numtoolarge(this, e): def is_numtoolarge(self, e):
t = ('asn1 encoding routines', 'a2d_ASN1_OBJECT', 'first num too large') t = ('asn1 encoding routines', 'a2d_ASN1_OBJECT', 'first num too large')
return isinstance(e.args, (list, tuple)) and len(e.args) == 1 and \ return isinstance(e.args, (list, tuple)) and len(e.args) == 1 and \
isinstance(e.args[0], (list, tuple)) and len(e.args[0]) == 2 and \ isinstance(e.args[0], (list, tuple)) and len(e.args[0]) == 2 and \
e.args[0][0] == e.args[0][1] == t e.args[0][0] == e.args[0][1] == t
def recv(this, bufsize, flags=None): def recv(self, bufsize, flags=None):
retval = None retval = None
try: try:
#print "recv: thread id:", thread.get_ident() if flags is None: retval = self.sslobj.recv(bufsize)
if flags is None: retval = this.sslobj.recv(bufsize) else: retval = self.sslobj.recv(bufsize, flags)
else: retval = this.sslobj.recv(bufsize, flags)
except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e:
log.debug("Recv: " + repr(e)) pass
# log.debug("Recv: " + repr(e))
except OpenSSL.SSL.SysCallError, e: except OpenSSL.SSL.SysCallError, e:
log.error("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e)) log.error("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True)
traceback.print_exc() #traceback.print_exc()
raise SSLWrapper.Error(this.sock or this.sslobj, e) raise SSLWrapper.Error(self.sock or self.sslobj, e)
except OpenSSL.SSL.Error, e: except OpenSSL.SSL.Error, e:
if this.is_numtoolarge(e): if self.is_numtoolarge(e):
# warn, but ignore this exception # warn, but ignore this exception
log.warning("Recv: OpenSSL: asn1enc: first num too large (ignored)") log.warning("Recv: OpenSSL: asn1enc: first num too large (ignored)")
else: else:
log.warning("Recv: Caught OpenSSL.SSL.Error:") log.error("Recv: Caught OpenSSL.SSL.Error:", exc_info=True)
traceback.print_exc() #traceback.print_exc()
print "Current Stack:" #print "Current Stack:"
traceback.print_stack() #traceback.print_stack()
raise SSLWrapper.Error(this.sock or this.sslobj, e) raise SSLWrapper.Error(self.sock or self.sslobj, e)
return retval return retval
def send(this, data, flags=None): def send(self, data, flags=None):
try: try:
#print "send: thread id:", thread.get_ident() if flags is None: return self.sslobj.send(data)
if flags is None: return this.sslobj.send(data) else: return self.sslobj.send(data, flags)
else: return this.sslobj.send(data, flags)
except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e:
log.debug("Send: " + repr(e)) #log.debug("Send: " + repr(e))
time.sleep(0.1) # prevent 100% CPU usage time.sleep(0.1) # prevent 100% CPU usage
except OpenSSL.SSL.SysCallError, e: except OpenSSL.SSL.SysCallError, e:
log.error("Send: Got OpenSSL.SSL.SysCallError: " + repr(e)) log.error("Send: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True)
traceback.print_exc() #traceback.print_exc()
raise SSLWrapper.Error(this.sock or this.sslobj, e) raise SSLWrapper.Error(self.sock or self.sslobj, e)
except OpenSSL.SSL.Error, e: except OpenSSL.SSL.Error, e:
if this.is_numtoolarge(e): if self.is_numtoolarge(e):
# warn, but ignore this exception # warn, but ignore this exception
log.warning("Send: OpenSSL: asn1enc: first num too large (ignored)") log.warning("Send: OpenSSL: asn1enc: first num too large (ignored)")
else: else:
log.warning("Send: Caught OpenSSL.SSL.Error:") log.error("Send: Caught OpenSSL.SSL.Error:", exc_info=True)
traceback.print_exc() #traceback.print_exc()
print "Current Stack:" #print "Current Stack:"
traceback.print_stack() #traceback.print_stack()
raise SSLWrapper.Error(this.sock or this.sslobj, e) raise SSLWrapper.Error(self.sock or self.sslobj, e)
return 0 return 0
class StdlibSSLWrapper(SSLWrapper): class StdlibSSLWrapper(SSLWrapper):
'''Wrapper class for Python's socket.ssl read() and write() methods''' '''Wrapper class for Python's socket.ssl read() and write() methods'''
def __init__(this, *args): def __init__(self, *args):
this.parent = SSLWrapper self.parent = SSLWrapper
this.parent.__init__(this, *args) self.parent.__init__(self, *args)
def recv(this, bufsize, flags=None): def recv(self, bufsize, flags=None):
# we simply ignore flags since ssl object doesn't support it
retval = None
try:
retval = this.sslobj.read(bufsize)
except socket.sslerror, e:
print "Got socket.sslerror"
print e, e.args
traceback.print_exc()
print "Current Stack:"
traceback.print_stack()
if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE):
raise SSLWrapper.Error(this.sock or this.sslobj, e)
return retval
def send(this, data, flags=None):
# we simply ignore flags since ssl object doesn't support it # we simply ignore flags since ssl object doesn't support it
try: try:
return this.sslobj.write(data) return self.sslobj.read(bufsize)
except socket.sslerror, e: except socket.sslerror, e:
print "Got socket.sslerror" log.debug("Recv: Caught socket.sslerror:", exc_info=True)
print e, e.args #traceback.print_exc()
traceback.print_exc()
print "Current Stack:"
traceback.print_stack()
if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE):
raise SSLWrapper.Error(this.sock or this.sslobj, e) raise SSLWrapper.Error(self.sock or self.sslobj, e)
return None
def send(self, data, flags=None):
# we simply ignore flags since ssl object doesn't support it
try:
return self.sslobj.write(data)
except socket.sslerror, e:
log.debug("Send: Caught socket.sslerror:", exc_info=True)
#traceback.print_exc()
if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE):
raise SSLWrapper.Error(self.sock or self.sslobj, e)
return 0 return 0
class NonBlockingTcp(PlugIn, IdleObject): class NonBlockingTcp(PlugIn, IdleObject):
@ -259,6 +252,7 @@ class NonBlockingTcp(PlugIn, IdleObject):
self.on_connect_failure = on_connect_failure self.on_connect_failure = on_connect_failure
self.on_receive = None self.on_receive = None
self.on_disconnect = None self.on_disconnect = None
self.printed_error = False
# 0 - not connected # 0 - not connected
# 1 - connected # 1 - connected
@ -288,6 +282,7 @@ class NonBlockingTcp(PlugIn, IdleObject):
Also registers self.disconnected method in the owner's dispatcher. Also registers self.disconnected method in the owner's dispatcher.
Called internally. ''' Called internally. '''
self.idlequeue = owner.idlequeue self.idlequeue = owner.idlequeue
self.printed_error = False
if not self._server: if not self._server:
self._server=(self._owner.Server,5222) self._server=(self._owner.Server,5222)
if self.connect(self._server) is False: if self.connect(self._server) is False:
@ -310,12 +305,14 @@ class NonBlockingTcp(PlugIn, IdleObject):
server=self._server server=self._server
else: else:
self._server = server self._server = server
self.printed_error = False
self.state = 0 self.state = 0
try: try:
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._sock.setblocking(False) self._sock.setblocking(False)
except: except:
traceback.print_exc() log.error("Exception trying to connect to %s:", self.getName(), exc_info=True)
#traceback.print_exc()
if self.on_connect_failure: if self.on_connect_failure:
self.on_connect_failure() self.on_connect_failure()
return False return False
@ -350,6 +347,10 @@ class NonBlockingTcp(PlugIn, IdleObject):
self._do_receive() self._do_receive()
def pollend(self, retry=False): def pollend(self, retry=False):
if not self.printed_error:
self.printed_error = True
try: self._do_receive(errors_only=True)
except: log.error("pollend: Got exception from _do_receive:", exc_info=True)
conn_failure_cb = self.on_connect_failure conn_failure_cb = self.on_connect_failure
self.disconnect() self.disconnect()
if conn_failure_cb: if conn_failure_cb:
@ -367,9 +368,9 @@ class NonBlockingTcp(PlugIn, IdleObject):
self._sock.shutdown(socket.SHUT_RDWR) self._sock.shutdown(socket.SHUT_RDWR)
except socket.error, e: except socket.error, e:
if e[0] != errno.ENOTCONN: if e[0] != errno.ENOTCONN:
traceback.print_exc() log.error("Error shutting down socket for %s:", self.getName(), exc_info=True)
try: self._sock.close() try: self._sock.close()
except: traceback.print_exc() except: log.error("Error closing socket for %s:", self.getName(), exc_info=True)
# socket descriptor cannot be (un)plugged anymore # socket descriptor cannot be (un)plugged anymore
self.fd = -1 self.fd = -1
if self.on_disconnect: if self.on_disconnect:
@ -409,7 +410,7 @@ class NonBlockingTcp(PlugIn, IdleObject):
if not recv_handler(None) and _tmp == self.on_receive: if not recv_handler(None) and _tmp == self.on_receive:
self.on_receive = recv_handler self.on_receive = recv_handler
def _do_receive(self): def _do_receive(self, errors_only=False):
''' Reads all pending incoming data. Calls owner's disconnected() method if appropriate.''' ''' Reads all pending incoming data. Calls owner's disconnected() method if appropriate.'''
ERR_DISCONN = -2 # Misc error signifying that we got disconnected ERR_DISCONN = -2 # Misc error signifying that we got disconnected
ERR_OTHER = -1 # Other error ERR_OTHER = -1 # Other error
@ -420,39 +421,35 @@ class NonBlockingTcp(PlugIn, IdleObject):
# get as many bites, as possible, but not more than RECV_BUFSIZE # get as many bites, as possible, but not more than RECV_BUFSIZE
received = self._recv(RECV_BUFSIZE) received = self._recv(RECV_BUFSIZE)
except (socket.error, socket.herror, socket.gaierror), e: except (socket.error, socket.herror, socket.gaierror), e:
print "Got socket exception" log.error("_do_receive: got %s:", e.__class__, exc_info=True)
traceback.print_exc() #traceback.print_exc()
print "Current Stack:" #print "Current Stack:"
traceback.print_stack() #traceback.print_stack()
errnum = e[0] errnum = e[0]
errtxt = str(errnum) + ':' + e[1] errtxt = str(errnum) + ':' + e[1]
except socket.sslerror, e: except socket.sslerror, e:
print "Got unknown socket.sslerror" log.error("_do_receive: got unknown %s:", e.__class__, exc_info=True)
traceback.print_exc() #traceback.print_exc()
print "Current Stack:" #print "Current Stack:"
traceback.print_stack() #traceback.print_stack()
errnum = ERR_OTHER errnum = ERR_OTHER
errtxt = repr("socket.sslerror: " + e.args) errtxt = repr("socket.sslerror: " + e.args)
except SSLWrapper.Error, e: except SSLWrapper.Error, e:
print "caught " + str(e) log.debug("Caught: %s", str(e))
errnum = gattr(e, 'errno', ERR_OTHER) errnum = gattr(e, 'errno', ERR_OTHER)
if not errnum: errnum = ERR_OTHER # unset, but we must put a status if not errnum: errnum = ERR_OTHER # unset, but we must put a status
errtxt = gattr(e, 'strerror') or repr(e.args) errtxt = gattr(e, 'strerror') or repr(e.args)
# Should we really do this? In C, recv() will happily return 0 if received == '':
# in nonblocking mode when there is no data waiting, and in
# some cases select() will mark the socket for reading when
# there is nothing to read, and the socket is still open. For
# example, this can happen when the remote sends a zero-length
# tcp packet.
if received is '':
errnum = ERR_DISCONN errnum = ERR_DISCONN
errtxt = "Connection closed unexpectedly" errtxt = "Connection closed unexpectedly"
if errnum in (ERR_DISCONN, errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN): if errnum in (ERR_DISCONN, errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN):
self.DEBUG(errtxt, 'error') self.DEBUG(errtxt, 'error')
log.error("Connection to %s lost: %s [%d]", self.getName(), errtxt, errnum) log.error("Connection to %s lost: %s [%d]", self.getName(), errtxt, errnum)
self.pollend(retry=(errnum in (ERR_DISCONN, errno.ECONNRESET))) self.printed_error = True
if not errors_only:
self.pollend(retry=(errnum in (ERR_DISCONN, errno.ECONNRESET)))
# don't process result, because it will raise an error # don't process result, because it will raise an error
return return
@ -460,12 +457,13 @@ class NonBlockingTcp(PlugIn, IdleObject):
if errnum != 0: if errnum != 0:
self.DEBUG(errtxt, 'error') self.DEBUG(errtxt, 'error')
log.error("Connection to %s lost: %s [%d]", self.getName(), errtxt, errnum) log.error("Connection to %s lost: %s [%d]", self.getName(), errtxt, errnum)
if self.state >= 0: self.printed_error = True
if not errors_only and self.state >= 0:
self.pollend(retry=True) self.pollend(retry=True)
return return
received = '' received = ''
if self.state < 0: if errors_only or self.state < 0:
return return
# we have received some bites, stop the timeout! # we have received some bites, stop the timeout!
@ -504,9 +502,10 @@ class NonBlockingTcp(PlugIn, IdleObject):
self._plug_idle() self._plug_idle()
self._on_send() self._on_send()
except socket.error, e: except socket.error, e:
traceback.print_exc()
if e[0] == socket.SSL_ERROR_WANT_WRITE: if e[0] == socket.SSL_ERROR_WANT_WRITE:
return True return True
log.error("_do_send:", exc_info=True)
#traceback.print_exc()
if self.state < 0: if self.state < 0:
self.disconnect() self.disconnect()
return return
@ -519,13 +518,16 @@ class NonBlockingTcp(PlugIn, IdleObject):
if self.state != 0: if self.state != 0:
return return
self._sock.setblocking(False) self._sock.setblocking(False)
self._send = self._sock.send
self._recv = self._sock.recv
errnum = 0 errnum = 0
try: try:
self._sock.connect(self._server) self._sock.connect(self._server)
except socket.error, e: except socket.error, e:
errnum = e[0] errnum = e[0]
if errnum != errno.EINPROGRESS: if errnum != errno.EINPROGRESS:
traceback.print_exc() log.error("_do_connect:", exc_info=True)
#traceback.print_exc()
# in progress, or would block # in progress, or would block
if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK):
return return
@ -541,8 +543,6 @@ class NonBlockingTcp(PlugIn, IdleObject):
self.state = 1 self.state = 1
self._sock.setblocking(False) self._sock.setblocking(False)
self._send = self._sock.send
self._recv = self._sock.recv
self._plug_idle() self._plug_idle()
if self.on_connect: if self.on_connect:
self.on_connect() self.on_connect()
@ -627,7 +627,8 @@ class NonBlockingTLS(PlugIn):
try: try:
res = self._startSSL() res = self._startSSL()
except Exception, e: except Exception, e:
traceback.print_exc() log.error("PlugIn: while trying _startSSL():", exc_info=True)
#traceback.print_exc()
self._owner.socket.pollend() self._owner.socket.pollend()
return return
self.tls_start() self.tls_start()
@ -669,37 +670,39 @@ class NonBlockingTLS(PlugIn):
self.tls_start() self.tls_start()
raise NodeProcessed raise NodeProcessed
def _dumpX509(this, cert): def _dumpX509(self, cert, stream=sys.stderr):
print "Digest (SHA-1):", cert.digest("sha1") print >> stream, "Digest (SHA-1):", cert.digest("sha1")
print "Digest (MD5):", cert.digest("md5") print >> stream, "Digest (MD5):", cert.digest("md5")
print "Serial #:", cert.get_serial_number() print >> stream, "Serial #:", cert.get_serial_number()
print "Version:", cert.get_version() print >> stream, "Version:", cert.get_version()
print "Expired:", torf(cert.has_expired(), "Yes", "No") print >> stream, "Expired:", torf(cert.has_expired(), "Yes", "No")
print "Subject:" print >> stream, "Subject:"
this._dumpX509Name(cert.get_subject()) self._dumpX509Name(cert.get_subject(), stream)
print "Issuer:" print >> stream, "Issuer:"
this._dumpX509Name(cert.get_issuer()) self._dumpX509Name(cert.get_issuer(), stream)
this._dumpPKey(cert.get_pubkey()) self._dumpPKey(cert.get_pubkey(), stream)
def _dumpX509Name(this, name): def _dumpX509Name(self, name, stream=sys.stderr):
print "X509Name:", str(name) print >> stream, "X509Name:", str(name)
def _dumpPKey(this, pkey): def _dumpPKey(self, pkey, stream=sys.stderr):
typedict = {OpenSSL.crypto.TYPE_RSA: "RSA", OpenSSL.crypto.TYPE_DSA: "DSA"} typedict = {OpenSSL.crypto.TYPE_RSA: "RSA", OpenSSL.crypto.TYPE_DSA: "DSA"}
print "PKey bits:", pkey.bits() print >> stream, "PKey bits:", pkey.bits()
print "PKey type: %s (%d)" % (typedict.get(pkey.type(), "Unknown"), pkey.type()) print >> stream, "PKey type: %s (%d)" % (typedict.get(pkey.type(), "Unknown"), pkey.type())
def _startSSL(self): def _startSSL(self):
''' Immidiatedly switch socket to TLS mode. Used internally.''' ''' Immidiatedly switch socket to TLS mode. Used internally.'''
print "_startSSL called" log.debug("_startSSL called")
if USE_PYOPENSSL: return self._startSSL_pyOpenSSL() if USE_PYOPENSSL: return self._startSSL_pyOpenSSL()
return self._startSSL_stdlib() return self._startSSL_stdlib()
def _startSSL_pyOpenSSL(self): def _startSSL_pyOpenSSL(self):
print "_startSSL_pyOpenSSL called, thread id:", thread.get_ident() #log.debug("_startSSL_pyOpenSSL called, thread id: %s", str(thread.get_ident()))
log.debug("_startSSL_pyOpenSSL called")
tcpsock = self._owner.Connection tcpsock = self._owner.Connection
# FIXME: should method be configurable? # FIXME: should method be configurable?
tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
tcpsock._sslContext.set_info_callback(self._ssl_info_callback) tcpsock._sslContext.set_info_callback(self._ssl_info_callback)
tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock) tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock)
tcpsock._sslObj.set_connect_state() # set to client mode tcpsock._sslObj.set_connect_state() # set to client mode
@ -708,22 +711,22 @@ class NonBlockingTLS(PlugIn):
tcpsock._recv = wrapper.recv tcpsock._recv = wrapper.recv
tcpsock._send = wrapper.send tcpsock._send = wrapper.send
print "Initiating handshake..." log.debug("Initiating handshake...")
#tcpsock._sslObj.setblocking(True) #tcpsock._sslObj.setblocking(True)
try: try:
self.starttls='in progress' self.starttls='in progress'
tcpsock._sslObj.do_handshake() tcpsock._sslObj.do_handshake()
except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e:
log.debug("do_handshake: " + repr(e)) pass
#tcpsock._sslObj.setblocking(False) #tcpsock._sslObj.setblocking(False)
#print "Done handshake" #print "Done handshake"
print "Async handshake started..." log.debug("Async handshake started...")
# fake it, for now # fake it, for now
self.starttls='success' self.starttls='success'
def _on_ssl_handshake_done(self): def _on_ssl_handshake_done(self):
print "Handshake done!" log.debug("Handshake done!")
#self.starttls='success' #self.starttls='success'
tcpsock = self._owner.Connection tcpsock = self._owner.Connection
@ -737,14 +740,14 @@ class NonBlockingTLS(PlugIn):
peercert = tcpsock._sslObj.get_peer_certificate() peercert = tcpsock._sslObj.get_peer_certificate()
ciphers = tcpsock._sslObj.get_cipher_list() ciphers = tcpsock._sslObj.get_cipher_list()
print "Ciphers:", ciphers print >> sys.stderr, "Ciphers:", ciphers
print "Peer cert:", peercert print >> sys.stderr, "Peer cert:", peercert
self._dumpX509(peercert) self._dumpX509(peercert)
print OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, peercert) print >> sys.stderr, OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, peercert)
def _startSSL_stdlib(self): def _startSSL_stdlib(self):
print "_startSSL_stdlib called" log.debug("_startSSL_stdlib called")
tcpsock=self._owner.Connection tcpsock=self._owner.Connection
tcpsock._sock.setblocking(True) tcpsock._sock.setblocking(True)
tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None) tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None)
@ -756,13 +759,16 @@ class NonBlockingTLS(PlugIn):
tcpsock._send = wrapper.send tcpsock._send = wrapper.send
self.starttls='success' self.starttls='success'
def _ssl_info_callback(this, sslconn, type, st): def _ssl_info_callback(self, sslconn, type, st):
# Exceptions can't propagate up through this callback, so print them here. # Exceptions can't propagate up through this callback, so print them here.
try: this._ssl_info_callback_guarded(sslconn, type, st) try:
except: traceback.print_exc() self._ssl_info_callback_guarded(sslconn, type, st)
except:
log.error("Exception caught in _ssl_info_callback:", exc_info=True)
traceback.print_exc() # Make sure something is printed, even if log is disabled.
def _ssl_info_callback_guarded(this, sslconn, type, st): def _ssl_info_callback_guarded(self, sslconn, type, st):
b = this.ssl_h_bits b = self.ssl_h_bits
#if type & b['SSL_CB_LOOP']: #if type & b['SSL_CB_LOOP']:
# if type & SSL_ST_CONNECT: tls_state = "connect" # if type & SSL_ST_CONNECT: tls_state = "connect"
@ -782,7 +788,7 @@ class NonBlockingTLS(PlugIn):
#print "mask:", mask, st #print "mask:", mask, st
if type & b['SSL_CB_HANDSHAKE_DONE']: if type & b['SSL_CB_HANDSHAKE_DONE']:
this._on_ssl_handshake_done() self._on_ssl_handshake_done()
def StartTLSHandler(self, conn, starttls): def StartTLSHandler(self, conn, starttls):
''' Handle server reply if TLS is allowed to process. Behaves accordingly. ''' Handle server reply if TLS is allowed to process. Behaves accordingly.
@ -797,7 +803,8 @@ class NonBlockingTLS(PlugIn):
try: try:
self._startSSL() self._startSSL()
except Exception, e: except Exception, e:
traceback.print_exc() log.error("StartTLSHandler:", exc_info=True)
#traceback.print_exc()
self._owner.socket.pollend() self._owner.socket.pollend()
return return
self._owner.Dispatcher.PlugOut() self._owner.Dispatcher.PlugOut()
@ -856,7 +863,8 @@ class NBHTTPPROXYsocket(NonBlockingTcp):
try: try:
proto, code, desc = reply.split('\n')[0].split(' ', 2) proto, code, desc = reply.split('\n')[0].split(' ', 2)
except: except:
traceback.print_exc() log.error("_on_headers_sent:", exc_info=True)
#traceback.print_exc()
raise error('Invalid proxy reply') raise error('Invalid proxy reply')
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')