nx_dht_get_peers.nx source
↩ module page · 117 lines · 8361 B
1// nx_dht_get_peers.nx -- SOVEREIGN DHT get_peers lookup (X-TORRENT-LIVE-002 R4c): find peers for a
2// TRACKERLESS info_hash by walking the DHT. Seeds from bootstrap nodes, get_peers each, collects
3// "values" (peers) and "nodes" (closer nodes to query next), a few BFS rounds. This is what makes the
4// operator's trackerless magnets (magnet:?xt=urn:btih:..&dn=.. , no tr=) find peers. Composes
5// nx_dht (KRPC) + nx_dns_a + raw UDP. v1 = BFS (no XOR routing yet); enough for popular swarms.
6// license_tier: ORIGINAL layer: peer-discovery (trackerless)
7// module: nishi-core.torrent.dht_get_peers
8// depends: nishi-core.torrent.dht, nishi-core.dns_a
9import "nx_dht.nx"
10import "nx_dns_a.nx"
11const K_MAGIC_8192: i64 = 8192
12const K_MAGIC_6881: i64 = 6881
13const K_MAGIC_25401: i64 = 25401
14
15func dgp_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
16func dgp_p2(s: *u8) -> i64 { sys_write(1, s, dgp_strlen(s)); return 0 }
17func dgp_pn2(v: i64) -> i64 { let t: *u8 = sys_mmap(28); var m: i64 = v; 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(28); var i: i64=0; while i<k { o[i]=t[k-1-i]; i=i+1 } sys_write(1,o,k); return 0 }
18
19func dgp_sockaddr(out: *u8, port: i64, a: i64, b: i64, c: i64, d: i64) -> i64 {
20 out[0]=2 as u8; out[1]=0 as u8; out[2]=((port>>8)&0xff) as u8; out[3]=(port&0xff) as u8
21 out[4]=a as u8; out[5]=b as u8; out[6]=c as u8; out[7]=d as u8
22 var i: i64=8; while i<16 { out[i]=0 as u8; i=i+1 }
23 return 16
24}
25
26// get_peers one node. peers -> pips/pports (ret count). nodes -> nips/nports (count via out_nn). 4s timeout.
27func dgp_query(ipv: i64, port: i64, nid: *u8, ih: *u8, pips: *i64, pports: *i64, pmax: i64, nips: *i64, nports: *i64, nmax: i64, out_nn: *i64) -> i64 {
28 out_nn[0] = 0
29 let fd: i64 = sys_socket(AF_INET, SOCK_DGRAM, 0); if fd < 0 { return 0 }
30 sys_set_socket_timeout(fd, 4)
31 let dest: *u8 = sys_mmap(16); dgp_sockaddr(dest, port, (ipv>>24)&0xff, (ipv>>16)&0xff, (ipv>>8)&0xff, ipv&0xff)
32 let tx: *u8 = sys_mmap(2); tx[0]=97 as u8; tx[1]=97 as u8
33 let q: *u8 = sys_mmap(128); let ql: i64 = dht_build_get_peers(nid, ih, tx, q)
34 sys_sendto(fd, q, ql, 0, dest, 16)
35 let r: *u8 = sys_mmap(K_MAGIC_8192); let n: i64 = sys_recvfrom(fd, r, K_MAGIC_8192, 0, 0 as *u8, 0 as *i64)
36 sys_close(fd)
37 if n < 1 { return 0 }
38 var np: i64 = dht_parse_values(r, n, pips, pports, pmax); if np < 0 { np = 0 }
39 var nn: i64 = dht_parse_nodes(r, n, nips, nports, nmax); if nn < 0 { nn = 0 }
40 out_nn[0] = nn
41 return np
42}
43
44// iterative get_peers: returns peer count into pips/pports (capacity pmax).
45// PARALLEL per round -- one socket, blast get_peers to the WHOLE frontier, then drain replies in a
46// short window. Dead nodes no longer cost 4s serially; a round is ~2s regardless of how many time out,
47// so we explore far more of the DHT (=> trackerless niche hashes actually get found). v2.
48func dgp_find(ih: *u8, nid: *u8, pips: *i64, pports: *i64, pmax: i64) -> i64 {
49 let cip: *i64 = sys_mmap(8*512) as *i64; let cpo: *i64 = sys_mmap(8*512) as *i64; var cn: i64 = 0
50 let xip: *i64 = sys_mmap(8*512) as *i64; let xpo: *i64 = sys_mmap(8*512) as *i64
51 let b: *u8 = sys_mmap(4)
52 if nx_dns_a_resolve("dht.transmissionbt.com" as *u8, b) == 1 { cip[cn]=((b[0] as i64)<<24)|((b[1] as i64)<<16)|((b[2] as i64)<<8)|(b[3] as i64); cpo[cn]=K_MAGIC_6881; cn=cn+1 }
53 if nx_dns_a_resolve("router.bittorrent.com" as *u8, b) == 1 { cip[cn]=((b[0] as i64)<<24)|((b[1] as i64)<<16)|((b[2] as i64)<<8)|(b[3] as i64); cpo[cn]=K_MAGIC_6881; cn=cn+1 }
54 if nx_dns_a_resolve("router.utorrent.com" as *u8, b) == 1 { cip[cn]=((b[0] as i64)<<24)|((b[1] as i64)<<16)|((b[2] as i64)<<8)|(b[3] as i64); cpo[cn]=K_MAGIC_6881; cn=cn+1 }
55 if nx_dns_a_resolve("dht.libtorrent.org" as *u8, b) == 1 { cip[cn]=((b[0] as i64)<<24)|((b[1] as i64)<<16)|((b[2] as i64)<<8)|(b[3] as i64); cpo[cn]=K_MAGIC_25401; cn=cn+1 }
56 // keep the bootstrap set aside and re-seed it into EVERY round's frontier so one unlucky round
57 // (all queried nodes stale -> 0 replies) can't collapse the search to nothing.
58 let bip: *i64 = sys_mmap(8*8) as *i64; let bpo: *i64 = sys_mmap(8*8) as *i64; var bn: i64 = cn
59 var bb: i64 = 0; while bb < cn { bip[bb]=cip[bb]; bpo[bb]=cpo[bb]; bb=bb+1 }
60 var found: i64 = 0
61 let tpip: *i64 = sys_mmap(8*128) as *i64; let tppo: *i64 = sys_mmap(8*128) as *i64
62 let tnip: *i64 = sys_mmap(8*128) as *i64; let tnpo: *i64 = sys_mmap(8*128) as *i64
63 let tx: *u8 = sys_mmap(2); tx[0]=97 as u8; tx[1]=97 as u8
64 let q: *u8 = sys_mmap(128); let r: *u8 = sys_mmap(K_MAGIC_8192); let dest: *u8 = sys_mmap(16)
65 let QPR: i64 = 96 // frontier nodes queried per round (parallel)
66 var round: i64 = 0
67 while round < 16 {
68 if found >= pmax { round = 16 } else { if cn == 0 { round = 16 } else {
69 let fd: i64 = sys_socket(AF_INET, SOCK_DGRAM, 0)
70 if fd < 0 { round = 16 } else {
71 sys_set_socket_timeout(fd, 2) // drain window: 2s after the last reply, then stop
72 var sent: i64 = 0; var i: i64 = 0
73 while i < cn { if i < QPR {
74 dgp_sockaddr(dest, cpo[i], (cip[i]>>24)&0xff, (cip[i]>>16)&0xff, (cip[i]>>8)&0xff, cip[i]&0xff)
75 let ql: i64 = dht_build_get_peers(nid, ih, tx, q)
76 sys_sendto(fd, q, ql, 0, dest, 16); sent = sent + 1
77 } i = i + 1 }
78 var xn: i64 = 0; var resp: i64 = 0; var go: i64 = 1
79 while go == 1 {
80 let n: i64 = sys_recvfrom(fd, r, K_MAGIC_8192, 0, 0 as *u8, 0 as *i64)
81 if n < 1 { go = 0 } else {
82 resp = resp + 1
83 var np: i64 = dht_parse_values(r, n, tpip, tppo, 128); if np < 0 { np = 0 }
84 var k: i64 = 0; while k < np { if found < pmax { pips[found]=tpip[k]; pports[found]=tppo[k]; found=found+1 } k=k+1 }
85 var nn: i64 = dht_parse_nodes(r, n, tnip, tnpo, 128); if nn < 0 { nn = 0 }
86 var m: i64 = 0; while m < nn { if xn < 512 { xip[xn]=tnip[m]; xpo[xn]=tnpo[m]; xn=xn+1 } m=m+1 }
87 if resp >= sent { go = 0 }
88 if found >= pmax { go = 0 }
89 }
90 }
91 sys_close(fd)
92 var ba: i64 = 0; while ba < bn { if xn < 512 { xip[xn]=bip[ba]; xpo[xn]=bpo[ba]; xn=xn+1 } ba=ba+1 } // re-seed bootstrap
93 dgp_p2(" round " as *u8); dgp_pn2(round); dgp_p2(": sent=" as *u8); dgp_pn2(sent); dgp_p2(" replies=" as *u8); dgp_pn2(resp); dgp_p2(" peers=" as *u8); dgp_pn2(found); dgp_p2(" nextNodes=" as *u8); dgp_pn2(xn); dgp_p2("\n" as *u8)
94 var c: i64 = 0; while c < xn { cip[c]=xip[c]; cpo[c]=xpo[c]; c=c+1 } cn = xn
95 round = round + 1
96 }
97 } }
98 }
99 return found
100}
101
102// ---- LIVE gate: get_peers for Big Buck Bunny (popular -> on the DHT). >0 peers = trackerless discovery works. ----
103func main() -> i64 {
104 let ih: *u8 = sys_mmap(20)
105 ih[0]=0xdd as u8; ih[1]=0x82 as u8; ih[2]=0x55 as u8; ih[3]=0xec as u8; ih[4]=0xdc as u8; ih[5]=0x7c as u8; ih[6]=0xa5 as u8; ih[7]=0x5f as u8; ih[8]=0xb0 as u8; ih[9]=0xbb as u8
106 ih[10]=0xf8 as u8; ih[11]=0x13 as u8; ih[12]=0x23 as u8; ih[13]=0xd8 as u8; ih[14]=0x70 as u8; ih[15]=0x62 as u8; ih[16]=0xdb as u8; ih[17]=0x1f as u8; ih[18]=0x6d as u8; ih[19]=0x1c as u8
107 let nid: *u8 = sys_mmap(20); var i: i64 = 0; while i < 20 { nid[i] = (0x41 + i) as u8; i = i + 1 }
108 let pips: *i64 = sys_mmap(8*256) as *i64; let pports: *i64 = sys_mmap(8*256) as *i64
109 dgp_p2("DHT get_peers(Big Buck Bunny) iterative lookup:\n" as *u8)
110 let c: i64 = dgp_find(ih, nid, pips, pports, 256)
111 dgp_p2("DHT-GETPEERS peers_found=" as *u8); dgp_pn2(c)
112 if c > 0 {
113 dgp_p2(" peer0=" as *u8); dgp_pn2((pips[0]>>24)&0xff); dgp_p2("." as *u8); dgp_pn2((pips[0]>>16)&0xff); dgp_p2("." as *u8); dgp_pn2((pips[0]>>8)&0xff); dgp_p2("." as *u8); dgp_pn2(pips[0]&0xff); dgp_p2(":" as *u8); dgp_pn2(pports[0])
114 dgp_p2(" verdict=GREEN (trackerless peer discovery works)\n" as *u8); sys_exit(0); return 0
115 }
116 dgp_p2(" verdict=RED (no peers via DHT)\n" as *u8); sys_exit(1); return 1
117}