1 /*	$NetBSD: check-tool.c,v 1.9 2023/01/25 21:43:22 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 <stdbool.h>
20 #include <stdio.h>
21 
22 #ifdef _WIN32
23 #include <Winsock2.h>
24 #endif /* ifdef _WIN32 */
25 
26 #include <isc/buffer.h>
27 #include <isc/log.h>
28 #include <isc/mem.h>
29 #include <isc/net.h>
30 #include <isc/netdb.h>
31 #include <isc/print.h>
32 #include <isc/region.h>
33 #include <isc/stdio.h>
34 #include <isc/string.h>
35 #include <isc/symtab.h>
36 #include <isc/types.h>
37 #include <isc/util.h>
38 
39 #include <dns/db.h>
40 #include <dns/dbiterator.h>
41 #include <dns/fixedname.h>
42 #include <dns/log.h>
43 #include <dns/name.h>
44 #include <dns/rdata.h>
45 #include <dns/rdataclass.h>
46 #include <dns/rdataset.h>
47 #include <dns/rdatasetiter.h>
48 #include <dns/rdatatype.h>
49 #include <dns/result.h>
50 #include <dns/types.h>
51 #include <dns/zone.h>
52 
53 #include <isccfg/log.h>
54 
55 #include <ns/log.h>
56 
57 #include "check-tool.h"
58 
59 #ifndef CHECK_SIBLING
60 #define CHECK_SIBLING 1
61 #endif /* ifndef CHECK_SIBLING */
62 
63 #ifndef CHECK_LOCAL
64 #define CHECK_LOCAL 1
65 #endif /* ifndef CHECK_LOCAL */
66 
67 #define CHECK(r)                             \
68 	do {                                 \
69 		result = (r);                \
70 		if (result != ISC_R_SUCCESS) \
71 			goto cleanup;        \
72 	} while (0)
73 
74 #define ERR_IS_CNAME	   1
75 #define ERR_NO_ADDRESSES   2
76 #define ERR_LOOKUP_FAILURE 3
77 #define ERR_EXTRA_A	   4
78 #define ERR_EXTRA_AAAA	   5
79 #define ERR_MISSING_GLUE   5
80 #define ERR_IS_MXCNAME	   6
81 #define ERR_IS_SRVCNAME	   7
82 
83 static const char *dbtype[] = { "rbt" };
84 
85 int debug = 0;
86 const char *journal = NULL;
87 bool nomerge = true;
88 #if CHECK_LOCAL
89 bool docheckmx = true;
90 bool dochecksrv = true;
91 bool docheckns = true;
92 #else  /* if CHECK_LOCAL */
93 bool docheckmx = false;
94 bool dochecksrv = false;
95 bool docheckns = false;
96 #endif /* if CHECK_LOCAL */
97 dns_zoneopt_t zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_CHECKMX |
98 			     DNS_ZONEOPT_MANYERRORS | DNS_ZONEOPT_CHECKNAMES |
99 			     DNS_ZONEOPT_CHECKINTEGRITY |
100 #if CHECK_SIBLING
101 			     DNS_ZONEOPT_CHECKSIBLING |
102 #endif /* if CHECK_SIBLING */
103 			     DNS_ZONEOPT_CHECKWILDCARD |
104 			     DNS_ZONEOPT_WARNMXCNAME | DNS_ZONEOPT_WARNSRVCNAME;
105 
106 /*
107  * This needs to match the list in bin/named/log.c.
108  */
109 static isc_logcategory_t categories[] = { { "", 0 },
110 					  { "unmatched", 0 },
111 					  { NULL, 0 } };
112 
113 static isc_symtab_t *symtab = NULL;
114 static isc_mem_t *sym_mctx;
115 
116 static void
freekey(char * key,unsigned int type,isc_symvalue_t value,void * userarg)117 freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
118 	UNUSED(type);
119 	UNUSED(value);
120 	isc_mem_free(userarg, key);
121 }
122 
123 static void
add(char * key,int value)124 add(char *key, int value) {
125 	isc_result_t result;
126 	isc_symvalue_t symvalue;
127 
128 	if (sym_mctx == NULL) {
129 		isc_mem_create(&sym_mctx);
130 	}
131 
132 	if (symtab == NULL) {
133 		result = isc_symtab_create(sym_mctx, 100, freekey, sym_mctx,
134 					   false, &symtab);
135 		if (result != ISC_R_SUCCESS) {
136 			return;
137 		}
138 	}
139 
140 	key = isc_mem_strdup(sym_mctx, key);
141 
142 	symvalue.as_pointer = NULL;
143 	result = isc_symtab_define(symtab, key, value, symvalue,
144 				   isc_symexists_reject);
145 	if (result != ISC_R_SUCCESS) {
146 		isc_mem_free(sym_mctx, key);
147 	}
148 }
149 
150 static bool
logged(char * key,int value)151 logged(char *key, int value) {
152 	isc_result_t result;
153 
154 	if (symtab == NULL) {
155 		return (false);
156 	}
157 
158 	result = isc_symtab_lookup(symtab, key, value, NULL);
159 	if (result == ISC_R_SUCCESS) {
160 		return (true);
161 	}
162 	return (false);
163 }
164 
165 static bool
checkns(dns_zone_t * zone,const dns_name_t * name,const dns_name_t * owner,dns_rdataset_t * a,dns_rdataset_t * aaaa)166 checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner,
167 	dns_rdataset_t *a, dns_rdataset_t *aaaa) {
168 	dns_rdataset_t *rdataset;
169 	dns_rdata_t rdata = DNS_RDATA_INIT;
170 	struct addrinfo hints, *ai, *cur;
171 	char namebuf[DNS_NAME_FORMATSIZE + 1];
172 	char ownerbuf[DNS_NAME_FORMATSIZE];
173 	char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
174 	bool answer = true;
175 	bool match;
176 	const char *type;
177 	void *ptr = NULL;
178 	int result;
179 
180 	REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
181 		a->type == dns_rdatatype_a);
182 	REQUIRE(aaaa == NULL || !dns_rdataset_isassociated(aaaa) ||
183 		aaaa->type == dns_rdatatype_aaaa);
184 
185 	if (a == NULL || aaaa == NULL) {
186 		return (answer);
187 	}
188 
189 	memset(&hints, 0, sizeof(hints));
190 	hints.ai_flags = AI_CANONNAME;
191 	hints.ai_family = PF_UNSPEC;
192 	hints.ai_socktype = SOCK_STREAM;
193 	hints.ai_protocol = IPPROTO_TCP;
194 
195 	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
196 	/*
197 	 * Turn off search.
198 	 */
199 	if (dns_name_countlabels(name) > 1U) {
200 		strlcat(namebuf, ".", sizeof(namebuf));
201 	}
202 	dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
203 
204 	result = getaddrinfo(namebuf, NULL, &hints, &ai);
205 	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
206 	switch (result) {
207 	case 0:
208 		/*
209 		 * Work around broken getaddrinfo() implementations that
210 		 * fail to set ai_canonname on first entry.
211 		 */
212 		cur = ai;
213 		while (cur != NULL && cur->ai_canonname == NULL &&
214 		       cur->ai_next != NULL)
215 		{
216 			cur = cur->ai_next;
217 		}
218 		if (cur != NULL && cur->ai_canonname != NULL &&
219 		    strcasecmp(cur->ai_canonname, namebuf) != 0 &&
220 		    !logged(namebuf, ERR_IS_CNAME))
221 		{
222 			dns_zone_log(zone, ISC_LOG_ERROR,
223 				     "%s/NS '%s' (out of zone) "
224 				     "is a CNAME '%s' (illegal)",
225 				     ownerbuf, namebuf, cur->ai_canonname);
226 			/* XXX950 make fatal for 9.5.0 */
227 			/* answer = false; */
228 			add(namebuf, ERR_IS_CNAME);
229 		}
230 		break;
231 	case EAI_NONAME:
232 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
233 	case EAI_NODATA:
234 #endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */
235 		if (!logged(namebuf, ERR_NO_ADDRESSES)) {
236 			dns_zone_log(zone, ISC_LOG_ERROR,
237 				     "%s/NS '%s' (out of zone) "
238 				     "has no addresses records (A or AAAA)",
239 				     ownerbuf, namebuf);
240 			add(namebuf, ERR_NO_ADDRESSES);
241 		}
242 		/* XXX950 make fatal for 9.5.0 */
243 		return (true);
244 
245 	default:
246 		if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
247 			dns_zone_log(zone, ISC_LOG_WARNING,
248 				     "getaddrinfo(%s) failed: %s", namebuf,
249 				     gai_strerror(result));
250 			add(namebuf, ERR_LOOKUP_FAILURE);
251 		}
252 		return (true);
253 	}
254 
255 	/*
256 	 * Check that all glue records really exist.
257 	 */
258 	if (!dns_rdataset_isassociated(a)) {
259 		goto checkaaaa;
260 	}
261 	result = dns_rdataset_first(a);
262 	while (result == ISC_R_SUCCESS) {
263 		dns_rdataset_current(a, &rdata);
264 		match = false;
265 		for (cur = ai; cur != NULL; cur = cur->ai_next) {
266 			if (cur->ai_family != AF_INET) {
267 				continue;
268 			}
269 			ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
270 			if (memcmp(ptr, rdata.data, rdata.length) == 0) {
271 				match = true;
272 				break;
273 			}
274 		}
275 		if (!match && !logged(namebuf, ERR_EXTRA_A)) {
276 			dns_zone_log(zone, ISC_LOG_ERROR,
277 				     "%s/NS '%s' "
278 				     "extra GLUE A record (%s)",
279 				     ownerbuf, namebuf,
280 				     inet_ntop(AF_INET, rdata.data, addrbuf,
281 					       sizeof(addrbuf)));
282 			add(namebuf, ERR_EXTRA_A);
283 			/* XXX950 make fatal for 9.5.0 */
284 			/* answer = false; */
285 		}
286 		dns_rdata_reset(&rdata);
287 		result = dns_rdataset_next(a);
288 	}
289 
290 checkaaaa:
291 	if (!dns_rdataset_isassociated(aaaa)) {
292 		goto checkmissing;
293 	}
294 	result = dns_rdataset_first(aaaa);
295 	while (result == ISC_R_SUCCESS) {
296 		dns_rdataset_current(aaaa, &rdata);
297 		match = false;
298 		for (cur = ai; cur != NULL; cur = cur->ai_next) {
299 			if (cur->ai_family != AF_INET6) {
300 				continue;
301 			}
302 			ptr = &((struct sockaddr_in6 *)(cur->ai_addr))
303 				       ->sin6_addr;
304 			if (memcmp(ptr, rdata.data, rdata.length) == 0) {
305 				match = true;
306 				break;
307 			}
308 		}
309 		if (!match && !logged(namebuf, ERR_EXTRA_AAAA)) {
310 			dns_zone_log(zone, ISC_LOG_ERROR,
311 				     "%s/NS '%s' "
312 				     "extra GLUE AAAA record (%s)",
313 				     ownerbuf, namebuf,
314 				     inet_ntop(AF_INET6, rdata.data, addrbuf,
315 					       sizeof(addrbuf)));
316 			add(namebuf, ERR_EXTRA_AAAA);
317 			/* XXX950 make fatal for 9.5.0. */
318 			/* answer = false; */
319 		}
320 		dns_rdata_reset(&rdata);
321 		result = dns_rdataset_next(aaaa);
322 	}
323 
324 checkmissing:
325 	/*
326 	 * Check that all addresses appear in the glue.
327 	 */
328 	if (!logged(namebuf, ERR_MISSING_GLUE)) {
329 		bool missing_glue = false;
330 		for (cur = ai; cur != NULL; cur = cur->ai_next) {
331 			switch (cur->ai_family) {
332 			case AF_INET:
333 				rdataset = a;
334 				ptr = &((struct sockaddr_in *)(cur->ai_addr))
335 					       ->sin_addr;
336 				type = "A";
337 				break;
338 			case AF_INET6:
339 				rdataset = aaaa;
340 				ptr = &((struct sockaddr_in6 *)(cur->ai_addr))
341 					       ->sin6_addr;
342 				type = "AAAA";
343 				break;
344 			default:
345 				continue;
346 			}
347 			match = false;
348 			if (dns_rdataset_isassociated(rdataset)) {
349 				result = dns_rdataset_first(rdataset);
350 			} else {
351 				result = ISC_R_FAILURE;
352 			}
353 			while (result == ISC_R_SUCCESS && !match) {
354 				dns_rdataset_current(rdataset, &rdata);
355 				if (memcmp(ptr, rdata.data, rdata.length) == 0)
356 				{
357 					match = true;
358 				}
359 				dns_rdata_reset(&rdata);
360 				result = dns_rdataset_next(rdataset);
361 			}
362 			if (!match) {
363 				dns_zone_log(zone, ISC_LOG_ERROR,
364 					     "%s/NS '%s' "
365 					     "missing GLUE %s record (%s)",
366 					     ownerbuf, namebuf, type,
367 					     inet_ntop(cur->ai_family, ptr,
368 						       addrbuf,
369 						       sizeof(addrbuf)));
370 				/* XXX950 make fatal for 9.5.0. */
371 				/* answer = false; */
372 				missing_glue = true;
373 			}
374 		}
375 		if (missing_glue) {
376 			add(namebuf, ERR_MISSING_GLUE);
377 		}
378 	}
379 	freeaddrinfo(ai);
380 	return (answer);
381 }
382 
383 static bool
checkmx(dns_zone_t * zone,const dns_name_t * name,const dns_name_t * owner)384 checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
385 	struct addrinfo hints, *ai, *cur;
386 	char namebuf[DNS_NAME_FORMATSIZE + 1];
387 	char ownerbuf[DNS_NAME_FORMATSIZE];
388 	int result;
389 	int level = ISC_LOG_ERROR;
390 	bool answer = true;
391 
392 	memset(&hints, 0, sizeof(hints));
393 	hints.ai_flags = AI_CANONNAME;
394 	hints.ai_family = PF_UNSPEC;
395 	hints.ai_socktype = SOCK_STREAM;
396 	hints.ai_protocol = IPPROTO_TCP;
397 
398 	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
399 	/*
400 	 * Turn off search.
401 	 */
402 	if (dns_name_countlabels(name) > 1U) {
403 		strlcat(namebuf, ".", sizeof(namebuf));
404 	}
405 	dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
406 
407 	result = getaddrinfo(namebuf, NULL, &hints, &ai);
408 	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
409 	switch (result) {
410 	case 0:
411 		/*
412 		 * Work around broken getaddrinfo() implementations that
413 		 * fail to set ai_canonname on first entry.
414 		 */
415 		cur = ai;
416 		while (cur != NULL && cur->ai_canonname == NULL &&
417 		       cur->ai_next != NULL)
418 		{
419 			cur = cur->ai_next;
420 		}
421 		if (cur != NULL && cur->ai_canonname != NULL &&
422 		    strcasecmp(cur->ai_canonname, namebuf) != 0)
423 		{
424 			if ((zone_options & DNS_ZONEOPT_WARNMXCNAME) != 0) {
425 				level = ISC_LOG_WARNING;
426 			}
427 			if ((zone_options & DNS_ZONEOPT_IGNOREMXCNAME) == 0) {
428 				if (!logged(namebuf, ERR_IS_MXCNAME)) {
429 					dns_zone_log(zone, level,
430 						     "%s/MX '%s' (out of zone)"
431 						     " is a CNAME '%s' "
432 						     "(illegal)",
433 						     ownerbuf, namebuf,
434 						     cur->ai_canonname);
435 					add(namebuf, ERR_IS_MXCNAME);
436 				}
437 				if (level == ISC_LOG_ERROR) {
438 					answer = false;
439 				}
440 			}
441 		}
442 		freeaddrinfo(ai);
443 		return (answer);
444 
445 	case EAI_NONAME:
446 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
447 	case EAI_NODATA:
448 #endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */
449 		if (!logged(namebuf, ERR_NO_ADDRESSES)) {
450 			dns_zone_log(zone, ISC_LOG_ERROR,
451 				     "%s/MX '%s' (out of zone) "
452 				     "has no addresses records (A or AAAA)",
453 				     ownerbuf, namebuf);
454 			add(namebuf, ERR_NO_ADDRESSES);
455 		}
456 		/* XXX950 make fatal for 9.5.0. */
457 		return (true);
458 
459 	default:
460 		if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
461 			dns_zone_log(zone, ISC_LOG_WARNING,
462 				     "getaddrinfo(%s) failed: %s", namebuf,
463 				     gai_strerror(result));
464 			add(namebuf, ERR_LOOKUP_FAILURE);
465 		}
466 		return (true);
467 	}
468 }
469 
470 static bool
checksrv(dns_zone_t * zone,const dns_name_t * name,const dns_name_t * owner)471 checksrv(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
472 	struct addrinfo hints, *ai, *cur;
473 	char namebuf[DNS_NAME_FORMATSIZE + 1];
474 	char ownerbuf[DNS_NAME_FORMATSIZE];
475 	int result;
476 	int level = ISC_LOG_ERROR;
477 	bool answer = true;
478 
479 	memset(&hints, 0, sizeof(hints));
480 	hints.ai_flags = AI_CANONNAME;
481 	hints.ai_family = PF_UNSPEC;
482 	hints.ai_socktype = SOCK_STREAM;
483 	hints.ai_protocol = IPPROTO_TCP;
484 
485 	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
486 	/*
487 	 * Turn off search.
488 	 */
489 	if (dns_name_countlabels(name) > 1U) {
490 		strlcat(namebuf, ".", sizeof(namebuf));
491 	}
492 	dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
493 
494 	result = getaddrinfo(namebuf, NULL, &hints, &ai);
495 	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
496 	switch (result) {
497 	case 0:
498 		/*
499 		 * Work around broken getaddrinfo() implementations that
500 		 * fail to set ai_canonname on first entry.
501 		 */
502 		cur = ai;
503 		while (cur != NULL && cur->ai_canonname == NULL &&
504 		       cur->ai_next != NULL)
505 		{
506 			cur = cur->ai_next;
507 		}
508 		if (cur != NULL && cur->ai_canonname != NULL &&
509 		    strcasecmp(cur->ai_canonname, namebuf) != 0)
510 		{
511 			if ((zone_options & DNS_ZONEOPT_WARNSRVCNAME) != 0) {
512 				level = ISC_LOG_WARNING;
513 			}
514 			if ((zone_options & DNS_ZONEOPT_IGNORESRVCNAME) == 0) {
515 				if (!logged(namebuf, ERR_IS_SRVCNAME)) {
516 					dns_zone_log(zone, level,
517 						     "%s/SRV '%s'"
518 						     " (out of zone) is a "
519 						     "CNAME '%s' (illegal)",
520 						     ownerbuf, namebuf,
521 						     cur->ai_canonname);
522 					add(namebuf, ERR_IS_SRVCNAME);
523 				}
524 				if (level == ISC_LOG_ERROR) {
525 					answer = false;
526 				}
527 			}
528 		}
529 		freeaddrinfo(ai);
530 		return (answer);
531 
532 	case EAI_NONAME:
533 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
534 	case EAI_NODATA:
535 #endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */
536 		if (!logged(namebuf, ERR_NO_ADDRESSES)) {
537 			dns_zone_log(zone, ISC_LOG_ERROR,
538 				     "%s/SRV '%s' (out of zone) "
539 				     "has no addresses records (A or AAAA)",
540 				     ownerbuf, namebuf);
541 			add(namebuf, ERR_NO_ADDRESSES);
542 		}
543 		/* XXX950 make fatal for 9.5.0. */
544 		return (true);
545 
546 	default:
547 		if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
548 			dns_zone_log(zone, ISC_LOG_WARNING,
549 				     "getaddrinfo(%s) failed: %s", namebuf,
550 				     gai_strerror(result));
551 			add(namebuf, ERR_LOOKUP_FAILURE);
552 		}
553 		return (true);
554 	}
555 }
556 
557 isc_result_t
setup_logging(isc_mem_t * mctx,FILE * errout,isc_log_t ** logp)558 setup_logging(isc_mem_t *mctx, FILE *errout, isc_log_t **logp) {
559 	isc_logdestination_t destination;
560 	isc_logconfig_t *logconfig = NULL;
561 	isc_log_t *log = NULL;
562 
563 	isc_log_create(mctx, &log, &logconfig);
564 	isc_log_registercategories(log, categories);
565 	isc_log_setcontext(log);
566 	dns_log_init(log);
567 	dns_log_setcontext(log);
568 	cfg_log_init(log);
569 	ns_log_init(log);
570 
571 	destination.file.stream = errout;
572 	destination.file.name = NULL;
573 	destination.file.versions = ISC_LOG_ROLLNEVER;
574 	destination.file.maximum_size = 0;
575 	isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC,
576 			      ISC_LOG_DYNAMIC, &destination, 0);
577 
578 	RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL) ==
579 		      ISC_R_SUCCESS);
580 
581 	*logp = log;
582 	return (ISC_R_SUCCESS);
583 }
584 
585 /*% scan the zone for oversize TTLs */
586 static isc_result_t
check_ttls(dns_zone_t * zone,dns_ttl_t maxttl)587 check_ttls(dns_zone_t *zone, dns_ttl_t maxttl) {
588 	isc_result_t result;
589 	dns_db_t *db = NULL;
590 	dns_dbversion_t *version = NULL;
591 	dns_dbnode_t *node = NULL;
592 	dns_dbiterator_t *dbiter = NULL;
593 	dns_rdatasetiter_t *rdsiter = NULL;
594 	dns_rdataset_t rdataset;
595 	dns_fixedname_t fname;
596 	dns_name_t *name;
597 	name = dns_fixedname_initname(&fname);
598 	dns_rdataset_init(&rdataset);
599 
600 	CHECK(dns_zone_getdb(zone, &db));
601 	INSIST(db != NULL);
602 
603 	CHECK(dns_db_newversion(db, &version));
604 	CHECK(dns_db_createiterator(db, 0, &dbiter));
605 
606 	for (result = dns_dbiterator_first(dbiter); result == ISC_R_SUCCESS;
607 	     result = dns_dbiterator_next(dbiter))
608 	{
609 		result = dns_dbiterator_current(dbiter, &node, name);
610 		if (result == DNS_R_NEWORIGIN) {
611 			result = ISC_R_SUCCESS;
612 		}
613 		CHECK(result);
614 
615 		CHECK(dns_db_allrdatasets(db, node, version, 0, 0, &rdsiter));
616 		for (result = dns_rdatasetiter_first(rdsiter);
617 		     result == ISC_R_SUCCESS;
618 		     result = dns_rdatasetiter_next(rdsiter))
619 		{
620 			dns_rdatasetiter_current(rdsiter, &rdataset);
621 			if (rdataset.ttl > maxttl) {
622 				char nbuf[DNS_NAME_FORMATSIZE];
623 				char tbuf[255];
624 				isc_buffer_t b;
625 				isc_region_t r;
626 
627 				dns_name_format(name, nbuf, sizeof(nbuf));
628 				isc_buffer_init(&b, tbuf, sizeof(tbuf) - 1);
629 				CHECK(dns_rdatatype_totext(rdataset.type, &b));
630 				isc_buffer_usedregion(&b, &r);
631 				r.base[r.length] = 0;
632 
633 				dns_zone_log(zone, ISC_LOG_ERROR,
634 					     "%s/%s TTL %d exceeds "
635 					     "maximum TTL %d",
636 					     nbuf, tbuf, rdataset.ttl, maxttl);
637 				dns_rdataset_disassociate(&rdataset);
638 				CHECK(ISC_R_RANGE);
639 			}
640 			dns_rdataset_disassociate(&rdataset);
641 		}
642 		if (result == ISC_R_NOMORE) {
643 			result = ISC_R_SUCCESS;
644 		}
645 		CHECK(result);
646 
647 		dns_rdatasetiter_destroy(&rdsiter);
648 		dns_db_detachnode(db, &node);
649 	}
650 
651 	if (result == ISC_R_NOMORE) {
652 		result = ISC_R_SUCCESS;
653 	}
654 
655 cleanup:
656 	if (node != NULL) {
657 		dns_db_detachnode(db, &node);
658 	}
659 	if (rdsiter != NULL) {
660 		dns_rdatasetiter_destroy(&rdsiter);
661 	}
662 	if (dbiter != NULL) {
663 		dns_dbiterator_destroy(&dbiter);
664 	}
665 	if (version != NULL) {
666 		dns_db_closeversion(db, &version, false);
667 	}
668 	if (db != NULL) {
669 		dns_db_detach(&db);
670 	}
671 
672 	return (result);
673 }
674 
675 /*% load the zone */
676 isc_result_t
load_zone(isc_mem_t * mctx,const char * zonename,const char * filename,dns_masterformat_t fileformat,const char * classname,dns_ttl_t maxttl,dns_zone_t ** zonep)677 load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
678 	  dns_masterformat_t fileformat, const char *classname,
679 	  dns_ttl_t maxttl, dns_zone_t **zonep) {
680 	isc_result_t result;
681 	dns_rdataclass_t rdclass;
682 	isc_textregion_t region;
683 	isc_buffer_t buffer;
684 	dns_fixedname_t fixorigin;
685 	dns_name_t *origin;
686 	dns_zone_t *zone = NULL;
687 
688 	REQUIRE(zonep == NULL || *zonep == NULL);
689 
690 	if (debug) {
691 		fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
692 			zonename, filename, classname);
693 	}
694 
695 	CHECK(dns_zone_create(&zone, mctx));
696 
697 	dns_zone_settype(zone, dns_zone_primary);
698 
699 	isc_buffer_constinit(&buffer, zonename, strlen(zonename));
700 	isc_buffer_add(&buffer, strlen(zonename));
701 	origin = dns_fixedname_initname(&fixorigin);
702 	CHECK(dns_name_fromtext(origin, &buffer, dns_rootname, 0, NULL));
703 	CHECK(dns_zone_setorigin(zone, origin));
704 	dns_zone_setdbtype(zone, 1, (const char *const *)dbtype);
705 	CHECK(dns_zone_setfile(zone, filename, fileformat,
706 			       &dns_master_style_default));
707 	if (journal != NULL) {
708 		CHECK(dns_zone_setjournal(zone, journal));
709 	}
710 
711 	DE_CONST(classname, region.base);
712 	region.length = strlen(classname);
713 	CHECK(dns_rdataclass_fromtext(&rdclass, &region));
714 
715 	dns_zone_setclass(zone, rdclass);
716 	dns_zone_setoption(zone, zone_options, true);
717 	dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
718 
719 	dns_zone_setmaxttl(zone, maxttl);
720 
721 	if (docheckmx) {
722 		dns_zone_setcheckmx(zone, checkmx);
723 	}
724 	if (docheckns) {
725 		dns_zone_setcheckns(zone, checkns);
726 	}
727 	if (dochecksrv) {
728 		dns_zone_setchecksrv(zone, checksrv);
729 	}
730 
731 	CHECK(dns_zone_load(zone, false));
732 
733 	/*
734 	 * When loading map files we can't catch oversize TTLs during
735 	 * load, so we check for them here.
736 	 */
737 	if (fileformat == dns_masterformat_map && maxttl != 0) {
738 		CHECK(check_ttls(zone, maxttl));
739 	}
740 
741 	if (zonep != NULL) {
742 		*zonep = zone;
743 		zone = NULL;
744 	}
745 
746 cleanup:
747 	if (zone != NULL) {
748 		dns_zone_detach(&zone);
749 	}
750 	return (result);
751 }
752 
753 /*% dump the zone */
754 isc_result_t
dump_zone(const char * zonename,dns_zone_t * zone,const char * filename,dns_masterformat_t fileformat,const dns_master_style_t * style,const uint32_t rawversion)755 dump_zone(const char *zonename, dns_zone_t *zone, const char *filename,
756 	  dns_masterformat_t fileformat, const dns_master_style_t *style,
757 	  const uint32_t rawversion) {
758 	isc_result_t result;
759 	FILE *output = stdout;
760 	const char *flags;
761 
762 	flags = (fileformat == dns_masterformat_text) ? "w" : "wb";
763 
764 	if (debug) {
765 		if (filename != NULL && strcmp(filename, "-") != 0) {
766 			fprintf(stderr, "dumping \"%s\" to \"%s\"\n", zonename,
767 				filename);
768 		} else {
769 			fprintf(stderr, "dumping \"%s\"\n", zonename);
770 		}
771 	}
772 
773 	if (filename != NULL && strcmp(filename, "-") != 0) {
774 		result = isc_stdio_open(filename, flags, &output);
775 
776 		if (result != ISC_R_SUCCESS) {
777 			fprintf(stderr,
778 				"could not open output "
779 				"file \"%s\" for writing\n",
780 				filename);
781 			return (ISC_R_FAILURE);
782 		}
783 	}
784 
785 	result = dns_zone_dumptostream(zone, output, fileformat, style,
786 				       rawversion);
787 	if (output != stdout) {
788 		(void)isc_stdio_close(output);
789 	}
790 
791 	return (result);
792 }
793 
794 #ifdef _WIN32
795 void
InitSockets(void)796 InitSockets(void) {
797 	WORD wVersionRequested;
798 	WSADATA wsaData;
799 	int err;
800 
801 	wVersionRequested = MAKEWORD(2, 0);
802 
803 	err = WSAStartup(wVersionRequested, &wsaData);
804 	if (err != 0) {
805 		fprintf(stderr, "WSAStartup() failed: %d\n", err);
806 		exit(1);
807 	}
808 }
809 
810 void
DestroySockets(void)811 DestroySockets(void) {
812 	WSACleanup();
813 }
814 #endif /* ifdef _WIN32 */
815