1 /* $NetBSD: dns.c,v 1.4 2025/01/26 16:25:51 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file */ 17 18 #include <inttypes.h> 19 #include <sched.h> /* IWYU pragma: keep */ 20 #include <setjmp.h> 21 #include <stdarg.h> 22 #include <stdbool.h> 23 #include <stddef.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <time.h> 27 #include <unistd.h> 28 29 #include <isc/buffer.h> 30 #include <isc/file.h> 31 #include <isc/hash.h> 32 #include <isc/hex.h> 33 #include <isc/lex.h> 34 #include <isc/managers.h> 35 #include <isc/mem.h> 36 #include <isc/netmgr.h> 37 #include <isc/os.h> 38 #include <isc/random.h> 39 #include <isc/result.h> 40 #include <isc/stdio.h> 41 #include <isc/string.h> 42 #include <isc/timer.h> 43 #include <isc/util.h> 44 45 #include <dns/callbacks.h> 46 #include <dns/db.h> 47 #include <dns/dispatch.h> 48 #include <dns/fixedname.h> 49 #include <dns/log.h> 50 #include <dns/name.h> 51 #include <dns/view.h> 52 #include <dns/zone.h> 53 54 #include <tests/dns.h> 55 56 dns_zonemgr_t *zonemgr = NULL; 57 58 /* 59 * Create a view. 60 */ 61 isc_result_t 62 dns_test_makeview(const char *name, bool with_dispatchmgr, bool with_cache, 63 dns_view_t **viewp) { 64 isc_result_t result; 65 dns_view_t *view = NULL; 66 dns_cache_t *cache = NULL; 67 dns_dispatchmgr_t *dispatchmgr = NULL; 68 69 if (with_dispatchmgr) { 70 result = dns_dispatchmgr_create(mctx, loopmgr, netmgr, 71 &dispatchmgr); 72 if (result != ISC_R_SUCCESS) { 73 return result; 74 } 75 } 76 77 result = dns_view_create(mctx, loopmgr, dispatchmgr, dns_rdataclass_in, 78 name, &view); 79 80 if (dispatchmgr != NULL) { 81 dns_dispatchmgr_detach(&dispatchmgr); 82 } 83 84 if (result != ISC_R_SUCCESS) { 85 return result; 86 } 87 88 if (with_cache) { 89 result = dns_cache_create(loopmgr, dns_rdataclass_in, "", mctx, 90 &cache); 91 if (result != ISC_R_SUCCESS) { 92 dns_view_detach(&view); 93 return result; 94 } 95 96 dns_view_setcache(view, cache, false); 97 /* 98 * Reference count for "cache" is now at 2, so decrement it in 99 * order for the cache to be automatically freed when "view" 100 * gets freed. 101 */ 102 dns_cache_detach(&cache); 103 } 104 105 *viewp = view; 106 107 return ISC_R_SUCCESS; 108 } 109 110 isc_result_t 111 dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view, 112 bool createview) { 113 dns_fixedname_t fixed_origin; 114 dns_zone_t *zone = NULL; 115 isc_result_t result; 116 dns_name_t *origin; 117 118 REQUIRE(view == NULL || !createview); 119 120 /* 121 * Create the zone structure. 122 */ 123 dns_zone_create(&zone, mctx, 0); 124 125 /* 126 * Set zone type and origin. 127 */ 128 dns_zone_settype(zone, dns_zone_primary); 129 origin = dns_fixedname_initname(&fixed_origin); 130 result = dns_name_fromstring(origin, name, dns_rootname, 0, NULL); 131 if (result != ISC_R_SUCCESS) { 132 goto detach_zone; 133 } 134 result = dns_zone_setorigin(zone, origin); 135 if (result != ISC_R_SUCCESS) { 136 goto detach_zone; 137 } 138 139 /* 140 * If requested, create a view. 141 */ 142 if (createview) { 143 result = dns_test_makeview("view", false, false, &view); 144 if (result != ISC_R_SUCCESS) { 145 goto detach_zone; 146 } 147 } 148 149 /* 150 * If a view was passed as an argument or created above, attach the 151 * created zone to it. Otherwise, set the zone's class to IN. 152 */ 153 if (view != NULL) { 154 dns_zone_setview(zone, view); 155 dns_zone_setclass(zone, view->rdclass); 156 dns_view_addzone(view, zone); 157 } else { 158 dns_zone_setclass(zone, dns_rdataclass_in); 159 } 160 161 *zonep = zone; 162 163 return ISC_R_SUCCESS; 164 165 detach_zone: 166 dns_zone_detach(&zone); 167 168 return result; 169 } 170 171 void 172 dns_test_setupzonemgr(void) { 173 REQUIRE(zonemgr == NULL); 174 175 dns_zonemgr_create(mctx, netmgr, &zonemgr); 176 } 177 178 isc_result_t 179 dns_test_managezone(dns_zone_t *zone) { 180 isc_result_t result; 181 REQUIRE(zonemgr != NULL); 182 183 result = dns_zonemgr_managezone(zonemgr, zone); 184 return result; 185 } 186 187 void 188 dns_test_releasezone(dns_zone_t *zone) { 189 REQUIRE(zonemgr != NULL); 190 dns_zonemgr_releasezone(zonemgr, zone); 191 } 192 193 void 194 dns_test_closezonemgr(void) { 195 REQUIRE(zonemgr != NULL); 196 197 dns_zonemgr_shutdown(zonemgr); 198 dns_zonemgr_detach(&zonemgr); 199 } 200 201 /* 202 * Sleep for 'usec' microseconds. 203 */ 204 void 205 dns_test_nap(uint32_t usec) { 206 struct timespec ts; 207 208 ts.tv_sec = usec / (long)US_PER_SEC; 209 ts.tv_nsec = (usec % (long)US_PER_SEC) * (long)NS_PER_US; 210 nanosleep(&ts, NULL); 211 } 212 213 isc_result_t 214 dns_test_loaddb(dns_db_t **db, dns_dbtype_t dbtype, const char *origin, 215 const char *testfile) { 216 isc_result_t result; 217 dns_fixedname_t fixed; 218 dns_name_t *name = NULL; 219 const char *dbimp = (dbtype == dns_dbtype_zone) ? ZONEDB_DEFAULT 220 : CACHEDB_DEFAULT; 221 222 name = dns_fixedname_initname(&fixed); 223 224 result = dns_name_fromstring(name, origin, dns_rootname, 0, NULL); 225 if (result != ISC_R_SUCCESS) { 226 return result; 227 } 228 229 result = dns_db_create(mctx, dbimp, name, dbtype, dns_rdataclass_in, 0, 230 NULL, db); 231 if (result != ISC_R_SUCCESS) { 232 return result; 233 } 234 235 result = dns_db_load(*db, testfile, dns_masterformat_text, 0); 236 return result; 237 } 238 239 static int 240 fromhex(char c) { 241 if (c >= '0' && c <= '9') { 242 return c - '0'; 243 } else if (c >= 'a' && c <= 'f') { 244 return c - 'a' + 10; 245 } else if (c >= 'A' && c <= 'F') { 246 return c - 'A' + 10; 247 } 248 249 printf("bad input format: %02x\n", c); 250 exit(3); 251 } 252 253 /* 254 * Format contents of given memory region as a hex string, using the buffer 255 * of length 'buflen' pointed to by 'buf'. 'buflen' must be at least three 256 * times 'len'. Always returns 'buf'. 257 */ 258 char * 259 dns_test_tohex(const unsigned char *data, size_t len, char *buf, 260 size_t buflen) { 261 isc_constregion_t source = { .base = data, .length = len }; 262 isc_buffer_t target; 263 isc_result_t result; 264 265 memset(buf, 0, buflen); 266 isc_buffer_init(&target, buf, buflen); 267 result = isc_hex_totext((isc_region_t *)&source, 1, " ", &target); 268 INSIST(result == ISC_R_SUCCESS); 269 270 return buf; 271 } 272 273 isc_result_t 274 dns_test_getdata(const char *file, unsigned char *buf, size_t bufsiz, 275 size_t *sizep) { 276 isc_result_t result; 277 unsigned char *bp; 278 char *rp, *wp; 279 char s[BUFSIZ]; 280 size_t len, i; 281 FILE *f = NULL; 282 int n; 283 284 result = isc_stdio_open(file, "r", &f); 285 if (result != ISC_R_SUCCESS) { 286 return result; 287 } 288 289 bp = buf; 290 while (fgets(s, sizeof(s), f) != NULL) { 291 rp = s; 292 wp = s; 293 len = 0; 294 while (*rp != '\0') { 295 if (*rp == '#') { 296 break; 297 } 298 if (*rp != ' ' && *rp != '\t' && *rp != '\r' && 299 *rp != '\n') 300 { 301 *wp++ = *rp; 302 len++; 303 } 304 rp++; 305 } 306 if (len == 0U) { 307 continue; 308 } 309 if (len % 2 != 0U) { 310 result = ISC_R_UNEXPECTEDEND; 311 break; 312 } 313 if (len > bufsiz * 2) { 314 result = ISC_R_NOSPACE; 315 break; 316 } 317 rp = s; 318 for (i = 0; i < len; i += 2) { 319 n = fromhex(*rp++); 320 n *= 16; 321 n += fromhex(*rp++); 322 *bp++ = n; 323 } 324 } 325 326 if (result == ISC_R_SUCCESS) { 327 *sizep = bp - buf; 328 } 329 330 isc_stdio_close(f); 331 return result; 332 } 333 334 static void 335 nullmsg(dns_rdatacallbacks_t *cb, const char *fmt, ...) { 336 UNUSED(cb); 337 UNUSED(fmt); 338 } 339 340 isc_result_t 341 dns_test_rdatafromstring(dns_rdata_t *rdata, dns_rdataclass_t rdclass, 342 dns_rdatatype_t rdtype, unsigned char *dst, 343 size_t dstlen, const char *src, bool warnings) { 344 dns_rdatacallbacks_t callbacks; 345 isc_buffer_t source, target; 346 isc_lex_t *lex = NULL; 347 isc_lexspecials_t specials = { 0 }; 348 isc_result_t result; 349 size_t length; 350 351 REQUIRE(rdata != NULL); 352 REQUIRE(DNS_RDATA_INITIALIZED(rdata)); 353 REQUIRE(dst != NULL); 354 REQUIRE(src != NULL); 355 356 /* 357 * Set up source to hold the input string. 358 */ 359 length = strlen(src); 360 isc_buffer_constinit(&source, src, length); 361 isc_buffer_add(&source, length); 362 363 /* 364 * Create a lexer as one is required by dns_rdata_fromtext(). 365 */ 366 isc_lex_create(mctx, 64, &lex); 367 368 /* 369 * Set characters which will be treated as valid multi-line RDATA 370 * delimiters while reading the source string. These should match 371 * specials from lib/dns/master.c. 372 */ 373 specials[0] = 1; 374 specials['('] = 1; 375 specials[')'] = 1; 376 specials['"'] = 1; 377 isc_lex_setspecials(lex, specials); 378 379 /* 380 * Expect DNS masterfile comments. 381 */ 382 isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); 383 384 /* 385 * Point lexer at source. 386 */ 387 result = isc_lex_openbuffer(lex, &source); 388 if (result != ISC_R_SUCCESS) { 389 goto destroy_lexer; 390 } 391 392 /* 393 * Set up target for storing uncompressed wire form of provided RDATA. 394 */ 395 isc_buffer_init(&target, dst, dstlen); 396 397 /* 398 * Set up callbacks so warnings and errors are not printed. 399 */ 400 if (!warnings) { 401 dns_rdatacallbacks_init(&callbacks); 402 callbacks.warn = callbacks.error = nullmsg; 403 } 404 405 /* 406 * Parse input string, determining result. 407 */ 408 result = dns_rdata_fromtext(rdata, rdclass, rdtype, lex, dns_rootname, 409 0, mctx, &target, &callbacks); 410 411 destroy_lexer: 412 isc_lex_destroy(&lex); 413 414 return result; 415 } 416 417 void 418 dns_test_namefromstring(const char *namestr, dns_fixedname_t *fname) { 419 size_t length; 420 isc_buffer_t *b = NULL; 421 isc_result_t result; 422 dns_name_t *name; 423 424 length = strlen(namestr); 425 426 name = dns_fixedname_initname(fname); 427 428 isc_buffer_allocate(mctx, &b, length); 429 430 isc_buffer_putmem(b, (const unsigned char *)namestr, length); 431 result = dns_name_fromtext(name, b, NULL, 0, NULL); 432 INSIST(result == ISC_R_SUCCESS); 433 434 isc_buffer_free(&b); 435 } 436 437 isc_result_t 438 dns_test_difffromchanges(dns_diff_t *diff, const zonechange_t *changes, 439 bool warnings) { 440 isc_result_t result = ISC_R_SUCCESS; 441 unsigned char rdata_buf[1024]; 442 dns_difftuple_t *tuple = NULL; 443 isc_consttextregion_t region; 444 dns_rdatatype_t rdatatype; 445 dns_fixedname_t fixedname; 446 dns_rdata_t rdata; 447 dns_name_t *name; 448 size_t i; 449 450 REQUIRE(diff != NULL); 451 REQUIRE(changes != NULL); 452 453 dns_diff_init(mctx, diff); 454 455 for (i = 0; changes[i].owner != NULL; i++) { 456 /* 457 * Parse owner name. 458 */ 459 name = dns_fixedname_initname(&fixedname); 460 result = dns_name_fromstring(name, changes[i].owner, 461 dns_rootname, 0, mctx); 462 if (result != ISC_R_SUCCESS) { 463 break; 464 } 465 466 /* 467 * Parse RDATA type. 468 */ 469 region.base = changes[i].type; 470 region.length = strlen(changes[i].type); 471 result = dns_rdatatype_fromtext(&rdatatype, 472 (isc_textregion_t *)®ion); 473 if (result != ISC_R_SUCCESS) { 474 break; 475 } 476 477 /* 478 * Parse RDATA. 479 */ 480 dns_rdata_init(&rdata); 481 result = dns_test_rdatafromstring( 482 &rdata, dns_rdataclass_in, rdatatype, rdata_buf, 483 sizeof(rdata_buf), changes[i].rdata, warnings); 484 if (result != ISC_R_SUCCESS) { 485 break; 486 } 487 488 /* 489 * Create a diff tuple for the parsed change and append it to 490 * the diff. 491 */ 492 result = dns_difftuple_create(mctx, changes[i].op, name, 493 changes[i].ttl, &rdata, &tuple); 494 if (result != ISC_R_SUCCESS) { 495 break; 496 } 497 dns_diff_append(diff, &tuple); 498 } 499 500 if (result != ISC_R_SUCCESS) { 501 dns_diff_clear(diff); 502 } 503 504 return result; 505 } 506