code wiki / (root) / nx_sni_extract.nx

nx_sni_extract.nx source

↩ module page · 50 lines · 2864 B

1// nx_sni_extract.nx -- extract the SNI host_name from a TLS ClientHello WITHOUT decrypting (the SNI is 2// cleartext in the first record). The sovereign SNI router on :443 peeks this to route the raw TCP connection 3// to the right per-property daemon (personal / client / business) -- the multi-tenant hosting platform's front. 4// Inverse of tls13_ext_emit_server_name (RFC 6066 ยง3, host_name only). license_tier: ORIGINAL expect_exit: 0 5import "nx_syscalls.nx" 6 7// big-endian u16 at buf[i..i+2] 8func sni_u16(buf: *u8, i: i64) -> i64 { return ((buf[i] as i64) << 8) | (buf[i+1] as i64) } 9 10// Extract the SNI host_name from a ClientHello (raw bytes starting at the TLS record header 0x16...) into out 11// (NUL-terminated). Returns the host length, or 0 if absent / malformed / truncated. Every step is bounds-checked 12// against n (defensive: hostile/partial input at the trust boundary -- rule 12). 13func sni_extract_hostname(hello: *u8, n: i64, out: *u8, cap: i64) -> i64 { 14 if n < 9 { return 0 } 15 if (hello[0] as i64) != 0x16 { return 0 } // not a TLS handshake record 16 if (hello[5] as i64) != 0x01 { return 0 } // handshake type != ClientHello 17 var p: i64 = 9 // ClientHello body: client_version(2)+random(32) 18 p = p + 2 + 32 19 if p + 1 > n { return 0 } 20 p = p + 1 + (hello[p] as i64) // session_id: len(1)+id 21 if p + 2 > n { return 0 } 22 p = p + 2 + sni_u16(hello, p) // cipher_suites: len(2)+suites 23 if p + 1 > n { return 0 } 24 p = p + 1 + (hello[p] as i64) // compression_methods: len(1)+methods 25 if p + 2 > n { return 0 } 26 let ext_total: i64 = sni_u16(hello, p); p = p + 2 // extensions: len(2)+exts 27 var ext_end: i64 = p + ext_total 28 if ext_end > n { ext_end = n } // tolerate a CH that spilled past the read; scan what we have 29 while p + 4 <= ext_end { 30 let ext_type: i64 = sni_u16(hello, p) 31 let ext_len: i64 = sni_u16(hello, p + 2) 32 let ext_data: i64 = p + 4 33 if ext_type == 0 { // server_name (SNI) 34 // ext_data: ServerNameList_len(2) + name_type(1) + host_name_len(2) + host_name 35 if ext_data + 5 > ext_end { return 0 } 36 let name_type: i64 = hello[ext_data + 2] as i64 37 let name_len: i64 = sni_u16(hello, ext_data + 3) 38 let name_off: i64 = ext_data + 5 39 if name_type != 0 { return 0 } // only host_name is registered 40 if name_off + name_len > ext_end { return 0 } 41 if name_len <= 0 { return 0 } 42 var k: i64 = 0 43 while k < name_len { if k < cap - 1 { out[k] = hello[name_off + k] } k = k + 1 } 44 out[name_len] = 0 as u8 45 return name_len 46 } 47 p = ext_data + ext_len 48 } 49 return 0 50}