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