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}