@scriptc/runtime 0.0.0 → 0.0.2
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/LICENSE +202 -0
- package/package.json +16 -6
- package/src/scr_array.c +488 -0
- package/src/scr_assert.c +1393 -0
- package/src/scr_async.c +2593 -0
- package/src/scr_async_dyn.c +607 -0
- package/src/scr_bytes.c +1425 -0
- package/src/scr_bytes_io.c +161 -0
- package/src/scr_child.c +3587 -0
- package/src/scr_closure.c +214 -0
- package/src/scr_console.c +143 -0
- package/src/scr_cycle.c +218 -0
- package/src/scr_dc.c +805 -0
- package/src/scr_dgram.c +1007 -0
- package/src/scr_dyn_handle.c +154 -0
- package/src/scr_dyn_invoke.c +644 -0
- package/src/scr_error.c +332 -0
- package/src/scr_events.c +714 -0
- package/src/scr_events_emitter.c +699 -0
- package/src/scr_exception.c +242 -0
- package/src/scr_fetch.c +1130 -0
- package/src/scr_fetch_curl.c +654 -0
- package/src/scr_http.c +3482 -0
- package/src/scr_http2.c +3030 -0
- package/src/scr_inspect.c +803 -0
- package/src/scr_inspect_island.c +51 -0
- package/src/scr_island.c +9363 -0
- package/src/scr_json.c +2151 -0
- package/src/scr_lib.c +3612 -0
- package/src/scr_loop_epoll.c +241 -0
- package/src/scr_loop_kqueue.c +121 -0
- package/src/scr_loop_wsapoll.c +208 -0
- package/src/scr_map.c +591 -0
- package/src/scr_net.c +3017 -0
- package/src/scr_net_island.c +458 -0
- package/src/scr_number.c +115 -0
- package/src/scr_object.c +25 -0
- package/src/scr_path.c +1266 -0
- package/src/scr_platform.h +119 -0
- package/src/scr_readline.c +287 -0
- package/src/scr_regex.c +1080 -0
- package/src/scr_runtime.h +5046 -0
- package/src/scr_stream.c +2877 -0
- package/src/scr_string.c +1264 -0
- package/src/scr_symbol.c +132 -0
- package/src/scr_test.c +662 -0
- package/src/scr_tls.c +1520 -0
- package/src/scr_union.c +105 -0
- package/src/scr_url.c +911 -0
- package/src/scr_url_internal.h +29 -0
- package/src/scr_url_params.c +561 -0
- package/src/scr_watch.c +568 -0
- package/src/scr_web.c +1778 -0
- package/src/scr_win.c +76 -0
- package/src/scr_zlib.c +197 -0
- package/src/scr_zlib_island.c +15 -0
- package/vendor/README.md +52 -0
- package/vendor/curl/COPYRIGHT +534 -0
- package/vendor/curl/include/curl/curl.h +3214 -0
- package/vendor/curl/include/curl/curlver.h +79 -0
- package/vendor/curl/include/curl/easy.h +125 -0
- package/vendor/curl/include/curl/header.h +74 -0
- package/vendor/curl/include/curl/mprintf.h +52 -0
- package/vendor/curl/include/curl/multi.h +460 -0
- package/vendor/curl/include/curl/options.h +70 -0
- package/vendor/curl/include/curl/stdcheaders.h +35 -0
- package/vendor/curl/include/curl/system.h +508 -0
- package/vendor/curl/include/curl/typecheck-gcc.h +716 -0
- package/vendor/curl/include/curl/urlapi.h +149 -0
- package/vendor/curl/include/curl/websockets.h +84 -0
- package/vendor/mbedtls/LICENSE +553 -0
- package/vendor/mbedtls/include/CMakeLists.txt +22 -0
- package/vendor/mbedtls/include/mbedtls/aes.h +631 -0
- package/vendor/mbedtls/include/mbedtls/aria.h +343 -0
- package/vendor/mbedtls/include/mbedtls/asn1.h +642 -0
- package/vendor/mbedtls/include/mbedtls/asn1write.h +390 -0
- package/vendor/mbedtls/include/mbedtls/base64.h +82 -0
- package/vendor/mbedtls/include/mbedtls/bignum.h +1088 -0
- package/vendor/mbedtls/include/mbedtls/block_cipher.h +76 -0
- package/vendor/mbedtls/include/mbedtls/build_info.h +194 -0
- package/vendor/mbedtls/include/mbedtls/camellia.h +305 -0
- package/vendor/mbedtls/include/mbedtls/ccm.h +526 -0
- package/vendor/mbedtls/include/mbedtls/chacha20.h +209 -0
- package/vendor/mbedtls/include/mbedtls/chachapoly.h +351 -0
- package/vendor/mbedtls/include/mbedtls/check_config.h +1149 -0
- package/vendor/mbedtls/include/mbedtls/cipher.h +1250 -0
- package/vendor/mbedtls/include/mbedtls/cmac.h +246 -0
- package/vendor/mbedtls/include/mbedtls/compat-2.x.h +46 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h +578 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_legacy_from_psa.h +873 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_psa_from_legacy.h +359 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_psa_superset_legacy.h +145 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_ssl.h +91 -0
- package/vendor/mbedtls/include/mbedtls/config_adjust_x509.h +35 -0
- package/vendor/mbedtls/include/mbedtls/config_psa.h +61 -0
- package/vendor/mbedtls/include/mbedtls/constant_time.h +36 -0
- package/vendor/mbedtls/include/mbedtls/ctr_drbg.h +596 -0
- package/vendor/mbedtls/include/mbedtls/debug.h +156 -0
- package/vendor/mbedtls/include/mbedtls/des.h +385 -0
- package/vendor/mbedtls/include/mbedtls/dhm.h +972 -0
- package/vendor/mbedtls/include/mbedtls/ecdh.h +455 -0
- package/vendor/mbedtls/include/mbedtls/ecdsa.h +674 -0
- package/vendor/mbedtls/include/mbedtls/ecjpake.h +298 -0
- package/vendor/mbedtls/include/mbedtls/ecp.h +1517 -0
- package/vendor/mbedtls/include/mbedtls/entropy.h +274 -0
- package/vendor/mbedtls/include/mbedtls/error.h +201 -0
- package/vendor/mbedtls/include/mbedtls/gcm.h +387 -0
- package/vendor/mbedtls/include/mbedtls/hkdf.h +124 -0
- package/vendor/mbedtls/include/mbedtls/hmac_drbg.h +434 -0
- package/vendor/mbedtls/include/mbedtls/lms.h +440 -0
- package/vendor/mbedtls/include/mbedtls/mbedtls_config.h +4446 -0
- package/vendor/mbedtls/include/mbedtls/md.h +526 -0
- package/vendor/mbedtls/include/mbedtls/md5.h +190 -0
- package/vendor/mbedtls/include/mbedtls/memory_buffer_alloc.h +142 -0
- package/vendor/mbedtls/include/mbedtls/net_sockets.h +299 -0
- package/vendor/mbedtls/include/mbedtls/nist_kw.h +166 -0
- package/vendor/mbedtls/include/mbedtls/oid.h +727 -0
- package/vendor/mbedtls/include/mbedtls/pem.h +160 -0
- package/vendor/mbedtls/include/mbedtls/pk.h +1303 -0
- package/vendor/mbedtls/include/mbedtls/pkcs12.h +186 -0
- package/vendor/mbedtls/include/mbedtls/pkcs5.h +198 -0
- package/vendor/mbedtls/include/mbedtls/pkcs7.h +252 -0
- package/vendor/mbedtls/include/mbedtls/platform.h +516 -0
- package/vendor/mbedtls/include/mbedtls/platform_time.h +79 -0
- package/vendor/mbedtls/include/mbedtls/platform_util.h +247 -0
- package/vendor/mbedtls/include/mbedtls/poly1305.h +168 -0
- package/vendor/mbedtls/include/mbedtls/private_access.h +20 -0
- package/vendor/mbedtls/include/mbedtls/psa_util.h +207 -0
- package/vendor/mbedtls/include/mbedtls/ripemd160.h +136 -0
- package/vendor/mbedtls/include/mbedtls/rsa.h +1170 -0
- package/vendor/mbedtls/include/mbedtls/sha1.h +219 -0
- package/vendor/mbedtls/include/mbedtls/sha256.h +200 -0
- package/vendor/mbedtls/include/mbedtls/sha3.h +172 -0
- package/vendor/mbedtls/include/mbedtls/sha512.h +208 -0
- package/vendor/mbedtls/include/mbedtls/ssl.h +5887 -0
- package/vendor/mbedtls/include/mbedtls/ssl_cache.h +187 -0
- package/vendor/mbedtls/include/mbedtls/ssl_ciphersuites.h +482 -0
- package/vendor/mbedtls/include/mbedtls/ssl_cookie.h +106 -0
- package/vendor/mbedtls/include/mbedtls/ssl_ticket.h +199 -0
- package/vendor/mbedtls/include/mbedtls/threading.h +167 -0
- package/vendor/mbedtls/include/mbedtls/timing.h +94 -0
- package/vendor/mbedtls/include/mbedtls/version.h +78 -0
- package/vendor/mbedtls/include/mbedtls/x509.h +500 -0
- package/vendor/mbedtls/include/mbedtls/x509_crl.h +184 -0
- package/vendor/mbedtls/include/mbedtls/x509_crt.h +1208 -0
- package/vendor/mbedtls/include/mbedtls/x509_csr.h +382 -0
- package/vendor/mbedtls/include/psa/build_info.h +20 -0
- package/vendor/mbedtls/include/psa/crypto.h +4998 -0
- package/vendor/mbedtls/include/psa/crypto_adjust_auto_enabled.h +31 -0
- package/vendor/mbedtls/include/psa/crypto_adjust_config_dependencies.h +51 -0
- package/vendor/mbedtls/include/psa/crypto_adjust_config_key_pair_types.h +101 -0
- package/vendor/mbedtls/include/psa/crypto_adjust_config_synonyms.h +49 -0
- package/vendor/mbedtls/include/psa/crypto_builtin_composites.h +214 -0
- package/vendor/mbedtls/include/psa/crypto_builtin_key_derivation.h +118 -0
- package/vendor/mbedtls/include/psa/crypto_builtin_primitives.h +114 -0
- package/vendor/mbedtls/include/psa/crypto_compat.h +230 -0
- package/vendor/mbedtls/include/psa/crypto_config.h +145 -0
- package/vendor/mbedtls/include/psa/crypto_driver_common.h +44 -0
- package/vendor/mbedtls/include/psa/crypto_driver_contexts_composites.h +151 -0
- package/vendor/mbedtls/include/psa/crypto_driver_contexts_key_derivation.h +52 -0
- package/vendor/mbedtls/include/psa/crypto_driver_contexts_primitives.h +105 -0
- package/vendor/mbedtls/include/psa/crypto_extra.h +2145 -0
- package/vendor/mbedtls/include/psa/crypto_legacy.h +88 -0
- package/vendor/mbedtls/include/psa/crypto_platform.h +102 -0
- package/vendor/mbedtls/include/psa/crypto_se_driver.h +1383 -0
- package/vendor/mbedtls/include/psa/crypto_sizes.h +1319 -0
- package/vendor/mbedtls/include/psa/crypto_struct.h +527 -0
- package/vendor/mbedtls/include/psa/crypto_types.h +508 -0
- package/vendor/mbedtls/include/psa/crypto_values.h +2782 -0
- package/vendor/mbedtls/library/aes.c +2294 -0
- package/vendor/mbedtls/library/aesce.c +624 -0
- package/vendor/mbedtls/library/aesce.h +136 -0
- package/vendor/mbedtls/library/aesni.c +846 -0
- package/vendor/mbedtls/library/aesni.h +162 -0
- package/vendor/mbedtls/library/alignment.h +704 -0
- package/vendor/mbedtls/library/aria.c +969 -0
- package/vendor/mbedtls/library/asn1parse.c +468 -0
- package/vendor/mbedtls/library/asn1write.c +440 -0
- package/vendor/mbedtls/library/base64.c +322 -0
- package/vendor/mbedtls/library/base64_internal.h +45 -0
- package/vendor/mbedtls/library/bignum.c +2583 -0
- package/vendor/mbedtls/library/bignum_core.c +1240 -0
- package/vendor/mbedtls/library/bignum_core.h +872 -0
- package/vendor/mbedtls/library/bignum_core_invasive.h +38 -0
- package/vendor/mbedtls/library/bignum_internal.h +122 -0
- package/vendor/mbedtls/library/bignum_mod.c +394 -0
- package/vendor/mbedtls/library/bignum_mod.h +452 -0
- package/vendor/mbedtls/library/bignum_mod_raw.c +276 -0
- package/vendor/mbedtls/library/bignum_mod_raw.h +416 -0
- package/vendor/mbedtls/library/bignum_mod_raw_invasive.h +34 -0
- package/vendor/mbedtls/library/block_cipher.c +207 -0
- package/vendor/mbedtls/library/block_cipher_internal.h +99 -0
- package/vendor/mbedtls/library/bn_mul.h +1094 -0
- package/vendor/mbedtls/library/camellia.c +1058 -0
- package/vendor/mbedtls/library/ccm.c +777 -0
- package/vendor/mbedtls/library/chacha20.c +563 -0
- package/vendor/mbedtls/library/chacha20_internal.h +23 -0
- package/vendor/mbedtls/library/chachapoly.c +487 -0
- package/vendor/mbedtls/library/check_crypto_config.h +136 -0
- package/vendor/mbedtls/library/cipher.c +1712 -0
- package/vendor/mbedtls/library/cipher_invasive.h +28 -0
- package/vendor/mbedtls/library/cipher_wrap.c +2482 -0
- package/vendor/mbedtls/library/cipher_wrap.h +178 -0
- package/vendor/mbedtls/library/cmac.c +1067 -0
- package/vendor/mbedtls/library/common.h +487 -0
- package/vendor/mbedtls/library/constant_time.c +247 -0
- package/vendor/mbedtls/library/constant_time_impl.h +541 -0
- package/vendor/mbedtls/library/constant_time_internal.h +579 -0
- package/vendor/mbedtls/library/ctr.h +35 -0
- package/vendor/mbedtls/library/ctr_drbg.c +1016 -0
- package/vendor/mbedtls/library/debug.c +475 -0
- package/vendor/mbedtls/library/debug_internal.h +185 -0
- package/vendor/mbedtls/library/des.c +1042 -0
- package/vendor/mbedtls/library/dhm.c +700 -0
- package/vendor/mbedtls/library/ecdh.c +696 -0
- package/vendor/mbedtls/library/ecdsa.c +858 -0
- package/vendor/mbedtls/library/ecjpake.c +1216 -0
- package/vendor/mbedtls/library/ecp.c +3674 -0
- package/vendor/mbedtls/library/ecp_curves.c +6125 -0
- package/vendor/mbedtls/library/ecp_curves_new.c +9 -0
- package/vendor/mbedtls/library/ecp_internal_alt.h +287 -0
- package/vendor/mbedtls/library/ecp_invasive.h +305 -0
- package/vendor/mbedtls/library/entropy.c +680 -0
- package/vendor/mbedtls/library/entropy_poll.c +233 -0
- package/vendor/mbedtls/library/entropy_poll.h +64 -0
- package/vendor/mbedtls/library/error.c +878 -0
- package/vendor/mbedtls/library/gcm.c +1330 -0
- package/vendor/mbedtls/library/hkdf.c +161 -0
- package/vendor/mbedtls/library/hmac_drbg.c +633 -0
- package/vendor/mbedtls/library/lmots.c +789 -0
- package/vendor/mbedtls/library/lmots.h +288 -0
- package/vendor/mbedtls/library/lms.c +778 -0
- package/vendor/mbedtls/library/md.c +1108 -0
- package/vendor/mbedtls/library/md5.c +426 -0
- package/vendor/mbedtls/library/md_psa.h +26 -0
- package/vendor/mbedtls/library/md_wrap.h +46 -0
- package/vendor/mbedtls/library/memory_buffer_alloc.c +751 -0
- package/vendor/mbedtls/library/mps_common.h +181 -0
- package/vendor/mbedtls/library/mps_error.h +89 -0
- package/vendor/mbedtls/library/mps_reader.c +538 -0
- package/vendor/mbedtls/library/mps_reader.h +366 -0
- package/vendor/mbedtls/library/mps_trace.c +112 -0
- package/vendor/mbedtls/library/mps_trace.h +154 -0
- package/vendor/mbedtls/library/net_sockets.c +694 -0
- package/vendor/mbedtls/library/nist_kw.c +729 -0
- package/vendor/mbedtls/library/oid.c +1166 -0
- package/vendor/mbedtls/library/padlock.c +157 -0
- package/vendor/mbedtls/library/padlock.h +111 -0
- package/vendor/mbedtls/library/pem.c +554 -0
- package/vendor/mbedtls/library/pk.c +1602 -0
- package/vendor/mbedtls/library/pk_ecc.c +261 -0
- package/vendor/mbedtls/library/pk_internal.h +241 -0
- package/vendor/mbedtls/library/pk_wrap.c +1618 -0
- package/vendor/mbedtls/library/pk_wrap.h +138 -0
- package/vendor/mbedtls/library/pkcs12.c +437 -0
- package/vendor/mbedtls/library/pkcs5.c +500 -0
- package/vendor/mbedtls/library/pkcs7.c +787 -0
- package/vendor/mbedtls/library/pkparse.c +1392 -0
- package/vendor/mbedtls/library/pkwrite.c +631 -0
- package/vendor/mbedtls/library/pkwrite.h +121 -0
- package/vendor/mbedtls/library/platform.c +402 -0
- package/vendor/mbedtls/library/platform_util.c +258 -0
- package/vendor/mbedtls/library/poly1305.c +492 -0
- package/vendor/mbedtls/library/psa_crypto.c +9532 -0
- package/vendor/mbedtls/library/psa_crypto_aead.c +646 -0
- package/vendor/mbedtls/library/psa_crypto_aead.h +499 -0
- package/vendor/mbedtls/library/psa_crypto_cipher.c +747 -0
- package/vendor/mbedtls/library/psa_crypto_cipher.h +316 -0
- package/vendor/mbedtls/library/psa_crypto_client.c +22 -0
- package/vendor/mbedtls/library/psa_crypto_core.h +983 -0
- package/vendor/mbedtls/library/psa_crypto_core_common.h +52 -0
- package/vendor/mbedtls/library/psa_crypto_driver_wrappers.h +2896 -0
- package/vendor/mbedtls/library/psa_crypto_driver_wrappers_no_static.c +256 -0
- package/vendor/mbedtls/library/psa_crypto_driver_wrappers_no_static.h +31 -0
- package/vendor/mbedtls/library/psa_crypto_ecp.c +594 -0
- package/vendor/mbedtls/library/psa_crypto_ecp.h +267 -0
- package/vendor/mbedtls/library/psa_crypto_ffdh.c +361 -0
- package/vendor/mbedtls/library/psa_crypto_ffdh.h +131 -0
- package/vendor/mbedtls/library/psa_crypto_hash.c +470 -0
- package/vendor/mbedtls/library/psa_crypto_hash.h +211 -0
- package/vendor/mbedtls/library/psa_crypto_invasive.h +92 -0
- package/vendor/mbedtls/library/psa_crypto_its.h +131 -0
- package/vendor/mbedtls/library/psa_crypto_mac.c +505 -0
- package/vendor/mbedtls/library/psa_crypto_mac.h +264 -0
- package/vendor/mbedtls/library/psa_crypto_pake.c +571 -0
- package/vendor/mbedtls/library/psa_crypto_pake.h +159 -0
- package/vendor/mbedtls/library/psa_crypto_random.c +181 -0
- package/vendor/mbedtls/library/psa_crypto_random.h +72 -0
- package/vendor/mbedtls/library/psa_crypto_random_impl.h +200 -0
- package/vendor/mbedtls/library/psa_crypto_rsa.c +714 -0
- package/vendor/mbedtls/library/psa_crypto_rsa.h +321 -0
- package/vendor/mbedtls/library/psa_crypto_se.c +373 -0
- package/vendor/mbedtls/library/psa_crypto_se.h +192 -0
- package/vendor/mbedtls/library/psa_crypto_slot_management.c +1137 -0
- package/vendor/mbedtls/library/psa_crypto_slot_management.h +344 -0
- package/vendor/mbedtls/library/psa_crypto_storage.c +481 -0
- package/vendor/mbedtls/library/psa_crypto_storage.h +392 -0
- package/vendor/mbedtls/library/psa_its_file.c +254 -0
- package/vendor/mbedtls/library/psa_util.c +614 -0
- package/vendor/mbedtls/library/psa_util_internal.h +100 -0
- package/vendor/mbedtls/library/ripemd160.c +490 -0
- package/vendor/mbedtls/library/rsa.c +3121 -0
- package/vendor/mbedtls/library/rsa_alt_helpers.c +455 -0
- package/vendor/mbedtls/library/rsa_alt_helpers.h +209 -0
- package/vendor/mbedtls/library/rsa_internal.h +188 -0
- package/vendor/mbedtls/library/sha1.c +480 -0
- package/vendor/mbedtls/library/sha256.c +983 -0
- package/vendor/mbedtls/library/sha3.c +721 -0
- package/vendor/mbedtls/library/sha512.c +1115 -0
- package/vendor/mbedtls/library/ssl_cache.c +410 -0
- package/vendor/mbedtls/library/ssl_ciphersuites.c +2050 -0
- package/vendor/mbedtls/library/ssl_ciphersuites_internal.h +154 -0
- package/vendor/mbedtls/library/ssl_client.c +1023 -0
- package/vendor/mbedtls/library/ssl_client.h +22 -0
- package/vendor/mbedtls/library/ssl_cookie.c +384 -0
- package/vendor/mbedtls/library/ssl_debug_helpers.h +85 -0
- package/vendor/mbedtls/library/ssl_debug_helpers_generated.c +251 -0
- package/vendor/mbedtls/library/ssl_misc.h +3198 -0
- package/vendor/mbedtls/library/ssl_msg.c +6619 -0
- package/vendor/mbedtls/library/ssl_ticket.c +556 -0
- package/vendor/mbedtls/library/ssl_tls.c +10234 -0
- package/vendor/mbedtls/library/ssl_tls12_client.c +3688 -0
- package/vendor/mbedtls/library/ssl_tls12_server.c +4311 -0
- package/vendor/mbedtls/library/ssl_tls13_client.c +3208 -0
- package/vendor/mbedtls/library/ssl_tls13_generic.c +1793 -0
- package/vendor/mbedtls/library/ssl_tls13_invasive.h +23 -0
- package/vendor/mbedtls/library/ssl_tls13_keys.c +1918 -0
- package/vendor/mbedtls/library/ssl_tls13_keys.h +668 -0
- package/vendor/mbedtls/library/ssl_tls13_server.c +3624 -0
- package/vendor/mbedtls/library/threading.c +193 -0
- package/vendor/mbedtls/library/threading_internal.h +28 -0
- package/vendor/mbedtls/library/timing.c +152 -0
- package/vendor/mbedtls/library/version.c +32 -0
- package/vendor/mbedtls/library/version_features.c +856 -0
- package/vendor/mbedtls/library/x509.c +1776 -0
- package/vendor/mbedtls/library/x509_create.c +570 -0
- package/vendor/mbedtls/library/x509_crl.c +708 -0
- package/vendor/mbedtls/library/x509_crt.c +3311 -0
- package/vendor/mbedtls/library/x509_csr.c +648 -0
- package/vendor/mbedtls/library/x509_internal.h +101 -0
- package/vendor/mbedtls/library/x509write.c +174 -0
- package/vendor/mbedtls/library/x509write_crt.c +688 -0
- package/vendor/mbedtls/library/x509write_csr.c +336 -0
- package/vendor/quickjs-ng/CMakeLists.txt +566 -0
- package/vendor/quickjs-ng/LICENSE +24 -0
- package/vendor/quickjs-ng/README.md +24 -0
- package/vendor/quickjs-ng/api-test.c +1195 -0
- package/vendor/quickjs-ng/builtin-array-fromasync.h +119 -0
- package/vendor/quickjs-ng/builtin-iterator-zip-keyed.h +332 -0
- package/vendor/quickjs-ng/builtin-iterator-zip.h +337 -0
- package/vendor/quickjs-ng/cutils.h +1998 -0
- package/vendor/quickjs-ng/dtoa.c +1619 -0
- package/vendor/quickjs-ng/dtoa.h +87 -0
- package/vendor/quickjs-ng/gen/function_source.c +81 -0
- package/vendor/quickjs-ng/gen/hello.c +53 -0
- package/vendor/quickjs-ng/gen/hello_module.c +106 -0
- package/vendor/quickjs-ng/gen/repl.c +3036 -0
- package/vendor/quickjs-ng/gen/standalone.c +323 -0
- package/vendor/quickjs-ng/gen/test_fib.c +81 -0
- package/vendor/quickjs-ng/libregexp-opcode.h +73 -0
- package/vendor/quickjs-ng/libregexp.c +3474 -0
- package/vendor/quickjs-ng/libregexp.h +101 -0
- package/vendor/quickjs-ng/libunicode-table.h +5173 -0
- package/vendor/quickjs-ng/libunicode.c +2069 -0
- package/vendor/quickjs-ng/libunicode.h +172 -0
- package/vendor/quickjs-ng/list.h +107 -0
- package/vendor/quickjs-ng/lre-test.c +52 -0
- package/vendor/quickjs-ng/qjs.c +762 -0
- package/vendor/quickjs-ng/qjsc.c +673 -0
- package/vendor/quickjs-ng/quickjs-atom.h +280 -0
- package/vendor/quickjs-ng/quickjs-c-atomics.h +54 -0
- package/vendor/quickjs-ng/quickjs-libc.c +5037 -0
- package/vendor/quickjs-ng/quickjs-libc.h +87 -0
- package/vendor/quickjs-ng/quickjs-opcode.h +377 -0
- package/vendor/quickjs-ng/quickjs.c +64041 -0
- package/vendor/quickjs-ng/quickjs.h +1447 -0
- package/vendor/quickjs-ng/run-test262.c +2374 -0
- package/vendor/quickjs-ng/unicode_gen.c +3121 -0
- package/vendor/quickjs-ng/unicode_gen_def.h +310 -0
- package/vendor/ryu/LICENSE-Boost +23 -0
- package/vendor/ryu/common.h +114 -0
- package/vendor/ryu/d2s.c +509 -0
- package/vendor/ryu/d2s_full_table.h +367 -0
- package/vendor/ryu/d2s_intrinsics.h +357 -0
- package/vendor/ryu/d2s_small_table.h +186 -0
- package/vendor/ryu/digit_table.h +35 -0
- package/vendor/ryu/ryu.h +46 -0
- package/vendor/zlib/LICENSE +22 -0
- package/vendor/zlib/adler32.c +164 -0
- package/vendor/zlib/compress.c +75 -0
- package/vendor/zlib/crc32.c +1049 -0
- package/vendor/zlib/crc32.h +9446 -0
- package/vendor/zlib/deflate.c +2139 -0
- package/vendor/zlib/deflate.h +377 -0
- package/vendor/zlib/gzclose.c +23 -0
- package/vendor/zlib/gzguts.h +214 -0
- package/vendor/zlib/gzlib.c +582 -0
- package/vendor/zlib/gzread.c +602 -0
- package/vendor/zlib/gzwrite.c +631 -0
- package/vendor/zlib/infback.c +628 -0
- package/vendor/zlib/inffast.c +320 -0
- package/vendor/zlib/inffast.h +11 -0
- package/vendor/zlib/inffixed.h +94 -0
- package/vendor/zlib/inflate.c +1526 -0
- package/vendor/zlib/inflate.h +126 -0
- package/vendor/zlib/inftrees.c +299 -0
- package/vendor/zlib/inftrees.h +62 -0
- package/vendor/zlib/trees.c +1117 -0
- package/vendor/zlib/trees.h +128 -0
- package/vendor/zlib/uncompr.c +85 -0
- package/vendor/zlib/zconf.h +543 -0
- package/vendor/zlib/zlib.h +1938 -0
- package/vendor/zlib/zutil.c +299 -0
- package/vendor/zlib/zutil.h +254 -0
- package/README.md +0 -3
- package/index.js +0 -2
package/src/scr_tls.c
ADDED
|
@@ -0,0 +1,1520 @@
|
|
|
1
|
+
/* node:tls + node:https — the TLS transport under the socket kind,
|
|
2
|
+
* layered exactly the way scr_http.c layers on scr_net.c: the socket
|
|
3
|
+
* surface (ScrNetSocket, and the http req/res/client handles above it)
|
|
4
|
+
* is unchanged, and this unit plugs the ScrNetTransportOps table into
|
|
5
|
+
* the socket so reads decrypt, writes encrypt, and readiness drives the
|
|
6
|
+
* handshake. Link-gated like every optional unit: TLS-free binaries
|
|
7
|
+
* never compile this file or the vendored library.
|
|
8
|
+
*
|
|
9
|
+
* ── Design note (the TLS provider decision) ──────────────────────────
|
|
10
|
+
*
|
|
11
|
+
* The provider is VENDORED mbedTLS (3.6 LTS, Apache-2.0, ~1.2MB
|
|
12
|
+
* archive, see vendor/README.md). The alternatives and why they lost:
|
|
13
|
+
*
|
|
14
|
+
* - Security.framework / SecureTransport: zero vendoring and present on
|
|
15
|
+
* every macOS, but (a) deprecated since 10.15 with no non-deprecated
|
|
16
|
+
* replacement below Network.framework's connection model, (b) macOS-
|
|
17
|
+
* only — the queued Linux port would need a second backend behind the
|
|
18
|
+
* same ops table, and (c) DISQUALIFYING for this program: a server
|
|
19
|
+
* identity must be a SecIdentity, which public API only mints through
|
|
20
|
+
* a keychain import — portless mints per-hostname cert/key PEM FILES
|
|
21
|
+
* with openssl and loads them headlessly; a temp-keychain dance per
|
|
22
|
+
* certificate is exactly the fragility this compiler avoids.
|
|
23
|
+
* - BoringSSL: no releases/versioning to pin, a CMake+Go build, and an
|
|
24
|
+
* order of magnitude more code than the slice needs.
|
|
25
|
+
* - LibreSSL libtls: the cleanest API of the four, but macOS ships no
|
|
26
|
+
* linkable libtls (the system LibreSSL is the CLI only) — it would be
|
|
27
|
+
* a brew dependency whose availability varies, weaker than the
|
|
28
|
+
* libcurl precedent (libcurl is a macOS SYSTEM library).
|
|
29
|
+
*
|
|
30
|
+
* mbedTLS wins on the two axes that matter here: PEM cert/key/CA
|
|
31
|
+
* loading is first-class (mbedtls_x509_crt_parse / pk_parse_key over
|
|
32
|
+
* bytes — the portless local-CA flow needs nothing else), and the same
|
|
33
|
+
* vendored tree compiles unchanged on Linux, so the port pays zero
|
|
34
|
+
* extra. It builds as plain C11 with the stock config (no generator
|
|
35
|
+
* scripts, no CMake — the libregexp cache precedent), and its
|
|
36
|
+
* nonblocking BIO contract (WANT_READ/WANT_WRITE) maps 1:1 onto the
|
|
37
|
+
* kqueue readiness loop. Migration cost if ever needed: everything
|
|
38
|
+
* mbedTLS-specific lives behind ScrNetTransportOps in this one file.
|
|
39
|
+
*
|
|
40
|
+
* ── The wiring ────────────────────────────────────────────────────────
|
|
41
|
+
*
|
|
42
|
+
* Server: tls.createServer(cert, key, handler) is an ordinary
|
|
43
|
+
* ScrNetServer whose native-connection hook installs the transport on
|
|
44
|
+
* each accepted socket and whose 'connection' listeners are DEFERRED to
|
|
45
|
+
* handshake completion — the handler is Node's 'secureConnection', fired
|
|
46
|
+
* with a socket that then behaves exactly like a net socket.
|
|
47
|
+
* https.createServer is scr_http_create_server with its parser hook
|
|
48
|
+
* WRAPPED: the TLS hook installs the transport first and attaches the
|
|
49
|
+
* http parser from on_established, so the parser only ever sees
|
|
50
|
+
* plaintext. Handshake failures on the server side tear the socket down
|
|
51
|
+
* SILENTLY — Node's default for 'tlsClientError' with no listener, which
|
|
52
|
+
* is what portless observes (it registers none).
|
|
53
|
+
*
|
|
54
|
+
* Client: https.request is scr_http_request_ex with a wrap hook that
|
|
55
|
+
* installs a client transport on the freshly dialed socket; the request
|
|
56
|
+
* head buffers in the socket's write buffer until the handshake
|
|
57
|
+
* establishes (the mid-connect buffering path, reused verbatim).
|
|
58
|
+
* rejectUnauthorized:false maps to VERIFY_NONE; `ca` replaces the trust
|
|
59
|
+
* anchors; with neither, /etc/ssl/cert.pem stands in for Node's bundled
|
|
60
|
+
* Mozilla roots (SEMANTICS.md documents the divergence). Verify
|
|
61
|
+
* failures surface as Node-shaped 'error' messages on the request
|
|
62
|
+
* ("self-signed certificate", "self-signed certificate in certificate
|
|
63
|
+
* chain", "unable to verify the first certificate", "certificate has
|
|
64
|
+
* expired") through the socket's pending-error sweep — then 'close',
|
|
65
|
+
* Node's order, exactly like ECONNREFUSED.
|
|
66
|
+
*
|
|
67
|
+
* Bounds (SEMANTICS.md divergence 55): no session resumption or
|
|
68
|
+
* tickets, no renegotiation/post-handshake auth, no client
|
|
69
|
+
* certificates, hostname verification is skipped for IP-literal peers
|
|
70
|
+
* (mbedTLS has no IP-SAN matching in 3.6), and non-verify client
|
|
71
|
+
* handshake failures carry a generic message where Node prints an
|
|
72
|
+
* OpenSSL one. */
|
|
73
|
+
#include "scr_runtime.h"
|
|
74
|
+
|
|
75
|
+
#include <errno.h>
|
|
76
|
+
#include <stdio.h>
|
|
77
|
+
#include <stdlib.h>
|
|
78
|
+
#include <string.h>
|
|
79
|
+
#include <unistd.h>
|
|
80
|
+
#ifdef _WIN32
|
|
81
|
+
#include <winsock2.h>
|
|
82
|
+
#include <ws2tcpip.h> /* inet_pton, in6_addr */
|
|
83
|
+
#else
|
|
84
|
+
#include <arpa/inet.h>
|
|
85
|
+
#endif
|
|
86
|
+
|
|
87
|
+
#include "mbedtls/ctr_drbg.h"
|
|
88
|
+
#include "mbedtls/entropy.h"
|
|
89
|
+
#include "mbedtls/error.h"
|
|
90
|
+
#include "mbedtls/net_sockets.h" /* the MBEDTLS_ERR_NET_* codes the BIOs answer */
|
|
91
|
+
#include "mbedtls/pk.h"
|
|
92
|
+
#include "mbedtls/ssl.h"
|
|
93
|
+
#include "mbedtls/x509_crt.h"
|
|
94
|
+
|
|
95
|
+
static void scr_tls_oom(void) {
|
|
96
|
+
fputs("scriptc: out of memory\n", stderr);
|
|
97
|
+
abort();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#ifdef _WIN32
|
|
101
|
+
/* ── the winsock arm (scr_net.c's respelling, the two calls this unit
|
|
102
|
+
* makes) ──────────────────────────────────────────────────────────────
|
|
103
|
+
* The BIOs and the SNI pre-parse do raw fd I/O beside the socket's own
|
|
104
|
+
* transport plumbing, so the same one-time respelling applies: socket
|
|
105
|
+
* reads are recv, writes are send (ReadFile/_read do not apply to
|
|
106
|
+
* winsock handles), and failures land their WSA code in errno TRANSLATED
|
|
107
|
+
* to the POSIX constant the call sites test. WSAECONNABORTED joins the
|
|
108
|
+
* reset arm deliberately: at the TLS layer both spell "the peer died",
|
|
109
|
+
* and the reset arms (CONN_RESET → silent server teardown / the client's
|
|
110
|
+
* socket-hang-up classification) are Node's rendering for each. Winsock
|
|
111
|
+
* is already started whenever this unit runs — every TLS socket is an
|
|
112
|
+
* ScrNetSocket, and scr_net.c initializes the poller (WSAStartup) before
|
|
113
|
+
* any socket exists. */
|
|
114
|
+
static int scr_tls_w32_map_err(int e) {
|
|
115
|
+
switch (e) {
|
|
116
|
+
case WSAEWOULDBLOCK: return EAGAIN;
|
|
117
|
+
case WSAEINTR: return EINTR;
|
|
118
|
+
case WSAECONNRESET: return ECONNRESET;
|
|
119
|
+
case WSAECONNABORTED: return ECONNRESET;
|
|
120
|
+
case WSAESHUTDOWN: return EPIPE; /* libuv's map: send-after-shutdown is EPIPE */
|
|
121
|
+
default: return e;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static ssize_t scr_tls_w32_read(int fd, void *buf, size_t len) {
|
|
126
|
+
int n = recv((SOCKET)fd, (char *)buf, len > 0x40000000 ? 0x40000000 : (int)len, 0);
|
|
127
|
+
if (n < 0) errno = scr_tls_w32_map_err((int)WSAGetLastError());
|
|
128
|
+
return n;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static ssize_t scr_tls_w32_write(int fd, const void *buf, size_t len) {
|
|
132
|
+
int n = send((SOCKET)fd, (const char *)buf, len > 0x40000000 ? 0x40000000 : (int)len, 0);
|
|
133
|
+
if (n < 0) errno = scr_tls_w32_map_err((int)WSAGetLastError());
|
|
134
|
+
return n;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#define read(fd, buf, len) scr_tls_w32_read((fd), (buf), (len))
|
|
138
|
+
#define write(fd, buf, len) scr_tls_w32_write((fd), (buf), (len))
|
|
139
|
+
#endif /* _WIN32 */
|
|
140
|
+
|
|
141
|
+
/* ── the unit RNG (one CTR_DRBG, lazily seeded) ──────────────────────── */
|
|
142
|
+
|
|
143
|
+
static mbedtls_entropy_context scr_tls_entropy;
|
|
144
|
+
static mbedtls_ctr_drbg_context scr_tls_drbg;
|
|
145
|
+
static bool scr_tls_rng_ready = false;
|
|
146
|
+
|
|
147
|
+
/* Defined with the dyn-member hooks at the tail of this file; registered
|
|
148
|
+
* from rng_init so every TLS entry point installs the TLSSocket member
|
|
149
|
+
* dispatch before any TLS socket can reach a dyn binding. */
|
|
150
|
+
static const ScrNetDynhTlsHooks SCR_TLS_DYNH;
|
|
151
|
+
|
|
152
|
+
static void scr_tls_rng_init(void) {
|
|
153
|
+
if (scr_tls_rng_ready) return;
|
|
154
|
+
mbedtls_entropy_init(&scr_tls_entropy);
|
|
155
|
+
mbedtls_ctr_drbg_init(&scr_tls_drbg);
|
|
156
|
+
if (mbedtls_ctr_drbg_seed(&scr_tls_drbg, mbedtls_entropy_func, &scr_tls_entropy,
|
|
157
|
+
(const unsigned char *)"scriptc-tls", 11) != 0) {
|
|
158
|
+
fputs("scriptc: tls: RNG seeding failed\n", stderr);
|
|
159
|
+
abort();
|
|
160
|
+
}
|
|
161
|
+
scr_net_set_dynh_tls(&SCR_TLS_DYNH);
|
|
162
|
+
scr_tls_rng_ready = true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* PEM parsers want a NUL-terminated buffer with the NUL counted. */
|
|
166
|
+
static int scr_tls_parse_crt(mbedtls_x509_crt *crt, const char *pem, size_t len) {
|
|
167
|
+
unsigned char *buf = malloc(len + 1);
|
|
168
|
+
if (!buf) scr_tls_oom();
|
|
169
|
+
memcpy(buf, pem, len);
|
|
170
|
+
buf[len] = 0;
|
|
171
|
+
int r = mbedtls_x509_crt_parse(crt, buf, len + 1);
|
|
172
|
+
free(buf);
|
|
173
|
+
return r;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static int scr_tls_parse_key(mbedtls_pk_context *pk, const char *pem, size_t len) {
|
|
177
|
+
unsigned char *buf = malloc(len + 1);
|
|
178
|
+
if (!buf) scr_tls_oom();
|
|
179
|
+
memcpy(buf, pem, len);
|
|
180
|
+
buf[len] = 0;
|
|
181
|
+
int r = mbedtls_pk_parse_key(pk, buf, len + 1, NULL, 0, mbedtls_ctr_drbg_random,
|
|
182
|
+
&scr_tls_drbg);
|
|
183
|
+
free(buf);
|
|
184
|
+
return r;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Bad cert/key material is a construction-time crash in Node (createServer
|
|
188
|
+
* throws); this runtime's shape for it is the print-and-die trap. */
|
|
189
|
+
static void scr_tls_bad_material(const char *what, int err) {
|
|
190
|
+
char detail[128];
|
|
191
|
+
mbedtls_strerror(err, detail, sizeof detail);
|
|
192
|
+
fflush(stdout);
|
|
193
|
+
fprintf(stderr, "scriptc: tls: failed to parse %s (%s)\n", what, detail);
|
|
194
|
+
_Exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* ── SecureContext (tls.createSecureContext) ─────────────────────────── */
|
|
198
|
+
|
|
199
|
+
/* An opaque refcounted parsed cert/key pair — what an SNI callback
|
|
200
|
+
* answers with; the handshake serves it via mbedtls_ssl_set_hs_own_cert. */
|
|
201
|
+
struct ScrSecureCtx {
|
|
202
|
+
size_t rc;
|
|
203
|
+
mbedtls_x509_crt cert;
|
|
204
|
+
mbedtls_pk_context pk;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
#ifdef SCR_RC_AUDIT
|
|
208
|
+
static long scr_live_secure_ctx = 0;
|
|
209
|
+
long scr_secure_ctx_live_count(void) { return scr_live_secure_ctx; }
|
|
210
|
+
#endif
|
|
211
|
+
|
|
212
|
+
ScrSecureCtx *scr_tls_create_secure_context(const char *cert, size_t cert_len,
|
|
213
|
+
const char *key, size_t key_len) {
|
|
214
|
+
scr_tls_rng_init();
|
|
215
|
+
ScrSecureCtx *c = calloc(1, sizeof *c);
|
|
216
|
+
if (!c) scr_tls_oom();
|
|
217
|
+
c->rc = 1;
|
|
218
|
+
mbedtls_x509_crt_init(&c->cert);
|
|
219
|
+
mbedtls_pk_init(&c->pk);
|
|
220
|
+
int r = scr_tls_parse_crt(&c->cert, cert, cert_len);
|
|
221
|
+
if (r != 0) scr_tls_bad_material("certificate", r);
|
|
222
|
+
r = scr_tls_parse_key(&c->pk, key, key_len);
|
|
223
|
+
if (r != 0) scr_tls_bad_material("private key", r);
|
|
224
|
+
#ifdef SCR_RC_AUDIT
|
|
225
|
+
scr_live_secure_ctx++;
|
|
226
|
+
#endif
|
|
227
|
+
return c;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
ScrSecureCtx *scr_secure_ctx_retain(ScrSecureCtx *c) {
|
|
231
|
+
if (c) c->rc++;
|
|
232
|
+
return c;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
void scr_secure_ctx_release(ScrSecureCtx *c) {
|
|
236
|
+
if (!c || --c->rc != 0) return;
|
|
237
|
+
mbedtls_x509_crt_free(&c->cert);
|
|
238
|
+
mbedtls_pk_free(&c->pk);
|
|
239
|
+
free(c);
|
|
240
|
+
#ifdef SCR_RC_AUDIT
|
|
241
|
+
scr_live_secure_ctx--;
|
|
242
|
+
#endif
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
void *scr_secure_ctx_retain_v(void *p) { return scr_secure_ctx_retain((ScrSecureCtx *)p); }
|
|
246
|
+
void scr_secure_ctx_release_v(void *p) { scr_secure_ctx_release((ScrSecureCtx *)p); }
|
|
247
|
+
|
|
248
|
+
/* ── the server config (one per created server, rc'd) ────────────────── */
|
|
249
|
+
|
|
250
|
+
typedef struct ScrTlsSrv {
|
|
251
|
+
size_t rc;
|
|
252
|
+
mbedtls_ssl_config conf;
|
|
253
|
+
mbedtls_x509_crt cert;
|
|
254
|
+
mbedtls_pk_context pk;
|
|
255
|
+
/* https: the wrapped http-parser connection hook, attached from
|
|
256
|
+
* on_established so it only ever sees plaintext */
|
|
257
|
+
ScrNetNativeConnFn inner_conn;
|
|
258
|
+
void *inner_ctx;
|
|
259
|
+
void (*inner_ctx_free)(void *);
|
|
260
|
+
/* h2 over TLS (http2.createSecureServer): the h2 session attach —
|
|
261
|
+
* on_established dispatches on the NEGOTIATED protocol (h2 → this
|
|
262
|
+
* hook; http/1.1 → inner_conn; no answer → teardown). */
|
|
263
|
+
ScrNetNativeConnFn h2_conn;
|
|
264
|
+
void *h2_ctx;
|
|
265
|
+
void (*h2_ctx_free)(void *);
|
|
266
|
+
/* SNI: the JS SNICallback (owned, nullable) and the emitted answer
|
|
267
|
+
* thunk that becomes each minted answer closure's fn */
|
|
268
|
+
ScrClosure *sni_cb;
|
|
269
|
+
void *sni_answer_fn;
|
|
270
|
+
} ScrTlsSrv;
|
|
271
|
+
|
|
272
|
+
/* The ALPN answers (static storage — mbedTLS keeps the pointers). Plain
|
|
273
|
+
* tls servers configure none, like Node; https advertises http/1.1; the
|
|
274
|
+
* h2 secure server advertises h2 alone (an http/1.1-only client fails
|
|
275
|
+
* the handshake with no_application_protocol — Node's h2-only split). */
|
|
276
|
+
static const char *SCR_TLS_ALPN_HTTP11[] = {"http/1.1", NULL};
|
|
277
|
+
static const char *SCR_TLS_ALPN_H2[] = {"h2", NULL};
|
|
278
|
+
|
|
279
|
+
static ScrTlsSrv *scr_tls_srv_new(const char *cert, size_t cert_len, const char *key,
|
|
280
|
+
size_t key_len, bool alpn_http11) {
|
|
281
|
+
scr_tls_rng_init();
|
|
282
|
+
ScrTlsSrv *s = calloc(1, sizeof *s);
|
|
283
|
+
if (!s) scr_tls_oom();
|
|
284
|
+
s->rc = 1;
|
|
285
|
+
mbedtls_x509_crt_init(&s->cert);
|
|
286
|
+
mbedtls_pk_init(&s->pk);
|
|
287
|
+
mbedtls_ssl_config_init(&s->conf);
|
|
288
|
+
int r = scr_tls_parse_crt(&s->cert, cert, cert_len);
|
|
289
|
+
if (r != 0) scr_tls_bad_material("certificate", r);
|
|
290
|
+
r = scr_tls_parse_key(&s->pk, key, key_len);
|
|
291
|
+
if (r != 0) scr_tls_bad_material("private key", r);
|
|
292
|
+
if (mbedtls_ssl_config_defaults(&s->conf, MBEDTLS_SSL_IS_SERVER,
|
|
293
|
+
MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
294
|
+
MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
|
|
295
|
+
scr_tls_oom();
|
|
296
|
+
}
|
|
297
|
+
mbedtls_ssl_conf_rng(&s->conf, mbedtls_ctr_drbg_random, &scr_tls_drbg);
|
|
298
|
+
mbedtls_ssl_conf_authmode(&s->conf, MBEDTLS_SSL_VERIFY_NONE); /* no client certs, Node's default */
|
|
299
|
+
if (mbedtls_ssl_conf_own_cert(&s->conf, &s->cert, &s->pk) != 0) {
|
|
300
|
+
scr_tls_bad_material("certificate/key pair", 0);
|
|
301
|
+
}
|
|
302
|
+
if (alpn_http11) mbedtls_ssl_conf_alpn_protocols(&s->conf, SCR_TLS_ALPN_HTTP11);
|
|
303
|
+
return s;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
static ScrTlsSrv *scr_tls_srv_retain(ScrTlsSrv *s) {
|
|
307
|
+
s->rc++;
|
|
308
|
+
return s;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
static void scr_tls_srv_release(ScrTlsSrv *s) {
|
|
312
|
+
if (!s || --s->rc != 0) return;
|
|
313
|
+
if (s->inner_ctx_free) s->inner_ctx_free(s->inner_ctx);
|
|
314
|
+
if (s->h2_ctx_free) s->h2_ctx_free(s->h2_ctx);
|
|
315
|
+
scr_closure_release(s->sni_cb);
|
|
316
|
+
mbedtls_ssl_config_free(&s->conf);
|
|
317
|
+
mbedtls_x509_crt_free(&s->cert);
|
|
318
|
+
mbedtls_pk_free(&s->pk);
|
|
319
|
+
free(s);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
static void scr_tls_srv_release_v(void *p) { scr_tls_srv_release((ScrTlsSrv *)p); }
|
|
323
|
+
|
|
324
|
+
/* ── the client config (one per https.request, owned by its tctx) ────── */
|
|
325
|
+
|
|
326
|
+
typedef struct ScrTlsCli {
|
|
327
|
+
mbedtls_ssl_config conf;
|
|
328
|
+
mbedtls_x509_crt ca;
|
|
329
|
+
char *hostname; /* NULL for IP-literal peers (no SNI, no name check) */
|
|
330
|
+
/* true when the handshake RECORDS verification (VERIFY_OPTIONAL /
|
|
331
|
+
* VERIFY_REQUIRED) — socket.authorized answers from the result; a
|
|
332
|
+
* VERIFY_NONE socket (the https client's rejectUnauthorized:false)
|
|
333
|
+
* never verified, so authorized answers false like Node's. */
|
|
334
|
+
bool verify_recorded;
|
|
335
|
+
} ScrTlsCli;
|
|
336
|
+
|
|
337
|
+
/* The default trust anchors when no `ca` option is given: the system
|
|
338
|
+
* bundle, standing in for Node's compiled-in Mozilla roots. Probed once
|
|
339
|
+
* across the distro spellings — /etc/ssl/cert.pem first (macOS ships it;
|
|
340
|
+
* Alpine links it), then Debian/Ubuntu's ca-certificates.crt,
|
|
341
|
+
* Fedora/RHEL's ca-bundle.crt, and openSUSE's ca-bundle.pem. On macOS
|
|
342
|
+
* only the first path exists, so the probe order changes nothing there.
|
|
343
|
+
* A host with none leaves the chain empty: verification fails, exactly
|
|
344
|
+
* the no-roots behavior /etc/ssl/cert.pem-only had. Windows is that host
|
|
345
|
+
* by construction (no PEM bundle ships; the OS store is cert-database-
|
|
346
|
+
* shaped) — a no-`ca` client there fails verification like a bundle-less
|
|
347
|
+
* Unix host, which agrees with the oracle wherever the fixtures tread
|
|
348
|
+
* (their CAs are local, never in Node's Mozilla roots either). */
|
|
349
|
+
static mbedtls_x509_crt scr_tls_system_roots;
|
|
350
|
+
static bool scr_tls_system_roots_loaded = false;
|
|
351
|
+
|
|
352
|
+
static mbedtls_x509_crt *scr_tls_system_ca(void) {
|
|
353
|
+
if (!scr_tls_system_roots_loaded) {
|
|
354
|
+
scr_tls_system_roots_loaded = true;
|
|
355
|
+
mbedtls_x509_crt_init(&scr_tls_system_roots);
|
|
356
|
+
static const char *const bundles[] = {
|
|
357
|
+
"/etc/ssl/cert.pem", /* macOS, Alpine */
|
|
358
|
+
"/etc/ssl/certs/ca-certificates.crt", /* Debian/Ubuntu */
|
|
359
|
+
"/etc/pki/tls/certs/ca-bundle.crt", /* Fedora/RHEL */
|
|
360
|
+
"/etc/ssl/ca-bundle.pem", /* openSUSE */
|
|
361
|
+
};
|
|
362
|
+
for (size_t i = 0; i < sizeof bundles / sizeof bundles[0]; i++) {
|
|
363
|
+
if (mbedtls_x509_crt_parse_file(&scr_tls_system_roots, bundles[i]) == 0) break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return &scr_tls_system_roots;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/* `verify_optional` is tls.connect's rejectUnauthorized:false: the
|
|
370
|
+
* handshake proceeds regardless of the verdict but verification still
|
|
371
|
+
* RUNS and records (socket.authorized / authorizationError answer
|
|
372
|
+
* Node's split); the https client's historical false keeps VERIFY_NONE
|
|
373
|
+
* (verify_optional false there — nothing recorded, nothing read). */
|
|
374
|
+
static ScrTlsCli *scr_tls_cli_new2(ScrStr *host, bool reject_unauthorized, const char *ca,
|
|
375
|
+
size_t ca_len, bool verify_optional) {
|
|
376
|
+
scr_tls_rng_init();
|
|
377
|
+
ScrTlsCli *c = calloc(1, sizeof *c);
|
|
378
|
+
if (!c) scr_tls_oom();
|
|
379
|
+
mbedtls_ssl_config_init(&c->conf);
|
|
380
|
+
mbedtls_x509_crt_init(&c->ca);
|
|
381
|
+
if (mbedtls_ssl_config_defaults(&c->conf, MBEDTLS_SSL_IS_CLIENT,
|
|
382
|
+
MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
383
|
+
MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
|
|
384
|
+
scr_tls_oom();
|
|
385
|
+
}
|
|
386
|
+
mbedtls_ssl_conf_rng(&c->conf, mbedtls_ctr_drbg_random, &scr_tls_drbg);
|
|
387
|
+
c->verify_recorded = reject_unauthorized || verify_optional;
|
|
388
|
+
if (ca_len > 0) {
|
|
389
|
+
int r = scr_tls_parse_crt(&c->ca, ca, ca_len);
|
|
390
|
+
if (r != 0) scr_tls_bad_material("ca certificate", r);
|
|
391
|
+
}
|
|
392
|
+
if (!reject_unauthorized && !verify_optional) {
|
|
393
|
+
mbedtls_ssl_conf_authmode(&c->conf, MBEDTLS_SSL_VERIFY_NONE);
|
|
394
|
+
} else {
|
|
395
|
+
mbedtls_ssl_conf_authmode(&c->conf, reject_unauthorized ? MBEDTLS_SSL_VERIFY_REQUIRED
|
|
396
|
+
: MBEDTLS_SSL_VERIFY_OPTIONAL);
|
|
397
|
+
mbedtls_ssl_conf_ca_chain(&c->conf, ca_len > 0 ? &c->ca : scr_tls_system_ca(), NULL);
|
|
398
|
+
}
|
|
399
|
+
/* SNI + name verification want the DNS name; an IP literal gets
|
|
400
|
+
* neither (mbedTLS 3.6 has no IP-SAN matching — SEMANTICS.md). */
|
|
401
|
+
struct in_addr a4;
|
|
402
|
+
struct in6_addr a6;
|
|
403
|
+
const char *h = host->len > 0 ? host->data : "localhost";
|
|
404
|
+
if (inet_pton(AF_INET, h, &a4) != 1 && inet_pton(AF_INET6, h, &a6) != 1) {
|
|
405
|
+
c->hostname = strdup(h);
|
|
406
|
+
if (!c->hostname) scr_tls_oom();
|
|
407
|
+
}
|
|
408
|
+
return c;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
static ScrTlsCli *scr_tls_cli_new(ScrStr *host, bool reject_unauthorized, const char *ca,
|
|
412
|
+
size_t ca_len) {
|
|
413
|
+
return scr_tls_cli_new2(host, reject_unauthorized, ca, ca_len, false);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
static void scr_tls_cli_free(ScrTlsCli *c) {
|
|
417
|
+
mbedtls_ssl_config_free(&c->conf);
|
|
418
|
+
mbedtls_x509_crt_free(&c->ca);
|
|
419
|
+
free(c->hostname);
|
|
420
|
+
free(c);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* ── the per-socket transport ────────────────────────────────────────── */
|
|
424
|
+
|
|
425
|
+
/* The SNI pre-parse states: NONE (no callback on the server — the
|
|
426
|
+
* handshake runs directly), COLLECT (buffering the ClientHello),
|
|
427
|
+
* WAIT (the JS callback is deciding), DONE (answered/skipped — the
|
|
428
|
+
* hello replayed into the bio; the handshake runs). */
|
|
429
|
+
enum { SCR_SNI_NONE = 0, SCR_SNI_COLLECT, SCR_SNI_WAIT, SCR_SNI_DONE };
|
|
430
|
+
|
|
431
|
+
typedef struct ScrTlsSock {
|
|
432
|
+
mbedtls_ssl_context ssl;
|
|
433
|
+
ScrNetSocket *sock; /* BORROWED: the tctx lives and dies with the socket */
|
|
434
|
+
ScrTlsSrv *srv; /* +1 server side, NULL client side */
|
|
435
|
+
ScrTlsCli *cli; /* owned client side, NULL server side */
|
|
436
|
+
bool notify_sent;
|
|
437
|
+
bool established; /* the handshake stood — socket.authorized reads gate on it */
|
|
438
|
+
/* the client 'session' event: listeners + the fired-once latch (Node
|
|
439
|
+
* fires per received ticket; this surface fires once per socket) */
|
|
440
|
+
ScrNetLs session_ls;
|
|
441
|
+
bool session_fired;
|
|
442
|
+
/* what the verify callback saw (the peer chain is gone once the
|
|
443
|
+
* handshake has failed, so the shape is recorded as it verifies) */
|
|
444
|
+
bool vrfy_leaf_self;
|
|
445
|
+
bool vrfy_chain_self;
|
|
446
|
+
bool vrfy_expired;
|
|
447
|
+
bool vrfy_cn_mismatch;
|
|
448
|
+
/* SNI pre-parse state (server side with an SNI callback only) */
|
|
449
|
+
int sni_state;
|
|
450
|
+
bool in_handshake; /* a synchronous cb answer must not re-pump */
|
|
451
|
+
unsigned char *hello; /* the raw ClientHello flight, replayed at DONE */
|
|
452
|
+
size_t hello_len, hello_cap;
|
|
453
|
+
ScrSecureCtx *sni_ctx; /* owned; the answer (NULL = default cert) */
|
|
454
|
+
} ScrTlsSock;
|
|
455
|
+
|
|
456
|
+
/* The client verify callback: records WHERE verification failed and how
|
|
457
|
+
* the presented chain is shaped, per certificate (depth 0 = leaf), so
|
|
458
|
+
* the failure message can be Node's exact split. Never overrides flags —
|
|
459
|
+
* the verdict stays mbedTLS's. */
|
|
460
|
+
static int scr_tls_vrfy_cb(void *ctx, mbedtls_x509_crt *crt, int depth, uint32_t *flags) {
|
|
461
|
+
ScrTlsSock *t = (ScrTlsSock *)ctx;
|
|
462
|
+
bool self = crt->issuer_raw.len == crt->subject_raw.len &&
|
|
463
|
+
memcmp(crt->issuer_raw.p, crt->subject_raw.p, crt->subject_raw.len) == 0;
|
|
464
|
+
if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) t->vrfy_expired = true;
|
|
465
|
+
if (*flags & MBEDTLS_X509_BADCERT_CN_MISMATCH) t->vrfy_cn_mismatch = true;
|
|
466
|
+
if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
|
|
467
|
+
if (depth == 0 && self) t->vrfy_leaf_self = true;
|
|
468
|
+
if (depth > 0 && self) t->vrfy_chain_self = true;
|
|
469
|
+
}
|
|
470
|
+
return 0;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/* The BIOs: nonblocking fd I/O in mbedTLS's own convention. A send that
|
|
474
|
+
* would block arms the socket's write filter so the handshake resumes on
|
|
475
|
+
* writability. */
|
|
476
|
+
static int scr_tls_bio_send(void *ctx, const unsigned char *buf, size_t len) {
|
|
477
|
+
ScrTlsSock *t = (ScrTlsSock *)ctx;
|
|
478
|
+
int fd = scr_net_sock_fd(t->sock);
|
|
479
|
+
if (fd < 0) return MBEDTLS_ERR_NET_CONN_RESET;
|
|
480
|
+
ssize_t n = write(fd, buf, len);
|
|
481
|
+
if (n >= 0) return (int)n;
|
|
482
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
|
|
483
|
+
scr_net_sock_transport_want_write(t->sock);
|
|
484
|
+
return MBEDTLS_ERR_SSL_WANT_WRITE;
|
|
485
|
+
}
|
|
486
|
+
if (errno == EPIPE || errno == ECONNRESET) return MBEDTLS_ERR_NET_CONN_RESET;
|
|
487
|
+
return MBEDTLS_ERR_NET_SEND_FAILED;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
static int scr_tls_bio_recv(void *ctx, unsigned char *buf, size_t len) {
|
|
491
|
+
ScrTlsSock *t = (ScrTlsSock *)ctx;
|
|
492
|
+
int fd = scr_net_sock_fd(t->sock);
|
|
493
|
+
if (fd < 0) return MBEDTLS_ERR_NET_CONN_RESET;
|
|
494
|
+
/* bytes a consumer peeked and unshifted (the demux path) re-enter the
|
|
495
|
+
* stream here, ahead of the kernel's */
|
|
496
|
+
size_t took = scr_net_sock_take_buffered(t->sock, (char *)buf, len);
|
|
497
|
+
if (took > 0) return (int)took;
|
|
498
|
+
ssize_t n = read(fd, buf, len);
|
|
499
|
+
if (n >= 0) return (int)n; /* 0 = EOF; mbedTLS maps it to CONN_EOF */
|
|
500
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
|
|
501
|
+
return MBEDTLS_ERR_SSL_WANT_READ;
|
|
502
|
+
}
|
|
503
|
+
if (errno == ECONNRESET) return MBEDTLS_ERR_NET_CONN_RESET;
|
|
504
|
+
return MBEDTLS_ERR_NET_RECV_FAILED;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/* ── the transport ops ───────────────────────────────────────────────── */
|
|
508
|
+
|
|
509
|
+
static void scr_tls_fire_session(ScrTlsSock *t);
|
|
510
|
+
|
|
511
|
+
static ssize_t scr_tls_xread(void *tctx, char *buf, size_t n) {
|
|
512
|
+
ScrTlsSock *t = (ScrTlsSock *)tctx;
|
|
513
|
+
for (;;) {
|
|
514
|
+
int r = mbedtls_ssl_read(&t->ssl, (unsigned char *)buf, n);
|
|
515
|
+
if (r > 0) return r;
|
|
516
|
+
if (r == 0 || r == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY || r == MBEDTLS_ERR_SSL_CONN_EOF) {
|
|
517
|
+
return 0; /* orderly (or abrupt-but-quiet) EOF — Node's 'end' either way */
|
|
518
|
+
}
|
|
519
|
+
#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
|
|
520
|
+
if (r == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) {
|
|
521
|
+
/* a 1.3 ticket, not app data: the client 'session' event's moment */
|
|
522
|
+
if (!scr_exc_pending()) scr_tls_fire_session(t);
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
#endif
|
|
526
|
+
if (r == MBEDTLS_ERR_SSL_WANT_READ || r == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
|
527
|
+
errno = EAGAIN;
|
|
528
|
+
return -1;
|
|
529
|
+
}
|
|
530
|
+
errno = ECONNRESET; /* protocol-level death reads as a reset */
|
|
531
|
+
return -1;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
static ssize_t scr_tls_xwrite(void *tctx, const char *buf, size_t n) {
|
|
536
|
+
ScrTlsSock *t = (ScrTlsSock *)tctx;
|
|
537
|
+
int r = mbedtls_ssl_write(&t->ssl, (const unsigned char *)buf, n);
|
|
538
|
+
if (r >= 0) return r;
|
|
539
|
+
if (r == MBEDTLS_ERR_SSL_WANT_READ || r == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
|
540
|
+
errno = EAGAIN;
|
|
541
|
+
return -1;
|
|
542
|
+
}
|
|
543
|
+
errno = EPIPE;
|
|
544
|
+
return -1;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* Node's message for the verify failures the local-CA flows can meet.
|
|
548
|
+
* The split mirrors Node's openssl-error mapping: a self-signed LEAF is
|
|
549
|
+
* "self-signed certificate"; a chain that carries its own (self-signed)
|
|
550
|
+
* root is "self-signed certificate in certificate chain"; a chain whose
|
|
551
|
+
* issuer never arrived is "unable to verify the first certificate". The
|
|
552
|
+
* shape comes from the verify callback's records — the peer chain is
|
|
553
|
+
* already gone once the handshake has failed. */
|
|
554
|
+
static void scr_tls_verify_msg(ScrTlsSock *t, char *out, size_t n) {
|
|
555
|
+
if (t->vrfy_expired) {
|
|
556
|
+
snprintf(out, n, "certificate has expired");
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
/* Node appends the --use-system-ca hint for exactly these two codes
|
|
560
|
+
* (src/crypto/crypto_common.cc: DEPTH_ZERO_SELF_SIGNED_CERT and
|
|
561
|
+
* UNABLE_TO_VERIFY_LEAF_SIGNATURE) — and NOT for the in-chain one. */
|
|
562
|
+
static const char hint[] =
|
|
563
|
+
"; if the root CA is installed locally, try running Node.js with --use-system-ca";
|
|
564
|
+
if (t->vrfy_leaf_self) {
|
|
565
|
+
snprintf(out, n, "self-signed certificate%s", hint);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (t->vrfy_chain_self) {
|
|
569
|
+
snprintf(out, n, "self-signed certificate in certificate chain");
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
/* the identity check only decides on a TRUSTED chain — Node's order
|
|
573
|
+
* (checkServerIdentity runs after verification succeeds) */
|
|
574
|
+
if (t->vrfy_cn_mismatch &&
|
|
575
|
+
(mbedtls_ssl_get_verify_result(&t->ssl) & MBEDTLS_X509_BADCERT_NOT_TRUSTED) == 0) {
|
|
576
|
+
snprintf(out, n, "Hostname/IP does not match certificate's altnames");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
snprintf(out, n, "unable to verify the first certificate%s", hint);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* ── the SNI pre-parse ────────────────────────────────────────────────
|
|
583
|
+
* mbedTLS's f_sni must answer synchronously from inside the handshake,
|
|
584
|
+
* but the JS SNICallback is asynchronous (per-hostname certs generate on
|
|
585
|
+
* demand — the portless flow). So a callback-carrying server resolves
|
|
586
|
+
* the name FIRST: it buffers the raw ClientHello flight off the socket,
|
|
587
|
+
* parses the server_name extension itself, parks the handshake while the
|
|
588
|
+
* JS callback decides, then replays the buffered bytes into the bio and
|
|
589
|
+
* runs the real handshake — whose f_sni serves the answered context. */
|
|
590
|
+
|
|
591
|
+
/* The socket whose handshake this unit is driving — f_sni's route back
|
|
592
|
+
* to the answered context (single-threaded runtime; set around
|
|
593
|
+
* mbedtls_ssl_handshake). */
|
|
594
|
+
static ScrTlsSock *scr_tls_current = NULL;
|
|
595
|
+
|
|
596
|
+
/* Defined with the other transport ops below; the answer path needs the
|
|
597
|
+
* table's identity to re-find its engine from the boxed socket. */
|
|
598
|
+
static const ScrNetTransportOps SCR_TLS_OPS;
|
|
599
|
+
|
|
600
|
+
static int scr_tls_f_sni(void *p, mbedtls_ssl_context *ssl, const unsigned char *name,
|
|
601
|
+
size_t len) {
|
|
602
|
+
(void)p; (void)name; (void)len;
|
|
603
|
+
ScrTlsSock *t = scr_tls_current;
|
|
604
|
+
if (t != NULL && t->sni_ctx != NULL) {
|
|
605
|
+
return mbedtls_ssl_set_hs_own_cert(ssl, &t->sni_ctx->cert, &t->sni_ctx->pk);
|
|
606
|
+
}
|
|
607
|
+
return 0; /* default cert (no answer / cb passed no context) */
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/* The ClientHello body walk (past the 4-byte handshake header): version +
|
|
611
|
+
* random, session id, cipher suites, compression, then the extension list
|
|
612
|
+
* — server_name (type 0) holds one host_name (type 0) entry. Answers 1
|
|
613
|
+
* with `out` set, 2 for a well-formed hello without SNI or anything
|
|
614
|
+
* malformed (the real handshake will judge it). */
|
|
615
|
+
static int scr_tls_parse_ch_body(const unsigned char *b, size_t n, char *out, size_t out_cap) {
|
|
616
|
+
size_t i = 34; /* version(2) + random(32) */
|
|
617
|
+
if (i + 1 > n) return 2;
|
|
618
|
+
i += 1 + b[i]; /* session id: length byte + body */
|
|
619
|
+
if (i + 2 > n) return 2;
|
|
620
|
+
size_t cs = ((size_t)b[i] << 8) | b[i + 1];
|
|
621
|
+
i += 2 + cs;
|
|
622
|
+
if (i + 1 > n) return 2;
|
|
623
|
+
i += 1 + b[i];
|
|
624
|
+
if (i + 2 > n) return 2;
|
|
625
|
+
size_t ext_total = ((size_t)b[i] << 8) | b[i + 1];
|
|
626
|
+
i += 2;
|
|
627
|
+
size_t end = i + ext_total <= n ? i + ext_total : n;
|
|
628
|
+
while (i + 4 <= end) {
|
|
629
|
+
size_t et = ((size_t)b[i] << 8) | b[i + 1];
|
|
630
|
+
size_t el = ((size_t)b[i + 2] << 8) | b[i + 3];
|
|
631
|
+
i += 4;
|
|
632
|
+
if (i + el > end) return 2;
|
|
633
|
+
if (et == 0) {
|
|
634
|
+
/* server_name: list length(2), then entry type(1) + length(2) + name */
|
|
635
|
+
if (el < 5 || b[i + 2] != 0) return 2;
|
|
636
|
+
size_t nl = ((size_t)b[i + 3] << 8) | b[i + 4];
|
|
637
|
+
if (nl == 0 || nl + 5 > el || nl + 1 > out_cap) return 2;
|
|
638
|
+
memcpy(out, b + i + 5, nl);
|
|
639
|
+
out[nl] = 0;
|
|
640
|
+
return 1;
|
|
641
|
+
}
|
|
642
|
+
i += el;
|
|
643
|
+
}
|
|
644
|
+
return 2;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/* Reassemble the handshake stream from the raw record flight and look for
|
|
648
|
+
* the server_name extension. 0 = need more bytes; 1 = found (out set);
|
|
649
|
+
* 2 = no SNI / not parseable (serve the default cert). */
|
|
650
|
+
static int scr_tls_parse_sni(const unsigned char *buf, size_t len, char *out, size_t out_cap) {
|
|
651
|
+
unsigned char *hs = NULL;
|
|
652
|
+
size_t hs_len = 0, hs_cap = 0;
|
|
653
|
+
size_t off = 0;
|
|
654
|
+
for (;;) {
|
|
655
|
+
if (off + 5 > len) { free(hs); return 0; }
|
|
656
|
+
if (buf[off] != 0x16) { free(hs); return 2; } /* not a handshake record */
|
|
657
|
+
size_t rlen = ((size_t)buf[off + 3] << 8) | buf[off + 4];
|
|
658
|
+
if (rlen == 0 || rlen > 16640) { free(hs); return 2; }
|
|
659
|
+
if (off + 5 + rlen > len) { free(hs); return 0; }
|
|
660
|
+
if (hs_len + rlen > hs_cap) {
|
|
661
|
+
hs_cap = hs_cap ? hs_cap * 2 : 4096;
|
|
662
|
+
while (hs_cap < hs_len + rlen) hs_cap *= 2;
|
|
663
|
+
hs = realloc(hs, hs_cap);
|
|
664
|
+
if (!hs) scr_tls_oom();
|
|
665
|
+
}
|
|
666
|
+
memcpy(hs + hs_len, buf + off + 5, rlen);
|
|
667
|
+
hs_len += rlen;
|
|
668
|
+
off += 5 + rlen;
|
|
669
|
+
if (hs_len >= 4) {
|
|
670
|
+
if (hs[0] != 0x01) { free(hs); return 2; } /* not a ClientHello */
|
|
671
|
+
size_t chlen = ((size_t)hs[1] << 16) | ((size_t)hs[2] << 8) | hs[3];
|
|
672
|
+
if (chlen > 131072) { free(hs); return 2; }
|
|
673
|
+
if (hs_len >= 4 + chlen) {
|
|
674
|
+
int r = scr_tls_parse_ch_body(hs + 4, chlen, out, out_cap);
|
|
675
|
+
free(hs);
|
|
676
|
+
return r;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/* Hand the buffered flight back: the bio reads it ahead of the kernel. */
|
|
683
|
+
static void scr_tls_sni_replay(ScrTlsSock *t) {
|
|
684
|
+
if (t->hello_len > 0) {
|
|
685
|
+
scr_net_sock_replay(t->sock, (const char *)t->hello, t->hello_len);
|
|
686
|
+
}
|
|
687
|
+
free(t->hello);
|
|
688
|
+
t->hello = NULL;
|
|
689
|
+
t->hello_len = t->hello_cap = 0;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/* The COLLECT step: buffer what the socket has, decide, invoke the JS
|
|
693
|
+
* callback. Answers like the transport handshake contract: 1 = proceed
|
|
694
|
+
* to the real handshake, 0 = parked (more bytes, or the callback is
|
|
695
|
+
* deciding), -1 = the socket died (already torn down). */
|
|
696
|
+
static int scr_tls_sni_collect(ScrTlsSock *t) {
|
|
697
|
+
char servername[256];
|
|
698
|
+
for (;;) {
|
|
699
|
+
if (t->hello_len > 0) {
|
|
700
|
+
int r = scr_tls_parse_sni(t->hello, t->hello_len, servername, sizeof servername);
|
|
701
|
+
if (r == 2 || t->hello_len > 65536) {
|
|
702
|
+
/* no SNI (or unparseable): skip the callback, serve the default
|
|
703
|
+
* pair — Node's SNICallback never fires without a server_name */
|
|
704
|
+
t->sni_state = SCR_SNI_DONE;
|
|
705
|
+
scr_tls_sni_replay(t);
|
|
706
|
+
return 1;
|
|
707
|
+
}
|
|
708
|
+
if (r == 1) {
|
|
709
|
+
t->sni_state = SCR_SNI_WAIT;
|
|
710
|
+
ScrClosure *cb = scr_closure_retain(t->srv->sni_cb);
|
|
711
|
+
ScrClosure *ans = scr_closure_new(t->srv->sni_answer_fn, 1);
|
|
712
|
+
ScrBox *box = scr_box_new_obj(scr_net_sock_retain_v, scr_net_sock_release_v, NULL);
|
|
713
|
+
scr_box_set_ref(box, scr_net_sock_retain(t->sock));
|
|
714
|
+
ans->caps[0] = box;
|
|
715
|
+
ScrStr *name = scr_str_new(servername, strlen(servername));
|
|
716
|
+
/* the callback owns its +1 params per the universal convention */
|
|
717
|
+
((void (*)(ScrClosure *, ScrStr *, ScrClosure *))cb->fn)(cb, name, ans);
|
|
718
|
+
scr_closure_release(cb);
|
|
719
|
+
if (scr_net_sock_fd(t->sock) < 0) return -1; /* a sync cb(err) tore it down */
|
|
720
|
+
if (t->sni_state == SCR_SNI_WAIT) return 0; /* asynchronous answer */
|
|
721
|
+
return 1; /* a synchronous answer already replayed the flight */
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
/* need more bytes: buffered (unshifted demux peeks) first, then the fd */
|
|
725
|
+
if (t->hello_len + 4096 > t->hello_cap) {
|
|
726
|
+
t->hello_cap = t->hello_cap ? t->hello_cap * 2 : 8192;
|
|
727
|
+
t->hello = realloc(t->hello, t->hello_cap);
|
|
728
|
+
if (!t->hello) scr_tls_oom();
|
|
729
|
+
}
|
|
730
|
+
size_t room = t->hello_cap - t->hello_len;
|
|
731
|
+
size_t took = scr_net_sock_take_buffered(t->sock, (char *)t->hello + t->hello_len, room);
|
|
732
|
+
if (took == 0) {
|
|
733
|
+
int fd = scr_net_sock_fd(t->sock);
|
|
734
|
+
if (fd < 0) return -1;
|
|
735
|
+
ssize_t n = read(fd, t->hello + t->hello_len, room);
|
|
736
|
+
if (n < 0) {
|
|
737
|
+
if (errno == EINTR) continue;
|
|
738
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; /* readiness re-drives */
|
|
739
|
+
scr_net_sock_transport_error(t->sock, NULL);
|
|
740
|
+
return -1;
|
|
741
|
+
}
|
|
742
|
+
if (n == 0) { /* the peer died mid-hello: silent server-side teardown */
|
|
743
|
+
scr_net_sock_transport_error(t->sock, NULL);
|
|
744
|
+
return -1;
|
|
745
|
+
}
|
|
746
|
+
took = (size_t)n;
|
|
747
|
+
}
|
|
748
|
+
t->hello_len += took;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/* The answer closure's runtime half (its fn is the emitted per-shape
|
|
753
|
+
* thunk; caps[0] boxes the socket). Late, repeated, or dead-socket
|
|
754
|
+
* answers are ignored — the first answer decides, like Node. */
|
|
755
|
+
void scr_tls_sni_answer(ScrClosure *self, bool has_err, ScrSecureCtx *ctx /*moves, nullable*/) {
|
|
756
|
+
ScrNetSocket *sock = (ScrNetSocket *)scr_box_get_ref(self->caps[0]);
|
|
757
|
+
ScrTlsSock *t =
|
|
758
|
+
sock != NULL ? (ScrTlsSock *)scr_net_sock_transport_ctx_for(sock, &SCR_TLS_OPS) : NULL;
|
|
759
|
+
if (t == NULL || t->sni_state != SCR_SNI_WAIT) {
|
|
760
|
+
scr_secure_ctx_release(ctx);
|
|
761
|
+
scr_net_sock_release(sock);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
t->sni_state = SCR_SNI_DONE;
|
|
765
|
+
if (has_err) {
|
|
766
|
+
/* Node destroys the socket ('tlsClientError'; silent with no
|
|
767
|
+
* listener — what portless observes). */
|
|
768
|
+
scr_secure_ctx_release(ctx);
|
|
769
|
+
free(t->hello);
|
|
770
|
+
t->hello = NULL;
|
|
771
|
+
t->hello_len = t->hello_cap = 0;
|
|
772
|
+
scr_net_sock_transport_error(t->sock, NULL);
|
|
773
|
+
scr_net_sock_release(sock);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
t->sni_ctx = ctx; /* moves; NULL = the default pair */
|
|
777
|
+
scr_tls_sni_replay(t);
|
|
778
|
+
/* An asynchronous answer must re-drive the parked handshake itself —
|
|
779
|
+
* the client is waiting for the ServerHello, so no readiness comes. A
|
|
780
|
+
* SYNCHRONOUS answer (cache hit) returns into the running collect
|
|
781
|
+
* step, which proceeds directly. */
|
|
782
|
+
if (!t->in_handshake) scr_net_sock_transport_resume(sock);
|
|
783
|
+
scr_net_sock_release(sock);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
static int scr_tls_handshake(void *tctx) {
|
|
787
|
+
ScrTlsSock *t = (ScrTlsSock *)tctx;
|
|
788
|
+
if (t->sni_state == SCR_SNI_COLLECT) {
|
|
789
|
+
t->in_handshake = true;
|
|
790
|
+
int c = scr_tls_sni_collect(t);
|
|
791
|
+
t->in_handshake = false;
|
|
792
|
+
if (c <= 0) return c;
|
|
793
|
+
}
|
|
794
|
+
if (t->sni_state == SCR_SNI_WAIT) return 0; /* the callback is deciding */
|
|
795
|
+
scr_tls_current = t;
|
|
796
|
+
int r = mbedtls_ssl_handshake(&t->ssl);
|
|
797
|
+
scr_tls_current = NULL;
|
|
798
|
+
if (r == 0) return 1;
|
|
799
|
+
if (r == MBEDTLS_ERR_SSL_WANT_READ || r == MBEDTLS_ERR_SSL_WANT_WRITE) return 0;
|
|
800
|
+
if (t->srv != NULL) {
|
|
801
|
+
/* Node's server default: 'tlsClientError' with no listener destroys
|
|
802
|
+
* the socket silently — exactly what portless (no listener) sees. */
|
|
803
|
+
scr_net_sock_transport_error(t->sock, NULL);
|
|
804
|
+
return -1;
|
|
805
|
+
}
|
|
806
|
+
char msg[160];
|
|
807
|
+
if (r == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
|
|
808
|
+
scr_tls_verify_msg(t, msg, sizeof msg);
|
|
809
|
+
} else if (r == MBEDTLS_ERR_SSL_CONN_EOF || r == MBEDTLS_ERR_NET_CONN_RESET) {
|
|
810
|
+
/* the peer died mid-handshake: the http layer's own premature-close
|
|
811
|
+
* shape ('socket hang up') is Node's message — teardown silently at
|
|
812
|
+
* this level and let the closed hook classify it */
|
|
813
|
+
scr_net_sock_transport_error(t->sock, NULL);
|
|
814
|
+
return -1;
|
|
815
|
+
} else {
|
|
816
|
+
/* non-verify handshake failures: honest but not Node's exact openssl
|
|
817
|
+
* string (SEMANTICS.md divergence 55) */
|
|
818
|
+
snprintf(msg, sizeof msg, "client TLS handshake failed");
|
|
819
|
+
}
|
|
820
|
+
scr_net_sock_transport_error(t->sock, msg);
|
|
821
|
+
return -1;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
static void scr_tls_on_established(void *tctx) {
|
|
825
|
+
ScrTlsSock *t = (ScrTlsSock *)tctx;
|
|
826
|
+
t->established = true;
|
|
827
|
+
if (t->cli != NULL) scr_tls_fire_session(t); /* the 1.2 arm; 1.3 fires on ticket receipt */
|
|
828
|
+
if (t->srv != NULL && t->srv->h2_conn != NULL) {
|
|
829
|
+
/* the h2 secure server: dispatch on the NEGOTIATED protocol — the
|
|
830
|
+
* h2 session attaches as the socket's native reader (it reads
|
|
831
|
+
* plaintext through this transport, exactly like the https parser);
|
|
832
|
+
* an http/1.1 answer routes to the wrapped parser when one exists;
|
|
833
|
+
* a client that answered nothing tears down (Node parks it as
|
|
834
|
+
* 'unknownProtocol' and destroys at the timeout — this surface
|
|
835
|
+
* destroys now; SEMANTICS.md). */
|
|
836
|
+
const char *alpn = mbedtls_ssl_get_alpn_protocol(&t->ssl);
|
|
837
|
+
if (alpn != NULL && strcmp(alpn, "h2") == 0) {
|
|
838
|
+
t->srv->h2_conn(t->srv->h2_ctx, t->sock);
|
|
839
|
+
} else if (alpn != NULL && t->srv->inner_conn != NULL && strcmp(alpn, "http/1.1") == 0) {
|
|
840
|
+
t->srv->inner_conn(t->srv->inner_ctx, t->sock);
|
|
841
|
+
} else {
|
|
842
|
+
scr_net_sock_transport_error(t->sock, NULL);
|
|
843
|
+
}
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (t->srv != NULL && t->srv->inner_conn != NULL) {
|
|
847
|
+
/* https: the http parser attaches now, over plaintext only */
|
|
848
|
+
t->srv->inner_conn(t->srv->inner_ctx, t->sock);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
static bool scr_tls_pending(void *tctx) {
|
|
853
|
+
ScrTlsSock *t = (ScrTlsSock *)tctx;
|
|
854
|
+
return mbedtls_ssl_check_pending(&t->ssl) != 0 ||
|
|
855
|
+
mbedtls_ssl_get_bytes_avail(&t->ssl) > 0;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
static void scr_tls_shutdown_write(void *tctx) {
|
|
859
|
+
ScrTlsSock *t = (ScrTlsSock *)tctx;
|
|
860
|
+
if (t->notify_sent) return;
|
|
861
|
+
t->notify_sent = true;
|
|
862
|
+
(void)mbedtls_ssl_close_notify(&t->ssl); /* best effort; EAGAIN drops it */
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
static void scr_tls_free(void *tctx) {
|
|
866
|
+
ScrTlsSock *t = (ScrTlsSock *)tctx;
|
|
867
|
+
mbedtls_ssl_free(&t->ssl);
|
|
868
|
+
if (t->srv) scr_tls_srv_release(t->srv);
|
|
869
|
+
if (t->cli) scr_tls_cli_free(t->cli);
|
|
870
|
+
scr_net_ls_drop(&t->session_ls);
|
|
871
|
+
free(t->hello);
|
|
872
|
+
scr_secure_ctx_release(t->sni_ctx);
|
|
873
|
+
free(t);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
static const ScrNetTransportOps SCR_TLS_OPS = {
|
|
877
|
+
.xread = scr_tls_xread,
|
|
878
|
+
.xwrite = scr_tls_xwrite,
|
|
879
|
+
.handshake = scr_tls_handshake,
|
|
880
|
+
.on_established = scr_tls_on_established,
|
|
881
|
+
.pending = scr_tls_pending,
|
|
882
|
+
.shutdown_write = scr_tls_shutdown_write,
|
|
883
|
+
.free = scr_tls_free,
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
/* ── the server surface ──────────────────────────────────────────────── */
|
|
887
|
+
|
|
888
|
+
/* The server's native-connection hook: one TLS engine per accepted
|
|
889
|
+
* socket; the deferred 'connection' fires from the net pump once the
|
|
890
|
+
* handshake stands. */
|
|
891
|
+
static void scr_tls_srv_on_conn(void *ctx, ScrNetSocket *sock) {
|
|
892
|
+
ScrTlsSrv *srv = (ScrTlsSrv *)ctx;
|
|
893
|
+
ScrTlsSock *t = calloc(1, sizeof *t);
|
|
894
|
+
if (!t) scr_tls_oom();
|
|
895
|
+
mbedtls_ssl_init(&t->ssl);
|
|
896
|
+
if (mbedtls_ssl_setup(&t->ssl, &srv->conf) != 0) scr_tls_oom();
|
|
897
|
+
t->sock = sock;
|
|
898
|
+
t->srv = scr_tls_srv_retain(srv);
|
|
899
|
+
if (srv->sni_cb != NULL) t->sni_state = SCR_SNI_COLLECT; /* pre-parse the hello */
|
|
900
|
+
mbedtls_ssl_set_bio(&t->ssl, t, scr_tls_bio_send, scr_tls_bio_recv, NULL);
|
|
901
|
+
scr_net_sock_set_transport(sock, &SCR_TLS_OPS, t);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
ScrNetServer *scr_tls_create_server(const char *cert, size_t cert_len, const char *key,
|
|
905
|
+
size_t key_len, ScrClosure *handler /*moves, nullable*/,
|
|
906
|
+
ScrNetConnFn fn) {
|
|
907
|
+
ScrTlsSrv *srv = scr_tls_srv_new(cert, cert_len, key, key_len, false);
|
|
908
|
+
ScrNetServer *s = scr_net_create_server(handler, fn);
|
|
909
|
+
scr_net_server_set_native_conn(s, &scr_tls_srv_on_conn, srv, &scr_tls_srv_release_v);
|
|
910
|
+
scr_net_server_defer_connections(s); /* the handler is 'secureConnection' */
|
|
911
|
+
return s;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
ScrNetServer *scr_https_create_server(const char *cert, size_t cert_len, const char *key,
|
|
915
|
+
size_t key_len, ScrClosure *handler /*moves*/,
|
|
916
|
+
ScrHttpReqFn fn) {
|
|
917
|
+
ScrNetServer *s = scr_http_create_server(handler, fn);
|
|
918
|
+
ScrTlsSrv *srv = scr_tls_srv_new(cert, cert_len, key, key_len, true /* ALPN http/1.1 */);
|
|
919
|
+
/* wrap the parser hook: TLS first, parser at establishment */
|
|
920
|
+
scr_net_server_get_native_conn(s, &srv->inner_conn, &srv->inner_ctx, &srv->inner_ctx_free);
|
|
921
|
+
scr_net_server_set_native_conn(s, &scr_tls_srv_on_conn, srv, &scr_tls_srv_release_v);
|
|
922
|
+
scr_net_server_defer_connections(s);
|
|
923
|
+
return s;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/* The SNI-callback form of the allowHTTP1 compatibility server: the same
|
|
927
|
+
* handler-less https server, plus the pre-parse machinery (design note
|
|
928
|
+
* atop the SNI section). A NULL sni_cb — the conditional spread's
|
|
929
|
+
* undefined arm — is exactly scr_https_create_server(…, NULL, NULL). */
|
|
930
|
+
ScrNetServer *scr_https_create_server_sni(const char *cert, size_t cert_len, const char *key,
|
|
931
|
+
size_t key_len, ScrClosure *sni_cb /*moves, nullable*/,
|
|
932
|
+
void *answer_fn) {
|
|
933
|
+
ScrNetServer *s = scr_http_create_server(NULL, NULL);
|
|
934
|
+
ScrTlsSrv *srv = scr_tls_srv_new(cert, cert_len, key, key_len, true /* ALPN http/1.1 */);
|
|
935
|
+
if (sni_cb != NULL) {
|
|
936
|
+
srv->sni_cb = sni_cb; /* moves */
|
|
937
|
+
srv->sni_answer_fn = answer_fn;
|
|
938
|
+
mbedtls_ssl_conf_sni(&srv->conf, &scr_tls_f_sni, NULL);
|
|
939
|
+
}
|
|
940
|
+
scr_net_server_get_native_conn(s, &srv->inner_conn, &srv->inner_ctx, &srv->inner_ctx_free);
|
|
941
|
+
scr_net_server_set_native_conn(s, &scr_tls_srv_on_conn, srv, &scr_tls_srv_release_v);
|
|
942
|
+
scr_net_server_defer_connections(s);
|
|
943
|
+
return s;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/* ── the client surface (https.request) ──────────────────────────────── */
|
|
947
|
+
|
|
948
|
+
static void scr_tls_cli_wrap(ScrNetSocket *sock, void *ctx) {
|
|
949
|
+
ScrTlsCli *cli = (ScrTlsCli *)ctx;
|
|
950
|
+
ScrTlsSock *t = calloc(1, sizeof *t);
|
|
951
|
+
if (!t) scr_tls_oom();
|
|
952
|
+
mbedtls_ssl_init(&t->ssl);
|
|
953
|
+
if (mbedtls_ssl_setup(&t->ssl, &cli->conf) != 0) scr_tls_oom();
|
|
954
|
+
if (cli->hostname != NULL) (void)mbedtls_ssl_set_hostname(&t->ssl, cli->hostname);
|
|
955
|
+
mbedtls_ssl_set_verify(&t->ssl, &scr_tls_vrfy_cb, t);
|
|
956
|
+
t->sock = sock;
|
|
957
|
+
t->cli = cli; /* the tctx owns the per-request config now */
|
|
958
|
+
mbedtls_ssl_set_bio(&t->ssl, t, scr_tls_bio_send, scr_tls_bio_recv, NULL);
|
|
959
|
+
scr_net_sock_set_transport(sock, &SCR_TLS_OPS, t);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/* The fetch unit's https leg: the same per-request client config +
|
|
963
|
+
* transport wrap https.request uses, split apart because fetch dials a
|
|
964
|
+
* RESOLVED IP while SNI and certificate verification must see the URL
|
|
965
|
+
* hostname. The ctx moves into the socket's transport at wrap time. */
|
|
966
|
+
void *scr_tls_fetch_client_ctx(ScrStr *host /*borrowed*/, bool reject_unauthorized) {
|
|
967
|
+
return scr_tls_cli_new(host, reject_unauthorized, NULL, 0);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
void scr_tls_fetch_client_wrap(ScrNetSocket *sock, void *ctx) {
|
|
971
|
+
scr_tls_cli_wrap(sock, ctx);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
ScrHttpClientReq *scr_https_request(ScrStr *host /*borrowed*/, double port,
|
|
975
|
+
ScrStr *path /*borrowed*/, ScrStr *method /*borrowed*/,
|
|
976
|
+
double timeout_ms, ScrArr *header_pairs /*borrowed*/,
|
|
977
|
+
bool auto_end, bool reject_unauthorized,
|
|
978
|
+
const char *ca /*borrowed, len 0 = none*/, size_t ca_len,
|
|
979
|
+
ScrClosure *cb /*moves, nullable*/, ScrHttpRespFn fn) {
|
|
980
|
+
ScrTlsCli *cli = scr_tls_cli_new(host, reject_unauthorized, ca, ca_len);
|
|
981
|
+
return scr_http_request_ex(host, port, path, method, timeout_ms, header_pairs, auto_end, cb,
|
|
982
|
+
fn, 443, &scr_tls_cli_wrap, cli);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/* ── RUNTIME options records (the divergence-66 stance) ────────────────
|
|
986
|
+
* A non-literal options value — the checked-dynamic JS lane's DOM record
|
|
987
|
+
* — reads its members at RUNTIME. Members the literal path lowers take
|
|
988
|
+
* the same lowering; members whose literal forms are compile fences
|
|
989
|
+
* become runtime gates that THROW the catchable fence instead of
|
|
990
|
+
* behaving differently; undocumented keys drop exactly like Node drops
|
|
991
|
+
* them (the value already evaluated, so dropping is always safe). */
|
|
992
|
+
|
|
993
|
+
static void scr_tls_opt_fence(const char *api, const char *key, const char *hint) {
|
|
994
|
+
ScrJsonBuf b;
|
|
995
|
+
scr_jb_init(&b);
|
|
996
|
+
scr_jb_puts(&b, api);
|
|
997
|
+
scr_jb_puts(&b, " option '");
|
|
998
|
+
scr_jb_puts(&b, key);
|
|
999
|
+
scr_jb_puts(&b, "' has no static lowering — ");
|
|
1000
|
+
scr_jb_puts(&b, hint);
|
|
1001
|
+
scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/* JS truthiness over the DOM kinds — Node reads requestCert /
|
|
1005
|
+
* rejectUnauthorized through `if (options.x)`. */
|
|
1006
|
+
static bool scr_tls_dyn_truthy(const ScrDyn *v) {
|
|
1007
|
+
switch (v->kind) {
|
|
1008
|
+
case SCR_DYN_UNDEF:
|
|
1009
|
+
case SCR_DYN_NULL: return false;
|
|
1010
|
+
case SCR_DYN_BOOL: return v->v.b;
|
|
1011
|
+
case SCR_DYN_NUM: return v->v.num != 0 && v->v.num == v->v.num;
|
|
1012
|
+
case SCR_DYN_STR: return v->v.str->len > 0;
|
|
1013
|
+
default: return true; /* objects, arrays, functions, handles, bytes */
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/* PEM material from a runtime option value: a string, a Buffer/Uint8Array,
|
|
1018
|
+
* or an array of those. `concat` (the client `ca` bundle) joins array
|
|
1019
|
+
* entries; cert/key arrays accept exactly one entry (one identity per
|
|
1020
|
+
* server here — multi-key servers fence). Returns +1 bytes, or NULL with
|
|
1021
|
+
* the exception pending. `what` names the fence ("a tls.createServer
|
|
1022
|
+
* 'cert' option"). */
|
|
1023
|
+
static ScrBytes *scr_tls_pem_dyn(const ScrDyn *v, const char *what, bool concat) {
|
|
1024
|
+
if (v->kind == SCR_DYN_STR) {
|
|
1025
|
+
ScrBytes *b = scr_bytes_new(SCR_BYTES_U8, (double)v->v.str->len);
|
|
1026
|
+
memcpy(b->data, v->v.str->data, v->v.str->len);
|
|
1027
|
+
return b;
|
|
1028
|
+
}
|
|
1029
|
+
if (v->kind == SCR_DYN_BYTES) return scr_dyn_bytes_copy_out(v);
|
|
1030
|
+
if (v->kind == SCR_DYN_ARR) {
|
|
1031
|
+
if (v->v.arr.len == 0) {
|
|
1032
|
+
ScrJsonBuf b;
|
|
1033
|
+
scr_jb_init(&b);
|
|
1034
|
+
scr_jb_puts(&b, what);
|
|
1035
|
+
scr_jb_puts(&b, " holding an empty array has no static lowering — pass PEM material");
|
|
1036
|
+
scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
|
|
1037
|
+
return NULL;
|
|
1038
|
+
}
|
|
1039
|
+
if (v->v.arr.len > 1 && !concat) {
|
|
1040
|
+
ScrJsonBuf b;
|
|
1041
|
+
scr_jb_init(&b);
|
|
1042
|
+
scr_jb_puts(&b, what);
|
|
1043
|
+
scr_jb_puts(&b, " with more than one array entry has no static lowering — one cert/key pair per server here");
|
|
1044
|
+
scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
|
|
1045
|
+
return NULL;
|
|
1046
|
+
}
|
|
1047
|
+
/* concatenate (mbedTLS parses concatenated PEM — the ca-bundle shape) */
|
|
1048
|
+
size_t total = 0;
|
|
1049
|
+
for (size_t i = 0; i < v->v.arr.len; i++) {
|
|
1050
|
+
const ScrDyn *e = v->v.arr.items[i];
|
|
1051
|
+
if (e->kind == SCR_DYN_STR) total += e->v.str->len + 1;
|
|
1052
|
+
else if (e->kind == SCR_DYN_BYTES) total += e->v.bytes->len + 1;
|
|
1053
|
+
else {
|
|
1054
|
+
scr_dyn_arg_type_fail(what, "of type string or an instance of Buffer or Uint8Array", e);
|
|
1055
|
+
return NULL;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
ScrBytes *b = scr_bytes_new(SCR_BYTES_U8, (double)total);
|
|
1059
|
+
size_t off = 0;
|
|
1060
|
+
for (size_t i = 0; i < v->v.arr.len; i++) {
|
|
1061
|
+
const ScrDyn *e = v->v.arr.items[i];
|
|
1062
|
+
const char *p = e->kind == SCR_DYN_STR ? e->v.str->data : (const char *)e->v.bytes->data;
|
|
1063
|
+
size_t n = e->kind == SCR_DYN_STR ? e->v.str->len : e->v.bytes->len;
|
|
1064
|
+
memcpy(b->data + off, p, n);
|
|
1065
|
+
off += n;
|
|
1066
|
+
b->data[off++] = '\n'; /* PEM blocks join on line boundaries */
|
|
1067
|
+
}
|
|
1068
|
+
return b;
|
|
1069
|
+
}
|
|
1070
|
+
scr_dyn_arg_type_fail(what, "of type string or an instance of Buffer or Uint8Array", v);
|
|
1071
|
+
return NULL;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/* The literal path's runtime-valued cert/key extraction (the compiler's
|
|
1075
|
+
* tls.pemDyn libCall): `what` arrives precomposed. undefined throws — the
|
|
1076
|
+
* literal walk required the member's presence. */
|
|
1077
|
+
ScrBytes *scr_tls_pem_from_dyn(const ScrDyn *v, const char *what) {
|
|
1078
|
+
if (v->kind == SCR_DYN_UNDEF || v->kind == SCR_DYN_NULL) {
|
|
1079
|
+
ScrJsonBuf b;
|
|
1080
|
+
scr_jb_init(&b);
|
|
1081
|
+
scr_jb_puts(&b, what);
|
|
1082
|
+
scr_jb_puts(&b, " holding undefined has no static lowering — pass PEM material (a string or a Buffer)");
|
|
1083
|
+
scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
|
|
1084
|
+
return NULL;
|
|
1085
|
+
}
|
|
1086
|
+
return scr_tls_pem_dyn(v, what, false);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/* The tls/https createServer documented keys OUTSIDE the implemented set
|
|
1090
|
+
* (cert/key/ca/requestCert/rejectUnauthorized/SNICallback walk their own
|
|
1091
|
+
* arms below): present-with-a-value throws the runtime fence — an option
|
|
1092
|
+
* that changes Node-observable behavior must work or fence, never
|
|
1093
|
+
* silently drop. Undocumented keys drop, exactly Node. */
|
|
1094
|
+
static const char *const SCR_TLS_SRV_FENCED_OPTIONS[] = {
|
|
1095
|
+
"ALPNProtocols", "ALPNCallback", "allowHalfOpen", "ciphers",
|
|
1096
|
+
"clientCertEngine", "crl", "dhparam", "ecdhCurve", "enableTrace",
|
|
1097
|
+
"handshakeTimeout", "highWaterMark", "honorCipherOrder", "keepAlive",
|
|
1098
|
+
"keepAliveInitialDelay", "maxVersion", "minVersion", "noDelay",
|
|
1099
|
+
"passphrase", "pauseOnConnect", "pfx", "privateKeyEngine",
|
|
1100
|
+
"privateKeyIdentifier", "pskCallback", "pskIdentityHint", "requestOCSP",
|
|
1101
|
+
"secureContext", "secureOptions", "secureProtocol", "sessionIdContext",
|
|
1102
|
+
"sessionTimeout", "sigalgs", "ticketKeys", NULL,
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
/* The server-options walk. Fills the cert/key out-params (+1 each) from a
|
|
1106
|
+
* DOM options record; false = exception pending (partial results released). */
|
|
1107
|
+
static bool scr_tls_srv_opts_walk(const ScrDyn *opts, const char *api, ScrBytes **cert_out,
|
|
1108
|
+
ScrBytes **key_out) {
|
|
1109
|
+
*cert_out = NULL;
|
|
1110
|
+
*key_out = NULL;
|
|
1111
|
+
if (opts == NULL || opts->kind != SCR_DYN_OBJ) {
|
|
1112
|
+
scr_dyn_arg_type_fail("options", "of type object", opts ? opts : scr_dyn_undefined());
|
|
1113
|
+
return false;
|
|
1114
|
+
}
|
|
1115
|
+
bool ok = true;
|
|
1116
|
+
for (size_t i = 0; ok && i < opts->v.obj.len; i++) {
|
|
1117
|
+
const ScrDynEntry *e = &opts->v.obj.entries[i];
|
|
1118
|
+
const ScrDyn *v = e->value;
|
|
1119
|
+
if (v->kind == SCR_DYN_UNDEF) continue; /* an undefined member reads as absent, like Node */
|
|
1120
|
+
if (strcmp(e->key, "cert") == 0 || strcmp(e->key, "key") == 0) {
|
|
1121
|
+
char what[96];
|
|
1122
|
+
snprintf(what, sizeof what, "a %s '%s' option", api, e->key);
|
|
1123
|
+
ScrBytes *b = scr_tls_pem_dyn(v, what, false);
|
|
1124
|
+
if (b == NULL) { ok = false; break; }
|
|
1125
|
+
ScrBytes **slot = e->key[0] == 'c' ? cert_out : key_out;
|
|
1126
|
+
scr_bytes_release(*slot);
|
|
1127
|
+
*slot = b;
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
1130
|
+
if (strcmp(e->key, "ca") == 0) {
|
|
1131
|
+
/* the server's `ca` verifies CLIENT certificates — inert without
|
|
1132
|
+
* requestCert (which fences below), so Node-observably droppable */
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
if (strcmp(e->key, "requestCert") == 0) {
|
|
1136
|
+
if (scr_tls_dyn_truthy(v)) {
|
|
1137
|
+
scr_tls_opt_fence(api, "requestCert",
|
|
1138
|
+
"client-certificate handshakes are not modeled (SEMANTICS.md divergence 55) — requestCert stays at Node's false default");
|
|
1139
|
+
ok = false;
|
|
1140
|
+
}
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
if (strcmp(e->key, "rejectUnauthorized") == 0) {
|
|
1144
|
+
continue; /* meaningful only with requestCert, which fences */
|
|
1145
|
+
}
|
|
1146
|
+
if (strcmp(e->key, "SNICallback") == 0) {
|
|
1147
|
+
scr_tls_opt_fence(api, "SNICallback",
|
|
1148
|
+
"SNICallback lowers only on http2.createSecureServer({ allowHTTP1: true, ... }) with a literal options object");
|
|
1149
|
+
ok = false;
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
for (size_t k = 0; SCR_TLS_SRV_FENCED_OPTIONS[k] != NULL; k++) {
|
|
1153
|
+
if (strcmp(e->key, SCR_TLS_SRV_FENCED_OPTIONS[k]) == 0) {
|
|
1154
|
+
scr_tls_opt_fence(api, e->key,
|
|
1155
|
+
"cert and key (PEM strings or Buffers) are the honestly-implemented members of a runtime options record");
|
|
1156
|
+
ok = false;
|
|
1157
|
+
break;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
/* anything else: an undocumented key, dropped like Node drops it */
|
|
1161
|
+
}
|
|
1162
|
+
if (ok && (*cert_out == NULL || *key_out == NULL)) {
|
|
1163
|
+
ScrJsonBuf b;
|
|
1164
|
+
scr_jb_init(&b);
|
|
1165
|
+
scr_jb_puts(&b, api);
|
|
1166
|
+
scr_jb_puts(&b, " without both cert and key has no static lowering — the supported options are { cert, key } (PEM strings or Buffers)");
|
|
1167
|
+
scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
|
|
1168
|
+
ok = false;
|
|
1169
|
+
}
|
|
1170
|
+
if (!ok) {
|
|
1171
|
+
scr_bytes_release(*cert_out);
|
|
1172
|
+
scr_bytes_release(*key_out);
|
|
1173
|
+
*cert_out = NULL;
|
|
1174
|
+
*key_out = NULL;
|
|
1175
|
+
}
|
|
1176
|
+
return ok;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
ScrNetServer *scr_tls_create_server_dyn(const ScrDyn *opts /*borrowed*/,
|
|
1180
|
+
ScrClosure *handler /*moves, nullable*/,
|
|
1181
|
+
ScrNetConnFn fn) {
|
|
1182
|
+
ScrBytes *cert, *key;
|
|
1183
|
+
if (!scr_tls_srv_opts_walk(opts, "tls.createServer", &cert, &key)) {
|
|
1184
|
+
scr_closure_release(handler);
|
|
1185
|
+
return NULL;
|
|
1186
|
+
}
|
|
1187
|
+
ScrNetServer *s = scr_tls_create_server((const char *)cert->data, cert->len,
|
|
1188
|
+
(const char *)key->data, key->len, handler, fn);
|
|
1189
|
+
scr_bytes_release(cert);
|
|
1190
|
+
scr_bytes_release(key);
|
|
1191
|
+
return s;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
ScrNetServer *scr_https_create_server_dyn(const ScrDyn *opts /*borrowed*/,
|
|
1195
|
+
ScrClosure *handler /*moves, nullable*/,
|
|
1196
|
+
ScrHttpReqFn fn) {
|
|
1197
|
+
ScrBytes *cert, *key;
|
|
1198
|
+
if (!scr_tls_srv_opts_walk(opts, "https.createServer", &cert, &key)) {
|
|
1199
|
+
scr_closure_release(handler);
|
|
1200
|
+
return NULL;
|
|
1201
|
+
}
|
|
1202
|
+
ScrNetServer *s = scr_https_create_server((const char *)cert->data, cert->len,
|
|
1203
|
+
(const char *)key->data, key->len, handler, fn);
|
|
1204
|
+
scr_bytes_release(cert);
|
|
1205
|
+
scr_bytes_release(key);
|
|
1206
|
+
return s;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/* ── tls.connect (the TLS client socket) ──────────────────────────────── */
|
|
1210
|
+
|
|
1211
|
+
/* tls.connect's documented keys outside the implemented set: present
|
|
1212
|
+
* throws (the server walk's stance). */
|
|
1213
|
+
static const char *const SCR_TLS_CONNECT_FENCED_OPTIONS[] = {
|
|
1214
|
+
"ALPNProtocols", "allowHalfOpen", "autoSelectFamily",
|
|
1215
|
+
"autoSelectFamilyAttemptTimeout", "cert", "checkServerIdentity",
|
|
1216
|
+
"ciphers", "clientCertEngine", "crl", "dhparam", "ecdhCurve",
|
|
1217
|
+
"enableTrace", "family", "fd", "hints", "highWaterMark",
|
|
1218
|
+
"honorCipherOrder", "keepAlive", "keepAliveInitialDelay", "key",
|
|
1219
|
+
"localAddress", "localPort", "lookup", "maxVersion", "minDHSize",
|
|
1220
|
+
"minVersion", "noDelay", "onread", "passphrase", "path", "pfx",
|
|
1221
|
+
"privateKeyEngine", "privateKeyIdentifier", "pskCallback", "readable",
|
|
1222
|
+
"secureContext", "secureOptions", "secureProtocol", "session",
|
|
1223
|
+
"sessionIdContext", "sigalgs", "signal", "socket", "ticketKeys",
|
|
1224
|
+
"timeout", "writable", NULL,
|
|
1225
|
+
};
|
|
1226
|
+
|
|
1227
|
+
ScrNetSocket *scr_tls_connect_dyn(double port, ScrStr *host /*borrowed, nullable*/,
|
|
1228
|
+
const ScrDyn *opts /*borrowed, nullable*/,
|
|
1229
|
+
ScrClosure *cb /*moves, nullable*/) {
|
|
1230
|
+
scr_tls_rng_init();
|
|
1231
|
+
bool reject = true;
|
|
1232
|
+
ScrBytes *ca = NULL;
|
|
1233
|
+
const ScrStr *servername = NULL;
|
|
1234
|
+
const ScrStr *opt_host = NULL;
|
|
1235
|
+
bool ok = true;
|
|
1236
|
+
if (opts != NULL && opts->kind != SCR_DYN_UNDEF && opts->kind != SCR_DYN_NULL) {
|
|
1237
|
+
if (opts->kind != SCR_DYN_OBJ) {
|
|
1238
|
+
scr_dyn_arg_type_fail("options", "of type object", opts);
|
|
1239
|
+
ok = false;
|
|
1240
|
+
}
|
|
1241
|
+
for (size_t i = 0; ok && i < opts->v.obj.len; i++) {
|
|
1242
|
+
const ScrDynEntry *e = &opts->v.obj.entries[i];
|
|
1243
|
+
const ScrDyn *v = e->value;
|
|
1244
|
+
if (v->kind == SCR_DYN_UNDEF) continue;
|
|
1245
|
+
if (strcmp(e->key, "port") == 0) {
|
|
1246
|
+
if (port >= 0) continue; /* the argument form wins, like Node */
|
|
1247
|
+
if (v->kind == SCR_DYN_NUM) port = v->v.num;
|
|
1248
|
+
else if (v->kind == SCR_DYN_STR) port = strtod(v->v.str->data, NULL); /* Node coerces numeric strings */
|
|
1249
|
+
else {
|
|
1250
|
+
scr_dyn_arg_type_fail("options.port", "of type number", v);
|
|
1251
|
+
ok = false;
|
|
1252
|
+
}
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
if (strcmp(e->key, "host") == 0) {
|
|
1256
|
+
if (v->kind == SCR_DYN_STR) opt_host = v->v.str;
|
|
1257
|
+
else {
|
|
1258
|
+
scr_dyn_arg_type_fail("options.host", "of type string", v);
|
|
1259
|
+
ok = false;
|
|
1260
|
+
}
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
if (strcmp(e->key, "rejectUnauthorized") == 0) {
|
|
1264
|
+
reject = scr_tls_dyn_truthy(v);
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (strcmp(e->key, "ca") == 0) {
|
|
1268
|
+
scr_bytes_release(ca);
|
|
1269
|
+
ca = scr_tls_pem_dyn(v, "a tls.connect 'ca' option", true);
|
|
1270
|
+
if (ca == NULL) ok = false;
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
if (strcmp(e->key, "servername") == 0) {
|
|
1274
|
+
if (v->kind == SCR_DYN_STR) servername = v->v.str;
|
|
1275
|
+
else {
|
|
1276
|
+
scr_dyn_arg_type_fail("options.servername", "of type string", v);
|
|
1277
|
+
ok = false;
|
|
1278
|
+
}
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
1281
|
+
for (size_t k = 0; SCR_TLS_CONNECT_FENCED_OPTIONS[k] != NULL; k++) {
|
|
1282
|
+
if (strcmp(e->key, SCR_TLS_CONNECT_FENCED_OPTIONS[k]) == 0) {
|
|
1283
|
+
scr_tls_opt_fence("tls.connect", e->key,
|
|
1284
|
+
"port/host/rejectUnauthorized/ca/servername are the honestly-implemented members of a runtime options record");
|
|
1285
|
+
ok = false;
|
|
1286
|
+
break;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
/* anything else: an undocumented key, dropped like Node drops it */
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
if (ok && port < 0) {
|
|
1293
|
+
scr_dyn_arg_type_fail("options.port", "of type number", scr_dyn_undefined());
|
|
1294
|
+
ok = false;
|
|
1295
|
+
}
|
|
1296
|
+
if (!ok) {
|
|
1297
|
+
scr_bytes_release(ca);
|
|
1298
|
+
scr_closure_release(cb);
|
|
1299
|
+
return NULL;
|
|
1300
|
+
}
|
|
1301
|
+
ScrStr *dial_host = (ScrStr *)(host != NULL && host->len > 0 ? host : opt_host);
|
|
1302
|
+
/* SNI + verification see the servername when given, the host name
|
|
1303
|
+
* otherwise (Node's servername default). */
|
|
1304
|
+
ScrStr *verify_host = (ScrStr *)(servername != NULL ? servername : dial_host);
|
|
1305
|
+
ScrStr *localhost_fallback = NULL;
|
|
1306
|
+
if (verify_host == NULL) {
|
|
1307
|
+
localhost_fallback = scr_str_new("localhost", 9);
|
|
1308
|
+
verify_host = localhost_fallback;
|
|
1309
|
+
}
|
|
1310
|
+
ScrNetSocket *s = scr_net_connect(port, dial_host, cb); /* cb fires post-handshake — secureConnect */
|
|
1311
|
+
ScrTlsCli *cli = scr_tls_cli_new2(verify_host, reject,
|
|
1312
|
+
ca != NULL ? (const char *)ca->data : NULL,
|
|
1313
|
+
ca != NULL ? ca->len : 0, !reject);
|
|
1314
|
+
scr_tls_cli_wrap(s, cli);
|
|
1315
|
+
scr_str_release(localhost_fallback);
|
|
1316
|
+
scr_bytes_release(ca);
|
|
1317
|
+
return s;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/* ── h2 over TLS (http2.createSecureServer / the https-authority client)
|
|
1321
|
+
* — the mbedTLS half: scr_http2.c owns the sessions and calls these two
|
|
1322
|
+
* (every http2-using binary links this unit — the moduleUsesTls switch
|
|
1323
|
+
* counts http2 libCalls). */
|
|
1324
|
+
|
|
1325
|
+
/* Wrap an h2 server: the TLS engine installs per accepted socket, ALPN
|
|
1326
|
+
* advertises h2 (plus http/1.1 when the caller wired an inner parser
|
|
1327
|
+
* hook first — none today), and on_established attaches the h2 session
|
|
1328
|
+
* via `h2_conn` (the ctx moves in with its release fn). */
|
|
1329
|
+
void scr_tls_server_wrap_h2(ScrNetServer *s, const char *cert, size_t cert_len,
|
|
1330
|
+
const char *key, size_t key_len,
|
|
1331
|
+
ScrNetNativeConnFn h2_conn, void *h2_ctx /*moves*/,
|
|
1332
|
+
void (*h2_ctx_free)(void *)) {
|
|
1333
|
+
ScrTlsSrv *srv = scr_tls_srv_new(cert, cert_len, key, key_len, false);
|
|
1334
|
+
mbedtls_ssl_conf_alpn_protocols(&srv->conf, SCR_TLS_ALPN_H2);
|
|
1335
|
+
srv->h2_conn = h2_conn;
|
|
1336
|
+
srv->h2_ctx = h2_ctx;
|
|
1337
|
+
srv->h2_ctx_free = h2_ctx_free;
|
|
1338
|
+
scr_net_server_get_native_conn(s, &srv->inner_conn, &srv->inner_ctx, &srv->inner_ctx_free);
|
|
1339
|
+
scr_net_server_set_native_conn(s, &scr_tls_srv_on_conn, srv, &scr_tls_srv_release_v);
|
|
1340
|
+
scr_net_server_defer_connections(s); /* 'connection'/'secureConnection' wait for the handshake */
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
/* The h2-over-TLS CLIENT wrap (http2.connect with an https authority):
|
|
1344
|
+
* a client transport offering ALPN ["h2"] — SNI/verification against
|
|
1345
|
+
* the authority host; `ca` replaces the trust anchors ("" = none) and
|
|
1346
|
+
* reject_unauthorized false records like tls.connect's. The session's
|
|
1347
|
+
* preface/SETTINGS ride the socket's write buffer until establishment. */
|
|
1348
|
+
void scr_tls_h2_client_wrap(ScrNetSocket *sock, ScrStr *host /*borrowed*/,
|
|
1349
|
+
bool reject_unauthorized, const char *ca, size_t ca_len) {
|
|
1350
|
+
ScrTlsCli *cli = scr_tls_cli_new2(host, reject_unauthorized, ca, ca_len, !reject_unauthorized);
|
|
1351
|
+
mbedtls_ssl_conf_alpn_protocols(&cli->conf, SCR_TLS_ALPN_H2);
|
|
1352
|
+
scr_tls_cli_wrap(sock, cli);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/* ── the checked-dynamic TLSSocket members (scr_net.c's hook table) ──── */
|
|
1356
|
+
|
|
1357
|
+
/* Node's authorizationError: the verify-failure CODE string (null when
|
|
1358
|
+
* authorized). Chain errors dominate the identity check — Node records
|
|
1359
|
+
* ssl.verifyError() first and only reaches checkServerIdentity's
|
|
1360
|
+
* ERR_TLS_CERT_ALTNAME_INVALID on a VERIFIED chain. */
|
|
1361
|
+
static const char *scr_tls_auth_code(ScrTlsSock *t) {
|
|
1362
|
+
uint32_t vr = mbedtls_ssl_get_verify_result(&t->ssl);
|
|
1363
|
+
if (t->vrfy_expired) return "CERT_HAS_EXPIRED";
|
|
1364
|
+
if (t->vrfy_leaf_self) return "DEPTH_ZERO_SELF_SIGNED_CERT";
|
|
1365
|
+
if (t->vrfy_chain_self) return "SELF_SIGNED_CERT_IN_CHAIN";
|
|
1366
|
+
if (vr & MBEDTLS_X509_BADCERT_NOT_TRUSTED) return "UNABLE_TO_VERIFY_LEAF_SIGNATURE";
|
|
1367
|
+
if (t->vrfy_cn_mismatch) return "ERR_TLS_CERT_ALTNAME_INVALID";
|
|
1368
|
+
return "UNABLE_TO_VERIFY_LEAF_SIGNATURE";
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
static ScrTlsSock *scr_tls_sock_of(ScrNetSocket *s) {
|
|
1372
|
+
return (ScrTlsSock *)scr_net_sock_transport_ctx_for(s, &SCR_TLS_OPS);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/* The client 'session' event: fires ONCE per socket with the serialized
|
|
1376
|
+
* mbedTLS session — a real ticket where one arrived (the 1.3 receipt
|
|
1377
|
+
* path), the established session otherwise (1.2). Node fires per
|
|
1378
|
+
* received ticket; the once-latch satisfies the suite's once-listener
|
|
1379
|
+
* shapes, and resumption itself stays out (divergence 55). */
|
|
1380
|
+
static void scr_tls_fire_session(ScrTlsSock *t) {
|
|
1381
|
+
if (t->session_fired || t->session_ls.n == 0 || t->cli == NULL) return;
|
|
1382
|
+
mbedtls_ssl_session sess;
|
|
1383
|
+
mbedtls_ssl_session_init(&sess);
|
|
1384
|
+
if (mbedtls_ssl_get_session(&t->ssl, &sess) != 0) {
|
|
1385
|
+
mbedtls_ssl_session_free(&sess); /* no resumable session yet (1.3 pre-ticket) */
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
size_t need = 0;
|
|
1389
|
+
(void)mbedtls_ssl_session_save(&sess, NULL, 0, &need);
|
|
1390
|
+
ScrBytes *buf = scr_bytes_new(SCR_BYTES_U8, (double)need);
|
|
1391
|
+
size_t used = 0;
|
|
1392
|
+
int ok = mbedtls_ssl_session_save(&sess, buf->data, need, &used) == 0;
|
|
1393
|
+
mbedtls_ssl_session_free(&sess);
|
|
1394
|
+
if (!ok) {
|
|
1395
|
+
scr_bytes_release(buf);
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
t->session_fired = true;
|
|
1399
|
+
ScrNetL *snap;
|
|
1400
|
+
size_t n = scr_net_ls_snapshot(&t->session_ls, &snap);
|
|
1401
|
+
scr_dyn_this_push(t->sock, SCR_DYNH_NET_SOCKET);
|
|
1402
|
+
for (size_t i = 0; i < n; i++) {
|
|
1403
|
+
if (!scr_exc_pending()) ((ScrNetDataFn)snap[i].fn)(snap[i].cb, buf);
|
|
1404
|
+
scr_closure_release(snap[i].cb);
|
|
1405
|
+
}
|
|
1406
|
+
scr_dyn_this_pop();
|
|
1407
|
+
free(snap);
|
|
1408
|
+
scr_bytes_release(buf);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/* The STATIC-lane TLSSocket members (the lowered socket surface). */
|
|
1412
|
+
|
|
1413
|
+
bool scr_tls_sock_authorized(ScrNetSocket *s) {
|
|
1414
|
+
ScrTlsSock *t = scr_tls_sock_of(s);
|
|
1415
|
+
return t != NULL && t->established && t->cli != NULL && t->cli->verify_recorded &&
|
|
1416
|
+
mbedtls_ssl_get_verify_result(&t->ssl) == 0;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
ScrStr *scr_tls_sock_auth_error(ScrNetSocket *s) {
|
|
1420
|
+
ScrTlsSock *t = scr_tls_sock_of(s);
|
|
1421
|
+
if (t == NULL || !t->established || t->cli == NULL || !t->cli->verify_recorded ||
|
|
1422
|
+
mbedtls_ssl_get_verify_result(&t->ssl) == 0) {
|
|
1423
|
+
return NULL; /* the null arm — authorized, or never verified */
|
|
1424
|
+
}
|
|
1425
|
+
const char *code = scr_tls_auth_code(t);
|
|
1426
|
+
return scr_str_new(code, strlen(code));
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
void scr_tls_sock_on_secure_connect(ScrNetSocket *s, ScrClosure *cb /*moves*/, bool once) {
|
|
1430
|
+
if (scr_tls_sock_of(s) == NULL) {
|
|
1431
|
+
scr_closure_release(cb); /* a plain socket never fires it, like Node */
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
scr_net_sock_on_connect(s, cb, once); /* fires at establishment — secureConnect timing */
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
void scr_tls_sock_on_session(ScrNetSocket *s, ScrClosure *cb /*moves*/, void *fn, bool once) {
|
|
1438
|
+
ScrTlsSock *t = scr_tls_sock_of(s);
|
|
1439
|
+
if (t == NULL) {
|
|
1440
|
+
scr_closure_release(cb); /* a plain socket never fires it, like Node */
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
scr_net_ls_add(&t->session_ls, cb, fn, once);
|
|
1444
|
+
if (t->established) scr_tls_fire_session(t); /* the ticket already arrived */
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
static bool scr_tls_dynh_get(ScrNetSocket *s, const char *key, ScrDyn **out) {
|
|
1448
|
+
ScrTlsSock *t = scr_tls_sock_of(s);
|
|
1449
|
+
if (t == NULL) return false; /* a plain socket: the net dispatch answers */
|
|
1450
|
+
if (strcmp(key, "authorized") == 0) {
|
|
1451
|
+
bool a = t->established && t->cli != NULL && t->cli->verify_recorded &&
|
|
1452
|
+
mbedtls_ssl_get_verify_result(&t->ssl) == 0;
|
|
1453
|
+
*out = scr_dyn_new_bool(a);
|
|
1454
|
+
return true;
|
|
1455
|
+
}
|
|
1456
|
+
if (strcmp(key, "authorizationError") == 0) {
|
|
1457
|
+
if (!t->established || t->cli == NULL || !t->cli->verify_recorded ||
|
|
1458
|
+
mbedtls_ssl_get_verify_result(&t->ssl) == 0) {
|
|
1459
|
+
*out = scr_dyn_new_null();
|
|
1460
|
+
return true;
|
|
1461
|
+
}
|
|
1462
|
+
const char *code = scr_tls_auth_code(t);
|
|
1463
|
+
ScrStr *str = scr_str_new(code, strlen(code));
|
|
1464
|
+
*out = scr_dyn_new_str(str);
|
|
1465
|
+
scr_str_release(str);
|
|
1466
|
+
return true;
|
|
1467
|
+
}
|
|
1468
|
+
return false;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
static bool scr_tls_dynh_on(ScrNetSocket *s, const char *event, const ScrDyn *cb, bool once) {
|
|
1472
|
+
ScrTlsSock *t = scr_tls_sock_of(s);
|
|
1473
|
+
if (t == NULL) return false;
|
|
1474
|
+
if (strcmp(event, "secureConnect") == 0) {
|
|
1475
|
+
/* a TLS socket's 'connect' list fires at establishment — exactly
|
|
1476
|
+
* Node's secureConnect timing (scr_net.c's transport pump) */
|
|
1477
|
+
scr_net_sock_on_connect(s, scr_dyn_listener_closure0(cb), once);
|
|
1478
|
+
return true;
|
|
1479
|
+
}
|
|
1480
|
+
if (strcmp(event, "session") == 0) {
|
|
1481
|
+
scr_net_ls_add(&t->session_ls, scr_dyn_listener_closure_data(cb),
|
|
1482
|
+
(void *)&scr_dyn_listener_fire_data, once);
|
|
1483
|
+
/* a late registration on an established socket fires directly (the
|
|
1484
|
+
* ticket already arrived — nothing further would trigger it) */
|
|
1485
|
+
if (t->established) scr_tls_fire_session(t);
|
|
1486
|
+
return true;
|
|
1487
|
+
}
|
|
1488
|
+
return false;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
static bool scr_tls_dynh_invoke(ScrNetSocket *s, const char *method, ScrDyn *const *args,
|
|
1492
|
+
size_t argc, ScrDyn **out) {
|
|
1493
|
+
(void)args;
|
|
1494
|
+
(void)argc;
|
|
1495
|
+
if (scr_tls_sock_of(s) == NULL) return false;
|
|
1496
|
+
static const char *const tls_methods[] = {
|
|
1497
|
+
"getPeerCertificate", "getCertificate", "getCipher", "getProtocol", "getSession",
|
|
1498
|
+
"getTLSTicket", "getEphemeralKeyInfo", "getFinished", "getPeerFinished",
|
|
1499
|
+
"getSharedSigalgs", "getPeerX509Certificate", "getX509Certificate",
|
|
1500
|
+
"exportKeyingMaterial", "isSessionReused", "renegotiate", "setMaxSendFragment",
|
|
1501
|
+
"enableTrace", "disableRenegotiation", "setServername", "setSession", NULL,
|
|
1502
|
+
};
|
|
1503
|
+
for (size_t i = 0; tls_methods[i] != NULL; i++) {
|
|
1504
|
+
if (strcmp(method, tls_methods[i]) == 0) {
|
|
1505
|
+
char msg[160];
|
|
1506
|
+
int n = snprintf(msg, sizeof msg,
|
|
1507
|
+
"'TLSSocket.prototype.%s' on a dynamic value is not supported yet", method);
|
|
1508
|
+
scr_throw_error_msg(SCR_ERR_ERROR, msg, (size_t)n);
|
|
1509
|
+
*out = NULL;
|
|
1510
|
+
return true;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
return false;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
static const ScrNetDynhTlsHooks SCR_TLS_DYNH = {
|
|
1517
|
+
&scr_tls_dynh_get,
|
|
1518
|
+
&scr_tls_dynh_on,
|
|
1519
|
+
&scr_tls_dynh_invoke,
|
|
1520
|
+
};
|