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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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