code wiki / (root) / nx_pex.nx

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}