code wiki / _hdl_build / nx_socks5_client_gate.nx
nx_socks5_client_gate.nx source
↩ module page · 216 lines · 12354 B
1// nx_socks5_client_gate.nx -- GATE for the sovereign SOCKS5 client + the anon transport interface.
2// TWO proofs, fail-closed:
3// PART A RFC 1928 KNOWN-ANSWER TEST (pure codec, NO network): greeting + CONNECT request bytes are
4// byte-exact vs the spec; replies parse; and every negative control (wrong version, REJECT
5// method, REP!=0 refusal, truncation, bad ATYP) is REFUSED. This is the liar-kill core.
6// PART B LIVE LOOPBACK TUNNEL: fork a mock SOCKS5 server on 127.0.0.1, drive the REAL s5_connect
7// through it via anon_connect(ANON_SOCKS5,...), and prove the returned fd carries app bytes
8// end-to-end (echo round-trip). Closes "codec correct" -> "the live socket path works".
9// Plus HONESTY invariants: anon_is_anonymizing(SOCKS5)==0 and anon_connect(ANON_TOR)->NOTIMPL --
10// the interface never pretends a bare proxy is anonymity nor fakes an unbuilt Tor backend.
11// GREEN iff every row passes. NO external network call (loopback only). Durable
12// knowledge/status/socks5_client_gate.log. license_tier: ORIGINAL expect_exit: 0
13import "nx_syscalls.nx"
14import "nx_socks5_client.nx"
15import "nx_anon_transport.nx"
16
17func g_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
18func g_n(v: i64) -> i64 { var m: i64=v; if m<0{g_w("-" as *u8);m=0-m} let t:*u8=sys_mmap(24); 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} let o:*u8=sys_mmap(24); var i:i64=0; while i<k{o[i]=t[k-1-i];i=i+1} sys_write(1,o,k); return 0 }
19func g_fw(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
20func g_fn(fd: i64, v: i64) -> i64 { var m: i64=v; let t:*u8=sys_mmap(24); 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} let o:*u8=sys_mmap(24); var i:i64=0; while i<k{o[i]=t[k-1-i];i=i+1} sys_write(fd,o,k); return 0 }
21
22// record one gate row: bump total, bump pass iff ok==1, print OK/FAIL.
23func g_row(id: *u8, ok: i64, pass: *i64, tot: *i64) -> i64 {
24 tot[0] = tot[0] + 1
25 g_w(" " as *u8); g_w(id)
26 if ok == 1 { g_w(": OK\n" as *u8); pass[0] = pass[0] + 1 } else { g_w(": FAIL\n" as *u8) }
27 return 0
28}
29
30// nap `ms` milliseconds via poll(NULL,0,ms) -- the sovereign bounded wait (no busy spin).
31func g_nap(ms: i64) -> i64 { sys_poll(0 as *u8, 0, ms); return 0 }
32
33// byte-compare buf[0..n) vs exp[0..n); 1 if identical, else 0.
34func g_bytes_eq(buf: *u8, exp: *u8, n: i64) -> i64 { var i: i64=0; while i<n { if buf[i]!=exp[i] { return 0 } i=i+1 } return 1 }
35
36// ---------- PART A: RFC 1928 codec known-answer test ----------
37func part_a(pass: *i64, tot: *i64) -> i64 {
38 g_w("-- PART A: RFC 1928 codec KAT (byte-exact + fail-closed negatives) --\n" as *u8)
39
40 // T1 greeting bytes == 05 01 00
41 let gb: *u8 = sys_mmap(8); let gn: i64 = s5_build_greeting(gb)
42 let ge: *u8 = sys_mmap(8); ge[0]=5 as u8; ge[1]=1 as u8; ge[2]=0 as u8
43 var ok: i64 = 0; if gn==3 { if g_bytes_eq(gb, ge, 3)==1 { ok=1 } }
44 g_row("T1 greeting bytes == 05 01 00" as *u8, ok, pass, tot)
45
46 // T2 method reply 05 00 -> no-auth (0)
47 let m1: *u8 = sys_mmap(4); m1[0]=5 as u8; m1[1]=0 as u8
48 ok = (s5_parse_method_reply(m1, 2) == 0) as i64
49 g_row("T2 method reply 05 00 -> no-auth selected" as *u8, ok, pass, tot)
50
51 // T3 method reply 05 FF -> REJECT (fail-closed)
52 let m2: *u8 = sys_mmap(4); m2[0]=5 as u8; m2[1]=0xff as u8
53 ok = (s5_parse_method_reply(m2, 2) < 0) as i64
54 g_row("T3 method reply 05 FF (no acceptable methods) -> REFUSED" as *u8, ok, pass, tot)
55
56 // T4 wrong version 04 00 -> refused
57 let m3: *u8 = sys_mmap(4); m3[0]=4 as u8; m3[1]=0 as u8
58 ok = (s5_parse_method_reply(m3, 2) < 0) as i64
59 g_row("T4 method reply wrong version (04) -> REFUSED" as *u8, ok, pass, tot)
60
61 // T5 truncated method reply (1 byte) -> refused
62 let m4: *u8 = sys_mmap(4); m4[0]=5 as u8
63 ok = (s5_parse_method_reply(m4, 1) < 0) as i64
64 g_row("T5 truncated method reply (1 byte) -> REFUSED" as *u8, ok, pass, tot)
65
66 // T6 CONNECT domain example.com:443 == 05 01 00 03 0B <example.com> 01 BB
67 let cb: *u8 = sys_mmap(64); let cn: i64 = s5_build_connect_domain("example.com" as *u8, 11, 443, cb)
68 let ce: *u8 = sys_mmap(64)
69 ce[0]=5 as u8; ce[1]=1 as u8; ce[2]=0 as u8; ce[3]=3 as u8; ce[4]=11 as u8
70 ce[5]=101 as u8; ce[6]=120 as u8; ce[7]=97 as u8; ce[8]=109 as u8; ce[9]=112 as u8; ce[10]=108 as u8; ce[11]=101 as u8; ce[12]=46 as u8; ce[13]=99 as u8; ce[14]=111 as u8; ce[15]=109 as u8
71 ce[16]=1 as u8; ce[17]=0xbb as u8 // port 443 = 0x01BB
72 ok = 0; if cn==18 { if g_bytes_eq(cb, ce, 18)==1 { ok=1 } }
73 g_row("T6 CONNECT domain example.com:443 byte-exact (incl port 01BB)" as *u8, ok, pass, tot)
74
75 // T7 CONNECT reply success, ATYP=IPv4 -> parsed OK
76 let r1: *u8 = sys_mmap(16); var i: i64=0; while i<10 { r1[i]=0 as u8; i=i+1 } r1[0]=5 as u8; r1[3]=1 as u8
77 ok = (s5_parse_connect_reply(r1, 10) == 0) as i64
78 g_row("T7 CONNECT reply success (ATYP=IPv4) -> parsed OK" as *u8, ok, pass, tot)
79
80 // T8 CONNECT reply REP=5 (proxy refused) -> refused
81 let r2: *u8 = sys_mmap(16); i=0; while i<10 { r2[i]=0 as u8; i=i+1 } r2[0]=5 as u8; r2[1]=5 as u8; r2[3]=1 as u8
82 ok = (s5_parse_connect_reply(r2, 10) < 0) as i64
83 g_row("T8 CONNECT reply REP=5 (proxy refused) -> REFUSED" as *u8, ok, pass, tot)
84
85 // T9 CONNECT reply success, ATYP=domain (len-prefixed) -> parsed OK ; total = 4+1+5+2 = 12
86 let r3: *u8 = sys_mmap(32); i=0; while i<12 { r3[i]=0 as u8; i=i+1 } r3[0]=5 as u8; r3[3]=3 as u8; r3[4]=5 as u8
87 ok = (s5_parse_connect_reply(r3, 12) == 0) as i64
88 g_row("T9 CONNECT reply success (ATYP=domain, len-prefixed) -> parsed OK" as *u8, ok, pass, tot)
89
90 // T10 truncated CONNECT reply (ATYP=IPv4 but only 6 bytes) -> refused
91 let r4: *u8 = sys_mmap(16); i=0; while i<6 { r4[i]=0 as u8; i=i+1 } r4[0]=5 as u8; r4[3]=1 as u8
92 ok = (s5_parse_connect_reply(r4, 6) < 0) as i64
93 g_row("T10 truncated CONNECT reply -> REFUSED" as *u8, ok, pass, tot)
94
95 // T11 CONNECT IPv4 127.0.0.1:8080 == 05 01 00 01 7F 00 00 01 1F 90
96 let ip: *u8 = sys_mmap(4); ip[0]=127 as u8; ip[1]=0 as u8; ip[2]=0 as u8; ip[3]=1 as u8
97 let ib: *u8 = sys_mmap(16); let iln: i64 = s5_build_connect_ipv4(ip, 8080, ib)
98 let ie: *u8 = sys_mmap(16); ie[0]=5 as u8; ie[1]=1 as u8; ie[2]=0 as u8; ie[3]=1 as u8; ie[4]=127 as u8; ie[5]=0 as u8; ie[6]=0 as u8; ie[7]=1 as u8; ie[8]=0x1f as u8; ie[9]=0x90 as u8
99 ok = 0; if iln==10 { if g_bytes_eq(ib, ie, 10)==1 { ok=1 } }
100 g_row("T11 CONNECT IPv4 127.0.0.1:8080 byte-exact (port 1F90)" as *u8, ok, pass, tot)
101
102 // T12 HONESTY: a bare SOCKS5 proxy is NOT anonymity
103 ok = (anon_is_anonymizing(ANON_SOCKS5) == 0) as i64
104 g_row("T12 HONESTY anon_is_anonymizing(SOCKS5)==0 (proxy alone is not anonymity)" as *u8, ok, pass, tot)
105
106 // T13 the interface routes ANON_TOR to NOTIMPL (honest stub, never a silent fake)
107 let ti: *u8 = sys_mmap(4); ti[0]=1 as u8
108 ok = (anon_connect(ANON_TOR, ti, 9050, "x" as *u8, 1, 443) == ANON_E_NOTIMPL) as i64
109 g_row("T13 anon_connect(ANON_TOR) -> NOTIMPL (no fake anonymity)" as *u8, ok, pass, tot)
110 return 0
111}
112
113// ---------- mock SOCKS5 server (child) ----------
114// Speaks just enough RFC 1928 to accept our client: no-auth negotiation, CONNECT (any ATYP), success
115// reply, then ONE echo round. Proves the REAL client drives a real socket. Fails closed on bad input.
116func mock_socks5_server(lfd: i64) -> i64 {
117 let cfd: i64 = sys_accept(lfd)
118 if cfd < 0 { return 1 }
119 s5_set_rcv_timeout(cfd, 5)
120 // greeting: VER NMETHODS METHODS... (read 2 header bytes, then NMETHODS method bytes)
121 let hdr: *u8 = sys_mmap(8)
122 if s5_read_full(cfd, hdr, 2) < 0 { sys_close(cfd); return 1 }
123 let nm: i64 = hdr[1] as i64
124 let methods: *u8 = sys_mmap(260)
125 if nm > 0 { if s5_read_full(cfd, methods, nm) < 0 { sys_close(cfd); return 1 } }
126 // reply: select no-auth -> 05 00
127 let mr: *u8 = sys_mmap(4); mr[0]=5 as u8; mr[1]=0 as u8
128 sys_write(cfd, mr, 2)
129 // CONNECT request: VER CMD RSV ATYP (4), then addr by ATYP, then 2 port bytes.
130 let req: *u8 = sys_mmap(512)
131 if s5_read_full(cfd, req, 4) < 0 { sys_close(cfd); return 1 }
132 let atyp: i64 = req[3] as i64
133 if atyp == 3 {
134 if s5_read_full(cfd, (req as i64 + 4) as *u8, 1) < 0 { sys_close(cfd); return 1 }
135 let dl: i64 = req[4] as i64
136 if s5_read_full(cfd, (req as i64 + 5) as *u8, dl + 2) < 0 { sys_close(cfd); return 1 }
137 } else {
138 var addrn: i64 = 0
139 if atyp == 1 { addrn = 4 }
140 if atyp == 4 { addrn = 16 }
141 if s5_read_full(cfd, (req as i64 + 4) as *u8, addrn + 2) < 0 { sys_close(cfd); return 1 }
142 }
143 // reply success, bound 0.0.0.0:0 (ATYP=IPv4) -> 05 00 00 01 00 00 00 00 00 00
144 let cr: *u8 = sys_mmap(16); var i: i64=0; while i<10 { cr[i]=0 as u8; i=i+1 } cr[0]=5 as u8; cr[3]=1 as u8
145 sys_write(cfd, cr, 10)
146 // echo one app payload back through the tunnel.
147 let app: *u8 = sys_mmap(1024)
148 let an: i64 = sys_read(cfd, app, 1024)
149 if an > 0 { sys_write(cfd, app, an) }
150 sys_close(cfd)
151 return 0
152}
153
154func main() -> i64 {
155 let pass: *i64 = sys_mmap(8) as *i64; pass[0]=0
156 let tot: *i64 = sys_mmap(8) as *i64; tot[0]=0
157 g_w("=== NX-SOCKS5-CLIENT GATE (RFC 1928 CONNECT: byte-exact KAT + live loopback tunnel) ===\n" as *u8)
158
159 part_a(pass, tot)
160
161 g_w("-- PART B: live loopback -- real s5_connect through a mock SOCKS5 server on 127.0.0.1 --\n" as *u8)
162 let port: i64 = 20600 + (sys_now_ms() % 800) // clock-derived port avoids rerun collisions
163
164 // bind+listen in the PARENT (queues our connect in the backlog -> no bind race), then fork the server.
165 let addr: *u8 = sys_mmap(16)
166 addr[0]=2 as u8; addr[1]=0 as u8; addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8
167 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8
168 var z: i64=8; while z<16 { addr[z]=0 as u8; z=z+1 }
169 let lfd: i64 = sys_socket(2, 1, 0)
170 let optv: *u8 = sys_mmap(4); optv[0]=1 as u8; optv[1]=0 as u8; optv[2]=0 as u8; optv[3]=0 as u8
171 sys_setsockopt(lfd, 1, 2, optv, 4) // SO_REUSEADDR
172 var bound: i64 = 0
173 if sys_bind(lfd, addr, 16) == 0 { if sys_listen(lfd, 8) == 0 { bound = 1 } }
174 if bound == 0 {
175 g_row("PART B loopback bind/listen 127.0.0.1" as *u8, 0, pass, tot)
176 } else {
177 let pid: i64 = sys_fork()
178 if pid == 0 {
179 mock_socks5_server(lfd)
180 sys_close(lfd)
181 sys_exit(0)
182 return 0
183 }
184 sys_close(lfd)
185 g_nap(30) // let the child reach accept() (belt-and-braces)
186 let pip: *u8 = sys_mmap(4); pip[0]=127 as u8; pip[1]=0 as u8; pip[2]=0 as u8; pip[3]=1 as u8
187 // connect the TARGET "media.example":443 THROUGH the proxy at 127.0.0.1:port, via the interface.
188 let fd: i64 = anon_connect(ANON_SOCKS5, pip, port, "media.example" as *u8, 13, 443)
189 var conn_ok: i64 = 0; if fd >= 0 { conn_ok = 1 }
190 g_row("PART B anon_connect(SOCKS5) returns a live tunnel fd" as *u8, conn_ok, pass, tot)
191
192 var echo_ok: i64 = 0
193 if fd >= 0 {
194 let msg: *u8 = "PING-NISHI-ANON" as *u8
195 anon_send(fd, msg, 15)
196 let rb: *u8 = sys_mmap(64)
197 let rn: i64 = s5_read_full(fd, rb, 15)
198 if rn == 15 { if g_bytes_eq(rb, msg, 15)==1 { echo_ok = 1 } }
199 anon_close(fd)
200 }
201 g_row("PART B tunnel carries app bytes end-to-end (echo round-trip matches)" as *u8, echo_ok, pass, tot)
202
203 nx_kill(pid, 9)
204 let st: *i64 = sys_mmap(8) as *i64
205 sys_wait4(pid, st, 0)
206 }
207
208 g_w("SOCKS5-CLIENT-GATE rows=" as *u8); g_n(tot[0]); g_w(" pass=" as *u8); g_n(pass[0])
209 if pass[0] == tot[0] {
210 g_w(" verdict=GREEN\n" as *u8)
211 let lg: i64 = sys_openat_append("knowledge/status/socks5_client_gate.log" as *u8, 0x1a4)
212 if lg >= 0 { g_fw(lg, "SOCKS5-CLIENT-GATE pass=" as *u8); g_fn(lg, pass[0]); g_fw(lg, "/" as *u8); g_fn(lg, tot[0]); g_fw(lg, " verdict=GREEN rfc1928=byte-exact loopback=live-tunnel epoch=" as *u8); g_fn(lg, sys_now_realtime_sec()); g_fw(lg, "\n" as *u8); sys_close(lg) }
213 sys_exit(0); return 0
214 }
215 g_w(" verdict=RED\n" as *u8); sys_exit(1); return 1
216}