code wiki / _hdl_build / nx_video_sniff.nx
nx_video_sniff.nx source
↩ module page · 465 lines · 26108 B
1// nx_video_sniff.nx -- the DISCOVERY->DOWNLOAD bridge for JS-hardened video sites. Runs a page's JS on the
2// sovereign engine (via nx_media_jsexec), captures the stream/media URLs its obfuscated code CONSTRUCTS and
3// fetches, RANKS them (a manifest beats a raw segment beats nothing), and emits a DOWNLOAD PLAN routing the
4// best target to the EXISTING X-DLP engine: .m3u8 -> nx_hls_get (handles master/variant + #EXT-X-KEY AES-128
5// decrypt + TS->fMP4), .mp4/.webm -> nx_video_get. This is the "sniff the video URL" step a browser
6// video-download extension does, made native.
7//
8// HONEST CAPABILITY LINE (what this + the X-DLP engine actually beat, and what they don't):
9// * OBFUSCATION -> BEATEN by EXECUTING the bundle (we run its JS) rather than reading it. The URL the code
10// builds at runtime surfaces regardless of how the source is mangled.
11// * SIGNED/TOKENED URLs -> captured WHEN the page builds them client-side from page data (id/hash/consts).
12// If a signed URL requires a SERVER round-trip (fetch config -> server returns signed manifest), round-1
13// pending-capture does NOT have it -> that needs the MULTI-ROUND real-network capture (next rung; here the
14// fetches PEND, no network). Named honestly, not overclaimed.
15// * #EXT-X-KEY AES-128 HLS encryption -> BEATEN (nx_hls_get + nx_aes_cbc, NIST-KAT'd, live-verified).
16// * WIDEVINE / PLAYREADY / FAIRPLAY EME DRM -> the REAL WALL. Content is decrypted by a closed CDM blob with
17// keys from a license server; decrypted frames never touch JS. NOT defeatable natively (needs a CDM) and
18// legally fraught. (ClearKey EME, which puts keys in JS, is the one EME variant that IS reachable -- a
19// later rung.) A site with genuine Widevine is out of reach no matter how good discovery is; MANY sites
20// that LOOK DRM'd are actually just obfuscation + signing + AES-128 = fully in reach.
21import "nx_syscalls.nx"
22import "nx_js_eval.nx"
23import "nx_media_jsexec.nx" // mjx_harvest_js, mjx_contains, mjx_is_media_url
24import "nx_yt_basejs.nx" // bjs_find_nfunc_name (NATIVE name-find -- no JS engine)
25import "nx_yt_nsolve.nx" // ns_solve (LAST-MILE: runs the site's own fn on the browser engine)
26
27const VSK_KILLED: i64 = 0 - 1 // the guard tripped: the page's JS was runaway/hostile and got killed
28const VSK_NONE: i64 = 0
29const VSK_HLS: i64 = 1 // .m3u8 -> nx_hls_get
30const VSK_DASH: i64 = 2 // .mpd -> DASH downloader (future rung; nx_hls_get is HLS-only)
31const VSK_DIRECT: i64 = 3 // .mp4/.webm/.mkv/.mov -> nx_video_get
32const VSK_SEGMENT: i64 = 4 // .ts/.m4s alone (no manifest) -> incomplete without the playlist
33
34// classify ONE url by extension (substring, so a ?token query doesn't defeat it).
35func vs_url_kind(u: *u8, ul: i64) -> i64 {
36 if mjx_contains(u, ul, ".m3u8" as *u8, 5) == 1 { return VSK_HLS }
37 if mjx_contains(u, ul, ".mpd" as *u8, 4) == 1 { return VSK_DASH }
38 if mjx_contains(u, ul, ".mp4" as *u8, 4) == 1 { return VSK_DIRECT }
39 if mjx_contains(u, ul, ".webm" as *u8, 5) == 1 { return VSK_DIRECT }
40 if mjx_contains(u, ul, ".mkv" as *u8, 4) == 1 { return VSK_DIRECT }
41 if mjx_contains(u, ul, ".mov" as *u8, 4) == 1 { return VSK_DIRECT }
42 if mjx_contains(u, ul, ".m4s" as *u8, 4) == 1 { return VSK_SEGMENT }
43 if mjx_contains(u, ul, ".ts" as *u8, 3) == 1 { return VSK_SEGMENT }
44 return VSK_NONE
45}
46// download preference: a MANIFEST (all qualities + segments) beats a direct file beats a bare segment.
47// HLS > DIRECT > DASH > SEGMENT. (DASH ranks below direct only because our downloader can't fetch it yet.)
48func vs_kind_rank(k: i64) -> i64 {
49 if k == VSK_HLS { return 5 }
50 if k == VSK_DIRECT { return 4 }
51 if k == VSK_DASH { return 3 }
52 if k == VSK_SEGMENT { return 2 }
53 return 0
54}
55// the X-DLP route name for a kind (what engine downloads it).
56func vs_route_name(k: i64) -> *u8 {
57 if k == VSK_HLS { return "hls\x00" as *u8 } // nx_hls_get (master/variant + AES-128 + TS->fMP4)
58 if k == VSK_DIRECT { return "direct\x00" as *u8 } // nx_video_get
59 if k == VSK_DASH { return "dash-todo\x00" as *u8 } // not yet downloadable (HLS-only engine)
60 if k == VSK_SEGMENT { return "segment-only\x00" as *u8 } // bare segment, no manifest captured
61 return "none\x00" as *u8
62}
63func vs_puts(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off] = s[i]; off = off + 1; i = i + 1 } return off }
64func vs_putn(dst: *u8, off: i64, v: i64) -> i64 { var m: i64 = v; if m == 0 { dst[off] = 48 as u8; return off + 1 } let t: *u8 = sys_mmap(24); var k: i64 = 0; while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var j: i64 = 0; while j < k { dst[off] = t[k - 1 - j]; off = off + 1; j = j + 1 } return off }
65func vs_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
66func vs_find(hay: *u8, hl: i64, needle: *u8, nl: i64) -> i64 {
67 if nl == 0 { return 0 }
68 if nl > hl { return 0 - 1 }
69 var i: i64 = 0; let last: i64 = hl - nl
70 while i <= last { var j: i64 = 0; var m: i64 = 1; while j < nl { if (hay[i + j] & 0xff) != (needle[j] & 0xff) { m = 0; j = nl } else { j = j + 1 } } if m == 1 { return i } i = i + 1 }
71 return 0 - 1
72}
73func vs_hexval(c: i64) -> i64 { if c >= 48 { if c <= 57 { return c - 48 } } if c >= 97 { if c <= 102 { return c - 87 } } if c >= 65 { if c <= 70 { return c - 55 } } return 0 }
74
75// ============ API layer: the sniff RESULT as JSON + request URL parsing (what a daemon route / MCP tool uses) ============
76// Format a sniff result (kind + its "ROUTE=<r> URL=<u>" plan) as a JSON API response:
77// {"found":true,"route":"hls","url":"https://.../master.m3u8"} (kind > 0)
78// {"found":false,"route":"none","url":null} (none/killed)
79func vs_result_json(kind: i64, plan: *u8, out: *u8, cap: i64) -> i64 {
80 let plen: i64 = vs_slen(plan)
81 let ui: i64 = vs_find(plan, plen, " URL=" as *u8, 5) // splits ROUTE=<r> | URL=<u>
82 var rend: i64 = plen; if ui >= 0 { rend = ui }
83 var o: i64 = 0
84 o = vs_puts(out, o, "{\"found\":" as *u8)
85 if kind > 0 { o = vs_puts(out, o, "true" as *u8) } else { o = vs_puts(out, o, "false" as *u8) }
86 o = vs_puts(out, o, ",\"route\":\"" as *u8)
87 var p: i64 = 6 // skip "ROUTE="
88 while p < rend { if o < (cap - 2) { out[o] = plan[p]; o = o + 1 } p = p + 1 }
89 o = vs_puts(out, o, "\",\"url\":" as *u8)
90 if kind > 0 {
91 if o < (cap - 1) { out[o] = 34 as u8; o = o + 1 } // opening quote
92 var q: i64 = ui + 5
93 while q < plen { let c: i64 = plan[q] & 0xff; if c == 34 { } else { if o < (cap - 2) { out[o] = c as u8; o = o + 1 } } q = q + 1 } // drop stray '"'
94 if o < (cap - 1) { out[o] = 34 as u8; o = o + 1 }
95 } else { o = vs_puts(out, o, "null" as *u8) }
96 o = vs_puts(out, o, "}" as *u8)
97 out[o] = 0 as u8
98 return o
99}
100// Extract the `url=` parameter from an HTTP query string, URL-decoded (%XX + '+'->space). Returns length.
101// The request-parsing half of the API: `GET /media/sniff?url=<percent-encoded page url>`.
102func vs_query_url(q: *u8, qlen: i64, out: *u8, cap: i64) -> i64 {
103 // find "url=" as a PARAMETER (at position 0 or right after '&'), not any substring (else "noturl=" matches).
104 var start: i64 = 0 - 1
105 var si: i64 = 0
106 while si < qlen {
107 var isp: i64 = 0
108 if si == 0 { isp = 1 } else { let pc: i64 = q[si - 1] & 0xff; if pc == 38 { isp = 1 } if pc == 63 { isp = 1 } } // '&' or '?' boundary
109 if isp == 1 { if (si + 4) <= qlen { if (q[si] & 0xff) == 117 { if (q[si + 1] & 0xff) == 114 { if (q[si + 2] & 0xff) == 108 { if (q[si + 3] & 0xff) == 61 { start = si; si = qlen } } } } } }
110 si = si + 1
111 }
112 if start < 0 { out[0] = 0 as u8; return 0 }
113 var i: i64 = start + 4
114 var o: i64 = 0
115 var go: i64 = 1
116 while go == 1 {
117 if i >= qlen { go = 0 }
118 else {
119 let c: i64 = q[i] & 0xff
120 if c == 38 { go = 0 } // '&' ends the value
121 else {
122 if c == 37 { // '%XX'
123 if (i + 2) < qlen { let hi: i64 = vs_hexval(q[i + 1] & 0xff); let lo: i64 = vs_hexval(q[i + 2] & 0xff); if o < (cap - 1) { out[o] = (hi * 16 + lo) as u8; o = o + 1 } i = i + 3 } else { i = i + 1 }
124 } else {
125 if c == 43 { if o < (cap - 1) { out[o] = 32 as u8; o = o + 1 } i = i + 1 } // '+' -> space
126 else { if o < (cap - 1) { out[o] = c as u8; o = o + 1 } i = i + 1 }
127 }
128 }
129 }
130 }
131 out[o] = 0 as u8
132 return o
133}
134
135// N-THROTTLE de-throttle, as a BROWSER-SNIFFER capability (per the JS = last-mile-only rule): given the player
136// `basejs` and a throttled `nval`, find the transform function's name NATIVELY (bjs_find_nfunc_name = text
137// analysis, no engine) then RUN that function -- the site's OWN code -- via ns_solve on the browser's JS engine
138// (the last mile). Writes n' to `out`, returns its length (or a negative ns_solve error / 0 if no name found).
139// This lives in the sniffer so the JS engine stays confined to the browser layer; the native extraction core
140// (nx_media_extract/signal/state/heal) never imports it. Callers: the sniffer/daemon turning a throttled
141// googlevideo URL into a full-speed one.
142func vs_solve_n(basejs: *u8, bl: i64, nval: *u8, nl: i64, out: *u8, cap: i64) -> i64 {
143 let name: *u8 = sys_mmap(256)
144 let namelen: i64 = bjs_find_nfunc_name(basejs, bl, name, 256)
145 if namelen == 0 { out[0] = 0 as u8; return 0 } // no name -> a self-heal LEARN signal
146 return ns_solve(basejs, bl, name, namelen, nval, nl, out, cap)
147}
148
149// SNIFF: run page JS, capture media URLs (into listbuf, \n-sep), pick the best download target, and write a
150// one-line PLAN into planbuf ("ROUTE=<route> URL=<besturl>"). Returns the primary kind (VSK_*). planbuf>=2560.
151func vs_sniff(js: *u8, jslen: i64, listbuf: *u8, listcap: i64, planbuf: *u8, plancap: i64) -> i64 {
152 let obx: *i64 = sys_mmap(16) as *i64
153 mjx_harvest_js(js, jslen, listbuf, listcap, obx) // listbuf = '\n'-separated deduped media URLs
154 let line: *u8 = sys_mmap(2048)
155 let best: *u8 = sys_mmap(2048)
156 best[0] = 0 as u8
157 var bestrank: i64 = 0
158 var bestkind: i64 = VSK_NONE
159 var p: i64 = 0
160 var go: i64 = 1
161 while go == 1 {
162 if listbuf[p] == (0 as u8) { go = 0 }
163 else {
164 // pull one line into `line`
165 var q: i64 = 0
166 var inl: i64 = 1
167 while inl == 1 {
168 let c: i64 = listbuf[p] & 0xff
169 if c == 0 { inl = 0 }
170 else {
171 if c == 10 { inl = 0; p = p + 1 }
172 else { if q < 2047 { line[q] = c as u8; q = q + 1 } p = p + 1 }
173 }
174 }
175 line[q] = 0 as u8
176 if q > 0 {
177 let k: i64 = vs_url_kind(line, q)
178 let r: i64 = vs_kind_rank(k)
179 if r > bestrank { bestrank = r; bestkind = k; var j: i64 = 0; while j < q { best[j] = line[j]; j = j + 1 } best[q] = 0 as u8 }
180 }
181 }
182 }
183 var o: i64 = 0
184 o = vs_puts(planbuf, o, "ROUTE=" as *u8)
185 o = vs_puts(planbuf, o, vs_route_name(bestkind))
186 o = vs_puts(planbuf, o, " URL=" as *u8)
187 if bestkind != VSK_NONE { o = vs_puts(planbuf, o, best) } else { o = vs_puts(planbuf, o, "-" as *u8) }
188 planbuf[o] = 0 as u8
189 return bestkind
190}
191
192// vs_sniff + DOM: identical harvest+plan, but seeds `dochtml` as the DOM so DOM-reading player JS (a plugin that
193// reads a data-* stream token off document.getElementById(...)) resolves its element and builds the real URL.
194func vs_sniff_dom(js: *u8, jslen: i64, dochtml: *u8, doclen: i64, listbuf: *u8, listcap: i64, planbuf: *u8, plancap: i64) -> i64 {
195 let obx: *i64 = sys_mmap(16) as *i64
196 mjx_harvest_js_dom(js, jslen, dochtml, doclen, listbuf, listcap, obx)
197 let line: *u8 = sys_mmap(2048)
198 let best: *u8 = sys_mmap(2048)
199 best[0] = 0 as u8
200 var bestrank: i64 = 0
201 var bestkind: i64 = VSK_NONE
202 var p: i64 = 0
203 var go: i64 = 1
204 while go == 1 {
205 if listbuf[p] == (0 as u8) { go = 0 }
206 else {
207 var q: i64 = 0
208 var inl: i64 = 1
209 while inl == 1 {
210 let c: i64 = listbuf[p] & 0xff
211 if c == 0 { inl = 0 }
212 else {
213 if c == 10 { inl = 0; p = p + 1 }
214 else { if q < 2047 { line[q] = c as u8; q = q + 1 } p = p + 1 }
215 }
216 }
217 line[q] = 0 as u8
218 if q > 0 {
219 let k: i64 = vs_url_kind(line, q)
220 let r: i64 = vs_kind_rank(k)
221 if r > bestrank { bestrank = r; bestkind = k; var j: i64 = 0; while j < q { best[j] = line[j]; j = j + 1 } best[q] = 0 as u8 }
222 }
223 }
224 }
225 var o: i64 = 0
226 o = vs_puts(planbuf, o, "ROUTE=" as *u8)
227 o = vs_puts(planbuf, o, vs_route_name(bestkind))
228 o = vs_puts(planbuf, o, " URL=" as *u8)
229 if bestkind != VSK_NONE { o = vs_puts(planbuf, o, best) } else { o = vs_puts(planbuf, o, "-" as *u8) }
230 planbuf[o] = 0 as u8
231 return bestkind
232}
233
234// GUARDED SNIFF -- run vs_sniff on UNTRUSTED page JS inside a FORKED CHILD that self-caps CPU-seconds
235// (RLIMIT_CPU) and virtual memory (RLIMIT_AS) BEFORE executing it. A hostile `while(true){}` (CPU spin) or
236// an alloc-bomb is KILLED by the KERNEL; the parent's blocking wait4 returns in ~cpu_secs and NEVER hangs or
237// OOMs. This is process-isolation safety -- it needs NO interpreter fuel budget (so it does NOT touch the
238// contended nx_js_eval hot loop) and makes running arbitrary LIVE bundles safe by construction. The child
239// hands the plan back through SHARED memory; a signaled (killed) child -> VSK_KILLED + a guard plan.
240// planbuf >= 2560. cpu_secs = the CPU budget (e.g. 5). Returns the primary kind, or VSK_KILLED.
241func vs_sniff_guarded(js: *u8, jslen: i64, planbuf: *u8, plancap: i64, cpu_secs: i64) -> i64 {
242 let shplan: *u8 = sys_mmap_shared(2560) // child writes the plan here, parent reads it
243 let shkind: *i64 = sys_mmap_shared(16) as *i64
244 shkind[0] = VSK_KILLED // default (child overwrites on clean completion)
245 shplan[0] = 0 as u8
246 let pid: i64 = sys_fork()
247 if pid < 0 { // fork failed -> FAIL SAFE (never run untrusted JS unguarded)
248 var e: i64 = vs_puts(planbuf, 0, "ROUTE=guard-unavailable URL=- (fork failed)" as *u8)
249 planbuf[e] = 0 as u8
250 return VSK_KILLED
251 }
252 if pid == 0 {
253 // CHILD: cap CPU + address space, THEN run the untrusted page JS.
254 let rl: *i64 = sys_mmap(16) as *i64
255 rl[0] = cpu_secs; rl[1] = cpu_secs + 1 // rlimit64{soft,hard}; SIGXCPU at soft, SIGKILL at hard
256 nx_prlimit(0, RLIMIT_CPU, rl as *u8, 0 as *u8)
257 let ra: *i64 = sys_mmap(16) as *i64
258 ra[0] = 8589934592; ra[1] = 8589934592 // 8 GiB virtual-memory cap (alloc-bomb backstop; generous
259 nx_prlimit(0, RLIMIT_AS, ra as *u8, 0 as *u8) // so the parent's TLS-stack footprint can't false-trip it)
260 let lb: *u8 = sys_mmap(8192)
261 let k: i64 = vs_sniff(js, jslen, lb, 8192, shplan, 2560)
262 shkind[0] = k
263 sys_exit(0)
264 }
265 // PARENT: block until the child exits or the kernel kills it (bounded by cpu_secs) -- never an infinite wait.
266 let st: *i64 = sys_mmap(16) as *i64
267 sys_wait4(pid, st, 0)
268 // copy the shared plan into the caller's buffer
269 var i: i64 = 0; var go: i64 = 1
270 while go == 1 {
271 if i >= (plancap - 1) { go = 0 }
272 else { let c: i64 = shplan[i] & 0xff; planbuf[i] = c as u8; if c == 0 { go = 0 } else { i = i + 1 } }
273 }
274 planbuf[i] = 0 as u8
275 // WIFSIGNALED: low 7 status bits != 0 => killed by a signal (SIGXCPU/SIGKILL/SIGSEGV) => the guard tripped.
276 if (st[0] & 0x7f) != 0 {
277 var o: i64 = vs_puts(planbuf, 0, "ROUTE=guard-killed URL=- (page JS killed by signal " as *u8)
278 o = vs_putn(planbuf, o, st[0] & 0x7f) // 24=SIGXCPU(cpu cap), 9=SIGKILL, 11=SIGSEGV
279 o = vs_puts(planbuf, o, ")" as *u8)
280 planbuf[o] = 0 as u8
281 return VSK_KILLED
282 }
283 return shkind[0]
284}
285
286// vs_sniff_guarded + DOM: same forked-child CPU/mem-capped guard, but seeds `dochtml` as the DOM for the run so
287// DOM-reading player JS resolves (fork copies dochtml into the child; safe by construction, identical guard).
288func vs_sniff_guarded_dom(js: *u8, jslen: i64, dochtml: *u8, doclen: i64, planbuf: *u8, plancap: i64, cpu_secs: i64) -> i64 {
289 let shplan: *u8 = sys_mmap_shared(2560)
290 let shkind: *i64 = sys_mmap_shared(16) as *i64
291 shkind[0] = VSK_KILLED
292 shplan[0] = 0 as u8
293 let pid: i64 = sys_fork()
294 if pid < 0 {
295 var e: i64 = vs_puts(planbuf, 0, "ROUTE=guard-unavailable URL=- (fork failed)" as *u8)
296 planbuf[e] = 0 as u8
297 return VSK_KILLED
298 }
299 if pid == 0 {
300 let rl: *i64 = sys_mmap(16) as *i64
301 rl[0] = cpu_secs; rl[1] = cpu_secs + 1
302 nx_prlimit(0, RLIMIT_CPU, rl as *u8, 0 as *u8)
303 let ra: *i64 = sys_mmap(16) as *i64
304 ra[0] = 8589934592; ra[1] = 8589934592
305 nx_prlimit(0, RLIMIT_AS, ra as *u8, 0 as *u8)
306 let lb: *u8 = sys_mmap(8192)
307 let k: i64 = vs_sniff_dom(js, jslen, dochtml, doclen, lb, 8192, shplan, 2560)
308 shkind[0] = k
309 sys_exit(0)
310 }
311 let st: *i64 = sys_mmap(16) as *i64
312 sys_wait4(pid, st, 0)
313 var i: i64 = 0; var go: i64 = 1
314 while go == 1 {
315 if i >= (plancap - 1) { go = 0 }
316 else { let c: i64 = shplan[i] & 0xff; planbuf[i] = c as u8; if c == 0 { go = 0 } else { i = i + 1 } }
317 }
318 planbuf[i] = 0 as u8
319 if (st[0] & 0x7f) != 0 {
320 var o: i64 = vs_puts(planbuf, 0, "ROUTE=guard-killed URL=- (page JS killed by signal " as *u8)
321 o = vs_putn(planbuf, o, st[0] & 0x7f)
322 o = vs_puts(planbuf, o, ")" as *u8)
323 planbuf[o] = 0 as u8
324 return VSK_KILLED
325 }
326 return shkind[0]
327}
328
329// lowercase one byte (for case-insensitive HTML tag matching -- <SCRIPT>, <Script>, <script> all match).
330func vs_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
331func vs_ci_at(buf: *u8, pos: i64, hlen: i64, lit: *u8, litlen: i64) -> i64 {
332 if (pos + litlen) > hlen { return 0 }
333 var j: i64 = 0
334 while j < litlen { if vs_lc(buf[pos + j] & 0xff) != (lit[j] & 0xff) { return 0 } j = j + 1 }
335 return 1
336}
337// extract every INLINE <script>...</script> body from HTML into `out`, ';'+newline separated so the scripts
338// run as one program (top-level vars shared across scripts, as in a real page). External <script src=...>
339// bodies are empty here -> contribute nothing (the live wire fetches those bundles separately + appends).
340// Returns bytes written. Case-insensitive tag match; tolerant of a missing close tag (body runs to EOF).
341func vs_extract_scripts(html: *u8, hlen: i64, out: *u8, cap: i64) -> i64 {
342 var o: i64 = 0
343 var i: i64 = 0
344 while i < hlen {
345 if vs_ci_at(html, i, hlen, "<script" as *u8, 7) == 1 {
346 var j: i64 = i + 7 // scan to end of the open tag '>'
347 var gt: i64 = 0
348 while gt == 0 {
349 if j >= hlen { gt = 1 } else { if (html[j] & 0xff) == 62 { gt = 1 } else { j = j + 1 } }
350 }
351 let b: i64 = j + 1 // body starts after '>'
352 var k: i64 = b // scan to "</script"
353 var cl: i64 = 0
354 while cl == 0 {
355 if k >= hlen { cl = 1 } else { if vs_ci_at(html, k, hlen, "</script" as *u8, 8) == 1 { cl = 1 } else { k = k + 1 } }
356 }
357 var p: i64 = b
358 while p < k { if o < (cap - 3) { out[o] = html[p]; o = o + 1 } p = p + 1 }
359 if o < (cap - 3) { out[o] = 59 as u8; o = o + 1; out[o] = 10 as u8; o = o + 1 } // ";\n"
360 i = k
361 } else { i = i + 1 }
362 }
363 out[o] = 0 as u8
364 return o
365}
366// scan the <script...> open tag starting at `ts` (the '<') for a src="..."/src='...' attribute; copy the
367// URL into `out`, set endbox[0] = index just past '>', and return the URL length (or -1 if no src). Handles
368// double/single quotes, src anywhere in the tag (after type=, etc.), case-insensitive.
369func vs_script_src(html: *u8, ts: i64, hlen: i64, out: *u8, urlcap: i64, endbox: *i64) -> i64 {
370 var j: i64 = ts + 7
371 var urllen: i64 = 0 - 1
372 var scanning: i64 = 1
373 while scanning == 1 {
374 if j >= hlen { scanning = 0; endbox[0] = hlen }
375 else {
376 let c: i64 = html[j] & 0xff
377 if c == 62 { scanning = 0; endbox[0] = j + 1 } // '>' ends the open tag
378 else {
379 var q: i64 = 0 - 1
380 if vs_ci_at(html, j, hlen, "src=\"" as *u8, 5) == 1 { q = 34 }
381 else { if vs_ci_at(html, j, hlen, "src='" as *u8, 5) == 1 { q = 39 } }
382 if q >= 0 {
383 let s: i64 = j + 5
384 var e: i64 = s
385 var fe: i64 = 0
386 while fe == 0 { if e >= hlen { fe = 1 } else { if (html[e] & 0xff) == q { fe = 1 } else { e = e + 1 } } }
387 var w: i64 = 0
388 var p: i64 = s
389 while p < e {
390 if vs_ci_at(html, p, e, "&" as *u8, 5) == 1 { if w < (urlcap - 1) { out[w] = 38 as u8; w = w + 1 } p = p + 5 } // & -> &
391 else { if w < (urlcap - 1) { out[w] = html[p]; w = w + 1 } p = p + 1 }
392 }
393 out[w] = 0 as u8
394 urllen = w
395 j = e // keep scanning to '>' (don't miss it)
396 } else { j = j + 1 }
397 }
398 }
399 }
400 return urllen
401}
402// extract every EXTERNAL <script src=...> URL into `out` (newline-separated); returns the count. The live
403// wire fetches each (resolving relative->absolute) + appends before the guarded sniff -- a hardened SPA's
404// real code lives in an external bundle, so this is what tells the wire what to pull.
405func vs_extract_script_srcs(html: *u8, hlen: i64, out: *u8, cap: i64) -> i64 {
406 let urlbuf: *u8 = sys_mmap(4096)
407 let endbox: *i64 = sys_mmap(8) as *i64
408 var o: i64 = 0
409 var count: i64 = 0
410 var i: i64 = 0
411 while i < hlen {
412 if vs_ci_at(html, i, hlen, "<script" as *u8, 7) == 1 {
413 endbox[0] = i + 7
414 let ul: i64 = vs_script_src(html, i, hlen, urlbuf, 4096, endbox)
415 if ul > 0 {
416 var p: i64 = 0
417 while p < ul { if o < (cap - 2) { out[o] = urlbuf[p]; o = o + 1 } p = p + 1 }
418 if o < (cap - 2) { out[o] = 10 as u8; o = o + 1 }
419 count = count + 1
420 }
421 i = endbox[0]
422 } else { i = i + 1 }
423 }
424 out[o] = 0 as u8
425 return count
426}
427// PAGE SNIFF: extract a page's inline scripts and run them through the GUARDED sniff -> download plan.
428// This is "paste a video-page URL, get the stream": the live wire fetches the HTML over sovereign TLS
429// (nx_video_fetch) and any external <script src> bundles, then calls this. Untrusted-JS-safe by the guard.
430func vs_sniff_page(html: *u8, hlen: i64, planbuf: *u8, plancap: i64, cpu_secs: i64) -> i64 {
431 let js: *u8 = sys_mmap(262144) // 256KB for concatenated inline scripts
432 let n: i64 = vs_extract_scripts(html, hlen, js, 262144)
433 return vs_sniff_guarded_dom(js, n, html, hlen, planbuf, plancap, cpu_secs) // seed the page HTML as the DOM
434}
435// PAGE SNIFF WITH EXTERNAL BUNDLE(S): inline scripts FIRST, then the already-fetched external bundle bytes
436// appended (';'+newline separated), then guarded-sniff. This is exactly the live wire's assembly (the only
437// live part it adds is the TLS GET of each <script src>); gateable by passing a synthetic fetched bundle.
438// `extjs`/`extlen` = the concatenation of every external bundle the wire fetched (in <script src> order).
439func vs_sniff_page_bundled(html: *u8, hlen: i64, extjs: *u8, extlen: i64, planbuf: *u8, plancap: i64, cpu_secs: i64) -> i64 {
440 let cap: i64 = 1048576 // 1MB combined (inline + external bundles)
441 let js: *u8 = sys_mmap(cap)
442 var o: i64 = vs_extract_scripts(html, hlen, js, cap) // inline first (shares top-level scope w/ bundles)
443 var p: i64 = 0
444 while p < extlen { if o < (cap - 3) { js[o] = extjs[p]; o = o + 1 } p = p + 1 }
445 if o < (cap - 3) { js[o] = 59 as u8; o = o + 1; js[o] = 10 as u8; o = o + 1 } // ";\n"
446 js[o] = 0 as u8
447 return vs_sniff_guarded_dom(js, o, html, hlen, planbuf, plancap, cpu_secs) // seed the page HTML as the DOM
448}
449// PREAMBLE variant: a surrogate scriptlet PREAMBLE (from nx_surrogate) runs FIRST, before the page's own inline
450// scripts + external bundles -- so the page's anti-adblock / tracker gates read back as success and the gated
451// media fetch fires. Same guarded (fork+rlimit) execution; the preamble shares top-level scope with the page.
452func vs_sniff_page_bundled_pre(preamble: *u8, plen: i64, html: *u8, hlen: i64, extjs: *u8, extlen: i64, planbuf: *u8, plancap: i64, cpu_secs: i64) -> i64 {
453 let cap: i64 = 1048576
454 let js: *u8 = sys_mmap(cap)
455 var o: i64 = 0
456 var i: i64 = 0
457 while i < plen { if o < (cap - 3) { js[o] = preamble[i]; o = o + 1 } i = i + 1 } // preamble FIRST
458 if o < (cap - 3) { js[o] = 59 as u8; o = o + 1; js[o] = 10 as u8; o = o + 1 } // ";\n"
459 o = o + vs_extract_scripts(html, hlen, ((js as i64) + o) as *u8, cap - o) // then inline scripts
460 var p: i64 = 0
461 while p < extlen { if o < (cap - 3) { js[o] = extjs[p]; o = o + 1 } p = p + 1 } // then external bundles
462 if o < (cap - 3) { js[o] = 59 as u8; o = o + 1; js[o] = 10 as u8; o = o + 1 }
463 js[o] = 0 as u8
464 return vs_sniff_guarded_dom(js, o, html, hlen, planbuf, plancap, cpu_secs) // seed the page HTML as the DOM
465}