code wiki / _hdl_build / nx_reach_probe.nx
nx_reach_probe.nx source
↩ module page · 122 lines · 5206 B
1// nx_reach_probe.nx -- REMOTE REACHABILITY probe: is <host>:<port> reachable from here, bits-up?
2// The triage organ's eye on the NAS/live-site world (everything else it watches is local).
3// usage: nx_reach_probe <host-or-dotted-ip> <port>
4// - dotted-quad parsed directly; hostnames via the team's OWN resolver (failover CF->G->Q9)
5// - TCP connect through the team's syscall wrappers; NO curl, NO sh
6// - SELF-BOUNDED: the network work runs in a watchdogged child; parent kills it at 10s
7// (the dsv <10s law -- a dead host must yield DOWN, never a 2-minute stall in a triage beat)
8// exit: 0 REACHABLE / 1 DNS-FAIL / 2 CONNECT-FAIL / 3 TIMEOUT / 4 usage
9// Composes nx_dns_resolve_a_record (failover resolver) + nx_http_client sockaddr layout.
10// license_tier: ORIGINAL
11import "nx_syscalls.nx"
12import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
13import "nx_dns_resolve_a_record.nx"
14const RP_MAGIC_1000000: i64 = 1000000
15func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
16func _pn(v: i64) -> i64 { let bb: *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;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
17func rp_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
18// parse decimal from NUL-terminated string; -1 if no digits
19func rp_num(s: *u8) -> i64 {
20 var v: i64 = 0
21 var any: i64 = 0
22 var i: i64 = 0
23 while s[i] != (0 as u8) {
24 if s[i] >= (48 as u8) { if s[i] <= (57 as u8) { v = v * 10 + ((s[i] as i64) - 48); any = 1 } }
25 i = i + 1
26 }
27 if any == 0 { return 0 - 1 }
28 return v
29}
30// dotted-quad "a.b.c.d" -> packed big-endian i64, or -1 if not a pure IPv4 literal
31func rp_parse_ip(s: *u8) -> i64 {
32 let oct: *i64 = sys_mmap(32) as *i64
33 var no: i64 = 0
34 var cur: i64 = 0
35 var any: i64 = 0
36 var i: i64 = 0
37 var bad: i64 = 0
38 while s[i] != (0 as u8) {
39 if s[i] == (46 as u8) {
40 if any == 0 { bad = 1 }
41 if no < 4 { oct[no] = cur }
42 no = no + 1; cur = 0; any = 0
43 } else {
44 if s[i] >= (48 as u8) {
45 if s[i] <= (57 as u8) { cur = cur * 10 + ((s[i] as i64) - 48); any = 1 } else { bad = 1 }
46 } else { bad = 1 }
47 }
48 i = i + 1
49 }
50 if any == 1 { if no < 4 { oct[no] = cur } no = no + 1 }
51 if bad == 1 { return 0 - 1 }
52 if no != 4 { return 0 - 1 }
53 var k: i64 = 0
54 while k < 4 { if oct[k] > 255 { return 0 - 1 } k = k + 1 }
55 return ((oct[0] & 0xff) << 24) | ((oct[1] & 0xff) << 16) | ((oct[2] & 0xff) << 8) | (oct[3] & 0xff)
56}
57// the actual network work (runs inside the watchdogged child)
58func rp_work(host: *u8, port: i64) -> i64 {
59 var ip: i64 = rp_parse_ip(host)
60 if ip < 0 {
61 let r: *DnsResolveResult = nx_dns_resolve_default(host, rp_len(host), sys_now_realtime_sec())
62 if r.verdict != NX_DNS_R_OK { return 1 }
63 ip = r.ipv4_packed
64 }
65 let sa: *u8 = sys_mmap(16)
66 sa[0] = 2; sa[1] = 0
67 sa[2] = (port >> 8) & 0xff
68 sa[3] = port & 0xff
69 sa[4] = (ip >> 24) & 0xff
70 sa[5] = (ip >> 16) & 0xff
71 sa[6] = (ip >> 8) & 0xff
72 sa[7] = ip & 0xff
73 var z: i64 = 8
74 while z < 16 { sa[z] = 0; z = z + 1 }
75 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
76 if fd < 0 { return 2 }
77 let cr: i64 = nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS)
78 sys_close(fd)
79 if cr < 0 { return 2 }
80 return 0
81}
82func rp_sleep_ms(ms: i64) -> i64 {
83 let ts: *i64 = sys_mmap(16) as *i64
84 ts[0] = ms / 1000
85 ts[1] = (ms % 1000) * RP_MAGIC_1000000
86 return __syscall(SYS_CLOCK_NANOSLEEP, 0, 0, ts, 0, 0, 0)
87}
88const RP_DEADLINE_TICKS: i64 = 100 // 100 x 100ms = 10s (the dsv law)
89func main(argc: i64, argv: *i64) -> i64 {
90 if argc < 3 { _p("usage: nx_reach_probe <host> <port>\n" as *u8); sys_exit(4); return 4 }
91 let host: *u8 = argv[1] as *u8
92 let port: i64 = rp_num(argv[2] as *u8)
93 if port <= 0 { _p("usage: nx_reach_probe <host> <port>\n" as *u8); sys_exit(4); return 4 }
94 let pid: i64 = sys_fork()
95 if pid == 0 { sys_exit(rp_work(host, port)); return 0 }
96 let st: *i64 = sys_mmap(16) as *i64
97 var ticks: i64 = 0
98 var rc: i64 = 3
99 var waiting: i64 = 1
100 while waiting == 1 {
101 let w: i64 = sys_wait4(pid, st, 1) // WNOHANG
102 if w == pid {
103 waiting = 0
104 if (st[0] % 128) != 0 { rc = 2 } else { rc = (st[0] >> 8) & 0xff }
105 } else {
106 ticks = ticks + 1
107 if ticks >= RP_DEADLINE_TICKS {
108 waiting = 0
109 nx_kill(pid, 9)
110 sys_wait4(pid, st, 0)
111 rc = 3
112 } else { rp_sleep_ms(100) }
113 }
114 }
115 _p("REACH host=" as *u8); _p(host); _p(" port=" as *u8); _pn(port); _p(" " as *u8)
116 if rc == 0 { _p("REACHABLE\n" as *u8) }
117 if rc == 1 { _p("DNS-FAIL\n" as *u8) }
118 if rc == 2 { _p("CONNECT-FAIL\n" as *u8) }
119 if rc == 3 { _p("TIMEOUT (watchdog killed at 10s)\n" as *u8) }
120 sys_exit(rc)
121 return rc
122}