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