nx_pex_gate.nx source
↩ module page · 159 lines · 8674 B
1// nx_pex_gate.nx -- SOVEREIGN LOOPBACK gate for BEP-11 ut_pex: proves PEX rides the REAL BEP-10
2// extension channel over a real TCP socket (not just the deterministic parser, which nx_pex's baked
3// KAT already covers). A mock peer does the BEP-3 handshake (ext bit) + BEP-10 ext handshake
4// (advertising ut_pex) + sends a real ut_pex ext message carrying a compact peer list; the client
5// dispatches on the ext-id WE advertised, learns the peer's ut_pex id, and pex_parse_added's the
6// gossiped peers. This is the live-channel proof that, once connected to ONE peer, we harvest MORE.
7// license_tier: ORIGINAL
8//
9// module: nishi-core.torrent.pex_gate
10// depends: nishi-core.torrent.pex, nishi-core.torrent.peerwire
11import "nx_pex.nx"
12import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
13import "nx_peerwire.nx"
14import "nx_gate_verdict.nx"
15
16const PW_EXTENDED: i64 = 20
17
18func gx_w(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 }
19func gx_wn(fd: i64, v: i64) -> i64 {
20 let t: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) }
21 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 }
22 let b: *u8 = sys_mmap(28); var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 }; sys_write(fd, b, k); return 0
23}
24func gx_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
25func gx_sockaddr(sa: *u8, port: i64, i0: i64, i1: i64, i2: i64, i3: i64) -> i64 {
26 sa[0] = 2 as u8; sa[1] = 0 as u8
27 sa[2] = ((port >> 8) & 0xff) as u8; sa[3] = (port & 0xff) as u8
28 sa[4] = i0 as u8; sa[5] = i1 as u8; sa[6] = i2 as u8; sa[7] = i3 as u8
29 var k: i64 = 8; while k < 16 { sa[k] = 0 as u8; k = k + 1 }
30 return 0
31}
32func gx_read_n(fd: i64, buf: *u8, n: i64) -> i64 {
33 var got: i64 = 0
34 while got < n { let r: i64 = sys_read(fd, buf + got, n - got); if r <= 0 { return 0 - 1 } got = got + r }
35 return got
36}
37func gx_write_n(fd: i64, buf: *u8, n: i64) -> i64 {
38 var off: i64 = 0
39 while off < n { let w: i64 = sys_write(fd, buf + off, n - off); if w <= 0 { return 0 - 1 } off = off + w }
40 return n
41}
42func gx_read_msg(fd: i64, mb: *u8, cap: i64) -> i64 {
43 if gx_read_n(fd, mb, 4) != 4 { return 0 - 1 }
44 let blen: i64 = _pw_get_u32(mb, 0)
45 if blen == 0 { return 0 }
46 if blen > cap - 4 { return 0 - 1 }
47 if gx_read_n(fd, mb + 4, blen) != blen { return 0 - 1 }
48 return blen
49}
50// build extended message id=20: payload = [ext_id] + ben(benlen); EXPLICIT length (compact peer
51// bytes can contain NUL, so a strlen-based builder would truncate). returns total bytes.
52func px_build_ext(ext_id: i64, ben: *u8, benlen: i64, out: *u8) -> i64 {
53 let pl: *u8 = sys_mmap(4096)
54 pl[0] = ext_id as u8
55 var i: i64 = 0; while i < benlen { pl[1 + i] = ben[i]; i = i + 1 }
56 return nx_pw_build_msg(PW_EXTENDED, pl, 1 + benlen, out)
57}
58
59func main() -> i64 {
60 let PORT: i64 = 53441
61 let ih: *u8 = sys_mmap(20); var z: i64 = 0; while z < 20 { ih[z] = (z + 1) as u8; z = z + 1 }
62 let pidb: *u8 = sys_mmap(20); z = 0; while z < 20 { pidb[z] = 80 as u8; z = z + 1 }
63 let sa: *u8 = sys_mmap(16); gx_sockaddr(sa, PORT, 127, 0, 0, 1)
64
65 // the 3 peers the mock will gossip via PEX
66 let gips: *i64 = sys_mmap(8 * 8) as *i64
67 let gpts: *i64 = sys_mmap(8 * 8) as *i64
68 gips[0] = (11 << 24) | (22 << 16) | (33 << 8) | 44; gpts[0] = 12345
69 gips[1] = (99 << 24) | (88 << 16) | (77 << 8) | 66; gpts[1] = 6881
70 gips[2] = (203 << 24) | (0 << 16) | (113 << 8) | 7; gpts[2] = 51999
71
72 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
73 var bound: i64 = 0
74 if lfd >= 0 {
75 let one: *u8 = sys_mmap(4); one[0] = 1 as u8; one[1] = 0 as u8; one[2] = 0 as u8; one[3] = 0 as u8
76 sys_setsockopt(lfd, SOL_SOCKET, 2, one, 4)
77 sys_set_socket_timeout(lfd, 3)
78 if sys_bind(lfd, sa, 16) >= 0 { if sys_listen(lfd, 1) >= 0 { bound = 1 } }
79 }
80 if bound == 0 { gx_w(1, "PEX-GATE authored=organ bound=0 verdict=RED\n" as *u8); sys_exit(1); return 1 }
81
82 let pid: i64 = sys_fork()
83 if pid == 0 {
84 // ---- MOCK PEER ----
85 let afd: i64 = sys_accept(lfd)
86 if afd >= 0 {
87 sys_set_socket_timeout(afd, 3)
88 let hb: *u8 = sys_mmap(128); gx_read_n(afd, hb, 68)
89 let oh: *u8 = sys_mmap(128); nx_pw_build_handshake(ih, pidb, oh); oh[25] = (oh[25] as i64 | 0x10) as u8; gx_write_n(afd, oh, 68)
90 let cmb: *u8 = sys_mmap(512); gx_read_msg(afd, cmb, 512) // client ext handshake
91 // mock's own ext handshake: advertise ut_pex=7 (so the client can learn our id)
92 let mben: *u8 = "d1:md6:ut_pexi7eee" as *u8
93 let om: *u8 = sys_mmap(512); let oml: i64 = px_build_ext(0, mben, gx_len(mben), om); gx_write_n(afd, om, oml)
94 // mock's PEX message: ext-id = PEX_OUR_ID (the id the client advertised for incoming ut_pex)
95 let pdict: *u8 = sys_mmap(512); let pdl: i64 = pex_build_added(gips, gpts, 3, pdict)
96 let pm: *u8 = sys_mmap(512); let pml: i64 = px_build_ext(PEX_OUR_ID, pdict, pdl, pm); gx_write_n(afd, pm, pml)
97 sys_close(afd)
98 }
99 sys_close(lfd)
100 sys_exit(0)
101 }
102
103 // ---- CLIENT ----
104 let cfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
105 sys_set_socket_timeout(cfd, 3)
106 var conn_ok: i64 = 0
107 if nx_connect_bounded(cfd, sa, 16, NX_CONN_DEFAULT_MS) >= 0 { conn_ok = 1 }
108 var hs_ok: i64 = 0; var ext_ok: i64 = 0; var peer_id_ok: i64 = 0
109 var pex_count: i64 = 0; var p0_ok: i64 = 0; var p2_ok: i64 = 0
110 if conn_ok == 1 {
111 let ch: *u8 = sys_mmap(128); nx_pw_build_handshake(ih, pidb, ch); ch[25] = (ch[25] as i64 | 0x10) as u8; gx_write_n(cfd, ch, 68)
112 let ph: *u8 = sys_mmap(128)
113 if gx_read_n(cfd, ph, 68) == 68 {
114 let gih: *u8 = sys_mmap(20); let gpid: *u8 = sys_mmap(20)
115 if nx_pw_parse_handshake(ph, 68, gih, gpid) == 1 { hs_ok = 1 }
116 }
117 if hs_ok == 1 {
118 // advertise ut_metadata=1 + ut_pex=PEX_OUR_ID(3); keys sorted (ut_metadata < ut_pex)
119 let cben: *u8 = "d1:md11:ut_metadatai1e6:ut_pexi3eee" as *u8
120 let cm: *u8 = sys_mmap(512); let cml: i64 = px_build_ext(0, cben, gx_len(cben), cm); gx_write_n(cfd, cm, cml)
121 // msg1: peer ext handshake (id20 ext-id0) -> learn peer ut_pex id
122 let mb1: *u8 = sys_mmap(512); let bl1: i64 = gx_read_msg(cfd, mb1, 512)
123 if bl1 > 2 { if nx_pw_msg_id(mb1) == PW_EXTENDED { if (mb1[5] as i64) == 0 {
124 ext_ok = 1
125 if pex_peer_id(mb1, 6, 4 + bl1) == 7 { peer_id_ok = 1 }
126 } } }
127 // msg2: peer PEX message (id20 ext-id == PEX_OUR_ID) -> harvest gossiped peers
128 let mb2: *u8 = sys_mmap(512); let bl2: i64 = gx_read_msg(cfd, mb2, 512)
129 if bl2 > 2 { if nx_pw_msg_id(mb2) == PW_EXTENDED { if (mb2[5] as i64) == PEX_OUR_ID {
130 let oip: *i64 = sys_mmap(8 * 8) as *i64; let opt: *i64 = sys_mmap(8 * 8) as *i64
131 pex_count = pex_parse_added(mb2, 6, 4 + bl2, oip, opt, 8)
132 if pex_count == 3 {
133 if oip[0] == gips[0] { if opt[0] == gpts[0] { p0_ok = 1 } }
134 if oip[2] == gips[2] { if opt[2] == gpts[2] { p2_ok = 1 } }
135 }
136 } } }
137 }
138 }
139 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0); sys_close(cfd); sys_close(lfd)
140
141 gx_w(1, "PEX-GATE authored=organ conn_ok=" as *u8); gx_wn(1, conn_ok)
142 gx_w(1, " handshake_ok=" as *u8); gx_wn(1, hs_ok)
143 gx_w(1, " ext_ok=" as *u8); gx_wn(1, ext_ok)
144 gx_w(1, " peer_ut_pex_id_ok=" as *u8); gx_wn(1, peer_id_ok)
145 gx_w(1, " pex_count=" as *u8); gx_wn(1, pex_count)
146 gx_w(1, " peer0_ok=" as *u8); gx_wn(1, p0_ok)
147 gx_w(1, " peer2_ok=" as *u8); gx_wn(1, p2_ok)
148 var allok: i64 = 0
149 if conn_ok == 1 { if hs_ok == 1 { if ext_ok == 1 { if peer_id_ok == 1 { if pex_count == 3 { if p0_ok == 1 { if p2_ok == 1 { allok = 1 } } } } } } }
150 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
151 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
152 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
153 let ctr__dry: *i64 = gv_ctr()
154 ctr__dry[0] = allok
155 ctr__dry[1] = 1
156 let rc__dry: i64 = gv_verdict("PEX-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
157 sys_exit(rc__dry)
158 return rc__dry
159}