@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.
Files changed (416) hide show
  1. package/LICENSE +202 -0
  2. package/package.json +16 -6
  3. package/src/scr_array.c +488 -0
  4. package/src/scr_assert.c +1393 -0
  5. package/src/scr_async.c +2593 -0
  6. package/src/scr_async_dyn.c +607 -0
  7. package/src/scr_bytes.c +1425 -0
  8. package/src/scr_bytes_io.c +161 -0
  9. package/src/scr_child.c +3587 -0
  10. package/src/scr_closure.c +214 -0
  11. package/src/scr_console.c +143 -0
  12. package/src/scr_cycle.c +218 -0
  13. package/src/scr_dc.c +805 -0
  14. package/src/scr_dgram.c +1007 -0
  15. package/src/scr_dyn_handle.c +154 -0
  16. package/src/scr_dyn_invoke.c +644 -0
  17. package/src/scr_error.c +332 -0
  18. package/src/scr_events.c +714 -0
  19. package/src/scr_events_emitter.c +699 -0
  20. package/src/scr_exception.c +242 -0
  21. package/src/scr_fetch.c +1130 -0
  22. package/src/scr_fetch_curl.c +654 -0
  23. package/src/scr_http.c +3482 -0
  24. package/src/scr_http2.c +3030 -0
  25. package/src/scr_inspect.c +803 -0
  26. package/src/scr_inspect_island.c +51 -0
  27. package/src/scr_island.c +9363 -0
  28. package/src/scr_json.c +2151 -0
  29. package/src/scr_lib.c +3612 -0
  30. package/src/scr_loop_epoll.c +241 -0
  31. package/src/scr_loop_kqueue.c +121 -0
  32. package/src/scr_loop_wsapoll.c +208 -0
  33. package/src/scr_map.c +591 -0
  34. package/src/scr_net.c +3017 -0
  35. package/src/scr_net_island.c +458 -0
  36. package/src/scr_number.c +115 -0
  37. package/src/scr_object.c +25 -0
  38. package/src/scr_path.c +1266 -0
  39. package/src/scr_platform.h +119 -0
  40. package/src/scr_readline.c +287 -0
  41. package/src/scr_regex.c +1080 -0
  42. package/src/scr_runtime.h +5046 -0
  43. package/src/scr_stream.c +2877 -0
  44. package/src/scr_string.c +1264 -0
  45. package/src/scr_symbol.c +132 -0
  46. package/src/scr_test.c +662 -0
  47. package/src/scr_tls.c +1520 -0
  48. package/src/scr_union.c +105 -0
  49. package/src/scr_url.c +911 -0
  50. package/src/scr_url_internal.h +29 -0
  51. package/src/scr_url_params.c +561 -0
  52. package/src/scr_watch.c +568 -0
  53. package/src/scr_web.c +1778 -0
  54. package/src/scr_win.c +76 -0
  55. package/src/scr_zlib.c +197 -0
  56. package/src/scr_zlib_island.c +15 -0
  57. package/vendor/README.md +52 -0
  58. package/vendor/curl/COPYRIGHT +534 -0
  59. package/vendor/curl/include/curl/curl.h +3214 -0
  60. package/vendor/curl/include/curl/curlver.h +79 -0
  61. package/vendor/curl/include/curl/easy.h +125 -0
  62. package/vendor/curl/include/curl/header.h +74 -0
  63. package/vendor/curl/include/curl/mprintf.h +52 -0
  64. package/vendor/curl/include/curl/multi.h +460 -0
  65. package/vendor/curl/include/curl/options.h +70 -0
  66. package/vendor/curl/include/curl/stdcheaders.h +35 -0
  67. package/vendor/curl/include/curl/system.h +508 -0
  68. package/vendor/curl/include/curl/typecheck-gcc.h +716 -0
  69. package/vendor/curl/include/curl/urlapi.h +149 -0
  70. package/vendor/curl/include/curl/websockets.h +84 -0
  71. package/vendor/mbedtls/LICENSE +553 -0
  72. package/vendor/mbedtls/include/CMakeLists.txt +22 -0
  73. package/vendor/mbedtls/include/mbedtls/aes.h +631 -0
  74. package/vendor/mbedtls/include/mbedtls/aria.h +343 -0
  75. package/vendor/mbedtls/include/mbedtls/asn1.h +642 -0
  76. package/vendor/mbedtls/include/mbedtls/asn1write.h +390 -0
  77. package/vendor/mbedtls/include/mbedtls/base64.h +82 -0
  78. package/vendor/mbedtls/include/mbedtls/bignum.h +1088 -0
  79. package/vendor/mbedtls/include/mbedtls/block_cipher.h +76 -0
  80. package/vendor/mbedtls/include/mbedtls/build_info.h +194 -0
  81. package/vendor/mbedtls/include/mbedtls/camellia.h +305 -0
  82. package/vendor/mbedtls/include/mbedtls/ccm.h +526 -0
  83. package/vendor/mbedtls/include/mbedtls/chacha20.h +209 -0
  84. package/vendor/mbedtls/include/mbedtls/chachapoly.h +351 -0
  85. package/vendor/mbedtls/include/mbedtls/check_config.h +1149 -0
  86. package/vendor/mbedtls/include/mbedtls/cipher.h +1250 -0
  87. package/vendor/mbedtls/include/mbedtls/cmac.h +246 -0
  88. package/vendor/mbedtls/include/mbedtls/compat-2.x.h +46 -0
  89. package/vendor/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h +578 -0
  90. package/vendor/mbedtls/include/mbedtls/config_adjust_legacy_from_psa.h +873 -0
  91. package/vendor/mbedtls/include/mbedtls/config_adjust_psa_from_legacy.h +359 -0
  92. package/vendor/mbedtls/include/mbedtls/config_adjust_psa_superset_legacy.h +145 -0
  93. package/vendor/mbedtls/include/mbedtls/config_adjust_ssl.h +91 -0
  94. package/vendor/mbedtls/include/mbedtls/config_adjust_x509.h +35 -0
  95. package/vendor/mbedtls/include/mbedtls/config_psa.h +61 -0
  96. package/vendor/mbedtls/include/mbedtls/constant_time.h +36 -0
  97. package/vendor/mbedtls/include/mbedtls/ctr_drbg.h +596 -0
  98. package/vendor/mbedtls/include/mbedtls/debug.h +156 -0
  99. package/vendor/mbedtls/include/mbedtls/des.h +385 -0
  100. package/vendor/mbedtls/include/mbedtls/dhm.h +972 -0
  101. package/vendor/mbedtls/include/mbedtls/ecdh.h +455 -0
  102. package/vendor/mbedtls/include/mbedtls/ecdsa.h +674 -0
  103. package/vendor/mbedtls/include/mbedtls/ecjpake.h +298 -0
  104. package/vendor/mbedtls/include/mbedtls/ecp.h +1517 -0
  105. package/vendor/mbedtls/include/mbedtls/entropy.h +274 -0
  106. package/vendor/mbedtls/include/mbedtls/error.h +201 -0
  107. package/vendor/mbedtls/include/mbedtls/gcm.h +387 -0
  108. package/vendor/mbedtls/include/mbedtls/hkdf.h +124 -0
  109. package/vendor/mbedtls/include/mbedtls/hmac_drbg.h +434 -0
  110. package/vendor/mbedtls/include/mbedtls/lms.h +440 -0
  111. package/vendor/mbedtls/include/mbedtls/mbedtls_config.h +4446 -0
  112. package/vendor/mbedtls/include/mbedtls/md.h +526 -0
  113. package/vendor/mbedtls/include/mbedtls/md5.h +190 -0
  114. package/vendor/mbedtls/include/mbedtls/memory_buffer_alloc.h +142 -0
  115. package/vendor/mbedtls/include/mbedtls/net_sockets.h +299 -0
  116. package/vendor/mbedtls/include/mbedtls/nist_kw.h +166 -0
  117. package/vendor/mbedtls/include/mbedtls/oid.h +727 -0
  118. package/vendor/mbedtls/include/mbedtls/pem.h +160 -0
  119. package/vendor/mbedtls/include/mbedtls/pk.h +1303 -0
  120. package/vendor/mbedtls/include/mbedtls/pkcs12.h +186 -0
  121. package/vendor/mbedtls/include/mbedtls/pkcs5.h +198 -0
  122. package/vendor/mbedtls/include/mbedtls/pkcs7.h +252 -0
  123. package/vendor/mbedtls/include/mbedtls/platform.h +516 -0
  124. package/vendor/mbedtls/include/mbedtls/platform_time.h +79 -0
  125. package/vendor/mbedtls/include/mbedtls/platform_util.h +247 -0
  126. package/vendor/mbedtls/include/mbedtls/poly1305.h +168 -0
  127. package/vendor/mbedtls/include/mbedtls/private_access.h +20 -0
  128. package/vendor/mbedtls/include/mbedtls/psa_util.h +207 -0
  129. package/vendor/mbedtls/include/mbedtls/ripemd160.h +136 -0
  130. package/vendor/mbedtls/include/mbedtls/rsa.h +1170 -0
  131. package/vendor/mbedtls/include/mbedtls/sha1.h +219 -0
  132. package/vendor/mbedtls/include/mbedtls/sha256.h +200 -0
  133. package/vendor/mbedtls/include/mbedtls/sha3.h +172 -0
  134. package/vendor/mbedtls/include/mbedtls/sha512.h +208 -0
  135. package/vendor/mbedtls/include/mbedtls/ssl.h +5887 -0
  136. package/vendor/mbedtls/include/mbedtls/ssl_cache.h +187 -0
  137. package/vendor/mbedtls/include/mbedtls/ssl_ciphersuites.h +482 -0
  138. package/vendor/mbedtls/include/mbedtls/ssl_cookie.h +106 -0
  139. package/vendor/mbedtls/include/mbedtls/ssl_ticket.h +199 -0
  140. package/vendor/mbedtls/include/mbedtls/threading.h +167 -0
  141. package/vendor/mbedtls/include/mbedtls/timing.h +94 -0
  142. package/vendor/mbedtls/include/mbedtls/version.h +78 -0
  143. package/vendor/mbedtls/include/mbedtls/x509.h +500 -0
  144. package/vendor/mbedtls/include/mbedtls/x509_crl.h +184 -0
  145. package/vendor/mbedtls/include/mbedtls/x509_crt.h +1208 -0
  146. package/vendor/mbedtls/include/mbedtls/x509_csr.h +382 -0
  147. package/vendor/mbedtls/include/psa/build_info.h +20 -0
  148. package/vendor/mbedtls/include/psa/crypto.h +4998 -0
  149. package/vendor/mbedtls/include/psa/crypto_adjust_auto_enabled.h +31 -0
  150. package/vendor/mbedtls/include/psa/crypto_adjust_config_dependencies.h +51 -0
  151. package/vendor/mbedtls/include/psa/crypto_adjust_config_key_pair_types.h +101 -0
  152. package/vendor/mbedtls/include/psa/crypto_adjust_config_synonyms.h +49 -0
  153. package/vendor/mbedtls/include/psa/crypto_builtin_composites.h +214 -0
  154. package/vendor/mbedtls/include/psa/crypto_builtin_key_derivation.h +118 -0
  155. package/vendor/mbedtls/include/psa/crypto_builtin_primitives.h +114 -0
  156. package/vendor/mbedtls/include/psa/crypto_compat.h +230 -0
  157. package/vendor/mbedtls/include/psa/crypto_config.h +145 -0
  158. package/vendor/mbedtls/include/psa/crypto_driver_common.h +44 -0
  159. package/vendor/mbedtls/include/psa/crypto_driver_contexts_composites.h +151 -0
  160. package/vendor/mbedtls/include/psa/crypto_driver_contexts_key_derivation.h +52 -0
  161. package/vendor/mbedtls/include/psa/crypto_driver_contexts_primitives.h +105 -0
  162. package/vendor/mbedtls/include/psa/crypto_extra.h +2145 -0
  163. package/vendor/mbedtls/include/psa/crypto_legacy.h +88 -0
  164. package/vendor/mbedtls/include/psa/crypto_platform.h +102 -0
  165. package/vendor/mbedtls/include/psa/crypto_se_driver.h +1383 -0
  166. package/vendor/mbedtls/include/psa/crypto_sizes.h +1319 -0
  167. package/vendor/mbedtls/include/psa/crypto_struct.h +527 -0
  168. package/vendor/mbedtls/include/psa/crypto_types.h +508 -0
  169. package/vendor/mbedtls/include/psa/crypto_values.h +2782 -0
  170. package/vendor/mbedtls/library/aes.c +2294 -0
  171. package/vendor/mbedtls/library/aesce.c +624 -0
  172. package/vendor/mbedtls/library/aesce.h +136 -0
  173. package/vendor/mbedtls/library/aesni.c +846 -0
  174. package/vendor/mbedtls/library/aesni.h +162 -0
  175. package/vendor/mbedtls/library/alignment.h +704 -0
  176. package/vendor/mbedtls/library/aria.c +969 -0
  177. package/vendor/mbedtls/library/asn1parse.c +468 -0
  178. package/vendor/mbedtls/library/asn1write.c +440 -0
  179. package/vendor/mbedtls/library/base64.c +322 -0
  180. package/vendor/mbedtls/library/base64_internal.h +45 -0
  181. package/vendor/mbedtls/library/bignum.c +2583 -0
  182. package/vendor/mbedtls/library/bignum_core.c +1240 -0
  183. package/vendor/mbedtls/library/bignum_core.h +872 -0
  184. package/vendor/mbedtls/library/bignum_core_invasive.h +38 -0
  185. package/vendor/mbedtls/library/bignum_internal.h +122 -0
  186. package/vendor/mbedtls/library/bignum_mod.c +394 -0
  187. package/vendor/mbedtls/library/bignum_mod.h +452 -0
  188. package/vendor/mbedtls/library/bignum_mod_raw.c +276 -0
  189. package/vendor/mbedtls/library/bignum_mod_raw.h +416 -0
  190. package/vendor/mbedtls/library/bignum_mod_raw_invasive.h +34 -0
  191. package/vendor/mbedtls/library/block_cipher.c +207 -0
  192. package/vendor/mbedtls/library/block_cipher_internal.h +99 -0
  193. package/vendor/mbedtls/library/bn_mul.h +1094 -0
  194. package/vendor/mbedtls/library/camellia.c +1058 -0
  195. package/vendor/mbedtls/library/ccm.c +777 -0
  196. package/vendor/mbedtls/library/chacha20.c +563 -0
  197. package/vendor/mbedtls/library/chacha20_internal.h +23 -0
  198. package/vendor/mbedtls/library/chachapoly.c +487 -0
  199. package/vendor/mbedtls/library/check_crypto_config.h +136 -0
  200. package/vendor/mbedtls/library/cipher.c +1712 -0
  201. package/vendor/mbedtls/library/cipher_invasive.h +28 -0
  202. package/vendor/mbedtls/library/cipher_wrap.c +2482 -0
  203. package/vendor/mbedtls/library/cipher_wrap.h +178 -0
  204. package/vendor/mbedtls/library/cmac.c +1067 -0
  205. package/vendor/mbedtls/library/common.h +487 -0
  206. package/vendor/mbedtls/library/constant_time.c +247 -0
  207. package/vendor/mbedtls/library/constant_time_impl.h +541 -0
  208. package/vendor/mbedtls/library/constant_time_internal.h +579 -0
  209. package/vendor/mbedtls/library/ctr.h +35 -0
  210. package/vendor/mbedtls/library/ctr_drbg.c +1016 -0
  211. package/vendor/mbedtls/library/debug.c +475 -0
  212. package/vendor/mbedtls/library/debug_internal.h +185 -0
  213. package/vendor/mbedtls/library/des.c +1042 -0
  214. package/vendor/mbedtls/library/dhm.c +700 -0
  215. package/vendor/mbedtls/library/ecdh.c +696 -0
  216. package/vendor/mbedtls/library/ecdsa.c +858 -0
  217. package/vendor/mbedtls/library/ecjpake.c +1216 -0
  218. package/vendor/mbedtls/library/ecp.c +3674 -0
  219. package/vendor/mbedtls/library/ecp_curves.c +6125 -0
  220. package/vendor/mbedtls/library/ecp_curves_new.c +9 -0
  221. package/vendor/mbedtls/library/ecp_internal_alt.h +287 -0
  222. package/vendor/mbedtls/library/ecp_invasive.h +305 -0
  223. package/vendor/mbedtls/library/entropy.c +680 -0
  224. package/vendor/mbedtls/library/entropy_poll.c +233 -0
  225. package/vendor/mbedtls/library/entropy_poll.h +64 -0
  226. package/vendor/mbedtls/library/error.c +878 -0
  227. package/vendor/mbedtls/library/gcm.c +1330 -0
  228. package/vendor/mbedtls/library/hkdf.c +161 -0
  229. package/vendor/mbedtls/library/hmac_drbg.c +633 -0
  230. package/vendor/mbedtls/library/lmots.c +789 -0
  231. package/vendor/mbedtls/library/lmots.h +288 -0
  232. package/vendor/mbedtls/library/lms.c +778 -0
  233. package/vendor/mbedtls/library/md.c +1108 -0
  234. package/vendor/mbedtls/library/md5.c +426 -0
  235. package/vendor/mbedtls/library/md_psa.h +26 -0
  236. package/vendor/mbedtls/library/md_wrap.h +46 -0
  237. package/vendor/mbedtls/library/memory_buffer_alloc.c +751 -0
  238. package/vendor/mbedtls/library/mps_common.h +181 -0
  239. package/vendor/mbedtls/library/mps_error.h +89 -0
  240. package/vendor/mbedtls/library/mps_reader.c +538 -0
  241. package/vendor/mbedtls/library/mps_reader.h +366 -0
  242. package/vendor/mbedtls/library/mps_trace.c +112 -0
  243. package/vendor/mbedtls/library/mps_trace.h +154 -0
  244. package/vendor/mbedtls/library/net_sockets.c +694 -0
  245. package/vendor/mbedtls/library/nist_kw.c +729 -0
  246. package/vendor/mbedtls/library/oid.c +1166 -0
  247. package/vendor/mbedtls/library/padlock.c +157 -0
  248. package/vendor/mbedtls/library/padlock.h +111 -0
  249. package/vendor/mbedtls/library/pem.c +554 -0
  250. package/vendor/mbedtls/library/pk.c +1602 -0
  251. package/vendor/mbedtls/library/pk_ecc.c +261 -0
  252. package/vendor/mbedtls/library/pk_internal.h +241 -0
  253. package/vendor/mbedtls/library/pk_wrap.c +1618 -0
  254. package/vendor/mbedtls/library/pk_wrap.h +138 -0
  255. package/vendor/mbedtls/library/pkcs12.c +437 -0
  256. package/vendor/mbedtls/library/pkcs5.c +500 -0
  257. package/vendor/mbedtls/library/pkcs7.c +787 -0
  258. package/vendor/mbedtls/library/pkparse.c +1392 -0
  259. package/vendor/mbedtls/library/pkwrite.c +631 -0
  260. package/vendor/mbedtls/library/pkwrite.h +121 -0
  261. package/vendor/mbedtls/library/platform.c +402 -0
  262. package/vendor/mbedtls/library/platform_util.c +258 -0
  263. package/vendor/mbedtls/library/poly1305.c +492 -0
  264. package/vendor/mbedtls/library/psa_crypto.c +9532 -0
  265. package/vendor/mbedtls/library/psa_crypto_aead.c +646 -0
  266. package/vendor/mbedtls/library/psa_crypto_aead.h +499 -0
  267. package/vendor/mbedtls/library/psa_crypto_cipher.c +747 -0
  268. package/vendor/mbedtls/library/psa_crypto_cipher.h +316 -0
  269. package/vendor/mbedtls/library/psa_crypto_client.c +22 -0
  270. package/vendor/mbedtls/library/psa_crypto_core.h +983 -0
  271. package/vendor/mbedtls/library/psa_crypto_core_common.h +52 -0
  272. package/vendor/mbedtls/library/psa_crypto_driver_wrappers.h +2896 -0
  273. package/vendor/mbedtls/library/psa_crypto_driver_wrappers_no_static.c +256 -0
  274. package/vendor/mbedtls/library/psa_crypto_driver_wrappers_no_static.h +31 -0
  275. package/vendor/mbedtls/library/psa_crypto_ecp.c +594 -0
  276. package/vendor/mbedtls/library/psa_crypto_ecp.h +267 -0
  277. package/vendor/mbedtls/library/psa_crypto_ffdh.c +361 -0
  278. package/vendor/mbedtls/library/psa_crypto_ffdh.h +131 -0
  279. package/vendor/mbedtls/library/psa_crypto_hash.c +470 -0
  280. package/vendor/mbedtls/library/psa_crypto_hash.h +211 -0
  281. package/vendor/mbedtls/library/psa_crypto_invasive.h +92 -0
  282. package/vendor/mbedtls/library/psa_crypto_its.h +131 -0
  283. package/vendor/mbedtls/library/psa_crypto_mac.c +505 -0
  284. package/vendor/mbedtls/library/psa_crypto_mac.h +264 -0
  285. package/vendor/mbedtls/library/psa_crypto_pake.c +571 -0
  286. package/vendor/mbedtls/library/psa_crypto_pake.h +159 -0
  287. package/vendor/mbedtls/library/psa_crypto_random.c +181 -0
  288. package/vendor/mbedtls/library/psa_crypto_random.h +72 -0
  289. package/vendor/mbedtls/library/psa_crypto_random_impl.h +200 -0
  290. package/vendor/mbedtls/library/psa_crypto_rsa.c +714 -0
  291. package/vendor/mbedtls/library/psa_crypto_rsa.h +321 -0
  292. package/vendor/mbedtls/library/psa_crypto_se.c +373 -0
  293. package/vendor/mbedtls/library/psa_crypto_se.h +192 -0
  294. package/vendor/mbedtls/library/psa_crypto_slot_management.c +1137 -0
  295. package/vendor/mbedtls/library/psa_crypto_slot_management.h +344 -0
  296. package/vendor/mbedtls/library/psa_crypto_storage.c +481 -0
  297. package/vendor/mbedtls/library/psa_crypto_storage.h +392 -0
  298. package/vendor/mbedtls/library/psa_its_file.c +254 -0
  299. package/vendor/mbedtls/library/psa_util.c +614 -0
  300. package/vendor/mbedtls/library/psa_util_internal.h +100 -0
  301. package/vendor/mbedtls/library/ripemd160.c +490 -0
  302. package/vendor/mbedtls/library/rsa.c +3121 -0
  303. package/vendor/mbedtls/library/rsa_alt_helpers.c +455 -0
  304. package/vendor/mbedtls/library/rsa_alt_helpers.h +209 -0
  305. package/vendor/mbedtls/library/rsa_internal.h +188 -0
  306. package/vendor/mbedtls/library/sha1.c +480 -0
  307. package/vendor/mbedtls/library/sha256.c +983 -0
  308. package/vendor/mbedtls/library/sha3.c +721 -0
  309. package/vendor/mbedtls/library/sha512.c +1115 -0
  310. package/vendor/mbedtls/library/ssl_cache.c +410 -0
  311. package/vendor/mbedtls/library/ssl_ciphersuites.c +2050 -0
  312. package/vendor/mbedtls/library/ssl_ciphersuites_internal.h +154 -0
  313. package/vendor/mbedtls/library/ssl_client.c +1023 -0
  314. package/vendor/mbedtls/library/ssl_client.h +22 -0
  315. package/vendor/mbedtls/library/ssl_cookie.c +384 -0
  316. package/vendor/mbedtls/library/ssl_debug_helpers.h +85 -0
  317. package/vendor/mbedtls/library/ssl_debug_helpers_generated.c +251 -0
  318. package/vendor/mbedtls/library/ssl_misc.h +3198 -0
  319. package/vendor/mbedtls/library/ssl_msg.c +6619 -0
  320. package/vendor/mbedtls/library/ssl_ticket.c +556 -0
  321. package/vendor/mbedtls/library/ssl_tls.c +10234 -0
  322. package/vendor/mbedtls/library/ssl_tls12_client.c +3688 -0
  323. package/vendor/mbedtls/library/ssl_tls12_server.c +4311 -0
  324. package/vendor/mbedtls/library/ssl_tls13_client.c +3208 -0
  325. package/vendor/mbedtls/library/ssl_tls13_generic.c +1793 -0
  326. package/vendor/mbedtls/library/ssl_tls13_invasive.h +23 -0
  327. package/vendor/mbedtls/library/ssl_tls13_keys.c +1918 -0
  328. package/vendor/mbedtls/library/ssl_tls13_keys.h +668 -0
  329. package/vendor/mbedtls/library/ssl_tls13_server.c +3624 -0
  330. package/vendor/mbedtls/library/threading.c +193 -0
  331. package/vendor/mbedtls/library/threading_internal.h +28 -0
  332. package/vendor/mbedtls/library/timing.c +152 -0
  333. package/vendor/mbedtls/library/version.c +32 -0
  334. package/vendor/mbedtls/library/version_features.c +856 -0
  335. package/vendor/mbedtls/library/x509.c +1776 -0
  336. package/vendor/mbedtls/library/x509_create.c +570 -0
  337. package/vendor/mbedtls/library/x509_crl.c +708 -0
  338. package/vendor/mbedtls/library/x509_crt.c +3311 -0
  339. package/vendor/mbedtls/library/x509_csr.c +648 -0
  340. package/vendor/mbedtls/library/x509_internal.h +101 -0
  341. package/vendor/mbedtls/library/x509write.c +174 -0
  342. package/vendor/mbedtls/library/x509write_crt.c +688 -0
  343. package/vendor/mbedtls/library/x509write_csr.c +336 -0
  344. package/vendor/quickjs-ng/CMakeLists.txt +566 -0
  345. package/vendor/quickjs-ng/LICENSE +24 -0
  346. package/vendor/quickjs-ng/README.md +24 -0
  347. package/vendor/quickjs-ng/api-test.c +1195 -0
  348. package/vendor/quickjs-ng/builtin-array-fromasync.h +119 -0
  349. package/vendor/quickjs-ng/builtin-iterator-zip-keyed.h +332 -0
  350. package/vendor/quickjs-ng/builtin-iterator-zip.h +337 -0
  351. package/vendor/quickjs-ng/cutils.h +1998 -0
  352. package/vendor/quickjs-ng/dtoa.c +1619 -0
  353. package/vendor/quickjs-ng/dtoa.h +87 -0
  354. package/vendor/quickjs-ng/gen/function_source.c +81 -0
  355. package/vendor/quickjs-ng/gen/hello.c +53 -0
  356. package/vendor/quickjs-ng/gen/hello_module.c +106 -0
  357. package/vendor/quickjs-ng/gen/repl.c +3036 -0
  358. package/vendor/quickjs-ng/gen/standalone.c +323 -0
  359. package/vendor/quickjs-ng/gen/test_fib.c +81 -0
  360. package/vendor/quickjs-ng/libregexp-opcode.h +73 -0
  361. package/vendor/quickjs-ng/libregexp.c +3474 -0
  362. package/vendor/quickjs-ng/libregexp.h +101 -0
  363. package/vendor/quickjs-ng/libunicode-table.h +5173 -0
  364. package/vendor/quickjs-ng/libunicode.c +2069 -0
  365. package/vendor/quickjs-ng/libunicode.h +172 -0
  366. package/vendor/quickjs-ng/list.h +107 -0
  367. package/vendor/quickjs-ng/lre-test.c +52 -0
  368. package/vendor/quickjs-ng/qjs.c +762 -0
  369. package/vendor/quickjs-ng/qjsc.c +673 -0
  370. package/vendor/quickjs-ng/quickjs-atom.h +280 -0
  371. package/vendor/quickjs-ng/quickjs-c-atomics.h +54 -0
  372. package/vendor/quickjs-ng/quickjs-libc.c +5037 -0
  373. package/vendor/quickjs-ng/quickjs-libc.h +87 -0
  374. package/vendor/quickjs-ng/quickjs-opcode.h +377 -0
  375. package/vendor/quickjs-ng/quickjs.c +64041 -0
  376. package/vendor/quickjs-ng/quickjs.h +1447 -0
  377. package/vendor/quickjs-ng/run-test262.c +2374 -0
  378. package/vendor/quickjs-ng/unicode_gen.c +3121 -0
  379. package/vendor/quickjs-ng/unicode_gen_def.h +310 -0
  380. package/vendor/ryu/LICENSE-Boost +23 -0
  381. package/vendor/ryu/common.h +114 -0
  382. package/vendor/ryu/d2s.c +509 -0
  383. package/vendor/ryu/d2s_full_table.h +367 -0
  384. package/vendor/ryu/d2s_intrinsics.h +357 -0
  385. package/vendor/ryu/d2s_small_table.h +186 -0
  386. package/vendor/ryu/digit_table.h +35 -0
  387. package/vendor/ryu/ryu.h +46 -0
  388. package/vendor/zlib/LICENSE +22 -0
  389. package/vendor/zlib/adler32.c +164 -0
  390. package/vendor/zlib/compress.c +75 -0
  391. package/vendor/zlib/crc32.c +1049 -0
  392. package/vendor/zlib/crc32.h +9446 -0
  393. package/vendor/zlib/deflate.c +2139 -0
  394. package/vendor/zlib/deflate.h +377 -0
  395. package/vendor/zlib/gzclose.c +23 -0
  396. package/vendor/zlib/gzguts.h +214 -0
  397. package/vendor/zlib/gzlib.c +582 -0
  398. package/vendor/zlib/gzread.c +602 -0
  399. package/vendor/zlib/gzwrite.c +631 -0
  400. package/vendor/zlib/infback.c +628 -0
  401. package/vendor/zlib/inffast.c +320 -0
  402. package/vendor/zlib/inffast.h +11 -0
  403. package/vendor/zlib/inffixed.h +94 -0
  404. package/vendor/zlib/inflate.c +1526 -0
  405. package/vendor/zlib/inflate.h +126 -0
  406. package/vendor/zlib/inftrees.c +299 -0
  407. package/vendor/zlib/inftrees.h +62 -0
  408. package/vendor/zlib/trees.c +1117 -0
  409. package/vendor/zlib/trees.h +128 -0
  410. package/vendor/zlib/uncompr.c +85 -0
  411. package/vendor/zlib/zconf.h +543 -0
  412. package/vendor/zlib/zlib.h +1938 -0
  413. package/vendor/zlib/zutil.c +299 -0
  414. package/vendor/zlib/zutil.h +254 -0
  415. package/README.md +0 -3
  416. 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
+ };