/* $NetBSD: dns.c,v 1.2.4.2 2024/02/29 12:35:58 martin Exp $ */ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #include #include /* IWYU pragma: keep */ #include #include #include #include #include #include #include #include #define UNIT_TESTING #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include dns_zonemgr_t *zonemgr = NULL; /* * Create a view. */ isc_result_t dns_test_makeview(const char *name, bool with_cache, dns_view_t **viewp) { isc_result_t result; dns_view_t *view = NULL; dns_cache_t *cache = NULL; result = dns_view_create(mctx, dns_rdataclass_in, name, &view); if (result != ISC_R_SUCCESS) { return (result); } if (with_cache) { result = dns_cache_create(mctx, mctx, taskmgr, timermgr, dns_rdataclass_in, "", "rbt", 0, NULL, &cache); if (result != ISC_R_SUCCESS) { dns_view_detach(&view); return (result); } dns_view_setcache(view, cache, false); /* * Reference count for "cache" is now at 2, so decrement it in * order for the cache to be automatically freed when "view" * gets freed. */ dns_cache_detach(&cache); } *viewp = view; return (ISC_R_SUCCESS); } isc_result_t dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view, bool createview) { dns_fixedname_t fixed_origin; dns_zone_t *zone = NULL; isc_result_t result; dns_name_t *origin; REQUIRE(view == NULL || !createview); /* * Create the zone structure. */ result = dns_zone_create(&zone, mctx); if (result != ISC_R_SUCCESS) { return (result); } /* * Set zone type and origin. */ dns_zone_settype(zone, dns_zone_primary); origin = dns_fixedname_initname(&fixed_origin); result = dns_name_fromstring(origin, name, 0, NULL); if (result != ISC_R_SUCCESS) { goto detach_zone; } result = dns_zone_setorigin(zone, origin); if (result != ISC_R_SUCCESS) { goto detach_zone; } /* * If requested, create a view. */ if (createview) { result = dns_test_makeview("view", false, &view); if (result != ISC_R_SUCCESS) { goto detach_zone; } } /* * If a view was passed as an argument or created above, attach the * created zone to it. Otherwise, set the zone's class to IN. */ if (view != NULL) { dns_zone_setview(zone, view); dns_zone_setclass(zone, view->rdclass); dns_view_addzone(view, zone); } else { dns_zone_setclass(zone, dns_rdataclass_in); } *zonep = zone; return (ISC_R_SUCCESS); detach_zone: dns_zone_detach(&zone); return (result); } isc_result_t dns_test_setupzonemgr(void) { isc_result_t result; REQUIRE(zonemgr == NULL); result = dns_zonemgr_create(mctx, taskmgr, timermgr, netmgr, &zonemgr); return (result); } isc_result_t dns_test_managezone(dns_zone_t *zone) { isc_result_t result; REQUIRE(zonemgr != NULL); result = dns_zonemgr_setsize(zonemgr, 1); if (result != ISC_R_SUCCESS) { return (result); } result = dns_zonemgr_managezone(zonemgr, zone); return (result); } void dns_test_releasezone(dns_zone_t *zone) { REQUIRE(zonemgr != NULL); dns_zonemgr_releasezone(zonemgr, zone); } void dns_test_closezonemgr(void) { REQUIRE(zonemgr != NULL); dns_zonemgr_shutdown(zonemgr); dns_zonemgr_detach(&zonemgr); } /* * Sleep for 'usec' microseconds. */ void dns_test_nap(uint32_t usec) { struct timespec ts; ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; nanosleep(&ts, NULL); } isc_result_t dns_test_loaddb(dns_db_t **db, dns_dbtype_t dbtype, const char *origin, const char *testfile) { isc_result_t result; dns_fixedname_t fixed; dns_name_t *name; name = dns_fixedname_initname(&fixed); result = dns_name_fromstring(name, origin, 0, NULL); if (result != ISC_R_SUCCESS) { return (result); } result = dns_db_create(mctx, "rbt", name, dbtype, dns_rdataclass_in, 0, NULL, db); if (result != ISC_R_SUCCESS) { return (result); } result = dns_db_load(*db, testfile, dns_masterformat_text, 0); return (result); } static int fromhex(char c) { if (c >= '0' && c <= '9') { return (c - '0'); } else if (c >= 'a' && c <= 'f') { return (c - 'a' + 10); } else if (c >= 'A' && c <= 'F') { return (c - 'A' + 10); } printf("bad input format: %02x\n", c); exit(3); } /* * Format contents of given memory region as a hex string, using the buffer * of length 'buflen' pointed to by 'buf'. 'buflen' must be at least three * times 'len'. Always returns 'buf'. */ char * dns_test_tohex(const unsigned char *data, size_t len, char *buf, size_t buflen) { isc_constregion_t source = { .base = data, .length = len }; isc_buffer_t target; isc_result_t result; memset(buf, 0, buflen); isc_buffer_init(&target, buf, buflen); result = isc_hex_totext((isc_region_t *)&source, 1, " ", &target); assert_int_equal(result, ISC_R_SUCCESS); return (buf); } isc_result_t dns_test_getdata(const char *file, unsigned char *buf, size_t bufsiz, size_t *sizep) { isc_result_t result; unsigned char *bp; char *rp, *wp; char s[BUFSIZ]; size_t len, i; FILE *f = NULL; int n; result = isc_stdio_open(file, "r", &f); if (result != ISC_R_SUCCESS) { return (result); } bp = buf; while (fgets(s, sizeof(s), f) != NULL) { rp = s; wp = s; len = 0; while (*rp != '\0') { if (*rp == '#') { break; } if (*rp != ' ' && *rp != '\t' && *rp != '\r' && *rp != '\n') { *wp++ = *rp; len++; } rp++; } if (len == 0U) { continue; } if (len % 2 != 0U) { result = ISC_R_UNEXPECTEDEND; break; } if (len > bufsiz * 2) { result = ISC_R_NOSPACE; break; } rp = s; for (i = 0; i < len; i += 2) { n = fromhex(*rp++); n *= 16; n += fromhex(*rp++); *bp++ = n; } } if (result == ISC_R_SUCCESS) { *sizep = bp - buf; } isc_stdio_close(f); return (result); } static void nullmsg(dns_rdatacallbacks_t *cb, const char *fmt, ...) { UNUSED(cb); UNUSED(fmt); } isc_result_t dns_test_rdatafromstring(dns_rdata_t *rdata, dns_rdataclass_t rdclass, dns_rdatatype_t rdtype, unsigned char *dst, size_t dstlen, const char *src, bool warnings) { dns_rdatacallbacks_t callbacks; isc_buffer_t source, target; isc_lex_t *lex = NULL; isc_lexspecials_t specials = { 0 }; isc_result_t result; size_t length; REQUIRE(rdata != NULL); REQUIRE(DNS_RDATA_INITIALIZED(rdata)); REQUIRE(dst != NULL); REQUIRE(src != NULL); /* * Set up source to hold the input string. */ length = strlen(src); isc_buffer_constinit(&source, src, length); isc_buffer_add(&source, length); /* * Create a lexer as one is required by dns_rdata_fromtext(). */ result = isc_lex_create(mctx, 64, &lex); if (result != ISC_R_SUCCESS) { return (result); } /* * Set characters which will be treated as valid multi-line RDATA * delimiters while reading the source string. These should match * specials from lib/dns/master.c. */ specials[0] = 1; specials['('] = 1; specials[')'] = 1; specials['"'] = 1; isc_lex_setspecials(lex, specials); /* * Expect DNS masterfile comments. */ isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); /* * Point lexer at source. */ result = isc_lex_openbuffer(lex, &source); if (result != ISC_R_SUCCESS) { goto destroy_lexer; } /* * Set up target for storing uncompressed wire form of provided RDATA. */ isc_buffer_init(&target, dst, dstlen); /* * Set up callbacks so warnings and errors are not printed. */ if (!warnings) { dns_rdatacallbacks_init(&callbacks); callbacks.warn = callbacks.error = nullmsg; } /* * Parse input string, determining result. */ result = dns_rdata_fromtext(rdata, rdclass, rdtype, lex, dns_rootname, 0, mctx, &target, &callbacks); destroy_lexer: isc_lex_destroy(&lex); return (result); } void dns_test_namefromstring(const char *namestr, dns_fixedname_t *fname) { size_t length; isc_buffer_t *b = NULL; isc_result_t result; dns_name_t *name; length = strlen(namestr); name = dns_fixedname_initname(fname); isc_buffer_allocate(mctx, &b, length); isc_buffer_putmem(b, (const unsigned char *)namestr, length); result = dns_name_fromtext(name, b, dns_rootname, 0, NULL); assert_int_equal(result, ISC_R_SUCCESS); isc_buffer_free(&b); } isc_result_t dns_test_difffromchanges(dns_diff_t *diff, const zonechange_t *changes, bool warnings) { isc_result_t result = ISC_R_SUCCESS; unsigned char rdata_buf[1024]; dns_difftuple_t *tuple = NULL; isc_consttextregion_t region; dns_rdatatype_t rdatatype; dns_fixedname_t fixedname; dns_rdata_t rdata; dns_name_t *name; size_t i; REQUIRE(diff != NULL); REQUIRE(changes != NULL); dns_diff_init(mctx, diff); for (i = 0; changes[i].owner != NULL; i++) { /* * Parse owner name. */ name = dns_fixedname_initname(&fixedname); result = dns_name_fromstring(name, changes[i].owner, 0, mctx); if (result != ISC_R_SUCCESS) { break; } /* * Parse RDATA type. */ region.base = changes[i].type; region.length = strlen(changes[i].type); result = dns_rdatatype_fromtext(&rdatatype, (isc_textregion_t *)®ion); if (result != ISC_R_SUCCESS) { break; } /* * Parse RDATA. */ dns_rdata_init(&rdata); result = dns_test_rdatafromstring( &rdata, dns_rdataclass_in, rdatatype, rdata_buf, sizeof(rdata_buf), changes[i].rdata, warnings); if (result != ISC_R_SUCCESS) { break; } /* * Create a diff tuple for the parsed change and append it to * the diff. */ result = dns_difftuple_create(mctx, changes[i].op, name, changes[i].ttl, &rdata, &tuple); if (result != ISC_R_SUCCESS) { break; } dns_diff_append(diff, &tuple); } if (result != ISC_R_SUCCESS) { dns_diff_clear(diff); } return (result); }