Skip to content

Commit 43260c6

Browse files
committed
extmod/modussl: improve exception error messages
1 parent 0bfd55a commit 43260c6

File tree

10 files changed

+236
-10
lines changed

10 files changed

+236
-10
lines changed

extmod/modussl_axtls.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,46 @@ struct ssl_args {
5454

5555
STATIC const mp_obj_type_t ussl_socket_type;
5656

57+
// Table of errors
58+
struct ssl_errs {
59+
int16_t errnum;
60+
const char *errstr;
61+
};
62+
STATIC const struct ssl_errs ssl_error_tab[] = {
63+
{ SSL_NOT_OK, "NOT_OK" },
64+
{ SSL_ERROR_DEAD, "DEAD" },
65+
{ SSL_CLOSE_NOTIFY, "CLOSE_NOTIFY" },
66+
{ SSL_EAGAIN, "EAGAIN" },
67+
{ SSL_ERROR_CONN_LOST, "CONN_LOST" },
68+
{ SSL_ERROR_RECORD_OVERFLOW, "RECORD_OVERFLOW" },
69+
{ SSL_ERROR_SOCK_SETUP_FAILURE, "SOCK_SETUP_FAILURE" },
70+
{ SSL_ERROR_INVALID_HANDSHAKE, "INVALID_HANDSHAKE" },
71+
{ SSL_ERROR_INVALID_PROT_MSG, "INVALID_PROT_MSG" },
72+
{ SSL_ERROR_INVALID_HMAC, "INVALID_HMAC" },
73+
{ SSL_ERROR_INVALID_VERSION, "INVALID_VERSION" },
74+
{ SSL_ERROR_UNSUPPORTED_EXTENSION, "UNSUPPORTED_EXTENSION" },
75+
{ SSL_ERROR_INVALID_SESSION, "INVALID_SESSION" },
76+
{ SSL_ERROR_NO_CIPHER, "NO_CIPHER" },
77+
{ SSL_ERROR_INVALID_CERT_HASH_ALG, "INVALID_CERT_HASH_ALG" },
78+
{ SSL_ERROR_BAD_CERTIFICATE, "BAD_CERTIFICATE" },
79+
{ SSL_ERROR_INVALID_KEY, "INVALID_KEY" },
80+
{ SSL_ERROR_FINISHED_INVALID, "FINISHED_INVALID" },
81+
{ SSL_ERROR_NO_CERT_DEFINED, "NO_CERT_DEFINED" },
82+
{ SSL_ERROR_NO_CLIENT_RENOG, "NO_CLIENT_RENOG" },
83+
{ SSL_ERROR_NOT_SUPPORTED, "NOT_SUPPORTED" },
84+
{ 0, 0 },
85+
};
86+
87+
STATIC NORETURN void ussl_raise_error(int err) {
88+
for (int i = 0; ssl_error_tab[i].errnum != 0; i++) {
89+
if (ssl_error_tab[i].errnum == err) {
90+
mp_raise_msg_varg(&mp_type_OSError, "AXTLS %d: %s", err, ssl_error_tab[i].errstr);
91+
}
92+
}
93+
mp_raise_OSError(err);
94+
}
95+
96+
5797
STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args) {
5898
#if MICROPY_PY_USSL_FINALISER
5999
mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t);
@@ -107,9 +147,8 @@ STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args
107147
int res = ssl_handshake_status(o->ssl_sock);
108148

109149
if (res != SSL_OK) {
110-
printf("ssl_handshake_status: %d\n", res);
111150
ssl_display_error(res);
112-
mp_raise_OSError(MP_EIO);
151+
ussl_raise_error(res);
113152
}
114153
}
115154

extmod/modussl_mbedtls.c

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
#include "py/runtime.h"
3636
#include "py/stream.h"
37+
#include "py/objstr.h"
3738

3839
// mbedtls_time_t
3940
#include "mbedtls/platform.h"
@@ -43,6 +44,8 @@
4344
#include "mbedtls/entropy.h"
4445
#include "mbedtls/ctr_drbg.h"
4546
#include "mbedtls/debug.h"
47+
#include "mbedtls/error.h"
48+
#include MBEDTLS_CONFIG_FILE
4649

4750
typedef struct _mp_obj_ssl_socket_t {
4851
mp_obj_base_t base;
@@ -74,6 +77,41 @@ STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, cons
7477
}
7578
#endif
7679

80+
STATIC NORETURN void mbedtls_raise_error(int err) {
81+
// "small" negative integer error codes come from underlying stream/sockets, not mbedtls
82+
if (err < 0 && err > -256) {
83+
mp_raise_OSError(-err);
84+
}
85+
#if defined(MBEDTLS_ERROR_C)
86+
// Including mbedtls_strerror takes about 16KB on the esp32 due to all the strings.
87+
// MBEDTLS_ERROR_C is the define used by mbedtls to conditionally include mbedtls_strerror.
88+
// It is set/unset in the MBEDTLS_CONFIG_FILE which is defined in the Makefile.
89+
#define ERR_STR_LEN 100 // mbedtls_strerror truncates if it doesn't fit
90+
91+
// Try to allocate memory for the message
92+
mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
93+
byte *o_str_buf = m_new_maybe(byte, ERR_STR_LEN);
94+
if (o_str == NULL || o_str_buf == NULL) {
95+
mp_raise_OSError(err);
96+
}
97+
98+
// print the error message into the allocated buffer
99+
mbedtls_strerror(err, (char *)o_str_buf, ERR_STR_LEN);
100+
size_t len = strnlen((char *)o_str_buf, ERR_STR_LEN);
101+
102+
// Put the exception object together
103+
o_str->base.type = &mp_type_str;
104+
o_str->data = o_str_buf;
105+
o_str->len = len;
106+
o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
107+
// raise
108+
mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)};
109+
nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args));
110+
#else
111+
mp_raise_OSError(err); // typ. err is negative
112+
#endif
113+
}
114+
77115
STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) {
78116
mp_obj_t sock = *(mp_obj_t *)ctx;
79117

@@ -85,7 +123,7 @@ STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) {
85123
if (mp_is_nonblocking_error(err)) {
86124
return MBEDTLS_ERR_SSL_WANT_WRITE;
87125
}
88-
return -err;
126+
return -err; // convert an MP_ERRNO to something mbedtls passes through as error
89127
} else {
90128
return out_sz;
91129
}
@@ -197,7 +235,6 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
197235
if (args->do_handshake.u_bool) {
198236
while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) {
199237
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
200-
printf("mbedtls_ssl_handshake error: -%x\n", -ret);
201238
goto cleanup;
202239
}
203240
}
@@ -221,7 +258,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
221258
} else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) {
222259
mp_raise_ValueError(MP_ERROR_TEXT("invalid cert"));
223260
} else {
224-
mp_raise_OSError(MP_EIO);
261+
mbedtls_raise_error(ret);
225262
}
226263
}
227264

ports/esp32/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ SRC_C = \
354354
mpthreadport.c \
355355
machine_rtc.c \
356356
machine_sdcard.c \
357+
lwip_err.c \
357358
$(wildcard $(BOARD_DIR)/*.c) \
358359
$(SRC_MOD)
359360

@@ -493,7 +494,7 @@ ESPIDF_SPI_FLASH_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/spi_flash/*.c))
493494
ESPIDF_ULP_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/ulp/*.c))
494495

495496
$(BUILD)/$(ESPCOMP)/lwip/%.o: CFLAGS += -Wno-address -Wno-unused-variable -Wno-unused-but-set-variable
496-
ESPIDF_LWIP_O = $(patsubst %.c,%.o,\
497+
ESPIDF_LWIP_O = $(patsubst %.c,%.o, $(filter-out %lwip/src/api/err.c, \
497498
$(wildcard $(ESPCOMP)/lwip/apps/dhcpserver/*.c) \
498499
$(wildcard $(ESPCOMP)/lwip/lwip/src/api/*.c) \
499500
$(wildcard $(ESPCOMP)/lwip/lwip/src/apps/sntp/*.c) \
@@ -504,7 +505,7 @@ ESPIDF_LWIP_O = $(patsubst %.c,%.o,\
504505
$(wildcard $(ESPCOMP)/lwip/lwip/src/netif/*/*/*.c) \
505506
$(wildcard $(ESPCOMP)/lwip/port/esp32/*.c) \
506507
$(wildcard $(ESPCOMP)/lwip/port/esp32/*/*.c) \
507-
)
508+
))
508509

509510
ESPIDF_MBEDTLS_O = $(patsubst %.c,%.o,\
510511
$(wildcard $(ESPCOMP)/mbedtls/mbedtls/library/*.c) \

ports/esp32/lwip_err.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// Micropython note: this is a copy of ESP-IDFv4.0's components/lwip/lwip/src/api/err.c with
3+
// the error EXXX constants replaced by MicroPython's to avoid getting the wrong values because
4+
// ESP-IDFv4.0 uses ones from newlib (in components/newlib) that have a bunch of different values.
5+
// It is assumed that ESP-IDFv4.1 fixes this discrepancy.
6+
7+
/*
8+
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9+
* All rights reserved.
10+
*
11+
* Redistribution and use in source and binary forms, with or without modification,
12+
* are permitted provided that the following conditions are met:
13+
*
14+
* 1. Redistributions of source code must retain the above copyright notice,
15+
* this list of conditions and the following disclaimer.
16+
* 2. Redistributions in binary form must reproduce the above copyright notice,
17+
* this list of conditions and the following disclaimer in the documentation
18+
* and/or other materials provided with the distribution.
19+
* 3. The name of the author may not be used to endorse or promote products
20+
* derived from this software without specific prior written permission.
21+
*
22+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23+
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24+
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25+
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27+
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31+
* OF SUCH DAMAGE.
32+
*
33+
* This file is part of the lwIP TCP/IP stack.
34+
*
35+
* Author: Adam Dunkels <adam@sics.se>
36+
*
37+
*/
38+
39+
#include "lwip/def.h"
40+
#include "lwip/sys.h"
41+
#include "py/mperrno.h"
42+
43+
/** Table to quickly map an lwIP error (err_t) to a socket error
44+
* by using -err as an index */
45+
static const int err_to_errno_table[] = {
46+
0, /* ERR_OK 0 No error, everything OK. */
47+
MP_ENOMEM, /* ERR_MEM -1 Out of memory error. */
48+
MP_ENOBUFS, /* ERR_BUF -2 Buffer error. */
49+
MP_EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
50+
MP_EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
51+
MP_EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
52+
MP_EINVAL, /* ERR_VAL -6 Illegal value. */
53+
MP_EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
54+
MP_EADDRINUSE, /* ERR_USE -8 Address in use. */
55+
MP_EALREADY, /* ERR_ALREADY -9 Already connecting. */
56+
MP_EISCONN, /* ERR_ISCONN -10 Conn already established.*/
57+
MP_ENOTCONN, /* ERR_CONN -11 Not connected. */
58+
-1, /* ERR_IF -12 Low-level netif error */
59+
MP_ECONNABORTED, /* ERR_ABRT -13 Connection aborted. */
60+
MP_ECONNRESET, /* ERR_RST -14 Connection reset. */
61+
MP_ENOTCONN, /* ERR_CLSD -15 Connection closed. */
62+
MP_EIO /* ERR_ARG -16 Illegal argument. */
63+
};
64+
65+
int
66+
err_to_errno(err_t err) {
67+
if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
68+
return EIO;
69+
}
70+
//printf("lwip err_to_errno %d->%d\n", err, err_to_errno_table[-err]);
71+
return err_to_errno_table[-err];
72+
}

ports/esp32/modsocket.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ void usocket_events_handler(void) {
153153

154154
#endif // MICROPY_PY_USOCKET_EVENTS
155155

156+
// EXXX error constants in ESP-IDF v4.0 and prior come from a newlib errno.h which has some
157+
// non-posix values, i.e., different from mperrno.h. This used to get fixed incompletely in
158+
// exception_from_errno, but it turns out to be easier to fix in lwip's err.c which maps lwip's
159+
// netif error codes into EXXX. The old code is left in here until the whole problem really
160+
// goes away with ESP-IDF v4.1 (we hope).
161+
#define exception_from_errno(errno) mp_raise_OSError(errno)
162+
#if 0
156163
NORETURN static void exception_from_errno(int _errno) {
157164
// Here we need to convert from lwip errno values to MicroPython's standard ones
158165
if (_errno == EADDRINUSE) {
@@ -162,6 +169,7 @@ NORETURN static void exception_from_errno(int _errno) {
162169
}
163170
mp_raise_OSError(_errno);
164171
}
172+
#endif
165173

166174
static inline void check_for_exceptions(void) {
167175
mp_handle_pending(true);

tests/extmod/ussl_basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
# create in client mode
1111
try:
12-
ss = ssl.wrap_socket(io.BytesIO())
12+
ss = ssl.wrap_socket(io.BytesIO(), server_hostname="test.example.com")
1313
except OSError as er:
1414
print("wrap_socket:", repr(er))
1515

tests/extmod/ussl_basic.py.exp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
ssl_handshake_status: -256
2-
wrap_socket: OSError(5,)
1+
wrap_socket: OSError('AXTLS -256: CONN_LOST',)
32
<_SSLSocket
43
4
54
b''

tests/net_inet/tls_num_errors.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# test that modtls produces a text error message
2+
3+
try:
4+
import usocket as socket, ussl as ssl, sys, micropython
5+
except:
6+
import socket, ssl, sys
7+
8+
9+
# test with heap locked to see it switch to number-only error message
10+
def test(addr):
11+
micropython.alloc_emergency_exception_buf(256)
12+
s = socket.socket()
13+
s.connect(addr)
14+
try:
15+
s.setblocking(False)
16+
s = ssl.wrap_socket(s, do_handshake=False)
17+
micropython.heap_lock()
18+
print("heap is locked")
19+
while True:
20+
ret = s.write('foo')
21+
if ret:
22+
break
23+
micropython.heap_unlock()
24+
print("wrap: no exception")
25+
except OSError as e:
26+
micropython.heap_unlock()
27+
# mbedtls produces "-29184"
28+
# axtls produces "RECORD_OVERFLOW"
29+
ok = "-29184" in str(e) or "RECORD_OVERFLOW" in str(e)
30+
print("wrap:", ok)
31+
if not ok:
32+
print("got exception:", e)
33+
s.close()
34+
35+
36+
if __name__ == "__main__":
37+
# connect to plain HTTP port, oops!
38+
addr = socket.getaddrinfo("micropython.org", 80)[0][-1]
39+
test(addr)

tests/net_inet/tls_num_errors.py.exp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
heap is locked
2+
wrap: True

tests/net_inet/tls_text_errors.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# test that modtls produces a text error message
2+
3+
try:
4+
import usocket as socket, ussl as ssl, sys
5+
except:
6+
import socket, ssl, sys
7+
8+
9+
def test(addr):
10+
s = socket.socket()
11+
s.connect(addr)
12+
try:
13+
s = ssl.wrap_socket(s)
14+
print("wrap: no exception")
15+
except OSError as e:
16+
# mbedtls produces "mbedtls -0x7200: SSL - An invalid SSL record was received"
17+
# axtls produces "RECORD_OVERFLOW"
18+
# CPython produces "[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1108)"
19+
ok = "invalid SSL record" in str(e) or "RECORD_OVERFLOW" in str(e) or \
20+
"wrong version" in str(e)
21+
print("wrap:", ok)
22+
if not ok:
23+
print("got exception:", e)
24+
s.close()
25+
26+
if __name__ == "__main__":
27+
# connect to plain HTTP port, oops!
28+
addr = socket.getaddrinfo("micropython.org", 80)[0][-1]
29+
test(addr)

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