1135446Strhodes/*
2254897Serwin * Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000-2002  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18254897Serwin/* $Id: check-tool.c,v 1.44 2011/12/22 07:32:39 each Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <stdio.h>
25135446Strhodes
26216175Sdougb#ifdef _WIN32
27216175Sdougb#include <Winsock2.h>
28216175Sdougb#endif
29216175Sdougb
30135446Strhodes#include "check-tool.h"
31135446Strhodes#include <isc/buffer.h>
32135446Strhodes#include <isc/log.h>
33193149Sdougb#include <isc/mem.h>
34193149Sdougb#include <isc/netdb.h>
35170222Sdougb#include <isc/net.h>
36135446Strhodes#include <isc/region.h>
37135446Strhodes#include <isc/stdio.h>
38174187Sdougb#include <isc/string.h>
39193149Sdougb#include <isc/symtab.h>
40135446Strhodes#include <isc/types.h>
41193149Sdougb#include <isc/util.h>
42135446Strhodes
43135446Strhodes#include <dns/fixedname.h>
44143731Sdougb#include <dns/log.h>
45135446Strhodes#include <dns/name.h>
46170222Sdougb#include <dns/rdata.h>
47135446Strhodes#include <dns/rdataclass.h>
48170222Sdougb#include <dns/rdataset.h>
49135446Strhodes#include <dns/types.h>
50135446Strhodes#include <dns/zone.h>
51135446Strhodes
52170222Sdougb#include <isccfg/log.h>
53170222Sdougb
54186462Sdougb#ifndef CHECK_SIBLING
55186462Sdougb#define CHECK_SIBLING 1
56186462Sdougb#endif
57186462Sdougb
58186462Sdougb#ifndef CHECK_LOCAL
59186462Sdougb#define CHECK_LOCAL 1
60186462Sdougb#endif
61186462Sdougb
62170222Sdougb#ifdef HAVE_ADDRINFO
63170222Sdougb#ifdef HAVE_GETADDRINFO
64170222Sdougb#ifdef HAVE_GAISTRERROR
65170222Sdougb#define USE_GETADDRINFO
66170222Sdougb#endif
67170222Sdougb#endif
68170222Sdougb#endif
69170222Sdougb
70135446Strhodes#define CHECK(r) \
71170222Sdougb	do { \
72135446Strhodes		result = (r); \
73170222Sdougb		if (result != ISC_R_SUCCESS) \
74170222Sdougb			goto cleanup; \
75186462Sdougb	} while (0)
76135446Strhodes
77193149Sdougb#define ERR_IS_CNAME 1
78193149Sdougb#define ERR_NO_ADDRESSES 2
79193149Sdougb#define ERR_LOOKUP_FAILURE 3
80193149Sdougb#define ERR_EXTRA_A 4
81193149Sdougb#define ERR_EXTRA_AAAA 5
82193149Sdougb#define ERR_MISSING_GLUE 5
83193149Sdougb#define ERR_IS_MXCNAME 6
84193149Sdougb#define ERR_IS_SRVCNAME 7
85193149Sdougb
86135446Strhodesstatic const char *dbtype[] = { "rbt" };
87135446Strhodes
88135446Strhodesint debug = 0;
89135446Strhodesisc_boolean_t nomerge = ISC_TRUE;
90186462Sdougb#if CHECK_LOCAL
91170222Sdougbisc_boolean_t docheckmx = ISC_TRUE;
92170222Sdougbisc_boolean_t dochecksrv = ISC_TRUE;
93170222Sdougbisc_boolean_t docheckns = ISC_TRUE;
94186462Sdougb#else
95186462Sdougbisc_boolean_t docheckmx = ISC_FALSE;
96186462Sdougbisc_boolean_t dochecksrv = ISC_FALSE;
97186462Sdougbisc_boolean_t docheckns = ISC_FALSE;
98186462Sdougb#endif
99186462Sdougbunsigned int zone_options = DNS_ZONEOPT_CHECKNS |
100170222Sdougb			    DNS_ZONEOPT_CHECKMX |
101143731Sdougb			    DNS_ZONEOPT_MANYERRORS |
102170222Sdougb			    DNS_ZONEOPT_CHECKNAMES |
103170222Sdougb			    DNS_ZONEOPT_CHECKINTEGRITY |
104186462Sdougb#if CHECK_SIBLING
105186462Sdougb			    DNS_ZONEOPT_CHECKSIBLING |
106186462Sdougb#endif
107170222Sdougb			    DNS_ZONEOPT_CHECKWILDCARD |
108170222Sdougb			    DNS_ZONEOPT_WARNMXCNAME |
109170222Sdougb			    DNS_ZONEOPT_WARNSRVCNAME;
110135446Strhodes
111170222Sdougb/*
112170222Sdougb * This needs to match the list in bin/named/log.c.
113170222Sdougb */
114170222Sdougbstatic isc_logcategory_t categories[] = {
115170222Sdougb	{ "",		     0 },
116170222Sdougb	{ "client",	     0 },
117170222Sdougb	{ "network",	     0 },
118170222Sdougb	{ "update",	     0 },
119170222Sdougb	{ "queries",	     0 },
120170222Sdougb	{ "unmatched", 	     0 },
121170222Sdougb	{ "update-security", 0 },
122193149Sdougb	{ "query-errors",    0 },
123170222Sdougb	{ NULL,		     0 }
124170222Sdougb};
125170222Sdougb
126193149Sdougbstatic isc_symtab_t *symtab = NULL;
127193149Sdougbstatic isc_mem_t *sym_mctx;
128193149Sdougb
129193149Sdougbstatic void
130193149Sdougbfreekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
131193149Sdougb	UNUSED(type);
132193149Sdougb	UNUSED(value);
133193149Sdougb	isc_mem_free(userarg, key);
134193149Sdougb}
135193149Sdougb
136193149Sdougbstatic void
137193149Sdougbadd(char *key, int value) {
138193149Sdougb	isc_result_t result;
139193149Sdougb	isc_symvalue_t symvalue;
140193149Sdougb
141193149Sdougb	if (sym_mctx == NULL) {
142193149Sdougb		result = isc_mem_create(0, 0, &sym_mctx);
143193149Sdougb		if (result != ISC_R_SUCCESS)
144193149Sdougb			return;
145193149Sdougb	}
146193149Sdougb
147193149Sdougb	if (symtab == NULL) {
148193149Sdougb		result = isc_symtab_create(sym_mctx, 100, freekey, sym_mctx,
149193149Sdougb					   ISC_FALSE, &symtab);
150193149Sdougb		if (result != ISC_R_SUCCESS)
151193149Sdougb			return;
152193149Sdougb	}
153193149Sdougb
154193149Sdougb	key = isc_mem_strdup(sym_mctx, key);
155193149Sdougb	if (key == NULL)
156193149Sdougb		return;
157193149Sdougb
158193149Sdougb	symvalue.as_pointer = NULL;
159193149Sdougb	result = isc_symtab_define(symtab, key, value, symvalue,
160193149Sdougb				   isc_symexists_reject);
161193149Sdougb	if (result != ISC_R_SUCCESS)
162193149Sdougb		isc_mem_free(sym_mctx, key);
163193149Sdougb}
164193149Sdougb
165170222Sdougbstatic isc_boolean_t
166193149Sdougblogged(char *key, int value) {
167193149Sdougb	isc_result_t result;
168193149Sdougb
169193149Sdougb	if (symtab == NULL)
170193149Sdougb		return (ISC_FALSE);
171193149Sdougb
172193149Sdougb	result = isc_symtab_lookup(symtab, key, value, NULL);
173193149Sdougb	if (result == ISC_R_SUCCESS)
174193149Sdougb		return (ISC_TRUE);
175193149Sdougb	return (ISC_FALSE);
176193149Sdougb}
177193149Sdougb
178193149Sdougbstatic isc_boolean_t
179170222Sdougbcheckns(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner,
180170222Sdougb	dns_rdataset_t *a, dns_rdataset_t *aaaa)
181170222Sdougb{
182170222Sdougb#ifdef USE_GETADDRINFO
183170222Sdougb	dns_rdataset_t *rdataset;
184170222Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
185170222Sdougb	struct addrinfo hints, *ai, *cur;
186170222Sdougb	char namebuf[DNS_NAME_FORMATSIZE + 1];
187170222Sdougb	char ownerbuf[DNS_NAME_FORMATSIZE];
188170222Sdougb	char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
189170222Sdougb	isc_boolean_t answer = ISC_TRUE;
190170222Sdougb	isc_boolean_t match;
191170222Sdougb	const char *type;
192170222Sdougb	void *ptr = NULL;
193170222Sdougb	int result;
194170222Sdougb
195170222Sdougb	REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
196170222Sdougb		a->type == dns_rdatatype_a);
197170222Sdougb	REQUIRE(aaaa == NULL || !dns_rdataset_isassociated(aaaa) ||
198170222Sdougb		aaaa->type == dns_rdatatype_aaaa);
199254402Serwin
200254402Serwin	if (a == NULL || aaaa == NULL)
201254402Serwin		return (answer);
202254402Serwin
203170222Sdougb	memset(&hints, 0, sizeof(hints));
204170222Sdougb	hints.ai_flags = AI_CANONNAME;
205170222Sdougb	hints.ai_family = PF_UNSPEC;
206170222Sdougb	hints.ai_socktype = SOCK_STREAM;
207170222Sdougb	hints.ai_protocol = IPPROTO_TCP;
208170222Sdougb
209170222Sdougb	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
210170222Sdougb	/*
211170222Sdougb	 * Turn off search.
212170222Sdougb	 */
213170222Sdougb	if (dns_name_countlabels(name) > 1U)
214170222Sdougb		strcat(namebuf, ".");
215170222Sdougb	dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
216186462Sdougb
217170222Sdougb	result = getaddrinfo(namebuf, NULL, &hints, &ai);
218170222Sdougb	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
219170222Sdougb	switch (result) {
220170222Sdougb	case 0:
221174187Sdougb		/*
222174187Sdougb		 * Work around broken getaddrinfo() implementations that
223174187Sdougb		 * fail to set ai_canonname on first entry.
224174187Sdougb		 */
225174187Sdougb		cur = ai;
226174187Sdougb		while (cur != NULL && cur->ai_canonname == NULL &&
227174187Sdougb		       cur->ai_next != NULL)
228174187Sdougb			cur = cur->ai_next;
229174187Sdougb		if (cur != NULL && cur->ai_canonname != NULL &&
230193149Sdougb		    strcasecmp(cur->ai_canonname, namebuf) != 0 &&
231193149Sdougb		    !logged(namebuf, ERR_IS_CNAME)) {
232170222Sdougb			dns_zone_log(zone, ISC_LOG_ERROR,
233170222Sdougb				     "%s/NS '%s' (out of zone) "
234193149Sdougb				     "is a CNAME '%s' (illegal)",
235193149Sdougb				     ownerbuf, namebuf,
236193149Sdougb				     cur->ai_canonname);
237170222Sdougb			/* XXX950 make fatal for 9.5.0 */
238170222Sdougb			/* answer = ISC_FALSE; */
239193149Sdougb			add(namebuf, ERR_IS_CNAME);
240170222Sdougb		}
241170222Sdougb		break;
242170222Sdougb	case EAI_NONAME:
243170222Sdougb#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
244170222Sdougb	case EAI_NODATA:
245170222Sdougb#endif
246193149Sdougb		if (!logged(namebuf, ERR_NO_ADDRESSES)) {
247193149Sdougb			dns_zone_log(zone, ISC_LOG_ERROR,
248193149Sdougb				     "%s/NS '%s' (out of zone) "
249193149Sdougb				     "has no addresses records (A or AAAA)",
250193149Sdougb				     ownerbuf, namebuf);
251193149Sdougb			add(namebuf, ERR_NO_ADDRESSES);
252193149Sdougb		}
253170222Sdougb		/* XXX950 make fatal for 9.5.0 */
254170222Sdougb		return (ISC_TRUE);
255170222Sdougb
256170222Sdougb	default:
257193149Sdougb		if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
258193149Sdougb			dns_zone_log(zone, ISC_LOG_WARNING,
259193149Sdougb				     "getaddrinfo(%s) failed: %s",
260193149Sdougb				     namebuf, gai_strerror(result));
261193149Sdougb			add(namebuf, ERR_LOOKUP_FAILURE);
262193149Sdougb		}
263170222Sdougb		return (ISC_TRUE);
264170222Sdougb	}
265254402Serwin
266170222Sdougb	/*
267170222Sdougb	 * Check that all glue records really exist.
268170222Sdougb	 */
269170222Sdougb	if (!dns_rdataset_isassociated(a))
270170222Sdougb		goto checkaaaa;
271170222Sdougb	result = dns_rdataset_first(a);
272170222Sdougb	while (result == ISC_R_SUCCESS) {
273170222Sdougb		dns_rdataset_current(a, &rdata);
274170222Sdougb		match = ISC_FALSE;
275170222Sdougb		for (cur = ai; cur != NULL; cur = cur->ai_next) {
276170222Sdougb			if (cur->ai_family != AF_INET)
277170222Sdougb				continue;
278170222Sdougb			ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
279170222Sdougb			if (memcmp(ptr, rdata.data, rdata.length) == 0) {
280170222Sdougb				match = ISC_TRUE;
281170222Sdougb				break;
282170222Sdougb			}
283170222Sdougb		}
284193149Sdougb		if (!match && !logged(namebuf, ERR_EXTRA_A)) {
285170222Sdougb			dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
286170222Sdougb				     "extra GLUE A record (%s)",
287170222Sdougb				     ownerbuf, namebuf,
288170222Sdougb				     inet_ntop(AF_INET, rdata.data,
289170222Sdougb					       addrbuf, sizeof(addrbuf)));
290193149Sdougb			add(namebuf, ERR_EXTRA_A);
291170222Sdougb			/* XXX950 make fatal for 9.5.0 */
292170222Sdougb			/* answer = ISC_FALSE; */
293170222Sdougb		}
294170222Sdougb		dns_rdata_reset(&rdata);
295170222Sdougb		result = dns_rdataset_next(a);
296170222Sdougb	}
297170222Sdougb
298170222Sdougb checkaaaa:
299170222Sdougb	if (!dns_rdataset_isassociated(aaaa))
300170222Sdougb		goto checkmissing;
301170222Sdougb	result = dns_rdataset_first(aaaa);
302170222Sdougb	while (result == ISC_R_SUCCESS) {
303170222Sdougb		dns_rdataset_current(aaaa, &rdata);
304170222Sdougb		match = ISC_FALSE;
305170222Sdougb		for (cur = ai; cur != NULL; cur = cur->ai_next) {
306170222Sdougb			if (cur->ai_family != AF_INET6)
307170222Sdougb				continue;
308170222Sdougb			ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
309170222Sdougb			if (memcmp(ptr, rdata.data, rdata.length) == 0) {
310170222Sdougb				match = ISC_TRUE;
311170222Sdougb				break;
312170222Sdougb			}
313170222Sdougb		}
314193149Sdougb		if (!match && !logged(namebuf, ERR_EXTRA_AAAA)) {
315170222Sdougb			dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
316170222Sdougb				     "extra GLUE AAAA record (%s)",
317170222Sdougb				     ownerbuf, namebuf,
318170222Sdougb				     inet_ntop(AF_INET6, rdata.data,
319170222Sdougb					       addrbuf, sizeof(addrbuf)));
320193149Sdougb			add(namebuf, ERR_EXTRA_AAAA);
321170222Sdougb			/* XXX950 make fatal for 9.5.0. */
322170222Sdougb			/* answer = ISC_FALSE; */
323170222Sdougb		}
324170222Sdougb		dns_rdata_reset(&rdata);
325170222Sdougb		result = dns_rdataset_next(aaaa);
326170222Sdougb	}
327170222Sdougb
328170222Sdougb checkmissing:
329170222Sdougb	/*
330170222Sdougb	 * Check that all addresses appear in the glue.
331170222Sdougb	 */
332193149Sdougb	if (!logged(namebuf, ERR_MISSING_GLUE)) {
333193149Sdougb		isc_boolean_t missing_glue = ISC_FALSE;
334193149Sdougb		for (cur = ai; cur != NULL; cur = cur->ai_next) {
335193149Sdougb			switch (cur->ai_family) {
336193149Sdougb			case AF_INET:
337193149Sdougb				rdataset = a;
338193149Sdougb				ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
339193149Sdougb				type = "A";
340193149Sdougb				break;
341193149Sdougb			case AF_INET6:
342193149Sdougb				rdataset = aaaa;
343193149Sdougb				ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
344193149Sdougb				type = "AAAA";
345193149Sdougb				break;
346193149Sdougb			default:
347193149Sdougb				 continue;
348193149Sdougb			}
349193149Sdougb			match = ISC_FALSE;
350193149Sdougb			if (dns_rdataset_isassociated(rdataset))
351193149Sdougb				result = dns_rdataset_first(rdataset);
352193149Sdougb			else
353193149Sdougb				result = ISC_R_FAILURE;
354193149Sdougb			while (result == ISC_R_SUCCESS && !match) {
355193149Sdougb				dns_rdataset_current(rdataset, &rdata);
356193149Sdougb				if (memcmp(ptr, rdata.data, rdata.length) == 0)
357193149Sdougb					match = ISC_TRUE;
358193149Sdougb				dns_rdata_reset(&rdata);
359193149Sdougb				result = dns_rdataset_next(rdataset);
360193149Sdougb			}
361193149Sdougb			if (!match) {
362193149Sdougb				dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
363193149Sdougb					     "missing GLUE %s record (%s)",
364193149Sdougb					     ownerbuf, namebuf, type,
365193149Sdougb					     inet_ntop(cur->ai_family, ptr,
366193149Sdougb						       addrbuf, sizeof(addrbuf)));
367193149Sdougb				/* XXX950 make fatal for 9.5.0. */
368193149Sdougb				/* answer = ISC_FALSE; */
369193149Sdougb				missing_glue = ISC_TRUE;
370193149Sdougb			}
371170222Sdougb		}
372193149Sdougb		if (missing_glue)
373193149Sdougb			add(namebuf, ERR_MISSING_GLUE);
374170222Sdougb	}
375170222Sdougb	freeaddrinfo(ai);
376170222Sdougb	return (answer);
377170222Sdougb#else
378170222Sdougb	return (ISC_TRUE);
379170222Sdougb#endif
380170222Sdougb}
381170222Sdougb
382170222Sdougbstatic isc_boolean_t
383170222Sdougbcheckmx(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
384170222Sdougb#ifdef USE_GETADDRINFO
385174187Sdougb	struct addrinfo hints, *ai, *cur;
386170222Sdougb	char namebuf[DNS_NAME_FORMATSIZE + 1];
387170222Sdougb	char ownerbuf[DNS_NAME_FORMATSIZE];
388170222Sdougb	int result;
389170222Sdougb	int level = ISC_LOG_ERROR;
390170222Sdougb	isc_boolean_t answer = ISC_TRUE;
391170222Sdougb
392170222Sdougb	memset(&hints, 0, sizeof(hints));
393170222Sdougb	hints.ai_flags = AI_CANONNAME;
394170222Sdougb	hints.ai_family = PF_UNSPEC;
395170222Sdougb	hints.ai_socktype = SOCK_STREAM;
396170222Sdougb	hints.ai_protocol = IPPROTO_TCP;
397170222Sdougb
398170222Sdougb	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
399170222Sdougb	/*
400170222Sdougb	 * Turn off search.
401170222Sdougb	 */
402170222Sdougb	if (dns_name_countlabels(name) > 1U)
403170222Sdougb		strcat(namebuf, ".");
404170222Sdougb	dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
405186462Sdougb
406170222Sdougb	result = getaddrinfo(namebuf, NULL, &hints, &ai);
407170222Sdougb	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
408170222Sdougb	switch (result) {
409170222Sdougb	case 0:
410174187Sdougb		/*
411174187Sdougb		 * Work around broken getaddrinfo() implementations that
412174187Sdougb		 * fail to set ai_canonname on first entry.
413174187Sdougb		 */
414174187Sdougb		cur = ai;
415174187Sdougb		while (cur != NULL && cur->ai_canonname == NULL &&
416174187Sdougb		       cur->ai_next != NULL)
417174187Sdougb			cur = cur->ai_next;
418174187Sdougb		if (cur != NULL && cur->ai_canonname != NULL &&
419174187Sdougb		    strcasecmp(cur->ai_canonname, namebuf) != 0) {
420170222Sdougb			if ((zone_options & DNS_ZONEOPT_WARNMXCNAME) != 0)
421170222Sdougb				level = ISC_LOG_WARNING;
422170222Sdougb			if ((zone_options & DNS_ZONEOPT_IGNOREMXCNAME) == 0) {
423193149Sdougb				if (!logged(namebuf, ERR_IS_MXCNAME)) {
424193149Sdougb					dns_zone_log(zone, level,
425193149Sdougb						     "%s/MX '%s' (out of zone)"
426193149Sdougb						     " is a CNAME '%s' "
427193149Sdougb						     "(illegal)",
428193149Sdougb						     ownerbuf, namebuf,
429193149Sdougb						     cur->ai_canonname);
430193149Sdougb					add(namebuf, ERR_IS_MXCNAME);
431193149Sdougb				}
432170222Sdougb				if (level == ISC_LOG_ERROR)
433170222Sdougb					answer = ISC_FALSE;
434170222Sdougb			}
435170222Sdougb		}
436170222Sdougb		freeaddrinfo(ai);
437170222Sdougb		return (answer);
438170222Sdougb
439170222Sdougb	case EAI_NONAME:
440170222Sdougb#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
441170222Sdougb	case EAI_NODATA:
442170222Sdougb#endif
443193149Sdougb		if (!logged(namebuf, ERR_NO_ADDRESSES)) {
444193149Sdougb			dns_zone_log(zone, ISC_LOG_ERROR,
445193149Sdougb				     "%s/MX '%s' (out of zone) "
446193149Sdougb				     "has no addresses records (A or AAAA)",
447193149Sdougb				     ownerbuf, namebuf);
448193149Sdougb			add(namebuf, ERR_NO_ADDRESSES);
449193149Sdougb		}
450170222Sdougb		/* XXX950 make fatal for 9.5.0. */
451170222Sdougb		return (ISC_TRUE);
452170222Sdougb
453170222Sdougb	default:
454193149Sdougb		if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
455193149Sdougb			dns_zone_log(zone, ISC_LOG_WARNING,
456170222Sdougb			     "getaddrinfo(%s) failed: %s",
457170222Sdougb			     namebuf, gai_strerror(result));
458193149Sdougb			add(namebuf, ERR_LOOKUP_FAILURE);
459193149Sdougb		}
460170222Sdougb		return (ISC_TRUE);
461170222Sdougb	}
462170222Sdougb#else
463170222Sdougb	return (ISC_TRUE);
464170222Sdougb#endif
465170222Sdougb}
466170222Sdougb
467170222Sdougbstatic isc_boolean_t
468170222Sdougbchecksrv(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
469170222Sdougb#ifdef USE_GETADDRINFO
470174187Sdougb	struct addrinfo hints, *ai, *cur;
471170222Sdougb	char namebuf[DNS_NAME_FORMATSIZE + 1];
472170222Sdougb	char ownerbuf[DNS_NAME_FORMATSIZE];
473170222Sdougb	int result;
474170222Sdougb	int level = ISC_LOG_ERROR;
475170222Sdougb	isc_boolean_t answer = ISC_TRUE;
476170222Sdougb
477170222Sdougb	memset(&hints, 0, sizeof(hints));
478170222Sdougb	hints.ai_flags = AI_CANONNAME;
479170222Sdougb	hints.ai_family = PF_UNSPEC;
480170222Sdougb	hints.ai_socktype = SOCK_STREAM;
481170222Sdougb	hints.ai_protocol = IPPROTO_TCP;
482170222Sdougb
483170222Sdougb	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
484170222Sdougb	/*
485170222Sdougb	 * Turn off search.
486170222Sdougb	 */
487170222Sdougb	if (dns_name_countlabels(name) > 1U)
488170222Sdougb		strcat(namebuf, ".");
489170222Sdougb	dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
490186462Sdougb
491170222Sdougb	result = getaddrinfo(namebuf, NULL, &hints, &ai);
492170222Sdougb	dns_name_format(name, namebuf, sizeof(namebuf) - 1);
493170222Sdougb	switch (result) {
494170222Sdougb	case 0:
495174187Sdougb		/*
496174187Sdougb		 * Work around broken getaddrinfo() implementations that
497174187Sdougb		 * fail to set ai_canonname on first entry.
498174187Sdougb		 */
499174187Sdougb		cur = ai;
500174187Sdougb		while (cur != NULL && cur->ai_canonname == NULL &&
501174187Sdougb		       cur->ai_next != NULL)
502174187Sdougb			cur = cur->ai_next;
503174187Sdougb		if (cur != NULL && cur->ai_canonname != NULL &&
504174187Sdougb		    strcasecmp(cur->ai_canonname, namebuf) != 0) {
505170222Sdougb			if ((zone_options & DNS_ZONEOPT_WARNSRVCNAME) != 0)
506170222Sdougb				level = ISC_LOG_WARNING;
507170222Sdougb			if ((zone_options & DNS_ZONEOPT_IGNORESRVCNAME) == 0) {
508193149Sdougb				if (!logged(namebuf, ERR_IS_SRVCNAME)) {
509193149Sdougb					dns_zone_log(zone, level, "%s/SRV '%s'"
510193149Sdougb						     " (out of zone) is a "
511193149Sdougb						     "CNAME '%s' (illegal)",
512193149Sdougb						     ownerbuf, namebuf,
513193149Sdougb						     cur->ai_canonname);
514193149Sdougb					add(namebuf, ERR_IS_SRVCNAME);
515193149Sdougb				}
516170222Sdougb				if (level == ISC_LOG_ERROR)
517170222Sdougb					answer = ISC_FALSE;
518170222Sdougb			}
519170222Sdougb		}
520170222Sdougb		freeaddrinfo(ai);
521170222Sdougb		return (answer);
522170222Sdougb
523170222Sdougb	case EAI_NONAME:
524170222Sdougb#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
525170222Sdougb	case EAI_NODATA:
526170222Sdougb#endif
527193149Sdougb		if (!logged(namebuf, ERR_NO_ADDRESSES)) {
528193149Sdougb			dns_zone_log(zone, ISC_LOG_ERROR,
529193149Sdougb				     "%s/SRV '%s' (out of zone) "
530193149Sdougb				     "has no addresses records (A or AAAA)",
531193149Sdougb				     ownerbuf, namebuf);
532193149Sdougb			add(namebuf, ERR_NO_ADDRESSES);
533193149Sdougb		}
534170222Sdougb		/* XXX950 make fatal for 9.5.0. */
535170222Sdougb		return (ISC_TRUE);
536170222Sdougb
537170222Sdougb	default:
538193149Sdougb		if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
539193149Sdougb			dns_zone_log(zone, ISC_LOG_WARNING,
540193149Sdougb				     "getaddrinfo(%s) failed: %s",
541193149Sdougb				     namebuf, gai_strerror(result));
542193149Sdougb			add(namebuf, ERR_LOOKUP_FAILURE);
543193149Sdougb		}
544170222Sdougb		return (ISC_TRUE);
545170222Sdougb	}
546170222Sdougb#else
547170222Sdougb	return (ISC_TRUE);
548170222Sdougb#endif
549170222Sdougb}
550170222Sdougb
551135446Strhodesisc_result_t
552193149Sdougbsetup_logging(isc_mem_t *mctx, FILE *errout, isc_log_t **logp) {
553135446Strhodes	isc_logdestination_t destination;
554135446Strhodes	isc_logconfig_t *logconfig = NULL;
555135446Strhodes	isc_log_t *log = NULL;
556135446Strhodes
557135446Strhodes	RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
558170222Sdougb	isc_log_registercategories(log, categories);
559135446Strhodes	isc_log_setcontext(log);
560170222Sdougb	dns_log_init(log);
561170222Sdougb	dns_log_setcontext(log);
562170222Sdougb	cfg_log_init(log);
563135446Strhodes
564193149Sdougb	destination.file.stream = errout;
565135446Strhodes	destination.file.name = NULL;
566135446Strhodes	destination.file.versions = ISC_LOG_ROLLNEVER;
567135446Strhodes	destination.file.maximum_size = 0;
568135446Strhodes	RUNTIME_CHECK(isc_log_createchannel(logconfig, "stderr",
569135446Strhodes				       ISC_LOG_TOFILEDESC,
570135446Strhodes				       ISC_LOG_DYNAMIC,
571135446Strhodes				       &destination, 0) == ISC_R_SUCCESS);
572135446Strhodes	RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
573135446Strhodes					 NULL, NULL) == ISC_R_SUCCESS);
574135446Strhodes
575135446Strhodes	*logp = log;
576135446Strhodes	return (ISC_R_SUCCESS);
577135446Strhodes}
578135446Strhodes
579170222Sdougb/*% load the zone */
580135446Strhodesisc_result_t
581135446Strhodesload_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
582170222Sdougb	  dns_masterformat_t fileformat, const char *classname,
583170222Sdougb	  dns_zone_t **zonep)
584135446Strhodes{
585135446Strhodes	isc_result_t result;
586135446Strhodes	dns_rdataclass_t rdclass;
587135446Strhodes	isc_textregion_t region;
588135446Strhodes	isc_buffer_t buffer;
589135446Strhodes	dns_fixedname_t fixorigin;
590135446Strhodes	dns_name_t *origin;
591135446Strhodes	dns_zone_t *zone = NULL;
592135446Strhodes
593135446Strhodes	REQUIRE(zonep == NULL || *zonep == NULL);
594135446Strhodes
595135446Strhodes	if (debug)
596135446Strhodes		fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
597135446Strhodes			zonename, filename, classname);
598135446Strhodes
599135446Strhodes	CHECK(dns_zone_create(&zone, mctx));
600135446Strhodes
601135446Strhodes	dns_zone_settype(zone, dns_zone_master);
602135446Strhodes
603254402Serwin	isc_buffer_constinit(&buffer, zonename, strlen(zonename));
604135446Strhodes	isc_buffer_add(&buffer, strlen(zonename));
605135446Strhodes	dns_fixedname_init(&fixorigin);
606135446Strhodes	origin = dns_fixedname_name(&fixorigin);
607224092Sdougb	CHECK(dns_name_fromtext(origin, &buffer, dns_rootname, 0, NULL));
608135446Strhodes	CHECK(dns_zone_setorigin(zone, origin));
609135446Strhodes	CHECK(dns_zone_setdbtype(zone, 1, (const char * const *) dbtype));
610170222Sdougb	CHECK(dns_zone_setfile2(zone, filename, fileformat));
611135446Strhodes
612135446Strhodes	DE_CONST(classname, region.base);
613135446Strhodes	region.length = strlen(classname);
614135446Strhodes	CHECK(dns_rdataclass_fromtext(&rdclass, &region));
615135446Strhodes
616135446Strhodes	dns_zone_setclass(zone, rdclass);
617135446Strhodes	dns_zone_setoption(zone, zone_options, ISC_TRUE);
618135446Strhodes	dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
619170222Sdougb	if (docheckmx)
620170222Sdougb		dns_zone_setcheckmx(zone, checkmx);
621170222Sdougb	if (docheckns)
622170222Sdougb		dns_zone_setcheckns(zone, checkns);
623170222Sdougb	if (dochecksrv)
624170222Sdougb		dns_zone_setchecksrv(zone, checksrv);
625135446Strhodes
626135446Strhodes	CHECK(dns_zone_load(zone));
627170222Sdougb	if (zonep != NULL) {
628135446Strhodes		*zonep = zone;
629135446Strhodes		zone = NULL;
630135446Strhodes	}
631135446Strhodes
632135446Strhodes cleanup:
633135446Strhodes	if (zone != NULL)
634135446Strhodes		dns_zone_detach(&zone);
635135446Strhodes	return (result);
636135446Strhodes}
637135446Strhodes
638170222Sdougb/*% dump the zone */
639135446Strhodesisc_result_t
640170222Sdougbdump_zone(const char *zonename, dns_zone_t *zone, const char *filename,
641254897Serwin	  dns_masterformat_t fileformat, const dns_master_style_t *style,
642254897Serwin	  const isc_uint32_t rawversion)
643135446Strhodes{
644135446Strhodes	isc_result_t result;
645135446Strhodes	FILE *output = stdout;
646245163Serwin	const char *flags;
647135446Strhodes
648245163Serwin	flags = (fileformat == dns_masterformat_text) ? "w+" : "wb+";
649245163Serwin
650135446Strhodes	if (debug) {
651193149Sdougb		if (filename != NULL && strcmp(filename, "-") != 0)
652135446Strhodes			fprintf(stderr, "dumping \"%s\" to \"%s\"\n",
653135446Strhodes				zonename, filename);
654135446Strhodes		else
655135446Strhodes			fprintf(stderr, "dumping \"%s\"\n", zonename);
656135446Strhodes	}
657135446Strhodes
658193149Sdougb	if (filename != NULL && strcmp(filename, "-") != 0) {
659245163Serwin		result = isc_stdio_open(filename, flags, &output);
660135446Strhodes
661135446Strhodes		if (result != ISC_R_SUCCESS) {
662135446Strhodes			fprintf(stderr, "could not open output "
663135446Strhodes				"file \"%s\" for writing\n", filename);
664135446Strhodes			return (ISC_R_FAILURE);
665135446Strhodes		}
666135446Strhodes	}
667135446Strhodes
668254897Serwin	result = dns_zone_dumptostream3(zone, output, fileformat, style,
669254897Serwin					rawversion);
670193149Sdougb	if (output != stdout)
671135446Strhodes		(void)isc_stdio_close(output);
672135446Strhodes
673135446Strhodes	return (result);
674135446Strhodes}
675216175Sdougb
676216175Sdougb#ifdef _WIN32
677216175Sdougbvoid
678216175SdougbInitSockets(void) {
679216175Sdougb	WORD wVersionRequested;
680216175Sdougb	WSADATA wsaData;
681216175Sdougb	int err;
682216175Sdougb
683216175Sdougb	wVersionRequested = MAKEWORD(2, 0);
684216175Sdougb
685216175Sdougb	err = WSAStartup( wVersionRequested, &wsaData );
686216175Sdougb	if (err != 0) {
687216175Sdougb		fprintf(stderr, "WSAStartup() failed: %d\n", err);
688216175Sdougb		exit(1);
689216175Sdougb	}
690216175Sdougb}
691216175Sdougb
692216175Sdougbvoid
693216175SdougbDestroySockets(void) {
694216175Sdougb	WSACleanup();
695216175Sdougb}
696216175Sdougb#endif
697216175Sdougb
698