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}