nx_net_diag.nx source
↩ module page · 136 lines · 8458 B
1// nx_net_diag.nx -- IN-PLACE network troubleshooter (operator 2026-07-04: "we need a tracert/ping type
2// capability so the smart fallback can troubleshoot + resolve in place"). Layers the failure so we know WHICH
3// layer breaks instead of guessing: DNS resolve -> TCP connect -> send a BROAD browser-like TLS 1.2 ClientHello
4// and READ exactly what the server returns (ServerHello=answered / Alert=negotiation-refused-with-reason /
5// nothing=silent fingerprint drop). This is what cracked "ukdevilz fails but Waterfox works from the same IP":
6// it distinguishes a network block (never happens -- DNS+TCP fine) from a TLS-fingerprint/cipher issue.
7// usage: nx_net_diag <host>
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
11import "nx_dns_resolve_a_record.nx" // nx_dns_resolve_default + DnsResolveResult + NX_DNS_R_OK
12const ND_MAGIC_1024: i64 = 1024
13const ND_MAGIC_16384: i64 = 16384
14
15const ND_AF_INET: i64 = 2
16const ND_SOCK_STREAM: i64 = 1
17
18func dw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
19func dn(v: i64) -> i64 { if v==0 { sys_write(1,"0" as *u8,1); return 0 } var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } let o: *u8=sys_mmap(24); var q: i64=k-1; var x: i64=0; while q>=0 { o[x]=t[q]; x=x+1; q=q-1 } sys_write(1,o,x); return 0 }
20func dhx(v: i64) -> i64 { let h: *u8="0123456789abcdef" as *u8; let o: *u8=sys_mmap(6); o[0]=48 as u8; o[1]=120 as u8; o[2]=h[(v>>12)&0xF] as u8; o[3]=h[(v>>8)&0xF] as u8; o[4]=h[(v>>4)&0xF] as u8; o[5]=h[v&0xF] as u8; sys_write(1,o,6); return 0 }
21func slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
22func sockaddr(out: *u8, ip: i64, port: i64) -> i64 {
23 out[0]=(ND_AF_INET&0xff) as u8; out[1]=((ND_AF_INET>>8)&0xff) as u8
24 out[2]=((port>>8)&0xff) as u8; out[3]=(port&0xff) as u8
25 out[4]=((ip>>24)&0xff) as u8; out[5]=((ip>>16)&0xff) as u8; out[6]=((ip>>8)&0xff) as u8; out[7]=(ip&0xff) as u8
26 var i: i64=8; while i<16 { out[i]=0 as u8; i=i+1 } return 16
27}
28func ap(out: *u8, o: i64, b: i64) -> i64 { out[o]=b as u8; return o+1 }
29// broad TLS 1.2 ClientHello with SNI=host (offers ECDSA+RSA ciphers, x25519/p256/p384, common sig-algs)
30func mkhello(host: *u8, hl: i64, out: *u8) -> i64 {
31 var o: i64=0
32 o=ap(out,o,0x16); o=ap(out,o,0x03); o=ap(out,o,0x01) // record: handshake, legacy 0x0301
33 let recpos: i64=o; o=o+2
34 o=ap(out,o,0x01) // handshake: client_hello
35 let hspos: i64=o; o=o+3
36 o=ap(out,o,0x03); o=ap(out,o,0x03) // client_version TLS 1.2
37 var i: i64=0; while i<32 { o=ap(out,o,0x41+i); i=i+1 } // random (fixed -- diagnostic only)
38 o=ap(out,o,0x00) // session_id len 0
39 // cipher suites (13 = 26 bytes): ECDHE-ECDSA/RSA GCM+CHACHA+CBC, plus AES128/256, plus 3DES-ish 000A
40 o=ap(out,o,0x00); o=ap(out,o,26)
41 let cs: *u8 = "\xC0\x2B\xC0\x2C\xC0\x2F\xC0\x30\xCC\xA9\xCC\xA8\xC0\x13\xC0\x14\x00\x9C\x00\x9D\x00\x2F\x00\x35\x00\x0A" as *u8
42 i=0; while i<26 { o=ap(out,o,cs[i] as i64); i=i+1 }
43 o=ap(out,o,0x01); o=ap(out,o,0x00) // compression: null
44 let extpos: i64=o; o=o+2
45 let extstart: i64=o
46 // server_name
47 o=ap(out,o,0x00); o=ap(out,o,0x00)
48 o=ap(out,o,((hl+5)>>8)&0xff); o=ap(out,o,(hl+5)&0xff)
49 o=ap(out,o,((hl+3)>>8)&0xff); o=ap(out,o,(hl+3)&0xff)
50 o=ap(out,o,0x00)
51 o=ap(out,o,(hl>>8)&0xff); o=ap(out,o,hl&0xff)
52 i=0; while i<hl { o=ap(out,o,host[i] as i64); i=i+1 }
53 // supported_groups: x25519, secp256r1, secp384r1
54 let sg: *u8 = "\x00\x0A\x00\x08\x00\x06\x00\x1D\x00\x17\x00\x18" as *u8
55 i=0; while i<12 { o=ap(out,o,sg[i] as i64); i=i+1 }
56 // ec_point_formats: uncompressed
57 let ec: *u8 = "\x00\x0B\x00\x02\x01\x00" as *u8
58 i=0; while i<6 { o=ap(out,o,ec[i] as i64); i=i+1 }
59 // signature_algorithms: ecdsa256/384, rsa_pss256/384, rsa_pkcs256/384, rsa_sha1
60 let sa2: *u8 = "\x00\x0D\x00\x10\x00\x0E\x04\x03\x05\x03\x08\x04\x08\x05\x04\x01\x05\x01\x02\x01" as *u8
61 i=0; while i<20 { o=ap(out,o,sa2[i] as i64); i=i+1 }
62 let extend: i64=o
63 let extlen: i64=extend-extstart
64 out[extpos]=((extlen>>8)&0xff) as u8; out[extpos+1]=(extlen&0xff) as u8
65 let hslen: i64=o-(hspos+3)
66 out[hspos]=((hslen>>16)&0xff) as u8; out[hspos+1]=((hslen>>8)&0xff) as u8; out[hspos+2]=(hslen&0xff) as u8
67 let reclen: i64=o-(recpos+2)
68 out[recpos]=((reclen>>8)&0xff) as u8; out[recpos+1]=(reclen&0xff) as u8
69 return o
70}
71
72func main(argc: i64, argv: *i64) -> i64 {
73 if argc < 2 { dw("usage: nx_net_diag <host>\n" as *u8); return 1 }
74 let host: *u8 = argv[1] as *u8
75 let hl: i64 = slen(host)
76 let now: i64 = sys_now_realtime_sec()
77 dw("=== nx_net_diag "); dw(host); dw(" ===\n" as *u8)
78
79 // LAYER 1: DNS
80 let r: *DnsResolveResult = nx_dns_resolve_default(host, hl, now)
81 if r.verdict != NX_DNS_R_OK { dw("[1 DNS ] FAIL (verdict="); dn(r.verdict); dw(") -> hostname does not resolve\n" as *u8); return 2 }
82 let ip: i64 = r.ipv4_packed
83 if ip == 0 { dw("[1 DNS ] FAIL (no A record)\n" as *u8); return 2 }
84 dw("[1 DNS ] OK "); dn((ip>>24)&0xff); dw("."); dn((ip>>16)&0xff); dw("."); dn((ip>>8)&0xff); dw("."); dn(ip&0xff); dw("\n" as *u8)
85
86 // LAYER 2: TCP
87 let fd: i64 = sys_socket(ND_AF_INET, ND_SOCK_STREAM, 0)
88 if fd < 0 { dw("[2 TCP ] socket() FAIL\n" as *u8); return 3 }
89 sys_set_socket_timeout(fd, 8)
90 let sa: *u8 = sys_mmap(16); sockaddr(sa, ip, 443)
91 let t0: i64 = sys_now_ms()
92 let cr: i64 = nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS)
93 if cr < 0 { dw("[2 TCP ] connect :443 FAIL (rc="); dn(cr); dw(") -> port filtered / host down / firewall\n" as *u8); sys_close(fd); return 3 }
94 dw("[2 TCP ] OK :443 connected in "); dn(sys_now_ms()-t0); dw("ms\n" as *u8)
95
96 // LAYER 3: TLS -- send a broad browser-like ClientHello, read the RAW first response
97 let hello: *u8 = sys_mmap(ND_MAGIC_1024)
98 let hlen: i64 = mkhello(host, hl, hello)
99 sys_write(fd, hello, hlen)
100 let resp: *u8 = sys_mmap(ND_MAGIC_16384)
101 let n: i64 = sys_read(fd, resp, ND_MAGIC_16384)
102 sys_close(fd)
103 if n <= 0 {
104 dw("[3 TLS ] SILENT DROP -- server returned NOTHING to our broad ClientHello (read="); dn(n); dw(")\n" as *u8)
105 dw(" => NOT a network block (DNS+TCP were fine). The server/CDN dropped us at the TLS layer\n" as *u8)
106 dw(" based on our ClientHello FINGERPRINT (anti-bot). A real browser's hello is accepted.\n" as *u8)
107 return 4
108 }
109 let ct: i64 = resp[0] as i64
110 if ct == 22 {
111 // ServerHello: [16][ver:2][len:2] | [02][hslen:3][ver:2][random:32][sidlen][sid][cipher:2]...
112 let shver: i64 = (resp[9] as i64)*256 + (resp[10] as i64)
113 let sidlen: i64 = resp[43] as i64
114 let cpos: i64 = 44 + sidlen
115 let cipher: i64 = (resp[cpos] as i64)*256 + (resp[cpos+1] as i64)
116 dw("[3 TLS ] ServerHello RECEIVED ("); dn(n); dw(" bytes) -- the server ANSWERS our hello!\n" as *u8)
117 dw(" chosen version="); dhx(shver); dw(" chosen cipher="); dhx(cipher); dw("\n" as *u8)
118 dw(" => the block is DOWNSTREAM in our client (cipher/cert/parse), NOT the hello. Fix: support this cipher/version.\n" as *u8)
119 return 0
120 }
121 if ct == 21 {
122 let level: i64 = resp[5] as i64
123 let desc: i64 = resp[6] as i64
124 dw("[3 TLS ] ALERT level="); dn(level); dw(" desc="); dn(desc); dw(" = " as *u8)
125 if desc == 40 { dw("handshake_failure (no common cipher/params)" as *u8) }
126 else { if desc == 70 { dw("protocol_version (server refuses our TLS version)" as *u8) }
127 else { if desc == 71 { dw("insufficient_security (server wants stronger ciphers)" as *u8) }
128 else { if desc == 47 { dw("illegal_parameter (malformed/unacceptable field)" as *u8) }
129 else { if desc == 112 { dw("unrecognized_name (SNI not served here)" as *u8) }
130 else { dw("(see RFC 8446 alert registry)" as *u8) } } } } }
131 dw("\n => the server EXPLICITLY refused negotiation -- adjust our hello to match.\n" as *u8)
132 return 5
133 }
134 dw("[3 TLS ] unexpected first byte "); dhx(ct); dw(" (not handshake/alert) -- garbage or non-TLS response\n" as *u8)
135 return 6
136}