Skip to content

Commit 71866ed

Browse files
DvdGiessendpgeorge
authored andcommitted
py/stream: Use detailed error strings for TLS sockets.
Both mbedTLS and axTLS have support for producing more detailed error strings. However, these are not used if the error is raised in stream protocol functions (read/write/ioctl). This commit adds support for more detailed error messages from streams. Under the hood it's using a new MP_STREAM_RAISE_ERROR ioctl request to pass the error code back to the stream implementation which can raise a more detailed error. If the ioctl is not implemented, we fall back to the old behaviour and raise an OSError with the error code. Currently the detailed messages are only implemented for TLS sockets since those already had helper functions for raising detailed exceptions, but can be easily implemented in any other stream. Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
1 parent 633a27b commit 71866ed

5 files changed

Lines changed: 61 additions & 23 deletions

File tree

extmod/modtls_axtls.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,12 @@ static mp_uint_t ssl_socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t ar
390390
ssl_ctx_free(self->ssl_ctx);
391391
self->ssl_sock = NULL;
392392
}
393+
#if MICROPY_STREAMS_DELEGATE_ERROR
394+
else if (request == MP_STREAM_RAISE_ERROR) {
395+
// Raise error with detailed error string
396+
ssl_raise_error((int)arg);
397+
}
398+
#endif
393399

394400
if (self->sock == MP_OBJ_NULL) {
395401
// Underlying socket may be null if the constructor raised an exception.

extmod/modtls_mbedtls.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,14 @@ static mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, i
870870
}
871871
}
872872
}
873-
} else {
873+
}
874+
#if MICROPY_STREAMS_DELEGATE_ERROR
875+
else if (request == MP_STREAM_RAISE_ERROR) {
876+
// Raise error with detailed error string
877+
mbedtls_raise_error((int)arg);
878+
}
879+
#endif
880+
else {
874881
// Unsupported ioctl.
875882
*errcode = MP_EINVAL;
876883
return MP_STREAM_ERROR;

py/mpconfig.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,12 @@ typedef time_t mp_timestamp_t;
10971097
#define MICROPY_STREAMS_POSIX_API (0)
10981098
#endif
10991099

1100+
// Whether to delegate error raising to stream implementations using the
1101+
// MP_STREAM_RAISE_ERROR ioctl to support raising more detailed messages.
1102+
#ifndef MICROPY_STREAMS_DELEGATE_ERROR
1103+
#define MICROPY_STREAMS_DELEGATE_ERROR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
1104+
#endif
1105+
11001106
// Whether to process __all__ when importing all public symbols from a module.
11011107
#ifndef MICROPY_MODULE___ALL__
11021108
#define MICROPY_MODULE___ALL__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES)

py/stream.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) {
108108
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("stream operation not supported"));
109109
}
110110

111+
static MP_NORETURN void mp_stream_raise_error(mp_obj_t stream, int error) {
112+
#if MICROPY_STREAMS_DELEGATE_ERROR
113+
const mp_stream_p_t *stream_p = mp_get_stream(stream);
114+
if (stream_p->ioctl != NULL) {
115+
int err;
116+
stream_p->ioctl(stream, MP_STREAM_RAISE_ERROR, error, &err);
117+
}
118+
#endif
119+
mp_raise_OSError(error);
120+
}
121+
111122
static mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) {
112123
// What to do if sz < -1? Python docs don't specify this case.
113124
// CPython does a readall, but here we silently let negatives through,
@@ -151,7 +162,7 @@ static mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte fl
151162
}
152163
break;
153164
}
154-
mp_raise_OSError(error);
165+
mp_stream_raise_error(args[0], error);
155166
}
156167

157168
if (out_sz < more_bytes) {
@@ -219,7 +230,7 @@ static mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte fl
219230
// this as EOF.
220231
return mp_const_none;
221232
}
222-
mp_raise_OSError(error);
233+
mp_stream_raise_error(args[0], error);
223234
} else {
224235
vstr.len = out_sz;
225236
if (stream_p->is_text) {
@@ -250,7 +261,7 @@ mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len, byte fla
250261
// no single byte could be readily written to it."
251262
return mp_const_none;
252263
}
253-
mp_raise_OSError(error);
264+
mp_stream_raise_error(self_in, error);
254265
} else {
255266
return MP_OBJ_NEW_SMALL_INT(out_sz);
256267
}
@@ -327,7 +338,7 @@ static mp_obj_t stream_readall(mp_obj_t self_in) {
327338
}
328339
break;
329340
}
330-
mp_raise_OSError(error);
341+
mp_stream_raise_error(self_in, error);
331342
}
332343
if (out_sz == 0) {
333344
break;
@@ -385,7 +396,7 @@ static mp_obj_t stream_unbuffered_readline(size_t n_args, const mp_obj_t *args)
385396
goto done;
386397
}
387398
}
388-
mp_raise_OSError(error);
399+
mp_stream_raise_error(args[0], error);
389400
}
390401
if (out_sz == 0) {
391402
done:
@@ -435,7 +446,7 @@ mp_obj_t mp_stream_close(mp_obj_t stream) {
435446
int error;
436447
mp_uint_t res = stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
437448
if (res == MP_STREAM_ERROR) {
438-
mp_raise_OSError(error);
449+
mp_stream_raise_error(stream, error);
439450
}
440451
return mp_const_none;
441452
}
@@ -463,7 +474,7 @@ static mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) {
463474
int error;
464475
mp_off_t res = mp_stream_seek(args[0], offset, whence, &error);
465476
if (res == (mp_off_t)-1) {
466-
mp_raise_OSError(error);
477+
mp_stream_raise_error(args[0], error);
467478
}
468479

469480
// TODO: Could be uint64
@@ -484,7 +495,7 @@ static mp_obj_t stream_flush(mp_obj_t self) {
484495
int error;
485496
mp_uint_t res = stream_p->ioctl(self, MP_STREAM_FLUSH, 0, &error);
486497
if (res == MP_STREAM_ERROR) {
487-
mp_raise_OSError(error);
498+
mp_stream_raise_error(self, error);
488499
}
489500
return mp_const_none;
490501
}
@@ -505,7 +516,7 @@ static mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) {
505516
int error;
506517
mp_uint_t res = stream_p->ioctl(args[0], mp_obj_get_int(args[1]), val, &error);
507518
if (res == MP_STREAM_ERROR) {
508-
mp_raise_OSError(error);
519+
mp_stream_raise_error(args[0], error);
509520
}
510521

511522
return mp_obj_new_int(res);

py/stream.h

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,27 @@
2727
#ifndef MICROPY_INCLUDED_PY_STREAM_H
2828
#define MICROPY_INCLUDED_PY_STREAM_H
2929

30+
#include "py/mpconfig.h"
3031
#include "py/obj.h"
3132
#include "py/mperrno.h"
3233

3334
#define MP_STREAM_ERROR ((mp_uint_t)-1)
3435

3536
// Stream ioctl request codes
36-
#define MP_STREAM_FLUSH (1)
37-
#define MP_STREAM_SEEK (2)
38-
#define MP_STREAM_POLL (3)
39-
#define MP_STREAM_CLOSE (4)
40-
#define MP_STREAM_TIMEOUT (5) // Get/set timeout (single op)
41-
#define MP_STREAM_GET_OPTS (6) // Get stream options
42-
#define MP_STREAM_SET_OPTS (7) // Set stream options
43-
#define MP_STREAM_GET_DATA_OPTS (8) // Get data/message options
44-
#define MP_STREAM_SET_DATA_OPTS (9) // Set data/message options
45-
#define MP_STREAM_GET_FILENO (10) // Get fileno of underlying file
46-
#define MP_STREAM_GET_BUFFER_SIZE (11) // Get preferred buffer size for file
37+
#define MP_STREAM_FLUSH (1)
38+
#define MP_STREAM_SEEK (2)
39+
#define MP_STREAM_POLL (3)
40+
#define MP_STREAM_CLOSE (4)
41+
#define MP_STREAM_TIMEOUT (5) // Get/set timeout (single op)
42+
#define MP_STREAM_GET_OPTS (6) // Get stream options
43+
#define MP_STREAM_SET_OPTS (7) // Set stream options
44+
#define MP_STREAM_GET_DATA_OPTS (8) // Get data/message options
45+
#define MP_STREAM_SET_DATA_OPTS (9) // Set data/message options
46+
#define MP_STREAM_GET_FILENO (10) // Get fileno of underlying file
47+
#define MP_STREAM_GET_BUFFER_SIZE (11) // Get preferred buffer size for file
48+
#if MICROPY_STREAMS_DELEGATE_ERROR
49+
#define MP_STREAM_RAISE_ERROR (12) // Raise an error with detailed error string
50+
#endif
4751

4852
// These poll ioctl values are compatible with Linux
4953
#define MP_STREAM_POLL_RD (0x0001)
@@ -68,8 +72,12 @@ struct mp_stream_seek_t {
6872

6973
// Stream protocol
7074
typedef struct _mp_stream_p_t {
71-
// On error, functions should return MP_STREAM_ERROR and fill in *errcode (values
72-
// are implementation-dependent, but will be exposed to user, e.g. via exception).
75+
// On error, functions should return MP_STREAM_ERROR and fill in *errcode
76+
// (values are are implementation-dependent, but will be exposed to user).
77+
// If MICROPY_STREAMS_DELEGATE_ERROR is enabled and ioctl is not null the
78+
// stream will receive a MP_STREAM_RAISE_ERROR ioctl request with the arg
79+
// containing the errcode, so it may raise a more detailed error. If that
80+
// ioctl returns without raising, an OSError with the errcode will raised.
7381
mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
7482
mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode);
7583
mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);

0 commit comments

Comments
 (0)