code wiki / (root) / nx_dht.nx

nx_dht.nx source

↩ module page · 185 lines · 11635 B

1// nx_dht.nx -- SOVEREIGN DHT (BEP-5 / Kademlia) KRPC primitives (X-TORRENT-LIVE-002 R4 foundation). 2// WHY: the operator's magnets are TRACKERLESS (magnet:?xt=urn:btih:..&dn=.. with no tr=). The ONLY 3// way to find peers for a trackerless magnet is the DHT -- a global Kademlia network queried with 4// KRPC (bencode over UDP). This rung = the message layer: build ping/get_peers queries + parse the 5// responses (values=peers, nodes=closer-nodes, token). The team's own organ speaks the protocol; 6// nothing external decides. Composes nx_bencode. UDP transport + iterative lookup = next rungs. 7// 8// KRPC (BEP-5), keys bencode-sorted: 9// ping q: d1:ad2:id20:<nid>e1:q4:ping1:t2:<tx>1:y1:qe 10// get_peers q: d1:ad2:id20:<nid>9:info_hash20:<ih>e1:q9:get_peers1:t2:<tx>1:y1:qe 11// response: d1:rd2:id20:<id>[5:token..][6:valuesl6:<peer>..e | 5:nodes<26B*k>]e1:t2:<tx>1:y1:re 12// license_tier: ORIGINAL layer: peer-discovery (trackerless) 13// module: nishi-core.torrent.dht 14// depends: nishi-core.bencode 15import "nx_bencode.nx" 16const K_MAGIC_6881: i64 = 6881 17const K_MAGIC_16909060: i64 = 16909060 18 19func dht_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 20func dht_put(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 21func dht_putraw(dst: *u8, off: i64, raw: *u8, len: i64) -> i64 { var i: i64 = 0; while i < len { dst[off+i] = raw[i]; i = i + 1 } return off + len } 22func dht_putn(dst: *u8, off: i64, v: i64) -> i64 { if v==0 { dst[off]=48 as u8; return off+1 } let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1} var o: i64=off; var i: i64=k-1; while i>=0 {dst[o]=t[i];o=o+1;i=i-1} return o } 23func dht_p2(s: *u8) -> i64 { sys_write(1, s, dht_strlen(s)); return 0 } 24func dht_pn2(v: i64) -> i64 { let b: *u8 = sys_mmap(28); var m: i64 = v; var k: i64 = 0; if m == 0 { b[0] = 48 as u8; k = 1 } let t: *u8 = sys_mmap(28); while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; let o: *u8 = sys_mmap(28); while i < k { o[i] = t[k-1-i]; i = i + 1 } sys_write(1, o, k); return 0 } 25func dht_report(label: *u8, ok: i64) -> i64 { if ok == 1 { dht_p2(" [PASS] " as *u8) } else { dht_p2(" [FAIL] " as *u8) } dht_p2(label); dht_p2("\n" as *u8); return 0 } 26 27// build a KRPC ping query. nid = our 20-byte node id, tx = 2-byte transaction id. returns length. 28func dht_build_ping(nid: *u8, tx: *u8, out: *u8) -> i64 { 29 var o: i64 = 0 30 o = dht_put(out, o, "d1:ad2:id20:" as *u8); o = dht_putraw(out, o, nid, 20) 31 o = dht_put(out, o, "e1:q4:ping1:t2:" as *u8); o = dht_putraw(out, o, tx, 2) 32 o = dht_put(out, o, "1:y1:qe" as *u8) 33 return o 34} 35// build a KRPC get_peers query for info_hash ih (20 bytes). returns length. 36func dht_build_get_peers(nid: *u8, ih: *u8, tx: *u8, out: *u8) -> i64 { 37 var o: i64 = 0 38 o = dht_put(out, o, "d1:ad2:id20:" as *u8); o = dht_putraw(out, o, nid, 20) 39 o = dht_put(out, o, "9:info_hash20:" as *u8); o = dht_putraw(out, o, ih, 20) 40 o = dht_put(out, o, "e1:q9:get_peers1:t2:" as *u8); o = dht_putraw(out, o, tx, 2) 41 o = dht_put(out, o, "1:y1:qe" as *u8) 42 return o 43} 44 45// build a KRPC announce_peer query (BEP-5): tells the node "a peer at <our external ip>:<port> holds ih". 46// token = the opaque token from a PRIOR get_peers response to that same node (proves we queried it recently). 47// a-dict keys bencode-sorted: id < info_hash < port < token. returns length. 48func dht_build_announce_peer(nid: *u8, ih: *u8, port: i64, token: *u8, tlen: i64, tx: *u8, out: *u8) -> i64 { 49 var o: i64 = 0 50 o = dht_put(out, o, "d1:ad2:id20:" as *u8); o = dht_putraw(out, o, nid, 20) 51 o = dht_put(out, o, "9:info_hash20:" as *u8); o = dht_putraw(out, o, ih, 20) 52 o = dht_put(out, o, "4:porti" as *u8); o = dht_putn(out, o, port); out[o]=101 as u8; o=o+1 // porti<port>e 53 o = dht_put(out, o, "5:token" as *u8); o = dht_putn(out, o, tlen); out[o]=58 as u8; o=o+1; o = dht_putraw(out, o, token, tlen) // token<tlen>:<bytes> 54 o = dht_put(out, o, "e1:q13:announce_peer1:t2:" as *u8); o = dht_putraw(out, o, tx, 2) 55 o = dht_put(out, o, "1:y1:qe" as *u8) 56 return o 57} 58// extract the opaque "token" from a get_peers response -> out (raw). returns token length, or -1 if absent. 59func dht_parse_token(buf: *u8, n: i64, out: *u8, max: i64) -> i64 { 60 let r: i64 = nx_bc_dict_get(buf, 0, n, "r" as *u8, 1) 61 if r < 0 { return 0-1 } 62 let to: i64 = nx_bc_dict_get(buf, r, n, "token" as *u8, 5) 63 if to < 0 { return 0-1 } 64 let so: *i64 = sys_mmap(16) as *i64; let sl: *i64 = sys_mmap(16) as *i64 65 if nx_bc_str(buf, to, n, so, sl) < 0 { return 0-1 } 66 let s: i64 = so[0]; var l: i64 = sl[0]; if l > max { l = max } 67 var i: i64 = 0; while i < l { out[i] = buf[s+i]; i = i + 1 } 68 return l 69} 70// is this a KRPC RESPONSE (y=r, has "r" dict) rather than an error? used to ack an announce_peer. 71func dht_is_response(buf: *u8, n: i64) -> i64 { if nx_bc_dict_get(buf, 0, n, "r" as *u8, 1) >= 0 { return 1 } return 0 } 72 73// parse the "values" peer list from a KRPC response -> ips[]/ports[] (compact 6B/peer). returns count 74// (0 if no values, -1 if not a valid response). ips packed b0<<24|b1<<16|b2<<8|b3 (UDP-path convention). 75func dht_parse_values(buf: *u8, n: i64, ips: *i64, ports: *i64, max: i64) -> i64 { 76 let r: i64 = nx_bc_dict_get(buf, 0, n, "r" as *u8, 1) 77 if r < 0 { return 0 - 1 } 78 let vo: i64 = nx_bc_dict_get(buf, r, n, "values" as *u8, 6) 79 if vo < 0 { return 0 } 80 if buf[vo] != (108 as u8) { return 0 } // 'l' = list 81 var o: i64 = vo + 1; var cnt: i64 = 0 82 let so: *i64 = sys_mmap(16) as *i64; let sl: *i64 = sys_mmap(16) as *i64 83 while o < n { 84 if buf[o] == (101 as u8) { o = n } else { // 'e' ends the list 85 if nx_bc_str(buf, o, n, so, sl) < 0 { o = n } else { 86 if sl[0] == 6 { if cnt < max { 87 let s: i64 = so[0] 88 let b0: i64 = buf[s] as i64; let b1: i64 = buf[s+1] as i64; let b2: i64 = buf[s+2] as i64; let b3: i64 = buf[s+3] as i64 89 ips[cnt] = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 90 ports[cnt] = ((buf[s+4] as i64) << 8) | (buf[s+5] as i64) 91 cnt = cnt + 1 92 } } 93 o = nx_bc_skip(buf, o, n) 94 } 95 } 96 } 97 return cnt 98} 99 100// parse the "nodes" compact list (26B each: 20 id + 4 ip + 2 port) -> node ip4s/ports for iterative 101// lookup. returns count. (kademlia walk toward the info_hash uses these when "values" is absent.) 102func dht_parse_nodes(buf: *u8, n: i64, ips: *i64, ports: *i64, max: i64) -> i64 { 103 let r: i64 = nx_bc_dict_get(buf, 0, n, "r" as *u8, 1) 104 if r < 0 { return 0 - 1 } 105 let no: i64 = nx_bc_dict_get(buf, r, n, "nodes" as *u8, 5) 106 if no < 0 { return 0 } 107 let so: *i64 = sys_mmap(16) as *i64; let sl: *i64 = sys_mmap(16) as *i64 108 if nx_bc_str(buf, no, n, so, sl) < 0 { return 0 } 109 let s: i64 = so[0]; let l: i64 = sl[0] 110 var cnt: i64 = 0; var i: i64 = 0 111 while i + 26 <= l { 112 if cnt < max { 113 let p: i64 = s + i + 20 // skip the 20-byte node id -> ip:port 114 let b0: i64 = buf[p] as i64; let b1: i64 = buf[p+1] as i64; let b2: i64 = buf[p+2] as i64; let b3: i64 = buf[p+3] as i64 115 ips[cnt] = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 116 ports[cnt] = ((buf[p+4] as i64) << 8) | (buf[p+5] as i64) 117 cnt = cnt + 1 118 } 119 i = i + 26 120 } 121 return cnt 122} 123 124// ---- baked KAT: deterministic + sovereign (no network). proves the KRPC wire format + value parse. ---- 125func main() -> i64 { 126 var pass: i64 = 0; var tot: i64 = 0 127 let nid: *u8 = sys_mmap(20); var i: i64 = 0; while i < 20 { nid[i] = 0xaa as u8; i = i + 1 } 128 let ih: *u8 = sys_mmap(20); i = 0; while i < 20 { ih[i] = 0xbb as u8; i = i + 1 } 129 let tx: *u8 = sys_mmap(2); tx[0] = 97 as u8; tx[1] = 98 as u8 // "ab" 130 131 // KAT1: ping query well-formed 132 let pbuf: *u8 = sys_mmap(128); let pl: i64 = dht_build_ping(nid, tx, pbuf) 133 var ok1: i64 = 0 134 // d1:ad2:id20:<20>e1:q4:ping1:t2:ab1:y1:qe -- check structure (top dict + q + a keys), not brittle length 135 if pl > 40 { if pbuf[0]==(100 as u8) { if nx_bc_dict_get(pbuf, 0, pl, "q" as *u8, 1) >= 0 { if nx_bc_dict_get(pbuf, 0, pl, "a" as *u8, 1) >= 0 { ok1 = 1 } } } } 136 tot = tot + 1; if ok1 == 1 { pass = pass + 1 } dht_report("build ping: well-formed KRPC (top dict + q + a)" as *u8, ok1) 137 138 // KAT2: get_peers query carries the info_hash 139 let gbuf: *u8 = sys_mmap(128); let gl: i64 = dht_build_get_peers(nid, ih, tx, gbuf) 140 var ok2: i64 = 0 141 let qv: i64 = nx_bc_dict_get(gbuf, 0, gl, "q" as *u8, 1) 142 let av: i64 = nx_bc_dict_get(gbuf, 0, gl, "a" as *u8, 1) 143 if qv >= 0 { if av >= 0 { if nx_bc_dict_get(gbuf, av, gl, "info_hash" as *u8, 9) >= 0 { ok2 = 1 } } } 144 tot = tot + 1; if ok2 == 1 { pass = pass + 1 } dht_report("build get_peers: a.info_hash present" as *u8, ok2) 145 146 // KAT3: parse a response with one value peer 1.2.3.4:6881 147 let rbuf: *u8 = sys_mmap(256); var o: i64 = 0 148 o = dht_put(rbuf, o, "d1:rd2:id20:" as *u8); o = dht_putraw(rbuf, o, nid, 20) 149 o = dht_put(rbuf, o, "5:token2:zz6:valuesl6:" as *u8) 150 rbuf[o]=1 as u8; o=o+1; rbuf[o]=2 as u8; o=o+1; rbuf[o]=3 as u8; o=o+1; rbuf[o]=4 as u8; o=o+1 151 let phi: i64 = (K_MAGIC_6881 >> 8) & 0xff; let plo: i64 = K_MAGIC_6881 & 0xff 152 rbuf[o] = phi as u8; o = o + 1; rbuf[o] = plo as u8; o = o + 1 153 o = dht_put(rbuf, o, "ee1:t2:ab1:y1:re" as *u8) 154 let ips: *i64 = sys_mmap(8*16) as *i64; let ports: *i64 = sys_mmap(8*16) as *i64 155 let cnt: i64 = dht_parse_values(rbuf, o, ips, ports, 16) 156 var ok3: i64 = 0 157 if cnt == 1 { if ips[0] == K_MAGIC_16909060 { if ports[0] == K_MAGIC_6881 { ok3 = 1 } } } 158 tot = tot + 1; if ok3 == 1 { pass = pass + 1 } dht_report("parse response values: 1.2.3.4:6881" as *u8, ok3) 159 160 // KAT4: announce_peer query carries info_hash + port(=6881) + token, keys sorted, q=announce_peer 161 let tok: *u8 = sys_mmap(8); tok[0]=122 as u8; tok[1]=122 as u8 // "zz" (as returned by KAT3's response) 162 let abuf: *u8 = sys_mmap(160); let al: i64 = dht_build_announce_peer(nid, ih, K_MAGIC_6881, tok, 2, tx, abuf) 163 var ok4: i64 = 0 164 let aav: i64 = nx_bc_dict_get(abuf, 0, al, "a" as *u8, 1) 165 let aqv: i64 = nx_bc_dict_get(abuf, 0, al, "q" as *u8, 1) 166 if aav >= 0 { if aqv >= 0 { 167 let ihp: i64 = nx_bc_dict_get(abuf, aav, al, "info_hash" as *u8, 9) 168 let prt: i64 = nx_bc_dict_get(abuf, aav, al, "port" as *u8, 4) 169 let tkp: i64 = nx_bc_dict_get(abuf, aav, al, "token" as *u8, 5) 170 if ihp >= 0 { if prt >= 0 { if tkp >= 0 { 171 let iv: *i64 = sys_mmap(8) as *i64 172 nx_bc_int(abuf, prt, al, iv); if iv[0] == K_MAGIC_6881 { ok4 = 1 } 173 } } } 174 } } 175 tot = tot + 1; if ok4 == 1 { pass = pass + 1 } dht_report("build announce_peer: a.{info_hash,port=6881,token}" as *u8, ok4) 176 177 // KAT5: parse the token back out of KAT3's get_peers response ("2:zz") 178 let tkb: *u8 = sys_mmap(32); let tkl: i64 = dht_parse_token(rbuf, o, tkb, 32) 179 var ok5: i64 = 0; if tkl == 2 { if tkb[0]==(122 as u8) { if tkb[1]==(122 as u8) { ok5 = 1 } } } 180 tot = tot + 1; if ok5 == 1 { pass = pass + 1 } dht_report("parse token from get_peers response: zz" as *u8, ok5) 181 182 dht_p2("DHT-KRPC-GATE authored=organ pass=" as *u8); dht_pn2(pass); dht_p2("/" as *u8); dht_pn2(tot) 183 if pass == tot { dht_p2(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 184 dht_p2(" verdict=RED\n" as *u8); sys_exit(1); return 1 185}