# PyDTLS sequential echo.

# Copyright 2012 Ray Brown
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# The License is also distributed with this work in the file named "LICENSE."
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""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 str(err).startswith("504:"):
                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 str(err).startswith("502:"):
                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:
            s = conn.shutdown()
            s.shutdown(socket.SHUT_RDWR)
        except SSLError as err:
            if str(err).startswith("502:"):
                continue
            raise
        break

if __name__ == "__main__":
    main()
