Added more on error evaluation and a method to get the peer certificate chain

* dtls/__init__.py: import error codes from err.py as error_codes for external access
* dtls/err.py: Added errors for ERR_WRONG_SSL_VERSION, ERR_CERTIFICATE_VERIFY_FAILED, ERR_NO_SHARED_CIPHER and ERR_SSL_HANDSHAKE_FAILURE
* dtls/openssl.py:
	- Added constant SSL_BUILD_CHAIN_FLAG_NONE for SSL_CTX_build_cert_chain()
	- Added method SSL_get_peer_cert_chain()
* dtls/patch.py: Added getpeercertchain() as method to ssl.SSLSocket()
* dtls/sslconnection.py:
	- Bugfix SSLContext.set_ecdh_curve() returns 1 for success and 0 for failure
	- SSLContext.build_cert_chain() changed default flags to SSL_BUILD_CHAIN_FLAG_NONE
	- In SSLConnection() the mtu size gets only set if no user config function is given
	- SSLConnection.listen() raises an exception for ERR_WRONG_VERSION_NUMBER, ERR_COOKIE_MISMATCH, ERR_NO_SHARED_CIPHER and all other unknown errors
	- SSLConnection.read() and write() now can also raise ERR_PORT_UNREACHABLE
	- If SSLConnection.write() successfully writes bytes to the peer, then the handshake is assumed to be okay
	- Added method SSLConnection.getpeercertchain()
* dtls/test/unit.py: ThreadedEchoServer() with an extra exception branch for the newly raised exceptions in SSLConnection.listen()
incoming
mcfreis 2017-03-20 16:39:50 +01:00
parent 13edf48fdf
commit ff509e0724
7 changed files with 207 additions and 121 deletions

View File

@ -1,3 +1,23 @@
2017-03-17 Björn Freise <mcfreis@gmx.net>
Added more on error evaluation and a method to get the peer certificate chain
* dtls/__init__.py: import error codes from err.py as error_codes for external access
* dtls/err.py: Added errors for ERR_WRONG_SSL_VERSION, ERR_CERTIFICATE_VERIFY_FAILED, ERR_NO_SHARED_CIPHER and ERR_SSL_HANDSHAKE_FAILURE
* dtls/openssl.py:
- Added constant SSL_BUILD_CHAIN_FLAG_NONE for SSL_CTX_build_cert_chain()
- Added method SSL_get_peer_cert_chain()
* dtls/patch.py: Added getpeercertchain() as method to ssl.SSLSocket()
* dtls/sslconnection.py:
- Bugfix SSLContext.set_ecdh_curve() returns 1 for success and 0 for failure
- SSLContext.build_cert_chain() changed default flags to SSL_BUILD_CHAIN_FLAG_NONE
- In SSLConnection() the mtu size gets only set if no user config function is given
- SSLConnection.listen() raises an exception for ERR_WRONG_VERSION_NUMBER, ERR_COOKIE_MISMATCH, ERR_NO_SHARED_CIPHER and all other unknown errors
- SSLConnection.read() and write() now can also raise ERR_PORT_UNREACHABLE
- If SSLConnection.write() successfully writes bytes to the peer, then the handshake is assumed to be okay
- Added method SSLConnection.getpeercertchain()
* dtls/test/unit.py: ThreadedEchoServer() with an extra exception branch for the newly raised exceptions in SSLConnection.listen()
2017-03-17 Björn Freise <mcfreis@gmx.net> 2017-03-17 Björn Freise <mcfreis@gmx.net>
Added certificate creation using ECDSA Added certificate creation using ECDSA

View File

@ -53,11 +53,12 @@ def _prep_bins():
for prebuilt_file in files: for prebuilt_file in files:
try: try:
copy(path.join(prebuilt_path, prebuilt_file), package_root) copy(path.join(prebuilt_path, prebuilt_file), package_root)
except IOError: except IOError:
pass pass
_prep_bins() # prepare before module imports _prep_bins() # prepare before module imports
from patch import do_patch from patch import do_patch
from sslconnection import SSLConnection from sslconnection import SSLConnection
from demux import force_routing_demux, reset_default_demux from demux import force_routing_demux, reset_default_demux
import err as error_codes

View File

@ -42,20 +42,24 @@ SSL_ERROR_WANT_ACCEPT = 8
ERR_BOTH_KEY_CERT_FILES = 500 ERR_BOTH_KEY_CERT_FILES = 500
ERR_BOTH_KEY_CERT_FILES_SVR = 298 ERR_BOTH_KEY_CERT_FILES_SVR = 298
ERR_NO_CERTS = 331 ERR_NO_CERTS = 331
ERR_NO_CIPHER = 501 ERR_NO_CIPHER = 501
ERR_READ_TIMEOUT = 502 ERR_READ_TIMEOUT = 502
ERR_WRITE_TIMEOUT = 503 ERR_WRITE_TIMEOUT = 503
ERR_HANDSHAKE_TIMEOUT = 504 ERR_HANDSHAKE_TIMEOUT = 504
ERR_PORT_UNREACHABLE = 505 ERR_PORT_UNREACHABLE = 505
ERR_WRONG_SSL_VERSION = 0x1409210A
ERR_WRONG_VERSION_NUMBER = 0x1408A10B ERR_WRONG_VERSION_NUMBER = 0x1408A10B
ERR_COOKIE_MISMATCH = 0x1408A134 ERR_COOKIE_MISMATCH = 0x1408A134
ERR_CERTIFICATE_VERIFY_FAILED = 0x14090086
ERR_NO_SHARED_CIPHER = 0x1408A0C1
ERR_SSL_HANDSHAKE_FAILURE = 0x1410C0E5
class SSLError(socket_error): class SSLError(socket_error):
"""This exception is raised by modules in the dtls package.""" """This exception is raised by modules in the dtls package."""
def __init__(self, *args): def __init__(self, *args):
super(SSLError, self).__init__(*args) super(SSLError, self).__init__(*args)
class InvalidSocketError(Exception): class InvalidSocketError(Exception):

View File

@ -92,12 +92,13 @@ SSL_VERIFY_CLIENT_ONCE = 0x04
SSL_SESS_CACHE_OFF = 0x0000 SSL_SESS_CACHE_OFF = 0x0000
SSL_SESS_CACHE_CLIENT = 0x0001 SSL_SESS_CACHE_CLIENT = 0x0001
SSL_SESS_CACHE_SERVER = 0x0002 SSL_SESS_CACHE_SERVER = 0x0002
SSL_SESS_CACHE_BOTH = SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_SERVER SSL_SESS_CACHE_BOTH = SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_SERVER
SSL_SESS_CACHE_NO_AUTO_CLEAR = 0x0080 SSL_SESS_CACHE_NO_AUTO_CLEAR = 0x0080
SSL_SESS_CACHE_NO_INTERNAL_LOOKUP = 0x0100 SSL_SESS_CACHE_NO_INTERNAL_LOOKUP = 0x0100
SSL_SESS_CACHE_NO_INTERNAL_STORE = 0x0200 SSL_SESS_CACHE_NO_INTERNAL_STORE = 0x0200
SSL_SESS_CACHE_NO_INTERNAL = \ SSL_SESS_CACHE_NO_INTERNAL = \
SSL_SESS_CACHE_NO_INTERNAL_LOOKUP | SSL_SESS_CACHE_NO_INTERNAL_STORE SSL_SESS_CACHE_NO_INTERNAL_LOOKUP | SSL_SESS_CACHE_NO_INTERNAL_STORE
SSL_BUILD_CHAIN_FLAG_NONE = 0x0
SSL_BUILD_CHAIN_FLAG_UNTRUSTED = 0x1 SSL_BUILD_CHAIN_FLAG_UNTRUSTED = 0x1
SSL_BUILD_CHAIN_FLAG_NO_ROOT = 0x2 SSL_BUILD_CHAIN_FLAG_NO_ROOT = 0x2
SSL_BUILD_CHAIN_FLAG_CHECK = 0x4 SSL_BUILD_CHAIN_FLAG_CHECK = 0x4
@ -345,18 +346,25 @@ class GENERAL_NAME(Structure):
class GENERAL_NAMES(STACK): class GENERAL_NAMES(STACK):
stack_element_type = GENERAL_NAME stack_element_type = GENERAL_NAME
def __init__(self, value): def __init__(self, value):
super(GENERAL_NAMES, self).__init__(value) super(GENERAL_NAMES, self).__init__(value)
class X509_NAME_ENTRY(Structure): class STACK_OF_X509(STACK):
_fields_ = [("object", c_void_p), stack_element_type = X509
("value", c_void_p),
("set", c_int), def __init__(self, value):
("size", c_int)] super(STACK_OF_X509, self).__init__(value)
class X509_NAME_ENTRY(Structure):
_fields_ = [("object", c_void_p),
("value", c_void_p),
("set", c_int),
("size", c_int)]
class ASN1_OCTET_STRING(Structure): class ASN1_OCTET_STRING(Structure):
_fields_ = [("length", c_int), _fields_ = [("length", c_int),
@ -597,8 +605,8 @@ __all__ = [
"SSL_CB_ACCEPT_LOOP", "SSL_CB_ACCEPT_EXIT", "SSL_CB_ACCEPT_LOOP", "SSL_CB_ACCEPT_EXIT",
"SSL_CB_CONNECT_LOOP", "SSL_CB_CONNECT_EXIT", "SSL_CB_CONNECT_LOOP", "SSL_CB_CONNECT_EXIT",
"SSL_CB_HANDSHAKE_START", "SSL_CB_HANDSHAKE_DONE", "SSL_CB_HANDSHAKE_START", "SSL_CB_HANDSHAKE_DONE",
"SSL_BUILD_CHAIN_FLAG_UNTRUSTED", "SSL_BUILD_CHAIN_FLAG_NO_ROOT", "SSL_BUILD_CHAIN_FLAG_CHECK", "SSL_BUILD_CHAIN_FLAG_NONE", "SSL_BUILD_CHAIN_FLAG_UNTRUSTED", "SSL_BUILD_CHAIN_FLAG_NO_ROOT",
"SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR", "SSL_BUILD_CHAIN_FLAG_CLEAR_ERROR", "SSL_BUILD_CHAIN_FLAG_CHECK", "SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR", "SSL_BUILD_CHAIN_FLAG_CLEAR_ERROR",
"SSL_FILE_TYPE_PEM", "SSL_FILE_TYPE_PEM",
"GEN_DIRNAME", "NID_subject_alt_name", "GEN_DIRNAME", "NID_subject_alt_name",
"CRYPTO_LOCK", "CRYPTO_LOCK",
@ -628,6 +636,7 @@ __all__ = [
"SSL_set1_curves", "SSL_set1_curves_list", "SSL_set1_curves", "SSL_set1_curves_list",
"SSL_set_mtu", "SSL_set_mtu",
"SSL_state_string_long", "SSL_alert_type_string_long", "SSL_alert_desc_string_long", "SSL_state_string_long", "SSL_alert_type_string_long", "SSL_alert_desc_string_long",
"SSL_get_peer_cert_chain",
"SSL_CTX_set_cookie_cb", "SSL_CTX_set_cookie_cb",
"OBJ_obj2txt", "decode_ASN1_STRING", "ASN1_TIME_print", "OBJ_obj2txt", "decode_ASN1_STRING", "ASN1_TIME_print",
"OBJ_nid2sn", "OBJ_nid2sn",
@ -736,6 +745,8 @@ map(lambda x: _make_function(*x), (
((c_int, "ret"), (SSL, "ssl"))), ((c_int, "ret"), (SSL, "ssl"))),
("SSL_get_peer_certificate", libssl, ("SSL_get_peer_certificate", libssl,
((X509, "ret"), (SSL, "ssl"))), ((X509, "ret"), (SSL, "ssl"))),
("SSL_get_peer_cert_chain", libssl,
((STACK_OF_X509, "ret"), (SSL, "ssl")), False),
("SSL_read", libssl, ("SSL_read", libssl,
((c_int, "ret"), (SSL, "ssl"), (c_void_p, "buf"), (c_int, "num")), False), ((c_int, "ret"), (SSL, "ssl"), (c_void_p, "buf"), (c_int, "num")), False),
("SSL_write", libssl, ("SSL_write", libssl,
@ -1149,9 +1160,19 @@ def GENERAL_NAME_print(general_name):
_free_func = addressof(c_void_p.in_dll(libcrypto, "sk_free")) _free_func = addressof(c_void_p.in_dll(libcrypto, "sk_free"))
def sk_pop_free(stack): def sk_pop_free(stack):
_sk_pop_free(stack, _free_func) _sk_pop_free(stack, _free_func)
def i2d_X509(x509): def i2d_X509(x509):
bio = _BIO(BIO_new(BIO_s_mem())) bio = _BIO(BIO_new(BIO_s_mem()))
_i2d_X509_bio(bio.value, x509) _i2d_X509_bio(bio.value, x509)
return BIO_get_mem_data(bio.value) return BIO_get_mem_data(bio.value)
def SSL_get_peer_cert_chain(ssl):
stack = _SSL_get_peer_cert_chain(ssl)
num = sk_num(stack)
certs = []
if num:
# why not use sk_value(): because it doesn't cast correct in this case?!
# certs = [(sk_value(stack, i)) for i in xrange(num)]
certs = [X509(_sk_value(stack, i)) for i in xrange(num)]
return stack, num, certs

View File

@ -198,18 +198,24 @@ def _SSLSocket_init(self, sock=None, keyfile=None, certfile=None,
self._user_config_ssl = cb_user_config_ssl self._user_config_ssl = cb_user_config_ssl
# Perform method substitution and addition (without reference cycle) # Perform method substitution and addition (without reference cycle)
self._real_connect = MethodType(_SSLSocket_real_connect, proxy(self)) self._real_connect = MethodType(_SSLSocket_real_connect, proxy(self))
self.listen = MethodType(_SSLSocket_listen, proxy(self)) self.listen = MethodType(_SSLSocket_listen, proxy(self))
self.accept = MethodType(_SSLSocket_accept, proxy(self)) self.accept = MethodType(_SSLSocket_accept, proxy(self))
self.get_timeout = MethodType(_SSLSocket_get_timeout, proxy(self)) self.get_timeout = MethodType(_SSLSocket_get_timeout, proxy(self))
self.handle_timeout = MethodType(_SSLSocket_handle_timeout, proxy(self)) self.handle_timeout = MethodType(_SSLSocket_handle_timeout, proxy(self))
def _SSLSocket_listen(self, ignored): # Extra
if self._connected: self.getpeercertchain = MethodType(_getpeercertchain, proxy(self))
raise ValueError("attempt to listen on connected SSLSocket!")
if self._sslobj: def _getpeercertchain(self, binary_form=False):
return return self._sslobj.getpeercertchain(binary_form)
self._sslobj = SSLConnection(socket(_sock=self._sock),
def _SSLSocket_listen(self, ignored):
if self._connected:
raise ValueError("attempt to listen on connected SSLSocket!")
if self._sslobj:
return
self._sslobj = SSLConnection(socket(_sock=self._sock),
self.keyfile, self.certfile, True, self.keyfile, self.certfile, True,
self.cert_reqs, self.ssl_version, self.cert_reqs, self.ssl_version,
self.ca_certs, self.ca_certs,

View File

@ -52,15 +52,15 @@ from weakref import proxy
from err import openssl_error, InvalidSocketError from err import openssl_error, InvalidSocketError
from err import raise_ssl_error from err import raise_ssl_error
from err import SSL_ERROR_WANT_READ, SSL_ERROR_SYSCALL from err import SSL_ERROR_WANT_READ, SSL_ERROR_SYSCALL
from err import ERR_WRONG_VERSION_NUMBER, ERR_COOKIE_MISMATCH, ERR_NO_CERTS from err import ERR_WRONG_VERSION_NUMBER, ERR_COOKIE_MISMATCH, ERR_NO_SHARED_CIPHER
from err import ERR_NO_CIPHER, ERR_HANDSHAKE_TIMEOUT, ERR_PORT_UNREACHABLE from err import ERR_NO_CIPHER, ERR_HANDSHAKE_TIMEOUT, ERR_PORT_UNREACHABLE
from err import ERR_READ_TIMEOUT, ERR_WRITE_TIMEOUT from err import ERR_READ_TIMEOUT, ERR_WRITE_TIMEOUT
from err import ERR_BOTH_KEY_CERT_FILES, ERR_BOTH_KEY_CERT_FILES_SVR from err import ERR_BOTH_KEY_CERT_FILES, ERR_BOTH_KEY_CERT_FILES_SVR, ERR_NO_CERTS
from x509 import _X509, decode_cert from x509 import _X509, decode_cert
from tlock import tlock_init from tlock import tlock_init
from openssl import * from openssl import *
from util import _Rsrc, _BIO from util import _Rsrc, _BIO
_logger = getLogger(__name__) _logger = getLogger(__name__)
PROTOCOL_DTLSv1 = 256 PROTOCOL_DTLSv1 = 256
@ -235,7 +235,8 @@ class SSLContext(object):
return sorted([x.name for x in curves] if bAsName else [x.nid for x in curves]) return sorted([x.name for x in curves] if bAsName else [x.nid for x in curves])
def set_ecdh_curve(self, curve_name=None): def set_ecdh_curve(self, curve_name=None):
u''' u''' Select a curve to use for ECDH(E) key exchange or set it to auto mode
Used for server only! Used for server only!
s.a. openssl.exe ecparam -list_curves s.a. openssl.exe ecparam -list_curves
@ -246,13 +247,13 @@ class SSLContext(object):
if curve_name: if curve_name:
retVal = SSL_CTX_set_ecdh_auto(self._ctx, 0) retVal = SSL_CTX_set_ecdh_auto(self._ctx, 0)
avail_curves = get_elliptic_curves() avail_curves = get_elliptic_curves()
self._ctx.key = [curve for curve in avail_curves if curve.name == curve_name][0].to_EC_KEY() key = [curve for curve in avail_curves if curve.name == curve_name][0].to_EC_KEY()
retVal = SSL_CTX_set_tmp_ecdh(self._ctx, self._ctx.key) retVal &= SSL_CTX_set_tmp_ecdh(self._ctx, key)
else: else:
retVal = SSL_CTX_set_ecdh_auto(self._ctx, 1) retVal = SSL_CTX_set_ecdh_auto(self._ctx, 1)
return retVal return retVal
def build_cert_chain(self, flags=SSL_BUILD_CHAIN_FLAG_NO_ROOT): def build_cert_chain(self, flags=SSL_BUILD_CHAIN_FLAG_NONE):
u''' u'''
Used for server side only! Used for server side only!
@ -557,11 +558,11 @@ class SSLConnection(object):
else: else:
post_init = self._init_client(peer_address) post_init = self._init_client(peer_address)
SSL_set_options(self._ssl.value, SSL_OP_NO_QUERY_MTU)
DTLS_set_link_mtu(self._ssl.value, 1500)
if self._user_config_ssl: if self._user_config_ssl:
self._user_config_ssl(self._intf_ssl) self._user_config_ssl(self._intf_ssl)
else:
SSL_set_options(self._ssl.value, SSL_OP_NO_QUERY_MTU)
DTLS_set_link_mtu(self._ssl.value, 1500)
SSL_set_bio(self._ssl.value, self._rbio.value, self._wbio.value) SSL_set_bio(self._ssl.value, self._rbio.value, self._wbio.value)
self._rbio.disown() self._rbio.disown()
self._wbio.disown() self._wbio.disown()
@ -632,22 +633,25 @@ class SSLConnection(object):
self._ssl.raw) self._ssl.raw)
dtls_peer_address = DTLSv1_listen(self._ssl.value) dtls_peer_address = DTLSv1_listen(self._ssl.value)
except openssl_error() as err: except openssl_error() as err:
if err.ssl_error == SSL_ERROR_WANT_READ: if err.ssl_error == SSL_ERROR_WANT_READ:
# This method must be called again to forward the next datagram # This method must be called again to forward the next datagram
_logger.debug("DTLSv1_listen must be resumed") _logger.debug("DTLSv1_listen must be resumed")
return return
elif err.errqueue and err.errqueue[0][0] == ERR_WRONG_VERSION_NUMBER: elif err.errqueue and err.errqueue[0][0] == ERR_WRONG_VERSION_NUMBER:
_logger.debug("Wrong version number; aborting handshake") _logger.debug("Wrong version number; aborting handshake")
return raise
elif err.errqueue and err.errqueue[0][0] == ERR_COOKIE_MISMATCH: elif err.errqueue and err.errqueue[0][0] == ERR_COOKIE_MISMATCH:
_logger.debug("Mismatching cookie received; aborting handshake") _logger.debug("Mismatching cookie received; aborting handshake")
return raise
_logger.exception("Unexpected error in DTLSv1_listen") elif err.errqueue and err.errqueue[0][0] == ERR_NO_SHARED_CIPHER:
raise _logger.debug("No shared cipher; aborting handshake")
finally: raise
self._listening = False _logger.exception("Unexpected error in DTLSv1_listen")
self._listening_peer_address = None raise
if type(peer_address) is tuple: finally:
self._listening = False
self._listening_peer_address = None
if type(peer_address) is tuple:
_logger.debug("New local peer: %s", dtls_peer_address) _logger.debug("New local peer: %s", dtls_peer_address)
self._pending_peer_address = peer_address self._pending_peer_address = peer_address
else: else:
@ -730,35 +734,48 @@ class SSLConnection(object):
Read up to len bytes and return them. Read up to len bytes and return them.
Arguments: Arguments:
len -- maximum number of bytes to read len -- maximum number of bytes to read
Return value: Return value:
string containing read bytes string containing read bytes
""" """
return self._wrap_socket_library_call( try:
lambda: SSL_read(self._ssl.value, len, buffer), ERR_READ_TIMEOUT) return self._wrap_socket_library_call(
lambda: SSL_read(self._ssl.value, len, buffer), ERR_READ_TIMEOUT)
def write(self, data): except openssl_error() as err:
"""Write data to connection if err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
raise_ssl_error(ERR_PORT_UNREACHABLE, err)
Write data as string of bytes. raise
def write(self, data):
"""Write data to connection
Write data as string of bytes.
Arguments: Arguments:
data -- buffer containing data to be written data -- buffer containing data to be written
Return value: Return value:
number of bytes actually transmitted number of bytes actually transmitted
""" """
return self._wrap_socket_library_call( try:
lambda: SSL_write(self._ssl.value, data), ERR_WRITE_TIMEOUT) ret = self._wrap_socket_library_call(
lambda: SSL_write(self._ssl.value, data), ERR_WRITE_TIMEOUT)
def shutdown(self): except openssl_error() as err:
"""Shut down the DTLS connection if err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
raise_ssl_error(ERR_PORT_UNREACHABLE, err)
This method attemps to complete a bidirectional shutdown between raise
peers. For non-blocking sockets, it should be called repeatedly until if ret:
self._handshake_done = True
return ret
def shutdown(self):
"""Shut down the DTLS connection
This method attemps to complete a bidirectional shutdown between
peers. For non-blocking sockets, it should be called repeatedly until
it no longer raises continuation request exceptions. it no longer raises continuation request exceptions.
""" """
@ -809,18 +826,33 @@ class SSLConnection(object):
return return
if binary_form: if binary_form:
return i2d_X509(peer_cert.value) return i2d_X509(peer_cert.value)
if self._cert_reqs == CERT_NONE: if self._cert_reqs == CERT_NONE:
return {} return {}
return decode_cert(peer_cert) return decode_cert(peer_cert)
peer_certificate = getpeercert # compatibility with _ssl call interface peer_certificate = getpeercert # compatibility with _ssl call interface
def cipher(self): def getpeercertchain(self, binary_form=False):
"""Retrieve information about the current cipher try:
stack, num, certs = SSL_get_peer_cert_chain(self._ssl.value)
Return a triple consisting of cipher name, SSL protocol version defining except openssl_error():
its use, and the number of secret bits. Return None if handshaking return
peer_cert_chain = [_Rsrc(cert) for cert in certs]
ret = []
if binary_form:
ret = [i2d_X509(x.value) for x in peer_cert_chain]
elif len(peer_cert_chain):
ret = [decode_cert(x) for x in peer_cert_chain]
return ret
def cipher(self):
"""Retrieve information about the current cipher
Return a triple consisting of cipher name, SSL protocol version defining
its use, and the number of secret bits. Return None if handshaking
has not been completed. has not been completed.
""" """

View File

@ -532,18 +532,20 @@ class ThreadedEchoServer(threading.Thread):
if acc_ret: if acc_ret:
newconn, connaddr = acc_ret newconn, connaddr = acc_ret
if test_support.verbose and self.chatty: if test_support.verbose and self.chatty:
sys.stdout.write(' server: new connection from ' sys.stdout.write(' server: new connection from '
+ str(connaddr) + '\n') + str(connaddr) + '\n')
handler = self.ConnectionHandler(self, newconn) handler = self.ConnectionHandler(self, newconn)
handler.start() handler.start()
except socket.timeout: except socket.timeout:
pass pass
except KeyboardInterrupt: except ssl.SSLError:
self.stop() pass
self.sock.close() except KeyboardInterrupt:
self.stop()
def register_handler(self, add): self.sock.close()
with self.num_handlers_lock:
def register_handler(self, add):
with self.num_handlers_lock:
if add: if add:
self.num_handlers += 1 self.num_handlers += 1
else: else: