code wiki / (root) / nx_http_fetch.nx

nx_http_fetch.nx source

↩ module page · 132 lines · 8569 B

1// nx_http_fetch.nx -- EAT THE DEBT: a plaintext HTTP (port 80, no TLS) fetch so HTTP-ONLY hosts are reachable. 2// images.cocodataset.org is an S3 static-website endpoint (HTTP-only) -> the TLS-only fetcher fails at handshake 3// (-3). This wires the SHIPPED pieces: nx_dns_a_resolve (host->IPv4, copied self-contained) + nx_http_client_get 4// (socket+GET+drain) + a minimal status/redirect parse. Follows http:// redirects (a redirect to https:// is 5// reported -- that host wants TLS, use the https fetcher). File-driven (data/mp_img_url.txt -> data/mp_img_dest.txt). 6// license_tier: ORIGINAL expect_exit: 0 7import "nx_syscalls.nx" 8import "nx_http_client.nx" 9const K_MAGIC_4660: i64 = 4660 10const K_MAGIC_2048: i64 = 2048 11const K_MAGIC_4096: i64 = 4096 12const K_MAGIC_1024: i64 = 1024 13const K_MAGIC_16777216: i64 = 16777216 14 15func w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 16func wn(v: i64) -> i64 { var m: i64=v; if m<0{w("-" as *u8);m=0-m} let t:*u8=sys_mmap(24); 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} var i:i64=0; let o:*u8=sys_mmap(24); while i<k{o[i]=t[k-1-i];i=i+1} sys_write(1,o,k); return 0 } 17func shx(v: i64) -> i64 { let b: *u8=sys_mmap(4); var i: i64=0; while i<2 { let n: i64=(v>>((1-i)*4))&0xF; if n<10{b[i]=(48+n) as u8} else {b[i]=(87+n) as u8} i=i+1 } sys_write(1,b,2); return 0 } 18func rdcfg(path: *u8, buf: *u8, cap: i64) -> i64 { let fd: i64=sys_openat_rd(path); if fd<0{return 0-1} var n: i64=sys_read(fd,buf,cap-1); sys_close(fd); if n<0{return 0-1} var go: i64=1; while go==1{go=0; if n>0{ let c: i64=buf[n-1] as i64; if c==10{n=n-1;go=1} if c==13{n=n-1;go=1} if c==32{n=n-1;go=1} }} buf[n]=0 as u8; return n } 19 20// ---- self-contained DNS A resolver (from nx_dns_a.nx; that file has a main -> copied) ---- 21func da_u16(buf: *u8, o: i64) -> i64 { return ((buf[o] as i64) << 8) | (buf[o+1] as i64) } 22func da_qname(host: *u8, out: *u8, off: i64) -> i64 { 23 var i: i64=0; var o: i64=off 24 while host[i] != (0 as u8) { 25 var j: i64=i; var go: i64=1 26 while go==1 { if host[j]==(0 as u8){go=0} else { if host[j]==(46 as u8){go=0} else {j=j+1} } } 27 let llen: i64=j-i; out[o]=llen as u8; o=o+1 28 var k: i64=i; while k<j { out[o]=host[k]; o=o+1; k=k+1 } 29 if host[j]==(46 as u8){i=j+1} else {i=j} 30 } 31 out[o]=0 as u8; o=o+1; return o 32} 33func da_build_query(host: *u8, qid: i64, out: *u8) -> i64 { 34 out[0]=((qid>>8)&0xff) as u8; out[1]=(qid&0xff) as u8; out[2]=1 as u8; out[3]=0 as u8 35 out[4]=0 as u8; out[5]=1 as u8; out[6]=0 as u8; out[7]=0 as u8; out[8]=0 as u8; out[9]=0 as u8; out[10]=0 as u8; out[11]=0 as u8 36 var o: i64=da_qname(host, out, 12); out[o]=0 as u8; out[o+1]=1 as u8; o=o+2; out[o]=0 as u8; out[o+1]=1 as u8; o=o+2; return o 37} 38func da_skip_name(buf: *u8, off: i64, n: i64) -> i64 { var o: i64=off; while o<n { let b: i64=buf[o] as i64; if b==0{return o+1} if (b&0xc0)==0xc0{return o+2} o=o+1+b } return o } 39func da_parse(buf: *u8, n: i64, out_ip: *u8) -> i64 { 40 if n<12 {return 0}; let anc: i64=da_u16(buf,6); if anc<1{return 0} 41 var o: i64=da_skip_name(buf,12,n); o=o+4; var a: i64=0 42 while a<anc { o=da_skip_name(buf,o,n); if o+10>n{return 0}; let typ: i64=da_u16(buf,o); let rdlen: i64=da_u16(buf,o+8); let rdata: i64=o+10 43 if typ==1 { if rdlen==4 { out_ip[0]=buf[rdata]; out_ip[1]=buf[rdata+1]; out_ip[2]=buf[rdata+2]; out_ip[3]=buf[rdata+3]; return 1 } } 44 o=rdata+rdlen; a=a+1 } 45 return 0 46} 47func da_sockaddr(sa: *u8, port: i64, i0: i64, i1: i64, i2: i64, i3: i64) -> i64 { 48 sa[0]=2 as u8; sa[1]=0 as u8; sa[2]=((port>>8)&0xff) as u8; sa[3]=(port&0xff) as u8 49 sa[4]=i0 as u8; sa[5]=i1 as u8; sa[6]=i2 as u8; sa[7]=i3 as u8; var k: i64=8; while k<16{sa[k]=0 as u8; k=k+1} return 0 50} 51func nx_dns_a_resolve(host: *u8, out_ip: *u8) -> i64 { 52 let fd: i64=sys_socket(AF_INET, SOCK_DGRAM, 0); if fd<0{return 0} 53 sys_set_socket_timeout(fd, 4) 54 let dest: *u8=sys_mmap(16); da_sockaddr(dest, 53, 8,8,8,8) 55 let q: *u8=sys_mmap(512); let qlen: i64=da_build_query(host, K_MAGIC_4660, q) 56 if sys_sendto(fd,q,qlen,0,dest,16)<0 {sys_close(fd); return 0} 57 let r: *u8=sys_mmap(K_MAGIC_2048); let rn: i64=sys_recvfrom(fd,r,K_MAGIC_2048,0,0 as *u8,0 as *i64); sys_close(fd) 58 if rn<12 {return 0}; return da_parse(r,rn,out_ip) 59} 60 61// find needle in buf[from..n); -1 if absent 62func hf_find(buf: *u8, n: i64, from: i64, needle: *u8) -> i64 { 63 var m: i64=0; while needle[m]!=(0 as u8){m=m+1}; if m==0{return 0-1} 64 var i: i64=from; while i+m<=n { var j: i64=0; var ok: i64=1; while j<m { if buf[i+j]!=needle[j]{ok=0;j=m} else {j=j+1} } if ok==1{return i} i=i+1 } 65 return 0-1 66} 67// parse url http://host/path -> host, path 68func hf_parse_url(url: *u8, hbuf: *u8, pbuf: *u8) -> i64 { 69 var i: i64=0 70 if url[0]==(104 as u8) { if url[4]==(58 as u8) { if url[5]==(47 as u8) { if url[6]==(47 as u8) { i=7 } } } } // skip http:// 71 var ho: i64=0; var go: i64=1 72 while go==1 { 73 let c: i64 = url[i] as i64 74 if c==0 { go=0 } else { if c==47 { go=0 } else { hbuf[ho]=url[i]; ho=ho+1; i=i+1 } } 75 } 76 hbuf[ho]=0 as u8 77 var po: i64=0 78 while url[i]!=(0 as u8) { pbuf[po]=url[i]; po=po+1; i=i+1 } 79 if po==0 { pbuf[0]=47 as u8; po=1 } 80 pbuf[po]=0 as u8 81 return 1 82} 83 84func main() -> i64 { 85 let url: *u8=sys_mmap(K_MAGIC_4096); let dest: *u8=sys_mmap(K_MAGIC_4096) 86 if rdcfg("data/mp_img_url.txt" as *u8, url, K_MAGIC_4096)<0 { w("no url\n" as *u8); sys_exit(1); return 1 } 87 if rdcfg("data/mp_img_dest.txt" as *u8, dest, K_MAGIC_4096)<0 { w("no dest\n" as *u8); sys_exit(1); return 1 } 88 let host: *u8=sys_mmap(K_MAGIC_1024); let path: *u8=sys_mmap(K_MAGIC_2048) 89 let cap: i64=K_MAGIC_16777216; let buf: *u8=sys_mmap(cap) 90 let ip: *u8=sys_mmap(8); let sa: *u8=sys_mmap(16); let verdict: *i64=sys_mmap(8) as *i64 91 var hop: i64=0; var n: i64=0; var status: i64=0 92 while hop<5 { 93 hf_parse_url(url, host, path) 94 var hl: i64=0; while host[hl]!=(0 as u8){hl=hl+1} 95 var pl: i64=0; while path[pl]!=(0 as u8){pl=pl+1} 96 w("hop " as *u8); wn(hop); w(": host=" as *u8); w(host); w(" path=" as *u8); w(path); w("\n" as *u8) 97 if nx_dns_a_resolve(host, ip)==0 { w("DNS resolve FAILED\n" as *u8); sys_exit(1); return 1 } 98 w(" ip=" as *u8); wn(ip[0] as i64); w("." as *u8); wn(ip[1] as i64); w("." as *u8); wn(ip[2] as i64); w("." as *u8); wn(ip[3] as i64); w("\n" as *u8) 99 nx_http_client_sockaddr_ipv4(sa, ip[0] as i64, ip[1] as i64, ip[2] as i64, ip[3] as i64, 80) 100 n = nx_http_client_get(sa, path, pl, host, hl, buf, cap, verdict) 101 if n<=0 { w("http GET FAILED n=" as *u8); wn(n); w("\n" as *u8); sys_exit(1); return 1 } 102 // status = 3 digits after "HTTP/1.x " 103 let sp: i64 = hf_find(buf, n, 0, " " as *u8) 104 status = 0 105 if sp>=0 { status = (buf[sp+1]-48)*100 + (buf[sp+2]-48)*10 + (buf[sp+3]-48) } 106 w(" status=" as *u8); wn(status); w(" bytes=" as *u8); wn(n); w("\n" as *u8) 107 if status>=200 { if status<300 { hop=999 } } 108 if status>=300 { if status<400 { 109 let lo: i64 = hf_find(buf, n, 0, "Location: " as *u8) 110 if lo<0 { w("3xx but no Location\n" as *u8); sys_exit(1); return 1 } 111 var s: i64=lo+10; var k: i64=0 112 while buf[s]!=(13 as u8) { if buf[s]==(10 as u8){ s=s } else { url[k]=buf[s]; k=k+1; s=s+1 } } 113 url[k]=0 as u8 114 w(" -> redirect to " as *u8); w(url); w("\n" as *u8) 115 if url[4]==(115 as u8) { w(" redirect is https:// -> needs the TLS fetcher (not this HTTP path)\n" as *u8); sys_exit(2); return 2 } 116 hop=hop+1 117 } } 118 if status<200 { w("bad status\n" as *u8); sys_exit(1); return 1 } 119 if hop != 999 { if status>=400 { w("http error status\n" as *u8); sys_exit(1); return 1 } } 120 } 121 // body = after \r\n\r\n 122 let bo: i64 = hf_find(buf, n, 0, "\r\n\r\n" as *u8) 123 if bo<0 { w("no header/body split\n" as *u8); sys_exit(1); return 1 } 124 let body: i64 = bo+4; let blen: i64 = n - body 125 w("magic=" as *u8); shx(buf[body] as i64); shx(buf[body+1] as i64); w(" body_bytes=" as *u8); wn(blen); w("\n" as *u8) 126 let fd: i64=sys_openat_wr(dest, 0x1a4); if fd<0 { w("open dest FAILED\n" as *u8); sys_exit(1); return 1 } 127 var wr: i64=0; while wr<blen { let k: i64=sys_write(fd, ((buf as i64)+body+wr) as *u8, blen-wr); if k<=0{wr=blen}else{wr=wr+k} } 128 sys_close(fd) 129 w("wrote " as *u8); wn(blen); w(" bytes -> " as *u8); w(dest); w("\n" as *u8) 130 sys_exit(0) 131 return 0 132}