@scriptc/runtime 0.0.11 → 0.0.13
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.
- package/package.json +1 -1
- package/src/scr_assert.c +13 -13
- package/src/scr_async.c +53 -8
- package/src/scr_async_dyn.c +163 -47
- package/src/scr_bytes_io.c +5 -5
- package/src/scr_dc.c +8 -8
- package/src/scr_dgram.c +1 -1
- package/src/scr_dyn_handle.c +5 -5
- package/src/scr_dyn_invoke.c +42 -18
- package/src/scr_error.c +3 -3
- package/src/scr_events_emitter.c +1 -1
- package/src/scr_http.c +1080 -34
- package/src/scr_http2.c +53 -3
- package/src/scr_inspect.c +3 -3
- package/src/scr_island.c +134 -25
- package/src/scr_json.c +124 -68
- package/src/scr_net.c +254 -35
- package/src/scr_qs.c +1 -1
- package/src/scr_runtime.h +278 -124
- package/src/scr_stream.c +4 -4
- package/src/scr_tls.c +42 -5
- package/src/scr_tls_ca.c +288 -0
- package/src/scr_web.c +1 -1
package/src/scr_stream.c
CHANGED
|
@@ -786,7 +786,7 @@ static void scr_stream_resume_kick(ScrStream *s) {
|
|
|
786
786
|
* parameters implicitly-any: the compiler-emitted invoke thunks box the
|
|
787
787
|
* chunk/encoding/error into dyn, and the COMPLETION callback arrives as
|
|
788
788
|
* a callable dyn function whose glue lands here — arguments arrive as
|
|
789
|
-
* borrowed
|
|
789
|
+
* borrowed dyn values: the error is null/undefined (none), an
|
|
790
790
|
* error-shaped object ({message}), or a string; transform/flush data is
|
|
791
791
|
* bytes, a string, or absent. clo->caps[0] boxes the retained stream. */
|
|
792
792
|
|
|
@@ -870,7 +870,7 @@ ScrDyn *scr_stream_done_dyn_l(ScrClosure *clo, ScrDyn *const *args, size_t argc)
|
|
|
870
870
|
}
|
|
871
871
|
|
|
872
872
|
/* Checked-dynamic chunks (the JS lane's push/write of any-typed values):
|
|
873
|
-
* dispatch by
|
|
873
|
+
* dispatch by dyn tag — bytes copy out, strings convert utf8, null takes
|
|
874
874
|
* the null path (EOF for push; ERR_STREAM_NULL_VALUES for write), and
|
|
875
875
|
* anything else throws the write TypeError (Node would throw
|
|
876
876
|
* ERR_INVALID_ARG_TYPE — approximated by the same shape). Borrow d. */
|
|
@@ -1531,7 +1531,7 @@ void scr_stream_set_flush(ScrStream *s, ScrClosure *cb /*moves*/, ScrStreamPlain
|
|
|
1531
1531
|
* keys are ignored exactly like Node), and callback slots holding
|
|
1532
1532
|
* callable values (Node's `typeof === 'function'` guard — anything else
|
|
1533
1533
|
* leaves the slot to the prototype/fallback) bind as closures whose one
|
|
1534
|
-
* cap boxes the dyn callable; the invs below build the
|
|
1534
|
+
* cap boxes the dyn callable; the invs below build the dyn arguments the
|
|
1535
1535
|
* way the compiler-emitted dyn thunks do (chunk as Buffer-flavored bytes,
|
|
1536
1536
|
* encoding 'buffer', the error via the boundary encoding, the completion
|
|
1537
1537
|
* callback as a callable dyn over the *_done glue). Options that Node's
|
|
@@ -3038,7 +3038,7 @@ static void scr_stream_run_tick(ScrStreamTick *t) {
|
|
|
3038
3038
|
/* Fully closed: nothing calls the option callbacks again (Node's
|
|
3039
3039
|
* contract), so drop them here — this breaks the reference cycle
|
|
3040
3040
|
* a callback capturing its own stream forms, including the dyn-
|
|
3041
|
-
* options closures whose stream edge rides through a
|
|
3041
|
+
* options closures whose stream edge rides through a dyn function
|
|
3042
3042
|
* value the cycle collector cannot traverse. */
|
|
3043
3043
|
if (st->r.read_cb) { scr_closure_release(st->r.read_cb); st->r.read_cb = NULL; }
|
|
3044
3044
|
if (st->w.write_cb) { scr_closure_release(st->w.write_cb); st->w.write_cb = NULL; }
|
package/src/scr_tls.c
CHANGED
|
@@ -350,7 +350,30 @@ typedef struct ScrTlsCli {
|
|
|
350
350
|
static mbedtls_x509_crt scr_tls_system_roots;
|
|
351
351
|
static bool scr_tls_system_roots_loaded = false;
|
|
352
352
|
|
|
353
|
+
/* setDefaultCACertificates' replacement anchors (scr_tls_ca.c — always
|
|
354
|
+
* compiled alongside this unit): parsed lazily per generation, so each
|
|
355
|
+
* set re-parses once and every later dial reuses the chain. An EMPTY
|
|
356
|
+
* replacement keeps an initialized, certificate-free chain — every
|
|
357
|
+
* verification fails, Node's own consequence of trusting nothing. */
|
|
358
|
+
static mbedtls_x509_crt scr_tls_override_roots;
|
|
359
|
+
static uint64_t scr_tls_override_parsed_gen = 0;
|
|
360
|
+
|
|
353
361
|
static mbedtls_x509_crt *scr_tls_system_ca(void) {
|
|
362
|
+
const char *pem = NULL;
|
|
363
|
+
size_t pem_len = 0;
|
|
364
|
+
uint64_t gen = 0;
|
|
365
|
+
if (scr_tls_ca_default_override(&pem, &pem_len, &gen)) {
|
|
366
|
+
if (gen != scr_tls_override_parsed_gen) {
|
|
367
|
+
if (scr_tls_override_parsed_gen != 0) mbedtls_x509_crt_free(&scr_tls_override_roots);
|
|
368
|
+
mbedtls_x509_crt_init(&scr_tls_override_roots);
|
|
369
|
+
/* +1 for the NUL — mbedTLS's PEM mode wants it counted. A parse
|
|
370
|
+
* failure inside one block leaves the chain with the blocks that
|
|
371
|
+
* did parse (the scr_tls_ca.c divergence note). */
|
|
372
|
+
if (pem_len > 0) (void)mbedtls_x509_crt_parse(&scr_tls_override_roots, (const unsigned char *)pem, pem_len + 1);
|
|
373
|
+
scr_tls_override_parsed_gen = gen;
|
|
374
|
+
}
|
|
375
|
+
return &scr_tls_override_roots;
|
|
376
|
+
}
|
|
354
377
|
if (!scr_tls_system_roots_loaded) {
|
|
355
378
|
scr_tls_system_roots_loaded = true;
|
|
356
379
|
mbedtls_x509_crt_init(&scr_tls_system_roots);
|
|
@@ -983,8 +1006,20 @@ ScrHttpClientReq *scr_https_request(ScrStr *host /*borrowed*/, double port,
|
|
|
983
1006
|
fn, 443, &scr_tls_cli_wrap, cli);
|
|
984
1007
|
}
|
|
985
1008
|
|
|
1009
|
+
ScrHttpClientReq *scr_https_request_agent(ScrStr *host /*borrowed*/, double port,
|
|
1010
|
+
ScrStr *path /*borrowed*/, ScrStr *method /*borrowed*/,
|
|
1011
|
+
double timeout_ms, ScrArr *header_pairs /*borrowed*/,
|
|
1012
|
+
bool auto_end, bool reject_unauthorized,
|
|
1013
|
+
const char *ca /*borrowed, len 0 = none*/, size_t ca_len,
|
|
1014
|
+
const ScrDyn *agent /*borrowed*/,
|
|
1015
|
+
ScrClosure *cb /*moves, nullable*/, ScrHttpRespFn fn) {
|
|
1016
|
+
ScrTlsCli *cli = scr_tls_cli_new(host, reject_unauthorized, ca, ca_len);
|
|
1017
|
+
return scr_http_request_agent_ex(host, port, path, method, timeout_ms, header_pairs, auto_end,
|
|
1018
|
+
agent, cb, fn, 443, &scr_tls_cli_wrap, cli);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
986
1021
|
/* ── RUNTIME options records (the divergence-66 stance) ────────────────
|
|
987
|
-
* A non-literal options value — the checked-dynamic JS lane's
|
|
1022
|
+
* A non-literal options value — the checked-dynamic JS lane's dyn record
|
|
988
1023
|
* — reads its members at RUNTIME. Members the literal path lowers take
|
|
989
1024
|
* the same lowering; members whose literal forms are compile fences
|
|
990
1025
|
* become runtime gates that THROW the catchable fence instead of
|
|
@@ -1002,7 +1037,7 @@ static void scr_tls_opt_fence(const char *api, const char *key, const char *hint
|
|
|
1002
1037
|
scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
|
|
1003
1038
|
}
|
|
1004
1039
|
|
|
1005
|
-
/* JS truthiness over the
|
|
1040
|
+
/* JS truthiness over the dyn kinds — Node reads requestCert /
|
|
1006
1041
|
* rejectUnauthorized through `if (options.x)`. */
|
|
1007
1042
|
static bool scr_tls_dyn_truthy(const ScrDyn *v) {
|
|
1008
1043
|
switch (v->kind) {
|
|
@@ -1219,9 +1254,11 @@ static bool scr_tls_opts_validate(const ScrDyn *opts) {
|
|
|
1219
1254
|
}
|
|
1220
1255
|
|
|
1221
1256
|
/* The server-options walk. Fills the cert/key out-params (+1 each) from a
|
|
1222
|
-
*
|
|
1223
|
-
|
|
1224
|
-
|
|
1257
|
+
* dyn options record; false = exception pending (partial results released).
|
|
1258
|
+
* Exported runtime-internally: scr_http2.c's createSecureServerDyn rides
|
|
1259
|
+
* the same walk (scr_http2.c always links alongside this unit). */
|
|
1260
|
+
bool scr_tls_srv_opts_walk(const ScrDyn *opts, const char *api, ScrBytes **cert_out,
|
|
1261
|
+
ScrBytes **key_out) {
|
|
1225
1262
|
*cert_out = NULL;
|
|
1226
1263
|
*key_out = NULL;
|
|
1227
1264
|
if (opts == NULL || opts->kind != SCR_DYN_OBJ) {
|
package/src/scr_tls_ca.c
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/* node:tls — the CA-store introspection slice: getCACertificates,
|
|
2
|
+
* rootCertificates, and setDefaultCACertificates (scr_runtime.h has the
|
|
3
|
+
* contracts). Its own link gate ("tlsca." libCalls), deliberately free
|
|
4
|
+
* of mbedTLS: everything here is PEM-BLOCK bookkeeping over the host
|
|
5
|
+
* bundle, so a program that only inspects the CA store keeps the lean
|
|
6
|
+
* link line. scr_tls.c (when it links — cc.ts compiles this unit
|
|
7
|
+
* alongside it) consults scr_tls_ca_default_override for its client
|
|
8
|
+
* trust anchors, which is what makes setDefaultCACertificates LIVE
|
|
9
|
+
* semantics: the next https/tls dial verifies against the replaced set.
|
|
10
|
+
*
|
|
11
|
+
* Divergences, documented: the host bundle stands in for Node's
|
|
12
|
+
* compiled-in Mozilla roots ('bundled' and rootCertificates) AND for the
|
|
13
|
+
* platform store ('system') — the same /etc/ssl/cert.pem stance
|
|
14
|
+
* scr_tls.c's anchors established; and set-time validation is PEM-BLOCK
|
|
15
|
+
* shaped (a well-formed block with corrupt DER inside is kept here where
|
|
16
|
+
* Node's X509 parse would drop it — such a cert then fails verification
|
|
17
|
+
* instead, the same observable outcome one step later). Deduplication is
|
|
18
|
+
* byte-exact over the extracted block where Node compares parsed X509
|
|
19
|
+
* identities; re-encoded duplicates of one certificate stay distinct
|
|
20
|
+
* entries, which no equality the tests observe can distinguish without a
|
|
21
|
+
* parser. */
|
|
22
|
+
#include "scr_runtime.h"
|
|
23
|
+
|
|
24
|
+
#include <stdio.h>
|
|
25
|
+
#include <stdlib.h>
|
|
26
|
+
#include <string.h>
|
|
27
|
+
|
|
28
|
+
/* The per-type caches: +1 held here for the process lifetime (an atexit
|
|
29
|
+
* teardown releases them so the RC audit stays clean). 'default' also
|
|
30
|
+
* keeps the concatenated PEM buffer scr_tls.c parses into anchors. */
|
|
31
|
+
static ScrArr *scr_ca_bundled = NULL;
|
|
32
|
+
static ScrArr *scr_ca_system = NULL;
|
|
33
|
+
static ScrArr *scr_ca_extra = NULL;
|
|
34
|
+
static ScrArr *scr_ca_default = NULL;
|
|
35
|
+
static char *scr_ca_override_buf = NULL;
|
|
36
|
+
static size_t scr_ca_override_len = 0;
|
|
37
|
+
static uint64_t scr_ca_override_gen = 0; /* 0 = never set */
|
|
38
|
+
|
|
39
|
+
static void scr_ca_oom(void) {
|
|
40
|
+
fputs("scriptc: out of memory\n", stderr);
|
|
41
|
+
abort();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static void scr_ca_teardown(void) {
|
|
45
|
+
if (scr_ca_bundled != NULL) scr_arr_release(scr_ca_bundled);
|
|
46
|
+
if (scr_ca_system != NULL) scr_arr_release(scr_ca_system);
|
|
47
|
+
if (scr_ca_extra != NULL) scr_arr_release(scr_ca_extra);
|
|
48
|
+
if (scr_ca_default != NULL) scr_arr_release(scr_ca_default);
|
|
49
|
+
scr_ca_bundled = scr_ca_system = scr_ca_extra = scr_ca_default = NULL;
|
|
50
|
+
free(scr_ca_override_buf);
|
|
51
|
+
scr_ca_override_buf = NULL;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static bool scr_ca_teardown_armed = false;
|
|
55
|
+
|
|
56
|
+
static void scr_ca_arm_teardown(void) {
|
|
57
|
+
if (!scr_ca_teardown_armed) {
|
|
58
|
+
scr_ca_teardown_armed = true;
|
|
59
|
+
atexit(scr_ca_teardown);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Portable bounded substring search (memmem is GNU/BSD-only; win32 has
|
|
64
|
+
* neither). Needles here are short constant markers — the naive scan is
|
|
65
|
+
* plenty. */
|
|
66
|
+
static const char *scr_ca_find(const char *hay, size_t hay_len, const char *needle, size_t needle_len) {
|
|
67
|
+
if (needle_len == 0 || hay_len < needle_len) return NULL;
|
|
68
|
+
for (size_t i = 0; i + needle_len <= hay_len; i++) {
|
|
69
|
+
if (hay[i] == needle[0] && memcmp(hay + i, needle, needle_len) == 0) return hay + i;
|
|
70
|
+
}
|
|
71
|
+
return NULL;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Every "-----BEGIN CERTIFICATE-----" ... "-----END CERTIFICATE-----"
|
|
75
|
+
* block of `text` (other PEM kinds — keys, CRLs — skip, like Node's CA
|
|
76
|
+
* loaders), pushed onto `arr` as one string each, END line included.
|
|
77
|
+
* Returns the number of blocks found. */
|
|
78
|
+
static size_t scr_ca_push_pem_blocks(ScrArr *arr, const char *text, size_t len) {
|
|
79
|
+
static const char BEGIN[] = "-----BEGIN CERTIFICATE-----";
|
|
80
|
+
static const char END[] = "-----END CERTIFICATE-----";
|
|
81
|
+
size_t found = 0;
|
|
82
|
+
const char *p = text;
|
|
83
|
+
const char *limit = text + len;
|
|
84
|
+
while (p < limit) {
|
|
85
|
+
const char *b = scr_ca_find(p, (size_t)(limit - p), BEGIN, sizeof BEGIN - 1);
|
|
86
|
+
if (b == NULL) break;
|
|
87
|
+
const char *e = scr_ca_find(b, (size_t)(limit - b), END, sizeof END - 1);
|
|
88
|
+
if (e == NULL) break;
|
|
89
|
+
const char *stop = e + (sizeof END - 1);
|
|
90
|
+
/* Node's entries carry the block's trailing newline when the source
|
|
91
|
+
* had one — keep it so round-trips through writeFileSync + parse
|
|
92
|
+
* stay concatenation-safe. */
|
|
93
|
+
if (stop < limit && *stop == '\r') stop++;
|
|
94
|
+
if (stop < limit && *stop == '\n') stop++;
|
|
95
|
+
scr_arr_push_ref(arr, scr_str_new(b, (size_t)(stop - b)));
|
|
96
|
+
found++;
|
|
97
|
+
p = stop;
|
|
98
|
+
}
|
|
99
|
+
return found;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* One whole file, malloc'd (NULL on any failure — absent bundles answer
|
|
103
|
+
* empty stores, the same silence Node has on hosts without the file). */
|
|
104
|
+
static char *scr_ca_read_file(const char *path, size_t *out_len) {
|
|
105
|
+
FILE *f = fopen(path, "rb");
|
|
106
|
+
if (f == NULL) return NULL;
|
|
107
|
+
if (fseek(f, 0, SEEK_END) != 0) {
|
|
108
|
+
fclose(f);
|
|
109
|
+
return NULL;
|
|
110
|
+
}
|
|
111
|
+
long sz = ftell(f);
|
|
112
|
+
if (sz < 0) {
|
|
113
|
+
fclose(f);
|
|
114
|
+
return NULL;
|
|
115
|
+
}
|
|
116
|
+
rewind(f);
|
|
117
|
+
char *buf = malloc((size_t)sz + 1);
|
|
118
|
+
if (buf == NULL) scr_ca_oom();
|
|
119
|
+
size_t got = fread(buf, 1, (size_t)sz, f);
|
|
120
|
+
fclose(f);
|
|
121
|
+
buf[got] = '\0';
|
|
122
|
+
*out_len = got;
|
|
123
|
+
return buf;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* The host bundle, probed in scr_tls.c's documented order. */
|
|
127
|
+
static ScrArr *scr_ca_load_host_bundle(void) {
|
|
128
|
+
ScrArr *arr = scr_arr_new(SCR_ELEM_STR, 128);
|
|
129
|
+
static const char *const bundles[] = {
|
|
130
|
+
"/etc/ssl/cert.pem", /* macOS, Alpine */
|
|
131
|
+
"/etc/ssl/certs/ca-certificates.crt", /* Debian/Ubuntu */
|
|
132
|
+
"/etc/pki/tls/certs/ca-bundle.crt", /* Fedora/RHEL */
|
|
133
|
+
"/etc/ssl/ca-bundle.pem", /* openSUSE */
|
|
134
|
+
};
|
|
135
|
+
for (size_t i = 0; i < sizeof bundles / sizeof bundles[0]; i++) {
|
|
136
|
+
size_t len = 0;
|
|
137
|
+
char *text = scr_ca_read_file(bundles[i], &len);
|
|
138
|
+
if (text == NULL) continue;
|
|
139
|
+
scr_ca_push_pem_blocks(arr, text, len);
|
|
140
|
+
free(text);
|
|
141
|
+
break; /* first bundle wins, like the anchor probe */
|
|
142
|
+
}
|
|
143
|
+
return arr;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static ScrArr *scr_ca_bundled_arr(void) {
|
|
147
|
+
if (scr_ca_bundled == NULL) {
|
|
148
|
+
scr_ca_arm_teardown();
|
|
149
|
+
scr_ca_bundled = scr_ca_load_host_bundle();
|
|
150
|
+
}
|
|
151
|
+
return scr_ca_bundled;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static ScrArr *scr_ca_system_arr(void) {
|
|
155
|
+
if (scr_ca_system == NULL) {
|
|
156
|
+
scr_ca_arm_teardown();
|
|
157
|
+
scr_ca_system = scr_ca_load_host_bundle();
|
|
158
|
+
}
|
|
159
|
+
return scr_ca_system;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static ScrArr *scr_ca_extra_arr(void) {
|
|
163
|
+
if (scr_ca_extra == NULL) {
|
|
164
|
+
scr_ca_arm_teardown();
|
|
165
|
+
scr_ca_extra = scr_arr_new(SCR_ELEM_STR, 4);
|
|
166
|
+
const char *path = getenv("NODE_EXTRA_CA_CERTS");
|
|
167
|
+
if (path != NULL && path[0] != '\0') {
|
|
168
|
+
size_t len = 0;
|
|
169
|
+
char *text = scr_ca_read_file(path, &len);
|
|
170
|
+
if (text != NULL) {
|
|
171
|
+
scr_ca_push_pem_blocks(scr_ca_extra, text, len);
|
|
172
|
+
free(text);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return scr_ca_extra;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* 'default' with no override: bundled ∪ extra (Node's own composition
|
|
180
|
+
* when no --use-*-ca flag redirects it). */
|
|
181
|
+
static ScrArr *scr_ca_default_arr(void) {
|
|
182
|
+
if (scr_ca_default == NULL) {
|
|
183
|
+
scr_ca_arm_teardown();
|
|
184
|
+
ScrArr *bundled = scr_ca_bundled_arr();
|
|
185
|
+
ScrArr *extra = scr_ca_extra_arr();
|
|
186
|
+
size_t nb = (size_t)scr_arr_len(bundled);
|
|
187
|
+
size_t ne = (size_t)scr_arr_len(extra);
|
|
188
|
+
scr_ca_default = scr_arr_new(SCR_ELEM_STR, nb + ne > 0 ? nb + ne : 1);
|
|
189
|
+
for (size_t i = 0; i < nb; i++) scr_arr_push_ref(scr_ca_default, scr_arr_get_ref(bundled, (double)i));
|
|
190
|
+
for (size_t i = 0; i < ne; i++) scr_arr_push_ref(scr_ca_default, scr_arr_get_ref(extra, (double)i));
|
|
191
|
+
}
|
|
192
|
+
return scr_ca_default;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
ScrArr *scr_tls_ca_get(ScrStr *type) {
|
|
196
|
+
ScrArr *arr = NULL;
|
|
197
|
+
if (strcmp(type->data, "default") == 0) arr = scr_ca_default_arr();
|
|
198
|
+
else if (strcmp(type->data, "bundled") == 0) arr = scr_ca_bundled_arr();
|
|
199
|
+
else if (strcmp(type->data, "system") == 0) arr = scr_ca_system_arr();
|
|
200
|
+
else if (strcmp(type->data, "extra") == 0) arr = scr_ca_extra_arr();
|
|
201
|
+
if (arr == NULL) {
|
|
202
|
+
/* Node's ERR_INVALID_ARG_VALUE TypeError, message-exact. */
|
|
203
|
+
char msg[256];
|
|
204
|
+
int n = snprintf(msg, sizeof msg, "The argument 'type' is invalid. Received '%s'", type->data);
|
|
205
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, n < 0 ? 0 : (size_t)n, "ERR_INVALID_ARG_VALUE");
|
|
206
|
+
return NULL;
|
|
207
|
+
}
|
|
208
|
+
return scr_arr_retain(arr);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
ScrArr *scr_tls_ca_root(void) { return scr_arr_retain(scr_ca_bundled_arr()); }
|
|
212
|
+
|
|
213
|
+
void scr_tls_ca_set_default(ScrArr *certs) {
|
|
214
|
+
size_t n = (size_t)scr_arr_len(certs);
|
|
215
|
+
/* Extract + dedupe first — a throw must leave the current set intact
|
|
216
|
+
* (Node's own error contract, pinned by the suite's error test). */
|
|
217
|
+
ScrArr *next = scr_arr_new(SCR_ELEM_STR, n > 0 ? n : 1);
|
|
218
|
+
size_t blocks = 0;
|
|
219
|
+
for (size_t i = 0; i < n; i++) {
|
|
220
|
+
ScrStr *entry = scr_arr_get_ref(certs, (double)i); /* +1 */
|
|
221
|
+
size_t before = (size_t)scr_arr_len(next);
|
|
222
|
+
blocks += scr_ca_push_pem_blocks(next, entry->data, entry->len);
|
|
223
|
+
/* Dedupe the freshly pushed blocks against everything before them. */
|
|
224
|
+
size_t after = (size_t)scr_arr_len(next);
|
|
225
|
+
for (size_t j = after; j > before; j--) {
|
|
226
|
+
ScrStr *cand = scr_arr_get_ref(next, (double)(j - 1)); /* +1 */
|
|
227
|
+
bool dup = false;
|
|
228
|
+
for (size_t k = 0; k + 1 < j && !dup; k++) {
|
|
229
|
+
ScrStr *prev = scr_arr_get_ref(next, (double)k); /* +1 */
|
|
230
|
+
dup = prev->len == cand->len && memcmp(prev->data, cand->data, cand->len) == 0;
|
|
231
|
+
scr_str_release(prev);
|
|
232
|
+
}
|
|
233
|
+
scr_str_release(cand);
|
|
234
|
+
if (dup) {
|
|
235
|
+
/* Drop the duplicate: rebuild without index j-1 (rare path —
|
|
236
|
+
* a simple splice via slice would copy anyway; do it in place). */
|
|
237
|
+
ScrArr *trimmed = scr_arr_new(SCR_ELEM_STR, after);
|
|
238
|
+
for (size_t k = 0; k < (size_t)scr_arr_len(next); k++) {
|
|
239
|
+
if (k == j - 1) continue;
|
|
240
|
+
scr_arr_push_ref(trimmed, scr_arr_get_ref(next, (double)k));
|
|
241
|
+
}
|
|
242
|
+
scr_arr_release(next);
|
|
243
|
+
next = trimmed;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
scr_str_release(entry);
|
|
247
|
+
}
|
|
248
|
+
if (n > 0 && blocks == 0) {
|
|
249
|
+
scr_arr_release(next);
|
|
250
|
+
static const char MSG[] = "No valid certificates found in the provided array";
|
|
251
|
+
scr_throw_error_msg_code(SCR_ERR_ERROR, MSG, sizeof MSG - 1, "ERR_CRYPTO_OPERATION_FAILED");
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
/* Commit: the cached 'default' array and the concatenated anchor
|
|
255
|
+
* buffer scr_tls.c parses (NUL-terminated for mbedTLS's PEM mode). */
|
|
256
|
+
scr_ca_arm_teardown();
|
|
257
|
+
if (scr_ca_default != NULL) scr_arr_release(scr_ca_default);
|
|
258
|
+
scr_ca_default = next;
|
|
259
|
+
size_t total = 0;
|
|
260
|
+
size_t count = (size_t)scr_arr_len(next);
|
|
261
|
+
for (size_t i = 0; i < count; i++) {
|
|
262
|
+
ScrStr *s = scr_arr_get_ref(next, (double)i);
|
|
263
|
+
total += s->len;
|
|
264
|
+
scr_str_release(s);
|
|
265
|
+
}
|
|
266
|
+
char *buf = malloc(total + 1);
|
|
267
|
+
if (buf == NULL) scr_ca_oom();
|
|
268
|
+
size_t off = 0;
|
|
269
|
+
for (size_t i = 0; i < count; i++) {
|
|
270
|
+
ScrStr *s = scr_arr_get_ref(next, (double)i);
|
|
271
|
+
memcpy(buf + off, s->data, s->len);
|
|
272
|
+
off += s->len;
|
|
273
|
+
scr_str_release(s);
|
|
274
|
+
}
|
|
275
|
+
buf[off] = '\0';
|
|
276
|
+
free(scr_ca_override_buf);
|
|
277
|
+
scr_ca_override_buf = buf;
|
|
278
|
+
scr_ca_override_len = off;
|
|
279
|
+
scr_ca_override_gen++;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
bool scr_tls_ca_default_override(const char **pem, size_t *len, uint64_t *gen) {
|
|
283
|
+
if (scr_ca_override_gen == 0) return false;
|
|
284
|
+
*pem = scr_ca_override_buf;
|
|
285
|
+
*len = scr_ca_override_len;
|
|
286
|
+
*gen = scr_ca_override_gen;
|
|
287
|
+
return true;
|
|
288
|
+
}
|
package/src/scr_web.c
CHANGED
|
@@ -791,7 +791,7 @@ static const char web_prelude[] =
|
|
|
791
791
|
" [Symbol.iterator]() { return this.entries(); }\n"
|
|
792
792
|
" }\n"
|
|
793
793
|
"\n"
|
|
794
|
-
/* Event + EventTarget + CustomEvent — the
|
|
794
|
+
/* Event + EventTarget + CustomEvent — the dyn event plumbing Node
|
|
795
795
|
* exposes as globals since v15. Synchronous dispatch on one target
|
|
796
796
|
* (no tree, no phases — composedPath answers []), once/capture-shaped
|
|
797
797
|
* options accepted, handleEvent objects honored, dispatchEvent
|