code wiki / (root) / nx_cam_chaturbate_probe.nx

nx_cam_chaturbate_probe.nx source

↩ module page · 283 lines · 13796 B

1// nx_cam_chaturbate_probe.nx -- the NETWORK DRIVER for nx_cam_chaturbate (/compare/mediaingest R9). 2// 3// SPLIT ON PURPOSE, following the R1 precedent in this same lane (nx_source_health.nx pure lib + 4// nx_source_health_probe.nx driver): the decision core stays import-light so its gate can prove every 5// state with no network and no TLS closure, and only THIS organ carries the fetch stack. The lane has 6// already paid for ignoring that split once -- nx_media_grab still cannot take its pacing import 7// because the added closure tipped it past the nx_cc module limit. 8// 9// VERBS 10// nx_cam_chaturbate_probe bulk [user] 11// GET /affiliates/api/onlinerooms/?format=json -- ONE request that carries EVERY online room. 12// With no user it prints the record count; with a user it prints that room's state. 13// THIS IS THE RATE-LIMIT-SAFE PATH and the default way to watch a fleet: N performers cost one 14// call, not N. Paying one ajax per performer is what flags an exit IP into a site-wide 403. 15// nx_cam_chaturbate_probe resolve <user> 16// POST /get_edge_hls_url_ajax/ -- the per-room URL resolve. Costs Cloudflare exposure, so it is 17// for the moment a recording actually starts, never for polling. 18// 19// *** THE RESOLVED URL IS NEVER FETCHED BY THIS ORGAN. *** 20// The edge token it carries is single-use / connection-bound: any fetch of the tokenised playlist -- 21// including a "just checking it works" GET -- consumes it and the real consumer then gets 403. This 22// organ prints the URL and stops. cb_capture_contract() states that rule in a return value so a gate 23// can assert it from outside, rather than in a comment no caller can see. 24// 25// Both verbs go through the R0 pacer, so this cannot itself become the thing that gets us throttled. 26// Egress is sovereign-owned only: no residential proxy pool, ever (standing bound, R12). 27// license_tier: ORIGINAL 28 29import "nx_syscalls.nx" 30import "nx_cam_chaturbate.nx" 31import "nx_source_health.nx" // R1 sh_preflight -- see p_preflight_skip / p_record below 32import "nx_x509_trust_store.nx" 33import "nx_tls13_client_validate_certificate.nx" 34import "nx_tls13_client_session_run.nx" 35import "nx_tls13_chrome_session.nx" 36import "nx_https_url_for_fetch.nx" 37import "nx_https_url_connect.nx" 38import "nx_https_post_complete.nx" 39import "nx_https_fetch_follow.nx" 40import "nx_http_response_parse.nx" 41import "nx_csprng.nx" 42import "nx_paced_fetch.nx" 43 44const P_OUT_CAP: i64 = 8388608 // 8 MiB: the online-rooms feed is large and must not be truncated 45const P_URL_CAP: i64 = 4096 46const P_BODY_CAP: i64 = 1024 47const P_RAND_BYTES: i64 = 32 48const P_VC_BYTES: i64 = 128 49const P_TARGET_BYTES: i64 = 64 50const P_FDP_BYTES: i64 = 16 51const P_SOCK_TMO_S: i64 = 20 52const P_MAX_HOPS: i64 = 4 53const P_TRUST_CERTS: i64 = 300 54const P_TRUST_CAP: i64 = 4194304 55const P_OFF_BYTES: i64 = 16 56const P_RESP_FIELDS_BYTES: i64 = 128 // nx_http_response_parse writes its parsed fields into this array 57const P_HOST: *u8 = "chaturbate.com" 58 59const P_CERTDATA: *u8 = "data/mozilla_certdata.txt" 60const P_AJAX_URL: *u8 = "https://chaturbate.com/get_edge_hls_url_ajax/" 61const P_AJAX_PATH: *u8 = "/get_edge_hls_url_ajax/" 62const P_BULK_URL: *u8 = "https://chaturbate.com/affiliates/api/onlinerooms/?format=json" 63const P_CT_FORM: *u8 = "application/x-www-form-urlencoded" 64const P_XHDR: *u8 = "X-Requested-With: XMLHttpRequest\r\n" 65 66const P_EXIT_USAGE: i64 = 2 67const P_EXIT_TRUST: i64 = 3 68const P_EXIT_CONNECT: i64 = 4 69const P_EXIT_TLS: i64 = 5 70const P_EXIT_HTTP: i64 = 6 71 72func p_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 73func p_w(s: *u8) -> i64 { sys_write(1, s, p_slen(s)); return 0 } 74func p_wb(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 } 75func p_wn(v: i64) -> i64 { cb_wn(v); return 0 } 76 77// header/body split: the first CRLFCRLF. Returns the body offset, or -1. 78func p_body_off(buf: *u8, n: i64) -> i64 { 79 var i: i64 = 0 80 while i + 3 < n { 81 if buf[i] == (13 as u8) { 82 if buf[i+1] == (10 as u8) { 83 if buf[i+2] == (13 as u8) { if buf[i+3] == (10 as u8) { return i + 4 } } 84 } 85 } 86 i = i + 1 87 } 88 return 0 - 1 89} 90 91// ---- POST the ajax and return the raw response length; sets status_out[0]. ---- 92// The ONLY network call this verb makes. It does NOT touch the URL it comes back with. 93func p_post_ajax(user: *u8, out: *u8, out_cap: i64, status_out: *i64) -> i64 { 94 status_out[0] = 0 95 let lr: i64 = nx_trust_store_load_from_certdata(P_CERTDATA, P_TRUST_CERTS, P_TRUST_CAP) 96 if lr <= 0 { return 0 - P_EXIT_TRUST } 97 let store: *TrustStore = lr as *TrustStore 98 99 let urlbuf: *u8 = sys_mmap(P_URL_CAP) 100 var ui: i64 = 0 101 while P_AJAX_URL[ui] != (0 as u8) { urlbuf[ui] = P_AJAX_URL[ui]; ui = ui + 1 } 102 urlbuf[ui] = 0 as u8 103 104 let target_raw: *u8 = sys_mmap(P_TARGET_BYTES) 105 let target: *NxHttpsTarget = target_raw as *NxHttpsTarget 106 target.url = nx_url_new() 107 target.port = 0 108 if nx_https_url_for_fetch(urlbuf, target) != NX_HTTPS_URL_OK { return 0 - P_EXIT_CONNECT } 109 110 let fd_p: *i64 = (sys_mmap(P_FDP_BYTES)) as *i64 111 if nx_https_url_connect(target, urlbuf, sys_now_realtime_sec(), fd_p) != NX_HTTPS_CONNECT_OK { return 0 - P_EXIT_CONNECT } 112 let fd: i64 = fd_p[0] 113 sys_set_socket_timeout(fd, P_SOCK_TMO_S) 114 115 let cr: *u8 = sys_mmap(P_RAND_BYTES); nx_csprng_fill(cr, P_RAND_BYTES) 116 let priv: *u8 = sys_mmap(P_RAND_BYTES); nx_csprng_fill(priv, P_RAND_BYTES) 117 let vc_raw: *u8 = sys_mmap(P_VC_BYTES) 118 let vc: *TlsValidationContext = vc_raw as *TlsValidationContext 119 vc.store = store 120 vc.sni_host = urlbuf + target.url.host_off 121 vc.sni_host_len = target.url.host_len 122 vc.now_epoch = sys_now_realtime_sec() 123 vc.cached_cert = 0 as *u8 124 vc.cached_cert_len = 0 125 vc.cert_out = 0 as *u8 126 vc.cert_out_cap = 0 127 vc.cert_out_len = 0 128 129 // Chrome-JA3 hello: CB's edge fingerprints its clients, so the minimal hello draws a challenge. 130 let sr: i64 = nx_tls13_client_session_run_chrome(fd, urlbuf + target.url.host_off, target.url.host_len, cr, priv, vc) 131 if sr <= 0 { sys_close(fd); return 0 - P_EXIT_TLS } 132 let s: *Tls13ClientSession = sr as *Tls13ClientSession 133 134 let body: *u8 = sys_mmap(P_BODY_CAP) 135 let bn: i64 = cb_build_body(user, body, P_BODY_CAP) 136 if bn <= 0 { sys_close(fd); return 0 - P_EXIT_USAGE } 137 138 let n: i64 = nx_https_post_complete_xhdr( 139 s, fd, 140 P_AJAX_PATH, p_slen(P_AJAX_PATH), 141 urlbuf + target.url.host_off, target.url.host_len, 142 P_CT_FORM, p_slen(P_CT_FORM), 143 body, bn, 144 "" as *u8, 0, 145 P_XHDR, p_slen(P_XHDR), 146 out, out_cap 147 ) 148 sys_close(fd) 149 if n <= 0 { return 0 - P_EXIT_HTTP } 150 151 let r: *i64 = (sys_mmap(P_RESP_FIELDS_BYTES)) as *i64 152 if nx_http_response_parse(out, n, r) != 0 { return 0 - P_EXIT_HTTP } 153 status_out[0] = r[1] 154 return n 155} 156 157// ---- R1 SOURCE HEALTH IN THE SHIPPING PATH ----------------------------------------------------- 158// nx_compare_rank measured R1 as FINISH state=LIB-GATE-ONLY importers=2 -- sh_preflight was imported 159// ONLY by its own gate and its own probe, i.e. by validation organs, never by a program that ships 160// work. A contract symbol that exists and that nothing in the working path calls is the adoption 161// inversion the estate keeps paying for: the rung reads done on the board and buys nothing. These two 162// helpers are the smallest honest fix -- the chaturbate lane ASKS before it spends a request and 163// RECORDS what happened after, so the persisted verdict a flapping CDN writes is actually consulted. 164func p_preflight_skip(now_ms: i64) -> i64 { 165 let v: i64 = sh_lookup(P_HOST, p_slen(P_HOST), now_ms) 166 if sh_should_skip(v) == 1 { 167 p_w("SKIP host=" as *u8); p_w(P_HOST) 168 p_w(" source-health=" as *u8); p_w(sh_verdict_str(v)) 169 p_w(" -- a persisted DEAD or WALLED verdict inside its TTL; not spending a request on it\n" as *u8) 170 return 1 171 } 172 return 0 173} 174 175// connect_ok is derived from what we actually observed, never assumed: a status line means the peer 176// answered. wall_code stays SH_ABT_CLEAN here because per-response bot-wall classification is R13's 177// job in the capture path, not this pre-flight's -- claiming a wall we did not classify would be the 178// over-blocking false positive R1 already had to correct once. 179func p_record(connect_ok: i64, status: i64, elapsed_ms: i64, now_ms: i64) -> i64 { 180 return sh_preflight(P_HOST, p_slen(P_HOST), connect_ok, status, elapsed_ms, SH_ABT_CLEAN, now_ms) 181} 182 183func main(argc: i64, argv: *i64) -> i64 { 184 if argc < 2 { 185 p_w("usage: nx_cam_chaturbate_probe bulk [user] | resolve <user>\n" as *u8) 186 p_w(" bulk -- ONE request covering every online room (the rate-limit-safe fleet path)\n" as *u8) 187 p_w(" resolve -- per-room POST; costs Cloudflare exposure, use only at record start\n" as *u8) 188 p_w(" NOTE: a resolved URL carries a SINGLE-USE edge token. This organ never fetches it.\n" as *u8) 189 sys_exit(P_EXIT_USAGE); return P_EXIT_USAGE 190 } 191 let verb: *u8 = argv[1] as *u8 192 193 // ---- bulk ---- 194 if cb_ieq(verb, 0, p_slen(verb), "bulk" as *u8) == 1 { 195 let store_r: i64 = nx_trust_store_load_from_certdata(P_CERTDATA, P_TRUST_CERTS, P_TRUST_CAP) 196 if store_r <= 0 { p_w("BULK trust-load FAIL\n" as *u8); sys_exit(P_EXIT_TRUST); return P_EXIT_TRUST } 197 let store: *TrustStore = store_r as *TrustStore 198 let out: *u8 = sys_mmap(P_OUT_CAP) 199 let st: *i64 = (sys_mmap(P_OFF_BYTES)) as *i64 200 let t0: i64 = sys_now_realtime_ms() 201 if p_preflight_skip(t0) == 1 { return 0 } 202 pf_before_burst(P_BULK_URL) 203 let n: i64 = nx_https_fetch_follow_chrome(P_BULK_URL, store, out, P_OUT_CAP, P_MAX_HOPS, st) 204 let t1: i64 = sys_now_realtime_ms() 205 pf_after(P_BULK_URL, st[0], 0) 206 var ok0: i64 = 0 207 if st[0] > 0 { ok0 = 1 } 208 p_record(ok0, st[0], t1 - t0, t1) 209 p_w("BULK http=" as *u8); p_wn(st[0]); p_w(" bytes=" as *u8); p_wn(n); p_w("\n" as *u8) 210 if n <= 0 { p_w("BULK fetch FAILED (rc above is -verdict)\n" as *u8); sys_exit(P_EXIT_HTTP); return P_EXIT_HTTP } 211 if argc >= 3 { 212 let user: *u8 = argv[2] as *u8 213 let off: *i64 = (sys_mmap(P_OFF_BYTES)) as *i64 214 let stt: i64 = cb_bulk_lookup(out, n, user, off) 215 p_w("BULK user=" as *u8); p_w(user); p_w(" state=" as *u8); p_w(cb_state_name(stt)) 216 p_w(" recordable=" as *u8); p_wn(cb_is_recordable(stt)); p_w("\n" as *u8) 217 } else { 218 p_w("BULK records=" as *u8); p_wn(cb_bulk_count(out, n)); p_w("\n" as *u8) 219 } 220 return 0 221 } 222 223 // ---- resolve ---- 224 if cb_ieq(verb, 0, p_slen(verb), "resolve" as *u8) == 1 { 225 if argc < 3 { p_w("usage: nx_cam_chaturbate_probe resolve <user>\n" as *u8); sys_exit(P_EXIT_USAGE); return P_EXIT_USAGE } 226 let user: *u8 = argv[2] as *u8 227 let out: *u8 = sys_mmap(P_OUT_CAP) 228 let st: *i64 = (sys_mmap(P_OFF_BYTES)) as *i64 229 let t0: i64 = sys_now_realtime_ms() 230 if p_preflight_skip(t0) == 1 { return 0 } 231 pf_before_burst(P_AJAX_URL) 232 let n: i64 = p_post_ajax(user, out, P_OUT_CAP, st) 233 let t1: i64 = sys_now_realtime_ms() 234 pf_after(P_AJAX_URL, st[0], 0) 235 var okr: i64 = 0 236 if st[0] > 0 { okr = 1 } 237 p_record(okr, st[0], t1 - t0, t1) 238 if n <= 0 { p_w("RESOLVE transport FAILED rc=" as *u8); p_wn(n); p_w("\n" as *u8); sys_exit(P_EXIT_HTTP); return P_EXIT_HTTP } 239 let bo: i64 = p_body_off(out, n) 240 var blen: i64 = 0 241 if bo >= 0 { blen = n - bo } 242 let off: *i64 = (sys_mmap(P_OFF_BYTES)) as *i64 243 var rs_off: i64 = 0 244 var rs_len: i64 = 0 245 var url_off: i64 = 0 246 var url_len: i64 = 0 247 if bo >= 0 { 248 rs_len = cb_field(((out as i64) + bo) as *u8, blen, "room_status" as *u8, off) 249 if rs_len > 0 { rs_off = off[0] } 250 url_len = cb_field(((out as i64) + bo) as *u8, blen, "url" as *u8, off) 251 if url_len > 0 { url_off = off[0] } else { if url_len == 0 { url_off = off[0] } } 252 } 253 if rs_len < 0 { rs_len = 0 } 254 if url_len < 0 { url_len = 0 } 255 let state: i64 = cb_decide(st[0], ((out as i64) + bo) as *u8, rs_off, rs_len, url_len) 256 p_w("RESOLVE user=" as *u8); p_w(user) 257 p_w(" http=" as *u8); p_wn(st[0]) 258 p_w(" state=" as *u8); p_w(cb_state_name(state)) 259 p_w(" recordable=" as *u8); p_wn(cb_is_recordable(state)) 260 p_w(" backoff=" as *u8); p_wn(cb_should_backoff(state)) 261 p_w("\n" as *u8) 262 if cb_is_recordable(state) == 1 { 263 let cmaf: i64 = cb_flag(((out as i64) + bo) as *u8, blen, "cmaf_edge" as *u8) 264 let rawurl: *u8 = ((out as i64) + bo + url_off) as *u8 265 p_w("RESOLVE cmaf_edge=" as *u8); p_wn(cmaf); p_w("\n" as *u8) 266 if cmaf == 1 { 267 let rw: *u8 = sys_mmap(P_URL_CAP) 268 let rn: i64 = cb_cmaf_rewrite(rawurl, url_len, rw, P_URL_CAP) 269 if rn > 0 { p_w("RESOLVE url=" as *u8); p_wb(rw, rn); p_w("\n" as *u8) } 270 else { p_w("RESOLVE url=" as *u8); p_wb(rawurl, url_len); p_w("\n" as *u8) } 271 } else { 272 p_w("RESOLVE url=" as *u8); p_wb(rawurl, url_len); p_w("\n" as *u8) 273 } 274 // The contract, printed so the next reader cannot miss it. 275 p_w("RESOLVE handoff=SINGLE-CONSUMER the edge token is single-use; hand this URL to ONE consumer and do not re-resolve or pre-probe it\n" as *u8) 276 } 277 return 0 278 } 279 280 p_w("unknown verb\n" as *u8) 281 sys_exit(P_EXIT_USAGE) 282 return P_EXIT_USAGE 283}