code wiki / _hdl_build / nx_sites_reconciled.nx
nx_sites_reconciled.nx source
↩ module page · 269 lines · 13104 B
1// nx_sites_reconciled.nx -- the ONE reconciled sovereign sites daemon (R3-assembly): cleanly LAYERED + fully
2// DATA-DRIVEN, replacing the two divergent F-level daemons. Composes the PROVEN TLS primitives (handshake/recv/
3// send, reused unchanged) with the GATED data-driven cores:
4// io: accept (fork-per-conn) -> MSG_PEEK ClientHello -> scs_pick_from_clienthello -> SNI host
5// data: cl_load(certs_generated.conf, sni) -> chain+key bytes (default-cert fallback) ; tables read at startup
6// TLS: nx_tls13_server_session_run(cfd, rnd, priv, chain, len, key) [proven]
7// logic: sh_handle(request, proxy_routes.conf, sites_generated.conf) -> PROXY-signal | static-200 | 404
8// io: PROXY -> buffered reverse-proxy relay to the backend ; else app_send the response
9// NO hardcoded routes/certs/hosts -- routes/certs/vhosts are the 3 data tables nx_domain_forge emits. Operator:
10// "fix the hardcoding, s-class exceed; append a domain -> the builders emit it." (Buffered proxy; streaming large
11// media = a later perf rung.) license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
14import "nx_csprng.nx"
15import "nx_tls13_server_session.nx"
16import "nx_tls13_server_session_run.nx"
17import "nx_tls13_server_session_app_data.nx"
18import "nx_sni_cert_select.nx"
19import "nx_cert_loader.nx"
20import "nx_site_handler.nx"
21import "nx_tls13_read_record_from_fd.nx" // robust TLS record reader (reassembles a record split across sys_reads) -- for the full-body receive loop
22const RD_MAGIC_20480: i64 = 20480
23
24const RD_PORT: i64 = 8443
25const RD_MSG_PEEK: i64 = 2
26const RD_PEEK_CAP: i64 = 8192
27const RD_OUTCAP: i64 = 4194304
28const RD_RELAYCAP: i64 = 4194304
29const RD_BUDGET: i64 = 1000000000
30const RD_MAXCHILD: i64 = 64
31
32const RD_REG: *u8 = "/volume1/homes/elderwesto/nishihost/certs_generated.conf" as *u8
33const RD_RT: *u8 = "/volume1/homes/elderwesto/nishihost/proxy_routes.conf" as *u8
34const RD_VT: *u8 = "/volume1/homes/elderwesto/nishihost/sites_generated.conf" as *u8
35const RD_DEF_CHAIN: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_fullchain.der" as *u8
36const RD_DEF_KEY: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_ecdsa_key.bin" as *u8
37
38func rd_readfile(path: *u8, szbox: *i64) -> *u8 { szbox[0] = 0; return sys_read_file(path, szbox) }
39
40// ---- ADDITIVE full-body receive (crown-jewel edge fix 2026-07-02) --------------------------------------------
41// The v1-style receive (single sys_read + one app_recv into a 20KB buffer) dropped any request body spanning >1
42// TLS record: a chunked-upload POST > ~16KB was truncated, so the backend saw a short body and reset (rc=-2).
43// rd_recv_request loop-reads complete records and reassembles the FULL request (headers + whole Content-Length
44// body) up to RD_PLAINCAP before relaying. Pattern adapted from the GATED nx_sites_daemon_v2 sd2_recv_request.
45// GET/no-body still returns at the headers; a small request completes in one record (behaviour unchanged).
46const RD_RECCAP: i64 = 20480 // one TLS record (16384 payload + header/tag slack)
47const RD_PLAINCAP: i64 = 65536 // full-request reassembly cap: headers + up to a ~63KB chunked-upload chunk
48
49func rd_lower(c: u8) -> u8 {
50 if c >= (65 as u8) { if c <= (90 as u8) { return (c + (32 as u8)) } }
51 return c
52}
53
54func rd_contains(hay: *u8, hlen: i64, needle: *u8, nlen: i64) -> i64 {
55 if nlen <= 0 { return 0 }
56 if hlen < nlen { return 0 }
57 var i: i64 = 0
58 let last: i64 = hlen - nlen
59 while i <= last {
60 var j: i64 = 0
61 var ok: i64 = 1
62 while j < nlen {
63 if hay[i + j] != needle[j] { ok = 0; j = nlen } else { j = j + 1 }
64 }
65 if ok == 1 { return 1 }
66 i = i + 1
67 }
68 return 0
69}
70
71func rd_body_start(hay: *u8, hlen: i64) -> i64 {
72 if hlen < 4 { return hlen }
73 var i: i64 = 0
74 let last: i64 = hlen - 4
75 while i <= last {
76 if hay[i] == (0x0D as u8) { if hay[i + 1] == (0x0A as u8) { if hay[i + 2] == (0x0D as u8) { if hay[i + 3] == (0x0A as u8) {
77 return i + 4
78 } } } }
79 i = i + 1
80 }
81 return hlen
82}
83
84func rd_content_length(req: *u8, reqn: i64) -> i64 {
85 let key: *u8 = "content-length:" as *u8
86 var i: i64 = 0
87 while i + 15 <= reqn {
88 var j: i64 = 0
89 var ok: i64 = 1
90 while j < 15 {
91 if rd_lower(req[i + j]) != key[j] { ok = 0; j = 15 } else { j = j + 1 }
92 }
93 if ok == 1 {
94 var k: i64 = i + 15
95 var run0: i64 = 1
96 while run0 == 1 { if k < reqn { if req[k] == (32 as u8) { k = k + 1 } else { run0 = 0 } } else { run0 = 0 } }
97 var v: i64 = 0
98 var got: i64 = 0
99 var run: i64 = 1
100 while run == 1 {
101 if k >= reqn { run = 0 } else {
102 let c: i64 = req[k] as i64
103 var dig: i64 = 0
104 if c >= 48 { if c <= 57 { dig = 1 } }
105 if dig == 1 { v = v * 10 + (c - 48); got = 1; k = k + 1 } else { run = 0 }
106 }
107 }
108 if got == 1 { return v }
109 return 0 - 1
110 }
111 i = i + 1
112 }
113 return 0 - 1
114}
115
116// Loop-read complete TLS records + app-decrypt+append until headers (CRLFCRLF) AND the full Content-Length body
117// are buffered (<= plain_cap), then return plaintext length. Bounded to 16 records. GET/no-body -> at headers.
118func rd_recv_request(s: *Tls13ServerSession, cfd: i64, rec_buf: *u8, plain: *u8, plain_cap: i64) -> i64 {
119 var total: i64 = 0
120 var recs: i64 = 0
121 var need: i64 = 0
122 while recs < 16 {
123 let rn: i64 = nx_tls13_read_record_from_fd(cfd, rec_buf, RD_RECCAP)
124 if rn <= 0 { if total > 0 { return total } return 0 - 1 }
125 let pn: i64 = nx_tls13_server_session_app_recv(s, rec_buf, rn, ((plain as i64) + total) as *u8, plain_cap - total)
126 if pn <= 0 { if total > 0 { return total } return 0 - 1 }
127 total = total + pn
128 if need == 0 {
129 if rd_contains(plain, total, "\r\n\r\n" as *u8, 4) == 1 {
130 let cl: i64 = rd_content_length(plain, total)
131 if cl <= 0 { return total }
132 let bs: i64 = rd_body_start(plain, total)
133 need = bs + cl
134 if need > plain_cap { need = plain_cap }
135 if total >= need { return total }
136 }
137 } else {
138 if total >= need { return total }
139 }
140 if total >= plain_cap { return total }
141 recs = recs + 1
142 }
143 return total
144}
145
146// buffered reverse-proxy relay: connect 127.0.0.1:port, send the decrypted request, read the backend response,
147// app_send it over the TLS session. (buffered; streaming for large media is a later perf rung.)
148func rd_relay(s: *Tls13ServerSession, port: i64, plain: *u8, plain_n: i64, out_rec: *u8, cfd: i64) -> i64 {
149 let fd: i64 = sys_socket(2, 1, 0)
150 if fd < 0 { return 0 - 1 }
151 sys_set_socket_timeout(fd, 20)
152 let a: *u8 = sys_mmap(16)
153 a[0] = 2 as u8; a[1] = 0 as u8; a[2] = ((port >> 8) & 0xff) as u8; a[3] = (port & 0xff) as u8
154 a[4] = 127 as u8; a[5] = 0 as u8; a[6] = 0 as u8; a[7] = 1 as u8
155 var zi: i64 = 8; while zi < 16 { a[zi] = 0 as u8; zi = zi + 1 }
156 if nx_connect_bounded(fd, a, 16, NX_CONN_DEFAULT_MS) != 0 { sys_close(fd); return 0 - 2 }
157 var sent: i64 = 0
158 var sgo: i64 = 1
159 while sgo == 1 {
160 if sent >= plain_n { sgo = 0 } else {
161 let w: i64 = sys_write(fd, ((plain as i64) + sent) as *u8, plain_n - sent)
162 if w <= 0 { sgo = 0 } else { sent = sent + w }
163 }
164 }
165 let buf: *u8 = sys_mmap(RD_RELAYCAP)
166 var tot: i64 = 0; var go: i64 = 1
167 while go == 1 { let r: i64 = sys_read(fd, ((buf as i64) + tot) as *u8, RD_RELAYCAP - tot); if r <= 0 { go = 0 } else { tot = tot + r; if tot >= RD_RELAYCAP { go = 0 } } }
168 sys_close(fd)
169 if tot <= 0 { return 0 - 3 }
170 let on: i64 = nx_tls13_server_session_app_send(s, buf, tot, out_rec, RD_OUTCAP)
171 if on > 0 { sys_write(cfd, out_rec, on) }
172 return tot
173}
174
175func main() -> i64 {
176 // ---- startup: load the 3 data tables + the default cert (fail-fast on the default) ----
177 let regsz: *i64 = sys_mmap(16) as *i64
178 let reg: *u8 = rd_readfile(RD_REG, regsz); var reg_n: i64 = 0; if (reg as i64) != 0 { reg_n = regsz[0] }
179 let rtsz: *i64 = sys_mmap(16) as *i64
180 let rt: *u8 = rd_readfile(RD_RT, rtsz); var rt_n: i64 = 0; if (rt as i64) != 0 { rt_n = rtsz[0] }
181 let vtsz: *i64 = sys_mmap(16) as *i64
182 let vt: *u8 = rd_readfile(RD_VT, vtsz); var vt_n: i64 = 0; if (vt as i64) != 0 { vt_n = vtsz[0] }
183 let dcsz: *i64 = sys_mmap(16) as *i64
184 let def_chain: *u8 = rd_readfile(RD_DEF_CHAIN, dcsz)
185 if (def_chain as i64) == 0 { sys_write(2, "FATAL: default cert chain\n" as *u8, 26); return 2 }
186 let def_chain_len: i64 = dcsz[0]
187 let dksz: *i64 = sys_mmap(16) as *i64
188 let def_key: *u8 = rd_readfile(RD_DEF_KEY, dksz)
189 if (def_key as i64) == 0 { sys_write(2, "FATAL: default key\n" as *u8, 19); return 2 }
190
191 // ---- listen 0.0.0.0:8443 ----
192 let addr: *u8 = sys_mmap(16)
193 addr[0] = 2 as u8; addr[1] = 0 as u8; addr[2] = ((RD_PORT >> 8) & 0xff) as u8; addr[3] = (RD_PORT & 0xff) as u8
194 addr[4] = 0 as u8; addr[5] = 0 as u8; addr[6] = 0 as u8; addr[7] = 0 as u8
195 var zj: i64 = 8; while zj < 16 { addr[zj] = 0 as u8; zj = zj + 1 }
196 let lfd: i64 = sys_socket(2, 1, 0)
197 if lfd < 0 { sys_write(2, "SOCKET-FAIL\n" as *u8, 12); return 1 }
198 let optv: *u8 = sys_mmap(4); optv[0] = 1 as u8
199 sys_setsockopt(lfd, 1, 2, optv, 4)
200 if sys_bind(lfd, addr, 16) < 0 { sys_write(2, "BIND-FAIL\n" as *u8, 10); return 1 }
201 if sys_listen(lfd, 64) < 0 { sys_write(2, "LISTEN-FAIL\n" as *u8, 12); return 1 }
202 sys_write(1, "RECONCILED-SITES-UP (data-driven: route+cert+vhost tables, no hardcoding)\n" as *u8, 73)
203 sys_set_socket_timeout(lfd, 5)
204
205 // ---- hoisted per-conn buffers (children get COW copies) ----
206 let server_random: *u8 = sys_mmap(32)
207 let server_priv: *u8 = sys_mmap(32)
208 let rec_buf: *u8 = sys_mmap(RD_MAGIC_20480)
209 let plain: *u8 = sys_mmap(RD_PLAINCAP) // 64KB: full-request reassembly (headers + up to a ~63KB chunked-upload body), not just one record
210 let out_rec: *u8 = sys_mmap(RD_OUTCAP)
211 let resp: *u8 = sys_mmap(RD_OUTCAP)
212 let sni_peek: *u8 = sys_mmap(RD_PEEK_CAP)
213 let sni_host: *u8 = sys_mmap(256)
214 let dec: *i64 = sys_mmap(64) as *i64
215 let cp: *i64 = sys_mmap(8) as *i64; let cl: *i64 = sys_mmap(8) as *i64
216 let kp: *i64 = sys_mmap(8) as *i64; let kl: *i64 = sys_mmap(8) as *i64
217 let reapst: *i64 = sys_mmap(8) as *i64
218 var served: i64 = 0
219 var live: i64 = 0
220
221 while served < RD_BUDGET {
222 while sys_wait4(0 - 1, reapst, 1) > 0 { live = live - 1 }
223 let cfd: i64 = sys_accept(lfd)
224 if cfd >= 0 {
225 if live >= RD_MAXCHILD { if sys_wait4(0 - 1, reapst, 0) > 0 { live = live - 1 } }
226 let pid: i64 = sys_fork()
227 if pid == 0 {
228 sys_close(lfd)
229 sys_set_socket_timeout(cfd, 10)
230 nx_csprng_fill(server_random, 32)
231 nx_csprng_fill(server_priv, 32)
232 // io: peek the ClientHello (non-consuming) -> SNI host (data: cl_load -> cert; else default)
233 let pk_n: i64 = sys_recvfrom(cfd, sni_peek, RD_PEEK_CAP, RD_MSG_PEEK, 0 as *u8, 0 as *i64)
234 sni_host[0] = 0 as u8
235 if pk_n > 0 { scs_pick_from_clienthello(sni_peek, pk_n, sni_host, 256) }
236 var hn: i64 = 0; while sni_host[hn] != (0 as u8) { hn = hn + 1 }
237 var sel_chain: *u8 = def_chain
238 var sel_chain_len: i64 = def_chain_len
239 var sel_key: *u8 = def_key
240 if hn > 0 { if reg_n > 0 {
241 if cl_load(reg, reg_n, sni_host, hn, cp, cl, kp, kl) == 1 {
242 sel_chain = cp[0] as *u8; sel_chain_len = cl[0]; sel_key = kp[0] as *u8
243 }
244 } }
245 let hs_rc: i64 = nx_tls13_server_session_run(cfd, server_random, server_priv, sel_chain, sel_chain_len, sel_key)
246 if hs_rc > 0 {
247 let s: *Tls13ServerSession = hs_rc as *Tls13ServerSession
248 let plain_n: i64 = rd_recv_request(s, cfd, rec_buf, plain, RD_PLAINCAP)
249 if plain_n > 0 {
250 dec[0] = 0 - 9
251 let on: i64 = sh_handle(plain, plain_n, rt, rt_n, vt, vt_n, resp, dec)
252 if dec[0] == SH_PROXY {
253 rd_relay(s, dec[1], plain, plain_n, out_rec, cfd)
254 } else {
255 let en: i64 = nx_tls13_server_session_app_send(s, resp, on, out_rec, RD_OUTCAP)
256 if en > 0 { sys_write(cfd, out_rec, en) }
257 }
258 }
259 }
260 sys_close(cfd)
261 sys_exit(0)
262 }
263 live = live + 1
264 served = served + 1
265 }
266 }
267 sys_close(lfd)
268 return 0
269}