nx_magnet_discover.nx source
↩ module page · 166 lines · 7622 B
1// nx_magnet_discover.nx -- paste a URL, get EVERY magnet on the page + info on each.
2// Fetches the URL over a VALIDATED TLS 1.3 session (nx_https_get + real CA store),
3// scans the response for every "magnet:?" link (decoding HTML & -> &), parses each
4// (info_hash, display name, tracker count) via mg_parse, dedupes by info_hash, and
5// writes a TSV the torrent daemon serves to the UI. The "find all magnets" rung.
6//
7// argv[1] = page URL (https://...) argv[2] = output TSV path
8// out rows: <hash_hex>\t<name>\t<ntrackers>\t<magnet_uri>\n ; plus <out>.status state line.
9// license_tier: ORIGINAL kind: discovery layer: L6 (over validated-HTTPS + magnet-parse)
10//
11// module: nishi-core.torrent.magnet_discover
12import "nx_magnet.nx"
13import "nx_https_get.nx"
14import "nx_x509_trust_store.nx"
15import "nx_pem_loader.nx"
16import "nx_csprng.nx"
17import "nx_syscalls.nx"
18const K_MAGIC_1024: i64 = 1024
19const K_MAGIC_1048576: i64 = 1048576
20const K_MAGIC_4096: i64 = 4096
21const K_MAGIC_4095: i64 = 4095
22
23func md_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 }
24func md_wn(fd: i64, v: i64) -> i64 {
25 let t: *u8=sys_mmap(28); var m: i64=v; if m<0 {m=0-m}
26 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}
27 let b: *u8=sys_mmap(28); var i: i64=0; while i<k {b[i]=t[k-1-i]; i=i+1}; sys_write(fd,b,k); return 0
28}
29func md_open_w(path: *u8) -> i64 { return sys_openat_wr(path, 0x1a4) }
30
31// hex-encode the 20-byte info_hash into out[0..40]+NUL
32func md_hex(ihash: *u8, out: *u8) -> i64 {
33 let hd: *u8 = "0123456789abcdef" as *u8
34 var i: i64 = 0
35 while i < 20 { let b: i64 = ihash[i] as i64; out[i*2]=hd[(b>>4)&0xf]; out[i*2+1]=hd[b&0xf]; i=i+1 }
36 out[40]=0 as u8
37 return 40
38}
39
40func md_is_term(c: i64) -> i64 {
41 if c==34 { return 1 } // "
42 if c==39 { return 1 } // '
43 if c==60 { return 1 } // <
44 if c==62 { return 1 } // >
45 if c==32 { return 1 } // space
46 if c==9 { return 1 } // tab
47 if c==10 { return 1 } // \n
48 if c==13 { return 1 } // \r
49 if c==92 { return 1 } // backslash
50 return 0
51}
52
53// write the status sidecar: <outpath>.status holding one state line
54func md_status(outpath: *u8, state: *u8, count: i64, bytes: i64) -> i64 {
55 let sp: *u8 = sys_mmap(K_MAGIC_1024)
56 var i: i64 = 0; while outpath[i]!=(0 as u8) { sp[i]=outpath[i]; i=i+1 }
57 let suf: *u8 = ".status" as *u8; var j: i64 = 0; while suf[j]!=(0 as u8) { sp[i]=suf[j]; i=i+1; j=j+1 } sp[i]=0 as u8
58 let fd: i64 = md_open_w(sp); if fd<0 { return 0 }
59 md_w(fd, "state=" as *u8); md_w(fd, state)
60 md_w(fd, "\ncount=" as *u8); md_wn(fd, count)
61 md_w(fd, "\nbytes=" as *u8); md_wn(fd, bytes); md_w(fd, "\n" as *u8)
62 sys_close(fd)
63 return 0
64}
65
66func main(argc: i64, argv: *i64) -> i64 {
67 var url: *u8 = "https://example.com/" as *u8
68 if argc >= 2 { url = argv[1] as *u8 }
69 var outpath: *u8 = "/mnt/c/Users/elder/Downloads/nishi-torrents/discover.tsv" as *u8
70 if argc >= 3 { outpath = argv[2] as *u8 }
71
72 md_status(outpath, "fetching" as *u8, 0, 0)
73
74 // ---- validated TLS 1.3 boot ----
75 let store: *TrustStore = trust_store_alloc(400)
76 let nroots: i64 = nx_pem_trust_load_file("/etc/ssl/certs/ca-certificates.crt\x00", store)
77 if nroots <= 0 { md_status(outpath, "error-no-ca" as *u8, 0, 0); sys_exit(1); return 1 }
78 let cr: *u8 = sys_mmap(32); let pk: *u8 = sys_mmap(32)
79 nx_csprng_fill(cr, 32); nx_csprng_fill(pk, 32)
80
81 let cap: i64 = K_MAGIC_1048576
82 let resp: *u8 = sys_mmap(cap)
83 let n: i64 = nx_https_get(url, cr, pk, store, sys_now_realtime_sec(), resp, cap)
84 if n <= 0 {
85 // negative = sealed verdict; surface it
86 let vp: *u8 = sys_mmap(64); var z: i64 = 0
87 let pre: *u8 = "error-fetch-" as *u8; while pre[z]!=(0 as u8){vp[z]=pre[z]; z=z+1}
88 var vv: i64 = 0 - n; if vv==0 { vv=1 }
89 let t: *u8=sys_mmap(28); var m: i64=vv; var k: i64=0; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1}
90 var q: i64=0; while q<k { vp[z]=t[k-1-q]; z=z+1; q=q+1 } vp[z]=0 as u8
91 md_status(outpath, vp, 0, 0)
92 let efd: i64 = md_open_w(outpath); if efd>=0 { sys_close(efd) } // empty result
93 sys_exit(1); return 1
94 }
95
96 // ---- scan for every magnet:?... ----
97 let outfd: i64 = md_open_w(outpath)
98 if outfd < 0 { md_status(outpath, "error-no-out" as *u8, 0, n); sys_exit(1); return 1 }
99
100 let ihash: *u8 = sys_mmap(32)
101 let trk: *i64 = sys_mmap(MG_NTRK_MAX*8) as *i64
102 let ntrk: *i64 = sys_mmap(16) as *i64
103 let name: *u8 = sys_mmap(K_MAGIC_1024)
104 let hexb: *u8 = sys_mmap(64)
105 let SEENMAX: i64 = 512
106 let seen: *u8 = sys_mmap(SEENMAX*42) // 41 hex+NUL per slot
107 var nseen: i64 = 0
108 var count: i64 = 0
109
110 var i: i64 = 0
111 while i + 8 <= n {
112 var hit: i64 = 0
113 if resp[i]==(109 as u8) { if resp[i+1]==(97 as u8) { if resp[i+2]==(103 as u8) { if resp[i+3]==(110 as u8) { if resp[i+4]==(101 as u8) { if resp[i+5]==(116 as u8) { if resp[i+6]==(58 as u8) { if resp[i+7]==(63 as u8) {
114 hit = 1
115 }}}}}}}}
116 if hit == 1 {
117 // copy the magnet, decoding & -> &
118 let mbuf: *u8 = sys_mmap(K_MAGIC_4096)
119 var j: i64 = i; var k: i64 = 0; var go: i64 = 1
120 while go == 1 {
121 if j >= n { go = 0 } else {
122 let c: i64 = resp[j] as i64
123 if md_is_term(c) == 1 { go = 0 } else {
124 if c == 38 {
125 if k < K_MAGIC_4095 { mbuf[k]=38 as u8; k=k+1 }
126 if j+4 < n { if resp[j+1]==(97 as u8) { if resp[j+2]==(109 as u8) { if resp[j+3]==(112 as u8) { if resp[j+4]==(59 as u8) { j=j+4 } } } } }
127 j = j+1
128 } else {
129 if k < K_MAGIC_4095 { mbuf[k]=c as u8; k=k+1 }
130 j = j+1
131 }
132 }
133 }
134 }
135 mbuf[k] = 0 as u8
136 i = j
137 // parse
138 if mg_parse(mbuf, ihash, trk, MG_NTRK_MAX, ntrk, name) == 1 {
139 md_hex(ihash, hexb)
140 // dedupe by hash
141 var dup: i64 = 0; var s: i64 = 0
142 while s < nseen {
143 var eq: i64 = 1; var x: i64 = 0
144 while x < 40 { if seen[s*42+x] != hexb[x] { eq = 0; x = 40 } else { x = x + 1 } }
145 if eq == 1 { dup = 1; s = nseen } else { s = s + 1 }
146 }
147 if dup == 0 {
148 if nseen < SEENMAX { var x: i64 = 0; while x < 41 { seen[nseen*42+x]=hexb[x]; x=x+1 } nseen = nseen + 1 }
149 // sanitize name: strip tab/newline
150 var y: i64 = 0; while name[y]!=(0 as u8) { let nc: i64 = name[y] as i64; if nc==9 { name[y]=32 as u8 } if nc==10 { name[y]=32 as u8 } if nc==13 { name[y]=32 as u8 } y=y+1 }
151 md_w(outfd, hexb); md_w(outfd, "\t" as *u8)
152 if name[0]==(0 as u8) { md_w(outfd, "(no name)" as *u8) } else { md_w(outfd, name) }
153 md_w(outfd, "\t" as *u8); md_wn(outfd, ntrk[0])
154 md_w(outfd, "\t" as *u8); md_w(outfd, mbuf); md_w(outfd, "\n" as *u8)
155 count = count + 1
156 }
157 }
158 } else { i = i + 1 }
159 }
160 sys_close(outfd)
161 md_status(outpath, "done" as *u8, count, n)
162
163 md_w(1, "DISCOVER url-bytes=" as *u8); md_wn(1, n); md_w(1, " magnets=" as *u8); md_wn(1, count); md_w(1, "\n" as *u8)
164 sys_exit(0)
165 return 0
166}