code wiki / _hdl_build / nx_pub_serve.nx

nx_pub_serve.nx source

↩ module page · 115 lines · 5276 B

1// nx_pub_serve.nx -- PUB-SERVE R0: the publisher's OWN sovereign static-content HTTP server. It serves the 2// publisher's liveroot as static files (GET /<path> -> liveroot/<path>), so PUBLISHING IS A FILE-DROP + SERVE 3// -- NO sites.elf daemon-swap, no live-touching a fragile compiled daemon. New content = a new file in liveroot, 4// served immediately. Sovereign (our own socket syscalls + HTTP/1.1 text; no nginx/3rd-party). Path-traversal 5// is refused at the boundary (rule #12). This is the rung that lets THE PUBLISHER manage serving end to end. 6// usage: nx_pub_serve <liveroot> <port>. Exposes psv_listen() so a gate can fork it on loopback. license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_publisher.nx" 9const K_MAGIC_1024: i64 = 1024 10const K_MAGIC_1280: i64 = 1280 11const K_MAGIC_16384: i64 = 16384 12const K_MAGIC_18620: i64 = 18620 13 14// extract the request path of "GET /<path> HTTP..." into out (NUL-term, leading '/' stripped). 1 if a GET, else 0. 15func psv_path(req: *u8, rn: i64, out: *u8) -> i64 { 16 if rn < 6 { return 0 } 17 if req[0] != (71 as u8) { return 0 } // 'G' 18 if req[1] != (69 as u8) { return 0 } // 'E' 19 if req[2] != (84 as u8) { return 0 } // 'T' 20 if req[4] != (47 as u8) { return 0 } // '/' 21 var i: i64 = 5 22 var o: i64 = 0 23 var stop: i64 = 0 24 while i < rn { 25 if stop == 0 { 26 let c: i64 = req[i] & 0xff 27 if c == 32 { stop = 1 } 28 if c == 63 { stop = 1 } 29 if c == 13 { stop = 1 } 30 if c == 10 { stop = 1 } 31 if stop == 0 { out[o] = c as u8; o = o + 1 } 32 } 33 i = i + 1 34 } 35 out[o] = 0 as u8 36 return 1 37} 38// 1 if the path is safe (no ".." traversal, no absolute escape). 39func psv_safe(path: *u8) -> i64 { 40 var i: i64 = 0 41 while path[i] != (0 as u8) { 42 if path[i] == (46 as u8) { if path[i+1] == (46 as u8) { return 0 } } // ".." 43 i = i + 1 44 } 45 return 1 46} 47func psv_send_ok(cfd: i64, data: *u8, len: i64) -> i64 { 48 let h: *u8 = sys_mmap(256); var o: i64 = 0 49 o = fa_cat(h, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8) 50 o = fa_catn(h, o, len) 51 o = fa_cat(h, o, "\r\nConnection: close\r\n\r\n" as *u8) 52 sys_write(cfd, h, o) 53 sys_write(cfd, data, len) 54 return 0 55} 56func psv_send_status(cfd: i64, code: i64, msg: *u8) -> i64 { 57 let h: *u8 = sys_mmap(256); var o: i64 = 0 58 o = fa_cat(h, o, "HTTP/1.1 " as *u8); o = fa_catn(h, o, code); o = fa_cat(h, o, " " as *u8); o = fa_cat(h, o, msg) 59 o = fa_cat(h, o, "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 60 sys_write(cfd, h, o) 61 return 0 62} 63// serve one request: map GET path -> liveroot/<path> (or index.html), 200 + bytes, else 404 / 403. 64func psv_handle(cfd: i64, liveroot: *u8, req: *u8, rn: i64) -> i64 { 65 var path: *u8 = sys_mmap(K_MAGIC_1024) 66 if psv_path(req, rn, path) == 0 { psv_send_status(cfd, 400, "Bad Request" as *u8); return 0 } 67 if path[0] == (0 as u8) { path = "index.html" as *u8 } // GET / -> index.html 68 if psv_safe(path) == 0 { psv_send_status(cfd, 403, "Forbidden" as *u8); return 0 } 69 let full: *u8 = sys_mmap(K_MAGIC_1280); pub_join(liveroot, path, full) 70 let lenbox: *i64 = sys_mmap(8) as *i64 71 let data: *u8 = sys_read_file(full, lenbox) 72 if (data as i64) == 0 { psv_send_status(cfd, 404, "Not Found" as *u8); return 0 } 73 psv_send_ok(cfd, data, lenbox[0]) 74 return 0 75} 76// bind <port> on loopback, accept-fork-serve from liveroot forever. returns nonzero only on bind/listen failure. 77func psv_listen(liveroot: *u8, port: i64) -> i64 { 78 let addr: *u8 = sys_mmap(16) 79 addr[0]=2 as u8; addr[1]=0 as u8; addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8 80 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8 81 var z: i64 = 8; while z < 16 { addr[z]=0 as u8; z=z+1 } 82 let lfd: i64 = sys_socket(2, 1, 0) 83 if lfd < 0 { return 10 } 84 let opt: *u8 = sys_mmap(4); opt[0]=1 as u8; opt[1]=0 as u8; opt[2]=0 as u8; opt[3]=0 as u8 85 sys_setsockopt(lfd, 1, 2, opt, 4) // SO_REUSEADDR 86 if sys_bind(lfd, addr, 16) < 0 { return 20 } 87 if sys_listen(lfd, 64) < 0 { return 30 } 88 let req: *u8 = sys_mmap(K_MAGIC_16384) 89 let reapst: *i64 = sys_mmap(16) as *i64 90 while 1 == 1 { 91 let cfd: i64 = sys_accept(lfd) 92 if cfd >= 0 { 93 let pid: i64 = sys_fork() 94 if pid == 0 { 95 sys_close(lfd) 96 let rn: i64 = sys_read(cfd, req, K_MAGIC_16384) 97 psv_handle(cfd, liveroot, req, rn) 98 sys_close(cfd) 99 sys_exit(0) 100 } 101 sys_close(cfd) 102 var reaped: i64 = 1 103 while reaped > 0 { reaped = sys_wait4(0 - 1, reapst, 1) } 104 } 105 } 106 return 0 107} 108 109func main(argc: i64, argv: *i64) -> i64 { 110 var liveroot: *u8 = "knowledge/publish/live" as *u8 111 var port: i64 = K_MAGIC_18620 112 if argc >= 2 { liveroot = argv[1] as *u8 } 113 if argc >= 3 { let pa: *u8 = argv[2] as *u8; var pv: i64=0; var pi: i64=0; while pa[pi]!=(0 as u8){ if pa[pi]>=(48 as u8){ if pa[pi]<=(57 as u8){ pv=pv*10+((pa[pi] as i64)-48) } } pi=pi+1 } if pv>0 { port=pv } } 114 return psv_listen(liveroot, port) 115}