code wiki / (root) / nx_peer_piece_live.nx

nx_peer_piece_live.nx source

↩ module page · 179 lines · 12328 B

1// nx_peer_piece_live.nx -- download a REAL data piece from a REAL peer + sha1-verify it against the 2// metadata piece hash (X-TORRENT-LIVE-001 L4a): the last engine step = real file bytes off the wire. 3// One peer connection: BEP-3 handshake -> BEP-10 ext -> BEP-9 metadata (piece map) -> INTERESTED -> 4// on UNCHOKE request piece 0's 16KiB blocks -> assemble -> sha1(piece0)==pieces[0]. DISPATCH state 5// machine (real peers interleave choke/bitfield/have). Fork-bounded per peer (no-hang). Reuses 6// nx_peer_meta_live (cl_*/pm_*) + nx_peerwire + nx_bencode + nx_sha1. GATE = LIVE: a real peer serves 7// a sha1-verified data piece of Big Buck Bunny. license_tier: ORIGINAL 8// 9// module: nishi-core.torrent.peer_piece_live 10// depends: nishi-core.torrent.peer_meta_live 11import "nx_peer_meta_live.nx" 12import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 13const K_MAGIC_262144: i64 = 262144 14const K_MAGIC_40000: i64 = 40000 15const K_MAGIC_16383: i64 = 16383 16const K_MAGIC_16384: i64 = 16384 17const K_MAGIC_25000: i64 = 25000 18 19// seq1734: was 4194304 (4 MiB). A torrent with plen > PCAP could NEVER complete a single piece: 20// nx_torrent_get.nx:440 guards block receipt with `if beg+bl<=PCAP` and has NO else branch, so every 21// block past the cap was DISCARDED SILENTLY -- pbytes never reached plen, the sha1 never verified, and 22// the piece was retried forever. MEASURED: gallery/javdb.com_ajvr-166 has plen=16777216 (16 MiB = 4x 23// the old cap) and sat at have=0 / 1069 pieces with 468 holder-backed -- we connected, got unchoked, 24// requested, received, and threw the bytes away. 16 MiB pieces are ordinary for multi-GB torrents; 25// 32 MiB covers the practical field with headroom. 26// WHY RAISING THIS IS NEARLY FREE: pbuf/piece0 come from sys_mmap, and mmap is LAZY -- pages commit 27// only when touched, so a 2 MiB-piece torrent still costs ~2 MiB RSS per child even though the 28// mapping is 32 MiB. Reserving address space is not spending memory. 29// ⚠A piece larger than this must still be REFUSED LOUDLY, never truncated -- see the plen<=PCAP 30// guards below, which are the shape nx_torrent_get's receipt path is missing. 31const PCAP: i64 = 33554432 // max piece buffer 32 MiB (was 4 MiB; see seq1734) 32 33// download+verify piece 0 from one peer; on success write [1][4 plen][20 hash] to res. 34func pp_child(ipv: i64, port: i64, ih: *u8, mypid: *u8, res: *u8) -> i64 { 35 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0) 36 if fd < 0 { return 0 } 37 sys_set_socket_timeout(fd, 6) 38 let dest: *u8 = sys_mmap(16); cl_sockaddr(dest, port, (ipv>>24)&0xff, (ipv>>16)&0xff, (ipv>>8)&0xff, ipv&0xff) 39 if nx_connect_bounded(fd, dest, 16, NX_CONN_DEFAULT_MS) != 0 { sys_close(fd); return 0 } 40 let ch: *u8 = sys_mmap(128); nx_pw_build_handshake(ih, mypid, ch); ch[25] = (ch[25] as i64 | 0x10) as u8 41 if cl_write_n(fd, ch, 68) != 68 { sys_close(fd); return 0 } 42 let ph: *u8 = sys_mmap(128) 43 if cl_read_n(fd, ph, 68) != 68 { sys_close(fd); return 0 } 44 let gih: *u8 = sys_mmap(20); let gpid: *u8 = sys_mmap(20) 45 if nx_pw_parse_handshake(ph, 68, gih, gpid) != 1 { sys_close(fd); return 0 } 46 let ceb: *u8 = sys_mmap(64); let cm: *u8 = sys_mmap(128) 47 cl_write_n(fd, cm, pm_build_ext(0, ceb, pm_bstr(ceb, 0, "d1:md11:ut_metadatai1eee" as *u8), cm)) 48 49 let meta: *u8 = sys_mmap(K_MAGIC_262144) 50 var meta_size: i64 = 0; var peer_utm: i64 = 0; var got_meta: i64 = 0; var nmeta: i64 = 0; var req_meta: i64 = 0 51 var meta_done: i64 = 0; var plen: i64 = 0; var pieces_off: i64 = 0 52 var interested: i64 = 0; var unchoked: i64 = 0; var req_data: i64 = 0 53 let piece0: *u8 = sys_mmap(PCAP); var p0got: i64 = 0 54 var done: i64 = 0 55 let mb: *u8 = sys_mmap(K_MAGIC_40000) 56 var loops: i64 = 0 57 var go: i64 = 1 58 while go == 1 { 59 if loops >= 400 { go = 0 } 60 if go == 1 { 61 let blen: i64 = pm_read_msg(fd, mb, K_MAGIC_40000) 62 if blen < 0 { go = 0 } 63 if blen == 0 { loops = loops + 1 } 64 if blen > 0 { 65 loops = loops + 1 66 let id: i64 = nx_pw_msg_id(mb) 67 let nt: i64 = 4 + blen 68 if id == PW_EXT { 69 let ext_id: i64 = mb[5] as i64 70 if ext_id == 0 { 71 let mo: i64 = nx_bc_dict_get(mb, 6, nt, "m" as *u8, 1) 72 if mo >= 0 { let uo: i64 = nx_bc_dict_get(mb, mo, nt, "ut_metadata" as *u8, 11); if uo >= 0 { let iv: *i64 = sys_mmap(16) as *i64; if nx_bc_int(mb, uo, nt, iv) >= 0 { peer_utm = iv[0] } } } 73 let so: i64 = nx_bc_dict_get(mb, 6, nt, "metadata_size" as *u8, 13); if so >= 0 { let iv2: *i64 = sys_mmap(16) as *i64; if nx_bc_int(mb, so, nt, iv2) >= 0 { meta_size = iv2[0] } } 74 if peer_utm > 0 { if meta_size > 0 { if req_meta == 0 { 75 nmeta = (meta_size + K_MAGIC_16383) / K_MAGIC_16384 76 var pi: i64 = 0 77 while pi < nmeta { let rb: *u8 = sys_mmap(64); var ro: i64 = pm_bstr(rb, 0, "d8:msg_typei0e5:piecei" as *u8); ro = pm_bint(rb, ro, pi); ro = pm_bstr(rb, ro, "ee" as *u8); let rm: *u8 = sys_mmap(128); cl_write_n(fd, rm, pm_build_ext(peer_utm, rb, ro, rm)); pi = pi + 1 } 78 req_meta = 1 79 } } } 80 } 81 if ext_id == 1 { 82 if meta_done == 0 { 83 let pio: i64 = nx_bc_dict_get(mb, 6, nt, "piece" as *u8, 5) 84 var pidx: i64 = 0; if pio >= 0 { let iv3: *i64 = sys_mmap(16) as *i64; if nx_bc_int(mb, pio, nt, iv3) >= 0 { pidx = iv3[0] } } 85 let raw: i64 = nx_bc_skip(mb, 6, nt) 86 if raw > 0 { let rawlen: i64 = nt - raw; var c: i64 = 0; while c < rawlen { if pidx*K_MAGIC_16384 + c < K_MAGIC_262144 { meta[pidx*K_MAGIC_16384 + c] = mb[raw + c] } c = c + 1 } got_meta = got_meta + 1 } 87 if got_meta >= nmeta { if nmeta > 0 { 88 let gh: *u8 = sys_mmap(20); sha1(meta, meta_size, gh) 89 var eqv: i64 = 1; var x: i64 = 0; while x < 20 { if gh[x] != ih[x] { eqv = 0; x = 20 } else { x = x + 1 } } 90 if eqv == 1 { 91 meta_done = 1 92 let plo: i64 = nx_bc_dict_get(meta, 0, meta_size, "piece length" as *u8, 12); let iv5: *i64 = sys_mmap(16) as *i64; if plo >= 0 { nx_bc_int(meta, plo, meta_size, iv5); plen = iv5[0] } 93 let pco: i64 = nx_bc_dict_get(meta, 0, meta_size, "pieces" as *u8, 6); let so5: *i64 = sys_mmap(16) as *i64; let sl5: *i64 = sys_mmap(16) as *i64; if pco >= 0 { nx_bc_str(meta, pco, meta_size, so5, sl5); pieces_off = so5[0] } 94 // send INTERESTED 95 let im: *u8 = sys_mmap(16); cl_write_n(fd, im, nx_pw_build_msg(NX_PW_INTERESTED, 0 as *u8, 0, im)); interested = 1 96 // if already unchoked, request piece 0 now 97 if unchoked == 1 { if req_data == 0 { if plen > 0 { if plen <= PCAP { 98 var b: i64 = 0; while b < plen { var blk: i64 = K_MAGIC_16384; if plen - b < blk { blk = plen - b } let rq: *u8 = sys_mmap(32); cl_write_n(fd, rq, nx_pw_build_request(0, b, blk, rq)); b = b + K_MAGIC_16384 } 99 req_data = 1 100 } } } } 101 } 102 } } 103 } 104 } 105 } 106 if id == NX_PW_UNCHOKE { unchoked = 1 107 if meta_done == 1 { if req_data == 0 { if plen > 0 { if plen <= PCAP { 108 var b: i64 = 0; while b < plen { var blk: i64 = K_MAGIC_16384; if plen - b < blk { blk = plen - b } let rq: *u8 = sys_mmap(32); cl_write_n(fd, rq, nx_pw_build_request(0, b, blk, rq)); b = b + K_MAGIC_16384 } 109 req_data = 1 110 } } } } 111 } 112 if id == NX_PW_CHOKE { unchoked = 0 } 113 if id == NX_PW_PIECE { 114 let pidx: i64 = _pw_get_u32(mb, 5); let beg: i64 = _pw_get_u32(mb, 9) 115 let blocklen: i64 = blen - 9 116 if pidx == 0 { if beg + blocklen <= PCAP { var c: i64 = 0; while c < blocklen { piece0[beg + c] = mb[13 + c]; c = c + 1 } p0got = p0got + blocklen } } 117 if p0got >= plen { if plen > 0 { 118 let gh2: *u8 = sys_mmap(20); sha1(piece0, plen, gh2) 119 var ev2: i64 = 1; var y: i64 = 0; while y < 20 { if gh2[y] != meta[pieces_off + y] { ev2 = 0; y = 20 } else { y = y + 1 } } 120 if ev2 == 1 { done = 1; go = 0 } 121 } } 122 } 123 } 124 } 125 } 126 if done == 1 { 127 let rf: i64 = sys_openat_wr(res, 0x1a4) 128 if rf >= 0 { let ob: *u8 = sys_mmap(32); ob[0]=1 as u8; ob[1]=(plen>>24)&0xff; ob[2]=(plen>>16)&0xff; ob[3]=(plen>>8)&0xff; ob[4]=plen&0xff; var k: i64=0; while k<20 { ob[5+k]=meta[pieces_off+k]; k=k+1 } cl_write_n(rf, ob, 25); sys_close(rf) } 129 } 130 sys_close(fd) 131 return 0 132} 133 134func main() -> i64 { 135 let ih: *u8 = sys_mmap(20) 136 ih[0]=221 as u8; ih[1]=130 as u8; ih[2]=85 as u8; ih[3]=236 as u8; ih[4]=220 as u8 137 ih[5]=124 as u8; ih[6]=165 as u8; ih[7]=95 as u8; ih[8]=176 as u8; ih[9]=187 as u8 138 ih[10]=248 as u8; ih[11]=19 as u8; ih[12]=35 as u8; ih[13]=216 as u8; ih[14]=112 as u8 139 ih[15]=98 as u8; ih[16]=219 as u8; ih[17]=31 as u8; ih[18]=109 as u8; ih[19]=28 as u8 140 let mypid: *u8 = sys_mmap(20); let ps: *u8 = "-NX0001-000000000000" as *u8; var z: i64 = 0; while z < 20 { mypid[z] = ps[z]; z = z + 1 } 141 142 let ips: *i64 = sys_mmap(8*80) as *i64; let ports: *i64 = sys_mmap(8*80) as *i64 143 let np: i64 = cl_announce(ih, mypid, ips, ports, 80) 144 cl_w(1, "PIECELIVE peers=" as *u8); cl_wn(1, np); cl_w(1, "\n" as *u8) 145 if np < 1 { cl_w(1, "PIECELIVE verdict=RED reason=no-peers (tracker rate-limit? re-run)\n" as *u8); sys_exit(1); return 1 } 146 147 let res: *u8 = "/tmp/_nx_piece_live.bin" as *u8 148 let st: *i64 = sys_mmap(16) as *i64 149 var tries: i64 = np; if tries > 30 { tries = 30 } 150 var ok: i64 = 0; var attempted: i64 = 0 151 var i: i64 = 0 152 while i < tries { 153 if ok == 0 { 154 let cf: i64 = sys_openat_wr(res, 0x1a4); if cf >= 0 { let zb: *u8 = sys_mmap(4); zb[0]=0 as u8; cl_write_n(cf, zb, 1); sys_close(cf) } 155 attempted = attempted + 1 156 let pid: i64 = sys_fork() 157 if pid == 0 { pp_child(ips[i], ports[i], ih, mypid, res); sys_exit(0) } 158 var el: i64 = 0; var g2: i64 = 1 159 while g2 == 1 { let r: i64 = sys_wait4(pid, st, WNOHANG); if r > 0 { g2 = 0 } else { if el >= K_MAGIC_25000 { g2 = 0 } else { sys_sleep_ms(250); el = el + 250 } } } 160 if sys_wait4(pid, st, WNOHANG) <= 0 { nx_kill(pid, 9); sys_wait4(pid, st, 0) } 161 let rf: i64 = sys_openat_rd(res) 162 if rf >= 0 { let rb: *u8 = sys_mmap(64); let rn: i64 = sys_read(rf, rb, 64); sys_close(rf) 163 if rn >= 5 { if (rb[0] as i64) == 1 { 164 ok = 1 165 let pl: i64 = ((rb[1] as i64)<<24)|((rb[2] as i64)<<16)|((rb[3] as i64)<<8)|(rb[4] as i64) 166 let a: i64 = ips[i] 167 cl_w(1, "PIECELIVE PIECE0 OK + sha1-VERIFIED from peer=" as *u8) 168 cl_wn(1, (a>>24)&0xff); cl_w(1, "." as *u8); cl_wn(1, (a>>16)&0xff); cl_w(1, "." as *u8); cl_wn(1, (a>>8)&0xff); cl_w(1, "." as *u8); cl_wn(1, a&0xff); cl_w(1, ":" as *u8); cl_wn(1, ports[i]) 169 cl_w(1, " piece_length=" as *u8); cl_wn(1, pl); cl_w(1, " bytes (REAL data, hash matches metadata)\n" as *u8) 170 } } 171 } 172 } 173 i = i + 1 174 } 175 cl_w(1, "PIECELIVE attempted=" as *u8); cl_wn(1, attempted) 176 if ok == 1 { cl_w(1, " verdict=GREEN (downloaded + verified a REAL data piece from a REAL peer)\n" as *u8); sys_exit(0); return 0 } 177 cl_w(1, " verdict=RED reason=no-peer-served-piece (choke/snub/slow; re-run/space announces)\n" as *u8); sys_exit(1) 178 return 1 179}