nx_pex.nx source
↩ module page · 183 lines · 9327 B
1// nx_pex.nx -- BEP-11 Peer Exchange (ut_pex): parse/build the compact peer lists that connected
2// peers gossip to each other over the BEP-10 extension channel. This is the high-value amplifier
3// for TRACKERLESS magnets + stuck (99.x%) swarms -- once we hold ONE peer, PEX hands us MORE peers
4// (often seeders the tracker/DHT never returned), directly attacking the operator's "no seeder /
5// can't finish" pain. Reuses the universal compact-6B/peer idiom (b0<<24|b1<<16|b2<<8|b3, port
6// b4<<8|b5) shared with nx_dht's value parse + the nx_bencode DECODE layer. Pure (no network):
7// "import strips main", so THIS file is BOTH the lib AND a baked-KAT self-gate (deterministic,
8// sovereign). The live BEP-10 round-trip is proven by nx_pex_gate. license_tier: ORIGINAL
9//
10// module: nishi-core.torrent.pex
11// depends: nishi-core.torrent.bencode
12import "nx_bencode.nx"
13import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
14const PEX_MAGIC_6881: i64 = 6881
15const PEX_MAGIC_51413: i64 = 51413
16const PEX_MAGIC_4096: i64 = 4096
17const PEX_MAGIC_6882: i64 = 6882
18
19// Our advertised ut_pex extension id (the id we put in OUR handshake m-dict so a peer uses it when
20// sending US a pex message). Receivers dispatch an incoming ext message on (mb[5] == PEX_OUR_ID).
21const PEX_OUR_ID: i64 = 3
22
23// copy NUL-terminated s into out at off; return new off. (s is a param -> safe to index; only
24// indexing a `const *u8` GLOBAL crashes nx_cc, not a param.)
25func px_puts(out: *u8, off: i64, s: *u8) -> i64 {
26 var i: i64 = 0
27 while s[i] != (0 as u8) { out[off + i] = s[i]; i = i + 1 }
28 return off + i
29}
30
31// write decimal v (>=0) into out at off; return new off.
32func px_putdec(out: *u8, off: i64, v: i64) -> i64 {
33 if v == 0 { out[off] = 48 as u8; return off + 1 }
34 let t: *u8 = sys_mmap(32)
35 var m: i64 = v
36 var k: i64 = 0
37 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
38 var i: i64 = 0
39 while i < k { out[off + i] = t[k - 1 - i]; i = i + 1 }
40 return off + k
41}
42
43// Build a BEP-11 PEX dict {added:<compact peers>} into out; returns total bytes written.
44// ips[i] packed b0<<24|b1<<16|b2<<8|b3 ; ports[i] in 0..65535. (added.f flags omitted in v1 --
45// peer discovery only needs the address list; flags are an optional refinement.)
46func pex_build_added(ips: *i64, ports: *i64, cnt: i64, out: *u8) -> i64 {
47 var o: i64 = 0
48 o = px_puts(out, o, "d5:added" as *u8) // dict open + key "added"
49 o = px_putdec(out, o, cnt * 6) // string length = 6 bytes/peer
50 out[o] = 58 as u8; o = o + 1 // ':'
51 var i: i64 = 0
52 while i < cnt {
53 let ip: i64 = ips[i]
54 let pt: i64 = ports[i]
55 out[o] = ((ip >> 24) & 0xff) as u8; o = o + 1
56 out[o] = ((ip >> 16) & 0xff) as u8; o = o + 1
57 out[o] = ((ip >> 8) & 0xff) as u8; o = o + 1
58 out[o] = (ip & 0xff) as u8; o = o + 1
59 out[o] = ((pt >> 8) & 0xff) as u8; o = o + 1
60 out[o] = (pt & 0xff) as u8; o = o + 1
61 i = i + 1
62 }
63 out[o] = 101 as u8; o = o + 1 // 'e' (dict close)
64 return o
65}
66
67// Parse compact peers from the `key` string of a bencoded PEX dict at [start,n) -> ips[]/ports[].
68// returns count (0 if key absent / not a string). OOB-safe (i+6<=l) + bounded (cnt<max). Used for
69// both "added" (new peers) and "dropped" (peers that left).
70func pex_parse_key(buf: *u8, start: i64, n: i64, key: *u8, keylen: i64, ips: *i64, ports: *i64, max: i64) -> i64 {
71 let ko: i64 = nx_bc_dict_get(buf, start, n, key, keylen)
72 if ko < 0 { return 0 }
73 let so: *i64 = sys_mmap(16) as *i64
74 let sl: *i64 = sys_mmap(16) as *i64
75 if nx_bc_str(buf, ko, n, so, sl) < 0 { return 0 }
76 let s: i64 = so[0]
77 let l: i64 = sl[0]
78 var cnt: i64 = 0
79 var i: i64 = 0
80 while i + 6 <= l {
81 if cnt < max {
82 let b0: i64 = buf[s + i] as i64
83 let b1: i64 = buf[s + i + 1] as i64
84 let b2: i64 = buf[s + i + 2] as i64
85 let b3: i64 = buf[s + i + 3] as i64
86 ips[cnt] = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
87 ports[cnt] = ((buf[s + i + 4] as i64) << 8) | (buf[s + i + 5] as i64)
88 cnt = cnt + 1
89 }
90 i = i + 6
91 }
92 return cnt
93}
94
95// convenience: parse the "added" (new peers) list.
96func pex_parse_added(buf: *u8, start: i64, n: i64, ips: *i64, ports: *i64, max: i64) -> i64 {
97 return pex_parse_key(buf, start, n, "added" as *u8, 5, ips, ports, max)
98}
99
100// Learn the peer's ut_pex id from their BEP-10 ext-handshake message (mb[6..] = bencoded, n=4+blen):
101// the id WE must use when SENDING a pex msg to them. returns id>0 or -1 if the peer lacks ut_pex.
102func pex_peer_id(mb: *u8, start: i64, n: i64) -> i64 {
103 let m_off: i64 = nx_bc_dict_get(mb, start, n, "m" as *u8, 1)
104 if m_off < 0 { return 0 - 1 }
105 let po: i64 = nx_bc_dict_get(mb, m_off, n, "ut_pex" as *u8, 6)
106 if po < 0 { return 0 - 1 }
107 let iv: *i64 = sys_mmap(16) as *i64
108 if nx_bc_int(mb, po, n, iv) < 0 { return 0 - 1 }
109 return iv[0]
110}
111
112// ---------------- baked KAT (deterministic, sovereign, no network) ----------------
113func px_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
114// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
115// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
116// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
117// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
118func px_wn(v: i64) -> i64 { nxi_out(v); return 0 }
119
120func main() -> i64 {
121 let ips: *i64 = sys_mmap(8 * 16) as *i64
122 let pts: *i64 = sys_mmap(8 * 16) as *i64
123 ips[0] = (1 << 24) | (2 << 16) | (3 << 8) | 4; pts[0] = PEX_MAGIC_6881
124 ips[1] = (10 << 24) | (20 << 16) | (30 << 8) | 40; pts[1] = PEX_MAGIC_51413
125 ips[2] = (255 << 24) | (0 << 16) | (0 << 8) | 1; pts[2] = 80
126 let oip: *i64 = sys_mmap(8 * 16) as *i64
127 let opt: *i64 = sys_mmap(8 * 16) as *i64
128
129 // T1: build 3 peers -> parse -> exact round-trip (build/parse symmetry)
130 let buf: *u8 = sys_mmap(PEX_MAGIC_4096)
131 let blen: i64 = pex_build_added(ips, pts, 3, buf)
132 let c1: i64 = pex_parse_added(buf, 0, blen, oip, opt, 16)
133 var t1: i64 = 0
134 if c1 == 3 { if oip[0] == ips[0] { if opt[0] == pts[0] { if oip[2] == ips[2] { if opt[2] == pts[2] { t1 = 1 } } } } }
135
136 // T2: parse INDEPENDENTLY-authored SPEC wire bytes (guards against build==parse-but-both-wrong)
137 // d5:added12:<1.2.3.4:6881><5.6.7.8:6882>e
138 let sb: *u8 = sys_mmap(64)
139 var so2: i64 = px_puts(sb, 0, "d5:added12:" as *u8)
140 sb[so2] = 1 as u8; sb[so2+1] = 2 as u8; sb[so2+2] = 3 as u8; sb[so2+3] = 4 as u8
141 sb[so2+4] = ((PEX_MAGIC_6881 >> 8) & 0xff) as u8; sb[so2+5] = (PEX_MAGIC_6881 & 0xff) as u8; so2 = so2 + 6
142 sb[so2] = 5 as u8; sb[so2+1] = 6 as u8; sb[so2+2] = 7 as u8; sb[so2+3] = 8 as u8
143 sb[so2+4] = ((PEX_MAGIC_6882 >> 8) & 0xff) as u8; sb[so2+5] = (PEX_MAGIC_6882 & 0xff) as u8; so2 = so2 + 6
144 sb[so2] = 101 as u8; so2 = so2 + 1
145 let c2: i64 = pex_parse_added(sb, 0, so2, oip, opt, 16)
146 var t2: i64 = 0
147 if c2 == 2 { if oip[0] == ((1<<24)|(2<<16)|(3<<8)|4) { if opt[0] == PEX_MAGIC_6881 { if oip[1] == ((5<<24)|(6<<16)|(7<<8)|8) { if opt[1] == PEX_MAGIC_6882 { t2 = 1 } } } } }
148
149 // T3: NEG -- valid dict without an "added" key -> 0 peers (no phantom peers)
150 let nb: *u8 = sys_mmap(32); let nbl: i64 = px_puts(nb, 0, "d4:spami1ee" as *u8)
151 let c3: i64 = pex_parse_added(nb, 0, nbl, oip, opt, 16)
152 var t3: i64 = 0; if c3 == 0 { t3 = 1 }
153
154 // T4: NEG -- "added" length NOT a multiple of 6 (8 bytes) -> floor to 1 peer, no OOB read
155 let ob: *u8 = sys_mmap(64); var oo: i64 = px_puts(ob, 0, "d5:added8:" as *u8)
156 var z: i64 = 0; while z < 8 { ob[oo + z] = (z + 1) as u8; z = z + 1 }; oo = oo + 8
157 ob[oo] = 101 as u8; oo = oo + 1
158 let c4: i64 = pex_parse_added(ob, 0, oo, oip, opt, 16)
159 var t4: i64 = 0; if c4 == 1 { if oip[0] == ((1<<24)|(2<<16)|(3<<8)|4) { t4 = 1 } }
160
161 // T5: bound -- parse 3-peer buffer with max=2 -> returns exactly 2 (no overflow past caller's array)
162 let c5: i64 = pex_parse_added(buf, 0, blen, oip, opt, 2)
163 var t5: i64 = 0; if c5 == 2 { t5 = 1 }
164
165 // T6: pex_peer_id learns the id from a handshake m-dict d1:md6:ut_pexi7eee -> 7 ; absent -> -1
166 let hb: *u8 = sys_mmap(64); let hbl: i64 = px_puts(hb, 0, "d1:md6:ut_pexi7eee" as *u8)
167 let pid: i64 = pex_peer_id(hb, 0, hbl)
168 let hb2: *u8 = sys_mmap(64); let hbl2: i64 = px_puts(hb2, 0, "d1:md11:ut_metadatai1eee" as *u8)
169 let pid2: i64 = pex_peer_id(hb2, 0, hbl2)
170 var t6: i64 = 0; if pid == 7 { if pid2 == (0 - 1) { t6 = 1 } }
171
172 px_w("PEX-KAT t1_build_parse=" as *u8); px_wn(t1)
173 px_w(" t2_spec_bytes=" as *u8); px_wn(t2)
174 px_w(" t3_neg_no_added=" as *u8); px_wn(t3)
175 px_w(" t4_neg_odd_len=" as *u8); px_wn(t4)
176 px_w(" t5_bound_max=" as *u8); px_wn(t5)
177 px_w(" t6_peer_id=" as *u8); px_wn(t6)
178 var pass: i64 = 0
179 if t1 == 1 { if t2 == 1 { if t3 == 1 { if t4 == 1 { if t5 == 1 { if t6 == 1 { pass = 1 } } } } } }
180 if pass == 1 { px_w(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 }
181 px_w(" verdict=RED\n" as *u8); sys_exit(1)
182 return 1
183}