Skip to content

Commit b0d4843

Browse files
committed
extmod/modussl_mbedtls: Fix support for ioctl(MP_STREAM_POLL).
During the initial handshake or subsequent renegotiation, the protocol might need to read in order to write (or conversely to write in order to read). It might be blocked from doing so by the state of the underlying socket (i.e. there is no data to read, or there is no space to write). The library indicates this condition by returning one of the errors `MBEDTLS_ERR_SSL_WANT_READ` or `MBEDTLS_ERR_SSL_WANT_WRITE`. When that happens, we need to enforce that the next poll operation only considers the direction that the library indicated. In addition, mbedtls does its own read buffering that we need to take into account while polling, and we need to save the last error between read()/write() and ioctl().
1 parent 988b6e2 commit b0d4843

File tree

4 files changed

+227
-1
lines changed

4 files changed

+227
-1
lines changed

extmod/modussl_mbedtls.c

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
#include "mbedtls/debug.h"
4747
#include "mbedtls/error.h"
4848

49+
#define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)
50+
4951
typedef struct _mp_obj_ssl_socket_t {
5052
mp_obj_base_t base;
5153
mp_obj_t sock;
@@ -56,6 +58,9 @@ typedef struct _mp_obj_ssl_socket_t {
5658
mbedtls_x509_crt cacert;
5759
mbedtls_x509_crt cert;
5860
mbedtls_pk_context pkey;
61+
62+
uintptr_t poll_mask; // Indicates which read or write operations the protocol needs next
63+
int last_error; // The last error code, if any
5964
} mp_obj_ssl_socket_t;
6065

6166
struct ssl_args {
@@ -165,6 +170,8 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
165170
#endif
166171
o->base.type = &ussl_socket_type;
167172
o->sock = sock;
173+
o->poll_mask = 0;
174+
o->last_error = 0;
168175

169176
int ret;
170177
mbedtls_ssl_init(&o->ssl);
@@ -306,6 +313,12 @@ STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin
306313

307314
STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
308315
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
316+
o->poll_mask = 0;
317+
318+
if (o->last_error) {
319+
*errcode = o->last_error;
320+
return MP_STREAM_ERROR;
321+
}
309322

310323
int ret = mbedtls_ssl_read(&o->ssl, buf, size);
311324
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
@@ -322,13 +335,22 @@ STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc
322335
// wanting to write next handshake message. The same may happen with
323336
// renegotation.
324337
ret = MP_EWOULDBLOCK;
338+
o->poll_mask = MP_STREAM_POLL_WR;
339+
} else {
340+
o->last_error = ret;
325341
}
326342
*errcode = ret;
327343
return MP_STREAM_ERROR;
328344
}
329345

330346
STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
331347
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
348+
o->poll_mask = 0;
349+
350+
if (o->last_error) {
351+
*errcode = o->last_error;
352+
return MP_STREAM_ERROR;
353+
}
332354

333355
int ret = mbedtls_ssl_write(&o->ssl, buf, size);
334356
if (ret >= 0) {
@@ -341,6 +363,9 @@ STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, in
341363
// wanting to read next handshake message. The same may happen with
342364
// renegotation.
343365
ret = MP_EWOULDBLOCK;
366+
o->poll_mask = MP_STREAM_POLL_RD;
367+
} else {
368+
o->last_error = ret;
344369
}
345370
*errcode = ret;
346371
return MP_STREAM_ERROR;
@@ -358,17 +383,56 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
358383

359384
STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
360385
mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in);
386+
mp_uint_t ret = 0;
387+
uintptr_t saved_arg = 0;
388+
mp_obj_t sock = self->sock;
389+
if (sock == NULL || self->last_error != 0) {
390+
// Closed or error socket:
391+
return MP_STREAM_POLL_NVAL;
392+
}
393+
361394
if (request == MP_STREAM_CLOSE) {
395+
self->sock = NULL;
362396
mbedtls_pk_free(&self->pkey);
363397
mbedtls_x509_crt_free(&self->cert);
364398
mbedtls_x509_crt_free(&self->cacert);
365399
mbedtls_ssl_free(&self->ssl);
366400
mbedtls_ssl_config_free(&self->conf);
367401
mbedtls_ctr_drbg_free(&self->ctr_drbg);
368402
mbedtls_entropy_free(&self->entropy);
403+
} else if (request == MP_STREAM_POLL) {
404+
// If the library signaled us that it needs reading or writing, only check that direction,
405+
// but save what the caller asked because we need to restore it later
406+
if (self->poll_mask && (arg & MP_STREAM_POLL_RDWR)) {
407+
saved_arg = arg & MP_STREAM_POLL_RDWR;
408+
arg = (arg & ~saved_arg) | self->poll_mask;
409+
}
410+
411+
// Take into account that the library might have buffered data already
412+
int has_pending = 0;
413+
if (arg & MP_STREAM_POLL_RD) {
414+
has_pending = mbedtls_ssl_check_pending(&self->ssl);
415+
if (has_pending) {
416+
ret |= MP_STREAM_POLL_RD;
417+
if (arg == MP_STREAM_POLL_RD) {
418+
// Shortcut if we only need to read and we have buffered data, no need to go to the underlying socket
419+
return MP_STREAM_POLL_RD;
420+
}
421+
}
422+
}
369423
}
424+
370425
// Pass all requests down to the underlying socket
371-
return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
426+
ret |= mp_get_stream(sock)->ioctl(sock, request, arg, errcode);
427+
428+
if (request == MP_STREAM_POLL) {
429+
// The direction the library needed is available, return a fake result to the caller so that
430+
// it reenters a read or a write to allow the handshake to progress
431+
if (ret & self->poll_mask) {
432+
ret |= saved_arg;
433+
}
434+
}
435+
return ret;
372436
}
373437

374438
STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = {
@@ -381,6 +445,9 @@ STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = {
381445
#if MICROPY_PY_USSL_FINALISER
382446
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
383447
#endif
448+
#if MICROPY_UNIX_COVERAGE
449+
{ MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) },
450+
#endif
384451
{ MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) },
385452
};
386453

tests/extmod/ussl_poll.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import uselect
2+
import ussl
3+
import io
4+
from micropython import const
5+
6+
_MP_STREAM_POLL_RD = const(0x0001)
7+
_MP_STREAM_POLL_WR = const(0x0004)
8+
_MP_STREAM_POLL_NVAL = const(0x0020)
9+
_MP_STREAM_POLL = const(3)
10+
_MP_STREAM_CLOSE = const(4)
11+
12+
13+
# Generated with `openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -sha256 -days 36500 -subj '/CN=localhost'`
14+
certificate = b'0\x82\x03\x0b0\x82\x01\xf3\xa0\x03\x02\x01\x02\x02\x14.\xab\x80\xd7\x8fL\xb5\x83T\xee|;O`\xd2\xc7>\xd1\xf4\x940\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000\x141\x120\x10\x06\x03U\x04\x03\x0c\tlocalhost0 \x17\r221215172840Z\x18\x0f21221121172840Z0\x141\x120\x10\x06\x03U\x04\x03\x0c\tlocalhost0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\x8dK\xde\x87:UFX\xf4\xa2\x89Om\xc6}ju\x7f\xa9)\x00\xdf\x9f\xbd\x8aM\x17\x1a\xbf\xfaD?\xbc\xcc6\xabG\x946\xe1\xa6\xc8\x1d\x8bh\x1bV\x0c\xc5\xbd\x11[!*\x0f\xcc\x82\x8a\xee\x96\xf0\xa3\xf0F\xe0\xa3\xe7%\x17??:\x11\x82W\x1f\x16\x8aw\x01\n\x02\xd63\x8a\xceW\xd9!\x15;\xe9L\x1cx\xa9M\xaeR\'\xd1\x95\xbd\x8a0N\xf2\xdbc\x0c\xc5\xd0\x0e\xc7<x(\xf4BJ\xd4CGh\xe5\xc7\xd6\x07]\xae\xeb\x88@\xcfp\xf8%_\xb6\xcfCr\x05l(?\x13\xfc\xb2\xe68\xb5\xf8\xd2(\x97a\x07pV#\x82J\x06\xa1x\xbd)\x89\\\x1e\xcb ;\x8e\xe6\x17\x92\xd2\x03.\xce\x90\xa5^\xfa\xcac\xd65\xe7\x0bR\xac\xff\x96:Dss\x1fiSu\x1a\xe8!+\x13\x8c\xc5\xa0\x8d\x18_{\xcf\xc9\xafa\xde\x02\xc70\x8eMm\xcb\xfa*\x08\xa3\x84\xe7\x1d\xd3-\xe8@\x13\xb3\x7f\xb6#\xcf\xeb\xec\x0c\x04\x98%\xea\x17\xf6*{\x02\x03\x01\x00\x01\xa3S0Q0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x142;\xee<\xcd\xbf9\xbc\xdc\xb8OP\xcd\x88}K+\xca_#0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x142;\xee<\xcd\xbf9\xbc\xdc\xb8OP\xcd\x88}K+\xca_#0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00p\x9c\x87\x85\xac\xb6\xa3C5\xff\xaf\'+\xa1\x8b]\x87(\xe1\xee"j\xbc\xe8\x15\xd1f7\xe9\xd4d\xff\x0c\x97\x10H\xc5\x88\xa1g\x15\x94\x1e/m\x93\x17\x84@@\xcd\xa9\x7f\xd9:\x02F\x86\x10\xa06\xe3[\xaf\xff\xa3\xa2\x90\xcb\xcf(\xa8\xd3\xb0\x07p^\xf7\x8a\xe6\xf2\'5\xd6\xe7\xac\x82S\xafZ)7\x81\xc2\xa1 \'\x1ft\xe0\x1b\\Y \xd5\xce\xefH&\xfc\x06\xed%\xc0\n\xb1\xfe\xa8\xc8k\xb8\xdaK\xad\xd3Z\x9d>\x88\xf1\xaa\xb8\xda\xe5\xcd\xd7\xe7\x95\xa0\xf4Q\xc2\xfb\xd7\xcd\x89\x8f1\x0e\x16\xa6\xf9#\x9c$\xaa#\xa8\xaa\x8f=\x8b\xf4\xce\x9c\xf0|\xca\xea%\x02\xd3\x13B\xf4\xfa\xdas\xc8\x15mL\x84\x82\xef\xb0\xe8e\xbc7\x10;\xd5V\xee\'\x1f\xf1d\xc5\xc8\xdf\xd5\xe5\x90\xb2)\x1c\xab;\xe4Jr\xb4B:\xd1\xedD\x8a\x8e\xb9%P\xecuM:(\x82eg\xce8U\x15\xeew\xe8-E\xa8\xa9#\xeb\xb0D\xe7\x99\xbb\xbe\t\xbd\xe6\xcd'
15+
key = b'0\x82\x04\xbd\x02\x01\x000\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x04\x82\x04\xa70\x82\x04\xa3\x02\x01\x00\x02\x82\x01\x01\x00\x8dK\xde\x87:UFX\xf4\xa2\x89Om\xc6}ju\x7f\xa9)\x00\xdf\x9f\xbd\x8aM\x17\x1a\xbf\xfaD?\xbc\xcc6\xabG\x946\xe1\xa6\xc8\x1d\x8bh\x1bV\x0c\xc5\xbd\x11[!*\x0f\xcc\x82\x8a\xee\x96\xf0\xa3\xf0F\xe0\xa3\xe7%\x17??:\x11\x82W\x1f\x16\x8aw\x01\n\x02\xd63\x8a\xceW\xd9!\x15;\xe9L\x1cx\xa9M\xaeR\'\xd1\x95\xbd\x8a0N\xf2\xdbc\x0c\xc5\xd0\x0e\xc7<x(\xf4BJ\xd4CGh\xe5\xc7\xd6\x07]\xae\xeb\x88@\xcfp\xf8%_\xb6\xcfCr\x05l(?\x13\xfc\xb2\xe68\xb5\xf8\xd2(\x97a\x07pV#\x82J\x06\xa1x\xbd)\x89\\\x1e\xcb ;\x8e\xe6\x17\x92\xd2\x03.\xce\x90\xa5^\xfa\xcac\xd65\xe7\x0bR\xac\xff\x96:Dss\x1fiSu\x1a\xe8!+\x13\x8c\xc5\xa0\x8d\x18_{\xcf\xc9\xafa\xde\x02\xc70\x8eMm\xcb\xfa*\x08\xa3\x84\xe7\x1d\xd3-\xe8@\x13\xb3\x7f\xb6#\xcf\xeb\xec\x0c\x04\x98%\xea\x17\xf6*{\x02\x03\x01\x00\x01\x02\x82\x01\x00\x13)HF\xaff\xf4\x10\xc90\'\x8d\xd1u\xba\xf9Np\xdf\x08S\xf9\x7f\x88\xad22!k\nm\xe9\xf4\xd8\xfe\xca\xf0|\x8d\x8a\xc1Z\xdf\x02\xd2=\xee\xc7\xf1\xa2\xe7\xb1#\x88\xb86\xc47B\x0e\xcf\x18tS\xb0P\x19\xdf\x02M\x8d\xed\xfa\x84\xe1\xfa\xb1\xccZB\x16P\xf7*k6\xfb[\xd3,5\x818T\x9e\x1a[\x91\xf9\xcb3\x95jlL\x8b\xd0Lg\x1c\xca\xd0\xa1\xc8l\x0fa\x82ja\ns\x0bB\x92\xf2\xac8\x04\xe5oj\xfc\xa3\x91\xa9\x85\xfd\xbcl8\x1e@\x1a\xc5:\xb7\'\xdbc\xfe\xc0XV(`\xec\x91\x95X\xcd\x91\xf4\xa3w\xf3qt\xc1HJN\xebQ\xdex\xa4\x16\xf0\xae2o\x92+\x0b\xa7\x12\x80qnaR\xb2\t\xdf\x93\xf90N<:\x02\x88K\xe2\x92Z1\xd9\x12?.d\x1e\xd44l\xbe:\xa1m\x89\xda\xb7\x84\xc6\x8d\x1a\xc7\x8b"\x9d\x85\xb3\xe6c\xf5\xd0\x9fox\xe5\xfb\xed\x159\xe5\xa35\x87\x00Z3\xefX\xf9\x02\x81\x81\x00\xb8\x94\x10\xf7`\xba\xf3\xb1\xee7\xc3F\xf2\xf3\x83l\xf8\xde\x9f\x15\x03#\xc4\xaf!Eu\xab\xf7xch\xa2t\xb7@>^5\x7f\xa5\x16\x86\x94r\xdf\x06)G\xc8\xfe\xf9\xa0X\xff\x19\xf5\x197c\xd3\xac\x8f*Y\x18G \xab\x96\xbf\xd7"\x04\xce\x0f\x81=\xbb\x1b\xaaf\x1e2\x96-\x9a\x93\xe5\x14\xa5a\x12\xb4\xe0\xed\xec\x8f\xca\xad\x9d\xeb\x8dJ=\x8aT]\x15\\\x98"h\x18\xc2\x81\xce\xae~%\xc4\x0e\xbb\xc7i\xf0Lc\x02\x81\x81\x00\xc3\xf8aP\xccM\xdb\xf7f\xe4]0M\x14\xeb!\xf4\xf6/\x8e\\\xca\xdf\xdci\x11\xe2|\xe4\x17\xc9\xb3\x89\x96\x0b\x05\x82u\xbaH&\xb1\x98-\xc2-\xc6\xaa\xbc/@k \xddd0DT\xc2K\xed\x15\r\\\x00\xc0S\xc3Y\xc1\xb1\xb1bnov\xf1\xc1\xe1\x8d\xc1\x07\xe2/\xcfd\x15\x98,N\x14\xbe\x1f\x0b\x95\x8a\x112\xa1O\x0c\xd1p\x14\xe6\xf1\x0e1`w\xcdqz\xb6\xfdt\x07J\x1bq\x88\x11\x12\xa6{\x1c\t\t\x02\x81\x80\x04\xc35H\xdc\xc3\x16$\xa2+\xe8*\xfd{\xd1\tO\xc6\x96\xbe9\r\x846\xac\x9a\x196-\xb5z\x83)\xa5\xefP\x86\x0br?\xef\xe7\x8b\xe3j\xaf\\~V.\xd6}dh\xc7tI\x01\xb4\x8f+\xd8\x08\xfd\xa3\xbc\xdf\xa0\xf5,w\x98\xbat\xfbH%"\x8d\xa0b\x1e.kI\xba\xb5\x81Yh\xd5\xf7\x92>\xfe\x8a\xc5\x0e\xd4\xc0\xf4\x11.\xdd\x1a\x87f3\xdc\xa8=\xf8\xadL\xee?\xe10Yj\xea\x0b#G:\xf1\x8bW\x02\x81\x80+b\x98\xc0\xd7\x8aA1\x83\x80\xf94\x91L\x19F:B*\x83\x1c\xfd\xf9\x13\x85\xdbd\xc5\xfb\x85\\\xad7\xbf\x95\x0f\x123\xd8\x1a\xd3\x1e,/\xad6\x8f.\x0b]v\xa8\x80\xed"\x9a \xf6\x96\xd1RZ\x7f\xcb\xa7\x8a\xec\xc0i\xe5\x9c\xdeE\x89gy\xf0\xc9\xd8\x92\x96r\x95[\xbaQQ\n\x90|t\xd1&t]\x15\xe4\xfa\xcd\x85\x7f\xb3\xfaYVKu\xb5\xee\xc2w$1c\xc3\xb6\xe5J=\xcb#\xb1\x8b\xecy\x82\xdai\x02\x81\x81\x00\x9f\xf2~7\\uZQ<$\xa2\xc8k\x94@\xc5\xcbt\x16\xf9\x8c\xf61Uj\xdf\xd2!\xa5\r\xb9Z\x8e\x1fJ\xd2\x96Z?\x9a\x07\x00\xeb\xf7\xc2\xe5\x08\xc9\x95\xb2\xb2\x8e\x8d\t\xc2Q\x16h\xc1\xd8p\x88\'\xa3gTO\x91\xb9\x00 }e`\xca2\x90\xcc5<\x19ws\x8c\x7fo\x13r\xc8\x89\x96j\n6\xbb\xe2\x891\xec)\xa6\x84\xf3\xcbwH\x10\xaeX\xa5\xf4\xd0\xa8\xca\xb0l\x1b\xfd\xd7\xec\xcd<\xb7\xaf\xcfw\xa2$'
16+
17+
18+
class _Pipe(io.IOBase):
19+
def __init__(self):
20+
self._other = None
21+
self.block_reads = False
22+
self.block_writes = False
23+
24+
self.write_buffers = []
25+
self.last_poll_arg = None
26+
27+
def readinto(self, buf):
28+
if self.block_reads or len(self._other.write_buffers) == 0:
29+
return None
30+
31+
read_buf = self._other.write_buffers[0]
32+
l = min(len(buf), len(read_buf))
33+
buf[:l] = read_buf[:l]
34+
if l == len(read_buf):
35+
self._other.write_buffers.pop(0)
36+
else:
37+
self._other.write_buffers[0] = read_buf[l:]
38+
return l
39+
40+
def write(self, buf):
41+
if self.block_writes:
42+
return None
43+
44+
self.write_buffers.append(memoryview(bytes(buf)))
45+
return len(buf)
46+
47+
def ioctl(self, request, arg):
48+
if request == _MP_STREAM_POLL:
49+
self.last_poll_arg = arg
50+
ret = 0
51+
if arg & _MP_STREAM_POLL_RD:
52+
if not self.block_reads and self._other.write_buffers:
53+
ret |= _MP_STREAM_POLL_RD
54+
if arg & _MP_STREAM_POLL_WR:
55+
if not self.block_writes:
56+
ret |= _MP_STREAM_POLL_WR
57+
return ret
58+
59+
elif request == _MP_STREAM_CLOSE:
60+
return 0
61+
62+
raise NotImplementedError()
63+
64+
@classmethod
65+
def new_pair(cls):
66+
p1 = cls()
67+
p2 = cls()
68+
p1._other = p2
69+
p2._other = p1
70+
return p1, p2
71+
72+
73+
def assert_poll(s, i, arg, expected_arg, expected_ret):
74+
ret = s.ioctl(_MP_STREAM_POLL, arg)
75+
assert i.last_poll_arg == expected_arg
76+
i.last_poll_arg = None
77+
assert ret == expected_ret
78+
79+
80+
def assert_raises(cb, *args, **kwargs):
81+
try:
82+
cb(*args, **kwargs)
83+
raise AssertionError("should have raised")
84+
except Exception as exc:
85+
pass
86+
87+
88+
client_io, server_io = _Pipe.new_pair()
89+
90+
client_io.block_reads = True
91+
client_io.block_writes = True
92+
client_sock = ussl.wrap_socket(client_io, do_handshake=False)
93+
94+
server_sock = ussl.wrap_socket(server_io, key=key, cert=certificate, server_side=True, do_handshake=False)
95+
96+
# Do a test read, at this point the TLS handshake wants to write,
97+
# so it returns None:
98+
assert client_sock.read(128) is None
99+
100+
# Polling for either read or write actually check if the underlying socket can write:
101+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_WR, 0)
102+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_WR, _MP_STREAM_POLL_WR, 0)
103+
104+
# Mark the socket as writable, and do another test read:
105+
client_io.block_writes = False
106+
assert client_sock.read(128) is None
107+
108+
# The client wrote the CLIENT_HELLO message
109+
assert len(client_io.write_buffers) == 1
110+
111+
# At this point the TLS handshake wants to read, but we don't know that yet:
112+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_RD, 0)
113+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_WR, _MP_STREAM_POLL_WR, _MP_STREAM_POLL_WR)
114+
115+
# Do a test write
116+
client_sock.write(b"foo")
117+
118+
# Now we know that we want to read:
119+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_RD, 0)
120+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_WR, _MP_STREAM_POLL_RD, 0)
121+
122+
# Unblock reads and nudge the two sockets:
123+
client_io.block_reads = False
124+
while server_io.write_buffers or client_io.write_buffers:
125+
if server_io.write_buffers:
126+
assert client_sock.read(128) is None
127+
if client_io.write_buffers:
128+
assert server_sock.read(128) is None
129+
130+
# At this point, the handshake is done, try writing data:
131+
client_sock.write(b"foo")
132+
assert server_sock.read(3) == b"foo"
133+
134+
# Test reading partial data:
135+
client_sock.write(b"foobar")
136+
assert server_sock.read(3) == b"foo"
137+
server_io.block_reads = True
138+
assert_poll(server_sock, server_io, _MP_STREAM_POLL_RD, None, _MP_STREAM_POLL_RD) # Did not go to the socket, just consumed buffered data
139+
assert server_sock.read(3) == b"bar"
140+
141+
142+
# Polling on a closed socket errors out:
143+
client_io, _ = _Pipe.new_pair()
144+
client_sock = ussl.wrap_socket(client_io, do_handshake=False)
145+
client_sock.close()
146+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, None, _MP_STREAM_POLL_NVAL) # Did not go to the socket
147+
148+
149+
# Errors propagates to poll:
150+
client_io, server_io = _Pipe.new_pair()
151+
client_sock = ussl.wrap_socket(client_io, do_handshake=False)
152+
153+
# The server returns garbage:
154+
server_io.write(b"fooba") # Needs to be exactly 5 bytes
155+
156+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_RD)
157+
assert_raises(client_sock.read, 128)
158+
assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, None, _MP_STREAM_POLL_NVAL) # Did not go to the socket

tests/extmod/ussl_poll.py.exp

Whitespace-only changes.

tests/run-tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
541541
if not has_coverage:
542542
skip_tests.add("cmdline/cmd_parsetree.py")
543543
skip_tests.add("cmdline/repl_sys_ps1_ps2.py")
544+
skip_tests.add("extmod/ussl_poll.py")
544545

545546
# Some tests shouldn't be run on a PC
546547
if args.target == "unix":

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