code wiki / (root) / nx_iot_announce_test.nx

nx_iot_announce_test.nx source

↩ module page · 162 lines · 7622 B

1// nx_iot_announce_test.nx -- in-memory smoke for nx_iot_announce. 2// 3// Exercises the parts that need no real sockets: 4// - classifier first-byte signature matrix 5// - discovery payload builders (Magic Home + WiZ) 6// - sealed-enum validity gates 7// - sockaddr port + IP decoders 8// 9// Real-network verification (binding sockets, broadcasting, 10// recvfrom-with-timeout) is documented in the roadmap and runs only 11// once user has the hub box on the LAN. This smoke is environment- 12// independent so it compiles + passes inside any container or qemu 13// run. 14// 15// expect_exit: 0 16// 17// license_tier: ORIGINAL 18 19import "nx_syscalls_x86_64.nx" 20import "nx_iot_announce.nx" 21 22func main() -> i64 { 23 // ---- Sealed enum: family validity gate ------------------------ 24 if nx_iot_family_is_valid(NX_IOT_FAMILY_UNKNOWN) != 1 { return 1 } 25 if nx_iot_family_is_valid(NX_IOT_FAMILY_TUYA_V33) != 1 { return 2 } 26 if nx_iot_family_is_valid(NX_IOT_FAMILY_MAGIC_HOME) != 1 { return 3 } 27 if nx_iot_family_is_valid(NX_IOT_FAMILY_WIZ) != 1 { return 4 } 28 if nx_iot_family_is_valid(NX_IOT_FAMILY_YEELIGHT) != 1 { return 5 } 29 if nx_iot_family_is_valid(NX_IOT_FAMILY_N) != 0 { return 6 } 30 if nx_iot_family_is_valid(-1) != 0 { return 7 } 31 if nx_iot_family_is_valid(99) != 0 { return 8 } 32 33 // ---- Sealed enum: announce verdict validity gate -------------- 34 if nx_iot_ann_verdict_is_valid(NX_IOT_ANN_VERDICT_UNKNOWN) != 1 { return 10 } 35 if nx_iot_ann_verdict_is_valid(NX_IOT_ANN_VERDICT_OK) != 1 { return 11 } 36 if nx_iot_ann_verdict_is_valid(NX_IOT_ANN_VERDICT_PARTIAL) != 1 { return 12 } 37 if nx_iot_ann_verdict_is_valid(NX_IOT_ANN_VERDICT_NO_DEVICES) != 1 { return 13 } 38 if nx_iot_ann_verdict_is_valid(NX_IOT_ANN_VERDICT_SOCKET_FAIL) != 1 { return 14 } 39 if nx_iot_ann_verdict_is_valid(NX_IOT_ANN_VERDICT_BIND_FAIL) != 1 { return 15 } 40 if nx_iot_ann_verdict_is_valid(NX_IOT_ANN_VERDICT_N) != 0 { return 16 } 41 if nx_iot_ann_verdict_is_valid(-5) != 0 { return 17 } 42 43 // ---- Classifier: Tuya v3.3 header sentinel -------------------- 44 let tuya: *u8 = sys_mmap(64) 45 tuya[0] = 0x00; tuya[1] = 0x00; tuya[2] = 0x55; tuya[3] = 0xAA 46 tuya[4] = 0x00; tuya[5] = 0x00; tuya[6] = 0x00; tuya[7] = 0x09 // seq 47 tuya[8] = 0x00; tuya[9] = 0x00; tuya[10] = 0x00; tuya[11] = 0x00 // cmd=0 BCAST 48 if nx_iot_classify_beacon(tuya, 12, 6667) != NX_IOT_FAMILY_TUYA_V33 { return 20 } 49 // Tuya signature is hard-walled, port doesn't matter 50 if nx_iot_classify_beacon(tuya, 12, 38899) != NX_IOT_FAMILY_TUYA_V33 { return 21 } 51 if nx_iot_classify_beacon(tuya, 12, 48899) != NX_IOT_FAMILY_TUYA_V33 { return 22 } 52 // Truncated < 4 bytes: cannot classify as Tuya 53 if nx_iot_classify_beacon(tuya, 3, 6667) != NX_IOT_FAMILY_UNKNOWN { return 23 } 54 55 // ---- Classifier: WiZ JSON ------------------------------------- 56 let wiz: *u8 = sys_mmap(64) 57 wiz[0] = 0x7B // { 58 wiz[1] = 0x22 // " 59 wiz[2] = 0x6D // m 60 wiz[3] = 0x65 // e 61 wiz[4] = 0x74 // t 62 if nx_iot_classify_beacon(wiz, 5, 38899) != NX_IOT_FAMILY_WIZ { return 30 } 63 // Leading-brace is the WiZ signature regardless of port 64 if nx_iot_classify_beacon(wiz, 5, 6667) != NX_IOT_FAMILY_WIZ { return 31 } 65 66 // ---- Classifier: Yeelight SSDP -------------------------------- 67 let yee: *u8 = sys_mmap(64) 68 yee[0] = 0x48 // H 69 yee[1] = 0x54 // T 70 yee[2] = 0x54 // T 71 yee[3] = 0x50 // P 72 yee[4] = 0x2F // / 73 yee[5] = 0x31 // 1 74 if nx_iot_classify_beacon(yee, 6, 1982) != NX_IOT_FAMILY_YEELIGHT { return 40 } 75 let yeenotify: *u8 = sys_mmap(64) 76 yeenotify[0] = 0x4E // N 77 yeenotify[1] = 0x4F // O 78 yeenotify[2] = 0x54 // T 79 yeenotify[3] = 0x49 // I 80 yeenotify[4] = 0x46 // F 81 yeenotify[5] = 0x59 // Y 82 if nx_iot_classify_beacon(yeenotify, 6, 1982) != NX_IOT_FAMILY_YEELIGHT { return 41 } 83 84 // ---- Classifier: Magic Home reply ----------------------------- 85 // Reply format: '192.168.1.10,B4E62D...,AK001-ZJ200' 86 let mh: *u8 = sys_mmap(64) 87 mh[0] = 0x31 // 1 88 mh[1] = 0x39 // 9 89 mh[2] = 0x32 // 2 90 mh[3] = 0x2E // . 91 if nx_iot_classify_beacon(mh, 4, 48899) != NX_IOT_FAMILY_MAGIC_HOME { return 50 } 92 // ASCII-digit alone with wrong port -> UNKNOWN (port-gated) 93 if nx_iot_classify_beacon(mh, 4, 6667) != NX_IOT_FAMILY_UNKNOWN { return 51 } 94 95 // ---- Classifier: unknown payload ------------------------------ 96 let junk: *u8 = sys_mmap(64) 97 junk[0] = 0xAB; junk[1] = 0xCD; junk[2] = 0xEF; junk[3] = 0x12 98 if nx_iot_classify_beacon(junk, 4, 6667) != NX_IOT_FAMILY_UNKNOWN { return 60 } 99 // Empty buffer 100 if nx_iot_classify_beacon(junk, 0, 6667) != NX_IOT_FAMILY_UNKNOWN { return 61 } 101 102 // ---- Discovery payload: Magic Home ---------------------------- 103 let mh_pl: *u8 = sys_mmap(32) 104 let mh_n: i64 = nx_iot_build_magic_home_discovery(mh_pl) 105 if mh_n != 17 { return 70 } 106 if mh_pl[0] != 0x48 { return 71 } // H 107 if mh_pl[1] != 0x46 { return 72 } // F 108 if mh_pl[2] != 0x2D { return 73 } // - 109 if mh_pl[16] != 0x44 { return 74 } // D 110 // Roundtrip: the built payload itself, fed back through the 111 // classifier with the right port, must hit MAGIC_HOME (first 112 // byte 'H' = 0x48 -- which would also match Yeelight via 113 // n>=6 'HTT' prefix; but mh_pl[1] = 0x46 'F' != 0x54 'T' so 114 // the Yeelight branch falls through). This proves the 115 // disambiguation is order-correct in the classifier. 116 if nx_iot_classify_beacon(mh_pl, mh_n, 48899) != NX_IOT_FAMILY_UNKNOWN { return 75 } 117 // Note: a hub-side ECHO of its own probe classifies as UNKNOWN 118 // because 'H' alone with the wrong 2nd byte doesn't match any 119 // family signature. That is correct -- our own probes should 120 // not be counted as devices. 121 122 // ---- Discovery payload: WiZ getPilot -------------------------- 123 let wz_pl: *u8 = sys_mmap(64) 124 let wz_n: i64 = nx_iot_build_wiz_discovery(wz_pl) 125 if wz_n != 33 { return 80 } 126 if wz_pl[0] != 0x7B { return 81 } // { 127 if wz_pl[1] != 0x22 { return 82 } // " 128 if wz_pl[32] != 0x7D { return 83 } // } 129 // Roundtrip: the built payload should classify as WIZ. 130 if nx_iot_classify_beacon(wz_pl, wz_n, 38899) != NX_IOT_FAMILY_WIZ { return 84 } 131 132 // ---- sockaddr port + IP decoders ------------------------------ 133 // Build a sockaddr_in by hand: 192.168.1.42:6667 -> verify the 134 // decoders read each field correctly. 135 let sa: *u8 = sys_mmap(16) 136 sa[0] = 0x02; sa[1] = 0x00 // sin_family = AF_INET (LE) 137 sa[2] = 0x1A; sa[3] = 0x0B // sin_port = 6667 BE 138 sa[4] = 192; sa[5] = 168 139 sa[6] = 1; sa[7] = 42 140 sa[8] = 0; sa[9] = 0; sa[10] = 0; sa[11] = 0 141 sa[12] = 0; sa[13] = 0; sa[14] = 0; sa[15] = 0 142 if nx_iot_sockaddr_port(sa) != 6667 { return 90 } 143 // 192.168.1.42 packed BE = 0xC0A8012A 144 if nx_iot_sockaddr_ip_be(sa) != 0xC0A8012A { return 91 } 145 146 // Another sockaddr: 10.0.0.1:48899 147 let sa2: *u8 = sys_mmap(16) 148 sa2[0] = 0x02; sa2[1] = 0x00 149 sa2[2] = 0xBF; sa2[3] = 0x03 // 48899 = 0xBF03 BE 150 sa2[4] = 10; sa2[5] = 0; sa2[6] = 0; sa2[7] = 1 151 sa2[8] = 0; sa2[9] = 0; sa2[10] = 0; sa2[11] = 0 152 sa2[12] = 0; sa2[13] = 0; sa2[14] = 0; sa2[15] = 0 153 if nx_iot_sockaddr_port(sa2) != 48899 { return 92 } 154 if nx_iot_sockaddr_ip_be(sa2) != 0x0A000001 { return 93 } 155 156 // ---- Port constants are what we think they are ---------------- 157 if NX_IOT_PORT_TUYA_V33 != 6667 { return 100 } 158 if NX_IOT_PORT_WIZ != 38899 { return 101 } 159 if NX_IOT_PORT_MAGIC_HOME != 48899 { return 102 } 160 161 return 0 162}