code wiki / (root) / nx_tracker.nx

nx_tracker.nx source

↩ module page · 108 lines · 4094 B

1// nx_tracker.nx -- BitTorrent HTTP tracker announce (bits-up), reuses the net 2// primitives. 3// 4// module: nishi-core.torrent.tracker 5// depends: nx_str.nx, nx_syscalls.nx, nx_bencode.nx 6// capability: CORE_COMPUTE 7// wired_status: FULLY_WIRED 8// 9// Build the announce request (%-encoded info_hash + peer_id + numeric params, 10// BEP-3) and parse the compact peer list (BEP-23: peers = 6N bytes, 4 IP + 2 11// port BE). The LIVE announce reuses nx_http_get_ua (the crawler's UA HTTP GET) 12// + nx_net_governor (ISP-safe pacing) + DNS/TLS -- the reusability payoff: the 13// torrent client's tracker step IS the search engine's net stack. Pure builder 14// + parser here (KAT'd); the socket round-trip is the shared primitive. 15 16import "nx_str.nx" 17import "nx_syscalls.nx" 18import "nx_bencode.nx" 19 20func _trk_hex(nib: i64) -> i64 { 21 if nib < 10 { return 0x30 + nib } 22 return 0x61 + (nib - 10) // 'a'..'f' 23} 24// RFC 3986 unreserved: A-Z a-z 0-9 - _ . ~ 25func _trk_unreserved(c: i64) -> i64 { 26 if c >= 0x41 { if c <= 0x5A { return 1 } } 27 if c >= 0x61 { if c <= 0x7A { return 1 } } 28 if c >= 0x30 { if c <= 0x39 { return 1 } } 29 if c == 0x2D { return 1 } 30 if c == 0x5F { return 1 } 31 if c == 0x2E { return 1 } 32 if c == 0x7E { return 1 } 33 return 0 34} 35// %-encode `n` raw bytes into out; returns chars written. 36func nx_trk_pct_encode(bytes: *u8, n: i64, out: *u8) -> i64 { 37 var o: i64 = 0 38 var i: i64 = 0 39 while i < n { 40 let c: i64 = bytes[i] as i64 41 if _trk_unreserved(c) == 1 { out[o] = c; o = o + 1 } 42 else { 43 out[o] = 0x25; out[o + 1] = _trk_hex((c >> 4) & 0xF); out[o + 2] = _trk_hex(c & 0xF) 44 o = o + 3 45 } 46 i = i + 1 47 } 48 return o 49} 50 51func _trk_lit(out: *u8, o: i64, s: *u8) -> i64 { 52 let n: i64 = nx_str_len(s); var i: i64 = 0 53 while i < n { out[o + i] = s[i]; i = i + 1 } 54 return o + n 55} 56func _trk_dec(out: *u8, o: i64, v: i64) -> i64 { 57 if v == 0 { out[o] = 0x30; return o + 1 } 58 var x: i64 = v; let tmp: *u8 = sys_mmap(32); var k: i64 = 0 59 while x > 0 { tmp[k] = 0x30 + (x - (x / 10) * 10); x = x / 10; k = k + 1 } 60 var oo: i64 = o 61 while k > 0 { k = k - 1; out[oo] = tmp[k]; oo = oo + 1 } 62 return oo 63} 64 65// Build the announce request PATH+QUERY into out (null-terminated). Returns len. 66// The host is resolved + fetched by the shared nx_http_get_ua. 67func nx_tracker_build_path(announce_path: *u8, aplen: i64, 68 info_hash: *u8, peer_id: *u8, port: i64, left: i64, 69 out: *u8) -> i64 { 70 var o: i64 = 0 71 var i: i64 = 0 72 while i < aplen { out[o] = announce_path[i]; o = o + 1; i = i + 1 } 73 o = _trk_lit(out, o, "?info_hash=") 74 o = o + nx_trk_pct_encode(info_hash, 20, ((out as i64) + o) as *u8) 75 o = _trk_lit(out, o, "&peer_id=") 76 o = o + nx_trk_pct_encode(peer_id, 20, ((out as i64) + o) as *u8) 77 o = _trk_lit(out, o, "&port=") 78 o = _trk_dec(out, o, port) 79 o = _trk_lit(out, o, "&uploaded=0&downloaded=0&left=") 80 o = _trk_dec(out, o, left) 81 o = _trk_lit(out, o, "&compact=1") 82 out[o] = 0 83 return o 84} 85 86// Parse a tracker response: compact peers (BEP-23) into out_ips[] (packed BE) 87// + out_ports[]. Returns peer count, or -1 if no compact peers field. 88func nx_tracker_parse_peers(resp: *u8, n: i64, out_ips: *i64, out_ports: *i64, max: i64) -> i64 { 89 let pk: i64 = nx_bc_dict_get(resp, 0, n, "peers", 5) 90 if pk < 0 { return 0 - 1 } 91 if nx_bc_type(resp, pk, n) != NX_BC_STR { return 0 - 1 } // dict-model peers not handled here 92 let so: *i64 = sys_mmap(8) as *i64 93 let sl: *i64 = sys_mmap(8) as *i64 94 nx_bc_str(resp, pk, n, so, sl) 95 let total: i64 = sl[0] / 6 96 var c: i64 = 0 97 var i: i64 = 0 98 while i < total { 99 if c < max { 100 let b: i64 = so[0] + i * 6 101 out_ips[c] = ((resp[b] as i64) << 24) | ((resp[b + 1] as i64) << 16) | ((resp[b + 2] as i64) << 8) | (resp[b + 3] as i64) 102 out_ports[c] = ((resp[b + 4] as i64) << 8) | (resp[b + 5] as i64) 103 c = c + 1 104 } 105 i = i + 1 106 } 107 return c 108}