code wiki / _hdl_build / nx_golive_dns_gate.nx
nx_golive_dns_gate.nx source
↩ module page · 292 lines · 14442 B
1// nx_golive_dns_gate.nx -- GO-LIVE GATE: every hosted domain must be testable INTERNALLY and
2// EXTERNALLY, by construction, so the "apex works but admin.<domain> is insecure on WiFi" class
3// (2026-07-04/05 andelinwest arc) can never ship again. Data-driven from
4// knowledge/hosting/golive_dns.conf (domain|public_ip|lan_ip|lan_wildcard). Per domain:
5// [A] EXTERNAL AUTHORITY -- Porkbun API (sovereign TLS, uninterceptable): apex A == public_ip
6// AND '*.domain' A == public_ip => any subdomain reaches the edge from the internet.
7// [B] INTERNAL LAN -- query the LAN router (dnsmasq) directly: apex -> lan_ip; and when
8// lan_wildcard=1 a CANARY subdomain (nx-golive-canary.<domain>) -> lan_ip. The canary is the
9// whole point: per-host override lists pass apex/www and silently miss new subdomains; only a
10// whole-domain wildcard answers a name nobody registered.
11// [C] EDGE SERVE -- TLS 1.3 to lan_ip:443 with SNI apex (+ canary when lan_wildcard=1): served
12// cert must VALIDATE for that SNI (chain + SAN + validity vs Mozilla store) and GET / must
13// return HTTP 2xx/3xx => sni_router -> cert-select -> proxy -> daemon serve the whole domain.
14// Every RED prints its exact remediation command. Exit 0 = all GREEN, 1 = any RED.
15// Honest limit: true external-vantage serving (hairpin-free) still needs one off-LAN check
16// (cellular funcheck row); [A]+[C] are the strongest in-LAN-provable superset.
17// usage: nx_golive_dns_gate (run nx_secret_cli get porkbun first -> /tmp/nxsecret.out)
18// license_tier: ORIGINAL
19import "nx_acme_porkbun.nx"
20import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
21import "nx_dns_resolve_a_record.nx"
22import "nx_csprng.nx"
23import "nx_tls13_client_session.nx"
24import "nx_tls13_client_session_run.nx"
25import "nx_https_get_complete.nx"
26
27const GD_CONF: *u8 = "knowledge/hosting/golive_dns.conf\x00" as *u8
28// NOTE nx_cc trap (pinned 2026-07-05): INDEXING a global const pointer (CONST_X[i]) -> empty .s.
29// The canary label therefore lives as a local string literal inside gd_domain, not a global const. // unregistered label: only a wildcard answers it
30
31func gd_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
32func gd_write(s: *u8, n: i64) -> i64 { sys_write(1, s, n); return 0 }
33func gd_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
34func gd_putn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); 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; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
35// dotted ipv4 -> packed BE; also prints via gd_ip
36func gd_ipparse(s: *u8, n: i64) -> i64 {
37 var packed: i64=0; var cur: i64=0; var i: i64=0
38 while i < n {
39 let c: i64 = s[i] as i64
40 if c == 46 { packed = (packed << 8) | cur; cur = 0 }
41 else { if c >= 48 { if c <= 57 { cur = cur*10 + (c-48) } } }
42 i = i + 1
43 }
44 return (packed << 8) | cur
45}
46func gd_ip(p: i64) -> i64 {
47 gd_putn((p>>24)&0xff); gd_puts("." as *u8); gd_putn((p>>16)&0xff); gd_puts("." as *u8)
48 gd_putn((p>>8)&0xff); gd_puts("." as *u8); gd_putn(p&0xff); return 0
49}
50func gd_contains(hay: *u8, n: i64, needle: *u8) -> i64 {
51 let nl: i64 = gd_slen(needle); if nl == 0 { return 0 }
52 var i: i64 = 0
53 while i + nl <= n { var j: i64=0; var ok: i64=1; while j<nl { if hay[i+j]!=needle[j] {ok=0; j=nl} else {j=j+1} } if ok==1 { return 1 } i=i+1 }
54 return 0
55}
56
57// ---- Porkbun record check: does the zone hold an A record `name` -> `ip`? ----
58// JSON shape (observed): {"id":"..","name":"<fqdn>","type":"A","content":"<ip>","ttl":..}
59// Scan for "name":"<fqdn>" then require "type":"A" and "content":"<ip>" within the same object window.
60func gd_pk_has_a(resp: *u8, rn: i64, fqdn: *u8, fqn: i64, ip: *u8, ipn: i64) -> i64 {
61 let pat: *u8 = sys_mmap(512)
62 var po: i64 = 0
63 let p1: *u8 = "\"name\":\"" as *u8
64 var i: i64 = 0
65 while p1[i] != (0 as u8) { pat[po] = p1[i]; po = po + 1; i = i + 1 }
66 i = 0
67 while i < fqn { pat[po] = fqdn[i]; po = po + 1; i = i + 1 }
68 pat[po] = 34 as u8; po = po + 1 // closing quote
69 pat[po] = 0 as u8
70 // find pat in resp
71 var s: i64 = 0
72 while s + po <= rn {
73 var j: i64 = 0; var ok: i64 = 1
74 while j < po { if resp[s+j] != pat[j] { ok = 0; j = po } else { j = j + 1 } }
75 if ok == 1 {
76 // object window: from match to the next '}' (Porkbun objects are flat)
77 var e: i64 = s
78 while e < rn { if (resp[e] as i64) == 125 { break } e = e + 1 }
79 let win: *u8 = resp + s
80 let wn: i64 = e - s
81 if gd_contains(win, wn, "\"type\":\"A\"" as *u8) == 1 {
82 // build "content":"<ip>"
83 let cp: *u8 = sys_mmap(128)
84 var co: i64 = 0
85 let c1: *u8 = "\"content\":\"" as *u8
86 var k: i64 = 0
87 while c1[k] != (0 as u8) { cp[co] = c1[k]; co = co + 1; k = k + 1 }
88 k = 0
89 while k < ipn { cp[co] = ip[k]; co = co + 1; k = k + 1 }
90 cp[co] = 34 as u8; co = co + 1
91 cp[co] = 0 as u8
92 if gd_contains(win, wn, cp) == 1 { return 1 }
93 }
94 }
95 s = s + 1
96 }
97 return 0
98}
99
100func gd_creds(ak: *i64, akn: *i64, sk: *i64, skn: *i64) -> i64 {
101 let raw: *u8 = sys_mmap(512)
102 let rawn: i64 = pk_readline_file("/tmp/nxsecret.out\x00" as *u8, raw, 512)
103 if rawn <= 0 { return 0 }
104 var base: i64 = 0
105 if rawn >= 3 { if (raw[0] as i64)==0xef { if (raw[1] as i64)==0xbb { if (raw[2] as i64)==0xbf { base=3 } } } }
106 var nl: i64 = base
107 while nl < rawn { if (raw[nl] as i64)==0x0a { break } nl=nl+1 }
108 ak[0] = (raw + base) as i64; akn[0] = nl - base
109 var s2: i64 = nl + 1
110 var nl2: i64 = s2
111 while nl2 < rawn { if (raw[nl2] as i64)==0x0a { break } nl2=nl2+1 }
112 sk[0] = (raw + s2) as i64; skn[0] = nl2 - s2
113 if skn[0] <= 0 { return 0 }
114 return 1
115}
116
117// ---- LAN resolve host (nul-terminated) via the router; expect packed ip. 1=match 0=no ----
118func gd_lan_is(host: *u8, want: i64) -> i64 {
119 let r: *DnsResolveResult = nx_dns_resolve_a_record(host, gd_slen(host), NX_DNS_R_LAN_IP, sys_now_realtime_sec())
120 if r.verdict != NX_DNS_R_OK { return 0 }
121 if r.ipv4_packed == want { return 1 }
122 return 0
123}
124
125// ---- TLS serve check: connect ip:443, SNI=host, validate cert, GET / -> 2xx/3xx. 1=green ----
126func gd_serve(ip: i64, host: *u8, store: *TrustStore) -> i64 {
127 let hn: i64 = gd_slen(host)
128 let fd: i64 = sys_socket(2, 1, 0)
129 if fd < 0 { return 0 }
130 sys_set_socket_timeout(fd, 20)
131 let sa: *u8 = sys_mmap(16)
132 sa[0]=2 as u8; sa[1]=0 as u8; sa[2]=1 as u8; sa[3]=187 as u8 // port 443
133 sa[4]=((ip>>24)&0xff) as u8; sa[5]=((ip>>16)&0xff) as u8; sa[6]=((ip>>8)&0xff) as u8; sa[7]=(ip&0xff) as u8
134 var z: i64=8; while z<16 { sa[z]=0 as u8; z=z+1 }
135 if nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); return 0 }
136 let cr: *u8 = sys_mmap(32); nx_csprng_fill(cr, 32)
137 let xp: *u8 = sys_mmap(32); nx_csprng_fill(xp, 32)
138 let vc_raw: *u8 = sys_mmap(128)
139 let vc: *TlsValidationContext = vc_raw as *TlsValidationContext
140 vc.store = store; vc.sni_host = host; vc.sni_host_len = hn; vc.now_epoch = sys_now_realtime_sec()
141 vc.cached_cert = 0 as *u8; vc.cached_cert_len = 0
142 vc.cert_out = 0 as *u8; vc.cert_out_cap = 0; vc.cert_out_len = 0
143 let sr: i64 = nx_tls13_client_session_run(fd, host, hn, cr, xp, vc)
144 if sr < 0 { sys_close(fd); return 0 }
145 let s: *Tls13ClientSession = sr as *Tls13ClientSession
146 let out: *u8 = sys_mmap(65536)
147 let n: i64 = nx_https_get_complete(s, fd, "/" as *u8, 1, host, hn, out, 65536)
148 sys_close(fd)
149 if n < 12 { return 0 }
150 // "HTTP/1.1 NNN" -> code at bytes 9..11
151 let code: i64 = ((out[9] as i64)-48)*100 + ((out[10] as i64)-48)*10 + ((out[11] as i64)-48)
152 if code >= 200 { if code < 400 { return 1 } }
153 return 0
154}
155
156func gd_check_line(tag: *u8, ok: i64, fixhint: *u8) -> i64 {
157 if ok == 1 { gd_puts(" GREEN " as *u8); gd_puts(tag); gd_puts("\n" as *u8); return 0 }
158 gd_puts(" RED " as *u8); gd_puts(tag); gd_puts("\n" as *u8)
159 gd_puts(" fix: " as *u8); gd_puts(fixhint); gd_puts("\n" as *u8)
160 return 1
161}
162
163// run every check for ONE conf line (domain|public_ip|lan_ip|wildcard); returns RED count.
164func gd_domain(line: *u8, ln: i64, store: *TrustStore, have_pk: i64, ak: *u8, akn: i64, sk: *u8, skn: i64) -> i64 {
165 var b1: i64 = 0 - 1
166 var b2: i64 = 0 - 1
167 var b3: i64 = 0 - 1
168 var q: i64 = 0
169 while q < ln {
170 if (line[q] as i64) == 124 {
171 if b1 < 0 { b1 = q }
172 else { if b2 < 0 { b2 = q } else { if b3 < 0 { b3 = q } } }
173 }
174 q = q + 1
175 }
176 if b3 <= 0 { return 0 }
177 let dom: *u8 = sys_mmap(b1 + 4)
178 var ci: i64 = 0
179 while ci < b1 { dom[ci] = line[ci]; ci = ci + 1 }
180 dom[b1] = 0 as u8
181 let dn: i64 = b1
182 let o1: i64 = b1 + 1
183 let pub_s: *u8 = line + o1
184 let pubn: i64 = b2 - b1 - 1
185 let o2: i64 = b2 + 1
186 let lan_s: *u8 = line + o2
187 let lann: i64 = b3 - b2 - 1
188 let wild: i64 = (line[b3+1] as i64) - 48
189 let lan_packed: i64 = gd_ipparse(lan_s, lann)
190 gd_puts(" [" as *u8)
191 gd_puts(dom)
192 gd_puts("] public=" as *u8)
193 gd_write(pub_s, pubn)
194 gd_puts(" lan=" as *u8)
195 gd_write(lan_s, lann)
196 gd_puts(" lan_wildcard=" as *u8)
197 gd_putn(wild)
198 gd_puts("\n" as *u8)
199
200 var reds: i64 = 0
201 // ---- [A] external authority: apex + wildcard A -> public ----
202 var a_apex: i64 = 0
203 var a_wild: i64 = 0
204 if have_pk == 1 {
205 let resp: *u8 = sys_mmap(131072)
206 let rn: *i64 = sys_mmap(8) as *i64
207 let prc: i64 = nx_porkbun_retrieve(ak, akn, sk, skn, dom, dn, store, sys_now_realtime_sec(), resp, 131072, rn)
208 if prc == NX_PORKBUN_OK {
209 a_apex = gd_pk_has_a(resp, rn[0], dom, dn, pub_s, pubn)
210 let wname: *u8 = sys_mmap(300)
211 wname[0] = 42 as u8
212 wname[1] = 46 as u8
213 var wi: i64 = 0
214 while wi < dn { wname[2+wi] = dom[wi]; wi = wi + 1 }
215 wname[2+dn] = 0 as u8
216 a_wild = gd_pk_has_a(resp, rn[0], wname, dn + 2, pub_s, pubn)
217 }
218 }
219 reds = reds + gd_check_line("[A1] porkbun apex A -> public edge" as *u8, a_apex, "nx_porkbun_publish_a <domain> apex <public_ip>" as *u8)
220 reds = reds + gd_check_line("[A2] porkbun star-wildcard A -> public edge (external: ANY subdomain reachable)" as *u8, a_wild, "nx_porkbun_publish_a <domain> star <public_ip>" as *u8)
221
222 // ---- [B] internal LAN split-horizon ----
223 let b_apex: i64 = gd_lan_is(dom, lan_packed)
224 reds = reds + gd_check_line("[B1] LAN router resolves apex -> lan edge" as *u8, b_apex, "whole-domain dnsmasq address entry on the router via nx_aw_routerfix (see golive_dns.conf header)" as *u8)
225 if wild == 1 {
226 let can: *u8 = sys_mmap(300)
227 let canlab: *u8 = "nx-golive-canary" as *u8
228 var co: i64 = 0
229 var gi: i64 = 0
230 while canlab[gi] != (0 as u8) { can[co] = canlab[gi]; co = co + 1; gi = gi + 1 }
231 can[co] = 46 as u8
232 co = co + 1
233 gi = 0
234 while gi < dn { can[co] = dom[gi]; co = co + 1; gi = gi + 1 }
235 can[co] = 0 as u8
236 let b_can: i64 = gd_lan_is(can, lan_packed)
237 reds = reds + gd_check_line("[B2] LAN wildcard: canary subdomain -> lan edge (nothing forgettable by construction)" as *u8, b_can, "same whole-domain address entry as B1 (per-host domain lists CANNOT pass this)" as *u8)
238 // ---- [C2] edge serve for canary (wildcard cert + whole-domain routing class-proof) ----
239 let c_can: i64 = gd_serve(lan_packed, can, store)
240 reds = reds + gd_check_line("[C2] edge serves canary SNI: valid cert + HTTP (whole-domain cert-select + route)" as *u8, c_can, "check nx_sni_route whole-domain match + wildcard cert (nx_edge_probe lan_ip 443 canary.domain /)" as *u8)
241 }
242 // ---- [C1] edge serve apex ----
243 let c_apex: i64 = gd_serve(lan_packed, dom, store)
244 reds = reds + gd_check_line("[C1] edge serves apex SNI: valid cert + HTTP 2xx/3xx" as *u8, c_apex, "nx_edge_probe lan_ip 443 domain / (sni_router, cert, proxy chain)" as *u8)
245 return reds
246}
247
248func main() -> i64 {
249 gd_puts("=== nx_golive_dns_gate: internal+external testability contract for every hosted domain ===\n" as *u8)
250 let lr: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt\x00" as *u8, 300, 4194304)
251 if lr <= 0 { gd_puts("VERDICT RED (trust store load failed)\n" as *u8); return 1 }
252 let store: *TrustStore = lr as *TrustStore
253 let akb: *i64 = sys_mmap(8) as *i64
254 let aknb: *i64 = sys_mmap(8) as *i64
255 let skb: *i64 = sys_mmap(8) as *i64
256 let sknb: *i64 = sys_mmap(8) as *i64
257 var have_pk: i64 = gd_creds(akb, aknb, skb, sknb)
258 if have_pk != 1 { gd_puts(" (porkbun creds missing: run nx_secret_cli get porkbun first; A-checks will be RED)\n" as *u8) }
259 let ak: *u8 = akb[0] as *u8
260 let sk: *u8 = skb[0] as *u8
261
262 let cbox: *i64 = sys_mmap(16) as *i64
263 let conf: *u8 = sys_read_file(GD_CONF, cbox)
264 if (conf as i64) == 0 { gd_puts("VERDICT RED (knowledge/hosting/golive_dns.conf missing)\n" as *u8); return 1 }
265 let cn: i64 = cbox[0]
266
267 var reds: i64 = 0
268 var domains: i64 = 0
269 var p: i64 = 0
270 while p < cn {
271 var e: i64 = p
272 while e < cn { if (conf[e] as i64) == 10 { break } e = e + 1 }
273 var ln: i64 = e - p
274 if ln > 0 { if (conf[p+ln-1] as i64) == 13 { ln = ln - 1 } }
275 var skip: i64 = 0
276 if ln <= 0 { skip = 1 }
277 if skip == 0 { if (conf[p] as i64) == 35 { skip = 1 } }
278 if skip == 0 {
279 domains = domains + 1
280 reds = reds + gd_domain(conf + p, ln, store, have_pk, ak, aknb[0], sk, sknb[0])
281 }
282 p = e + 1
283 }
284 gd_puts("domains checked: " as *u8)
285 gd_putn(domains)
286 gd_puts("\n" as *u8)
287 if reds == 0 { gd_puts("VERDICT GREEN -- every hosted domain is internally + externally testable by construction\n" as *u8); return 0 }
288 gd_puts("VERDICT RED -- failing checks: " as *u8)
289 gd_putn(reds)
290 gd_puts(" (each RED above carries its exact remediation)\n" as *u8)
291 return 1
292}