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.
103 lines
3.2 KiB
Python
103 lines
3.2 KiB
Python
# PyDTLS sequential echo. Written by Ray Brown.
|
|
"""PyDTLS sequential echo
|
|
|
|
This script runs a sequential echo server. It is sequential in that it will
|
|
respond without error only to a single sclient that invokes the following steps
|
|
in order:
|
|
* DTLS cookie exchange on port 28000 of localhost
|
|
* DTLS handshake (application-default ciphers)
|
|
* Write and receive echo back for an arbitrary number of datagrams
|
|
* Isue shutdown notification and receive the shutdown notification response
|
|
|
|
Note that this script's operation is slow and inefficient on purpose: it
|
|
invokes the demux without socket select, but with 5-second timeouts after
|
|
the cookie exchange; this is done so that one can follow the debug logs when
|
|
operating this server from a client shell interactively.
|
|
"""
|
|
|
|
import socket
|
|
from os import path
|
|
from logging import basicConfig, DEBUG
|
|
basicConfig(level=DEBUG) # set now for dtls import code
|
|
from dtls.sslconnection import SSLConnection
|
|
from dtls.err import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_ZERO_RETURN
|
|
|
|
def main():
|
|
sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
sck.bind(("127.0.0.1", 28000))
|
|
sck.settimeout(30)
|
|
cert_path = path.join(path.abspath(path.dirname(__file__)), "certs")
|
|
scn = SSLConnection(
|
|
sck,
|
|
keyfile=path.join(cert_path, "server-key.pem"),
|
|
certfile=path.join(cert_path, "server-cert.pem"),
|
|
server_side=True,
|
|
ca_certs=path.join(cert_path, "ca-cert.pem"),
|
|
do_handshake_on_connect=False)
|
|
cnt = 0
|
|
|
|
while True:
|
|
cnt += 1
|
|
print "Listen invocation: %d" % cnt
|
|
peer_address = scn.listen()
|
|
if peer_address:
|
|
print "Completed listening for peer: %s" % str(peer_address)
|
|
break
|
|
|
|
print "Accepting..."
|
|
conn = scn.accept()[0]
|
|
sck.settimeout(5)
|
|
conn.get_socket(True).settimeout(5)
|
|
|
|
cnt = 0
|
|
while True:
|
|
cnt += 1
|
|
print "Listen invocation: %d" % cnt
|
|
peer_address = scn.listen()
|
|
assert not peer_address
|
|
print "Handshake invocation: %d" % cnt
|
|
try:
|
|
conn.do_handshake()
|
|
except SSLError as err:
|
|
if len(err.args) > 1 and err.args[1].args[0] == SSL_ERROR_WANT_READ:
|
|
continue
|
|
raise
|
|
print "Completed handshaking with peer"
|
|
break
|
|
|
|
cnt = 0
|
|
while True:
|
|
cnt += 1
|
|
print "Listen invocation: %d" % cnt
|
|
peer_address = scn.listen()
|
|
assert not peer_address
|
|
print "Read invocation: %d" % cnt
|
|
try:
|
|
message = conn.read()
|
|
except SSLError as err:
|
|
if err.args[0] == SSL_ERROR_WANT_READ:
|
|
continue
|
|
if err.args[0] == SSL_ERROR_ZERO_RETURN:
|
|
break
|
|
raise
|
|
print message
|
|
conn.write("Back to you: " + message)
|
|
|
|
cnt = 0
|
|
while True:
|
|
cnt += 1
|
|
print "Listen invocation: %d" % cnt
|
|
peer_address = scn.listen()
|
|
assert not peer_address
|
|
print "Shutdown invocation: %d" % cnt
|
|
try:
|
|
conn.shutdown()
|
|
except SSLError as err:
|
|
if err.args[0] == SSL_ERROR_WANT_READ:
|
|
continue
|
|
raise
|
|
break
|
|
|
|
if __name__ == "__main__":
|
|
main()
|