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}