Skip to content

Commit f68a1ca

Browse files
committed
mbedtls: fix poll ioctl
1 parent 12a1e82 commit f68a1ca

File tree

4 files changed

+188
-9
lines changed

4 files changed

+188
-9
lines changed

extmod/modussl_mbedtls.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ typedef struct _mp_obj_ssl_socket_t {
6464
mbedtls_x509_crt cert;
6565
mbedtls_pk_context pkey;
6666
uint8_t poll_flag;
67+
uint8_t poll_by_read; // true: at next poll try to read first
6768
} mp_obj_ssl_socket_t;
6869

6970
struct ssl_args {
@@ -226,6 +227,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
226227
}
227228

228229
o->poll_flag = 0;
230+
o->poll_by_read = 0;
229231
if (args->do_handshake.u_bool) {
230232
while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) {
231233
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
@@ -285,6 +287,9 @@ STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc
285287
return 0;
286288
}
287289
if (ret >= 0) {
290+
// if we got all we wanted, for the next poll try a read first 'cause
291+
// there may be data in the mbedtls record buffer
292+
o->poll_by_read = ret == size;
288293
return ret;
289294
}
290295
if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
@@ -342,6 +347,13 @@ STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, i
342347
mbedtls_ctr_drbg_free(&self->ctr_drbg);
343348
mbedtls_entropy_free(&self->entropy);
344349
} else if (request == MP_STREAM_POLL) {
350+
mp_uint_t ret = 0;
351+
// If the last read returned everything asked for there may be more in the mbedtls buffer,
352+
// so find out. (There doesn't seem to be an equivalent issue with writes.)
353+
if ((arg & MP_STREAM_POLL_RD) && self->poll_by_read) {
354+
size_t avail = mbedtls_ssl_get_bytes_avail(&self->ssl);
355+
if (avail > 0) ret = MP_STREAM_POLL_RD;
356+
}
345357
// If we're polling to read but not write but mbedtls previously said it needs to write in
346358
// order to be able to read then poll for both and if either is available pretend the socket
347359
// is readable. When the app then performs a read, mbedtls is happy to perform the writes as
@@ -350,7 +362,7 @@ STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, i
350362
if ((arg & MP_STREAM_POLL_RD) && !(arg & MP_STREAM_POLL_WR) &&
351363
self->poll_flag & READ_NEEDS_WRITE) {
352364
arg |= MP_STREAM_POLL_WR;
353-
mp_uint_t ret = mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
365+
ret |= mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
354366
if (ret & MP_STREAM_POLL_WR) {
355367
ret |= MP_STREAM_POLL_RD;
356368
ret &= ~MP_STREAM_POLL_WR;
@@ -360,14 +372,15 @@ STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, i
360372
} else if ((arg & MP_STREAM_POLL_WR) && !(arg & MP_STREAM_POLL_RD) &&
361373
self->poll_flag & WRITE_NEEDS_READ) {
362374
arg |= MP_STREAM_POLL_RD;
363-
mp_uint_t ret = mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
375+
ret |= mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
364376
if (ret & MP_STREAM_POLL_RD) {
365377
ret |= MP_STREAM_POLL_WR;
366378
ret &= ~MP_STREAM_POLL_RD;
367379
}
368380
return ret;
369381
}
370-
// fall-through if there's no wonky XX_NEEDS_YY situation
382+
// Pass down to underlying socket
383+
return ret | mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
371384
}
372385
// Pass all requests down to the underlying socket
373386
return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);

tests/multi_net/ssl_data.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,58 @@
11
# Simple test creating an SSL connection and transferring some data
22
# This test won't run under CPython because it requires key/cert
33

4-
import usocket as socket, ussl as ssl
4+
try:
5+
import usocket as socket, ussl as ssl, ubinascii as binascii, uselect as select
6+
except ModuleNotFoundError:
7+
import socket, ssl, binascii, select
58

69
PORT = 8000
710

11+
# This self-signed key/cert pair is randomly generated and to be used for
12+
# testing/demonstration only.
13+
# openssl req -x509 -newkey rsa:1024 -keyout key.pem -out cert.pem -days 36500 -nodes
14+
cert = """
15+
-----BEGIN CERTIFICATE-----
16+
MIICaDCCAdGgAwIBAgIUaYEwlY581HuPWHm2ndTWejuggAIwDQYJKoZIhvcNAQEL
17+
BQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
18+
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA0MTgxOTAwMDBaGA8yMTIw
19+
MDMyNTE5MDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx
20+
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0B
21+
AQEFAAOBjQAwgYkCgYEAxmACtMgGR2tTKVHzxG67Yx61pWNynXUE0q00yJ0a34AK
22+
uQKzvyEdvkk5lL3snV4N5wKeRgWmS3/krl/YQO+Rk4eSJRqJc8INd3qSOFSNUgPg
23+
W0VPP9vPox8au5Ngqn06jgtdD1F0a6Z+f+N3+JyRPAaetIWlFC9WEn+zzz0/cmkC
24+
AwEAAaNTMFEwHQYDVR0OBBYEFBaI7GVj4GjxPWq+RO7A/4INOq2RMB8GA1UdIwQY
25+
MBaAFBaI7GVj4GjxPWq+RO7A/4INOq2RMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
26+
hvcNAQELBQADgYEAMpdYd8jkWxoXMxV+X2rpyx/BnPrPa+l2LehlulrU7lRh4QIU
27+
t4f+W+yBvkFscPatpRfJoXXqregmhLxo8poKw08pjn7DNKBzcsPsxnmRIvFZuL2J
28+
wYHGyP9HcMpsnx+UW2YjjQ4R1I0smRI7ZKiax8AJkN/P9eHH9Xku6ostXYk=
29+
-----END CERTIFICATE-----
30+
"""
31+
key = """
32+
-----BEGIN PRIVATE KEY-----
33+
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMZgArTIBkdrUylR
34+
88Ruu2MetaVjcp11BNKtNMidGt+ACrkCs78hHb5JOZS97J1eDecCnkYFpkt/5K5f
35+
2EDvkZOHkiUaiXPCDXd6kjhUjVID4FtFTz/bz6MfGruTYKp9Oo4LXQ9RdGumfn/j
36+
d/ickTwGnrSFpRQvVhJ/s889P3JpAgMBAAECgYBPkxnizM3//iRY0d/37vdKFnqF
37+
AnRqhxNNM1+WDbdG6kTi3BugUrdsqlDnwpvUsHLhNOKqcf+4D3B7JkVIHxGEqLSl
38+
YMbQrldodPwIP0ycf9hegzuhEvuYGkex22edmQ5brkdIt6QCv0QRtProYowJx4p6
39+
CuM5423ORejs6Vw9gQJBAOF//1Ovmm5Q1d90ZzjFhZCwG3/z5uwqZMGBxJTaibSC
40+
O5cci3n9Tcc4AebnMf5eyrXHovtSg1FfDxS+IUccXRECQQDhNM3R31YvYmRZwrTn
41+
f71y+buXpUtMDUDhFK8FNZN1/zJ6dJVrWQ/MVj+TaNjLUYNdPmRPHQdt8+Fx65y9
42+
95/ZAkEAqgmkdGwz3P9jZm4V778xqhrBgche1rJY63l4zG3F7LFPUfEaU1BoN9LJ
43+
zF2FWzQLUutIwI5FqzQs4Q1FdqOyoQJBALAL1iUMwFO0R5v/X+lj6xXY8PM/jJf7
44+
+E67G4In+okQIEanojJTYc0rUvGJ0YdGxjj6z/EkUS17qy2hsFq0GykCQQCiucp9
45+
7kbPpzw/gW+ERfoLgtZKrP/+Au9C5sz2wxUpeKhYihVePF8pmytyD8mqt/3LIJhZ
46+
NA2FEss2+KJUCjHc
47+
-----END PRIVATE KEY-----
48+
"""
49+
chain = cert + key
50+
# Produce cert/key for MicroPython
51+
cert = cert[cert.index("M"):cert.index("=")+2]
52+
key = key[key.index("M"):key.rstrip().rindex("\n")+1]
53+
cert = binascii.a2b_base64(cert)
54+
key = binascii.a2b_base64(key)
55+
856

957
# Server
1058
def instance0():
@@ -15,7 +63,15 @@ def instance0():
1563
s.listen(1)
1664
multitest.next()
1765
s2, _ = s.accept()
18-
s2 = ssl.wrap_socket(s2, server_side=True)
66+
if hasattr(ssl, 'SSLContext'):
67+
fn = '/tmp/MP_test_cert.pem'
68+
with open(fn, "w") as f:
69+
f.write(chain)
70+
ctx = ssl.SSLContext()
71+
ctx.load_cert_chain(fn)
72+
s2 = ctx.wrap_socket(s2, server_side=True)
73+
else:
74+
s2 = ssl.wrap_socket(s2, server_side=True, key=key, cert=cert)
1975
print(s2.read(16))
2076
s2.write(b"server to client")
2177
s.close()

tests/multi_net/ssl_data.py.exp

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/multi_net/ssl_select.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Test creating an SSL connection and transferring some data, and specifically
2+
# using select to "poll" for reading while reading chunks that are smaller than
3+
# the SSL record transmitted to make sure that the poll ioctl handles the case
4+
# where the ssl layer has some bytes buffered internally and the underlying
5+
# raw socket is not readable.
6+
# This test won't run under CPython because it doesn't fix the bug this test tests:
7+
# https://docs.python.org/3/library/ssl.html#notes-on-non-blocking-sockets
8+
9+
try:
10+
import usocket as socket, ussl as ssl, ubinascii as binascii, uselect as select
11+
except ModuleNotFoundError:
12+
import socket, ssl, binascii, select
13+
14+
PORT = 8000
15+
16+
# This self-signed key/cert pair is randomly generated and to be used for
17+
# testing/demonstration only.
18+
# openssl req -x509 -newkey rsa:1024 -keyout key.pem -out cert.pem -days 36500 -nodes
19+
cert = """
20+
-----BEGIN CERTIFICATE-----
21+
MIICaDCCAdGgAwIBAgIUaYEwlY581HuPWHm2ndTWejuggAIwDQYJKoZIhvcNAQEL
22+
BQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
23+
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA0MTgxOTAwMDBaGA8yMTIw
24+
MDMyNTE5MDAwMFowRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx
25+
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0B
26+
AQEFAAOBjQAwgYkCgYEAxmACtMgGR2tTKVHzxG67Yx61pWNynXUE0q00yJ0a34AK
27+
uQKzvyEdvkk5lL3snV4N5wKeRgWmS3/krl/YQO+Rk4eSJRqJc8INd3qSOFSNUgPg
28+
W0VPP9vPox8au5Ngqn06jgtdD1F0a6Z+f+N3+JyRPAaetIWlFC9WEn+zzz0/cmkC
29+
AwEAAaNTMFEwHQYDVR0OBBYEFBaI7GVj4GjxPWq+RO7A/4INOq2RMB8GA1UdIwQY
30+
MBaAFBaI7GVj4GjxPWq+RO7A/4INOq2RMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
31+
hvcNAQELBQADgYEAMpdYd8jkWxoXMxV+X2rpyx/BnPrPa+l2LehlulrU7lRh4QIU
32+
t4f+W+yBvkFscPatpRfJoXXqregmhLxo8poKw08pjn7DNKBzcsPsxnmRIvFZuL2J
33+
wYHGyP9HcMpsnx+UW2YjjQ4R1I0smRI7ZKiax8AJkN/P9eHH9Xku6ostXYk=
34+
-----END CERTIFICATE-----
35+
"""
36+
key = """
37+
-----BEGIN PRIVATE KEY-----
38+
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMZgArTIBkdrUylR
39+
88Ruu2MetaVjcp11BNKtNMidGt+ACrkCs78hHb5JOZS97J1eDecCnkYFpkt/5K5f
40+
2EDvkZOHkiUaiXPCDXd6kjhUjVID4FtFTz/bz6MfGruTYKp9Oo4LXQ9RdGumfn/j
41+
d/ickTwGnrSFpRQvVhJ/s889P3JpAgMBAAECgYBPkxnizM3//iRY0d/37vdKFnqF
42+
AnRqhxNNM1+WDbdG6kTi3BugUrdsqlDnwpvUsHLhNOKqcf+4D3B7JkVIHxGEqLSl
43+
YMbQrldodPwIP0ycf9hegzuhEvuYGkex22edmQ5brkdIt6QCv0QRtProYowJx4p6
44+
CuM5423ORejs6Vw9gQJBAOF//1Ovmm5Q1d90ZzjFhZCwG3/z5uwqZMGBxJTaibSC
45+
O5cci3n9Tcc4AebnMf5eyrXHovtSg1FfDxS+IUccXRECQQDhNM3R31YvYmRZwrTn
46+
f71y+buXpUtMDUDhFK8FNZN1/zJ6dJVrWQ/MVj+TaNjLUYNdPmRPHQdt8+Fx65y9
47+
95/ZAkEAqgmkdGwz3P9jZm4V778xqhrBgche1rJY63l4zG3F7LFPUfEaU1BoN9LJ
48+
zF2FWzQLUutIwI5FqzQs4Q1FdqOyoQJBALAL1iUMwFO0R5v/X+lj6xXY8PM/jJf7
49+
+E67G4In+okQIEanojJTYc0rUvGJ0YdGxjj6z/EkUS17qy2hsFq0GykCQQCiucp9
50+
7kbPpzw/gW+ERfoLgtZKrP/+Au9C5sz2wxUpeKhYihVePF8pmytyD8mqt/3LIJhZ
51+
NA2FEss2+KJUCjHc
52+
-----END PRIVATE KEY-----
53+
"""
54+
chain = cert + key
55+
# Produce cert/key for MicroPython
56+
cert = cert[cert.index("M"):cert.index("=")+2]
57+
key = key[key.index("M"):key.rstrip().rindex("\n")+1]
58+
cert = binascii.a2b_base64(cert)
59+
key = binascii.a2b_base64(key)
60+
61+
# Server
62+
def instance0():
63+
multitest.globals(IP=multitest.get_network_ip())
64+
s = socket.socket()
65+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
66+
s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
67+
s.listen(1)
68+
multitest.next()
69+
s2, _ = s.accept()
70+
if hasattr(ssl, 'SSLContext'):
71+
fn = '/tmp/MP_test_cert.pem'
72+
with open(fn, "w") as f:
73+
f.write(chain)
74+
ctx = ssl.SSLContext()
75+
ctx.load_cert_chain(fn)
76+
s2 = ctx.wrap_socket(s2, server_side=True)
77+
else:
78+
s2 = ssl.wrap_socket(s2, server_side=True, key=key, cert=cert)
79+
print(s2.read(16))
80+
s2.write(b"server to client")
81+
# test larger client->server record being read in small chunks
82+
total = 0
83+
fileno = s2
84+
if hasattr(s2, 'fileno'):
85+
fileno = s2.fileno()
86+
while True:
87+
if hasattr(ssl, 'SSLContext'):
88+
# select doesn't actually work right in CPython! so skip it
89+
sel = [[1]]
90+
else:
91+
sel = select.select([fileno], [], [])
92+
if len(sel[0]) == 1:
93+
buf = s2.read(20)
94+
print("got", len(buf))
95+
total += len(buf)
96+
if total == 200:
97+
break
98+
print("got", total)
99+
s2.write(b"server to client 2")
100+
print("DONE", len(buf))
101+
s.close()
102+
103+
104+
# Client
105+
def instance1():
106+
multitest.next()
107+
s = socket.socket()
108+
s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
109+
s = ssl.wrap_socket(s)
110+
s.write(b"client to server")
111+
print(s.read(16))
112+
s.write(bytes(200))
113+
print(s.read(18))
114+
s.close()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy