code wiki / (root) / nx_https_url_connect.nx

nx_https_url_connect.nx source

↩ module page · 220 lines · 9210 B

1// nx_https_url_connect.nx -- step 2 of the nx_https_client 2// orchestrator wiring arc. 3// 4// Takes an already-validated NxHttpsTarget (from 5// nx_https_url_for_fetch step 1) PLUS the URL buffer it was 6// parsed from, resolves the host via DNS, builds a sockaddr, and 7// TCP-connects to host:port. Returns a connected file descriptor 8// ready for the TLS handshake (step 3). 9// 10// Composes 3 shipped primitives: 11// nx_https_url_for_fetch (c0117caa) -- already-parsed target 12// nx_dns_resolve_default -- hostname -> packed IPv4 13// sys_socket / sys_connect / sys_close -- BSD socket primitives 14// 15// Public API: 16// nx_https_url_connect(target, url_buf, now_unix, out_fd) -> verdict 17// nx_https_connect_verdict_is_valid(v) -> 0|1 18// 19// Why the url_buf parameter is separate: 20// NxHttpsTarget.url.host_off is an OFFSET into the URL string 21// the caller originally passed to nx_https_url_for_fetch. This 22// primitive needs the resolved host bytes, so the caller must 23// pass the same url_buf. An alternative API would have 24// NxHttpsTarget carry the url_buf pointer itself -- that's a 25// refactor for a future commit; for now we keep the explicit 26// parameter to avoid breaking step 1's existing API. 27// 28// Sealed verdict enum: 29// NX_HTTPS_CONNECT_OK socket connected; *out_fd valid 30// NX_HTTPS_CONNECT_DNS_FAIL DNS resolution returned no IPv4 31// NX_HTTPS_CONNECT_SOCKET_FAIL sys_socket returned negative 32// NX_HTTPS_CONNECT_CONNECT_FAIL sys_connect returned negative 33// 34// On any non-OK return, *out_fd is set to -1 and any socket 35// already allocated is closed. Callers don't need to clean up on 36// failure verdicts. 37// 38// Per Cardinals 9 (single-responsibility -- step 2 only; TLS + 39// HTTP are separate primitives), 12 (defensive at boundaries -- 40// close fd on every failure path to avoid descriptor leaks), 23 41// (preamble names the url_buf API caveat + the queued refactor). 42// 43// license_tier: INDEPENDENT_REDERIVE 44// genealogy_id: international-research-sources/ietf/rfc_1035 + bsd_sockets 45// lineage_id: nishi_https_url_connect_q10 46 47// nx_safety_envelope: 48// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 49// sil_target: SIL1 50// evidence: [bulk_applied_2026-05-19, https-url-connect-step-2] 51// verdict: NOT_YET_EVALUATED 52 53import "nx_syscalls.nx" 54import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 55import "nx_dns_resolve_a_record.nx" 56import "nx_https_url_for_fetch.nx" 57const NX_MAGIC_65535: i64 = 65535 58 59const NX_HTTPS_CONNECT_OK: i64 = 1 60const NX_HTTPS_CONNECT_DNS_FAIL: i64 = 2 61const NX_HTTPS_CONNECT_SOCKET_FAIL: i64 = 3 62const NX_HTTPS_CONNECT_CONNECT_FAIL: i64 = 4 63const NX_HTTPS_CONNECT_VERDICT_N: i64 = 5 64 65const NX_HTTPS_AF_INET: i64 = 2 66const NX_HTTPS_SOCK_STREAM: i64 = 1 67 68func nx_https_connect_verdict_is_valid(v: i64) -> i64 { 69 if v < NX_HTTPS_CONNECT_OK { return 0 } 70 if v >= NX_HTTPS_CONNECT_VERDICT_N { return 0 } 71 return 1 72} 73 74// Build an IPv4 sockaddr_in into a 16-byte buffer. 75// [0..1] sin_family = AF_INET = 2 (little-endian short) 76// [2..3] sin_port = port in BIG-endian 77// [4..7] sin_addr = ipv4 bytes a.b.c.d in network order 78// [8..15] padding zero 79// 80// `ipv4_packed` is the DNS result's packed format: low 32 bits 81// hold the 4 octets in big-endian order (1.2.3.4 -> 0x01020304). 82func nx_https_build_sockaddr(out: *u8, ipv4_packed: i64, port: i64) -> i64 { 83 out[0] = (NX_HTTPS_AF_INET & 0xff) as u8 84 out[1] = ((NX_HTTPS_AF_INET >> 8) & 0xff) as u8 85 out[2] = ((port >> 8) & 0xff) as u8 86 out[3] = (port & 0xff) as u8 87 out[4] = ((ipv4_packed >> 24) & 0xff) as u8 88 out[5] = ((ipv4_packed >> 16) & 0xff) as u8 89 out[6] = ((ipv4_packed >> 8) & 0xff) as u8 90 out[7] = (ipv4_packed & 0xff) as u8 91 var i: i64 = 8 92 while i < 16 { out[i] = 0 as u8; i = i + 1 } 93 return 16 94} 95 96// ---- OPTIONAL connect-override (curl --connect-to class; the vantage false-RED cure) ---- 97// When armed, EVERY nx_https_url_connect in this process connects to the pinned a.b.c.d:port 98// endpoint (skipping DNS) while the caller keeps SNI + Host + cert-name = the URL host. Lets 99// verifiers fetch our OWN vhosts deterministically from the sovereign edge (127.0.0.1:8443), 100// bypassing the DSM nginx that co-squats :443 for unclaimed SNIs -- the nx_page_verify / 101// nav-canary false-RED family. Default OFF (static box null) = byte-identical legacy DNS path. 102// Same semantics as the proven nx_https_get_cli override; parser mirrors hgc_parse_ipport 103// (the CLI migrates onto this exported pair on next touch, migrate-on-touch law). 104static hco_box: *i64 105 106// Parse "a.b.c.d:port" -> packed ipv4 + port. Returns 1 ok / 0 bad-spec (outs untouched on 0). 107func nx_https_parse_ipport(s: *u8, ip_out: *i64, port_out: *i64) -> i64 { 108 var packed: i64 = 0 109 var val: i64 = 0 110 var nocts: i64 = 0 111 var port: i64 = 0 112 var indots: i64 = 1 113 var i: i64 = 0 114 while s[i] != (0 as u8) { 115 let c: i64 = s[i] as i64 116 if indots == 1 { 117 if c == 46 { packed = (packed << 8) | (val & 0xff); nocts = nocts + 1; val = 0 } 118 else { if c == 58 { packed = (packed << 8) | (val & 0xff); nocts = nocts + 1; val = 0; indots = 0 } 119 else { if c < 48 { return 0 } if c > 57 { return 0 } val = val * 10 + (c - 48) } } 120 } else { 121 if c < 48 { return 0 } 122 if c > 57 { return 0 } 123 port = port * 10 + (c - 48) 124 } 125 i = i + 1 126 } 127 // no ':' -> no port given 128 if indots == 1 { return 0 } 129 if nocts != 4 { return 0 } 130 if port <= 0 { return 0 } 131 if port > NX_MAGIC_65535 { return 0 } 132 ip_out[0] = packed 133 port_out[0] = port 134 return 1 135} 136 137// Arm the process-wide override from "a.b.c.d:port". Returns 1 armed / 0 bad-spec (state untouched). 138func nx_https_connect_override_set(spec: *u8) -> i64 { 139 let ipbox: *i64 = sys_mmap(8) as *i64 140 let portbox: *i64 = sys_mmap(8) as *i64 141 if nx_https_parse_ipport(spec, ipbox, portbox) != 1 { return 0 } 142 if (hco_box as i64) == 0 { hco_box = sys_mmap(16) as *i64 } 143 hco_box[0] = ipbox[0] 144 hco_box[1] = portbox[0] 145 return 1 146} 147 148// Resolve target.url.host via DNS + TCP-connect to (resolved IP, 149// target.port). Writes the connected fd to *out_fd on success. 150func nx_https_url_connect( 151 target: *NxHttpsTarget, 152 url_buf: *u8, 153 now_unix: i64, 154 out_fd: *i64 155) -> i64 { 156 *out_fd = 0 - 1 157 158 // ---- connect-override armed? pinned-endpoint connect, DNS skipped (TLS/SNI stay the URL host) ---- 159 if (hco_box as i64) != 0 { if hco_box[1] != 0 { 160 let ofd: i64 = sys_socket(NX_HTTPS_AF_INET, NX_HTTPS_SOCK_STREAM, 0) 161 if ofd < 0 { return NX_HTTPS_CONNECT_SOCKET_FAIL } 162 sys_set_socket_timeout(ofd, 10) 163 let osa: *u8 = sys_mmap(16) 164 nx_https_build_sockaddr(osa, hco_box[0], hco_box[1]) 165 if nx_connect_bounded(ofd, osa, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(ofd); return NX_HTTPS_CONNECT_CONNECT_FAIL } 166 let ond: *u8 = sys_mmap(4) 167 ond[0] = 1 as u8 168 sys_setsockopt(ofd, 6, 1, ond, 4) 169 *out_fd = ofd 170 return NX_HTTPS_CONNECT_OK 171 } } 172 173 // ---- DNS resolve ---- 174 let host_ptr: *u8 = url_buf + target.url.host_off 175 let host_len: i64 = target.url.host_len 176 177 let dns_result: *DnsResolveResult = nx_dns_resolve_default( 178 host_ptr, host_len, now_unix 179 ) 180 if dns_result.verdict != NX_DNS_R_OK { return NX_HTTPS_CONNECT_DNS_FAIL } 181 if dns_result.ipv4_packed == 0 { return NX_HTTPS_CONNECT_DNS_FAIL } 182 183 // ---- TCP socket ---- 184 let fd: i64 = sys_socket(NX_HTTPS_AF_INET, NX_HTTPS_SOCK_STREAM, 0) 185 if fd < 0 { return NX_HTTPS_CONNECT_SOCKET_FAIL } 186 sys_set_socket_timeout(fd, 10) // 10s read/write timeout: a hung server fails gracefully, never blocks forever 187 188 // ---- Build sockaddr + connect ---- 189 let sa: *u8 = sys_mmap(16) 190 nx_https_build_sockaddr(sa, dns_result.ipv4_packed, target.port) 191 192 let cr: i64 = nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS) 193 if cr < 0 { 194 sys_close(fd) 195 return NX_HTTPS_CONNECT_CONNECT_FAIL 196 } 197 198 // TCP_NODELAY (IPPROTO_TCP=6, TCP_NODELAY=1): disable Nagle on the CLIENT socket. 199 // Without it, client-side Nagle x server delayed-ACK adds up to ~40ms per burst -- 200 // MEASURED live by nx_video_qoe_live 2026-07-03: 20fps ran clean (3ms path) but every 201 // faster cadence hit an exact ~40ms cycle floor. Latency-first for every sovereign TLS 202 // client (funcheck, researchers, the native video client, QoE probes). Mirrors the 203 // accept-side nodelay the sites daemons already set. Best-effort: a failure here only 204 // costs latency, never correctness -- so the verdict does not change. 205 let nodelay: *u8 = sys_mmap(4) 206 nodelay[0] = 1 as u8 207 nodelay[1] = 0 as u8 208 nodelay[2] = 0 as u8 209 nodelay[3] = 0 as u8 210 sys_setsockopt(fd, 6, 1, nodelay, 4) 211 212 *out_fd = fd 213 return NX_HTTPS_CONNECT_OK 214} 215 216// Compile-only smoke. Real KAT in nx_https_url_connect_test.nx -- 217// verifies sockaddr-builder byte layout + verdict dispatch. 218func main() -> i64 { 219 return 0 220}