@scriptc/runtime 0.0.8 → 0.0.10
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 +43 -4
- package/src/scr_bytes.c +65 -18
- package/src/scr_bytes_io.c +507 -1
- package/src/scr_dc.c +6 -0
- package/src/scr_dgram.c +176 -1
- package/src/scr_dyn_handle.c +8 -66
- package/src/scr_dyn_invoke.c +96 -2
- package/src/scr_error.c +15 -0
- package/src/scr_events_emitter.c +34 -0
- package/src/scr_exception.c +4 -1
- package/src/scr_inspect.c +27 -3
- package/src/scr_island.c +417 -3
- package/src/scr_json.c +643 -6
- package/src/scr_lib.c +122 -4
- package/src/scr_library.c +105 -2
- package/src/scr_net.c +112 -0
- package/src/scr_number.c +29 -19
- package/src/scr_runtime.h +358 -9
- package/src/scr_stream.c +49 -2
- package/src/scr_tls.c +172 -0
package/src/scr_tls.c
CHANGED
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
#include "scr_runtime.h"
|
|
74
74
|
|
|
75
75
|
#include <errno.h>
|
|
76
|
+
#include <math.h>
|
|
76
77
|
#include <stdio.h>
|
|
77
78
|
#include <stdlib.h>
|
|
78
79
|
#include <string.h>
|
|
@@ -1102,6 +1103,121 @@ static const char *const SCR_TLS_SRV_FENCED_OPTIONS[] = {
|
|
|
1102
1103
|
"sessionTimeout", "sigalgs", "ticketKeys", NULL,
|
|
1103
1104
|
};
|
|
1104
1105
|
|
|
1106
|
+
/* ── the typed option-validation ladders (checked-dynamic lane) ────────
|
|
1107
|
+
* Node's configSecureContext / Server-constructor argument contracts over
|
|
1108
|
+
* every PRESENT validated key, run BEFORE the pem walk and its fences so
|
|
1109
|
+
* Node's typed errors always come first (the invalid-input probes isolate
|
|
1110
|
+
* one bad option per call, so the fixed order below matches each).
|
|
1111
|
+
* false = exception pending. */
|
|
1112
|
+
static bool scr_tls_opts_validate(const ScrDyn *opts) {
|
|
1113
|
+
if (opts == NULL || opts->kind != SCR_DYN_OBJ) return true;
|
|
1114
|
+
static const char *const STR_OPTS[] = { "ciphers", "passphrase", "ecdhCurve", "sessionIdContext", NULL };
|
|
1115
|
+
for (size_t i = 0; STR_OPTS[i] != NULL; i++) {
|
|
1116
|
+
const ScrDyn *v = scr_dyn_obj_get(opts, STR_OPTS[i], strlen(STR_OPTS[i]));
|
|
1117
|
+
if (v != NULL && v->kind != SCR_DYN_UNDEF && v->kind != SCR_DYN_NULL && v->kind != SCR_DYN_STR) {
|
|
1118
|
+
char name[48];
|
|
1119
|
+
snprintf(name, sizeof name, "options.%s", STR_OPTS[i]);
|
|
1120
|
+
scr_dyn_prop_type_fail(name, "of type string", v);
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
static const char *const ENGINE_OPTS[] = { "clientCertEngine", "privateKeyEngine", "privateKeyIdentifier", NULL };
|
|
1125
|
+
for (size_t i = 0; ENGINE_OPTS[i] != NULL; i++) {
|
|
1126
|
+
const ScrDyn *v = scr_dyn_obj_get(opts, ENGINE_OPTS[i], strlen(ENGINE_OPTS[i]));
|
|
1127
|
+
if (v != NULL && v->kind != SCR_DYN_UNDEF && v->kind != SCR_DYN_NULL && v->kind != SCR_DYN_STR) {
|
|
1128
|
+
char name[48];
|
|
1129
|
+
snprintf(name, sizeof name, "options.%s", ENGINE_OPTS[i]);
|
|
1130
|
+
scr_dyn_prop_type_fail(name, "of type string or one of null or undefined", v);
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
static const char *const VERSION_OPTS[] = { "minVersion", "maxVersion", NULL };
|
|
1135
|
+
for (size_t i = 0; VERSION_OPTS[i] != NULL; i++) {
|
|
1136
|
+
const ScrDyn *v = scr_dyn_obj_get(opts, VERSION_OPTS[i], strlen(VERSION_OPTS[i]));
|
|
1137
|
+
if (v == NULL || v->kind == SCR_DYN_UNDEF) continue;
|
|
1138
|
+
bool valid = false;
|
|
1139
|
+
if (v->kind == SCR_DYN_STR) {
|
|
1140
|
+
static const char *const KNOWN[] = { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3", NULL };
|
|
1141
|
+
for (size_t k = 0; KNOWN[k] != NULL; k++) {
|
|
1142
|
+
if (v->v.str->len == strlen(KNOWN[k]) && memcmp(v->v.str->data, KNOWN[k], v->v.str->len) == 0) {
|
|
1143
|
+
valid = true;
|
|
1144
|
+
break;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (!valid) {
|
|
1149
|
+
/* %j: strings render JSON-quoted, everything else inspect-lite. */
|
|
1150
|
+
char rendered[96];
|
|
1151
|
+
if (v->kind == SCR_DYN_STR) {
|
|
1152
|
+
snprintf(rendered, sizeof rendered, "\"%.*s\"",
|
|
1153
|
+
(int)(v->v.str->len < 80 ? v->v.str->len : 80), v->v.str->data);
|
|
1154
|
+
} else {
|
|
1155
|
+
/* %j over non-strings: JSON.stringify's scalar renderings. */
|
|
1156
|
+
char lite[64];
|
|
1157
|
+
snprintf(rendered, sizeof rendered, "%s", scr_dyn_inspect_lite(v, lite, sizeof lite));
|
|
1158
|
+
}
|
|
1159
|
+
char msg[192];
|
|
1160
|
+
int len = snprintf(msg, sizeof msg, "%s is not a valid %s TLS protocol version",
|
|
1161
|
+
rendered, VERSION_OPTS[i][2] == 'n' ? "minimum" : "maximum");
|
|
1162
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, (size_t)len, "ERR_TLS_INVALID_PROTOCOL_VERSION");
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
static const char *const NUM_OPTS[] = { "handshakeTimeout", "keepAliveInitialDelay", NULL };
|
|
1167
|
+
for (size_t i = 0; NUM_OPTS[i] != NULL; i++) {
|
|
1168
|
+
const ScrDyn *v = scr_dyn_obj_get(opts, NUM_OPTS[i], strlen(NUM_OPTS[i]));
|
|
1169
|
+
if (v != NULL && v->kind != SCR_DYN_UNDEF && v->kind != SCR_DYN_NULL && v->kind != SCR_DYN_NUM) {
|
|
1170
|
+
char name[48];
|
|
1171
|
+
snprintf(name, sizeof name, "options.%s", NUM_OPTS[i]);
|
|
1172
|
+
scr_dyn_prop_type_fail(name, "of type number", v);
|
|
1173
|
+
return false;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
{
|
|
1177
|
+
const ScrDyn *v = scr_dyn_obj_get(opts, "sessionTimeout", 14);
|
|
1178
|
+
if (v != NULL && v->kind != SCR_DYN_UNDEF && v->kind != SCR_DYN_NULL) {
|
|
1179
|
+
if (v->kind != SCR_DYN_NUM) {
|
|
1180
|
+
scr_dyn_prop_type_fail("options.sessionTimeout", "of type number", v);
|
|
1181
|
+
return false;
|
|
1182
|
+
}
|
|
1183
|
+
double n = v->v.num;
|
|
1184
|
+
char recv[48], msg[192];
|
|
1185
|
+
if (!(isfinite(n) && trunc(n) == n)) {
|
|
1186
|
+
scr_num_received(n, recv);
|
|
1187
|
+
int len = snprintf(msg, sizeof msg,
|
|
1188
|
+
"The value of \"options.sessionTimeout\" is out of range. It must be an integer. Received %s", recv);
|
|
1189
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
1190
|
+
return false;
|
|
1191
|
+
}
|
|
1192
|
+
if (n < 0 || n > 2147483647.0) {
|
|
1193
|
+
scr_num_received(n, recv);
|
|
1194
|
+
int len = snprintf(msg, sizeof msg,
|
|
1195
|
+
"The value of \"options.sessionTimeout\" is out of range. It must be >= 0 && <= 2147483647. Received %s", recv);
|
|
1196
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
1197
|
+
return false;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
{
|
|
1202
|
+
const ScrDyn *v = scr_dyn_obj_get(opts, "ticketKeys", 10);
|
|
1203
|
+
if (v != NULL && v->kind != SCR_DYN_UNDEF && v->kind != SCR_DYN_NULL) {
|
|
1204
|
+
if (v->kind != SCR_DYN_BYTES) {
|
|
1205
|
+
scr_dyn_prop_type_fail("options.ticketKeys", "an instance of Buffer, TypedArray, or DataView", v);
|
|
1206
|
+
return false;
|
|
1207
|
+
}
|
|
1208
|
+
double bytelen = scr_bytes_byte_len(v->v.bytes);
|
|
1209
|
+
if (bytelen != 48) {
|
|
1210
|
+
char msg[160];
|
|
1211
|
+
int len = snprintf(msg, sizeof msg,
|
|
1212
|
+
"The property 'options.ticketKeys' must be exactly 48 bytes. Received %.0f", bytelen);
|
|
1213
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, (size_t)len, "ERR_INVALID_ARG_VALUE");
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
return true;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1105
1221
|
/* The server-options walk. Fills the cert/key out-params (+1 each) from a
|
|
1106
1222
|
* DOM options record; false = exception pending (partial results released). */
|
|
1107
1223
|
static bool scr_tls_srv_opts_walk(const ScrDyn *opts, const char *api, ScrBytes **cert_out,
|
|
@@ -1112,6 +1228,7 @@ static bool scr_tls_srv_opts_walk(const ScrDyn *opts, const char *api, ScrBytes
|
|
|
1112
1228
|
scr_dyn_arg_type_fail("options", "of type object", opts ? opts : scr_dyn_undefined());
|
|
1113
1229
|
return false;
|
|
1114
1230
|
}
|
|
1231
|
+
if (!scr_tls_opts_validate(opts)) return false; /* Node's typed errors first */
|
|
1115
1232
|
bool ok = true;
|
|
1116
1233
|
for (size_t i = 0; ok && i < opts->v.obj.len; i++) {
|
|
1117
1234
|
const ScrDynEntry *e = &opts->v.obj.entries[i];
|
|
@@ -1176,6 +1293,45 @@ static bool scr_tls_srv_opts_walk(const ScrDyn *opts, const char *api, ScrBytes
|
|
|
1176
1293
|
return ok;
|
|
1177
1294
|
}
|
|
1178
1295
|
|
|
1296
|
+
/* createSecureContext over a RUNTIME options record: Node's typed
|
|
1297
|
+
* validations first (scr_tls_opts_validate), then the pem walk — a bag
|
|
1298
|
+
* that validates AND carries both cert and key builds the real context;
|
|
1299
|
+
* everything else met its ladder error or the walk's per-key fence.
|
|
1300
|
+
* +1, or NULL with the exception pending. */
|
|
1301
|
+
ScrSecureCtx *scr_tls_create_secure_context_dyn(const ScrDyn *opts /*borrowed*/) {
|
|
1302
|
+
ScrBytes *cert, *key;
|
|
1303
|
+
if (!scr_tls_srv_opts_walk(opts, "tls.createSecureContext", &cert, &key)) return NULL;
|
|
1304
|
+
ScrSecureCtx *c = scr_tls_create_secure_context((const char *)cert->data, cert->len,
|
|
1305
|
+
(const char *)key->data, key->len);
|
|
1306
|
+
scr_bytes_release(cert);
|
|
1307
|
+
scr_bytes_release(key);
|
|
1308
|
+
return c;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/* tls.getCACertificates(type): validateString, then the documented name
|
|
1312
|
+
* set — an unknown name answers ERR_INVALID_ARG_VALUE; the real CA list
|
|
1313
|
+
* has no lowering, so a valid name meets the fence. Always throws. */
|
|
1314
|
+
void scr_tls_ca_certs_chk(const ScrDyn *type, const ScrStr *fence) {
|
|
1315
|
+
if (type->kind != SCR_DYN_STR) {
|
|
1316
|
+
scr_dyn_arg_type_fail("type", "of type string", type);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
static const char *const KNOWN[] = { "default", "system", "bundled", "extra", NULL };
|
|
1320
|
+
bool valid = false;
|
|
1321
|
+
for (size_t i = 0; KNOWN[i] != NULL; i++) {
|
|
1322
|
+
if (type->v.str->len == strlen(KNOWN[i]) &&
|
|
1323
|
+
memcmp(type->v.str->data, KNOWN[i], type->v.str->len) == 0) {
|
|
1324
|
+
valid = true;
|
|
1325
|
+
break;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
if (!valid) {
|
|
1329
|
+
scr_dyn_arg_value_fail("type", NULL, type);
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
scr_throw_lowering_fence(fence);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1179
1335
|
ScrNetServer *scr_tls_create_server_dyn(const ScrDyn *opts /*borrowed*/,
|
|
1180
1336
|
ScrClosure *handler /*moves, nullable*/,
|
|
1181
1337
|
ScrNetConnFn fn) {
|
|
@@ -1241,6 +1397,22 @@ ScrNetSocket *scr_tls_connect_dyn(double port, ScrStr *host /*borrowed, nullable
|
|
|
1241
1397
|
for (size_t i = 0; ok && i < opts->v.obj.len; i++) {
|
|
1242
1398
|
const ScrDynEntry *e = &opts->v.obj.entries[i];
|
|
1243
1399
|
const ScrDyn *v = e->value;
|
|
1400
|
+
if (strcmp(e->key, "checkServerIdentity") == 0) {
|
|
1401
|
+
/* Node spreads user options over the defaults, so a PRESENT key
|
|
1402
|
+
* replaces the builtin verifier even when its value is undefined
|
|
1403
|
+
* — validateFunction then throws for anything non-callable. A
|
|
1404
|
+
* real function keeps the fence (custom verification has no
|
|
1405
|
+
* lowering). */
|
|
1406
|
+
if (v->kind != SCR_DYN_FUNC) {
|
|
1407
|
+
scr_dyn_prop_type_fail("options.checkServerIdentity", "of type function", v);
|
|
1408
|
+
ok = false;
|
|
1409
|
+
} else {
|
|
1410
|
+
scr_tls_opt_fence("tls.connect", "checkServerIdentity",
|
|
1411
|
+
"custom identity verification has no lowering — the runtime verifies against the servername/host");
|
|
1412
|
+
ok = false;
|
|
1413
|
+
}
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1244
1416
|
if (v->kind == SCR_DYN_UNDEF) continue;
|
|
1245
1417
|
if (strcmp(e->key, "port") == 0) {
|
|
1246
1418
|
if (port >= 0) continue; /* the argument form wins, like Node */
|