diff --git a/firmwareapi/micropython/usocket.md b/firmwareapi/micropython/usocket.md index ce125fd..0a2fdfb 100644 --- a/firmwareapi/micropython/usocket.md +++ b/firmwareapi/micropython/usocket.md @@ -146,3 +146,8 @@ Write the buffer of bytes to the socket. Return value: number of bytes written. +#### socket.do_handshake\(\) + +Perform the SSL handshake on the previously "wrapped" socket with ssl.wrap_socket(). +COuld be used when the socket is non-blocking and the SSL handshake is not performed during connect(). + diff --git a/firmwareapi/micropython/ussl.md b/firmwareapi/micropython/ussl.md index 1ee9327..63ed3a6 100644 --- a/firmwareapi/micropython/ussl.md +++ b/firmwareapi/micropython/ussl.md @@ -37,4 +37,5 @@ SSL sockets inherit all methods and from the standard sockets, see the `usocket` ## Constants * `ssl.CERT_NONE`, `ssl.CERT_OPTIONAL`, `ssl.CERT_REQUIRED`: Supported values in `cert_reqs` +* `ssl.SSL_TIMEOUT`: raised by a "wrapped" socket when socket.do_handshake() is called diff --git a/tutorials/all/socket.md b/tutorials/all/socket.md index c29bc56..257fa66 100644 --- a/tutorials/all/socket.md +++ b/tutorials/all/socket.md @@ -48,6 +48,8 @@ while True: c = c+1 ``` +### Using a client with non-blocking sockets + The following example sets up a client which can connect to a server with 2 non-blocking sockets, create a new thread to handle the non-blocking sockets. ```python @@ -135,3 +137,109 @@ for s in socket_list: _thread.start_new_thread(socket_thread, (p,)) ``` +### Connecting to a server with non-blocking SSL wrapped socket + +```python + +import socket +import ssl +import _thread +import time +import uerrno +import uselect + +# Helper function for doing the handshake +def handshake_helper(sock): + + while True: + time.sleep_ms(1) + try: + # Perform the handshake + sock.do_handshake() + return + except ssl.SSLError as e: + # For now raise any other errors then TIMEOUT... + if e.args[0] != ssl.SSL_TIMEOUT: + raise + +def socket_thread(p): + + while True: + # Wait for any action to happen infinitely + l = p.poll() + + # Start processing the actions happened + for t in l: + # First element of the returned tuple is the socket itself + sock = t[0] + # Second element of the returned tuple is the events happened + event = t[1] + + # If any error or connection drop happened close the socket + if(event & uselect.POLLERR or event & uselect.POLLHUP): + p.unregister(sock) + sock.close() + + # If any new data is received then get it + elif(event & uselect.POLLIN): + # If any error occurs during receiving here, do "nothing", poll() will return with error event, close the socket there + try: + r = sock.recv(1) + # If recv() returns with 0 the other end closed the connection + if len(r) == 0: + p.unregister(sock) + sock.close() + else: + # Do something with the received data... + print("Data received: " + str(r)) + except: + pass + + # If the socket is writable then we may expect it is connected, do handshake and send some data + elif(event & uselect.POLLOUT): + + try: + # Performing the SSL handshake + handshake_helper(sock) + except: + # An error happened, close the socket, unregister it from poll + p.unregister(sock) + sock.close() + + # If any error occurs during sending here, do "nothing", poll() will return with error event, close the socket there + try: + sock.send("Data to send") + # We only want to send one message on this socket, in the future wait only for new incoming messages + p.modify(sock, uselect.POLLIN | uselect.POLLHUP | uselect.POLLERR) + except: + pass + + +# Create and wrap the socket +s = socket.socket() +ssl_socket = ssl.wrap_socket(s, keyfile="/flash/cert/my_private_key.pem", + certfile="/flash/cert/my_public_key.pem", + server_side=False, + cert_reqs=ssl.CERT_REQUIRED, + ca_certs="/flash/cert/my_CA_cert.pem" + ) +# Set the wrapped socket to non-blocking +ssl_socket.setblocking(False) +# Create a new poll object +p = uselect.poll() +# Register the wrapped socket into the poll object, wait only for write and error events +p.register(ssl_socket, uselect.POLLOUT | uselect.POLLHUP | uselect.POLLERR) + + # Try to connect to the server +try: + ssl_socket.connect(socket.getaddrinfo("192.168.0.234", 6543)[0][-1]) +except OSError as e: + # In case of non-blocking socket the connect() raises eception of EINPROGRESS what is expected + if e.args[0] != uerrno.EINPROGRESS: + # Raise all other errors + raise + +# Start the thread which takes care of the non-blocking socket through the created poll object +_thread.start_new_thread(socket_thread, (p)) + +```