code wiki / _hdl_build / nx_portdup.nx
nx_portdup.nx source
↩ module page · 242 lines · 10911 B
1// nx_portdup.nx -- detect the WRONG-ANSWER MACHINE: two daemons LISTENING on one port (seq1785450532).
2//
3// WHAT IT CATCHES, and why nothing else did. On 2026-07-30 both nx_mgmt_api AND nx_opaque_login were
4// LISTENING on 127.0.0.1:18098 at the same time. The kernel hands each connection to whichever accepts
5// first, so a VALID control-plane route answered `404 no such route` on some requests and worked on
6// others. Every component was individually healthy -- correct binary, correct source, listener present,
7// health OK -- so the fault was filed for MONTHS as a "transport flake" and blamed on the edge. It was
8// two listeners. The banked fd-scrub note predicted the successor would fail to BIND (EADDRINUSE); the
9// reality is worse, because BOTH hold the socket and there is NO ERROR ANYWHERE to find.
10//
11// WHY A DEDICATED TOOTH: liveness ("is it up?") and health ("does it answer?") are both TRUE during this
12// fault -- the squatter answers, just wrongly. Only the SHAPE of the listen table shows it, and nothing
13// in the ecosystem looked at that shape.
14//
15// FALSE-POSITIVE DISCIPLINE (this is why it is a watch list, not a sweep of every port): DSM legitimately
16// runs SO_REUSEPORT worker pools -- :443, :5000, :5001 each show EIGHT holders by design, and the sovereign
17// edge deliberately co-binds :443 alongside DSM's nginx (a known, accepted arrangement). Flagging those
18// would train the operator to ignore this tooth, so it reports ONLY on ports declared as
19// single-owner in knowledge/portdup_watch.conf. A port not on the list is never judged.
20//
21// nx_portdup check [watchconf] -- exit 1 if any WATCHED port has more than one LISTEN holder
22// nx_portdup list [watchconf] -- holder count for every watched port (no verdict)
23// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
24import "nx_syscalls.nx"
25
26const PD_NETCAP: i64 = 2097152
27const PD_CONFCAP: i64 = 65536
28const PD_MAXPORT: i64 = 512
29const PD_NL: i64 = 10
30const PD_HASH: i64 = 35
31const PD_COLON: i64 = 58
32const PD_SPACE: i64 = 32
33const PD_TAB: i64 = 9
34const PD_STDOUT: i64 = 1
35const PD_STDERR: i64 = 2
36const PD_LISTEN_HI: i64 = 48 // '0' of the "0A" state field
37const PD_LISTEN_LO: i64 = 65 // 'A'
38const PD_EXIT_DUP: i64 = 1
39const PD_EXIT_USAGE: i64 = 2
40const PD_EXIT_REFUSED: i64 = 3
41const PD_DEF_CONF: *u8 = "knowledge/portdup_watch.conf" as *u8
42const PD_TCP4: *u8 = "/proc/net/tcp" as *u8
43const PD_TCP6: *u8 = "/proc/net/tcp6" as *u8
44
45func pd_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
46func pd_puts(s: *u8) -> i64 { sys_write(PD_STDOUT, s, pd_slen(s)); return 0 }
47func pd_werr(s: *u8) -> i64 { sys_write(PD_STDERR, s, pd_slen(s)); return 0 }
48func pd_putn(v: i64) -> i64 {
49 let t: *u8 = sys_mmap(32); var m: i64 = v
50 if m < 0 { m = 0 - m; sys_write(PD_STDOUT, "-" as *u8, 1) }
51 var k: i64 = 0
52 if m == 0 { t[0] = 48 as u8; k = 1 }
53 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
54 let b: *u8 = sys_mmap(32); var i: i64 = 0
55 while i < k { b[i] = t[k-1-i]; i = i + 1 }
56 sys_write(PD_STDOUT, b, k)
57 return 0
58}
59func pd_eqs(a: *u8, b: *u8) -> i64 {
60 var i: i64 = 0
61 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
62 if b[i] != (0 as u8) { return 0 }
63 return 1
64}
65func pd_read(path: *u8, b: *u8, cap: i64) -> i64 {
66 let fd: i64 = sys_openat_rd(path)
67 if fd < 0 { return 0 - 1 }
68 var n: i64 = 0
69 var go: i64 = 1
70 while go == 1 {
71 let r: i64 = sys_read(fd, (b as i64 + n) as *u8, cap - n)
72 if r > 0 { n = n + r } else { go = 0 }
73 if n >= cap { go = 0 }
74 }
75 sys_close(fd)
76 return n
77}
78func pd_hexval(c: i64) -> i64 {
79 if c >= 48 { if c <= 57 { return c - 48 } }
80 if c >= 65 { if c <= 70 { return c - 55 } }
81 if c >= 97 { if c <= 102 { return c - 87 } }
82 return 0 - 1
83}
84
85// Count LISTEN sockets per port in a /proc/net/tcp-style table, accumulating into cnt[] indexed by the
86// caller's port list. Format: " sl local_address rem_address st ...", local_address = HEXIP:HEXPORT,
87// st == "0A" is LISTEN. Parsed positionally by whitespace tokens so it does not care about column widths.
88func pd_scan(buf: *u8, n: i64, ports: *i64, nports: i64, cnt: *i64) -> i64 {
89 var rows: i64 = 0
90 var ls: i64 = 0
91 var i: i64 = 0
92 var line: i64 = 0
93 while i <= n {
94 var eol: i64 = 0
95 if i == n { eol = 1 } else { if buf[i] == (PD_NL as u8) { eol = 1 } }
96 if eol == 1 {
97 if line > 0 { if i > ls {
98 // token 1 = sl, token 2 = local_address, token 4 = st
99 // Whitespace tokenizer, written as explicit skip-then-take with single exit flags. The
100 // first draft folded the two scans together with index arithmetic and was simply wrong;
101 // this function decides whether a port is judged at all, so it is written to be read.
102 var tok: i64 = 0
103 var p: i64 = ls
104 var lport: i64 = 0 - 1
105 var isl: i64 = 0
106 var scan: i64 = 1
107 while scan == 1 {
108 var sgo: i64 = 1
109 while sgo == 1 {
110 if p < i { if buf[p] == (PD_SPACE as u8) { p = p + 1 } else { sgo = 0 } } else { sgo = 0 }
111 }
112 if p >= i { scan = 0 } else {
113 let st2: i64 = p
114 var ego: i64 = 1
115 while ego == 1 {
116 if p < i { if buf[p] != (PD_SPACE as u8) { p = p + 1 } else { ego = 0 } } else { ego = 0 }
117 }
118 let e: i64 = p
119 if e > st2 {
120 tok = tok + 1
121 if tok == 2 {
122 // local_address: find ':' then parse hex port
123 var c: i64 = st2
124 var cp: i64 = 0 - 1
125 while c < e { if buf[c] == (PD_COLON as u8) { cp = c; c = e } else { c = c + 1 } }
126 if cp > 0 {
127 var v: i64 = 0
128 var h: i64 = cp + 1
129 var ok: i64 = 1
130 while h < e {
131 let d: i64 = pd_hexval(buf[h] as i64)
132 if d < 0 { ok = 0; h = e } else { v = v * 16 + d; h = h + 1 }
133 }
134 if ok == 1 { lport = v }
135 }
136 }
137 if tok == 4 {
138 if e - st2 == 2 {
139 if buf[st2] == (PD_LISTEN_HI as u8) { if buf[st2+1] == (PD_LISTEN_LO as u8) { isl = 1 } }
140 }
141 }
142 }
143 }
144 }
145 if isl == 1 { if lport >= 0 {
146 rows = rows + 1
147 var q: i64 = 0
148 while q < nports { if ports[q] == lport { cnt[q] = cnt[q] + 1; q = nports } else { q = q + 1 } }
149 } }
150 } }
151 line = line + 1
152 ls = i + 1
153 }
154 i = i + 1
155 }
156 return rows
157}
158
159func main(argc: i64, argv: *i64) -> i64 {
160 if argc < 2 { pd_werr("usage: nx_portdup check|list [watchconf]\n" as *u8); sys_exit(PD_EXIT_USAGE); return PD_EXIT_USAGE }
161 let verb: *u8 = argv[1] as *u8
162 var confp: *u8 = PD_DEF_CONF
163 if argc >= 3 { confp = argv[2] as *u8 }
164
165 let conf: *u8 = sys_mmap(PD_CONFCAP)
166 let cn: i64 = pd_read(confp, conf, PD_CONFCAP - 1)
167 if cn <= 0 {
168 pd_werr("REFUSED: watch list absent/empty: " as *u8); pd_werr(confp); pd_werr("\n" as *u8)
169 sys_exit(PD_EXIT_REFUSED); return PD_EXIT_REFUSED
170 }
171 // conf rows: "<port> <name>"; '#' comments. Only DECLARED single-owner ports are ever judged.
172 let ports: *i64 = sys_mmap(8 * PD_MAXPORT) as *i64
173 let cnt: *i64 = sys_mmap(8 * PD_MAXPORT) as *i64
174 let nameoff: *i64 = sys_mmap(8 * PD_MAXPORT) as *i64
175 var nports: i64 = 0
176 var ls: i64 = 0
177 var i: i64 = 0
178 while i <= cn {
179 var eol: i64 = 0
180 if i == cn { eol = 1 } else { if conf[i] == (PD_NL as u8) { eol = 1 } }
181 if eol == 1 {
182 if i > ls { if conf[ls] != (PD_HASH as u8) {
183 var v: i64 = 0
184 var got: i64 = 0
185 var p: i64 = ls
186 while p < i {
187 let c: i64 = conf[p] as i64
188 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); got = 1; p = p + 1 } else { p = i } } else { p = i }
189 }
190 if got == 1 { if nports < PD_MAXPORT {
191 ports[nports] = v; cnt[nports] = 0; nameoff[nports] = ls; nports = nports + 1
192 } }
193 } }
194 ls = i + 1
195 }
196 i = i + 1
197 }
198 if nports == 0 {
199 pd_werr("REFUSED: no ports declared in the watch list\n" as *u8)
200 sys_exit(PD_EXIT_REFUSED); return PD_EXIT_REFUSED
201 }
202
203 let nb: *u8 = sys_mmap(PD_NETCAP)
204 var scanned: i64 = 0
205 let n4: i64 = pd_read(PD_TCP4, nb, PD_NETCAP - 1)
206 if n4 > 0 { scanned = scanned + pd_scan(nb, n4, ports, nports, cnt) }
207 let n6: i64 = pd_read(PD_TCP6, nb, PD_NETCAP - 1)
208 if n6 > 0 { scanned = scanned + pd_scan(nb, n6, ports, nports, cnt) }
209 if n4 <= 0 { if n6 <= 0 {
210 // FAIL-CLOSED: an unreadable listen table means the tooth CANNOT answer, and a tooth that
211 // silently answers GREEN when it cannot see is worse than no tooth.
212 pd_werr("REFUSED: cannot read /proc/net/tcp or tcp6 -- UNMEASURED, not clean\n" as *u8)
213 sys_exit(PD_EXIT_REFUSED); return PD_EXIT_REFUSED
214 } }
215
216 var dups: i64 = 0
217 var missing: i64 = 0
218 var k: i64 = 0
219 while k < nports {
220 if sd_is_list(verb) == 1 {
221 pd_puts(" port " as *u8); pd_putn(ports[k])
222 pd_puts(" holders=" as *u8); pd_putn(cnt[k]); pd_puts("\n" as *u8)
223 }
224 if cnt[k] > 1 {
225 pd_puts("PORT-DUP " as *u8); pd_putn(ports[k])
226 pd_puts(" has " as *u8); pd_putn(cnt[k])
227 pd_puts(" LISTEN holders -- connections are split between them, so a VALID request can get a WRONG answer from whichever accepts first (seq1785450532)\n" as *u8)
228 dups = dups + 1
229 }
230 if cnt[k] == 0 { missing = missing + 1 }
231 k = k + 1
232 }
233 pd_puts("PORTDUP watched=" as *u8); pd_putn(nports)
234 pd_puts(" listen_rows=" as *u8); pd_putn(scanned)
235 pd_puts(" dup=" as *u8); pd_putn(dups)
236 pd_puts(" unbound=" as *u8); pd_putn(missing)
237 pd_puts("\n" as *u8)
238 if dups > 0 { sys_exit(PD_EXIT_DUP); return PD_EXIT_DUP }
239 sys_exit(0)
240 return 0
241}
242func sd_is_list(v: *u8) -> i64 { if pd_eqs(v, "list" as *u8) == 1 { return 1 } return 0 }