check-tool.c revision 1.9
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
83static const char *dbtype[] = { "rbt" };
84
85int debug = 0;
86const char *journal = NULL;
87bool nomerge = true;
88#if CHECK_LOCAL
89bool docheckmx = true;
90bool dochecksrv = true;
91bool docheckns = true;
92#else  /* if CHECK_LOCAL */
93bool docheckmx = false;
94bool dochecksrv = false;
95bool docheckns = false;
96#endif /* if CHECK_LOCAL */
97dns_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 */
109static isc_logcategory_t categories[] = { { "", 0 },
110					  { "unmatched", 0 },
111					  { NULL, 0 } };
112
113static isc_symtab_t *symtab = NULL;
114static isc_mem_t *sym_mctx;
115
116static void
117freekey(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
123static void
124add(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
150static bool
151logged(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
165static bool
166checkns(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
290checkaaaa:
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
324checkmissing:
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
383static bool
384checkmx(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
470static bool
471checksrv(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
557isc_result_t
558setup_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 */
586static isc_result_t
587check_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
655cleanup:
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 */
676isc_result_t
677load_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
746cleanup:
747	if (zone != NULL) {
748		dns_zone_detach(&zone);
749	}
750	return (result);
751}
752
753/*% dump the zone */
754isc_result_t
755dump_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
795void
796InitSockets(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
810void
811DestroySockets(void) {
812	WSACleanup();
813}
814#endif /* ifdef _WIN32 */
815