nx_stun_client_gate.nx source
↩ module page · 133 lines · 6871 B
1// nx_stun_client_gate.nx -- proves the sovereign STUN client two ways:
2// PART A (pure): sc_find_xma walks a real STUN response (RFC 5769 2.2, where XOR-MAPPED-ADDRESS is NOT the
3// first attribute) and locates it, decoding 192.0.2.1:32853; a response with no XMA returns -1; the
4// dotted-quad parser accepts valid IPs and rejects malformed ones.
5// PART B (LIVE, real UDP): a fork loopback -- child = an inline reflexive responder, parent = sc_query --
6// proves sc_query performs a real Binding request/response round-trip and RECOVERS a reflexive address.
7// The server side is inlined from nx_stun primitives (not imported) to avoid a diamond import of nx_stun.
8// license_tier: ORIGINAL expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_stun_client.nx"
11
12func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
13func gn(v: i64) -> i64 {
14 let b: *u8=sys_mmap(28); var m: i64=v; if m<0 {sys_write(1,"-" as *u8,1); m=0-m}
15 let t: *u8=sys_mmap(28); var k: i64=0; if m==0 {t[0]=48 as u8;k=1} while m>0 {t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}
16 var i: i64=0; while i<k {b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0
17}
18func gck(pass: i64, name: *u8, fails: *i64) -> i64 {
19 if pass==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8); fails[0]=fails[0]+1 }
20 gw(name); gw("\n" as *u8); return 0
21}
22func hxv(c: i64) -> i64 {
23 if c>=48 { if c<=57 { return c-48 } }
24 if c>=97 { if c<=102 { return c-87 } }
25 if c>=65 { if c<=70 { return c-55 } }
26 return 0
27}
28func hexdec(hex: *u8, nbytes: i64, out: *u8) -> i64 { var i: i64=0; while i<nbytes { out[i]=((hxv(hex[i*2] as i64)<<4) | hxv(hex[i*2+1] as i64)) as u8; i=i+1 } return 0 }
29
30// inline reflexive responder (server child): build header + XOR-MAPPED-ADDRESS(src) + FINGERPRINT.
31func g_respond(buf: *u8, txid: *u8, port: i64, addr: i64) -> i64 {
32 var o: i64 = st_write_header(buf, STUN_BINDING_RESPONSE, 0, txid)
33 st_put16(buf, o, STUN_ATTR_XOR_MAPPED_ADDRESS); st_put16(buf, o+2, 8)
34 let xp: *u8 = (buf as i64 + o + 4) as *u8
35 st_xma_encode(xp, port, addr)
36 o = o + 12
37 st_put16(buf, 2, (o-20)+8)
38 let fp: i64 = st_fingerprint(buf, o)
39 st_put16(buf, o, STUN_ATTR_FINGERPRINT); st_put16(buf, o+2, 4); st_put32(buf, o+4, fp)
40 return o + 8
41}
42
43const CGT_SPORT: i64 = 53230
44
45func main() -> i64 {
46 let fails: *i64 = sys_mmap(16) as *i64
47 fails[0]=0
48 gw("=== nx_stun_client_gate -- sovereign STUN client (pure XMA-walk + live UDP query) ===\n" as *u8)
49
50 // ---- PART A ----
51 // A1: sc_find_xma on the RFC 5769 2.2 response (XMA is the SECOND attribute, after SOFTWARE)
52 let resp: *u8 = sys_mmap(128)
53 hexdec("0101003c2112a442b7e7a701bc34d686fa87dfae8022000b7465737420766563746f7220002000080001a147e112a643000800142b91f599fd9e90c38c7489f92af9ba53f06be7d780280004c07d4c96" as *u8, 80, resp)
54 var t1: i64=1
55 let xoff: i64 = sc_find_xma(resp, 80)
56 if xoff != 40 { t1=0 }
57 if xoff >= 0 {
58 let xma: *u8 = (resp as i64 + xoff) as *u8
59 if st_xma_port(xma) != 32853 { t1=0 }
60 if st_xma_addr(xma) != 0xC0000201 { t1=0 }
61 }
62 gck(t1, "A1 sc_find_xma locates XOR-MAPPED-ADDRESS past SOFTWARE -> 192.0.2.1:32853 (RFC 5769 2.2)" as *u8, fails)
63
64 // A2: a response with no XMA (header only) -> -1
65 var t2: i64=1
66 let bare: *u8 = sys_mmap(32)
67 let txz: *u8 = sys_mmap(16); var zi: i64=0; while zi<12 { txz[zi]=0 as u8; zi=zi+1 }
68 st_write_header(bare, STUN_BINDING_RESPONSE, 0, txz)
69 if sc_find_xma(bare, 20) != (0-1) { t2=0 }
70 gck(t2, "A2 sc_find_xma returns -1 when no XOR-MAPPED-ADDRESS is present" as *u8, fails)
71
72 // A3: dotted-quad parser
73 var t3: i64=1
74 let oct: *i64 = sys_mmap(64) as *i64
75 if cp_parse_ip("203.0.113.7" as *u8, oct)!=1 { t3=0 }
76 if oct[0]!=203 { t3=0 }
77 if oct[1]!=0 { t3=0 }
78 if oct[2]!=113 { t3=0 }
79 if oct[3]!=7 { t3=0 }
80 if cp_parse_ip("1.2.3" as *u8, oct)!=0 { t3=0 } // too few octets
81 if cp_parse_ip("256.0.0.1" as *u8, oct)!=0 { t3=0 } // octet out of range
82 if cp_parse_ip("1.2.3.4.5" as *u8, oct)!=0 { t3=0 } // too many
83 gck(t3, "A3 dotted-quad parser accepts valid IPv4 + rejects malformed" as *u8, fails)
84
85 // ---- PART B: LIVE UDP query (fork loopback; bind server BEFORE fork) ----
86 var t4: i64=1
87 let ssa: *u8 = sys_mmap(16)
88 ssa[0]=2 as u8; ssa[1]=0 as u8; ssa[2]=((CGT_SPORT>>8)&0xff) as u8; ssa[3]=(CGT_SPORT&0xff) as u8
89 ssa[4]=127 as u8; ssa[5]=0 as u8; ssa[6]=0 as u8; ssa[7]=1 as u8
90 var pk: i64=8; while pk<16 { ssa[pk]=0 as u8; pk=pk+1 }
91 let sfd: i64 = sys_socket(AF_INET, SOCK_DGRAM, 0)
92 var bound: i64=0
93 if sfd>=0 { sys_set_socket_timeout(sfd, 3); if sys_bind(sfd, ssa, 16)>=0 { bound=1 } }
94 if bound==0 { gck(0, "B live bind server 127.0.0.1:53230" as *u8, fails) } else {
95 let pid: i64 = sys_fork()
96 if pid==0 {
97 // CHILD = inline reflexive responder
98 let rb: *u8 = sys_mmap(2048)
99 let rout: *u8 = sys_mmap(2048)
100 let csrc: *u8 = sys_mmap(16)
101 let cslen: *i64 = sys_mmap(16) as *i64; cslen[0]=16
102 let cn: i64 = sys_recvfrom(sfd, rb, 2048, 0, csrc, cslen)
103 if cn>=20 {
104 let sport: i64 = (((csrc[2] as i64)&0xff)<<8) | ((csrc[3] as i64)&0xff)
105 let saddr: i64 = (((csrc[4] as i64)&0xff)<<24) | (((csrc[5] as i64)&0xff)<<16) | (((csrc[6] as i64)&0xff)<<8) | ((csrc[7] as i64)&0xff)
106 let txid: *u8 = (rb as i64 + 8) as *u8
107 let rrn: i64 = g_respond(rout, txid, sport, saddr)
108 sys_sendto(sfd, rout, rrn, 0, csrc, cslen[0])
109 }
110 sys_close(sfd)
111 sys_exit(0)
112 }
113 // PARENT = sc_query against the loopback server
114 let oip: *i64 = sys_mmap(16) as *i64
115 let oport: *i64 = sys_mmap(16) as *i64
116 let rc: i64 = sc_query(127, 0, 0, 1, CGT_SPORT, oip, oport)
117 let st: *i64 = sys_mmap(16) as *i64
118 sys_wait4(pid, st, 0)
119 if rc != 0 { t4=0 }
120 if oip[0] != 0x7F000001 { t4=0 } // reflexive IP is our loopback
121 if oport[0] <= 0 { t4=0 } // and a real (ephemeral) port
122 gw(" [measure] LIVE sc_query rc=" as *u8); gn(rc); gw(" reflexive=" as *u8)
123 let a: i64 = oip[0]
124 gn((a>>24)&0xff); gw("." as *u8); gn((a>>16)&0xff); gw("." as *u8); gn((a>>8)&0xff); gw("." as *u8); gn(a&0xff); gw(":" as *u8); gn(oport[0]); gw("\n" as *u8)
125 gck(t4, "B4 LIVE UDP: sc_query round-trips a Binding request/response and recovers reflexive 127.0.0.1:<port>" as *u8, fails)
126 }
127
128 gw(" fails=" as *u8); gn(fails[0]); gw("\n" as *u8)
129 if fails[0]==0 { gw("VERDICT: verdict=GREEN (sovereign STUN client discovers reflexive addresses over real UDP)\n" as *u8); sys_exit(0) }
130 gw("VERDICT: verdict=RED\n" as *u8)
131 sys_exit(1)
132 return 1
133}