freedos-micro-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- freedos_micro_python/__init__.py +8 -0
- freedos_micro_python/cli.py +106 -0
- freedos_micro_python/gen_qstrdefs.py +275 -0
- freedos_micro_python/port/arch/bpstruct.h +2 -0
- freedos_micro_python/port/arch/cc.h +4 -0
- freedos_micro_python/port/arch/epstruct.h +1 -0
- freedos_micro_python/port/base64_uc386dos.c +164 -0
- freedos_micro_python/port/file_uc386dos.c +228 -0
- freedos_micro_python/port/lib/axtls/crypto/crypto.h +45 -0
- freedos_micro_python/port/lwip-arch-cc.h +46 -0
- freedos_micro_python/port/lwip_uc386dos.c +248 -0
- freedos_micro_python/port/lwipopts.h +117 -0
- freedos_micro_python/port/math_gamma.c +63 -0
- freedos_micro_python/port/modtime_uc386dos.c +60 -0
- freedos_micro_python/port/modtls_axtls_uc386dos.c +461 -0
- freedos_micro_python/port/mpconfigport.h +358 -0
- freedos_micro_python/port/mphal_uc386dos.c +103 -0
- freedos_micro_python/port/mphalport.h +11 -0
- freedos_micro_python/port/os_uc386dos.c +264 -0
- freedos_micro_python/port/path_uc386dos.c +307 -0
- freedos_micro_python/port/pktdrv_uc386dos.c +650 -0
- freedos_micro_python/port/qstrdefsport.h +2 -0
- freedos_micro_python/port/shutil_uc386dos.c +111 -0
- freedos_micro_python/port/tempfile_uc386dos.c +129 -0
- freedos_micro_python/port/time_real_uc386dos.c +77 -0
- freedos_micro_python/port/uc386_net_uc386dos.c +126 -0
- freedos_micro_python/port/urllib_parse_uc386dos.c +360 -0
- freedos_micro_python/port/urllib_uc386dos.c +29 -0
- freedos_micro_python/scripts/build.sh +641 -0
- freedos_micro_python/scripts/build_port.sh +241 -0
- freedos_micro_python/scripts/fetch.sh +238 -0
- freedos_micro_python-0.1.0.dist-info/METADATA +131 -0
- freedos_micro_python-0.1.0.dist-info/RECORD +37 -0
- freedos_micro_python-0.1.0.dist-info/WHEEL +5 -0
- freedos_micro_python-0.1.0.dist-info/entry_points.txt +2 -0
- freedos_micro_python-0.1.0.dist-info/licenses/LICENSE +25 -0
- freedos_micro_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Lanczos approximation for tgamma + lgamma.
|
|
2
|
+
// Replaces the NaN stubs that lived in lib/i386_dos_libc.asm.
|
|
3
|
+
//
|
|
4
|
+
// References:
|
|
5
|
+
// - Numerical Recipes in C, §6.1
|
|
6
|
+
// - https://en.wikipedia.org/wiki/Lanczos_approximation
|
|
7
|
+
//
|
|
8
|
+
// Coefficients (g=5, n=6) from Press et al. give ~10⁻¹⁰ relative
|
|
9
|
+
// error on the half-plane Re(z) ≥ 0.5. For x < 0.5 we use the
|
|
10
|
+
// reflection formula
|
|
11
|
+
// gamma(z) * gamma(1 - z) = pi / sin(pi z)
|
|
12
|
+
//
|
|
13
|
+
// uc386 lowers `double` through the x87 FPU and has sin/cos/exp/
|
|
14
|
+
// log/pow/sqrt in the libc — Lanczos compiles to a few dozen FPU
|
|
15
|
+
// ops via the C math primitives.
|
|
16
|
+
|
|
17
|
+
#include <math.h>
|
|
18
|
+
|
|
19
|
+
static const double LANCZOS_G = 5.0;
|
|
20
|
+
static const double LANCZOS_C[7] = {
|
|
21
|
+
1.000000000190015,
|
|
22
|
+
76.18009172947146,
|
|
23
|
+
-86.50532032941677,
|
|
24
|
+
24.01409824083091,
|
|
25
|
+
-1.231739572450155,
|
|
26
|
+
1.208650973866179e-3,
|
|
27
|
+
-5.395239384953e-6,
|
|
28
|
+
};
|
|
29
|
+
#define LANCZOS_SQRT_2PI 2.5066282746310005
|
|
30
|
+
|
|
31
|
+
double tgamma(double x) {
|
|
32
|
+
// Special cases.
|
|
33
|
+
if (isnan(x)) return x;
|
|
34
|
+
if (x == 0.0) return 1.0 / x; // ±inf, preserves sign
|
|
35
|
+
if (x < 0.0 && x == (double)(long long)x) return 0.0 / 0.0; // negative integer → NaN
|
|
36
|
+
if (isinf(x) && x > 0.0) return x;
|
|
37
|
+
|
|
38
|
+
// Reflection for x < 0.5.
|
|
39
|
+
if (x < 0.5) {
|
|
40
|
+
double pi = 3.141592653589793;
|
|
41
|
+
return pi / (sin(pi * x) * tgamma(1.0 - x));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Lanczos: shift z = x - 1 so the formula gives gamma(z+1) = gamma(x).
|
|
45
|
+
double z = x - 1.0;
|
|
46
|
+
double t = z + LANCZOS_G + 0.5;
|
|
47
|
+
double sum = LANCZOS_C[0];
|
|
48
|
+
for (int k = 1; k <= 6; k++) {
|
|
49
|
+
sum += LANCZOS_C[k] / (z + (double)k);
|
|
50
|
+
}
|
|
51
|
+
return LANCZOS_SQRT_2PI * pow(t, z + 0.5) * exp(-t) * sum;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
double lgamma(double x) {
|
|
55
|
+
// log(|gamma(x)|), implemented via tgamma. For large positive
|
|
56
|
+
// x where tgamma overflows, this loses precision; a proper
|
|
57
|
+
// lgamma would use Stirling-with-Bernoulli directly. Adequate
|
|
58
|
+
// for moderate ranges.
|
|
59
|
+
if (isnan(x)) return x;
|
|
60
|
+
if (isinf(x)) return x > 0.0 ? x : -x;
|
|
61
|
+
double g = tgamma(x);
|
|
62
|
+
return log(g < 0.0 ? -g : g);
|
|
63
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// uc386-dos `time` module port shim — included into
|
|
2
|
+
// `extmod/modtime.c` via `MICROPY_PY_TIME_INCLUDEFILE`. Provides
|
|
3
|
+
// `mp_time_time_get` (used by `time.time()`) and
|
|
4
|
+
// `mp_time_localtime_get` (used by `time.localtime()` / `gmtime()`)
|
|
5
|
+
// by reading the DOS clock via INT 21h AH=0x2A (date) and
|
|
6
|
+
// AH=0x2C (time). Matching `mp_hal_time_ns` lives in
|
|
7
|
+
// `mphal_uc386dos.c`.
|
|
8
|
+
//
|
|
9
|
+
// Caveats:
|
|
10
|
+
// - DOS has only second-precision clock; the time_ns helper
|
|
11
|
+
// multiplies seconds * 1e9 (no sub-second contribution).
|
|
12
|
+
// - DOS dates start in 1980, so anything before that comes back
|
|
13
|
+
// as 1970-01-01.
|
|
14
|
+
// - Default uc386-dos epoch is 2000-01-01 (mpconfig.h's default
|
|
15
|
+
// `MICROPY_EPOCH_IS_2000=1`); seconds returned are
|
|
16
|
+
// `seconds_since_2000`.
|
|
17
|
+
// - Smoke tests under dos_emu get a synthetic deterministic
|
|
18
|
+
// date (2026-05-03 12:34:00) — see src/uc386/dos_emu.py's
|
|
19
|
+
// INT 21h AH=0x2A/0x2C handlers. Real DOS via PMODE/W reads
|
|
20
|
+
// the host's RTC.
|
|
21
|
+
|
|
22
|
+
#include "shared/timeutils/timeutils.h"
|
|
23
|
+
|
|
24
|
+
extern void dos_get_datetime(unsigned char out[7]);
|
|
25
|
+
|
|
26
|
+
static inline void uc386dos_read_datetime(timeutils_struct_time_t *tm) {
|
|
27
|
+
unsigned char raw[7];
|
|
28
|
+
dos_get_datetime(raw);
|
|
29
|
+
tm->tm_year = (uint16_t)(raw[0] | (raw[1] << 8));
|
|
30
|
+
tm->tm_mon = raw[2];
|
|
31
|
+
tm->tm_mday = raw[3];
|
|
32
|
+
tm->tm_hour = raw[4];
|
|
33
|
+
tm->tm_min = raw[5];
|
|
34
|
+
tm->tm_sec = raw[6];
|
|
35
|
+
// tm_wday / tm_yday left zero (timeutils helpers fill them
|
|
36
|
+
// when they convert seconds → struct_time, but the DOS
|
|
37
|
+
// get-date INT only returns day-of-week which we don't
|
|
38
|
+
// bother harvesting).
|
|
39
|
+
tm->tm_wday = 0;
|
|
40
|
+
tm->tm_yday = 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static mp_obj_t mp_time_time_get(void) {
|
|
44
|
+
timeutils_struct_time_t tm;
|
|
45
|
+
uc386dos_read_datetime(&tm);
|
|
46
|
+
// With MICROPY_TIMESTAMP_IMPL=1 (UINT) `mp_timestamp_t` is
|
|
47
|
+
// `mp_uint_t` (32-bit on i386). seconds_since_epoch fits a
|
|
48
|
+
// 32-bit small int through year 2068 (epoch=1970) or
|
|
49
|
+
// year 2136 (epoch=2000) — fine for DOS. Avoid
|
|
50
|
+
// `mp_obj_new_int_from_ll` here: it's a stub that always
|
|
51
|
+
// raises OverflowError when LONGINT_IMPL_NONE.
|
|
52
|
+
mp_timestamp_t secs = timeutils_seconds_since_epoch(
|
|
53
|
+
tm.tm_year, tm.tm_mon, tm.tm_mday,
|
|
54
|
+
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
55
|
+
return mp_obj_new_int_from_uint((mp_uint_t)secs);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static void mp_time_localtime_get(timeutils_struct_time_t *tm) {
|
|
59
|
+
uc386dos_read_datetime(tm);
|
|
60
|
+
}
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* uc386-dos modtls — fork of upstream/extmod/modtls_axtls.c
|
|
3
|
+
* (https://github.com/micropython/micropython, MIT) with the
|
|
4
|
+
* CPython-compatible cert-verification surface that upstream's
|
|
5
|
+
* minimal version omits:
|
|
6
|
+
*
|
|
7
|
+
* - `verify_mode` is a real settable attribute (CERT_NONE /
|
|
8
|
+
* CERT_REQUIRED), backed by a field on the SSLContext.
|
|
9
|
+
* - `SSLContext.load_verify_locations(cadata=...)` loads one
|
|
10
|
+
* or more PEM-encoded CA certs into the context (axtls's
|
|
11
|
+
* SSL_OBJ_X509_CACERT slot). cadata accepts a bytes/str of
|
|
12
|
+
* PEM data; cafile/capath aren't supported (no VFS exposure
|
|
13
|
+
* to axtls).
|
|
14
|
+
* - When `verify_mode == CERT_REQUIRED`, the wrap_socket path
|
|
15
|
+
* drops the `SSL_SERVER_VERIFY_LATER` option so axtls fails
|
|
16
|
+
* the handshake on bad certs instead of silently accepting.
|
|
17
|
+
*
|
|
18
|
+
* Build-side: this file replaces upstream's modtls_axtls.c in the
|
|
19
|
+
* source list. Both register `MP_QSTR_tls`, but only one TU
|
|
20
|
+
* actually compiles thanks to the swap in build_port.sh.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
#include <stdio.h>
|
|
24
|
+
#include <string.h>
|
|
25
|
+
|
|
26
|
+
#include "py/runtime.h"
|
|
27
|
+
#include "py/stream.h"
|
|
28
|
+
#include "py/objstr.h"
|
|
29
|
+
|
|
30
|
+
#if MICROPY_PY_SSL && MICROPY_SSL_AXTLS
|
|
31
|
+
|
|
32
|
+
#include "ssl.h"
|
|
33
|
+
|
|
34
|
+
#define PROTOCOL_TLS_CLIENT (0)
|
|
35
|
+
#define PROTOCOL_TLS_SERVER (1)
|
|
36
|
+
|
|
37
|
+
#define CERT_NONE (0)
|
|
38
|
+
#define CERT_REQUIRED (2) // matches CPython ssl.CERT_REQUIRED
|
|
39
|
+
|
|
40
|
+
typedef struct _mp_obj_ssl_context_t {
|
|
41
|
+
mp_obj_base_t base;
|
|
42
|
+
mp_obj_t key;
|
|
43
|
+
mp_obj_t cert;
|
|
44
|
+
SSL_CTX *ssl_ctx;
|
|
45
|
+
int verify_mode;
|
|
46
|
+
} mp_obj_ssl_context_t;
|
|
47
|
+
|
|
48
|
+
typedef struct _mp_obj_ssl_socket_t {
|
|
49
|
+
mp_obj_base_t base;
|
|
50
|
+
mp_obj_t sock;
|
|
51
|
+
SSL_CTX *ssl_ctx;
|
|
52
|
+
SSL *ssl_sock;
|
|
53
|
+
byte *buf;
|
|
54
|
+
uint32_t bytes_left;
|
|
55
|
+
bool blocking;
|
|
56
|
+
} mp_obj_ssl_socket_t;
|
|
57
|
+
|
|
58
|
+
static const mp_obj_type_t ssl_context_type;
|
|
59
|
+
static const mp_obj_type_t ssl_socket_type;
|
|
60
|
+
|
|
61
|
+
static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t sock,
|
|
62
|
+
bool server_side, bool do_handshake_on_connect, mp_obj_t server_hostname);
|
|
63
|
+
|
|
64
|
+
// Error-string tables (verbatim from upstream).
|
|
65
|
+
static const char *const ssl_error_tab1[] = {
|
|
66
|
+
"NOT_OK", "DEAD", "CLOSE_NOTIFY", "EAGAIN",
|
|
67
|
+
};
|
|
68
|
+
static const char *const ssl_error_tab2[] = {
|
|
69
|
+
"CONN_LOST", "RECORD_OVERFLOW", "SOCK_SETUP_FAILURE", NULL,
|
|
70
|
+
"INVALID_HANDSHAKE", "INVALID_PROT_MSG", "INVALID_HMAC",
|
|
71
|
+
"INVALID_VERSION", "UNSUPPORTED_EXTENSION", "INVALID_SESSION",
|
|
72
|
+
"NO_CIPHER", "INVALID_CERT_HASH_ALG", "BAD_CERTIFICATE",
|
|
73
|
+
"INVALID_KEY", NULL, "FINISHED_INVALID", "NO_CERT_DEFINED",
|
|
74
|
+
"NO_CLIENT_RENOG", "NOT_SUPPORTED",
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
static MP_NORETURN void ssl_raise_error(int err) {
|
|
78
|
+
MP_STATIC_ASSERT(SSL_NOT_OK - 3 == SSL_EAGAIN);
|
|
79
|
+
MP_STATIC_ASSERT(SSL_ERROR_CONN_LOST - 18 == SSL_ERROR_NOT_SUPPORTED);
|
|
80
|
+
|
|
81
|
+
const char *errstr = NULL;
|
|
82
|
+
if (SSL_NOT_OK >= err && err >= SSL_EAGAIN) {
|
|
83
|
+
errstr = ssl_error_tab1[SSL_NOT_OK - err];
|
|
84
|
+
} else if (SSL_ERROR_CONN_LOST >= err && err >= SSL_ERROR_NOT_SUPPORTED) {
|
|
85
|
+
errstr = ssl_error_tab2[SSL_ERROR_CONN_LOST - err];
|
|
86
|
+
}
|
|
87
|
+
if (errstr == NULL) {
|
|
88
|
+
mp_raise_OSError(err);
|
|
89
|
+
}
|
|
90
|
+
mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
|
|
91
|
+
if (o_str == NULL) {
|
|
92
|
+
mp_raise_OSError(err);
|
|
93
|
+
}
|
|
94
|
+
o_str->base.type = &mp_type_str;
|
|
95
|
+
o_str->data = (const byte *)errstr;
|
|
96
|
+
o_str->len = strlen((char *)o_str->data);
|
|
97
|
+
o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
|
|
98
|
+
|
|
99
|
+
mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str) };
|
|
100
|
+
nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// SSLContext —————————————————————————————————————————————————
|
|
104
|
+
|
|
105
|
+
static mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args,
|
|
106
|
+
size_t n_kw, const mp_obj_t *args) {
|
|
107
|
+
mp_arg_check_num(n_args, n_kw, 1, 1, false);
|
|
108
|
+
// The `protocol` argument is accepted but ignored — axtls picks
|
|
109
|
+
// its own version from CONFIG_SSL_PROT_LOW.
|
|
110
|
+
|
|
111
|
+
#if MICROPY_PY_SSL_FINALISER
|
|
112
|
+
mp_obj_ssl_context_t *self = mp_obj_malloc_with_finaliser(mp_obj_ssl_context_t, type_in);
|
|
113
|
+
#else
|
|
114
|
+
mp_obj_ssl_context_t *self = mp_obj_malloc(mp_obj_ssl_context_t, type_in);
|
|
115
|
+
#endif
|
|
116
|
+
self->key = mp_const_none;
|
|
117
|
+
self->cert = mp_const_none;
|
|
118
|
+
self->verify_mode = CERT_NONE;
|
|
119
|
+
|
|
120
|
+
// We allocate the SSL_CTX up front so load_verify_locations /
|
|
121
|
+
// load_cert_chain can stash data into it before any wrap_socket.
|
|
122
|
+
// SSL_DEFAULT_CLNT_SESS = 1 (single session resumption slot).
|
|
123
|
+
self->ssl_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER, SSL_DEFAULT_CLNT_SESS);
|
|
124
|
+
if (self->ssl_ctx == NULL) {
|
|
125
|
+
mp_raise_OSError(MP_ENOMEM);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return MP_OBJ_FROM_PTR(self);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
|
132
|
+
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in);
|
|
133
|
+
if (dest[0] == MP_OBJ_NULL) {
|
|
134
|
+
// Load.
|
|
135
|
+
if (attr == MP_QSTR_verify_mode) {
|
|
136
|
+
dest[0] = MP_OBJ_NEW_SMALL_INT(self->verify_mode);
|
|
137
|
+
} else {
|
|
138
|
+
dest[1] = MP_OBJ_SENTINEL;
|
|
139
|
+
}
|
|
140
|
+
} else if (dest[1] != MP_OBJ_NULL) {
|
|
141
|
+
// Store.
|
|
142
|
+
if (attr == MP_QSTR_verify_mode) {
|
|
143
|
+
int v = mp_obj_get_int(dest[1]);
|
|
144
|
+
if (v != CERT_NONE && v != CERT_REQUIRED) {
|
|
145
|
+
mp_raise_ValueError(MP_ERROR_TEXT("verify_mode must be CERT_NONE or CERT_REQUIRED"));
|
|
146
|
+
}
|
|
147
|
+
self->verify_mode = v;
|
|
148
|
+
dest[0] = MP_OBJ_NULL; // signal: store accepted
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static mp_obj_t ssl_context_load_cert_chain(mp_obj_t self_in, mp_obj_t cert, mp_obj_t pkey) {
|
|
154
|
+
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in);
|
|
155
|
+
self->cert = cert;
|
|
156
|
+
self->key = pkey;
|
|
157
|
+
return mp_const_none;
|
|
158
|
+
}
|
|
159
|
+
static MP_DEFINE_CONST_FUN_OBJ_3(ssl_context_load_cert_chain_obj, ssl_context_load_cert_chain);
|
|
160
|
+
|
|
161
|
+
// SSLContext.load_verify_locations(*, cafile=None, cadata=None)
|
|
162
|
+
// - cafile: ignored (no VFS bridge to axtls); raises if non-None.
|
|
163
|
+
// - cadata: bytes/str of PEM-encoded CA certs (concatenated).
|
|
164
|
+
// One call may load multiple certs — axtls's ssl_obj_memory_load
|
|
165
|
+
// walks consecutive PEM blocks when CONFIG_SSL_HAS_PEM=1.
|
|
166
|
+
static mp_obj_t ssl_context_load_verify_locations(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
167
|
+
enum { ARG_cafile, ARG_cadata };
|
|
168
|
+
static const mp_arg_t allowed_args[] = {
|
|
169
|
+
{ MP_QSTR_cafile, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
|
170
|
+
{ MP_QSTR_cadata, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
174
|
+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
175
|
+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
|
|
176
|
+
MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
177
|
+
|
|
178
|
+
if (args[ARG_cafile].u_obj != mp_const_none) {
|
|
179
|
+
mp_raise_NotImplementedError(MP_ERROR_TEXT("cafile not supported (use cadata=)"));
|
|
180
|
+
}
|
|
181
|
+
if (args[ARG_cadata].u_obj == mp_const_none) {
|
|
182
|
+
mp_raise_TypeError(MP_ERROR_TEXT("cadata is required"));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
size_t len;
|
|
186
|
+
const byte *data = (const byte *)mp_obj_str_get_data(args[ARG_cadata].u_obj, &len);
|
|
187
|
+
int res = ssl_obj_memory_load(self->ssl_ctx, SSL_OBJ_X509_CACERT, data, (int)len, NULL);
|
|
188
|
+
if (res != SSL_OK) {
|
|
189
|
+
ssl_raise_error(res);
|
|
190
|
+
}
|
|
191
|
+
return mp_const_none;
|
|
192
|
+
}
|
|
193
|
+
static MP_DEFINE_CONST_FUN_OBJ_KW(ssl_context_load_verify_locations_obj, 1, ssl_context_load_verify_locations);
|
|
194
|
+
|
|
195
|
+
static mp_obj_t ssl_context_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
196
|
+
enum { ARG_server_side, ARG_do_handshake_on_connect, ARG_server_hostname };
|
|
197
|
+
static const mp_arg_t allowed_args[] = {
|
|
198
|
+
{ MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
|
|
199
|
+
{ MP_QSTR_do_handshake_on_connect, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
|
|
200
|
+
{ MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
204
|
+
mp_obj_t sock = pos_args[1];
|
|
205
|
+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
206
|
+
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args,
|
|
207
|
+
MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
208
|
+
|
|
209
|
+
return ssl_socket_make_new(self, sock, args[ARG_server_side].u_bool,
|
|
210
|
+
args[ARG_do_handshake_on_connect].u_bool, args[ARG_server_hostname].u_obj);
|
|
211
|
+
}
|
|
212
|
+
static MP_DEFINE_CONST_FUN_OBJ_KW(ssl_context_wrap_socket_obj, 2, ssl_context_wrap_socket);
|
|
213
|
+
|
|
214
|
+
static const mp_rom_map_elem_t ssl_context_locals_dict_table[] = {
|
|
215
|
+
{ MP_ROM_QSTR(MP_QSTR_load_cert_chain), MP_ROM_PTR(&ssl_context_load_cert_chain_obj) },
|
|
216
|
+
{ MP_ROM_QSTR(MP_QSTR_load_verify_locations), MP_ROM_PTR(&ssl_context_load_verify_locations_obj) },
|
|
217
|
+
{ MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&ssl_context_wrap_socket_obj) },
|
|
218
|
+
};
|
|
219
|
+
static MP_DEFINE_CONST_DICT(ssl_context_locals_dict, ssl_context_locals_dict_table);
|
|
220
|
+
|
|
221
|
+
static MP_DEFINE_CONST_OBJ_TYPE(
|
|
222
|
+
ssl_context_type,
|
|
223
|
+
MP_QSTR_SSLContext,
|
|
224
|
+
MP_TYPE_FLAG_NONE,
|
|
225
|
+
make_new, ssl_context_make_new,
|
|
226
|
+
attr, ssl_context_attr,
|
|
227
|
+
locals_dict, &ssl_context_locals_dict
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// SSLSocket ——————————————————————————————————————————————————
|
|
231
|
+
|
|
232
|
+
static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t sock,
|
|
233
|
+
bool server_side, bool do_handshake_on_connect, mp_obj_t server_hostname) {
|
|
234
|
+
|
|
235
|
+
#if MICROPY_PY_SSL_FINALISER
|
|
236
|
+
mp_obj_ssl_socket_t *o = mp_obj_malloc_with_finaliser(mp_obj_ssl_socket_t, &ssl_socket_type);
|
|
237
|
+
#else
|
|
238
|
+
mp_obj_ssl_socket_t *o = mp_obj_malloc(mp_obj_ssl_socket_t, &ssl_socket_type);
|
|
239
|
+
#endif
|
|
240
|
+
o->buf = NULL;
|
|
241
|
+
o->bytes_left = 0;
|
|
242
|
+
o->sock = MP_OBJ_NULL;
|
|
243
|
+
o->blocking = true;
|
|
244
|
+
o->ssl_ctx = ssl_context->ssl_ctx;
|
|
245
|
+
|
|
246
|
+
if (ssl_context->key != mp_const_none) {
|
|
247
|
+
size_t len;
|
|
248
|
+
const byte *data = (const byte *)mp_obj_str_get_data(ssl_context->key, &len);
|
|
249
|
+
int res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_RSA_KEY, data, len, NULL);
|
|
250
|
+
if (res != SSL_OK) {
|
|
251
|
+
mp_raise_ValueError(MP_ERROR_TEXT("invalid key"));
|
|
252
|
+
}
|
|
253
|
+
data = (const byte *)mp_obj_str_get_data(ssl_context->cert, &len);
|
|
254
|
+
res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_X509_CERT, data, len, NULL);
|
|
255
|
+
if (res != SSL_OK) {
|
|
256
|
+
mp_raise_ValueError(MP_ERROR_TEXT("invalid cert"));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (server_side) {
|
|
261
|
+
o->ssl_sock = ssl_server_new(o->ssl_ctx, (long)sock);
|
|
262
|
+
} else {
|
|
263
|
+
SSL_EXTENSIONS *ext = ssl_ext_new();
|
|
264
|
+
if (server_hostname != mp_const_none) {
|
|
265
|
+
ext->host_name = (char *)mp_obj_str_get_str(server_hostname);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// CERT_NONE: keep SSL_SERVER_VERIFY_LATER so axtls accepts
|
|
269
|
+
// any cert. CERT_REQUIRED: clear it so axtls fails the
|
|
270
|
+
// handshake on chain validation errors. The CTX-level
|
|
271
|
+
// options were stamped at ssl_ctx_new time; per-session
|
|
272
|
+
// override goes through ssl_client_new's own option mask
|
|
273
|
+
// — except axtls doesn't have one. The CTX option therefore
|
|
274
|
+
// governs. When the SSLContext was created we set
|
|
275
|
+
// SSL_SERVER_VERIFY_LATER unconditionally; we now (lazily)
|
|
276
|
+
// adjust it on the ctx if verify_mode is CERT_REQUIRED.
|
|
277
|
+
if (ssl_context->verify_mode == CERT_REQUIRED) {
|
|
278
|
+
// axtls's SSL_CTX has the options on the ssl_ctx struct
|
|
279
|
+
// itself; the public way to flip is to rebuild the ctx,
|
|
280
|
+
// but for our config (skeleton client) we can poke it.
|
|
281
|
+
// Cleaner: extend ssl_ctx_new at ctx creation time —
|
|
282
|
+
// but that means we'd have to know verify_mode before
|
|
283
|
+
// load_verify_locations was called. So do it here.
|
|
284
|
+
o->ssl_ctx->options &= ~SSL_SERVER_VERIFY_LATER;
|
|
285
|
+
} else {
|
|
286
|
+
o->ssl_ctx->options |= SSL_SERVER_VERIFY_LATER;
|
|
287
|
+
}
|
|
288
|
+
if (!do_handshake_on_connect) {
|
|
289
|
+
o->ssl_ctx->options |= SSL_CONNECT_IN_PARTS;
|
|
290
|
+
} else {
|
|
291
|
+
o->ssl_ctx->options &= ~SSL_CONNECT_IN_PARTS;
|
|
292
|
+
}
|
|
293
|
+
if (ssl_context->key != mp_const_none) {
|
|
294
|
+
o->ssl_ctx->options |= SSL_NO_DEFAULT_KEY;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
o->ssl_sock = ssl_client_new(o->ssl_ctx, (long)sock, NULL, 0, ext);
|
|
298
|
+
|
|
299
|
+
if (do_handshake_on_connect) {
|
|
300
|
+
int r = ssl_handshake_status(o->ssl_sock);
|
|
301
|
+
if (r != SSL_OK) {
|
|
302
|
+
if (r == SSL_CLOSE_NOTIFY) {
|
|
303
|
+
r = MP_ENOTCONN;
|
|
304
|
+
} else if (r == SSL_EAGAIN) {
|
|
305
|
+
r = MP_EAGAIN;
|
|
306
|
+
}
|
|
307
|
+
ssl_raise_error(r);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
o->sock = sock;
|
|
313
|
+
return o;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
static mp_uint_t ssl_socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
|
|
317
|
+
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
|
|
318
|
+
if (o->ssl_sock == NULL) {
|
|
319
|
+
*errcode = EBADF;
|
|
320
|
+
return MP_STREAM_ERROR;
|
|
321
|
+
}
|
|
322
|
+
while (o->bytes_left == 0) {
|
|
323
|
+
mp_int_t r = ssl_read(o->ssl_sock, &o->buf);
|
|
324
|
+
if (r == SSL_OK) {
|
|
325
|
+
if (o->blocking) {
|
|
326
|
+
continue;
|
|
327
|
+
} else {
|
|
328
|
+
goto eagain;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (r < 0) {
|
|
332
|
+
if (r == SSL_CLOSE_NOTIFY || r == SSL_ERROR_CONN_LOST) {
|
|
333
|
+
return 0;
|
|
334
|
+
}
|
|
335
|
+
if (r == SSL_EAGAIN) {
|
|
336
|
+
eagain:
|
|
337
|
+
r = MP_EAGAIN;
|
|
338
|
+
}
|
|
339
|
+
*errcode = r;
|
|
340
|
+
return MP_STREAM_ERROR;
|
|
341
|
+
}
|
|
342
|
+
o->bytes_left = r;
|
|
343
|
+
}
|
|
344
|
+
if (size > o->bytes_left) {
|
|
345
|
+
size = o->bytes_left;
|
|
346
|
+
}
|
|
347
|
+
memcpy(buf, o->buf, size);
|
|
348
|
+
o->buf += size;
|
|
349
|
+
o->bytes_left -= size;
|
|
350
|
+
return size;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
static mp_uint_t ssl_socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
|
|
354
|
+
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
|
|
355
|
+
if (o->ssl_sock == NULL) {
|
|
356
|
+
*errcode = EBADF;
|
|
357
|
+
return MP_STREAM_ERROR;
|
|
358
|
+
}
|
|
359
|
+
mp_int_t r;
|
|
360
|
+
eagain:
|
|
361
|
+
r = ssl_write(o->ssl_sock, buf, size);
|
|
362
|
+
if (r == 0) {
|
|
363
|
+
if (o->blocking) {
|
|
364
|
+
goto eagain;
|
|
365
|
+
} else {
|
|
366
|
+
r = SSL_EAGAIN;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (r < 0) {
|
|
370
|
+
if (r == SSL_CLOSE_NOTIFY || r == SSL_ERROR_CONN_LOST) {
|
|
371
|
+
return 0;
|
|
372
|
+
}
|
|
373
|
+
if (r == SSL_EAGAIN) {
|
|
374
|
+
r = MP_EAGAIN;
|
|
375
|
+
}
|
|
376
|
+
*errcode = r;
|
|
377
|
+
return MP_STREAM_ERROR;
|
|
378
|
+
}
|
|
379
|
+
return r;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
static mp_uint_t ssl_socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
|
383
|
+
mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in);
|
|
384
|
+
if (request == MP_STREAM_CLOSE) {
|
|
385
|
+
if (self->ssl_sock == NULL) {
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
ssl_free(self->ssl_sock);
|
|
389
|
+
// Note: we DON'T ssl_ctx_free here — the ctx is owned by
|
|
390
|
+
// the SSLContext now, so multiple wrap_socket calls share
|
|
391
|
+
// the same ctx (and its loaded CA bundle).
|
|
392
|
+
self->ssl_sock = NULL;
|
|
393
|
+
}
|
|
394
|
+
#if MICROPY_STREAMS_DELEGATE_ERROR
|
|
395
|
+
else if (request == MP_STREAM_RAISE_ERROR) {
|
|
396
|
+
ssl_raise_error((int)arg);
|
|
397
|
+
}
|
|
398
|
+
#endif
|
|
399
|
+
if (self->sock == MP_OBJ_NULL) {
|
|
400
|
+
return 0;
|
|
401
|
+
}
|
|
402
|
+
return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
static mp_obj_t ssl_socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
|
|
406
|
+
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in);
|
|
407
|
+
mp_obj_t sock = o->sock;
|
|
408
|
+
mp_obj_t dest[3];
|
|
409
|
+
mp_load_method(sock, MP_QSTR_setblocking, dest);
|
|
410
|
+
dest[2] = flag_in;
|
|
411
|
+
mp_obj_t res = mp_call_method_n_kw(1, 0, dest);
|
|
412
|
+
o->blocking = mp_obj_is_true(flag_in);
|
|
413
|
+
return res;
|
|
414
|
+
}
|
|
415
|
+
static MP_DEFINE_CONST_FUN_OBJ_2(ssl_socket_setblocking_obj, ssl_socket_setblocking);
|
|
416
|
+
|
|
417
|
+
static const mp_rom_map_elem_t ssl_socket_locals_dict_table[] = {
|
|
418
|
+
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
|
419
|
+
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
|
420
|
+
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
|
|
421
|
+
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
|
422
|
+
{ MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&ssl_socket_setblocking_obj) },
|
|
423
|
+
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
|
|
424
|
+
#if MICROPY_PY_SSL_FINALISER
|
|
425
|
+
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
|
|
426
|
+
#endif
|
|
427
|
+
};
|
|
428
|
+
static MP_DEFINE_CONST_DICT(ssl_socket_locals_dict, ssl_socket_locals_dict_table);
|
|
429
|
+
|
|
430
|
+
static const mp_stream_p_t ssl_socket_stream_p = {
|
|
431
|
+
.read = ssl_socket_read,
|
|
432
|
+
.write = ssl_socket_write,
|
|
433
|
+
.ioctl = ssl_socket_ioctl,
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
static MP_DEFINE_CONST_OBJ_TYPE(
|
|
437
|
+
ssl_socket_type,
|
|
438
|
+
MP_QSTR_SSLSocket,
|
|
439
|
+
MP_TYPE_FLAG_NONE,
|
|
440
|
+
protocol, &ssl_socket_stream_p,
|
|
441
|
+
locals_dict, &ssl_socket_locals_dict
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
// `ssl` / `tls` module —————————————————————————————————————————
|
|
445
|
+
|
|
446
|
+
static const mp_rom_map_elem_t mp_module_tls_globals_table[] = {
|
|
447
|
+
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_tls) },
|
|
448
|
+
{ MP_ROM_QSTR(MP_QSTR_SSLContext), MP_ROM_PTR(&ssl_context_type) },
|
|
449
|
+
{ MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_CLIENT), MP_ROM_INT(PROTOCOL_TLS_CLIENT) },
|
|
450
|
+
{ MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_SERVER), MP_ROM_INT(PROTOCOL_TLS_SERVER) },
|
|
451
|
+
{ MP_ROM_QSTR(MP_QSTR_CERT_NONE), MP_ROM_INT(CERT_NONE) },
|
|
452
|
+
{ MP_ROM_QSTR(MP_QSTR_CERT_REQUIRED), MP_ROM_INT(CERT_REQUIRED) },
|
|
453
|
+
};
|
|
454
|
+
static MP_DEFINE_CONST_DICT(mp_module_tls_globals, mp_module_tls_globals_table);
|
|
455
|
+
|
|
456
|
+
const mp_obj_module_t mp_module_tls = {
|
|
457
|
+
.base = { &mp_type_module },
|
|
458
|
+
.globals = (mp_obj_dict_t *)&mp_module_tls_globals,
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
#endif // MICROPY_PY_SSL && MICROPY_SSL_AXTLS
|