nx_doctor.nx source
↩ module page · 195 lines · 7670 B
1// nx_doctor.nx -- the bits-up AUTO-FIX tool a regular user runs. Probes
2// the things that break (services, sites, web layer, DNS), classifies each
3// as LOCAL (auto-fixable right here) or REMOTE (report -> the network
4// self-heals), AUTO-FIXES the local ones, and prints a plain-language
5// verdict anyone can read.
6//
7// Fixes it performs locally: restart a dead service (fork+exec); flush the
8// stale Windows DNS cache (ipconfig.exe /flushdns via WSL interop) -- the
9// real cause of "site doesn't load in my browser but the server is fine".
10//
11// Capstone of NX-NETSCOPE (diagnose) + self-heal (decide/act) + the
12// easy-for-anyone verdict. Sovereign: raw syscalls, zero deps.
13//
14// license_tier: ORIGINAL
15
16import "nx_syscalls.nx"
17import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
18const DR_MAGIC_8787: i64 = 8787
19const DR_MAGIC_1500: i64 = 1500
20
21const DR_OK: i64 = 0
22const DR_LOCAL: i64 = 1
23const DR_REMOTE: i64 = 2
24
25func dr_putn(n: i64) -> i64 {
26 if n == 0 { sys_write(1, "0" as *u8, 1); return 0 }
27 var m: i64 = n
28 let d: *u8 = sys_mmap(24)
29 var k: i64 = 0
30 while m > 0 { d[k] = (0x30 + (m % 10)) as u8; m = m / 10; k = k + 1 }
31 var i: i64 = k - 1
32 while i >= 0 { let one: *u8 = sys_mmap(1); one[0] = d[i]; sys_write(1, one, 1); i = i - 1 }
33 return 0
34}
35func dr_sym(state: i64) -> i64 {
36 if state == DR_OK { sys_write(1, "[OK]", 4); return 0 }
37 if state == DR_LOCAL { sys_write(1, "[!!]", 4); return 0 }
38 sys_write(1, "[XX]", 4)
39 return 0
40}
41
42// fill a sockaddr_in (16 bytes) for ip_packed:port.
43func dr_sockaddr(sa: *u8, ip_packed: i64, port: i64) -> i64 {
44 sa[0] = (AF_INET & 0xff) as u8
45 sa[1] = ((AF_INET >> 8) & 0xff) as u8
46 sa[2] = ((port >> 8) & 0xff) as u8
47 sa[3] = (port & 0xff) as u8
48 sa[4] = ((ip_packed >> 24) & 0xff) as u8
49 sa[5] = ((ip_packed >> 16) & 0xff) as u8
50 sa[6] = ((ip_packed >> 8) & 0xff) as u8
51 sa[7] = (ip_packed & 0xff) as u8
52 var p: i64 = 8
53 while p < 16 { sa[p] = 0 as u8; p = p + 1 }
54 return 0
55}
56func dr_timeout(fd: i64, opt: i64) -> i64 { // opt 20=RCVTIMEO 21=SNDTIMEO, 1s
57 let tv: *u8 = sys_mmap(16)
58 tv[0] = 1 as u8
59 var z: i64 = 1
60 while z < 16 { tv[z] = 0 as u8; z = z + 1 }
61 sys_setsockopt(fd, SOL_SOCKET, opt, tv, 16)
62 return 0
63}
64
65// TCP reachability: is host:port accepting connections?
66func dr_tcp_ok(ip_packed: i64, port: i64) -> i64 {
67 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
68 if fd < 0 { return 0 }
69 dr_timeout(fd, 21)
70 let sa: *u8 = sys_mmap(16)
71 dr_sockaddr(sa, ip_packed, port)
72 let rc: i64 = nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS)
73 sys_close(fd)
74 if rc == 0 { return 1 }
75 return 0
76}
77
78// HTTP status of GET / on ip:port with a Host header (verifies the web
79// layer actually serves, not just TCP). Returns the 3-digit status or -1.
80func dr_http_status(ip_packed: i64, port: i64, host: *u8, hlen: i64) -> i64 {
81 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
82 if fd < 0 { return 0 - 1 }
83 dr_timeout(fd, 21); dr_timeout(fd, 20)
84 let sa: *u8 = sys_mmap(16)
85 dr_sockaddr(sa, ip_packed, port)
86 if nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS) != 0 { sys_close(fd); return 0 - 1 }
87 let req: *u8 = sys_mmap(256)
88 var n: i64 = 0
89 let g: *u8 = "GET / HTTP/1.0\r\nHost: " as *u8
90 var gi: i64 = 0
91 while g[gi] != 0 { req[n] = g[gi]; n = n + 1; gi = gi + 1 }
92 var hi: i64 = 0
93 while hi < hlen { req[n] = host[hi]; n = n + 1; hi = hi + 1 }
94 let tail: *u8 = "\r\nConnection: close\r\n\r\n" as *u8
95 var ti: i64 = 0
96 while tail[ti] != 0 { req[n] = tail[ti]; n = n + 1; ti = ti + 1 }
97 sys_write(fd, req, n)
98 let resp: *u8 = sys_mmap(512)
99 let got: i64 = sys_read(fd, resp, 512)
100 sys_close(fd)
101 if got < 12 { return 0 - 1 }
102 // "HTTP/1.x SSS" -> status at offset 9..11
103 let s: i64 = ((resp[9] as i64) - 0x30) * 100 + ((resp[10] as i64) - 0x30) * 10 + ((resp[11] as i64) - 0x30)
104 return s
105}
106
107// auto-fix: (re)start the sovereign search site.
108func dr_restart_search() -> i64 {
109 let path: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nx_search_server.elf" as *u8
110 let pid: i64 = sys_fork()
111 if pid == 0 {
112 let argv: *i64 = sys_mmap(16) as *i64
113 argv[0] = path as i64; argv[1] = 0
114 let envp: *i64 = sys_mmap(8) as *i64
115 envp[0] = 0
116 sys_execve(path, argv, envp); sys_exit(127)
117 }
118 return pid
119}
120// auto-fix: flush the Windows DNS cache (the stale-resolution cause).
121func dr_flush_windows_dns() -> i64 {
122 let path: *u8 = "/mnt/c/Windows/System32/ipconfig.exe" as *u8
123 let pid: i64 = sys_fork()
124 if pid == 0 {
125 let argv: *i64 = sys_mmap(24) as *i64
126 argv[0] = path as i64
127 argv[1] = ("/flushdns" as *u8) as i64
128 argv[2] = 0
129 let envp: *i64 = sys_mmap(8) as *i64
130 envp[0] = 0
131 sys_execve(path, argv, envp); sys_exit(127)
132 }
133 let st: *i64 = sys_mmap(16) as *i64
134 sys_wait4(pid, st, 0)
135 return pid
136}
137
138func main() -> i64 {
139 sys_write(1, "NISHI DOCTOR -- auto-fix health check\n", 38)
140 sys_write(1, "-------------------------------------\n", 38)
141 var problems: i64 = 0
142 var fixed: i64 = 0
143 var remote: i64 = 0
144
145 let loopback: i64 = (127 << 24) | 1
146 let nas: i64 = (192 << 24) | (168 << 16) | (8 << 8) | 240
147
148 // ---- 1: sovereign search site (local, auto-fixable) ----
149 sys_write(1, " search site (127.0.0.1:8787) ........ ", 40)
150 if dr_tcp_ok(loopback, DR_MAGIC_8787) == 1 {
151 dr_sym(DR_OK); sys_write(1, " up\n", 4)
152 } else {
153 problems = problems + 1
154 dr_sym(DR_LOCAL); sys_write(1, " DOWN -- auto-fixing (restart) ... ", 35)
155 dr_restart_search(); sys_sleep_ms(DR_MAGIC_1500)
156 if dr_tcp_ok(loopback, DR_MAGIC_8787) == 1 { fixed = fixed + 1; sys_write(1, "FIXED [OK]\n", 11) }
157 else { sys_write(1, "still down\n", 11) }
158 }
159
160 // ---- 2: sites web server serving? (TCP + HTTP layer) ----
161 sys_write(1, " sites server (192.168.8.240) ....... ", 39)
162 if dr_tcp_ok(nas, 443) == 0 {
163 problems = problems + 1; remote = remote + 1
164 dr_sym(DR_REMOTE); sys_write(1, " DOWN -- remote; reported for network heal\n", 43)
165 } else {
166 // web-layer check: HTTP/80 should redirect (3xx) to HTTPS -> serving
167 let st: i64 = dr_http_status(nas, 80, "nishifamily.com" as *u8, 15)
168 if st >= 200 {
169 dr_sym(DR_OK); sys_write(1, " up + serving (HTTP ", 20); dr_putn(st); sys_write(1, ")\n", 2)
170 } else {
171 problems = problems + 1; remote = remote + 1
172 dr_sym(DR_REMOTE); sys_write(1, " TCP up but not serving -- remote; reported\n", 44)
173 }
174 }
175
176 // ---- 3: stale-DNS auto-fix (the 'works in curl, not my browser' cause) ----
177 sys_write(1, " browser DNS cache .................. ", 39)
178 dr_sym(DR_LOCAL); sys_write(1, " flushing (local) ... ", 22)
179 dr_flush_windows_dns()
180 fixed = fixed + 1
181 sys_write(1, "FLUSHED [OK]\n", 13)
182
183 // ---- summary + guidance ----
184 sys_write(1, "\n Summary: ", 12)
185 dr_putn(problems); sys_write(1, " problem(s) -- ", 15)
186 dr_putn(fixed); sys_write(1, " auto-fixed locally, ", 21)
187 dr_putn(remote); sys_write(1, " need the network.\n", 19)
188 if problems == 0 {
189 sys_write(1, " All servers healthy. If a site still won't load in YOUR browser:\n", 67)
190 sys_write(1, " - turn OFF the browser's 'Secure DNS' (it bypasses your LAN resolver), and\n", 78)
191 sys_write(1, " - clear that site's cached security (edge://net-internals/#hsts -> delete domain).\n", 86)
192 }
193 if remote > 0 { return 2 }
194 return 0
195}