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