nx_port_audit.nx source
↩ module page · 142 lines · 7879 B
1// nx_port_audit.nx -- SOVEREIGN LISTENING_VS_CLAIMED audit (the capability nx_daemon_health_audit lists
2// as MISSING). Parses /proc/net/tcp (no ss/netstat/shell) for sockets in LISTEN state (st=0A), extracts
3// their local ports, and checks each KNOWN Nishi daemon port: is something ACTUALLY listening, or does the
4// daemon merely CLAIM up (pidfile UP, port unbound -- the exact torrent :8097 restart-wedge symptom)?
5// Run: nx_port_audit. license_tier: ORIGINAL
6// module: nishi-core.perception.port_audit capability: PERCEPTION (daemon health)
7import "nx_syscalls.nx"
8const PA_MAGIC_1048576: i64 = 1048576
9const PA_MAGIC_8097: i64 = 8097
10const PA_MAGIC_18090: i64 = 18090
11const PA_MAGIC_7702: i64 = 7702
12const PA_MAGIC_8446: i64 = 8446
13const PA_MAGIC_8447: i64 = 8447
14const PA_MAGIC_8443: i64 = 8443
15const PA_MAGIC_8080: i64 = 8080
16
17const PA_CAP: i64 = 1024
18
19func pa_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
20func pa_n(v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} 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} let b: *u8=sys_mmap(28); var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
21func pa_hv(c: i64) -> i64 { if c>=48 { if c<=57 { return c-48 } } if c>=65 { if c<=70 { return c-55 } } if c>=97 { if c<=102 { return c-87 } } return 0-1 }
22
23// parse all LISTEN (st=0A) local ports from `path` (/proc/net/tcp or tcp6) into out[] starting at `start`.
24// line fields: 0=sl 1=local(ADDR:PORT) 2=rem 3=st ; port = the 4 hex after the LAST ':' in the local field.
25func pa_scan_file(path: *u8, out: *i64, start: i64, cap: i64) -> i64 {
26 let fd: i64 = sys_openat_rd(path); if fd < 0 { return start }
27 let buf: *u8 = sys_mmap(PA_MAGIC_1048576); var total: i64 = 0; var r: i64 = 1
28 while r > 0 { r = sys_read(fd, ((buf as i64)+total) as *u8, PA_MAGIC_1048576-total); if r > 0 { total = total + r } }
29 sys_close(fd)
30 var n: i64 = start; var i: i64 = 0; var lineno: i64 = 0
31 while i < total {
32 let ls: i64 = i
33 var le: i64 = ls; var fe: i64 = 0
34 while fe == 0 { if le >= total { fe = 1 } else { if buf[le]==(10 as u8) { fe = 1 } else { le = le + 1 } } }
35 i = le + 1
36 if lineno > 0 {
37 var p: i64 = ls; var d: i64 = 0
38 d = 0; while d == 0 { if p >= le { d = 1 } else { if buf[p]==(32 as u8) { p = p + 1 } else { d = 1 } } } // skip leading spaces
39 d = 0; while d == 0 { if p >= le { d = 1 } else { if buf[p]==(32 as u8) { d = 1 } else { p = p + 1 } } } // skip token0 (sl)
40 d = 0; while d == 0 { if p >= le { d = 1 } else { if buf[p]==(32 as u8) { p = p + 1 } else { d = 1 } } } // skip spaces
41 let t1: i64 = p
42 d = 0; while d == 0 { if p >= le { d = 1 } else { if buf[p]==(32 as u8) { d = 1 } else { p = p + 1 } } } // token1 = local_addr [t1,p)
43 let te: i64 = p
44 var colon: i64 = 0 - 1; var q: i64 = t1
45 while q < te { if buf[q]==(58 as u8) { colon = q } q = q + 1 } // ':' in local_addr
46 var port: i64 = 0
47 if colon >= 0 { var h: i64 = colon + 1; while h < te { let hv: i64 = pa_hv(buf[h] as i64); if hv >= 0 { port = port*16 + hv } h = h + 1 } }
48 d = 0; while d == 0 { if p >= le { d = 1 } else { if buf[p]==(32 as u8) { p = p + 1 } else { d = 1 } } } // skip spaces
49 d = 0; while d == 0 { if p >= le { d = 1 } else { if buf[p]==(32 as u8) { d = 1 } else { p = p + 1 } } } // skip token2 (rem)
50 d = 0; while d == 0 { if p >= le { d = 1 } else { if buf[p]==(32 as u8) { p = p + 1 } else { d = 1 } } } // skip spaces -> token3 = st
51 if p + 1 < le { if buf[p]==(48 as u8) { if buf[p+1]==(65 as u8) { if n < cap { out[n] = port; n = n + 1 } } } } // "0A" = LISTEN
52 }
53 lineno = lineno + 1
54 }
55 return n
56}
57
58func pa_check(ports: *i64, np: i64, port: i64, name: *u8) -> i64 {
59 var found: i64 = 0; var i: i64 = 0; while i < np { if ports[i] == port { found = 1 } i = i + 1 }
60 pa_p(" :" as *u8); pa_n(port); pa_p(" " as *u8); pa_p(name); pa_p(" -> " as *u8)
61 if found == 1 { pa_p("LISTENING\n" as *u8) } else { pa_p("DOWN (pidfile may CLAIM up, but nothing is bound)\n" as *u8) }
62 return found
63}
64
65// ---- SSOT-DRIVEN AUDIT (2026-08-07) ---------------------------------------------------------
66// WAS: seven hardcoded PA_MAGIC_* ports, i.e. 7 of the 46 services actually listening -- an audit
67// covering 15% of the estate that could never notice a new service or a moved one. The port map now
68// lives in knowledge/status/portmap.conf (rule 11: data, not code), so this reads every declared row.
69// REFUSES on a missing registry rather than printing an all-clear: an audit that silently checks
70// nothing is the precondition lie -- "I could not look" must never wear the same word as "all good".
71const PA_CONF: *u8 = "knowledge/status/portmap.conf"
72const PA_CONFCAP: i64 = 65536
73const PA_HASH: i64 = 35
74const PA_TABC: i64 = 9
75const PA_LF: i64 = 10
76
77func pa_eol(buf: *u8, from: i64, total: i64) -> i64 {
78 var k: i64 = from
79 while k < total { if (buf[k] as i64) == PA_LF { return k } k = k + 1 }
80 return total
81}
82func pa_tabpos(buf: *u8, from: i64, eol: i64) -> i64 {
83 var k: i64 = from
84 while k < eol { if (buf[k] as i64) == PA_TABC { return k } k = k + 1 }
85 return 0 - 1
86}
87
88// returns rows audited, or -1 when the registry is absent; down_out[0] = count of DECLARED-but-DOWN
89func pa_audit_conf(ports: *i64, np: i64, down_out: *i64) -> i64 {
90 let fd: i64 = sys_openat_rd(PA_CONF)
91 if fd < 0 { return 0 - 1 }
92 let buf: *u8 = sys_mmap(PA_CONFCAP)
93 var total: i64 = 0
94 var r: i64 = 1
95 while r > 0 { r = sys_read(fd, ((buf as i64)+total) as *u8, PA_CONFCAP-total); if r > 0 { total = total + r } }
96 sys_close(fd)
97 var i: i64 = 0
98 var rows: i64 = 0
99 var down: i64 = 0
100 while i < total {
101 let e: i64 = pa_eol(buf, i, total)
102 if (buf[i] as i64) != PA_HASH {
103 let t: i64 = pa_tabpos(buf, i, e)
104 if t > i {
105 var p: i64 = 0
106 var k: i64 = i
107 while k < t { let c: i64 = buf[k] as i64; if c >= 48 { if c <= 57 { p = p * 10 + (c - 48) } } k = k + 1 }
108 if p > 0 {
109 buf[e] = 0 as u8
110 if pa_check(ports, np, p, ((buf as i64) + t + 1) as *u8) == 0 { down = down + 1 }
111 rows = rows + 1
112 }
113 }
114 }
115 i = e + 1
116 }
117 down_out[0] = down
118 return rows
119}
120
121func main() -> i64 {
122 pa_p("=== NISHI PORT AUDIT (LISTENING vs CLAIMED) ===\n" as *u8)
123 let ports: *i64 = sys_mmap(8*PA_CAP) as *i64
124 let np4: i64 = pa_scan_file("/proc/net/tcp" as *u8, ports, 0, PA_CAP)
125 let np: i64 = pa_scan_file("/proc/net/tcp6" as *u8, ports, np4, PA_CAP) // include IPv6 LISTENers (dual-stack daemons)
126 pa_p("total LISTEN ports (tcp+tcp6)=" as *u8); pa_n(np); pa_p("\n" as *u8)
127 let dn: *i64 = sys_mmap(8) as *i64
128 dn[0] = 0
129 let rows: i64 = pa_audit_conf(ports, np, dn)
130 if rows < 0 {
131 pa_p("REFUSED: knowledge/status/portmap.conf is ABSENT -- audited NOTHING.\n" as *u8)
132 pa_p(" This is not an all-clear. Restore the registry, then re-run.\n" as *u8)
133 sys_exit(2); return 2
134 }
135 pa_p("audited " as *u8); pa_n(rows); pa_p(" declared ports, DOWN=" as *u8); pa_n(dn[0]); pa_p("\n" as *u8)
136 // The exit code now MEANS something. It used to be sys_exit(0) unconditionally, so a service
137 // being down could never fail anything and nothing could gate on this organ -- a report wearing
138 // a gate name. 0 = every declared port is bound, 1 = at least one declared service is DOWN,
139 // 2 = registry missing (could not look).
140 if dn[0] > 0 { sys_exit(1); return 1 }
141 sys_exit(0); return 0
142}