code wiki / (root) / nx_dht_get_peers.nx

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}