SSL standard library module wire-in
A patch implementation is provided, which augments and alters the Python standard library's ssl module to support passing of datagram sockets, in which case this package's DTLS protocol support will be activated. The ssl module's interface is intended to operate identically regardless of whether the DTLS protocol or another protocol is chosen. The following features of the ssl module are explicitly supported with datagram sockets: * socket wrapping, unwrapping, and re-wrapping * threaded UDP servers * asynchronous UDP servers (asyncore integration) * socket servers (SocketServer integration) The following modules have been added: * dtls.patch: standard library module patching code and substitution functions and methods * unit.py: this is a port of the standard library's testing module test_ssl.py for datagram sockets; all tests pass at this time; a couple of inapplicable tests have been dropped; a few other tests have been added Also note that the err module's exception raising mechanism has been augmented so as to raise exceptions of type ssl.SSLError (as opposed to dtls.err.SSLError) when instructed to do so through activation of the patching mechanism. This allows code written against the standard library module's interface to remain unchanged. In some cases, types derived from ssl.SSLError are raised.incoming
parent
4464d0bd84
commit
22083e8221
|
@ -1,11 +1,19 @@
|
||||||
# PyDTLS: datagram TLS for Python. Written by Ray Brown.
|
# PyDTLS: datagram TLS for Python. Written by Ray Brown.
|
||||||
"""PyDTLS package
|
"""PyDTLS package
|
||||||
|
|
||||||
This package exports OpenSSL's DTLS support to Python. Importing it will add
|
This package exports OpenSSL's DTLS support to Python. Calling its patch
|
||||||
the constant PROTOCOL_DTLSv1 to the Python standard library's ssl module.
|
function will add the constant PROTOCOL_DTLSv1 to the Python standard library's
|
||||||
Passing a datagram socket to that module's wrap_socket function (or
|
ssl module. Subsequently passing a datagram socket to that module's
|
||||||
instantiating its SSLSocket class with a datagram socket) will activate this
|
wrap_socket function (or instantiating its SSLSocket class with a datagram
|
||||||
module's DTLS implementation for the returned SSLSocket instance.
|
socket) will activate this module's DTLS implementation for the returned
|
||||||
|
SSLSocket instance.
|
||||||
|
|
||||||
|
Instead of or in addition to invoking the patch functionality, the
|
||||||
|
SSLConnection class can be used directly for secure communication over datagram
|
||||||
|
sockets.
|
||||||
|
|
||||||
wrap_socket's parameters and their semantics have been maintained.
|
wrap_socket's parameters and their semantics have been maintained.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from patch import do_patch
|
||||||
|
from sslconnection import SSLConnection
|
||||||
|
|
58
dtls/err.py
58
dtls/err.py
|
@ -25,7 +25,9 @@ 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_HANDSHAKE_TIMEOUT = 502
|
||||||
|
ERR_PORT_UNREACHABLE = 503
|
||||||
ERR_COOKIE_MISMATCH = 0x1408A134
|
ERR_COOKIE_MISMATCH = 0x1408A134
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,27 +37,48 @@ class SSLError(socket_error):
|
||||||
super(SSLError, self).__init__(*args)
|
super(SSLError, self).__init__(*args)
|
||||||
|
|
||||||
|
|
||||||
class OpenSSLError(SSLError):
|
|
||||||
"""This exception is raised when an error occurs in the OpenSSL library"""
|
|
||||||
def __init__(self, ssl_error, errqueue, result, func, args):
|
|
||||||
self.ssl_error = ssl_error
|
|
||||||
self.errqueue = errqueue
|
|
||||||
self.result = result
|
|
||||||
self.func = func
|
|
||||||
self.args = args
|
|
||||||
super(OpenSSLError, self).__init__(ssl_error, errqueue,
|
|
||||||
result, func, args)
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidSocketError(Exception):
|
class InvalidSocketError(Exception):
|
||||||
"""There is a problem with a socket passed to the dtls package."""
|
"""There is a problem with a socket passed to the dtls package."""
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
super(InvalidSocketError, self).__init__(*args)
|
super(InvalidSocketError, self).__init__(*args)
|
||||||
|
|
||||||
|
|
||||||
def raise_ssl_error(code):
|
def _make_opensslerror_class():
|
||||||
|
global _OpenSSLError
|
||||||
|
class __OpenSSLError(SSLError):
|
||||||
|
"""
|
||||||
|
This exception is raised when an error occurs in the OpenSSL library
|
||||||
|
"""
|
||||||
|
def __init__(self, ssl_error, errqueue, result, func, args):
|
||||||
|
self.ssl_error = ssl_error
|
||||||
|
self.errqueue = errqueue
|
||||||
|
self.result = result
|
||||||
|
self.func = func
|
||||||
|
self.args = args
|
||||||
|
SSLError.__init__(self, ssl_error, errqueue,
|
||||||
|
result, func, args)
|
||||||
|
|
||||||
|
_OpenSSLError = __OpenSSLError
|
||||||
|
|
||||||
|
_make_opensslerror_class()
|
||||||
|
|
||||||
|
def openssl_error():
|
||||||
|
"""Return the OpenSSL error type for use in exception clauses"""
|
||||||
|
return _OpenSSLError
|
||||||
|
|
||||||
|
def raise_as_ssl_module_error():
|
||||||
|
"""Exceptions raised from this module are instances of ssl.SSLError"""
|
||||||
|
import ssl
|
||||||
|
global SSLError
|
||||||
|
SSLError = ssl.SSLError
|
||||||
|
_make_opensslerror_class()
|
||||||
|
|
||||||
|
def raise_ssl_error(code, nested=None):
|
||||||
"""Raise an SSL error with the given error code"""
|
"""Raise an SSL error with the given error code"""
|
||||||
raise SSLError(str(code) + ": " + _ssl_errors[code])
|
err_string = str(code) + ": " + _ssl_errors[code]
|
||||||
|
if nested:
|
||||||
|
raise SSLError(err_string, nested)
|
||||||
|
raise SSLError(err_string)
|
||||||
|
|
||||||
_ssl_errors = {
|
_ssl_errors = {
|
||||||
ERR_NO_CERTS: "No root certificates specified for verification " + \
|
ERR_NO_CERTS: "No root certificates specified for verification " + \
|
||||||
|
@ -63,5 +86,8 @@ _ssl_errors = {
|
||||||
ERR_BOTH_KEY_CERT_FILES: "Both the key & certificate files " + \
|
ERR_BOTH_KEY_CERT_FILES: "Both the key & certificate files " + \
|
||||||
"must be specified",
|
"must be specified",
|
||||||
ERR_BOTH_KEY_CERT_FILES_SVR: "Both the key & certificate files must be " + \
|
ERR_BOTH_KEY_CERT_FILES_SVR: "Both the key & certificate files must be " + \
|
||||||
"specified for server-side operation"
|
"specified for server-side operation",
|
||||||
|
ERR_NO_CIPHER: "No cipher can be selected.",
|
||||||
|
ERR_HANDSHAKE_TIMEOUT: "The handshake operation timed out",
|
||||||
|
ERR_PORT_UNREACHABLE: "The peer address is not reachable",
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ import array
|
||||||
import socket
|
import socket
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from os import path
|
from os import path
|
||||||
from err import OpenSSLError
|
from datetime import timedelta
|
||||||
|
from err import openssl_error
|
||||||
from err import SSL_ERROR_NONE
|
from err import SSL_ERROR_NONE
|
||||||
from util import _BIO
|
from util import _BIO
|
||||||
import ctypes
|
import ctypes
|
||||||
|
@ -64,6 +65,7 @@ else:
|
||||||
#
|
#
|
||||||
BIO_NOCLOSE = 0x00
|
BIO_NOCLOSE = 0x00
|
||||||
BIO_CLOSE = 0x01
|
BIO_CLOSE = 0x01
|
||||||
|
SSLEAY_VERSION = 0
|
||||||
SSL_VERIFY_NONE = 0x00
|
SSL_VERIFY_NONE = 0x00
|
||||||
SSL_VERIFY_PEER = 0x01
|
SSL_VERIFY_PEER = 0x01
|
||||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02
|
SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02
|
||||||
|
@ -91,6 +93,8 @@ BIO_CTRL_DGRAM_SET_CONNECTED = 32
|
||||||
BIO_CTRL_DGRAM_GET_PEER = 46
|
BIO_CTRL_DGRAM_GET_PEER = 46
|
||||||
BIO_CTRL_DGRAM_SET_PEER = 44
|
BIO_CTRL_DGRAM_SET_PEER = 44
|
||||||
BIO_C_SET_NBIO = 102
|
BIO_C_SET_NBIO = 102
|
||||||
|
DTLS_CTRL_GET_TIMEOUT = 73
|
||||||
|
DTLS_CTRL_HANDLE_TIMEOUT = 74
|
||||||
DTLS_CTRL_LISTEN = 75
|
DTLS_CTRL_LISTEN = 75
|
||||||
X509_NAME_MAXLEN = 256
|
X509_NAME_MAXLEN = 256
|
||||||
GETS_MAXLEN = 2048
|
GETS_MAXLEN = 2048
|
||||||
|
@ -248,6 +252,11 @@ class X509V3_EXT_METHOD(Structure):
|
||||||
("i2d", c_int)] # remaining fields omitted
|
("i2d", c_int)] # remaining fields omitted
|
||||||
|
|
||||||
|
|
||||||
|
class TIMEVAL(Structure):
|
||||||
|
_fields_ = [("tv_sec", c_long),
|
||||||
|
("tv_usec", c_long)]
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Socket address conversions
|
# Socket address conversions
|
||||||
#
|
#
|
||||||
|
@ -366,7 +375,7 @@ def raise_ssl_error(result, func, args, ssl):
|
||||||
_logger.debug("SSL error raised: ssl_error: %d, result: %d, " +
|
_logger.debug("SSL error raised: ssl_error: %d, result: %d, " +
|
||||||
"errqueue: %s, func_name: %s",
|
"errqueue: %s, func_name: %s",
|
||||||
ssl_error, result, errqueue, func.func_name)
|
ssl_error, result, errqueue, func.func_name)
|
||||||
raise OpenSSLError(ssl_error, errqueue, result, func, args)
|
raise openssl_error()(ssl_error, errqueue, result, func, args)
|
||||||
|
|
||||||
def find_ssl_arg(args):
|
def find_ssl_arg(args):
|
||||||
for arg in args:
|
for arg in args:
|
||||||
|
@ -424,6 +433,7 @@ def _make_function(name, lib, args, export=True, errcheck="default"):
|
||||||
_subst = {c_long_parm: c_long}
|
_subst = {c_long_parm: c_long}
|
||||||
_sigs = {}
|
_sigs = {}
|
||||||
__all__ = ["BIO_NOCLOSE", "BIO_CLOSE",
|
__all__ = ["BIO_NOCLOSE", "BIO_CLOSE",
|
||||||
|
"SSLEAY_VERSION",
|
||||||
"SSL_VERIFY_NONE", "SSL_VERIFY_PEER",
|
"SSL_VERIFY_NONE", "SSL_VERIFY_PEER",
|
||||||
"SSL_VERIFY_FAIL_IF_NO_PEER_CERT", "SSL_VERIFY_CLIENT_ONCE",
|
"SSL_VERIFY_FAIL_IF_NO_PEER_CERT", "SSL_VERIFY_CLIENT_ONCE",
|
||||||
"SSL_SESS_CACHE_OFF", "SSL_SESS_CACHE_CLIENT",
|
"SSL_SESS_CACHE_OFF", "SSL_SESS_CACHE_CLIENT",
|
||||||
|
@ -432,6 +442,7 @@ __all__ = ["BIO_NOCLOSE", "BIO_CLOSE",
|
||||||
"SSL_SESS_CACHE_NO_INTERNAL_STORE", "SSL_SESS_CACHE_NO_INTERNAL",
|
"SSL_SESS_CACHE_NO_INTERNAL_STORE", "SSL_SESS_CACHE_NO_INTERNAL",
|
||||||
"SSL_FILE_TYPE_PEM",
|
"SSL_FILE_TYPE_PEM",
|
||||||
"GEN_DIRNAME", "NID_subject_alt_name",
|
"GEN_DIRNAME", "NID_subject_alt_name",
|
||||||
|
"DTLSv1_get_timeout", "DTLSv1_handle_timeout",
|
||||||
"DTLSv1_listen",
|
"DTLSv1_listen",
|
||||||
"BIO_gets", "BIO_read", "BIO_get_mem_data",
|
"BIO_gets", "BIO_read", "BIO_get_mem_data",
|
||||||
"BIO_dgram_set_connected",
|
"BIO_dgram_set_connected",
|
||||||
|
@ -450,6 +461,8 @@ __all__ = ["BIO_NOCLOSE", "BIO_CLOSE",
|
||||||
map(lambda x: _make_function(*x), (
|
map(lambda x: _make_function(*x), (
|
||||||
("SSL_library_init", libssl, ((c_int, "ret"),)),
|
("SSL_library_init", libssl, ((c_int, "ret"),)),
|
||||||
("SSL_load_error_strings", libssl, ((None, "ret"),)),
|
("SSL_load_error_strings", libssl, ((None, "ret"),)),
|
||||||
|
("SSLeay", libcrypto, ((c_long_parm, "ret"),)),
|
||||||
|
("SSLeay_version", libcrypto, ((c_char_p, "ret"), (c_int, "t"))),
|
||||||
("DTLSv1_server_method", libssl, ((DTLSv1Method, "ret"),)),
|
("DTLSv1_server_method", libssl, ((DTLSv1Method, "ret"),)),
|
||||||
("DTLSv1_client_method", libssl, ((DTLSv1Method, "ret"),)),
|
("DTLSv1_client_method", libssl, ((DTLSv1Method, "ret"),)),
|
||||||
("SSL_CTX_new", libssl, ((SSLCTX, "ret"), (DTLSv1Method, "meth"))),
|
("SSL_CTX_new", libssl, ((SSLCTX, "ret"), (DTLSv1Method, "meth"))),
|
||||||
|
@ -514,6 +527,7 @@ map(lambda x: _make_function(*x), (
|
||||||
((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,
|
||||||
((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_pending", libssl, ((c_int, "ret"), (SSL, "ssl")), True, None),
|
||||||
("SSL_shutdown", libssl, ((c_int, "ret"), (SSL, "ssl"))),
|
("SSL_shutdown", libssl, ((c_int, "ret"), (SSL, "ssl"))),
|
||||||
("SSL_set_read_ahead", libssl,
|
("SSL_set_read_ahead", libssl,
|
||||||
((None, "ret"), (SSL, "ssl"), (c_int, "yes"))),
|
((None, "ret"), (SSL, "ssl"), (c_int, "yes"))),
|
||||||
|
@ -630,6 +644,28 @@ def BIO_dgram_set_peer(bio, peer_address):
|
||||||
def BIO_set_nbio(bio, n):
|
def BIO_set_nbio(bio, n):
|
||||||
_BIO_ctrl(bio, BIO_C_SET_NBIO, 1 if n else 0, None)
|
_BIO_ctrl(bio, BIO_C_SET_NBIO, 1 if n else 0, None)
|
||||||
|
|
||||||
|
def DTLSv1_get_timeout(ssl):
|
||||||
|
tv = TIMEVAL()
|
||||||
|
ret = _SSL_ctrl(ssl, DTLS_CTRL_GET_TIMEOUT, 0, byref(tv))
|
||||||
|
if ret != 1:
|
||||||
|
return
|
||||||
|
return timedelta(seconds=tv.tv_sec, microseconds=tv.tv_usec)
|
||||||
|
|
||||||
|
def DTLSv1_handle_timeout(ssl):
|
||||||
|
ret = _SSL_ctrl(ssl, DTLS_CTRL_HANDLE_TIMEOUT, 0, None)
|
||||||
|
if ret == 0:
|
||||||
|
# It was too early to call: no timer had yet expired
|
||||||
|
return False
|
||||||
|
if ret == 1:
|
||||||
|
# Buffered messages were retransmitted
|
||||||
|
return True
|
||||||
|
# There was an error: either too many timeouts have occurred or a
|
||||||
|
# retransmission failed
|
||||||
|
assert ret < 0
|
||||||
|
if ret > 0:
|
||||||
|
ret = -10
|
||||||
|
errcheck_p(ret, _SSL_ctrl, (ssl, DTLS_CTRL_HANDLE_TIMEOUT, 0, None))
|
||||||
|
|
||||||
def DTLSv1_listen(ssl):
|
def DTLSv1_listen(ssl):
|
||||||
su = sockaddr_u()
|
su = sockaddr_u()
|
||||||
ret = _SSL_ctrl(ssl, DTLS_CTRL_LISTEN, 0, byref(su))
|
ret = _SSL_ctrl(ssl, DTLS_CTRL_LISTEN, 0, byref(su))
|
||||||
|
@ -642,7 +678,10 @@ def SSL_read(ssl, length):
|
||||||
return buf.raw[:res_len]
|
return buf.raw[:res_len]
|
||||||
|
|
||||||
def SSL_write(ssl, data):
|
def SSL_write(ssl, data):
|
||||||
str_data = str(data)
|
if hasattr(data, "tobytes") and callable(data.tobytes):
|
||||||
|
str_data = data.tobytes()
|
||||||
|
else:
|
||||||
|
str_data = str(data)
|
||||||
return _SSL_write(ssl, str_data, len(str_data))
|
return _SSL_write(ssl, str_data, len(str_data))
|
||||||
|
|
||||||
def OBJ_obj2txt(asn1_object, no_name):
|
def OBJ_obj2txt(asn1_object, no_name):
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
# Patch: patching of the Python stadard library's ssl module for transparent
|
||||||
|
# use of datagram sockets. Written by Ray Brown.
|
||||||
|
"""Patch
|
||||||
|
|
||||||
|
This module is used to patch the Python standard library's ssl module. Patching
|
||||||
|
has the following effects:
|
||||||
|
|
||||||
|
* The constant PROTOCOL_DTLSv1 is added at ssl module level
|
||||||
|
* DTLSv1's protocol name is added to the ssl module's id-to-name dictionary
|
||||||
|
* The constants DTLS_OPENSSL_VERSION* are added at the ssl module level
|
||||||
|
* Instntiation of ssl.SSLSocket with sock.type == socket.SOCK_DGRAM is
|
||||||
|
supported and leads to substitution of this module's DTLS code paths for
|
||||||
|
that SSLSocket instance
|
||||||
|
* Direct instantiation of SSLSocket as well as instantiation through
|
||||||
|
ssl.wrap_socket are supported
|
||||||
|
* Invocation of the function get_server_certificate with a value of
|
||||||
|
PROTOCOL_DTLSv1 for the parameter ssl_version is supported
|
||||||
|
"""
|
||||||
|
|
||||||
|
from socket import SOCK_DGRAM, socket, _delegate_methods, error as socket_error
|
||||||
|
from socket import AF_INET, SOCK_DGRAM
|
||||||
|
from sslconnection import SSLConnection, PROTOCOL_DTLSv1, CERT_NONE
|
||||||
|
from sslconnection import DTLS_OPENSSL_VERSION_NUMBER, DTLS_OPENSSL_VERSION
|
||||||
|
from sslconnection import DTLS_OPENSSL_VERSION_INFO
|
||||||
|
from err import raise_as_ssl_module_error
|
||||||
|
from types import MethodType
|
||||||
|
from weakref import proxy
|
||||||
|
import errno
|
||||||
|
|
||||||
|
def do_patch():
|
||||||
|
import ssl as _ssl # import to be avoided if ssl module is never patched
|
||||||
|
global _orig_SSLSocket_init, _orig_get_server_certificate
|
||||||
|
global ssl
|
||||||
|
ssl = _ssl
|
||||||
|
ssl.PROTOCOL_DTLSv1 = PROTOCOL_DTLSv1
|
||||||
|
ssl._PROTOCOL_NAMES[PROTOCOL_DTLSv1] = "DTLSv1"
|
||||||
|
ssl.DTLS_OPENSSL_VERSION_NUMBER = DTLS_OPENSSL_VERSION_NUMBER
|
||||||
|
ssl.DTLS_OPENSSL_VERSION = DTLS_OPENSSL_VERSION
|
||||||
|
ssl.DTLS_OPENSSL_VERSION_INFO = DTLS_OPENSSL_VERSION_INFO
|
||||||
|
_orig_SSLSocket_init = ssl.SSLSocket.__init__
|
||||||
|
_orig_get_server_certificate = ssl.get_server_certificate
|
||||||
|
ssl.SSLSocket.__init__ = _SSLSocket_init
|
||||||
|
ssl.get_server_certificate = _get_server_certificate
|
||||||
|
raise_as_ssl_module_error()
|
||||||
|
|
||||||
|
PROTOCOL_SSLv3 = 1
|
||||||
|
PROTOCOL_SSLv23 = 2
|
||||||
|
|
||||||
|
def _get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None):
|
||||||
|
"""Retrieve a server certificate
|
||||||
|
|
||||||
|
Retrieve the certificate from the server at the specified address,
|
||||||
|
and return it as a PEM-encoded string.
|
||||||
|
If 'ca_certs' is specified, validate the server cert against it.
|
||||||
|
If 'ssl_version' is specified, use it in the connection attempt.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if ssl_version != PROTOCOL_DTLSv1:
|
||||||
|
return _orig_get_server_certificate(addr, ssl_version, ca_certs)
|
||||||
|
|
||||||
|
host, port = addr
|
||||||
|
if (ca_certs is not None):
|
||||||
|
cert_reqs = ssl.CERT_REQUIRED
|
||||||
|
else:
|
||||||
|
cert_reqs = ssl.CERT_NONE
|
||||||
|
s = ssl.wrap_socket(socket(AF_INET, SOCK_DGRAM),
|
||||||
|
ssl_version=ssl_version,
|
||||||
|
cert_reqs=cert_reqs, ca_certs=ca_certs)
|
||||||
|
s.connect(addr)
|
||||||
|
dercert = s.getpeercert(True)
|
||||||
|
s.close()
|
||||||
|
return ssl.DER_cert_to_PEM_cert(dercert)
|
||||||
|
|
||||||
|
def _SSLSocket_init(self, sock, keyfile=None, certfile=None,
|
||||||
|
server_side=False, cert_reqs=CERT_NONE,
|
||||||
|
ssl_version=PROTOCOL_SSLv23, ca_certs=None,
|
||||||
|
do_handshake_on_connect=True,
|
||||||
|
suppress_ragged_eofs=True, ciphers=None):
|
||||||
|
is_connection = is_datagram = False
|
||||||
|
if isinstance(sock, SSLConnection):
|
||||||
|
is_connection = True
|
||||||
|
elif hasattr(sock, "type") and sock.type == SOCK_DGRAM:
|
||||||
|
is_datagram = True
|
||||||
|
if not is_connection and not is_datagram:
|
||||||
|
# Non-DTLS code path
|
||||||
|
return _orig_SSLSocket_init(self, sock, keyfile, certfile,
|
||||||
|
server_side, cert_reqs,
|
||||||
|
ssl_version, ca_certs,
|
||||||
|
do_handshake_on_connect,
|
||||||
|
suppress_ragged_eofs, ciphers)
|
||||||
|
# DTLS code paths: datagram socket and newly accepted DTLS connection
|
||||||
|
if is_datagram:
|
||||||
|
socket.__init__(self, _sock=sock._sock)
|
||||||
|
else:
|
||||||
|
socket.__init__(self, _sock=sock.get_socket(True)._sock)
|
||||||
|
# Copy instance initialization from SSLSocket class
|
||||||
|
for attr in _delegate_methods:
|
||||||
|
try:
|
||||||
|
delattr(self, attr)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if certfile and not keyfile:
|
||||||
|
keyfile = certfile
|
||||||
|
if is_datagram:
|
||||||
|
# see if it's connected
|
||||||
|
try:
|
||||||
|
socket.getpeername(self)
|
||||||
|
except socket_error, e:
|
||||||
|
if e.errno != errno.ENOTCONN:
|
||||||
|
raise
|
||||||
|
# no, no connection yet
|
||||||
|
self._connected = False
|
||||||
|
self._sslobj = None
|
||||||
|
else:
|
||||||
|
# yes, create the SSL object
|
||||||
|
self._connected = True
|
||||||
|
self._sslobj = SSLConnection(sock, keyfile, certfile,
|
||||||
|
server_side, cert_reqs,
|
||||||
|
ssl_version, ca_certs,
|
||||||
|
do_handshake_on_connect,
|
||||||
|
suppress_ragged_eofs, ciphers)
|
||||||
|
else:
|
||||||
|
self._sslobj = sock
|
||||||
|
|
||||||
|
self.keyfile = keyfile
|
||||||
|
self.certfile = certfile
|
||||||
|
self.cert_reqs = cert_reqs
|
||||||
|
self.ssl_version = ssl_version
|
||||||
|
self.ca_certs = ca_certs
|
||||||
|
self.ciphers = ciphers
|
||||||
|
self.do_handshake_on_connect = do_handshake_on_connect
|
||||||
|
self.suppress_ragged_eofs = suppress_ragged_eofs
|
||||||
|
self._makefile_refs = 0
|
||||||
|
|
||||||
|
# Perform method substitution and addition (without reference cycle)
|
||||||
|
self._real_connect = MethodType(_SSLSocket_real_connect, proxy(self))
|
||||||
|
self.listen = MethodType(_SSLSocket_listen, proxy(self))
|
||||||
|
self.accept = MethodType(_SSLSocket_accept, proxy(self))
|
||||||
|
self.get_timeout = MethodType(_SSLSocket_get_timeout, proxy(self))
|
||||||
|
self.handle_timeout = MethodType(_SSLSocket_handle_timeout, proxy(self))
|
||||||
|
|
||||||
|
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.cert_reqs, self.ssl_version,
|
||||||
|
self.ca_certs,
|
||||||
|
self.do_handshake_on_connect,
|
||||||
|
self.suppress_ragged_eofs, self.ciphers)
|
||||||
|
|
||||||
|
def _SSLSocket_accept(self):
|
||||||
|
if self._connected:
|
||||||
|
raise ValueError("attempt to accept on connected SSLSocket!")
|
||||||
|
if not self._sslobj:
|
||||||
|
raise ValueError("attempt to accept on SSLSocket prior to listen!")
|
||||||
|
acc_ret = self._sslobj.accept()
|
||||||
|
if not acc_ret:
|
||||||
|
return
|
||||||
|
new_conn, addr = acc_ret
|
||||||
|
new_ssl_sock = ssl.SSLSocket(new_conn, self.keyfile, self.certfile, True,
|
||||||
|
self.cert_reqs, self.ssl_version,
|
||||||
|
self.ca_certs,
|
||||||
|
self.do_handshake_on_connect,
|
||||||
|
self.suppress_ragged_eofs, self.ciphers)
|
||||||
|
return new_ssl_sock, addr
|
||||||
|
|
||||||
|
def _SSLSocket_real_connect(self, addr, return_errno):
|
||||||
|
if self._connected:
|
||||||
|
raise ValueError("attempt to connect already-connected SSLSocket!")
|
||||||
|
self._sslobj = SSLConnection(socket(_sock=self._sock),
|
||||||
|
self.keyfile, self.certfile, False,
|
||||||
|
self.cert_reqs, self.ssl_version,
|
||||||
|
self.ca_certs,
|
||||||
|
self.do_handshake_on_connect,
|
||||||
|
self.suppress_ragged_eofs, self.ciphers)
|
||||||
|
try:
|
||||||
|
self._sslobj.connect(addr)
|
||||||
|
except socket_error as e:
|
||||||
|
if return_errno:
|
||||||
|
return e.errno
|
||||||
|
else:
|
||||||
|
self._sslobj = None
|
||||||
|
raise e
|
||||||
|
self._connected = True
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
do_patch()
|
||||||
|
|
||||||
|
def _SSLSocket_get_timeout(self):
|
||||||
|
return self._sslobj.get_timeout()
|
||||||
|
|
||||||
|
def _SSLSocket_handle_timeout(self):
|
||||||
|
return self._sslobj.handle_timeout()
|
|
@ -29,9 +29,11 @@ import hmac
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from os import urandom
|
from os import urandom
|
||||||
from weakref import proxy
|
from weakref import proxy
|
||||||
from err import OpenSSLError, 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, ERR_COOKIE_MISMATCH, ERR_NO_CERTS
|
from err import SSL_ERROR_WANT_READ, SSL_ERROR_SYSCALL
|
||||||
|
from err import ERR_COOKIE_MISMATCH, ERR_NO_CERTS
|
||||||
|
from err import ERR_NO_CIPHER, ERR_HANDSHAKE_TIMEOUT, ERR_PORT_UNREACHABLE
|
||||||
from x509 import _X509, decode_cert
|
from x509 import _X509, decode_cert
|
||||||
from openssl import *
|
from openssl import *
|
||||||
from util import _Rsrc, _BIO
|
from util import _Rsrc, _BIO
|
||||||
|
@ -48,6 +50,14 @@ CERT_REQUIRED = 2
|
||||||
#
|
#
|
||||||
SSL_library_init()
|
SSL_library_init()
|
||||||
SSL_load_error_strings()
|
SSL_load_error_strings()
|
||||||
|
DTLS_OPENSSL_VERSION_NUMBER = SSLeay()
|
||||||
|
DTLS_OPENSSL_VERSION = SSLeay_version(SSLEAY_VERSION)
|
||||||
|
DTLS_OPENSSL_VERSION_INFO = (
|
||||||
|
DTLS_OPENSSL_VERSION_NUMBER >> 28 & 0xFF, # major
|
||||||
|
DTLS_OPENSSL_VERSION_NUMBER >> 20 & 0xFF, # minor
|
||||||
|
DTLS_OPENSSL_VERSION_NUMBER >> 12 & 0xFF, # fix
|
||||||
|
DTLS_OPENSSL_VERSION_NUMBER >> 4 & 0xFF, # patch
|
||||||
|
DTLS_OPENSSL_VERSION_NUMBER & 0xF) # status
|
||||||
|
|
||||||
|
|
||||||
class _CTX(_Rsrc):
|
class _CTX(_Rsrc):
|
||||||
|
@ -98,9 +108,11 @@ class SSLConnection(object):
|
||||||
|
|
||||||
_rnd_key = urandom(16)
|
_rnd_key = urandom(16)
|
||||||
|
|
||||||
def _init_server(self):
|
def _init_server(self, peer_address):
|
||||||
if self._sock.type != socket.SOCK_DGRAM:
|
if self._sock.type != socket.SOCK_DGRAM:
|
||||||
raise InvalidSocketError("sock must be of type SOCK_DGRAM")
|
raise InvalidSocketError("sock must be of type SOCK_DGRAM")
|
||||||
|
if peer_address:
|
||||||
|
raise InvalidSocketError("server-side socket must be unconnected")
|
||||||
|
|
||||||
from demux import UDPDemux
|
from demux import UDPDemux
|
||||||
self._udp_demux = UDPDemux(self._sock)
|
self._udp_demux = UDPDemux(self._sock)
|
||||||
|
@ -127,7 +139,7 @@ class SSLConnection(object):
|
||||||
self._ssl = _SSL(SSL_new(self._ctx.value))
|
self._ssl = _SSL(SSL_new(self._ctx.value))
|
||||||
SSL_set_accept_state(self._ssl.value)
|
SSL_set_accept_state(self._ssl.value)
|
||||||
|
|
||||||
def _init_client(self):
|
def _init_client(self, peer_address):
|
||||||
if self._sock.type != socket.SOCK_DGRAM:
|
if self._sock.type != socket.SOCK_DGRAM:
|
||||||
raise InvalidSocketError("sock must be of type SOCK_DGRAM")
|
raise InvalidSocketError("sock must be of type SOCK_DGRAM")
|
||||||
|
|
||||||
|
@ -141,6 +153,8 @@ class SSLConnection(object):
|
||||||
self._config_ssl_ctx(verify_mode)
|
self._config_ssl_ctx(verify_mode)
|
||||||
self._ssl = _SSL(SSL_new(self._ctx.value))
|
self._ssl = _SSL(SSL_new(self._ctx.value))
|
||||||
SSL_set_connect_state(self._ssl.value)
|
SSL_set_connect_state(self._ssl.value)
|
||||||
|
if peer_address:
|
||||||
|
return lambda: self.connect(peer_address)
|
||||||
|
|
||||||
def _config_ssl_ctx(self, verify_mode):
|
def _config_ssl_ctx(self, verify_mode):
|
||||||
SSL_CTX_set_verify(self._ctx.value, verify_mode)
|
SSL_CTX_set_verify(self._ctx.value, verify_mode)
|
||||||
|
@ -153,7 +167,10 @@ class SSLConnection(object):
|
||||||
if self._ca_certs:
|
if self._ca_certs:
|
||||||
SSL_CTX_load_verify_locations(self._ctx.value, self._ca_certs, None)
|
SSL_CTX_load_verify_locations(self._ctx.value, self._ca_certs, None)
|
||||||
if self._ciphers:
|
if self._ciphers:
|
||||||
SSL_CTX_set_cipher_list(self._ctx.value, self._ciphers)
|
try:
|
||||||
|
SSL_CTX_set_cipher_list(self._ctx.value, self._ciphers)
|
||||||
|
except openssl_error() as err:
|
||||||
|
raise_ssl_error(ERR_NO_CIPHER, err)
|
||||||
|
|
||||||
def _copy_server(self):
|
def _copy_server(self):
|
||||||
source = self._sock
|
source = self._sock
|
||||||
|
@ -171,6 +188,7 @@ class SSLConnection(object):
|
||||||
new_source_rbio = _BIO(BIO_new_dgram(source._rsock.fileno(),
|
new_source_rbio = _BIO(BIO_new_dgram(source._rsock.fileno(),
|
||||||
BIO_NOCLOSE))
|
BIO_NOCLOSE))
|
||||||
source._ssl = _SSL(SSL_new(self._ctx.value))
|
source._ssl = _SSL(SSL_new(self._ctx.value))
|
||||||
|
SSL_set_accept_state(source._ssl.value)
|
||||||
source._rbio = new_source_rbio
|
source._rbio = new_source_rbio
|
||||||
source._wbio = new_source_wbio
|
source._wbio = new_source_wbio
|
||||||
SSL_set_bio(source._ssl.value,
|
SSL_set_bio(source._ssl.value,
|
||||||
|
@ -179,6 +197,20 @@ class SSLConnection(object):
|
||||||
new_source_rbio.disown()
|
new_source_rbio.disown()
|
||||||
new_source_wbio.disown()
|
new_source_wbio.disown()
|
||||||
|
|
||||||
|
def _reconnect_unwrapped(self):
|
||||||
|
source = self._sock
|
||||||
|
self._sock = source._wsock
|
||||||
|
self._udp_demux = source._demux
|
||||||
|
self._rsock = source._rsock
|
||||||
|
self._ctx = source._ctx
|
||||||
|
self._wbio = _BIO(BIO_new_dgram(self._sock.fileno(), BIO_NOCLOSE))
|
||||||
|
self._rbio = _BIO(BIO_new_dgram(self._rsock.fileno(), BIO_NOCLOSE))
|
||||||
|
BIO_dgram_set_peer(self._wbio.value, source._peer_address)
|
||||||
|
self._ssl = _SSL(SSL_new(self._ctx.value))
|
||||||
|
SSL_set_accept_state(self._ssl.value)
|
||||||
|
if self._do_handshake_on_connect:
|
||||||
|
return lambda: self.do_handshake()
|
||||||
|
|
||||||
def _check_nbio(self):
|
def _check_nbio(self):
|
||||||
BIO_set_nbio(self._wbio.value, self._sock.gettimeout() is not None)
|
BIO_set_nbio(self._wbio.value, self._sock.gettimeout() is not None)
|
||||||
if self._wbio is not self._rbio:
|
if self._wbio is not self._rbio:
|
||||||
|
@ -234,15 +266,24 @@ class SSLConnection(object):
|
||||||
self._handshake_done = False
|
self._handshake_done = False
|
||||||
|
|
||||||
if isinstance(sock, SSLConnection):
|
if isinstance(sock, SSLConnection):
|
||||||
self._copy_server()
|
post_init = self._copy_server()
|
||||||
elif server_side:
|
elif isinstance(sock, _UnwrappedSocket):
|
||||||
self._init_server()
|
post_init = self._reconnect_unwrapped()
|
||||||
else:
|
else:
|
||||||
self._init_client()
|
try:
|
||||||
|
peer_address = sock.getpeername()
|
||||||
|
except socket.error:
|
||||||
|
peer_address = None
|
||||||
|
if server_side:
|
||||||
|
post_init = self._init_server(peer_address)
|
||||||
|
else:
|
||||||
|
post_init = self._init_client(peer_address)
|
||||||
|
|
||||||
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()
|
||||||
|
if post_init:
|
||||||
|
post_init()
|
||||||
|
|
||||||
def get_socket(self, inbound):
|
def get_socket(self, inbound):
|
||||||
"""Retrieve a socket used by this connection
|
"""Retrieve a socket used by this connection
|
||||||
|
@ -305,7 +346,7 @@ class SSLConnection(object):
|
||||||
_logger.debug("Invoking DTLSv1_listen for ssl: %d",
|
_logger.debug("Invoking DTLSv1_listen for ssl: %d",
|
||||||
self._ssl.value._as_parameter)
|
self._ssl.value._as_parameter)
|
||||||
dtls_peer_address = DTLSv1_listen(self._ssl.value)
|
dtls_peer_address = DTLSv1_listen(self._ssl.value)
|
||||||
except OpenSSLError 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")
|
||||||
|
@ -345,6 +386,7 @@ class SSLConnection(object):
|
||||||
self._cert_reqs, PROTOCOL_DTLSv1,
|
self._cert_reqs, PROTOCOL_DTLSv1,
|
||||||
self._ca_certs, self._do_handshake_on_connect,
|
self._ca_certs, self._do_handshake_on_connect,
|
||||||
self._suppress_ragged_eofs, self._ciphers)
|
self._suppress_ragged_eofs, self._ciphers)
|
||||||
|
new_peer = self._pending_peer_address
|
||||||
self._pending_peer_address = None
|
self._pending_peer_address = None
|
||||||
if self._do_handshake_on_connect:
|
if self._do_handshake_on_connect:
|
||||||
# Note that since that connection's socket was just created in its
|
# Note that since that connection's socket was just created in its
|
||||||
|
@ -354,7 +396,7 @@ class SSLConnection(object):
|
||||||
# will hang in this call
|
# will hang in this call
|
||||||
new_conn.do_handshake()
|
new_conn.do_handshake()
|
||||||
_logger.debug("Accept returning new connection for new peer")
|
_logger.debug("Accept returning new connection for new peer")
|
||||||
return new_conn
|
return new_conn, new_peer
|
||||||
|
|
||||||
def connect(self, peer_address):
|
def connect(self, peer_address):
|
||||||
"""Client-side UDP connection establishment
|
"""Client-side UDP connection establishment
|
||||||
|
@ -368,6 +410,7 @@ class SSLConnection(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._sock.connect(peer_address)
|
self._sock.connect(peer_address)
|
||||||
|
peer_address = self._sock.getpeername() # substituted host addrinfo
|
||||||
BIO_dgram_set_connected(self._wbio.value, peer_address)
|
BIO_dgram_set_connected(self._wbio.value, peer_address)
|
||||||
assert self._wbio is self._rbio
|
assert self._wbio is self._rbio
|
||||||
if self._do_handshake_on_connect:
|
if self._do_handshake_on_connect:
|
||||||
|
@ -382,7 +425,15 @@ class SSLConnection(object):
|
||||||
|
|
||||||
_logger.debug("Initiating handshake...")
|
_logger.debug("Initiating handshake...")
|
||||||
self._check_nbio()
|
self._check_nbio()
|
||||||
SSL_do_handshake(self._ssl.value)
|
try:
|
||||||
|
SSL_do_handshake(self._ssl.value)
|
||||||
|
except openssl_error() as err:
|
||||||
|
if err.ssl_error == SSL_ERROR_WANT_READ and \
|
||||||
|
self.get_socket(True).gettimeout():
|
||||||
|
raise_ssl_error(ERR_HANDSHAKE_TIMEOUT, err)
|
||||||
|
elif err.ssl_error == SSL_ERROR_SYSCALL and err.result == -1:
|
||||||
|
raise_ssl_error(ERR_PORT_UNREACHABLE, err)
|
||||||
|
raise
|
||||||
self._handshake_done = True
|
self._handshake_done = True
|
||||||
_logger.debug("...completed handshake")
|
_logger.debug("...completed handshake")
|
||||||
|
|
||||||
|
@ -423,19 +474,30 @@ class SSLConnection(object):
|
||||||
it no longer raises continuation request exceptions.
|
it no longer raises continuation request exceptions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if hasattr(self, "_listening"):
|
||||||
|
# Listening server-side sockets cannot be shut down
|
||||||
|
return
|
||||||
|
|
||||||
self._check_nbio()
|
self._check_nbio()
|
||||||
try:
|
try:
|
||||||
SSL_shutdown(self._ssl.value)
|
SSL_shutdown(self._ssl.value)
|
||||||
except OpenSSLError as err:
|
except openssl_error() as err:
|
||||||
if err.result == 0:
|
if err.result == 0:
|
||||||
# close-notify alert was just sent; wait for same from peer
|
# close-notify alert was just sent; wait for same from peer
|
||||||
# Note: while it might seem wise to suppress further read-aheads
|
# Note: while it might seem wise to suppress further read-aheads
|
||||||
# with SSL_set_read_ahead here, doing so causes a shutdown
|
# with SSL_set_read_ahead here, doing so causes a shutdown
|
||||||
# failure (ret: -1, SSL_ERROR_SYSCALL) on the DTLS shutdown
|
# failure (ret: -1, SSL_ERROR_SYSCALL) on the DTLS shutdown
|
||||||
# initiator side.
|
# initiator side. And test_starttls does pass.
|
||||||
SSL_shutdown(self._ssl.value)
|
SSL_shutdown(self._ssl.value)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
if hasattr(self, "_udp_demux"):
|
||||||
|
# Return wrapped connected server socket (non-listening)
|
||||||
|
return _UnwrappedSocket(self._sock, self._rsock, self._udp_demux,
|
||||||
|
self._ctx,
|
||||||
|
BIO_dgram_get_peer(self._wbio.value))
|
||||||
|
# Return unwrapped client-side socket
|
||||||
|
return self._sock
|
||||||
|
|
||||||
def getpeercert(self, binary_form=False):
|
def getpeercert(self, binary_form=False):
|
||||||
"""Retrieve the peer's certificate
|
"""Retrieve the peer's certificate
|
||||||
|
@ -453,7 +515,7 @@ class SSLConnection(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
peer_cert = _X509(SSL_get_peer_certificate(self._ssl.value))
|
peer_cert = _X509(SSL_get_peer_certificate(self._ssl.value))
|
||||||
except OpenSSLError:
|
except openssl_error():
|
||||||
return
|
return
|
||||||
|
|
||||||
if binary_form:
|
if binary_form:
|
||||||
|
@ -462,6 +524,8 @@ class SSLConnection(object):
|
||||||
return {}
|
return {}
|
||||||
return decode_cert(peer_cert)
|
return decode_cert(peer_cert)
|
||||||
|
|
||||||
|
peer_certificate = getpeercert # compatibility with _ssl call interface
|
||||||
|
|
||||||
def cipher(self):
|
def cipher(self):
|
||||||
"""Retrieve information about the current cipher
|
"""Retrieve information about the current cipher
|
||||||
|
|
||||||
|
@ -478,3 +542,95 @@ class SSLConnection(object):
|
||||||
cipher_version = SSL_CIPHER_get_version(current_cipher)
|
cipher_version = SSL_CIPHER_get_version(current_cipher)
|
||||||
cipher_bits = SSL_CIPHER_get_bits(current_cipher)
|
cipher_bits = SSL_CIPHER_get_bits(current_cipher)
|
||||||
return cipher_name, cipher_version, cipher_bits
|
return cipher_name, cipher_version, cipher_bits
|
||||||
|
|
||||||
|
def pending(self):
|
||||||
|
"""Retrieve number of buffered bytes
|
||||||
|
|
||||||
|
Return the number of bytes that have been read from the socket and
|
||||||
|
buffered by this connection. Return 0 if no bytes have been buffered.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return SSL_pending(self._ssl.value)
|
||||||
|
|
||||||
|
def get_timeout(self):
|
||||||
|
"""Retrieve the retransmission timedelta
|
||||||
|
|
||||||
|
Since datagrams are subject to packet loss, DTLS will perform
|
||||||
|
packet retransmission if a response is not received after a certain
|
||||||
|
time interval during the handshaking phase. When using non-blocking
|
||||||
|
sockets, the application must call back after that time interval to
|
||||||
|
allow for the retransmission to occur. This method returns the
|
||||||
|
timedelta after which to perform the call to handle_timeout, or None
|
||||||
|
if no such callback is needed given the current handshake state.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return DTLSv1_get_timeout(self._ssl.value)
|
||||||
|
|
||||||
|
def handle_timeout(self):
|
||||||
|
"""Perform datagram retransmission, if required
|
||||||
|
|
||||||
|
This method should be called after the timedelta retrieved from
|
||||||
|
get_timeout has expired, and no datagrams were received in the
|
||||||
|
meantime. If datagrams were received, a new timeout needs to be
|
||||||
|
requested.
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
True -- retransmissions were performed successfully
|
||||||
|
False -- a timeout was not in effect or had not yet expired
|
||||||
|
|
||||||
|
Exceptions:
|
||||||
|
Raised when retransmissions fail or too many timeouts occur.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return DTLSv1_handle_timeout(self._ssl.value)
|
||||||
|
|
||||||
|
|
||||||
|
class _UnwrappedSocket(socket.socket):
|
||||||
|
"""Unwrapped server-side socket
|
||||||
|
|
||||||
|
Depending on UDP demux implementation, there may not be single socket
|
||||||
|
that can be used for both reading and writing to the client socket with
|
||||||
|
which it is associated. An object of this type is therefore returned from
|
||||||
|
the SSLSocket's unwrap method to allow for unencrypted communication over
|
||||||
|
the established channels, including the demux.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, wsock, rsock, demux, ctx, peer_address):
|
||||||
|
socket.socket.__init__(self, _sock=rsock._sock)
|
||||||
|
for attr in "send", "sendto", "sendall":
|
||||||
|
try:
|
||||||
|
delattr(self, attr)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
self._wsock = wsock
|
||||||
|
self._rsock = rsock # continue to reference to hold in demux map
|
||||||
|
self._demux = demux
|
||||||
|
self._ctx = ctx
|
||||||
|
self._peer_address = peer_address
|
||||||
|
|
||||||
|
def send(self, data, flags=0):
|
||||||
|
__doc__ = self._wsock.send.__doc__
|
||||||
|
return self._wsock.sendto(data, flags, self._peer_address)
|
||||||
|
|
||||||
|
def sendto(self, data, flags_or_addr, addr=None):
|
||||||
|
__doc__ = self._wsock.sendto.__doc__
|
||||||
|
return self._wsock.sendto(data, flags_or_addr, addr)
|
||||||
|
|
||||||
|
def sendall(self, data, flags=0):
|
||||||
|
__doc__ = self._wsock.sendall.__doc__
|
||||||
|
amount = len(data)
|
||||||
|
count = 0
|
||||||
|
while (count < amount):
|
||||||
|
v = self.send(data[count:], flags)
|
||||||
|
count += v
|
||||||
|
return amount
|
||||||
|
|
||||||
|
def getpeername(self):
|
||||||
|
__doc__ = self._wsock.getpeername.__doc__
|
||||||
|
return self._peer_address
|
||||||
|
|
||||||
|
def connect(self, addr):
|
||||||
|
__doc__ = self._wsock.connect.__doc__
|
||||||
|
raise ValueError("Cannot connect already connected unwrapped socket")
|
||||||
|
|
||||||
|
connect_ex = connect
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L
|
||||||
|
opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH
|
||||||
|
fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB
|
||||||
|
AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU
|
||||||
|
D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA
|
||||||
|
IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM
|
||||||
|
oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0
|
||||||
|
ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/
|
||||||
|
loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j
|
||||||
|
oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA
|
||||||
|
z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq
|
||||||
|
ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV
|
||||||
|
q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
Just bad cert data
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L
|
||||||
|
opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH
|
||||||
|
fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB
|
||||||
|
AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU
|
||||||
|
D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA
|
||||||
|
IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM
|
||||||
|
oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0
|
||||||
|
ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/
|
||||||
|
loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j
|
||||||
|
oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA
|
||||||
|
z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq
|
||||||
|
ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV
|
||||||
|
q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
Just bad cert data
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,40 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
Bad Key, though the cert should be OK
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD
|
||||||
|
VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x
|
||||||
|
IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT
|
||||||
|
U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1
|
||||||
|
NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl
|
||||||
|
bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m
|
||||||
|
dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj
|
||||||
|
aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh
|
||||||
|
m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8
|
||||||
|
M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn
|
||||||
|
fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC
|
||||||
|
AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb
|
||||||
|
08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx
|
||||||
|
CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/
|
||||||
|
iHkC6gGdBJhogs4=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
Bad Key, though the cert should be OK
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD
|
||||||
|
VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x
|
||||||
|
IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT
|
||||||
|
U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1
|
||||||
|
NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl
|
||||||
|
bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m
|
||||||
|
dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj
|
||||||
|
aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh
|
||||||
|
m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8
|
||||||
|
M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn
|
||||||
|
fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC
|
||||||
|
AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb
|
||||||
|
08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx
|
||||||
|
CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/
|
||||||
|
iHkC6gGdBJhogs4=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,21 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAuPd3JmydJfXhyii0
|
||||||
|
agsVgRMOUcOyuldbaf/Lu4bZ+U0zH0OSoYkv0Ahbz7ehK+oGMeUy/SuGVAn7JLyj
|
||||||
|
zlYi8QIDAQABAkAygtnV82lC2Y/Mbis+nkJEGlkZuRCQ1JRRMRqI3n2eF6CviqF3
|
||||||
|
PiBXIEEExzKihC9bvbHKTAkYDLr+/4YpbiQBAiEA7JLS5Lp7KI/ayWwEzl2r5XXu
|
||||||
|
k/cbH++A4zZz6A9XIsECIQDIJ8ciDa5/VGyQnYMzBNgKnwaFDDBOiEUFDaU/9ZN8
|
||||||
|
MQIgCG3Gw819G9ncQrbtiOi/eiJ0iKMSPVYMMow7HvaE9UECIQCLyQwPwlJd5s4z
|
||||||
|
aW4ZkYZ4VHuvK8YI8q6RSuhf9Nhd4QIgFbRNdEeehgrzGzGug2yVCMzVzS3MQNBJ
|
||||||
|
6LqBZaPlFsM=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBgDCCASoCAQEwDQYJKoZIhvcNAQEEBQAwSjELMAkGA1UEBhMCVVMxEzARBgNV
|
||||||
|
BAgTCldhc2hpbmd0b24xEzARBgNVBAoTClJheSBDQSBJbmMxETAPBgNVBAMTCFJh
|
||||||
|
eUNBSW5jMB4XDTEyMDkyMTIxMTYxOFoXDTEzMDkyMTIxMTYxOFowTDELMAkGA1UE
|
||||||
|
BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xFDASBgNVBAoTC1JheSBTcnYgSW5j
|
||||||
|
MRIwEAYDVQQDEwlSYXlTcnZJbmMwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAuPd3
|
||||||
|
JmydJfXhyii0agsVgRMOUcOyuldbaf/Lu4bZ+U0zH0OSoYkv0Ahbz7ehK+oGMeUy
|
||||||
|
/SuGVAn7JLyjzlYi8QIDAQABMA0GCSqGSIb3DQEBBAUAA0EAEkxVF8HEGV8N4mYA
|
||||||
|
hDciYpttnnb9pYL1okHGrhaIFqu9D10LfP1SKps/6s/qNSk3YaIVjydWOHEf6xr4
|
||||||
|
zJkiFw==
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXAIBAAKBgQC89ZNxjTgWgq7Z1g0tJ65w+k7lNAj5IgjLb155UkUrz0XsHDnH
|
||||||
|
FlbsVUg2Xtk6+bo2UEYIzN7cIm5ImpmyW/2z0J1IDVDlvR2xJ659xrE0v5c2cB6T
|
||||||
|
f9lnNTwpSoeK24Nd7Jwq4j9vk95fLrdqsBq0/KVlsCXeixS/CaqqduXfvwIDAQAB
|
||||||
|
AoGAQFko4uyCgzfxr4Ezb4Mp5pN3Npqny5+Jey3r8EjSAX9Ogn+CNYgoBcdtFgbq
|
||||||
|
1yif/0sK7ohGBJU9FUCAwrqNBI9ZHB6rcy7dx+gULOmRBGckln1o5S1+smVdmOsW
|
||||||
|
7zUVLBVByKuNWqTYFlzfVd6s4iiXtAE2iHn3GCyYdlICwrECQQDhMQVxHd3EFbzg
|
||||||
|
SFmJBTARlZ2GKA3c1g/h9/XbkEPQ9/RwI3vnjJ2RaSnjlfoLl8TOcf0uOGbOEyFe
|
||||||
|
19RvCLXjAkEA1s+UE5ziF+YVkW3WolDCQ2kQ5WG9+ccfNebfh6b67B7Ln5iG0Sbg
|
||||||
|
ky9cjsO3jbMJQtlzAQnH1850oRD5Gi51dQJAIbHCDLDZU9Ok1TI+I2BhVuA6F666
|
||||||
|
lEZ7TeZaJSYq34OaUYUdrwG9OdqwZ9sy9LUav4ESzu2lhEQchCJrKMn23QJAReqs
|
||||||
|
ZLHUeTjfXkVk7dHhWPWSlUZ6AhmIlA/AQ7Payg2/8wM/JkZEJEPvGVykms9iPUrv
|
||||||
|
frADRr+hAGe43IewnQJBAJWKZllPgKuEBPwoEldHNS8nRu61D7HzxEzQ2xnfj+Nk
|
||||||
|
2fgf1MAzzTRsikfGENhVsVWeqOcijWb6g5gsyCmlRpc=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICsDCCAhmgAwIBAgIJAOqYOYFJfEEoMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eSBMdGQwHhcNMDgwNjI2MTgxNTUyWhcNMDkwNjI2MTgxNTUyWjBF
|
||||||
|
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
|
||||||
|
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
|
||||||
|
gQC89ZNxjTgWgq7Z1g0tJ65w+k7lNAj5IgjLb155UkUrz0XsHDnHFlbsVUg2Xtk6
|
||||||
|
+bo2UEYIzN7cIm5ImpmyW/2z0J1IDVDlvR2xJ659xrE0v5c2cB6Tf9lnNTwpSoeK
|
||||||
|
24Nd7Jwq4j9vk95fLrdqsBq0/KVlsCXeixS/CaqqduXfvwIDAQABo4GnMIGkMB0G
|
||||||
|
A1UdDgQWBBTctMtI3EO9OjLI0x9Zo2ifkwIiNjB1BgNVHSMEbjBsgBTctMtI3EO9
|
||||||
|
OjLI0x9Zo2ifkwIiNqFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
|
||||||
|
U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAOqYOYFJ
|
||||||
|
fEEoMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAQwa7jya/DfhaDn7E
|
||||||
|
usPkpgIX8WCL2B1SqnRTXEZfBPPVq/cUmFGyEVRVATySRuMwi8PXbVcOhXXuocA+
|
||||||
|
43W+iIsD9pXapCZhhOerCq18TC1dWK98vLUsoK8PMjB6e5H/O8bqojv0EeC+fyCw
|
||||||
|
eSHj5jpC8iZKjCHBn+mAi4cQ514=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -45,7 +45,7 @@ def main():
|
||||||
break
|
break
|
||||||
|
|
||||||
print "Accepting..."
|
print "Accepting..."
|
||||||
conn = scn.accept()
|
conn = scn.accept()[0]
|
||||||
sck.settimeout(5)
|
sck.settimeout(5)
|
||||||
conn.get_socket(True).settimeout(5)
|
conn.get_socket(True).settimeout(5)
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ def main():
|
||||||
try:
|
try:
|
||||||
conn.do_handshake()
|
conn.do_handshake()
|
||||||
except SSLError as err:
|
except SSLError as err:
|
||||||
if err.args[0] == SSL_ERROR_WANT_READ:
|
if len(err.args) > 1 and err.args[1].args[0] == SSL_ERROR_WANT_READ:
|
||||||
continue
|
continue
|
||||||
raise
|
raise
|
||||||
print "Completed handshaking with peer"
|
print "Completed handshaking with peer"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue