nx_dns_authoritative_test.nx source
↩ module page · 235 lines · 8710 B
1// nx_dns_authoritative_test.nx -- smoke for authoritative DNS.
2//
3// Covers:
4// 1. Verdict + RCODE enum gates
5// 2. Zone init + add_a + lookup
6// 3. ASCII name decode from wire format
7// 4. Response build for NOERROR (A record) verifying wire bytes
8// 5. Response build for NXDOMAIN
9// 6. Top-level serve_query end-to-end
10//
11// expect_exit: 0
12//
13// license_tier: ORIGINAL
14
15import "nx_syscalls_x86_64.nx"
16import "nx_dns_authoritative.nx"
17
18func bytes_eq(a: *u8, b: *u8, n: i64) -> i64 {
19 var i: i64 = 0
20 while i < n {
21 if a[i] != b[i] { return 0 }
22 i = i + 1
23 }
24 return 1
25}
26
27func main() -> i64 {
28 // ---- Verdict enum gate ----
29 if nxda_verdict_is_valid(NXDA_OK) != 1 { return 1 }
30 if nxda_verdict_is_valid(NXDA_PARSE_ERR) != 1 { return 2 }
31 if nxda_verdict_is_valid(NXDA_VERDICT_N) != 0 { return 3 }
32 if nxda_verdict_is_valid(-1) != 0 { return 4 }
33
34 if bytes_eq(nxda_verdict_name(NXDA_BIND_ERR),
35 "BIND_ERR" as *u8, 8) != 1 { return 5 }
36 if bytes_eq(nxda_verdict_name(NXDA_PARSE_ERR),
37 "PARSE_ERR" as *u8, 9) != 1 { return 6 }
38
39 // ---- Zone init + add + lookup ----
40 let zone_raw: *u8 = sys_mmap(NX_DNS_ZONE_BYTES)
41 let zone: *NxDnsZone = zone_raw as *NxDnsZone
42 let names_buf: *u8 = sys_mmap(1024)
43 let rz: i64 = nx_dns_zone_init(zone, 16, names_buf, 1024)
44 if rz != NXDA_OK { return 10 }
45
46 let r_add: i64 = nx_dns_zone_add_a(zone,
47 "nishifamily.com" as *u8, 15,
48 192, 0, 2, 42)
49 if r_add != NXDA_OK { return 11 }
50
51 let r_add2: i64 = nx_dns_zone_add_a(zone,
52 "audit.nishifamily.com" as *u8, 21,
53 192, 0, 2, 43)
54 if r_add2 != NXDA_OK { return 12 }
55
56 // Lookup hit
57 let idx: i64 = nx_dns_zone_lookup_a(zone,
58 "nishifamily.com" as *u8, 15)
59 if idx != 0 { return 20 }
60
61 let idx2: i64 = nx_dns_zone_lookup_a(zone,
62 "audit.nishifamily.com" as *u8, 21)
63 if idx2 != 1 { return 21 }
64
65 // Lookup miss
66 let idx_miss: i64 = nx_dns_zone_lookup_a(zone,
67 "evil.com" as *u8, 8)
68 if idx_miss != -1 { return 22 }
69
70 // ---- Wire-format name decode ----
71 // Build a wire-format query for "test.example.com" (16 bytes).
72 // Header (12 zeros) + [4]test[7]example[3]com[0] = 12 + 18 = 30 bytes.
73 let wire: *u8 = sys_mmap(64)
74 var i: i64 = 0
75 while i < 12 { wire[i] = 0 as u8; i = i + 1 }
76 wire[12] = 4 as u8
77 wire[13] = 0x74 as u8 // t
78 wire[14] = 0x65 as u8 // e
79 wire[15] = 0x73 as u8 // s
80 wire[16] = 0x74 as u8 // t
81 wire[17] = 7 as u8
82 wire[18] = 0x65 as u8 // e
83 wire[19] = 0x78 as u8 // x
84 wire[20] = 0x61 as u8 // a
85 wire[21] = 0x6d as u8 // m
86 wire[22] = 0x70 as u8 // p
87 wire[23] = 0x6c as u8 // l
88 wire[24] = 0x65 as u8 // e
89 wire[25] = 3 as u8
90 wire[26] = 0x63 as u8 // c
91 wire[27] = 0x6f as u8 // o
92 wire[28] = 0x6d as u8 // m
93 wire[29] = 0 as u8
94
95 let dec_buf: *u8 = sys_mmap(64)
96 let after: *i64 = sys_mmap(8) as *i64
97 let n_dec: i64 = nx_dns_decode_name_ascii(wire, 30, 12,
98 dec_buf, 64, after)
99 if n_dec != 16 { return 30 }
100 if bytes_eq(dec_buf, "test.example.com" as *u8, 16) != 1 { return 31 }
101 if after[0] != 30 { return 32 }
102
103 // ---- Build response: NOERROR + A record ----
104 // Build a synthetic query: header(12) + [3]foo[3]com[0] + QTYPE A + CLASS IN.
105 let q: *u8 = sys_mmap(64)
106 // tx_id = 0x1234
107 q[0] = 0x12 as u8; q[1] = 0x34 as u8
108 // flags = 0x0100 (RD)
109 q[2] = 0x01 as u8; q[3] = 0x00 as u8
110 // QDCOUNT 1, ANCOUNT 0, NSCOUNT 0, ARCOUNT 0
111 q[4] = 0 as u8; q[5] = 1 as u8
112 q[6] = 0 as u8; q[7] = 0 as u8
113 q[8] = 0 as u8; q[9] = 0 as u8
114 q[10] = 0 as u8; q[11] = 0 as u8
115 // QNAME = [3]foo[3]com[0]
116 q[12] = 3 as u8
117 q[13] = 0x66 as u8; q[14] = 0x6f as u8; q[15] = 0x6f as u8 // foo
118 q[16] = 3 as u8
119 q[17] = 0x63 as u8; q[18] = 0x6f as u8; q[19] = 0x6d as u8 // com
120 q[20] = 0 as u8
121 // QTYPE = A (1)
122 q[21] = 0 as u8; q[22] = 1 as u8
123 // QCLASS = IN (1)
124 q[23] = 0 as u8; q[24] = 1 as u8
125
126 let resp: *u8 = sys_mmap(128)
127 let ipv4: *u8 = sys_mmap(4)
128 ipv4[0] = 1 as u8; ipv4[1] = 2 as u8; ipv4[2] = 3 as u8; ipv4[3] = 4 as u8
129
130 let n_resp: i64 = nx_dns_build_response(q, 25,
131 NXDA_RCODE_NOERROR,
132 ipv4, 300,
133 resp, 128)
134 // Expected: 12 (header) + 13 (question echo) + 16 (answer) = 41 bytes
135 if n_resp != 41 { return 40 }
136 // tx_id echo
137 if resp[0] != 0x12 as u8 { return 41 }
138 if resp[1] != 0x34 as u8 { return 42 }
139 // QR=1, AA=1, RCODE=0 -> flags = 0x8400
140 if resp[2] != 0x84 as u8 { return 43 }
141 if resp[3] != 0x00 as u8 { return 44 }
142 // QDCOUNT = 1, ANCOUNT = 1
143 if resp[5] != 1 as u8 { return 45 }
144 if resp[7] != 1 as u8 { return 46 }
145 // Answer name pointer (0xC00C points to offset 12 = start of QNAME)
146 if resp[25] != 0xc0 as u8 { return 47 }
147 if resp[26] != 0x0c as u8 { return 48 }
148 // TYPE A
149 if resp[27] != 0 as u8 { return 49 }
150 if resp[28] != 1 as u8 { return 50 }
151 // RDATA = 1.2.3.4
152 if resp[37] != 1 as u8 { return 51 }
153 if resp[38] != 2 as u8 { return 52 }
154 if resp[39] != 3 as u8 { return 53 }
155 if resp[40] != 4 as u8 { return 54 }
156
157 // ---- Build response: NXDOMAIN (no answer) ----
158 let n_nx: i64 = nx_dns_build_response(q, 25,
159 NXDA_RCODE_NXDOMAIN,
160 0 as *u8, 0,
161 resp, 128)
162 // Expected: 12 + 13 = 25 bytes (no answer section)
163 if n_nx != 25 { return 60 }
164 // Flags: QR=1 AA=1 RCODE=3 -> 0x8403
165 if resp[3] != 0x03 as u8 { return 61 }
166 // ANCOUNT = 0
167 if resp[7] != 0 as u8 { return 62 }
168
169 // ---- End-to-end serve_query ----
170 // Build a query for "nishifamily.com" (15 bytes ASCII).
171 // wire: [11]nishifamily[3]com[0] = 11+1+3+1+1 = 17 bytes
172 let q2: *u8 = sys_mmap(128)
173 q2[0] = 0xab as u8; q2[1] = 0xcd as u8
174 q2[2] = 0x01 as u8; q2[3] = 0x00 as u8
175 q2[4] = 0 as u8; q2[5] = 1 as u8
176 q2[6] = 0 as u8; q2[7] = 0 as u8
177 q2[8] = 0 as u8; q2[9] = 0 as u8
178 q2[10] = 0 as u8; q2[11] = 0 as u8
179 // QNAME = [11]nishifamily[3]com[0]
180 q2[12] = 11 as u8
181 q2[13] = 0x6e as u8; q2[14] = 0x69 as u8; q2[15] = 0x73 as u8
182 q2[16] = 0x68 as u8; q2[17] = 0x69 as u8; q2[18] = 0x66 as u8
183 q2[19] = 0x61 as u8; q2[20] = 0x6d as u8; q2[21] = 0x69 as u8
184 q2[22] = 0x6c as u8; q2[23] = 0x79 as u8
185 q2[24] = 3 as u8
186 q2[25] = 0x63 as u8; q2[26] = 0x6f as u8; q2[27] = 0x6d as u8
187 q2[28] = 0 as u8
188 // QTYPE A
189 q2[29] = 0 as u8; q2[30] = 1 as u8
190 // QCLASS IN
191 q2[31] = 0 as u8; q2[32] = 1 as u8
192
193 let resp2: *u8 = sys_mmap(256)
194 let n_full: i64 = nx_dns_serve_query(zone, q2, 33, resp2, 256)
195 // Expected: 12 header + 21 question + 16 answer = 49 bytes
196 if n_full != 49 { return 70 }
197 // tx_id echo
198 if resp2[0] != 0xab as u8 { return 71 }
199 if resp2[1] != 0xcd as u8 { return 72 }
200 // RCODE = 0 (NOERROR), AA=1, QR=1
201 if resp2[3] != 0x00 as u8 { return 73 }
202 // ANCOUNT = 1
203 if resp2[7] != 1 as u8 { return 74 }
204 // RDATA = 192.0.2.42 (the zone entry)
205 if resp2[n_full - 4] != 192 as u8 { return 75 }
206 if resp2[n_full - 3] != 0 as u8 { return 76 }
207 if resp2[n_full - 2] != 2 as u8 { return 77 }
208 if resp2[n_full - 1] != 42 as u8 { return 78 }
209
210 // ---- serve_query for out-of-zone -> NXDOMAIN ----
211 // Build a query for "evil.com"
212 let q3: *u8 = sys_mmap(64)
213 q3[0] = 0xee as u8; q3[1] = 0xff as u8
214 q3[2] = 0x01 as u8; q3[3] = 0x00 as u8
215 q3[4] = 0 as u8; q3[5] = 1 as u8
216 q3[6] = 0 as u8; q3[7] = 0 as u8
217 q3[8] = 0 as u8; q3[9] = 0 as u8
218 q3[10] = 0 as u8; q3[11] = 0 as u8
219 // [4]evil[3]com[0]
220 q3[12] = 4 as u8
221 q3[13] = 0x65 as u8; q3[14] = 0x76 as u8; q3[15] = 0x69 as u8; q3[16] = 0x6c as u8
222 q3[17] = 3 as u8
223 q3[18] = 0x63 as u8; q3[19] = 0x6f as u8; q3[20] = 0x6d as u8
224 q3[21] = 0 as u8
225 q3[22] = 0 as u8; q3[23] = 1 as u8
226 q3[24] = 0 as u8; q3[25] = 1 as u8
227
228 let resp3: *u8 = sys_mmap(128)
229 let n_nx3: i64 = nx_dns_serve_query(zone, q3, 26, resp3, 128)
230 if n_nx3 < 0 { return 80 }
231 // RCODE = 3 (NXDOMAIN) -> resp[3] = 0x03
232 if resp3[3] != 0x03 as u8 { return 81 }
233
234 return 0
235}