code wiki / (root) / nx_torrent_seed.nx

nx_torrent_seed.nx source

↩ module page · 582 lines · 35150 B

1// nx_torrent_seed.nx -- SOVEREIGN BitTorrent SEEDER (the missing UPLOAD half of the stack). 2// 3// Until now the Nishi torrent stack was a pure LEECHER: DNS->tracker/DHT->peers->handshake->metadata-> 4// pieces->disk (all proven live), but it NEVER served pieces back. A pure leecher is punished by 5// BitTorrent tit-for-tat (seeders never optimistically-unchoke it -> it stalls -- exactly what 6// nx_torrent_seedeval's "IGNORING" verdict describes) and it cannot SHARE at all (only the HTTP /dist 7// mirror could). This organ is the symmetric other half: it ACCEPTS incoming peers and SERVES verified 8// blocks from a real file, so we become a real swarm member (seed-while-download + seed-after-complete). 9// 10// Composes nx_peerwire (BEP-3 build/parse) + nx_syscalls (sockets/files). Content is mapped by info_hash 11// via a seed registry (like dist_index.conf): rows = <name>\t<ih_hex40>\t<piece_length>\t<total>\t<srcpath>. 12// The seeder core `ts_serve_peer` is a LIB fn (import strips main) so the gate can drive it directly. 13// 14// nx_torrent_seed serve <port> [registry] -- accept-loop, serve every registered torrent 15// nx_torrent_seed serve1 <port> <ih_hex40> <plen> <total> <src> -- serve ONE torrent (deploy/test convenience) 16// 17// NEVER-BRICK by construction: writes ZERO persistent hardware state -- only network sends + file READS. 18// license_tier: ORIGINAL layer: L4 (peer-wire serving) module: nishi-core.torrent.seed 19// depends: nishi-core.torrent.peerwire, nishi-core.syscalls, nishi-core.torrent.mse_wire 20// MSE/PE: inbound peers are demuxed (0x13=plaintext BT / else=encrypted MSE) so DPI can't fingerprint us, 21// while legacy plaintext peers still work (backward compatible). All post-handshake I/O routes through MseCtx. 22import "nx_peerwire.nx" 23import "nx_mse_wire.nx" 24import "nx_ipfilter.nx" // ip-filter: reject inbound peers whose IP is in a blocked range (privacy while WAN-seeding) 25const TS_MAGIC_2026: i64 = 2026 26const TS_MAGIC_65536: i64 = 65536 27const TS_MAGIC_1048576: i64 = 1048576 28const TS_MAGIC_1024: i64 = 1024 29const TS_MAGIC_6881: i64 = 6881 30 31const TS_IPF_PATH: *u8 = "/volume1/ai/torrent/data/ipfilter.bin" as *u8 32const TS_IPF_MAXP: i64 = 80000 33const TS_SA_CELL: i64 = 32 // getpeername sockaddr scratch (mmap'd + munmap'd per call -- leak-free pair) 34const TS_AL_CELL: i64 = 8 // getpeername addrlen out-cell (same leak-free pair) 35 36// getpeername(afd) -> peer IPv4 as host-order u32 (portable syscall 205; x86-64 52). 0 on failure. LEAK-FREE (capture-then-munmap): 37// this runs in the ACCEPT-LOOP PARENT once per inbound connection -- the old leak-both-pages-per-call version 38// was the fleet certifier's first standing catch (264kB/min at idle = 2 pages x ~33 swarm connections/min; 39// the ep-out-param class, invisible until the 4-meter tooth watched the parent's VmSize trend). 40func ts_peer_ip(afd: i64) -> i64 { 41 let sa: *u8 = sys_mmap(TS_SA_CELL); let al: *i64 = sys_mmap(TS_AL_CELL) as *i64; al[0]=16 42 let rc: i64 = __syscall(205, afd, sa as i64, al as i64, 0, 0, 0) 43 var ip: i64 = 0 44 if rc == 0 { ip = ((sa[4] as i64)<<24)|((sa[5] as i64)<<16)|((sa[6] as i64)<<8)|(sa[7] as i64) } 45 sys_munmap(sa, TS_SA_CELL); sys_munmap(al as *u8, TS_AL_CELL) 46 return ip 47} 48// 1 = this accepted fd's peer is blocked (reject), 0 = ok to serve. cnt<=0 -> never blocks. 49func ts_ipf_reject(afd: i64, arr: *i64, cnt: i64) -> i64 { 50 if cnt <= 0 { return 0 } 51 let ip: i64 = ts_peer_ip(afd); if ip == 0 { return 0 } 52 return ipf_blocked(arr, cnt, ip) 53} 54 55const TS_BLOCK_MAX: i64 = 32768 // cap a single served block (std request = 16384) 56const TS_MSG_CAP: i64 = 131072 // cap an inbound control message (drain if larger) 57const TS_REG_DEFAULT: *u8 = "/volume1/ai/torrent/seed_index.conf" as *u8 58const TS_PEER_TIMEOUT: i64 = 30 // per-peer socket timeout (no-hang law) 59// CONCURRENCY BOUND (2026-07-30 seq1310): a swarm burst used to fork a handler per inbound peer with 60// NO cap on how many run at once -- each pays the MSE DH handshake burn (~20% CPU for seconds), so a 61// busy swarm was a standing NAS load producer. When live handlers >= the cap, the new connection is 62// CLOSED immediately (cheapest signal; BitTorrent peers retry). Limit is DATA-DRIVEN via the conf 63// (one integer, first line); absent/unreadable -> TS_LIVE_DEFAULT. 64const TS_LIMITS_PATH: *u8 = "/volume1/ai/torrent/seed_limits.conf" as *u8 65const TS_LIVE_DEFAULT: i64 = 4 66 67func ts_max_live() -> i64 { 68 let fd: i64 = sys_openat_rd(TS_LIMITS_PATH) 69 if fd < 0 { return TS_LIVE_DEFAULT } 70 let b: *u8 = sys_mmap(32) 71 let n: i64 = sys_read(fd, b, 31) 72 sys_close(fd) 73 if n <= 0 { return TS_LIVE_DEFAULT } 74 b[n] = 0 as u8 75 let v: i64 = ts_atoi(b) 76 if v <= 0 { return TS_LIVE_DEFAULT } 77 return v 78} 79 80func ts_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 81// PER-CALL SCRATCH -> LAZY STATICS (2026-08-14). ts_wn did TWO sys_mmap(28) calls PER INVOCATION and 82// freed neither. sys_mmap is a REAL mmap syscall, so the kernel rounds each to a full page: ~8 KB 83// LEAKED PER NUMBER PRINTED. ts_wn is the number printer for EVERY log line this organ writes, and the 84// parent re-logs its SATURATED line every time `satlogged` resets -- which is on every accepted 85// connection -- so a seeder oscillating around max_live prints, and leaks, continuously. 86// MEASURED 2026-08-14 by nx_leak_check on the live seeder parent (pid 5093, PPid=1, RSS 195 MB): 87// mem LEAK(rate=131) and rss LEAK(rate=131) kB/min == ~16 ts_wn calls/min at 8 KB each. It was the only 88// allocating call in the parent's accept loop -- ts_accept_loop_registry's own 4 mmaps are all setup, 89// hoisted above `while run == 1`. 90// THIS IS THE SAME DEFECT nx_seg_store ALREADY FIXED IN ss_hget, in a different file: that header even 91// states the law -- "a per-call allocation in a primitive is a per-CALLER-LOOP leak -- audit the 92// primitive's call frequency, not just its own body." It was fixed there and never swept for siblings. 93// SAFE AS STATICS: t and b are pure scratch, written then consumed by sys_write immediately, with no 94// intervening call that could re-enter ts_wn. Static POINTER + lazy mmap is the established idiom here 95// (a BSS static ARRAY silently crashes handler modules on startup, so never use [N]u8). 96static ts_scr_t: *u8 97static ts_scr_b: *u8 98func ts_wn(fd: i64, v: i64) -> i64 { 99 if (ts_scr_t as i64)==0 { ts_scr_t=sys_mmap(28) } 100 if (ts_scr_b as i64)==0 { ts_scr_b=sys_mmap(28) } 101 let t: *u8=ts_scr_t; var m: i64=v; if m<0 {m=0-m; sys_write(fd,"-" as *u8,1)} 102 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} 103 let b: *u8=ts_scr_b; var i: i64=0; while i<k {b[i]=t[k-1-i]; i=i+1}; sys_write(fd,b,k); return 0 104} 105func ts_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 106func ts_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c<48 {return v} if c>57 {return v} v=v*10+(c-48); i=i+1 } return v } 107 108// address helper (AF_INET, INADDR_ANY for a listening seeder, or a specific ip for the gate loopback) 109func ts_sockaddr(sa: *u8, port: i64, i0: i64, i1: i64, i2: i64, i3: i64) -> i64 { 110 sa[0]=2 as u8; sa[1]=0 as u8 111 sa[2]=((port>>8)&0xff) as u8; sa[3]=(port&0xff) as u8 112 sa[4]=i0 as u8; sa[5]=i1 as u8; sa[6]=i2 as u8; sa[7]=i3 as u8 113 var k: i64=8; while k<16 { sa[k]=0 as u8; k=k+1 } 114 return 0 115} 116func ts_read_n(fd: i64, buf: *u8, n: i64) -> i64 { 117 var got: i64=0; while got<n { let r: i64=sys_read(fd, buf+got, n-got); if r<=0 { return 0-1 } got=got+r } return got 118} 119func ts_write_n(fd: i64, buf: *u8, n: i64) -> i64 { 120 var off: i64=0; while off<n { let wr: i64=sys_write(fd, buf+off, n-off); if wr<=0 { return 0-1 } off=off+wr } return n 121} 122// read one length-prefixed message into mb. return: >0 body-len, 0 keep-alive, -1 disconnect, -2 oversized(drained) 123func ts_read_msg(fd: i64, mb: *u8, cap: i64) -> i64 { 124 if ts_read_n(fd, mb, 4) != 4 { return 0-1 } 125 let blen: i64 = _pw_get_u32(mb, 0) 126 if blen == 0 { return 0 } 127 if blen > cap-4 { 128 // oversized (e.g. a huge bitfield we don't need) -> drain + ignore, stay in sync 129 let skip: *u8 = sys_mmap(TS_MAGIC_65536) 130 var left: i64 = blen 131 while left > 0 { var take: i64=left; if take>TS_MAGIC_65536 { take=TS_MAGIC_65536 } if ts_read_n(fd, skip, take)!=take { return 0-1 } left=left-take } 132 return 0-2 133 } 134 if ts_read_n(fd, mb+4, blen) != blen { return 0-1 } 135 return blen 136} 137 138// per-connection DH private key (MSE_N limbs). Not a CSPRNG, but varies by wall-clock so Ya isn't constant 139// across peers; nonzero + ~128-bit. (Hardening TODO: a real random source for forward secrecy.) 140func ts_dh_priv(x: *i64) -> i64 { return mse_gen_priv(x) } // CSPRNG per-connection key (nx_mse_wire) 141// ts_read_msg over an MseCtx (transparent decrypt; plaintext ctx == raw == identical to ts_read_msg). 142func ts_read_msg_ctx(ctx: *MseCtx, afd: i64, mb: *u8, cap: i64) -> i64 { 143 if mse_read(ctx, afd, mb, 4) != 4 { return 0-1 } 144 let blen: i64 = _pw_get_u32(mb, 0) 145 if blen == 0 { return 0 } 146 if blen > cap-4 { 147 let skip: *u8 = sys_mmap(TS_MAGIC_65536); var left: i64 = blen 148 while left > 0 { var take: i64=left; if take>TS_MAGIC_65536 { take=TS_MAGIC_65536 } if mse_read(ctx, afd, skip, take)!=take { return 0-1 } left=left-take } 149 return 0-2 150 } 151 if mse_read(ctx, afd, mb+4, blen) != blen { return 0-1 } 152 return blen 153} 154func ts_memeq(a: *u8, b: *u8, n: i64) -> i64 { var i: i64=0; while i<n { if a[i]!=b[i] { return 0 } i=i+1 } return 1 } 155func ts_hexval(c: i64) -> i64 { 156 if c>=48 { if c<=57 { return c-48 } } 157 if c>=97 { if c<=102 { return c-87 } } // a-f 158 if c>=65 { if c<=70 { return c-55 } } // A-F 159 return 0 160} 161// 40-char hex info_hash -> 20 raw bytes. returns 1 on ok, 0 if too short. 162func ts_hex2bin(hex: *u8, out: *u8) -> i64 { 163 var i: i64=0 164 while i<20 { 165 let hc: i64 = hex[i*2] as i64; let lc: i64 = hex[i*2+1] as i64 166 if hc==0 { return 0 } if lc==0 { return 0 } 167 out[i] = ((ts_hexval(hc)*16)+ts_hexval(lc)) as u8 168 i=i+1 169 } 170 return 1 171} 172 173// Build an all-present BITFIELD for npc pieces (a seed HAS everything). MSB-first; trailing bits 0 per BEP-3. 174func ts_build_full_bitfield(npc: i64, bf: *u8) -> i64 { 175 let full: i64 = npc / 8 176 let rem: i64 = npc - full*8 177 var bi: i64=0 178 while bi<full { bf[bi]=0xff as u8; bi=bi+1 } 179 var bflen: i64 = full 180 if rem>0 { 181 var mask: i64=0; var pw: i64=128; var k: i64=0 182 while k<rem { mask=mask+pw; pw=pw/2; k=k+1 } // top `rem` bits set, no shifts 183 bf[full]=mask as u8 184 bflen = full+1 185 } 186 return bflen 187} 188// PARTIAL SEED: derive <dir>/download.done from srcpath (<dir>/download.part) and read the per-piece have map 189// (npc bytes, 1=have). Returns 1 = PARTIAL mode (advertise+serve ONLY have pieces -> safe to seed a selective/ 190// complete-on-wanted torrent), 0 = FULL mode (no download.done, e.g. a plain seeded file -> advertise all). 191func ts_load_have(srcpath: *u8, npc: i64, have: *u8) -> i64 { 192 let dp: *u8 = sys_mmap(768); var o: i64=0; var lastsl: i64=0; var i: i64=0 193 while srcpath[i]!=(0 as u8) { if srcpath[i]==(47 as u8) { lastsl=i } i=i+1 } 194 i=0; while i<=lastsl { dp[o]=srcpath[i]; o=o+1; i=i+1 } 195 let sfx: *u8="download.done" as *u8; var j: i64=0; while sfx[j]!=(0 as u8) { dp[o]=sfx[j]; o=o+1; j=j+1 } dp[o]=0 as u8 196 let fd: i64 = sys_openat_rd(dp); if fd<0 { return 0 } 197 let n: i64 = sys_read(fd, have, npc); sys_close(fd) 198 if n < npc { return 0 } // no/short done file -> FULL mode 199 return 1 200} 201// bitfield (MSB-first per BEP-3) from the have map -- only pieces we actually hold get their bit set. 202func ts_build_bitfield_have(npc: i64, have: *u8, bf: *u8) -> i64 { 203 let nbytes: i64 = (npc+7)/8; var b: i64=0; while b<nbytes { bf[b]=0 as u8; b=b+1 } 204 var p: i64=0; while p<npc { if (have[p] as i64)==1 { let by: i64=p/8; let bit: i64=7-(p-by*8); bf[by]=((bf[by] as i64)|(1<<bit)) as u8 } p=p+1 } 205 return nbytes 206} 207 208// Read + parse the peer's 68-byte handshake off a fresh socket. Writes the info_hash the peer WANTS 209// into out_ih (20 bytes) and its peer id into out_pid. Returns 1 ok, 0 on failure. Sets the no-hang timeout. 210func ts_read_hs(afd: i64, out_ih: *u8, out_pid: *u8) -> i64 { 211 sys_set_socket_timeout(afd, TS_PEER_TIMEOUT) 212 let hb: *u8 = sys_mmap(128) 213 if ts_read_n(afd, hb, 68) != 68 { return 0 } 214 if nx_pw_parse_handshake(hb, 68, out_ih, out_pid) != 1 { return 0 } 215 return 1 216} 217 218// ==== THE SEED CORE (post-handshake) ==== reply handshake -> BITFIELD(all) -> UNCHOKE -> serve REQUEST->PIECE 219// from the file. Assumes the peer's handshake is already read+validated. ih = info_hash to echo/serve; srcpath = 220// the file; plen/total/npc = geometry. Returns #blocks served, or -1 on a send failure. 221func ts_serve_after_hs(ctx: *MseCtx, afd: i64, ih: *u8, my_pid: *u8, srcpath: *u8, plen: i64, total: i64, npc: i64) -> i64 { 222 // 2. reply with our handshake (same info_hash) -- via ctx (encrypted if MSE, raw if plaintext) 223 let oh: *u8 = sys_mmap(128); nx_pw_build_handshake(ih, my_pid, oh) 224 if mse_write(ctx, afd, oh, 68) < 0 { return 0-1 } 225 // 3. BITFIELD: advertise ONLY the pieces we hold (PARTIAL mode from download.done) so a selective/ 226 // complete-on-wanted torrent seeds honestly; FULL if no download.done (a plain seeded file). 227 let have: *u8 = sys_mmap(npc + 64); let have_loaded: i64 = ts_load_have(srcpath, npc, have) 228 let bf: *u8 = sys_mmap(npc/8 + 64) 229 var bflen: i64 = 0 230 if have_loaded==1 { bflen = ts_build_bitfield_have(npc, have, bf) } else { bflen = ts_build_full_bitfield(npc, bf) } 231 let bfmsg: *u8 = sys_mmap(npc/8 + 128) 232 let bfl: i64 = nx_pw_build_msg(NX_PW_BITFIELD, bf, bflen, bfmsg) 233 if mse_write(ctx, afd, bfmsg, bfl) < 0 { return 0-1 } 234 // 4. UNCHOKE (a seed unchokes leechers so they can request) 235 let un: *u8 = sys_mmap(16); let ul: i64 = nx_pw_build_msg(NX_PW_UNCHOKE, 0 as *u8, 0, un) 236 if mse_write(ctx, afd, un, ul) < 0 { return 0-1 } 237 // 5. request loop -- serve blocks straight from the file 238 let fd: i64 = sys_openat_rd(srcpath); if fd < 0 { return 0-1 } 239 let mb: *u8 = sys_mmap(TS_MSG_CAP) 240 let frame: *u8 = sys_mmap(TS_BLOCK_MAX + 64) // rebuilt per request -> mse_write's in-place encrypt is safe 241 var served: i64 = 0 242 var run: i64 = 1 243 while run == 1 { 244 let mlen: i64 = ts_read_msg_ctx(ctx, afd, mb, TS_MSG_CAP) 245 if mlen == 0-1 { run = 0 } // peer gone / timeout 246 else { if mlen > 0 { 247 let id: i64 = nx_pw_msg_id(mb) 248 if id == NX_PW_REQUEST { 249 let index: i64 = _pw_get_u32(mb, 5) 250 let begin: i64 = _pw_get_u32(mb, 9) 251 let length: i64 = _pw_get_u32(mb, 13) 252 var may_serve: i64=1; if have_loaded==1 { if index>=0 { if index<npc { if (have[index] as i64)==0 { may_serve=0 } } } } // PARTIAL: never serve a piece we don't hold (no garbage/zeros) 253 if may_serve==1 { if length>0 { if length<=TS_BLOCK_MAX { if index>=0 { if index<npc { 254 let off: i64 = index*plen + begin 255 var rl: i64 = length 256 if off+rl > total { rl = total-off } // clamp to EOF (last piece) 257 if rl > 0 { if off>=0 { 258 // PIECE frame: [len BE][id=7][index 4][begin 4][block rl] 259 _pw_put_u32(frame, 0, 1 + 8 + rl) 260 frame[4] = NX_PW_PIECE as u8 261 _pw_put_u32(frame, 5, index) 262 _pw_put_u32(frame, 9, begin) 263 sys_lseek(fd, off, 0) 264 let dst: *u8 = frame + 13 265 if ts_read_n(fd, dst, rl) == rl { // FILE read stays raw (not the socket) 266 if mse_write(ctx, afd, frame, 13+rl) < 0 { run = 0 } else { served = served+1 } 267 } 268 } } 269 } } } } } 270 } 271 // INTERESTED / NOT_INTERESTED / HAVE / CANCEL / BITFIELD -> ignore (already unchoked, we serve on request) 272 } } 273 } 274 sys_close(fd) 275 return served 276} 277 278// serve ONE connected peer for a SINGLE known torrent (ih must match). DEMUX: 0x13 => plaintext BT handshake; 279// else => encrypted MSE. Both paths converge on ts_serve_after_hs via an MseCtx (plaintext ctx = passthrough). 280func ts_serve_peer(afd: i64, ih: *u8, my_pid: *u8, srcpath: *u8, plen: i64, total: i64, npc: i64) -> i64 { 281 sys_set_socket_timeout(afd, TS_PEER_TIMEOUT) 282 let fb: *u8 = sys_mmap(8); if ts_read_n(afd, fb, 1) != 1 { return 0-1 } // peek the first byte 283 let ctx: *MseCtx = sys_mmap(MSE_CTX_BYTES) as *MseCtx 284 let gih: *u8 = sys_mmap(20); let gpid: *u8 = sys_mmap(20) 285 if mse_classify_first_byte(fb[0] as i64) == 0 { 286 // PLAINTEXT: fb[0]=0x13; read the remaining 67 bytes of the BT handshake 287 let hb: *u8 = sys_mmap(128); hb[0]=fb[0]; if ts_read_n(afd, (hb as i64 + 1) as *u8, 67) != 67 { return 0-1 } 288 if nx_pw_parse_handshake(hb, 68, gih, gpid) != 1 { return 0-1 } 289 if ts_memeq(gih, ih, 20) != 1 { return 0-1 } 290 mse_ctx_plain(ctx) 291 return ts_serve_after_hs(ctx, afd, ih, my_pid, srcpath, plen, total, npc) 292 } 293 // MSE: fb[0] is Ya[0]. Do the encrypted handshake (SKEY = our ih), then read the peer's ENCRYPTED BT handshake. 294 let xpriv: *i64 = sys_mmap((MSE_N+2)*8) as *i64; ts_dh_priv(xpriv) 295 if mse_accept_pfx(afd, ih, xpriv, fb, 1, ctx) != 1 { return 0-1 } 296 let hb: *u8 = sys_mmap(128); if mse_read(ctx, afd, hb, 68) != 68 { return 0-1 } 297 if nx_pw_parse_handshake(hb, 68, gih, gpid) != 1 { return 0-1 } 298 if ts_memeq(gih, ih, 20) != 1 { return 0-1 } 299 return ts_serve_after_hs(ctx, afd, ih, my_pid, srcpath, plen, total, npc) 300} 301 302// row helpers for the registry (tab-separated fields, newline rows) 303func ts_eol(b: *u8, from: i64, blen: i64) -> i64 { var e: i64=from; while e<blen { if b[e]==(10 as u8) { return e } e=e+1 } return blen } 304func ts_tab(b: *u8, from: i64, limit: i64) -> i64 { var e: i64=from; while e<limit { if b[e]==(9 as u8) { return e } e=e+1 } return limit } 305func ts_field(row: *u8, rlen: i64, idx: i64, out: *u8) -> i64 { 306 var fs: i64=0; var f: i64=0; while f<idx { let t: i64=ts_tab(row,fs,rlen); fs=t+1; f=f+1 } 307 let fe: i64=ts_tab(row,fs,rlen); var o: i64=0; var k: i64=fs; while k<fe { out[o]=row[k]; o=o+1; k=k+1 } out[o]=0 as u8; return o 308} 309// resolve an info_hash (20 raw bytes) in the registry -> srcpath + piece_length + total. returns npc, or -1. 310// registry row = <name>\t<ih_hex40>\t<piece_length>\t<total>\t<srcpath> 311func ts_lookup(reg: *u8, want_ih: *u8, out_src: *u8, out_plen: *i64, out_total: *i64) -> i64 { 312 let buf: *u8 = sys_mmap(TS_MAGIC_1048576) 313 let fd: i64 = sys_openat_rd(reg); if fd<0 { return 0-1 } 314 let blen: i64 = sys_read(fd, buf, TS_MAGIC_1048576); sys_close(fd); if blen<=0 { return 0-1 } 315 let ihhex: *u8 = sys_mmap(64); let rawih: *u8 = sys_mmap(20) 316 let numbuf: *u8 = sys_mmap(32) 317 var ls: i64=0 318 while ls<blen { 319 let le: i64 = ts_eol(buf, ls, blen) 320 let row: *u8 = ((buf as i64)+ls) as *u8; let rlen: i64 = le-ls 321 ts_field(row, rlen, 1, ihhex) 322 if ts_hex2bin(ihhex, rawih)==1 { if ts_memeq(rawih, want_ih, 20)==1 { 323 ts_field(row, rlen, 2, numbuf); let plen: i64 = ts_atoi(numbuf); out_plen[0]=plen 324 ts_field(row, rlen, 3, numbuf); let total: i64 = ts_atoi(numbuf); out_total[0]=total 325 ts_field(row, rlen, 4, out_src) 326 if plen<=0 { return 0-1 } 327 return (total + plen - 1) / plen 328 } } 329 ls = le+1 330 } 331 return 0-1 332} 333 334// MSE registry SKEY-scan: find the registered info_hash whose HASH(req2,ih)==target (from mse_skey_target). 335// Writes that ih (20 raw bytes) to out_ih + its srcpath/plen/total. Returns npc, or -1 if none match. 336func ts_registry_scan(reg: *u8, target: *u8, out_ih: *u8, out_src: *u8, out_plen: *i64, out_total: *i64) -> i64 { 337 let buf: *u8 = sys_mmap(TS_MAGIC_1048576) 338 let fd: i64 = sys_openat_rd(reg); if fd<0 { return 0-1 } 339 let blen: i64 = sys_read(fd, buf, TS_MAGIC_1048576); sys_close(fd); if blen<=0 { return 0-1 } 340 let ihhex: *u8 = sys_mmap(64); let rawih: *u8 = sys_mmap(20); let numbuf: *u8 = sys_mmap(32) 341 var ls: i64=0 342 while ls<blen { 343 let le: i64 = ts_eol(buf, ls, blen) 344 let row: *u8 = ((buf as i64)+ls) as *u8; let rlen: i64 = le-ls 345 ts_field(row, rlen, 1, ihhex) 346 if ts_hex2bin(ihhex, rawih)==1 { if mse_skey_matches(rawih, target)==1 { 347 var i: i64=0; while i<20 { out_ih[i]=rawih[i]; i=i+1 } 348 ts_field(row, rlen, 2, numbuf); let plen: i64 = ts_atoi(numbuf); out_plen[0]=plen 349 ts_field(row, rlen, 3, numbuf); let total: i64 = ts_atoi(numbuf); out_total[0]=total 350 ts_field(row, rlen, 4, out_src) 351 if plen<=0 { return 0-1 } 352 return (total + plen - 1) / plen 353 } } 354 ls = le+1 355 } 356 return 0-1 357} 358 359// serve ONE connected peer, resolving the torrent by info_hash against the registry. DEMUX: 0x13 => plaintext 360// (peer's handshake carries the info_hash -> ts_lookup); else => MSE (DH, then SKEY-scan identifies the torrent). 361func ts_serve_peer_registry(afd: i64, my_pid: *u8, reg: *u8) -> i64 { 362 sys_set_socket_timeout(afd, TS_PEER_TIMEOUT) 363 let fb: *u8 = sys_mmap(8); if ts_read_n(afd, fb, 1) != 1 { return 0-1 } 364 let ctx: *MseCtx = sys_mmap(MSE_CTX_BYTES) as *MseCtx 365 let gih: *u8 = sys_mmap(20); let gpid: *u8 = sys_mmap(20) 366 let src: *u8 = sys_mmap(TS_MAGIC_1024); let plenb: *i64 = sys_mmap(16) as *i64; let totalb: *i64 = sys_mmap(16) as *i64 367 if mse_classify_first_byte(fb[0] as i64) == 0 { 368 // PLAINTEXT 369 let hb: *u8 = sys_mmap(128); hb[0]=fb[0]; if ts_read_n(afd, (hb as i64 + 1) as *u8, 67) != 67 { return 0-1 } 370 if nx_pw_parse_handshake(hb, 68, gih, gpid) != 1 { return 0-1 } 371 let npc: i64 = ts_lookup(reg, gih, src, plenb, totalb) 372 if npc < 0 { return 0-2 } 373 mse_ctx_plain(ctx) 374 return ts_serve_after_hs(ctx, afd, gih, my_pid, src, plenb[0], totalb[0], npc) 375 } 376 // MSE: DH phase (stream-syncs past the peer's PadA), then identify the torrent by SKEY-scan 377 let xpriv: *i64 = sys_mmap((MSE_N+2)*8) as *i64; ts_dh_priv(xpriv) 378 let sbytes: *u8 = sys_mmap(128); let xr: *u8 = sys_mmap(24) 379 if mse_accept_dh(afd, xpriv, fb, 1, sbytes, xr) != 1 { return 0-1 } 380 let target: *u8 = sys_mmap(24); mse_skey_target(sbytes, xr, target) 381 let npc: i64 = ts_registry_scan(reg, target, gih, src, plenb, totalb) 382 if npc < 0 { return 0-2 } // no registered torrent matched -> not for us 383 if mse_accept_finish(afd, gih, sbytes, ctx) != 1 { return 0-1 } 384 let hb: *u8 = sys_mmap(128); if mse_read(ctx, afd, hb, 68) != 68 { return 0-1 } // peer's encrypted BT handshake 385 let g2: *u8 = sys_mmap(20); let gp2: *u8 = sys_mmap(20) 386 if nx_pw_parse_handshake(hb, 68, g2, gp2) != 1 { return 0-1 } 387 if ts_memeq(g2, gih, 20) != 1 { return 0-1 } 388 return ts_serve_after_hs(ctx, afd, gih, my_pid, src, plenb[0], totalb[0], npc) 389} 390 391// registry seeding daemon: LISTEN forever, per peer resolve the requested info_hash in the registry + serve it. 392// One port serves EVERY registered torrent -- the real multi-torrent seed. Fork-per-peer + WNOHANG reap. 393func ts_accept_loop_registry(port: i64, my_pid: *u8, reg: *u8, max_conns: i64) -> i64 { 394 let sa: *u8 = sys_mmap(16); ts_sockaddr(sa, port, 0, 0, 0, 0) // INADDR_ANY (inbound peers) 395 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0) 396 if lfd < 0 { ts_w(1, "nx_torrent_seed SOCKET-FAIL\n" as *u8); sys_exit(1); return 1 } 397 let one: *u8 = sys_mmap(4); one[0]=1 as u8; one[1]=0 as u8; one[2]=0 as u8; one[3]=0 as u8 398 sys_setsockopt(lfd, SOL_SOCKET, 2, one, 4) // SO_REUSEADDR 399 if sys_bind(lfd, sa, 16) < 0 { ts_w(1, "nx_torrent_seed BIND-FAIL port=" as *u8); ts_wn(1, port); ts_w(1, "\n" as *u8); sys_exit(1); return 1 } 400 if sys_listen(lfd, 16) < 0 { ts_w(1, "nx_torrent_seed LISTEN-FAIL\n" as *u8); sys_exit(1); return 1 } 401 ts_w(1, "nx_torrent_seed REGISTRY LIVE :" as *u8); ts_wn(1, port); ts_w(1, " reg=" as *u8); ts_w(1, reg); ts_w(1, " max_conns=" as *u8); ts_wn(1, max_conns); ts_w(1, "\n" as *u8) 402 let rg_ipf: *i64 = sys_mmap(TS_IPF_MAXP*16 + 64) as *i64 403 let rg_ipf_n: i64 = ipf_load(TS_IPF_PATH, rg_ipf, TS_IPF_MAXP) 404 if rg_ipf_n > 0 { ts_w(1, "nx_torrent_seed ip-filter ranges=" as *u8); ts_wn(1, rg_ipf_n); ts_w(1, " (reject blocked inbound)\n" as *u8) } 405 let st: *i64 = sys_mmap(16) as *i64 406 let maxlive: i64 = ts_max_live() 407 ts_w(1, "nx_torrent_seed[reg] max_live=" as *u8); ts_wn(1, maxlive); ts_w(1, "\n" as *u8) 408 var live: i64 = 0 409 var satlogged: i64 = 0 410 var served_conns: i64 = 0 411 var consec: i64 = 0 412 var run: i64 = 1 413 while run == 1 { 414 // ★REAP FIRST, AND READ THE OUTCOME. Reaping used to discard the exit status entirely, and the child 415 // exited 0 unconditionally, so the parent could never learn that every handshake was failing. 416 var reaped: i64 = sys_wait4(0-1, st, 1) 417 while reaped > 0 { 418 if live > 0 { live = live - 1 } 419 if ((st[0] >> 8) & 0xff) == 0 { consec = 0 } else { consec = consec + 1 } 420 reaped = sys_wait4(0-1, st, 1) 421 } 422 let d: i64 = ts_backoff_ms(consec) 423 if d > 0 { sys_sleep_ms(d) } 424 let afd: i64 = sys_accept(lfd) 425 // ★THE MISSING BRANCH. `if afd >= 0 {...}` with no else meant a failing accept re-looped instantly. 426 if afd < 0 { consec = consec + 1 } else { 427 if ts_ipf_reject(afd, rg_ipf, rg_ipf_n) == 1 { sys_close(afd) } else { 428 if live >= maxlive { 429 // saturated: refuse the connection outright (no DH burn); peers retry. Log once per episode. 430 if satlogged == 0 { ts_w(1, "nx_torrent_seed[reg] SATURATED live=" as *u8); ts_wn(1, live); ts_w(1, " refusing new peers until a handler exits\n" as *u8); satlogged = 1 } 431 sys_close(afd) 432 } else { 433 satlogged = 0 434 let pid: i64 = sys_fork() 435 if pid == 0 { 436 sys_close(lfd) 437 let n: i64 = ts_serve_peer_registry(afd, my_pid, reg) 438 ts_w(1, "nx_torrent_seed[reg] served blocks=" as *u8); ts_wn(1, n); ts_w(1, "\n" as *u8) 439 sys_close(afd) 440 // ★THE EXIT CODE IS THE SIGNAL. Without it the parent is blind to a fast-failing serve. 441 if n <= 0 { sys_exit(1) } // MEASURED TS_MAGIC_2026-07-30 (nx_seedchurn_gate): useful=11 permille. n==0 is a completed handshake that moved NOTHING; counting it as success is what stopped this brake from ever engaging. 442 sys_exit(0) 443 } 444 sys_close(afd) 445 live = live + 1 446 served_conns = served_conns + 1 447 if max_conns > 0 { if served_conns >= max_conns { sys_wait4(0-1, st, 0); run = 0 } } 448 } 449 } } 450 } 451 sys_close(lfd) 452 return 0 453} 454 455// ---- ACCEPT BACKOFF POLICY ---- 456// ★WHY THIS EXISTS (measured live 2026-07-30). Both accept loops had NO negative branch on sys_accept: 457// `if afd >= 0 { ... }` with no else, so a persistently failing accept (EMFILE, ENFILE, ECONNABORTED) 458// re-looped with ZERO delay -- an unconditional 100%-of-a-core spin. Separately the forked child called 459// sys_exit(0) UNCONDITIONALLY, so the parent could never learn that ts_serve_peer_registry had returned -2; 460// /tmp/seed.log showed `served blocks=-2` repeating without end while the parent forked again immediately, 461// forever. A sibling measured nx_torrent_seed at 71.6% of a core and named the signature exactly: a hot 462// accept loop with no errno branch and no backoff. 463// ★THE maxlive SATURATION GUARD ABOVE DOES NOT COVER THIS. It bounds CONCURRENT handlers; when handlers 464// fail INSTANTLY, `live` never rises, so the loop forks flat out and stays under the cap the whole time. 465// A limit on concurrency is not a limit on RATE. 466// ★ONE MECHANISM FOR BOTH FAULTS, because they are the same failure: work is attempted as fast as the CPU 467// allows and none of it succeeds. A failed accept and a fast-failing serve feed one counter. 468// ★PURE FUNCTION ON PURPOSE -- same discipline as `hc_keep_delay` and `ba_verdict`. A policy welded into an 469// accept loop beside its own syscalls cannot be tested without putting a host into the required state, and 470// "every peer handshake fails" is not a condition you can summon on a shared box. That is how it survived. 471// ★NEVER STOPS ACCEPTING, ALWAYS SELF-HEALS: the delay is capped and ONE successful serve resets it to 0. 472const TS_BK_TOL: i64 = 8 // real peers abort mid-handshake; tolerate a burst before slowing at all 473const TS_BK_N2: i64 = 64 474const TS_BK_N3: i64 = 512 475const TS_BK_D1: i64 = 50 476const TS_BK_D2: i64 = 250 477const TS_BK_D3: i64 = 1000 // ceiling: one attempt per second is still a live seeder, not a dead one 478func ts_backoff_ms(consec_fail: i64) -> i64 { 479 if consec_fail < TS_BK_TOL { return 0 } 480 if consec_fail < TS_BK_N2 { return TS_BK_D1 } 481 if consec_fail < TS_BK_N3 { return TS_BK_D2 } 482 return TS_BK_D3 483} 484// The pre-fix behaviour, kept ONLY so the gate can prove the defect was real (see hc_keep_delay's twin). 485func ts_backoff_ms_old(consec_fail: i64) -> i64 { return 0 } 486 487// ---- daemon: accept loop, fork per peer, reap zombies ---- 488// max_conns > 0 -> serve that many accepted peers then exit (orphan-safe one-shot for demos/tests); 489// max_conns == 0 -> run forever (the real seeding daemon). 490func ts_accept_loop(port: i64, ih: *u8, my_pid: *u8, src: *u8, plen: i64, total: i64, npc: i64, max_conns: i64) -> i64 { 491 let sa: *u8 = sys_mmap(16); ts_sockaddr(sa, port, 0, 0, 0, 0) // INADDR_ANY 492 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0) 493 if lfd < 0 { ts_w(1, "nx_torrent_seed SOCKET-FAIL\n" as *u8); sys_exit(1); return 1 } 494 let one: *u8 = sys_mmap(4); one[0]=1 as u8; one[1]=0 as u8; one[2]=0 as u8; one[3]=0 as u8 495 sys_setsockopt(lfd, SOL_SOCKET, 2, one, 4) // SO_REUSEADDR 496 if sys_bind(lfd, sa, 16) < 0 { ts_w(1, "nx_torrent_seed BIND-FAIL port=" as *u8); ts_wn(1, port); ts_w(1, "\n" as *u8); sys_exit(1); return 1 } 497 if sys_listen(lfd, 16) < 0 { ts_w(1, "nx_torrent_seed LISTEN-FAIL\n" as *u8); sys_exit(1); return 1 } 498 ts_w(1, "nx_torrent_seed LIVE :" as *u8); ts_wn(1, port); ts_w(1, " npc=" as *u8); ts_wn(1, npc); ts_w(1, " plen=" as *u8); ts_wn(1, plen); ts_w(1, " max_conns=" as *u8); ts_wn(1, max_conns); ts_w(1, "\n" as *u8) 499 let al_ipf: *i64 = sys_mmap(TS_IPF_MAXP*16 + 64) as *i64 500 let al_ipf_n: i64 = ipf_load(TS_IPF_PATH, al_ipf, TS_IPF_MAXP) 501 let st: *i64 = sys_mmap(16) as *i64 502 let maxlive: i64 = ts_max_live() 503 var live: i64 = 0 504 var satlogged: i64 = 0 505 var served_conns: i64 = 0 506 var run: i64 = 1 507 var consec: i64 = 0 508 while run == 1 { 509 // reap finished handlers (WNOHANG) so we never accumulate zombies -- and LEARN from the exit code 510 var reaped: i64 = sys_wait4(0-1, st, 1) 511 while reaped > 0 { 512 if live > 0 { live = live - 1 } 513 if ((st[0] >> 8) & 0xff) == 0 { consec = 0 } else { consec = consec + 1 } 514 reaped = sys_wait4(0-1, st, 1) 515 } 516 let d: i64 = ts_backoff_ms(consec) 517 if d > 0 { sys_sleep_ms(d) } 518 let afd: i64 = sys_accept(lfd) 519 if afd < 0 { consec = consec + 1 } else { 520 if ts_ipf_reject(afd, al_ipf, al_ipf_n) == 1 { sys_close(afd) } else { 521 if live >= maxlive { 522 if satlogged == 0 { ts_w(1, "nx_torrent_seed SATURATED live=" as *u8); ts_wn(1, live); ts_w(1, " refusing new peers until a handler exits\n" as *u8); satlogged = 1 } 523 sys_close(afd) 524 } else { 525 satlogged = 0 526 let pid: i64 = sys_fork() 527 if pid == 0 { 528 sys_close(lfd) 529 let n: i64 = ts_serve_peer(afd, ih, my_pid, src, plen, total, npc) 530 ts_w(1, "nx_torrent_seed served blocks=" as *u8); ts_wn(1, n); ts_w(1, "\n" as *u8) 531 sys_close(afd) 532 if n <= 0 { sys_exit(1) } // MEASURED TS_MAGIC_2026-07-30 (nx_seedchurn_gate): useful=11 permille. n==0 is a completed handshake that moved NOTHING; counting it as success is what stopped this brake from ever engaging. 533 sys_exit(0) 534 } 535 sys_close(afd) 536 live = live + 1 537 served_conns = served_conns + 1 538 if max_conns > 0 { if served_conns >= max_conns { 539 // wait for the in-flight handler to finish, then exit cleanly (no orphan) 540 sys_wait4(0-1, st, 0) 541 run = 0 542 } } 543 } 544 } } 545 } 546 sys_close(lfd) 547 return 0 548} 549 550func main(argc: i64, argv: *i64) -> i64 { 551 if argc < 2 { ts_w(1, "usage: nx_torrent_seed serve <port> [reg] | serve1 <port> <ih_hex40> <plen> <total> <src>\n" as *u8); sys_exit(2); return 2 } 552 let mode: *u8 = argv[1] as *u8 553 // our peer id: "-NX0001-" + 12 filler bytes (Azureus-style client tag, deterministic) 554 let my_pid: *u8 = sys_mmap(20) 555 let tag: *u8 = "-NX0001-nishiseed01" as *u8 556 var z: i64=0; while z<19 { my_pid[z]=tag[z]; z=z+1 } my_pid[19]=48 as u8 557 if mode[0] == (115 as u8) { if mode[5] == (49 as u8) { 558 // "serve1": single torrent from args (optional trailing max_conns; 0/absent = forever) 559 if argc < 7 { ts_w(1, "usage: nx_torrent_seed serve1 <port> <ih_hex40> <plen> <total> <src> [max_conns]\n" as *u8); sys_exit(2); return 2 } 560 let port: i64 = ts_atoi(argv[2] as *u8) 561 let ih: *u8 = sys_mmap(20) 562 if ts_hex2bin(argv[3] as *u8, ih) != 1 { ts_w(1, "bad info_hash hex (need 40 chars)\n" as *u8); sys_exit(2); return 2 } 563 let plen: i64 = ts_atoi(argv[4] as *u8) 564 let total: i64 = ts_atoi(argv[5] as *u8) 565 let src: *u8 = argv[6] as *u8 566 var maxc: i64 = 0 567 if argc >= 8 { maxc = ts_atoi(argv[7] as *u8) } 568 let npc: i64 = (total + plen - 1) / plen 569 return ts_accept_loop(port, ih, my_pid, src, plen, total, npc, maxc) 570 } else { 571 // "serve [port] [registry] [max_conns]": the real multi-torrent seeding daemon (serves every registered info_hash) 572 var port: i64 = TS_MAGIC_6881 573 if argc >= 3 { port = ts_atoi(argv[2] as *u8) } 574 var reg: *u8 = TS_REG_DEFAULT 575 if argc >= 4 { reg = argv[3] as *u8 } 576 var maxc: i64 = 0 577 if argc >= 5 { maxc = ts_atoi(argv[4] as *u8) } 578 return ts_accept_loop_registry(port, my_pid, reg, maxc) 579 } } 580 ts_w(1, "usage: nx_torrent_seed serve [port] [registry] | serve1 <port> <ih_hex40> <plen> <total> <src> [max_conns]\n" as *u8) 581 sys_exit(2); return 2 582}