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.
Files changed (37) hide show
  1. freedos_micro_python/__init__.py +8 -0
  2. freedos_micro_python/cli.py +106 -0
  3. freedos_micro_python/gen_qstrdefs.py +275 -0
  4. freedos_micro_python/port/arch/bpstruct.h +2 -0
  5. freedos_micro_python/port/arch/cc.h +4 -0
  6. freedos_micro_python/port/arch/epstruct.h +1 -0
  7. freedos_micro_python/port/base64_uc386dos.c +164 -0
  8. freedos_micro_python/port/file_uc386dos.c +228 -0
  9. freedos_micro_python/port/lib/axtls/crypto/crypto.h +45 -0
  10. freedos_micro_python/port/lwip-arch-cc.h +46 -0
  11. freedos_micro_python/port/lwip_uc386dos.c +248 -0
  12. freedos_micro_python/port/lwipopts.h +117 -0
  13. freedos_micro_python/port/math_gamma.c +63 -0
  14. freedos_micro_python/port/modtime_uc386dos.c +60 -0
  15. freedos_micro_python/port/modtls_axtls_uc386dos.c +461 -0
  16. freedos_micro_python/port/mpconfigport.h +358 -0
  17. freedos_micro_python/port/mphal_uc386dos.c +103 -0
  18. freedos_micro_python/port/mphalport.h +11 -0
  19. freedos_micro_python/port/os_uc386dos.c +264 -0
  20. freedos_micro_python/port/path_uc386dos.c +307 -0
  21. freedos_micro_python/port/pktdrv_uc386dos.c +650 -0
  22. freedos_micro_python/port/qstrdefsport.h +2 -0
  23. freedos_micro_python/port/shutil_uc386dos.c +111 -0
  24. freedos_micro_python/port/tempfile_uc386dos.c +129 -0
  25. freedos_micro_python/port/time_real_uc386dos.c +77 -0
  26. freedos_micro_python/port/uc386_net_uc386dos.c +126 -0
  27. freedos_micro_python/port/urllib_parse_uc386dos.c +360 -0
  28. freedos_micro_python/port/urllib_uc386dos.c +29 -0
  29. freedos_micro_python/scripts/build.sh +641 -0
  30. freedos_micro_python/scripts/build_port.sh +241 -0
  31. freedos_micro_python/scripts/fetch.sh +238 -0
  32. freedos_micro_python-0.1.0.dist-info/METADATA +131 -0
  33. freedos_micro_python-0.1.0.dist-info/RECORD +37 -0
  34. freedos_micro_python-0.1.0.dist-info/WHEEL +5 -0
  35. freedos_micro_python-0.1.0.dist-info/entry_points.txt +2 -0
  36. freedos_micro_python-0.1.0.dist-info/licenses/LICENSE +25 -0
  37. 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