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