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, "&amp;" as *u8, 5) == 1 { if w < (urlcap - 1) { out[w] = 38 as u8; w = w + 1 } p = p + 5 } // &amp; -> & 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}