code wiki / (root) / nx_net_diag.nx

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}