Skip to content

Commit

Permalink
Add basic SSL support
Browse files Browse the repository at this point in the history
This is a partial support as the CA certificate is not validated. This
is so as this functionality is not implemented in all micropython ports.

For example, it isn't currently supported in the esp32 port although a
draft of the feature has been published:
micropython/micropython#5998

As the CA functionality is not there, we cannot use this parameter
do decide when to switch to SSL mode as done in the CPython version.
Instead we enable SSL when connecting to port 443 or 8443 which are
well known ports used for TLS communications.

One more thing is that this library relies on short reads to get data
from the socket as it always ask for the max buffer length when reading.
In micropython, the interface provided for SSL socket is only the one
of a stream which doesn't allow short reads. To go around this we change
the socket to non blocking which which has the side-effect of allowing
short reads. However we then need to do manual polling and timeout on
the socket which we do here.
  • Loading branch information
fgervais committed Dec 9, 2020
1 parent 6a8a62a commit c124e22
Showing 1 changed file with 24 additions and 2 deletions.
26 changes: 24 additions & 2 deletions blynklib_mp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
__version__ = '0.2.6'

import usocket as socket
import ussl as ssl
import utime as time
import ustruct as struct
import uselect as select
Expand Down Expand Up @@ -125,6 +126,7 @@ def internal_msg(self, *args):
class Connection(Protocol):
SOCK_MAX_TIMEOUT = const(5)
SOCK_TIMEOUT = 0.05
SOCK_SSL_TIMEOUT = const(1)
EAGAIN = const(11)
ETIMEDOUT = const(60)
RETRIES_TX_DELAY = const(2)
Expand Down Expand Up @@ -164,15 +166,28 @@ def send(self, data):
try:
retries -= 1
self._last_send_time = ticks_ms()
return self._socket.send(data)
try:
bytes_written = self._socket.send(data)
except AttributeError:
bytes_written = self._socket.write(data)
return bytes_written
except (IOError, OSError):
sleep_ms(self.RETRIES_TX_DELAY)

def receive(self, length, timeout):
d_buff = b''
try:
self._set_socket_timeout(timeout)
d_buff += self._socket.recv(length)
try:
d_buff += self._socket.recv(length)
except AttributeError:
timeout = self.SOCK_SSL_TIMEOUT
while not d_buff and timeout > 0:
ret = self._socket.read(length)
if ret:
d_buff += ret
timeout -= self.SOCK_TIMEOUT
time.sleep(self.SOCK_TIMEOUT)
if len(d_buff) >= length:
d_buff = d_buff[:length]
return d_buff
Expand Down Expand Up @@ -203,6 +218,13 @@ def _get_socket(self):
self._socket = socket.socket()
self._socket.connect(socket.getaddrinfo(self.server, self.port)[0][-1])
self._set_socket_timeout(self.SOCK_TIMEOUT)
if self.port == 443 or self.port == 8443:
self.log('Using SSL socket...')
self._socket = ssl.wrap_socket(self._socket)
# Short reads are not supported in ssl mode. We work around
# this by setting the socket non blocking and doing manual
# polling/timeout.
self._socket.setblocking(False)
self.log('Connected to server')
except Exception as g_exc:
raise BlynkError('Server connection failed: {}'.format(g_exc))
Expand Down

0 comments on commit c124e22

Please sign in to comment.