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}