1 1.11 christos /* $NetBSD: check-tool.c,v 1.12 2026/01/29 18:36:26 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.8 christos * SPDX-License-Identifier: MPL-2.0 7 1.8 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.6 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos /*! \file */ 17 1.1 christos 18 1.5 christos #include <inttypes.h> 19 1.11 christos #include <netdb.h> 20 1.3 christos #include <stdbool.h> 21 1.1 christos #include <stdio.h> 22 1.1 christos 23 1.1 christos #include <isc/buffer.h> 24 1.1 christos #include <isc/log.h> 25 1.1 christos #include <isc/mem.h> 26 1.5 christos #include <isc/net.h> 27 1.1 christos #include <isc/region.h> 28 1.10 christos #include <isc/result.h> 29 1.1 christos #include <isc/stdio.h> 30 1.1 christos #include <isc/string.h> 31 1.1 christos #include <isc/symtab.h> 32 1.1 christos #include <isc/types.h> 33 1.1 christos #include <isc/util.h> 34 1.1 christos 35 1.1 christos #include <dns/db.h> 36 1.1 christos #include <dns/dbiterator.h> 37 1.1 christos #include <dns/fixedname.h> 38 1.1 christos #include <dns/log.h> 39 1.1 christos #include <dns/name.h> 40 1.1 christos #include <dns/rdata.h> 41 1.1 christos #include <dns/rdataclass.h> 42 1.1 christos #include <dns/rdataset.h> 43 1.1 christos #include <dns/rdatasetiter.h> 44 1.1 christos #include <dns/rdatatype.h> 45 1.1 christos #include <dns/types.h> 46 1.1 christos #include <dns/zone.h> 47 1.1 christos 48 1.1 christos #include <isccfg/log.h> 49 1.1 christos 50 1.1 christos #include <ns/log.h> 51 1.1 christos 52 1.5 christos #include "check-tool.h" 53 1.5 christos 54 1.1 christos #ifndef CHECK_SIBLING 55 1.1 christos #define CHECK_SIBLING 1 56 1.5 christos #endif /* ifndef CHECK_SIBLING */ 57 1.1 christos 58 1.1 christos #ifndef CHECK_LOCAL 59 1.1 christos #define CHECK_LOCAL 1 60 1.5 christos #endif /* ifndef CHECK_LOCAL */ 61 1.1 christos 62 1.5 christos #define ERR_IS_CNAME 1 63 1.5 christos #define ERR_NO_ADDRESSES 2 64 1.1 christos #define ERR_LOOKUP_FAILURE 3 65 1.5 christos #define ERR_EXTRA_A 4 66 1.5 christos #define ERR_EXTRA_AAAA 5 67 1.5 christos #define ERR_MISSING_GLUE 5 68 1.5 christos #define ERR_IS_MXCNAME 6 69 1.5 christos #define ERR_IS_SRVCNAME 7 70 1.1 christos 71 1.11 christos static const char *dbtype[] = { ZONEDB_DEFAULT }; 72 1.1 christos 73 1.1 christos int debug = 0; 74 1.1 christos const char *journal = NULL; 75 1.3 christos bool nomerge = true; 76 1.1 christos #if CHECK_LOCAL 77 1.3 christos bool docheckmx = true; 78 1.3 christos bool dochecksrv = true; 79 1.3 christos bool docheckns = true; 80 1.5 christos #else /* if CHECK_LOCAL */ 81 1.3 christos bool docheckmx = false; 82 1.3 christos bool dochecksrv = false; 83 1.3 christos bool docheckns = false; 84 1.5 christos #endif /* if CHECK_LOCAL */ 85 1.5 christos dns_zoneopt_t zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_CHECKMX | 86 1.11 christos DNS_ZONEOPT_CHECKDUPRR | DNS_ZONEOPT_CHECKSPF | 87 1.5 christos DNS_ZONEOPT_MANYERRORS | DNS_ZONEOPT_CHECKNAMES | 88 1.3 christos DNS_ZONEOPT_CHECKINTEGRITY | 89 1.1 christos #if CHECK_SIBLING 90 1.3 christos DNS_ZONEOPT_CHECKSIBLING | 91 1.5 christos #endif /* if CHECK_SIBLING */ 92 1.11 christos DNS_ZONEOPT_CHECKSVCB | DNS_ZONEOPT_CHECKWILDCARD | 93 1.5 christos DNS_ZONEOPT_WARNMXCNAME | DNS_ZONEOPT_WARNSRVCNAME; 94 1.1 christos 95 1.1 christos /* 96 1.1 christos * This needs to match the list in bin/named/log.c. 97 1.1 christos */ 98 1.5 christos static isc_logcategory_t categories[] = { { "", 0 }, 99 1.5 christos { "unmatched", 0 }, 100 1.5 christos { NULL, 0 } }; 101 1.1 christos 102 1.1 christos static isc_symtab_t *symtab = NULL; 103 1.1 christos static isc_mem_t *sym_mctx; 104 1.1 christos 105 1.1 christos static void 106 1.1 christos freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) { 107 1.1 christos UNUSED(type); 108 1.1 christos UNUSED(value); 109 1.1 christos isc_mem_free(userarg, key); 110 1.1 christos } 111 1.1 christos 112 1.1 christos static void 113 1.1 christos add(char *key, int value) { 114 1.1 christos isc_result_t result; 115 1.1 christos isc_symvalue_t symvalue; 116 1.1 christos 117 1.1 christos if (sym_mctx == NULL) { 118 1.5 christos isc_mem_create(&sym_mctx); 119 1.1 christos } 120 1.1 christos 121 1.1 christos if (symtab == NULL) { 122 1.1 christos result = isc_symtab_create(sym_mctx, 100, freekey, sym_mctx, 123 1.3 christos false, &symtab); 124 1.5 christos if (result != ISC_R_SUCCESS) { 125 1.1 christos return; 126 1.5 christos } 127 1.1 christos } 128 1.1 christos 129 1.1 christos key = isc_mem_strdup(sym_mctx, key); 130 1.1 christos 131 1.1 christos symvalue.as_pointer = NULL; 132 1.1 christos result = isc_symtab_define(symtab, key, value, symvalue, 133 1.1 christos isc_symexists_reject); 134 1.5 christos if (result != ISC_R_SUCCESS) { 135 1.1 christos isc_mem_free(sym_mctx, key); 136 1.5 christos } 137 1.1 christos } 138 1.1 christos 139 1.3 christos static bool 140 1.1 christos logged(char *key, int value) { 141 1.1 christos isc_result_t result; 142 1.1 christos 143 1.5 christos if (symtab == NULL) { 144 1.11 christos return false; 145 1.5 christos } 146 1.1 christos 147 1.1 christos result = isc_symtab_lookup(symtab, key, value, NULL); 148 1.5 christos if (result == ISC_R_SUCCESS) { 149 1.11 christos return true; 150 1.5 christos } 151 1.11 christos return false; 152 1.1 christos } 153 1.1 christos 154 1.3 christos static bool 155 1.1 christos checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner, 156 1.5 christos dns_rdataset_t *a, dns_rdataset_t *aaaa) { 157 1.1 christos dns_rdataset_t *rdataset; 158 1.1 christos dns_rdata_t rdata = DNS_RDATA_INIT; 159 1.1 christos struct addrinfo hints, *ai, *cur; 160 1.1 christos char namebuf[DNS_NAME_FORMATSIZE + 1]; 161 1.1 christos char ownerbuf[DNS_NAME_FORMATSIZE]; 162 1.1 christos char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")]; 163 1.3 christos bool answer = true; 164 1.3 christos bool match; 165 1.1 christos const char *type; 166 1.1 christos void *ptr = NULL; 167 1.1 christos int result; 168 1.1 christos 169 1.1 christos REQUIRE(a == NULL || !dns_rdataset_isassociated(a) || 170 1.1 christos a->type == dns_rdatatype_a); 171 1.1 christos REQUIRE(aaaa == NULL || !dns_rdataset_isassociated(aaaa) || 172 1.1 christos aaaa->type == dns_rdatatype_aaaa); 173 1.1 christos 174 1.5 christos if (a == NULL || aaaa == NULL) { 175 1.11 christos return answer; 176 1.5 christos } 177 1.1 christos 178 1.1 christos memset(&hints, 0, sizeof(hints)); 179 1.1 christos hints.ai_flags = AI_CANONNAME; 180 1.1 christos hints.ai_family = PF_UNSPEC; 181 1.1 christos hints.ai_socktype = SOCK_STREAM; 182 1.1 christos hints.ai_protocol = IPPROTO_TCP; 183 1.1 christos 184 1.1 christos dns_name_format(name, namebuf, sizeof(namebuf) - 1); 185 1.1 christos /* 186 1.1 christos * Turn off search. 187 1.1 christos */ 188 1.1 christos if (dns_name_countlabels(name) > 1U) { 189 1.1 christos strlcat(namebuf, ".", sizeof(namebuf)); 190 1.1 christos } 191 1.1 christos dns_name_format(owner, ownerbuf, sizeof(ownerbuf)); 192 1.1 christos 193 1.1 christos result = getaddrinfo(namebuf, NULL, &hints, &ai); 194 1.1 christos dns_name_format(name, namebuf, sizeof(namebuf) - 1); 195 1.1 christos switch (result) { 196 1.1 christos case 0: 197 1.1 christos /* 198 1.1 christos * Work around broken getaddrinfo() implementations that 199 1.1 christos * fail to set ai_canonname on first entry. 200 1.1 christos */ 201 1.1 christos cur = ai; 202 1.1 christos while (cur != NULL && cur->ai_canonname == NULL && 203 1.9 christos cur->ai_next != NULL) 204 1.9 christos { 205 1.1 christos cur = cur->ai_next; 206 1.5 christos } 207 1.1 christos if (cur != NULL && cur->ai_canonname != NULL && 208 1.1 christos strcasecmp(cur->ai_canonname, namebuf) != 0 && 209 1.5 christos !logged(namebuf, ERR_IS_CNAME)) 210 1.5 christos { 211 1.1 christos dns_zone_log(zone, ISC_LOG_ERROR, 212 1.1 christos "%s/NS '%s' (out of zone) " 213 1.1 christos "is a CNAME '%s' (illegal)", 214 1.5 christos ownerbuf, namebuf, cur->ai_canonname); 215 1.1 christos /* XXX950 make fatal for 9.5.0 */ 216 1.3 christos /* answer = false; */ 217 1.1 christos add(namebuf, ERR_IS_CNAME); 218 1.1 christos } 219 1.1 christos break; 220 1.1 christos case EAI_NONAME: 221 1.1 christos #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 222 1.1 christos case EAI_NODATA: 223 1.5 christos #endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */ 224 1.1 christos if (!logged(namebuf, ERR_NO_ADDRESSES)) { 225 1.1 christos dns_zone_log(zone, ISC_LOG_ERROR, 226 1.1 christos "%s/NS '%s' (out of zone) " 227 1.1 christos "has no addresses records (A or AAAA)", 228 1.1 christos ownerbuf, namebuf); 229 1.1 christos add(namebuf, ERR_NO_ADDRESSES); 230 1.1 christos } 231 1.1 christos /* XXX950 make fatal for 9.5.0 */ 232 1.11 christos return true; 233 1.1 christos 234 1.1 christos default: 235 1.1 christos if (!logged(namebuf, ERR_LOOKUP_FAILURE)) { 236 1.1 christos dns_zone_log(zone, ISC_LOG_WARNING, 237 1.5 christos "getaddrinfo(%s) failed: %s", namebuf, 238 1.5 christos gai_strerror(result)); 239 1.1 christos add(namebuf, ERR_LOOKUP_FAILURE); 240 1.1 christos } 241 1.11 christos return true; 242 1.1 christos } 243 1.1 christos 244 1.1 christos /* 245 1.1 christos * Check that all glue records really exist. 246 1.1 christos */ 247 1.5 christos if (!dns_rdataset_isassociated(a)) { 248 1.1 christos goto checkaaaa; 249 1.5 christos } 250 1.1 christos result = dns_rdataset_first(a); 251 1.1 christos while (result == ISC_R_SUCCESS) { 252 1.1 christos dns_rdataset_current(a, &rdata); 253 1.3 christos match = false; 254 1.1 christos for (cur = ai; cur != NULL; cur = cur->ai_next) { 255 1.5 christos if (cur->ai_family != AF_INET) { 256 1.1 christos continue; 257 1.5 christos } 258 1.1 christos ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr; 259 1.1 christos if (memcmp(ptr, rdata.data, rdata.length) == 0) { 260 1.3 christos match = true; 261 1.1 christos break; 262 1.1 christos } 263 1.1 christos } 264 1.1 christos if (!match && !logged(namebuf, ERR_EXTRA_A)) { 265 1.5 christos dns_zone_log(zone, ISC_LOG_ERROR, 266 1.5 christos "%s/NS '%s' " 267 1.1 christos "extra GLUE A record (%s)", 268 1.1 christos ownerbuf, namebuf, 269 1.5 christos inet_ntop(AF_INET, rdata.data, addrbuf, 270 1.5 christos sizeof(addrbuf))); 271 1.1 christos add(namebuf, ERR_EXTRA_A); 272 1.1 christos /* XXX950 make fatal for 9.5.0 */ 273 1.3 christos /* answer = false; */ 274 1.1 christos } 275 1.1 christos dns_rdata_reset(&rdata); 276 1.1 christos result = dns_rdataset_next(a); 277 1.1 christos } 278 1.1 christos 279 1.5 christos checkaaaa: 280 1.5 christos if (!dns_rdataset_isassociated(aaaa)) { 281 1.1 christos goto checkmissing; 282 1.5 christos } 283 1.1 christos result = dns_rdataset_first(aaaa); 284 1.1 christos while (result == ISC_R_SUCCESS) { 285 1.1 christos dns_rdataset_current(aaaa, &rdata); 286 1.3 christos match = false; 287 1.1 christos for (cur = ai; cur != NULL; cur = cur->ai_next) { 288 1.5 christos if (cur->ai_family != AF_INET6) { 289 1.1 christos continue; 290 1.5 christos } 291 1.5 christos ptr = &((struct sockaddr_in6 *)(cur->ai_addr)) 292 1.5 christos ->sin6_addr; 293 1.1 christos if (memcmp(ptr, rdata.data, rdata.length) == 0) { 294 1.3 christos match = true; 295 1.1 christos break; 296 1.1 christos } 297 1.1 christos } 298 1.1 christos if (!match && !logged(namebuf, ERR_EXTRA_AAAA)) { 299 1.5 christos dns_zone_log(zone, ISC_LOG_ERROR, 300 1.5 christos "%s/NS '%s' " 301 1.1 christos "extra GLUE AAAA record (%s)", 302 1.1 christos ownerbuf, namebuf, 303 1.5 christos inet_ntop(AF_INET6, rdata.data, addrbuf, 304 1.5 christos sizeof(addrbuf))); 305 1.1 christos add(namebuf, ERR_EXTRA_AAAA); 306 1.1 christos /* XXX950 make fatal for 9.5.0. */ 307 1.3 christos /* answer = false; */ 308 1.1 christos } 309 1.1 christos dns_rdata_reset(&rdata); 310 1.1 christos result = dns_rdataset_next(aaaa); 311 1.1 christos } 312 1.1 christos 313 1.5 christos checkmissing: 314 1.1 christos /* 315 1.1 christos * Check that all addresses appear in the glue. 316 1.1 christos */ 317 1.1 christos if (!logged(namebuf, ERR_MISSING_GLUE)) { 318 1.3 christos bool missing_glue = false; 319 1.1 christos for (cur = ai; cur != NULL; cur = cur->ai_next) { 320 1.1 christos switch (cur->ai_family) { 321 1.1 christos case AF_INET: 322 1.1 christos rdataset = a; 323 1.5 christos ptr = &((struct sockaddr_in *)(cur->ai_addr)) 324 1.5 christos ->sin_addr; 325 1.1 christos type = "A"; 326 1.1 christos break; 327 1.1 christos case AF_INET6: 328 1.1 christos rdataset = aaaa; 329 1.5 christos ptr = &((struct sockaddr_in6 *)(cur->ai_addr)) 330 1.5 christos ->sin6_addr; 331 1.1 christos type = "AAAA"; 332 1.1 christos break; 333 1.1 christos default: 334 1.5 christos continue; 335 1.1 christos } 336 1.3 christos match = false; 337 1.5 christos if (dns_rdataset_isassociated(rdataset)) { 338 1.1 christos result = dns_rdataset_first(rdataset); 339 1.5 christos } else { 340 1.1 christos result = ISC_R_FAILURE; 341 1.5 christos } 342 1.1 christos while (result == ISC_R_SUCCESS && !match) { 343 1.1 christos dns_rdataset_current(rdataset, &rdata); 344 1.1 christos if (memcmp(ptr, rdata.data, rdata.length) == 0) 345 1.5 christos { 346 1.3 christos match = true; 347 1.5 christos } 348 1.1 christos dns_rdata_reset(&rdata); 349 1.1 christos result = dns_rdataset_next(rdataset); 350 1.1 christos } 351 1.1 christos if (!match) { 352 1.5 christos dns_zone_log(zone, ISC_LOG_ERROR, 353 1.5 christos "%s/NS '%s' " 354 1.1 christos "missing GLUE %s record (%s)", 355 1.1 christos ownerbuf, namebuf, type, 356 1.1 christos inet_ntop(cur->ai_family, ptr, 357 1.5 christos addrbuf, 358 1.5 christos sizeof(addrbuf))); 359 1.1 christos /* XXX950 make fatal for 9.5.0. */ 360 1.3 christos /* answer = false; */ 361 1.3 christos missing_glue = true; 362 1.1 christos } 363 1.1 christos } 364 1.5 christos if (missing_glue) { 365 1.1 christos add(namebuf, ERR_MISSING_GLUE); 366 1.5 christos } 367 1.1 christos } 368 1.1 christos freeaddrinfo(ai); 369 1.11 christos return answer; 370 1.1 christos } 371 1.1 christos 372 1.3 christos static bool 373 1.1 christos checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) { 374 1.1 christos struct addrinfo hints, *ai, *cur; 375 1.1 christos char namebuf[DNS_NAME_FORMATSIZE + 1]; 376 1.1 christos char ownerbuf[DNS_NAME_FORMATSIZE]; 377 1.1 christos int result; 378 1.1 christos int level = ISC_LOG_ERROR; 379 1.3 christos bool answer = true; 380 1.1 christos 381 1.1 christos memset(&hints, 0, sizeof(hints)); 382 1.1 christos hints.ai_flags = AI_CANONNAME; 383 1.1 christos hints.ai_family = PF_UNSPEC; 384 1.1 christos hints.ai_socktype = SOCK_STREAM; 385 1.1 christos hints.ai_protocol = IPPROTO_TCP; 386 1.1 christos 387 1.1 christos dns_name_format(name, namebuf, sizeof(namebuf) - 1); 388 1.1 christos /* 389 1.1 christos * Turn off search. 390 1.1 christos */ 391 1.1 christos if (dns_name_countlabels(name) > 1U) { 392 1.1 christos strlcat(namebuf, ".", sizeof(namebuf)); 393 1.1 christos } 394 1.1 christos dns_name_format(owner, ownerbuf, sizeof(ownerbuf)); 395 1.1 christos 396 1.1 christos result = getaddrinfo(namebuf, NULL, &hints, &ai); 397 1.1 christos dns_name_format(name, namebuf, sizeof(namebuf) - 1); 398 1.1 christos switch (result) { 399 1.1 christos case 0: 400 1.1 christos /* 401 1.1 christos * Work around broken getaddrinfo() implementations that 402 1.1 christos * fail to set ai_canonname on first entry. 403 1.1 christos */ 404 1.1 christos cur = ai; 405 1.1 christos while (cur != NULL && cur->ai_canonname == NULL && 406 1.9 christos cur->ai_next != NULL) 407 1.9 christos { 408 1.1 christos cur = cur->ai_next; 409 1.5 christos } 410 1.1 christos if (cur != NULL && cur->ai_canonname != NULL && 411 1.5 christos strcasecmp(cur->ai_canonname, namebuf) != 0) 412 1.5 christos { 413 1.5 christos if ((zone_options & DNS_ZONEOPT_WARNMXCNAME) != 0) { 414 1.1 christos level = ISC_LOG_WARNING; 415 1.5 christos } 416 1.1 christos if ((zone_options & DNS_ZONEOPT_IGNOREMXCNAME) == 0) { 417 1.1 christos if (!logged(namebuf, ERR_IS_MXCNAME)) { 418 1.1 christos dns_zone_log(zone, level, 419 1.1 christos "%s/MX '%s' (out of zone)" 420 1.1 christos " is a CNAME '%s' " 421 1.1 christos "(illegal)", 422 1.1 christos ownerbuf, namebuf, 423 1.1 christos cur->ai_canonname); 424 1.1 christos add(namebuf, ERR_IS_MXCNAME); 425 1.1 christos } 426 1.5 christos if (level == ISC_LOG_ERROR) { 427 1.3 christos answer = false; 428 1.5 christos } 429 1.1 christos } 430 1.1 christos } 431 1.1 christos freeaddrinfo(ai); 432 1.11 christos return answer; 433 1.1 christos 434 1.1 christos case EAI_NONAME: 435 1.1 christos #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 436 1.1 christos case EAI_NODATA: 437 1.5 christos #endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */ 438 1.1 christos if (!logged(namebuf, ERR_NO_ADDRESSES)) { 439 1.1 christos dns_zone_log(zone, ISC_LOG_ERROR, 440 1.1 christos "%s/MX '%s' (out of zone) " 441 1.1 christos "has no addresses records (A or AAAA)", 442 1.1 christos ownerbuf, namebuf); 443 1.1 christos add(namebuf, ERR_NO_ADDRESSES); 444 1.1 christos } 445 1.1 christos /* XXX950 make fatal for 9.5.0. */ 446 1.11 christos return true; 447 1.1 christos 448 1.1 christos default: 449 1.1 christos if (!logged(namebuf, ERR_LOOKUP_FAILURE)) { 450 1.1 christos dns_zone_log(zone, ISC_LOG_WARNING, 451 1.5 christos "getaddrinfo(%s) failed: %s", namebuf, 452 1.5 christos gai_strerror(result)); 453 1.1 christos add(namebuf, ERR_LOOKUP_FAILURE); 454 1.1 christos } 455 1.11 christos return true; 456 1.1 christos } 457 1.1 christos } 458 1.1 christos 459 1.3 christos static bool 460 1.1 christos checksrv(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) { 461 1.1 christos struct addrinfo hints, *ai, *cur; 462 1.1 christos char namebuf[DNS_NAME_FORMATSIZE + 1]; 463 1.1 christos char ownerbuf[DNS_NAME_FORMATSIZE]; 464 1.1 christos int result; 465 1.1 christos int level = ISC_LOG_ERROR; 466 1.3 christos bool answer = true; 467 1.1 christos 468 1.1 christos memset(&hints, 0, sizeof(hints)); 469 1.1 christos hints.ai_flags = AI_CANONNAME; 470 1.1 christos hints.ai_family = PF_UNSPEC; 471 1.1 christos hints.ai_socktype = SOCK_STREAM; 472 1.1 christos hints.ai_protocol = IPPROTO_TCP; 473 1.1 christos 474 1.1 christos dns_name_format(name, namebuf, sizeof(namebuf) - 1); 475 1.1 christos /* 476 1.1 christos * Turn off search. 477 1.1 christos */ 478 1.1 christos if (dns_name_countlabels(name) > 1U) { 479 1.1 christos strlcat(namebuf, ".", sizeof(namebuf)); 480 1.1 christos } 481 1.1 christos dns_name_format(owner, ownerbuf, sizeof(ownerbuf)); 482 1.1 christos 483 1.1 christos result = getaddrinfo(namebuf, NULL, &hints, &ai); 484 1.1 christos dns_name_format(name, namebuf, sizeof(namebuf) - 1); 485 1.1 christos switch (result) { 486 1.1 christos case 0: 487 1.1 christos /* 488 1.1 christos * Work around broken getaddrinfo() implementations that 489 1.1 christos * fail to set ai_canonname on first entry. 490 1.1 christos */ 491 1.1 christos cur = ai; 492 1.1 christos while (cur != NULL && cur->ai_canonname == NULL && 493 1.9 christos cur->ai_next != NULL) 494 1.9 christos { 495 1.1 christos cur = cur->ai_next; 496 1.5 christos } 497 1.1 christos if (cur != NULL && cur->ai_canonname != NULL && 498 1.5 christos strcasecmp(cur->ai_canonname, namebuf) != 0) 499 1.5 christos { 500 1.5 christos if ((zone_options & DNS_ZONEOPT_WARNSRVCNAME) != 0) { 501 1.1 christos level = ISC_LOG_WARNING; 502 1.5 christos } 503 1.1 christos if ((zone_options & DNS_ZONEOPT_IGNORESRVCNAME) == 0) { 504 1.1 christos if (!logged(namebuf, ERR_IS_SRVCNAME)) { 505 1.5 christos dns_zone_log(zone, level, 506 1.5 christos "%s/SRV '%s'" 507 1.1 christos " (out of zone) is a " 508 1.1 christos "CNAME '%s' (illegal)", 509 1.1 christos ownerbuf, namebuf, 510 1.1 christos cur->ai_canonname); 511 1.1 christos add(namebuf, ERR_IS_SRVCNAME); 512 1.1 christos } 513 1.5 christos if (level == ISC_LOG_ERROR) { 514 1.3 christos answer = false; 515 1.5 christos } 516 1.1 christos } 517 1.1 christos } 518 1.1 christos freeaddrinfo(ai); 519 1.11 christos return answer; 520 1.1 christos 521 1.1 christos case EAI_NONAME: 522 1.1 christos #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 523 1.1 christos case EAI_NODATA: 524 1.5 christos #endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */ 525 1.1 christos if (!logged(namebuf, ERR_NO_ADDRESSES)) { 526 1.1 christos dns_zone_log(zone, ISC_LOG_ERROR, 527 1.1 christos "%s/SRV '%s' (out of zone) " 528 1.1 christos "has no addresses records (A or AAAA)", 529 1.1 christos ownerbuf, namebuf); 530 1.1 christos add(namebuf, ERR_NO_ADDRESSES); 531 1.1 christos } 532 1.1 christos /* XXX950 make fatal for 9.5.0. */ 533 1.11 christos return true; 534 1.1 christos 535 1.1 christos default: 536 1.1 christos if (!logged(namebuf, ERR_LOOKUP_FAILURE)) { 537 1.1 christos dns_zone_log(zone, ISC_LOG_WARNING, 538 1.5 christos "getaddrinfo(%s) failed: %s", namebuf, 539 1.5 christos gai_strerror(result)); 540 1.1 christos add(namebuf, ERR_LOOKUP_FAILURE); 541 1.1 christos } 542 1.11 christos return true; 543 1.1 christos } 544 1.1 christos } 545 1.1 christos 546 1.1 christos isc_result_t 547 1.1 christos setup_logging(isc_mem_t *mctx, FILE *errout, isc_log_t **logp) { 548 1.1 christos isc_logdestination_t destination; 549 1.1 christos isc_logconfig_t *logconfig = NULL; 550 1.1 christos isc_log_t *log = NULL; 551 1.1 christos 552 1.5 christos isc_log_create(mctx, &log, &logconfig); 553 1.1 christos isc_log_registercategories(log, categories); 554 1.1 christos isc_log_setcontext(log); 555 1.1 christos dns_log_init(log); 556 1.1 christos dns_log_setcontext(log); 557 1.1 christos cfg_log_init(log); 558 1.1 christos ns_log_init(log); 559 1.1 christos 560 1.1 christos destination.file.stream = errout; 561 1.1 christos destination.file.name = NULL; 562 1.1 christos destination.file.versions = ISC_LOG_ROLLNEVER; 563 1.1 christos destination.file.maximum_size = 0; 564 1.5 christos isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC, 565 1.5 christos ISC_LOG_DYNAMIC, &destination, 0); 566 1.5 christos 567 1.5 christos RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL) == 568 1.5 christos ISC_R_SUCCESS); 569 1.1 christos 570 1.1 christos *logp = log; 571 1.11 christos return ISC_R_SUCCESS; 572 1.1 christos } 573 1.1 christos 574 1.1 christos /*% load the zone */ 575 1.1 christos isc_result_t 576 1.1 christos load_zone(isc_mem_t *mctx, const char *zonename, const char *filename, 577 1.1 christos dns_masterformat_t fileformat, const char *classname, 578 1.5 christos dns_ttl_t maxttl, dns_zone_t **zonep) { 579 1.1 christos isc_result_t result; 580 1.1 christos dns_rdataclass_t rdclass; 581 1.1 christos isc_textregion_t region; 582 1.1 christos isc_buffer_t buffer; 583 1.1 christos dns_fixedname_t fixorigin; 584 1.1 christos dns_name_t *origin; 585 1.1 christos dns_zone_t *zone = NULL; 586 1.1 christos 587 1.1 christos REQUIRE(zonep == NULL || *zonep == NULL); 588 1.1 christos 589 1.5 christos if (debug) { 590 1.1 christos fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n", 591 1.1 christos zonename, filename, classname); 592 1.5 christos } 593 1.1 christos 594 1.11 christos dns_zone_create(&zone, mctx, 0); 595 1.1 christos 596 1.8 christos dns_zone_settype(zone, dns_zone_primary); 597 1.1 christos 598 1.1 christos isc_buffer_constinit(&buffer, zonename, strlen(zonename)); 599 1.1 christos isc_buffer_add(&buffer, strlen(zonename)); 600 1.1 christos origin = dns_fixedname_initname(&fixorigin); 601 1.1 christos CHECK(dns_name_fromtext(origin, &buffer, dns_rootname, 0, NULL)); 602 1.1 christos CHECK(dns_zone_setorigin(zone, origin)); 603 1.5 christos dns_zone_setdbtype(zone, 1, (const char *const *)dbtype); 604 1.10 christos if (strcmp(filename, "-") == 0) { 605 1.10 christos CHECK(dns_zone_setstream(zone, stdin, fileformat, 606 1.10 christos &dns_master_style_default)); 607 1.10 christos } else { 608 1.10 christos CHECK(dns_zone_setfile(zone, filename, fileformat, 609 1.10 christos &dns_master_style_default)); 610 1.10 christos } 611 1.5 christos if (journal != NULL) { 612 1.1 christos CHECK(dns_zone_setjournal(zone, journal)); 613 1.5 christos } 614 1.1 christos 615 1.11 christos region.base = UNCONST(classname); 616 1.1 christos region.length = strlen(classname); 617 1.1 christos CHECK(dns_rdataclass_fromtext(&rdclass, ®ion)); 618 1.1 christos 619 1.1 christos dns_zone_setclass(zone, rdclass); 620 1.3 christos dns_zone_setoption(zone, zone_options, true); 621 1.1 christos dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge); 622 1.1 christos 623 1.1 christos dns_zone_setmaxttl(zone, maxttl); 624 1.1 christos 625 1.5 christos if (docheckmx) { 626 1.1 christos dns_zone_setcheckmx(zone, checkmx); 627 1.5 christos } 628 1.5 christos if (docheckns) { 629 1.1 christos dns_zone_setcheckns(zone, checkns); 630 1.5 christos } 631 1.5 christos if (dochecksrv) { 632 1.1 christos dns_zone_setchecksrv(zone, checksrv); 633 1.5 christos } 634 1.1 christos 635 1.3 christos CHECK(dns_zone_load(zone, false)); 636 1.1 christos 637 1.1 christos if (zonep != NULL) { 638 1.1 christos *zonep = zone; 639 1.1 christos zone = NULL; 640 1.1 christos } 641 1.1 christos 642 1.5 christos cleanup: 643 1.5 christos if (zone != NULL) { 644 1.1 christos dns_zone_detach(&zone); 645 1.5 christos } 646 1.11 christos return result; 647 1.1 christos } 648 1.1 christos 649 1.1 christos /*% dump the zone */ 650 1.1 christos isc_result_t 651 1.1 christos dump_zone(const char *zonename, dns_zone_t *zone, const char *filename, 652 1.1 christos dns_masterformat_t fileformat, const dns_master_style_t *style, 653 1.5 christos const uint32_t rawversion) { 654 1.1 christos isc_result_t result; 655 1.1 christos FILE *output = stdout; 656 1.1 christos const char *flags; 657 1.1 christos 658 1.4 christos flags = (fileformat == dns_masterformat_text) ? "w" : "wb"; 659 1.1 christos 660 1.1 christos if (debug) { 661 1.5 christos if (filename != NULL && strcmp(filename, "-") != 0) { 662 1.5 christos fprintf(stderr, "dumping \"%s\" to \"%s\"\n", zonename, 663 1.5 christos filename); 664 1.5 christos } else { 665 1.1 christos fprintf(stderr, "dumping \"%s\"\n", zonename); 666 1.5 christos } 667 1.1 christos } 668 1.1 christos 669 1.1 christos if (filename != NULL && strcmp(filename, "-") != 0) { 670 1.1 christos result = isc_stdio_open(filename, flags, &output); 671 1.1 christos 672 1.1 christos if (result != ISC_R_SUCCESS) { 673 1.5 christos fprintf(stderr, 674 1.5 christos "could not open output " 675 1.5 christos "file \"%s\" for writing\n", 676 1.5 christos filename); 677 1.11 christos return ISC_R_FAILURE; 678 1.1 christos } 679 1.1 christos } 680 1.1 christos 681 1.3 christos result = dns_zone_dumptostream(zone, output, fileformat, style, 682 1.3 christos rawversion); 683 1.5 christos if (output != stdout) { 684 1.1 christos (void)isc_stdio_close(output); 685 1.5 christos } 686 1.1 christos 687 1.11 christos return result; 688 1.1 christos } 689