check-tool.c revision 1.5
1/*	$NetBSD: check-tool.c,v 1.5 2020/05/24 19:46:10 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/*! \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 (/*CONSTCOND*/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
81static const char *dbtype[] = { "rbt" };
82
83int debug = 0;
84const char *journal = NULL;
85bool nomerge = true;
86#if CHECK_LOCAL
87bool docheckmx = true;
88bool dochecksrv = true;
89bool docheckns = true;
90#else  /* if CHECK_LOCAL */
91bool docheckmx = false;
92bool dochecksrv = false;
93bool docheckns = false;
94#endif /* if CHECK_LOCAL */
95dns_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 */
107static isc_logcategory_t categories[] = { { "", 0 },
108					  { "unmatched", 0 },
109					  { NULL, 0 } };
110
111static isc_symtab_t *symtab = NULL;
112static isc_mem_t *sym_mctx;
113
114static void
115freekey(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
121static void
122add(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
148static bool
149logged(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
163static bool
164checkns(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
287checkaaaa:
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
321checkmissing:
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
380static bool
381checkmx(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
466static bool
467checksrv(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
552isc_result_t
553setup_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 */
581static isc_result_t
582check_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
650cleanup:
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 */
671isc_result_t
672load_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_master);
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
741cleanup:
742	if (zone != NULL) {
743		dns_zone_detach(&zone);
744	}
745	return (result);
746}
747
748/*% dump the zone */
749isc_result_t
750dump_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
790void
791InitSockets(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
805void
806DestroySockets(void) {
807	WSACleanup();
808}
809#endif /* ifdef _WIN32 */
810