code wiki / (root) / nx_archive_server.nx

nx_archive_server.nx source

↩ module page · 141 lines · 10011 B

1// nx_archive_server.nx -- sovereign localhost HTTP server for the time machine (R5 serve side). Binds 0.0.0.0:8080, 2// serves files under web_assets/ (path-traversal rejected), Content-Type by extension. "/" -> a landing page. Also 3// hosts the SUPERVISED do-not-recover blocklist: GET /blocklist renders it live from seg_store (justification + 4// per-entry action), GET /blocklist/approve?id=N and /blocklist/retract?id=N are the operator ACTIONS (flip status on 5// seg_store, then 303 back to /blocklist). Pending entries never block until approved here. Fetch stack. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_blocklist_view.nx" 8const K_MAGIC_8080: i64 = 8080 9const K_MAGIC_8192: i64 = 8192 10const K_MAGIC_2048: i64 = 2048 11const K_MAGIC_2097152: i64 = 2097152 12 13func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 14func ap(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { buf[off+i]=s[i]; i=i+1 } return off+i } 15func apn(buf: *u8, off: i64, v: i64) -> i64 { if v==0 { buf[off]=0x30 as u8; return off+1 } var m: i64=v; 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 o: i64=off; var q: i64=k-1; while q>=0 { buf[o]=t[q]; o=o+1; q=q-1 } return o } 16func endswith(path: *u8, plen: i64, ext: *u8, elen: i64) -> i64 { if plen<elen { return 0 } var i: i64=0; while i<elen { if (path[plen-elen+i] as i64) != (ext[i] as i64) { return 0 } i=i+1 } return 1 } 17func ctype(path: *u8, plen: i64) -> *u8 { 18 if endswith(path,plen,".html" as *u8,5)==1 { return "text/html" as *u8 } 19 if endswith(path,plen,".css" as *u8,4)==1 { return "text/css" as *u8 } 20 if endswith(path,plen,".gif" as *u8,4)==1 { return "image/gif" as *u8 } 21 if endswith(path,plen,".jpg" as *u8,4)==1 { return "image/jpeg" as *u8 } 22 if endswith(path,plen,".jpeg" as *u8,5)==1 { return "image/jpeg" as *u8 } 23 if endswith(path,plen,".png" as *u8,4)==1 { return "image/png" as *u8 } 24 return "text/plain" as *u8 25} 26func has_dotdot(buf: *u8, off: i64, len: i64) -> i64 { var i: i64=off; while i+1<off+len { if (buf[i] as i64)==0x2e { if (buf[i+1] as i64)==0x2e { return 1 } } i=i+1 } return 0 } 27// exact path match: req[pst..pst+plen) == lit (litlen bytes) 28func path_eq(req: *u8, pst: i64, plen: i64, lit: *u8, litlen: i64) -> i64 { if plen!=litlen { return 0 } var i: i64=0; while i<litlen { if (req[pst+i] as i64)!=(lit[i] as i64) { return 0 } i=i+1 } return 1 } 29func path_starts(req: *u8, pst: i64, plen: i64, lit: *u8, litlen: i64) -> i64 { if plen<litlen { return 0 } var i: i64=0; while i<litlen { if (req[pst+i] as i64)!=(lit[i] as i64) { return 0 } i=i+1 } return 1 } 30// extract the value of "id=" from query req[qst..qen) into idbuf (NUL-terminated); returns its length (0 if none) 31func qid(req: *u8, qst: i64, qen: i64, idbuf: *u8) -> i64 { 32 idbuf[0]=0 as u8 33 if qst<0 { return 0 } 34 var i: i64=qst 35 while i+2<qen { if (req[i] as i64)==0x69 { if (req[i+1] as i64)==0x64 { if (req[i+2] as i64)==0x3d { 36 var s: i64=i+3; var o: i64=0 37 while s<qen { let c: i64=req[s] as i64; if c==0x26 { s=qen } else { if o<200 { idbuf[o]=req[s]; o=o+1 } s=s+1 } } 38 idbuf[o]=0 as u8; return o 39 } } } i=i+1 } 40 return 0 41} 42 43func main() -> i64 { 44 let fd: i64=sys_socket(2, 1, 0) 45 if fd<0 { sw("socket fail\n" as *u8); return 1 } 46 let opt: *i64=sys_mmap(8) as *i64; opt[0]=1 47 __syscall(54, fd, 1, 2, opt as i64, 4, 0) 48 let addr: *u8=sys_mmap(16) 49 addr[0]=2 as u8; addr[1]=0 as u8 50 addr[2]=31 as u8; addr[3]=144 as u8 // port K_MAGIC_8080 (big-endian 0x1F90) 51 var z: i64=4; while z<16 { addr[z]=0 as u8; z=z+1 } 52 if sys_bind(fd, addr, 16)<0 { sw("bind fail (port 8080 in use?)\n" as *u8); return 1 } 53 if sys_listen(fd, 16)<0 { sw("listen fail\n" as *u8); return 1 } 54 sw("nx_archive_server: serving web_assets/ + /blocklist on http://localhost:8080/\n" as *u8) 55 56 let req: *u8=sys_mmap(K_MAGIC_8192) 57 let hdr: *u8=sys_mmap(K_MAGIC_8192) 58 let fpath: *u8=sys_mmap(K_MAGIC_2048) 59 let lb: *i64=sys_mmap(16) as *i64 60 let htmlbuf: *u8=sys_mmap(K_MAGIC_2097152) 61 62 var go: i64=1 63 while go==1 { 64 let cfd: i64=sys_accept(fd) 65 if cfd<0 { } else { 66 let rn: i64=sys_read(cfd, req, K_MAGIC_8192) 67 if rn<=0 { sys_close(cfd) } else { 68 // path = after first space, up to next space/?/end; query = after '?' up to space 69 var f1: i64=0; var i: i64=0; var g1: i64=1 70 while g1==1 { if i<rn { if (req[i] as i64)==0x20 { f1=i; g1=0 } else { i=i+1 } } else { f1=rn; g1=0 } } 71 var pst: i64=f1+1 72 var pen: i64=pst; var j: i64=pst; var g2: i64=1 73 while g2==1 { if j<rn { let c: i64=req[j] as i64; if c==0x20 { pen=j; g2=0 } else { if c==0x3f { pen=j; g2=0 } else { j=j+1 } } } else { pen=j; g2=0 } } 74 let plen: i64=pen-pst 75 var qst: i64=0-1; var qen: i64=0-1 76 if pen<rn { if (req[pen] as i64)==0x3f { qst=pen+1; var jj: i64=qst; var g3: i64=1; while g3==1 { if jj<rn { if (req[jj] as i64)==0x20 { qen=jj; g3=0 } else { jj=jj+1 } } else { qen=jj; g3=0 } } } } 77 78 var routed: i64=0 79 if has_dotdot(req, pst, plen)==1 { 80 var ho: i64=ap(hdr,0,"HTTP/1.1 403 Forbidden\r\nContent-Length: 9\r\nConnection: close\r\n\r\nForbidden" as *u8) 81 sys_write(cfd, hdr, ho); sys_close(cfd); routed=1 82 } 83 // /blocklist -- live supervised view rendered from seg_store 84 if routed==0 { if path_eq(req,pst,plen,"/blocklist" as *u8,10)==1 { 85 let bo: i64=bl_render_html(BL_PREFIX, htmlbuf) 86 var ho: i64=ap(hdr,0,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: " as *u8) 87 ho=apn(hdr,ho,bo); ho=ap(hdr,ho,"\r\nConnection: close\r\n\r\n" as *u8) 88 sys_write(cfd, hdr, ho); sys_write(cfd, htmlbuf, bo); sys_close(cfd); routed=1 89 } } 90 // /blocklist/approve?id=N -- operator ACTION: flip to approved, then back to the list 91 if routed==0 { if path_starts(req,pst,plen,"/blocklist/approve" as *u8,18)==1 { 92 let idb: *u8=sys_mmap(256); qid(req, qst, qen, idb) 93 if (idb[0] as i64)!=0 { bl_approve(idb) } 94 var ho: i64=ap(hdr,0,"HTTP/1.1 303 See Other\r\nLocation: /blocklist\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 95 sys_write(cfd, hdr, ho); sys_close(cfd); routed=1 96 } } 97 // /blocklist/retract?id=N -- operator ACTION: retract (stops blocking; history kept) 98 if routed==0 { if path_starts(req,pst,plen,"/blocklist/retract" as *u8,18)==1 { 99 let idb: *u8=sys_mmap(256); qid(req, qst, qen, idb) 100 if (idb[0] as i64)!=0 { bl_retract(idb) } 101 var ho: i64=ap(hdr,0,"HTTP/1.1 303 See Other\r\nLocation: /blocklist\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 102 sys_write(cfd, hdr, ho); sys_close(cfd); routed=1 103 } } 104 // "/" -> landing page 105 if routed==0 { if plen<=1 { 106 var bo: i64=0 107 bo=ap(req,bo,"<!DOCTYPE html><meta charset=utf-8><title>Nishi Time Machine</title><body style='font-family:system-ui;background:#0e0f13;color:#e7e7ea;max-width:760px;margin:40px auto;padding:0 20px'><h1>&#128336; Nishi Time Machine</h1><ul style='line-height:2'>" as *u8) 108 bo=ap(req,bo,"<li><a href=/archive/page3.com/index.html><b>page3.com</b> &mdash; scroll the history (134 captures 1996-2003), local media</a>" as *u8) 109 bo=ap(req,bo,"<li><a href=/archive/megastar.co.uk/index.html><b>megastar.co.uk</b> &mdash; scroll the history (61 captures, 1998+)</a>" as *u8) 110 bo=ap(req,bo,"<li><a href=/archive/recovery.html>media recovery &mdash; perceptual fingerprint + CID dedup</a>" as *u8) 111 bo=ap(req,bo,"<li><a href=/blocklist><b>do-not-recover blocklist</b> &mdash; supervised, justified, approve on the page</a>" as *u8) 112 bo=ap(req,bo,"</ul><p style=color:#8a8f9a>Served sovereignly &mdash; our own TLS-1.3 fetch, parsers, image decode, seg_store, and this HTTP server.</p></body>" as *u8) 113 var ho: i64=ap(hdr,0,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: " as *u8) 114 ho=apn(hdr,ho,bo); ho=ap(hdr,ho,"\r\nConnection: close\r\n\r\n" as *u8) 115 sys_write(cfd, hdr, ho); sys_write(cfd, req, bo); sys_close(cfd); routed=1 116 } } 117 // static file under web_assets/ 118 if routed==0 { 119 var fp: i64=ap(fpath,0,"web_assets" as *u8) 120 var k: i64=pst 121 while k<pen { fpath[fp]=req[k]; fp=fp+1; k=k+1 } 122 fpath[fp]=0 as u8 123 let body: *u8=sys_read_file(fpath, lb) 124 if body==(0 as *u8) { 125 var ho: i64=ap(hdr,0,"HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\nConnection: close\r\n\r\nNot found" as *u8) 126 sys_write(cfd, hdr, ho); sys_close(cfd) 127 } else { 128 let fn: i64=lb[0] 129 var ho: i64=ap(hdr,0,"HTTP/1.1 200 OK\r\nContent-Type: " as *u8) 130 ho=ap(hdr,ho,ctype(fpath,fp)); ho=ap(hdr,ho,"\r\nContent-Length: " as *u8); ho=apn(hdr,ho,fn); ho=ap(hdr,ho,"\r\nConnection: close\r\n\r\n" as *u8) 131 sys_write(cfd, hdr, ho) 132 var sent: i64=0 133 while sent<fn { let wn: i64=sys_write(cfd, ((body as i64)+sent) as *u8, fn-sent); if wn<=0 { sent=fn } else { sent=sent+wn } } 134 sys_close(cfd) 135 } 136 } 137 } 138 } 139 } 140 return 0 141}