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}