1 /* $NetBSD: dns_rr_test.c,v 1.2 2025/02/25 19:15:44 christos Exp $ */
2
3 /*
4 * System library.
5 */
6 #include <sys_defs.h>
7 #include <stdlib.h>
8
9 /*
10 * Utility library.
11 */
12 #include <msg.h>
13 #include <msg_vstream.h>
14 #include <mymalloc.h>
15 #include <stringops.h>
16 #include <vstring.h>
17
18 /*
19 * DNS library.
20 */
21 #include <dns.h>
22
23 #define STR(x) vstring_str(x)
24
25 /*
26 * Test helpers. TODO: move eq_dns_rr() to testing/dns_rr_testers.c; need to
27 * verify that the expected difference is reported, or use a GTEST matcher.
28 */
29
30 /* print_dns_rr - format as { qname, reply, flags } */
31
print_dns_rr(VSTRING * buf,DNS_RR * rr)32 static char *print_dns_rr(VSTRING *buf, DNS_RR *rr)
33 {
34 static VSTRING *tmp;
35
36 if (tmp == 0)
37 tmp = vstring_alloc(100);
38 vstring_sprintf(buf, "{qname=%s, reply='%s', flags=0x%x}",
39 rr->qname, dns_strrecord(tmp, rr), rr->flags);
40 return (STR(buf));
41 }
42
43 /* eq_dns_rr - predicate that two lists are equivalent */
44
eq_dns_rr(DNS_RR * got,DNS_RR * want)45 static int eq_dns_rr(DNS_RR *got, DNS_RR *want)
46 {
47 VSTRING *got_buf = 0;
48 VSTRING *want_buf = 0;
49
50 #define EQ_DNS_RR_RETURN(val) do { \
51 if (got_buf) \
52 vstring_free(got_buf); \
53 if (want_buf) \
54 vstring_free(want_buf); \
55 return (val); \
56 } while (0)
57
58 /* Same length. */
59 if (got == 0 && want == 0)
60 EQ_DNS_RR_RETURN(1);
61 if (want == 0) {
62 msg_warn("got %s, want null",
63 print_dns_rr(got_buf = vstring_alloc(100), got));
64 }
65 if (got == 0) {
66 msg_warn("got null, want %s",
67 print_dns_rr(want_buf = vstring_alloc(100), want));
68 EQ_DNS_RR_RETURN(0);
69 }
70 /* Same query name, resource record, flags. */
71 if (strcmp(print_dns_rr(got_buf = vstring_alloc(100), got),
72 print_dns_rr(want_buf = vstring_alloc(100), want)) != 0) {
73 msg_warn("got %s, want %s", STR(want_buf), STR(got_buf));
74 EQ_DNS_RR_RETURN(0);
75 }
76 /* Same children. */
77 EQ_DNS_RR_RETURN(eq_dns_rr(got->next, want->next));
78 }
79
eq_dns_rr_free(DNS_RR * got,DNS_RR * want)80 static int eq_dns_rr_free(DNS_RR *got, DNS_RR *want)
81 {
82 int res = eq_dns_rr(got, want);
83
84 dns_rr_free(got);
85 dns_rr_free(want);
86 return (res);
87 }
88
89 /*
90 * Tests and test cases.
91 */
92 typedef struct TEST_CASE {
93 const char *label; /* identifies test case */
94 int (*fn) (void);
95 } TEST_CASE;
96
97 #define PASS (0)
98 #define FAIL (1)
99
100 /*
101 * Begin helper tests. TODO: move these to testing/dns_rr_testers_test.c.
102 */
103
eq_dns_rr_qname_differ(void)104 static int eq_dns_rr_qname_differ(void)
105 {
106 DNS_RR *got = dns_rr_create("qa", "ra", T_SRV, C_IN, 3600, 1, 25, 1, "mxa", 4);
107 DNS_RR *want = dns_rr_copy(got);
108
109 myfree(want->qname);
110 want->qname = mystrdup("qb");
111 return (!eq_dns_rr_free(got, want));
112 }
113
eq_dns_rr_reply_differ(void)114 static int eq_dns_rr_reply_differ(void)
115 {
116 DNS_RR *got = dns_rr_create("qa", "ra", T_SRV, C_IN, 3600, 1, 25, 1, "mxa", 4);
117 DNS_RR *want = dns_rr_copy(got);
118
119 want->port += 1;
120 return (!eq_dns_rr_free(got, want));
121 }
122
123 /*
124 * End helper tests.
125 */
126
127 /*
128 * Begin DNS_RR tests.
129 */
130
eq_dns_rr_flags_differ(void)131 static int eq_dns_rr_flags_differ(void)
132 {
133 DNS_RR *got = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
134 DNS_RR *want = dns_rr_copy(got);
135
136 want->flags |= DNS_RR_FLAG_TRUNCATED;
137 return (!eq_dns_rr_free(got, want));
138 }
139
append_to_null_from_null(void)140 static int append_to_null_from_null(void)
141 {
142 DNS_RR *got = dns_rr_append((DNS_RR *) 0, (DNS_RR *) 0);
143 DNS_RR *want = 0;
144
145 return (eq_dns_rr_free(got, want));
146 }
147
append_to_elem_from_null(void)148 static int append_to_elem_from_null(void)
149 {
150 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
151 DNS_RR *got, *want;
152
153 got = dns_rr_append(dns_rr_copy(a), (DNS_RR *) 0);
154
155 want = a;
156
157 return (eq_dns_rr_free(got, want));
158 }
159
appent_to_null_from_elem(void)160 static int appent_to_null_from_elem(void)
161 {
162 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
163 DNS_RR *got, *want;
164
165 got = dns_rr_append((DNS_RR *) 0, dns_rr_copy(a));
166
167 want = a;
168
169 return (eq_dns_rr_free(got, want));
170 }
171
append_to_elem_from_elem(void)172 static int append_to_elem_from_elem(void)
173 {
174 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
175 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
176 DNS_RR *got, *want;
177
178 got = dns_rr_append(dns_rr_copy(a), dns_rr_copy(b));
179
180 (want = a)->next = b;
181
182 return (eq_dns_rr_free(got, want));
183 }
184
append_to_elem_from_list(void)185 static int append_to_elem_from_list(void)
186 {
187 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
188 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
189 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
190 DNS_RR *got, *want;
191
192 got = dns_rr_append(dns_rr_copy(a),
193 dns_rr_append(dns_rr_copy(b),
194 dns_rr_copy(c)));
195
196 ((want = a)->next = b)->next = c;
197
198 return (eq_dns_rr_free(got, want));
199 }
200
append_to_list_from_elem(void)201 static int append_to_list_from_elem(void)
202 {
203 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
204 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
205 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
206 DNS_RR *got, *want;
207
208 got = dns_rr_append(dns_rr_append(dns_rr_copy(a),
209 dns_rr_copy(b)),
210 dns_rr_copy(c));
211
212 ((want = a)->next = b)->next = c;
213
214 return (eq_dns_rr_free(got, want));
215 }
216
append_to_list_from_list(void)217 static int append_to_list_from_list(void)
218 {
219 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
220 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
221 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
222 DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 4);
223 DNS_RR *got, *want;
224
225 got = dns_rr_append(dns_rr_append(dns_rr_copy(a),
226 dns_rr_copy(b)),
227 dns_rr_append(dns_rr_copy(c),
228 dns_rr_copy(d)));
229
230 (((want = a)->next = b)->next = c)->next = d;
231
232 return (eq_dns_rr_free(got, want));
233 }
234
append_propagates_flags(void)235 static int append_propagates_flags(void)
236 {
237 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
238 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
239 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
240 DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 4);
241 DNS_RR *left = dns_rr_append(dns_rr_copy(a), dns_rr_copy(b));
242 DNS_RR *rite = dns_rr_append(dns_rr_copy(c), dns_rr_copy(d));
243 DNS_RR *got, *want, *rr;
244
245 for (rr = rite; rr; rr = rr->next)
246 rr->flags |= DNS_RR_FLAG_TRUNCATED;
247
248 got = dns_rr_append(left, rite);
249
250 (((want = a)->next = b)->next = c)->next = d;
251 for (rr = want; rr; rr = rr->next)
252 rr->flags |= DNS_RR_FLAG_TRUNCATED;
253
254 return (eq_dns_rr_free(got, want));
255 }
256
append_to_list_from_list_truncate(void)257 static int append_to_list_from_list_truncate(void)
258 {
259 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
260 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
261 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
262 DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 4);
263 DNS_RR *got, *want, *rr;
264
265 var_dns_rr_list_limit = 3;
266
267 ((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
268 for (rr = want; rr; rr = rr->next)
269 rr->flags |= DNS_RR_FLAG_TRUNCATED;
270
271 got = dns_rr_append(dns_rr_append(a, b),
272 dns_rr_append(c, d));
273
274 return (eq_dns_rr_free(got, want));
275 }
276
append_to_list_from_elem_elem_truncate(void)277 static int append_to_list_from_elem_elem_truncate(void)
278 {
279 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
280 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
281 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
282 DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 4);
283 DNS_RR *got, *want, *rr;
284
285 var_dns_rr_list_limit = 2;
286
287 (want = dns_rr_copy(a))->next = dns_rr_copy(b);
288 for (rr = want; rr; rr = rr->next)
289 rr->flags |= DNS_RR_FLAG_TRUNCATED;
290
291 got = dns_rr_append(a, b);
292 got = dns_rr_append(got, c); /* should be logged */
293 got = dns_rr_append(got, d); /* should be silent */
294
295 return (eq_dns_rr_free(got, want));
296 }
297
append_to_list_from_elem_truncate(void)298 static int append_to_list_from_elem_truncate(void)
299 {
300 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
301 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
302 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
303 DNS_RR *got, *want, *rr;
304
305 var_dns_rr_list_limit = 2;
306
307 (want = dns_rr_copy(a))->next = dns_rr_copy(b);
308 for (rr = want; rr; rr = rr->next)
309 rr->flags |= DNS_RR_FLAG_TRUNCATED;
310
311 got = dns_rr_append(dns_rr_append(a, b), c);
312
313 return (eq_dns_rr_free(got, want));
314 }
315
append_to_elem_from_list_truncate(void)316 static int append_to_elem_from_list_truncate(void)
317 {
318 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
319 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
320 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
321 DNS_RR *got, *want, *rr;
322
323 var_dns_rr_list_limit = 2;
324
325 (want = dns_rr_copy(a))->next = dns_rr_copy(b);
326 for (rr = want; rr; rr = rr->next)
327 rr->flags |= DNS_RR_FLAG_TRUNCATED;
328
329 got = dns_rr_append(a, dns_rr_append(b, c));
330
331 return (eq_dns_rr_free(got, want));
332 }
333
append_to_list_from_elem_exact_fit(void)334 static int append_to_list_from_elem_exact_fit(void)
335 {
336 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
337 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
338 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
339 DNS_RR *got, *want;
340
341 var_dns_rr_list_limit = 3;
342
343 ((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
344
345 got = dns_rr_append(dns_rr_append(a, b), c);
346
347 return (eq_dns_rr_free(got, want));
348 }
349
append_to_elem_from_list_exact_fit(void)350 static int append_to_elem_from_list_exact_fit(void)
351 {
352 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
353 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
354 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
355 DNS_RR *got, *want;
356
357 var_dns_rr_list_limit = 3;
358
359 ((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
360
361 got = dns_rr_append(a, dns_rr_append(b, c));
362
363 return (eq_dns_rr_free(got, want));
364 }
365
delete_middle_element(void)366 static int delete_middle_element(void)
367 {
368 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
369 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
370 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
371 DNS_RR *got, *want, *list;
372
373 ((list = a)->next = b)->next = c;
374 (want = dns_rr_copy(a))->next = dns_rr_copy(c);
375 got = dns_rr_remove(list, b);
376
377 return (eq_dns_rr_free(got, want));
378 }
379
delete_first_element(void)380 static int delete_first_element(void)
381 {
382 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
383 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
384 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
385 DNS_RR *got, *want, *list;
386
387 ((list = a)->next = b)->next = c;
388 (want = dns_rr_copy(b))->next = dns_rr_copy(c);
389 got = dns_rr_remove(list, a);
390
391 return (eq_dns_rr_free(got, want));
392 }
393
delete_last_element(void)394 static int delete_last_element(void)
395 {
396 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
397 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
398 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
399 DNS_RR *got, *want, *list;
400
401 ((list = a)->next = b)->next = c;
402 (want = dns_rr_copy(a))->next = dns_rr_copy(b);
403 got = dns_rr_remove(list, c);
404
405 return (eq_dns_rr_free(got, want));
406 }
407
detach_middle_element(void)408 static int detach_middle_element(void)
409 {
410 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
411 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
412 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
413 DNS_RR *got, *want, *list;
414
415 ((list = a)->next = b)->next = c;
416 (want = dns_rr_copy(a))->next = dns_rr_copy(c);
417 got = dns_rr_detach(list, b);
418 dns_rr_free(b);
419
420 return (eq_dns_rr_free(got, want));
421 }
422
detach_first_element(void)423 static int detach_first_element(void)
424 {
425 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
426 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
427 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
428 DNS_RR *got, *want, *list;
429
430 ((list = a)->next = b)->next = c;
431 (want = dns_rr_copy(b))->next = dns_rr_copy(c);
432 got = dns_rr_detach(list, a);
433 dns_rr_free(a);
434
435 return (eq_dns_rr_free(got, want));
436 }
437
detach_last_element(void)438 static int detach_last_element(void)
439 {
440 DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
441 DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
442 DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
443 DNS_RR *got, *want, *list;
444
445 ((list = a)->next = b)->next = c;
446 (want = dns_rr_copy(a))->next = dns_rr_copy(b);
447 got = dns_rr_detach(list, c);
448 dns_rr_free(c);
449
450 return (eq_dns_rr_free(got, want));
451 }
452
453 /*
454 * The test cases.
455 */
456 static const TEST_CASE test_cases[] = {
457
458 /*
459 * Test eq_dns_rr; TODO: move to testing/dns_rr_testers_test.c
460 */
461 "eq_dns_rr qname differ", eq_dns_rr_qname_differ,
462 "eq_dns_rr reply differ", eq_dns_rr_reply_differ,
463 "eq_dns_rr flags differ", eq_dns_rr_flags_differ,
464
465 /*
466 * Test dns_rr_append() without truncation.
467 */
468 "append to null from null", append_to_null_from_null,
469 "append to null from element", appent_to_null_from_elem,
470 "append to element from null", append_to_elem_from_null,
471 "append to element from element", append_to_elem_from_elem,
472 "append to element from list", append_to_elem_from_list,
473 "append to list from element", append_to_list_from_elem,
474 "append to list from list", append_to_list_from_list,
475
476 /*
477 * Test dns_rr_append() flag propagation.
478 */
479 "append propagates flags", append_propagates_flags,
480
481 /*
482 * Test dns_rr_append() with truncation.
483 */
484 "append to list from list truncate", append_to_list_from_list_truncate,
485 "append to list from element element truncate", append_to_list_from_elem_elem_truncate,
486 "append to list from element truncate", append_to_list_from_elem_truncate,
487 "append to element from list truncate", append_to_elem_from_list_truncate,
488 "append to list from element exact fit", append_to_list_from_elem_exact_fit,
489 "append to element from list exact fit", append_to_elem_from_list_exact_fit,
490
491 /*
492 * TODO: tests for dns_rr_sort(), dns_rr_srv_sort(), dns_rr_shuffle(),
493 * etc.
494 */
495 "delete element from list (middle)", delete_middle_element,
496 "delete element from list (first)", delete_first_element,
497 "delete element from list (last)", delete_last_element,
498 "detach element from list (middle)", detach_middle_element,
499 "detach element from list (first)", detach_first_element,
500 "detach element from list (last)", detach_last_element,
501 0,
502 };
503
main(int argc,char ** argv)504 int main(int argc, char **argv)
505 {
506 const TEST_CASE *tp;
507 int pass = 0;
508 int fail = 0;
509 VSTRING *res_buf = vstring_alloc(100);
510 int saved_limit = var_dns_rr_list_limit;
511
512 msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
513
514 for (tp = test_cases; tp->label != 0; tp++) {
515 msg_info("RUN %s", tp->label);
516 if (tp->fn() == 0) {
517 fail++;
518 msg_info("FAIL %s", tp->label);
519 } else {
520 msg_info("PASS %s", tp->label);
521 pass++;
522 }
523 var_dns_rr_list_limit = saved_limit;
524 }
525 msg_info("PASS=%d FAIL=%d", pass, fail);
526 vstring_free(res_buf);
527 exit(fail != 0);
528 }
529