server.c revision 182645
1135446Strhodes/*
2182645Sdougb * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2003  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
18182645Sdougb/* $Id: server.c,v 1.419.18.57.10.3 2008/07/23 12:04:32 marka Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <stdlib.h>
25135446Strhodes
26135446Strhodes#include <isc/app.h>
27135446Strhodes#include <isc/base64.h>
28135446Strhodes#include <isc/dir.h>
29135446Strhodes#include <isc/entropy.h>
30135446Strhodes#include <isc/file.h>
31135446Strhodes#include <isc/hash.h>
32135446Strhodes#include <isc/lex.h>
33135446Strhodes#include <isc/parseint.h>
34135446Strhodes#include <isc/print.h>
35135446Strhodes#include <isc/resource.h>
36135446Strhodes#include <isc/stdio.h>
37135446Strhodes#include <isc/string.h>
38135446Strhodes#include <isc/task.h>
39135446Strhodes#include <isc/timer.h>
40135446Strhodes#include <isc/util.h>
41135446Strhodes
42135446Strhodes#include <isccfg/namedconf.h>
43135446Strhodes
44135446Strhodes#include <bind9/check.h>
45135446Strhodes
46170222Sdougb#include <dns/acache.h>
47135446Strhodes#include <dns/adb.h>
48135446Strhodes#include <dns/cache.h>
49135446Strhodes#include <dns/db.h>
50135446Strhodes#include <dns/dispatch.h>
51170222Sdougb#ifdef DLZ
52170222Sdougb#include <dns/dlz.h>
53170222Sdougb#endif
54135446Strhodes#include <dns/forward.h>
55135446Strhodes#include <dns/journal.h>
56135446Strhodes#include <dns/keytable.h>
57170222Sdougb#include <dns/lib.h>
58135446Strhodes#include <dns/master.h>
59135446Strhodes#include <dns/masterdump.h>
60135446Strhodes#include <dns/order.h>
61135446Strhodes#include <dns/peer.h>
62135446Strhodes#include <dns/portlist.h>
63135446Strhodes#include <dns/rdataclass.h>
64135446Strhodes#include <dns/rdataset.h>
65135446Strhodes#include <dns/rdatastruct.h>
66135446Strhodes#include <dns/resolver.h>
67135446Strhodes#include <dns/rootns.h>
68135446Strhodes#include <dns/secalg.h>
69135446Strhodes#include <dns/stats.h>
70135446Strhodes#include <dns/tkey.h>
71135446Strhodes#include <dns/view.h>
72135446Strhodes#include <dns/zone.h>
73135446Strhodes#include <dns/zt.h>
74135446Strhodes
75135446Strhodes#include <dst/dst.h>
76135446Strhodes#include <dst/result.h>
77135446Strhodes
78135446Strhodes#include <named/client.h>
79135446Strhodes#include <named/config.h>
80135446Strhodes#include <named/control.h>
81135446Strhodes#include <named/interfacemgr.h>
82135446Strhodes#include <named/log.h>
83135446Strhodes#include <named/logconf.h>
84135446Strhodes#include <named/lwresd.h>
85135446Strhodes#include <named/main.h>
86135446Strhodes#include <named/os.h>
87135446Strhodes#include <named/server.h>
88135446Strhodes#include <named/tkeyconf.h>
89135446Strhodes#include <named/tsigconf.h>
90135446Strhodes#include <named/zoneconf.h>
91153816Sdougb#ifdef HAVE_LIBSCF
92153816Sdougb#include <named/ns_smf_globals.h>
93153816Sdougb#include <stdlib.h>
94153816Sdougb#endif
95135446Strhodes
96170222Sdougb/*%
97135446Strhodes * Check an operation for failure.  Assumes that the function
98135446Strhodes * using it has a 'result' variable and a 'cleanup' label.
99135446Strhodes */
100135446Strhodes#define CHECK(op) \
101135446Strhodes	do { result = (op); 				  	 \
102135446Strhodes	       if (result != ISC_R_SUCCESS) goto cleanup; 	 \
103135446Strhodes	} while (0)
104135446Strhodes
105135446Strhodes#define CHECKM(op, msg) \
106135446Strhodes	do { result = (op); 				  	  \
107135446Strhodes	       if (result != ISC_R_SUCCESS) {			  \
108135446Strhodes			isc_log_write(ns_g_lctx,		  \
109135446Strhodes				      NS_LOGCATEGORY_GENERAL,	  \
110135446Strhodes				      NS_LOGMODULE_SERVER,	  \
111135446Strhodes				      ISC_LOG_ERROR,		  \
112135446Strhodes				      "%s: %s", msg,		  \
113135446Strhodes				      isc_result_totext(result)); \
114135446Strhodes			goto cleanup;				  \
115135446Strhodes		}						  \
116135446Strhodes	} while (0)						  \
117135446Strhodes
118135446Strhodes#define CHECKMF(op, msg, file) \
119135446Strhodes	do { result = (op); 				  	  \
120135446Strhodes	       if (result != ISC_R_SUCCESS) {			  \
121135446Strhodes			isc_log_write(ns_g_lctx,		  \
122135446Strhodes				      NS_LOGCATEGORY_GENERAL,	  \
123135446Strhodes				      NS_LOGMODULE_SERVER,	  \
124135446Strhodes				      ISC_LOG_ERROR,		  \
125135446Strhodes				      "%s '%s': %s", msg, file,	  \
126135446Strhodes				      isc_result_totext(result)); \
127135446Strhodes			goto cleanup;				  \
128135446Strhodes		}						  \
129135446Strhodes	} while (0)						  \
130135446Strhodes
131135446Strhodes#define CHECKFATAL(op, msg) \
132135446Strhodes	do { result = (op); 				  	  \
133135446Strhodes	       if (result != ISC_R_SUCCESS)			  \
134135446Strhodes			fatal(msg, result);			  \
135135446Strhodes	} while (0)						  \
136135446Strhodes
137135446Strhodesstruct ns_dispatch {
138135446Strhodes	isc_sockaddr_t			addr;
139135446Strhodes	unsigned int			dispatchgen;
140135446Strhodes	dns_dispatch_t			*dispatch;
141135446Strhodes	ISC_LINK(struct ns_dispatch)	link;
142135446Strhodes};
143135446Strhodes
144135446Strhodesstruct dumpcontext {
145135446Strhodes	isc_mem_t			*mctx;
146135446Strhodes	isc_boolean_t			dumpcache;
147135446Strhodes	isc_boolean_t			dumpzones;
148135446Strhodes	FILE				*fp;
149135446Strhodes	ISC_LIST(struct viewlistentry)	viewlist;
150135446Strhodes	struct viewlistentry		*view;
151135446Strhodes	struct zonelistentry		*zone;
152135446Strhodes	dns_dumpctx_t			*mdctx;
153135446Strhodes	dns_db_t			*db;
154135446Strhodes	dns_db_t			*cache;
155135446Strhodes	isc_task_t			*task;
156135446Strhodes	dns_dbversion_t			*version;
157135446Strhodes};
158135446Strhodes
159135446Strhodesstruct viewlistentry {
160135446Strhodes	dns_view_t			*view;
161135446Strhodes	ISC_LINK(struct viewlistentry)	link;
162135446Strhodes	ISC_LIST(struct zonelistentry)	zonelist;
163135446Strhodes};
164135446Strhodes
165135446Strhodesstruct zonelistentry {
166135446Strhodes	dns_zone_t			*zone;
167135446Strhodes	ISC_LINK(struct zonelistentry)	link;
168135446Strhodes};
169135446Strhodes
170170222Sdougb/*
171170222Sdougb * These zones should not leak onto the Internet.
172170222Sdougb */
173170222Sdougbstatic const struct {
174170222Sdougb	const char	*zone;
175170222Sdougb	isc_boolean_t	rfc1918;
176170222Sdougb} empty_zones[] = {
177170222Sdougb#ifdef notyet
178170222Sdougb	/* RFC 1918 */
179170222Sdougb	{ "10.IN-ADDR.ARPA", ISC_TRUE },
180170222Sdougb	{ "16.172.IN-ADDR.ARPA", ISC_TRUE },
181170222Sdougb	{ "17.172.IN-ADDR.ARPA", ISC_TRUE },
182170222Sdougb	{ "18.172.IN-ADDR.ARPA", ISC_TRUE },
183170222Sdougb	{ "19.172.IN-ADDR.ARPA", ISC_TRUE },
184170222Sdougb	{ "20.172.IN-ADDR.ARPA", ISC_TRUE },
185170222Sdougb	{ "21.172.IN-ADDR.ARPA", ISC_TRUE },
186170222Sdougb	{ "22.172.IN-ADDR.ARPA", ISC_TRUE },
187170222Sdougb	{ "23.172.IN-ADDR.ARPA", ISC_TRUE },
188170222Sdougb	{ "24.172.IN-ADDR.ARPA", ISC_TRUE },
189170222Sdougb	{ "25.172.IN-ADDR.ARPA", ISC_TRUE },
190170222Sdougb	{ "26.172.IN-ADDR.ARPA", ISC_TRUE },
191170222Sdougb	{ "27.172.IN-ADDR.ARPA", ISC_TRUE },
192170222Sdougb	{ "28.172.IN-ADDR.ARPA", ISC_TRUE },
193170222Sdougb	{ "29.172.IN-ADDR.ARPA", ISC_TRUE },
194170222Sdougb	{ "30.172.IN-ADDR.ARPA", ISC_TRUE },
195170222Sdougb	{ "31.172.IN-ADDR.ARPA", ISC_TRUE },
196170222Sdougb	{ "168.192.IN-ADDR.ARPA", ISC_TRUE },
197170222Sdougb#endif
198170222Sdougb
199170222Sdougb	/* RFC 3330 */
200170222Sdougb	{ "127.IN-ADDR.ARPA", ISC_FALSE },	/* LOOPBACK */
201170222Sdougb	{ "254.169.IN-ADDR.ARPA", ISC_FALSE },	/* LINK LOCAL */
202170222Sdougb	{ "2.0.192.IN-ADDR.ARPA", ISC_FALSE },	/* TEST NET */
203170222Sdougb	{ "255.255.255.255.IN-ADDR.ARPA", ISC_FALSE },	/* BROADCAST */
204170222Sdougb
205170222Sdougb	/* Local IPv6 Unicast Addresses */
206170222Sdougb	{ "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE },
207170222Sdougb	{ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE },
208170222Sdougb	/* LOCALLY ASSIGNED LOCAL ADDRES S SCOPE */
209170222Sdougb	{ "D.F.IP6.ARPA", ISC_FALSE },
210170222Sdougb	{ "8.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
211170222Sdougb	{ "9.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
212170222Sdougb	{ "A.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
213170222Sdougb	{ "B.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
214170222Sdougb
215170222Sdougb	{ NULL, ISC_FALSE }
216170222Sdougb};
217170222Sdougb
218135446Strhodesstatic void
219135446Strhodesfatal(const char *msg, isc_result_t result);
220135446Strhodes
221135446Strhodesstatic void
222135446Strhodesns_server_reload(isc_task_t *task, isc_event_t *event);
223135446Strhodes
224135446Strhodesstatic isc_result_t
225165071Sdougbns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
226170222Sdougb			cfg_aclconfctx_t *actx,
227135446Strhodes			isc_mem_t *mctx, ns_listenelt_t **target);
228135446Strhodesstatic isc_result_t
229165071Sdougbns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
230170222Sdougb			 cfg_aclconfctx_t *actx,
231135446Strhodes			 isc_mem_t *mctx, ns_listenlist_t **target);
232135446Strhodes
233135446Strhodesstatic isc_result_t
234165071Sdougbconfigure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
235165071Sdougb		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype);
236135446Strhodes
237135446Strhodesstatic isc_result_t
238165071Sdougbconfigure_alternates(const cfg_obj_t *config, dns_view_t *view,
239165071Sdougb		     const cfg_obj_t *alternates);
240135446Strhodes
241135446Strhodesstatic isc_result_t
242165071Sdougbconfigure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
243165071Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
244170222Sdougb	       cfg_aclconfctx_t *aclconf);
245135446Strhodes
246135446Strhodesstatic void
247135446Strhodesend_reserved_dispatches(ns_server_t *server, isc_boolean_t all);
248135446Strhodes
249170222Sdougb/*%
250135446Strhodes * Configure a single view ACL at '*aclp'.  Get its configuration by
251135446Strhodes * calling 'getvcacl' (for per-view configuration) and maybe 'getscacl'
252135446Strhodes * (for a global default).
253135446Strhodes */
254135446Strhodesstatic isc_result_t
255165071Sdougbconfigure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
256170222Sdougb		   const char *aclname, cfg_aclconfctx_t *actx,
257135446Strhodes		   isc_mem_t *mctx, dns_acl_t **aclp)
258135446Strhodes{
259135446Strhodes	isc_result_t result;
260165071Sdougb	const cfg_obj_t *maps[3];
261165071Sdougb	const cfg_obj_t *aclobj = NULL;
262135446Strhodes	int i = 0;
263135446Strhodes
264135446Strhodes	if (*aclp != NULL)
265135446Strhodes		dns_acl_detach(aclp);
266135446Strhodes	if (vconfig != NULL)
267135446Strhodes		maps[i++] = cfg_tuple_get(vconfig, "options");
268135446Strhodes	if (config != NULL) {
269165071Sdougb		const cfg_obj_t *options = NULL;
270135446Strhodes		(void)cfg_map_get(config, "options", &options);
271135446Strhodes		if (options != NULL)
272135446Strhodes			maps[i++] = options;
273135446Strhodes	}
274135446Strhodes	maps[i] = NULL;
275135446Strhodes
276165071Sdougb	(void)ns_config_get(maps, aclname, &aclobj);
277135446Strhodes	if (aclobj == NULL)
278135446Strhodes		/*
279135446Strhodes		 * No value available.  *aclp == NULL.
280135446Strhodes		 */
281135446Strhodes		return (ISC_R_SUCCESS);
282135446Strhodes
283170222Sdougb	result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
284170222Sdougb				    actx, mctx, aclp);
285135446Strhodes
286135446Strhodes	return (result);
287135446Strhodes}
288135446Strhodes
289135446Strhodesstatic isc_result_t
290165071Sdougbconfigure_view_dnsseckey(const cfg_obj_t *vconfig, const cfg_obj_t *key,
291135446Strhodes			 dns_keytable_t *keytable, isc_mem_t *mctx)
292135446Strhodes{
293135446Strhodes	dns_rdataclass_t viewclass;
294135446Strhodes	dns_rdata_dnskey_t keystruct;
295135446Strhodes	isc_uint32_t flags, proto, alg;
296165071Sdougb	const char *keystr, *keynamestr;
297135446Strhodes	unsigned char keydata[4096];
298135446Strhodes	isc_buffer_t keydatabuf;
299135446Strhodes	unsigned char rrdata[4096];
300135446Strhodes	isc_buffer_t rrdatabuf;
301135446Strhodes	isc_region_t r;
302135446Strhodes	dns_fixedname_t fkeyname;
303135446Strhodes	dns_name_t *keyname;
304135446Strhodes	isc_buffer_t namebuf;
305135446Strhodes	isc_result_t result;
306135446Strhodes	dst_key_t *dstkey = NULL;
307135446Strhodes
308135446Strhodes	flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags"));
309135446Strhodes	proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol"));
310135446Strhodes	alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm"));
311135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
312135446Strhodes	keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
313135446Strhodes
314135446Strhodes	if (vconfig == NULL)
315135446Strhodes		viewclass = dns_rdataclass_in;
316135446Strhodes	else {
317165071Sdougb		const cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class");
318135446Strhodes		CHECK(ns_config_getclass(classobj, dns_rdataclass_in,
319135446Strhodes					 &viewclass));
320135446Strhodes	}
321135446Strhodes	keystruct.common.rdclass = viewclass;
322135446Strhodes	keystruct.common.rdtype = dns_rdatatype_dnskey;
323135446Strhodes	/*
324135446Strhodes	 * The key data in keystruct is not dynamically allocated.
325135446Strhodes	 */
326135446Strhodes	keystruct.mctx = NULL;
327135446Strhodes
328135446Strhodes	ISC_LINK_INIT(&keystruct.common, link);
329135446Strhodes
330135446Strhodes	if (flags > 0xffff)
331135446Strhodes		CHECKM(ISC_R_RANGE, "key flags");
332135446Strhodes	if (proto > 0xff)
333135446Strhodes		CHECKM(ISC_R_RANGE, "key protocol");
334135446Strhodes	if (alg > 0xff)
335135446Strhodes		CHECKM(ISC_R_RANGE, "key algorithm");
336135446Strhodes	keystruct.flags = (isc_uint16_t)flags;
337135446Strhodes	keystruct.protocol = (isc_uint8_t)proto;
338135446Strhodes	keystruct.algorithm = (isc_uint8_t)alg;
339135446Strhodes
340135446Strhodes	isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
341135446Strhodes	isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
342135446Strhodes
343135446Strhodes	keystr = cfg_obj_asstring(cfg_tuple_get(key, "key"));
344135446Strhodes	CHECK(isc_base64_decodestring(keystr, &keydatabuf));
345135446Strhodes	isc_buffer_usedregion(&keydatabuf, &r);
346135446Strhodes	keystruct.datalen = r.length;
347135446Strhodes	keystruct.data = r.base;
348135446Strhodes
349170222Sdougb	if ((keystruct.algorithm == DST_ALG_RSASHA1 ||
350170222Sdougb	     keystruct.algorithm == DST_ALG_RSAMD5) &&
351170222Sdougb	    r.length > 1 && r.base[0] == 1 && r.base[1] == 3)
352170222Sdougb		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
353170222Sdougb			    "trusted key '%s' has a weak exponent",
354170222Sdougb			    keynamestr);
355170222Sdougb
356135446Strhodes	CHECK(dns_rdata_fromstruct(NULL,
357135446Strhodes				   keystruct.common.rdclass,
358135446Strhodes				   keystruct.common.rdtype,
359135446Strhodes				   &keystruct, &rrdatabuf));
360135446Strhodes	dns_fixedname_init(&fkeyname);
361135446Strhodes	isc_buffer_init(&namebuf, keynamestr, strlen(keynamestr));
362135446Strhodes	isc_buffer_add(&namebuf, strlen(keynamestr));
363135446Strhodes	CHECK(dns_name_fromtext(keyname, &namebuf,
364135446Strhodes				dns_rootname, ISC_FALSE,
365135446Strhodes				NULL));
366135446Strhodes	CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf,
367135446Strhodes			      mctx, &dstkey));
368135446Strhodes
369135446Strhodes	CHECK(dns_keytable_add(keytable, &dstkey));
370135446Strhodes	INSIST(dstkey == NULL);
371135446Strhodes	return (ISC_R_SUCCESS);
372135446Strhodes
373135446Strhodes cleanup:
374135446Strhodes	if (result == DST_R_NOCRYPTO) {
375135446Strhodes		cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
376135446Strhodes			    "ignoring trusted key for '%s': no crypto support",
377135446Strhodes			    keynamestr);
378135446Strhodes		result = ISC_R_SUCCESS;
379135446Strhodes	} else {
380135446Strhodes		cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
381135446Strhodes			    "configuring trusted key for '%s': %s",
382135446Strhodes			    keynamestr, isc_result_totext(result));
383135446Strhodes		result = ISC_R_FAILURE;
384135446Strhodes	}
385135446Strhodes
386135446Strhodes	if (dstkey != NULL)
387135446Strhodes		dst_key_free(&dstkey);
388135446Strhodes
389135446Strhodes	return (result);
390135446Strhodes}
391135446Strhodes
392170222Sdougb/*%
393135446Strhodes * Configure DNSSEC keys for a view.  Currently used only for
394135446Strhodes * the security roots.
395135446Strhodes *
396135446Strhodes * The per-view configuration values and the server-global defaults are read
397135446Strhodes * from 'vconfig' and 'config'.  The variable to be configured is '*target'.
398135446Strhodes */
399135446Strhodesstatic isc_result_t
400165071Sdougbconfigure_view_dnsseckeys(const cfg_obj_t *vconfig, const cfg_obj_t *config,
401135446Strhodes			  isc_mem_t *mctx, dns_keytable_t **target)
402135446Strhodes{
403135446Strhodes	isc_result_t result;
404165071Sdougb	const cfg_obj_t *keys = NULL;
405165071Sdougb	const cfg_obj_t *voptions = NULL;
406165071Sdougb	const cfg_listelt_t *element, *element2;
407165071Sdougb	const cfg_obj_t *keylist;
408165071Sdougb	const cfg_obj_t *key;
409135446Strhodes	dns_keytable_t *keytable = NULL;
410135446Strhodes
411135446Strhodes	CHECK(dns_keytable_create(mctx, &keytable));
412135446Strhodes
413135446Strhodes	if (vconfig != NULL)
414135446Strhodes		voptions = cfg_tuple_get(vconfig, "options");
415135446Strhodes
416135446Strhodes	keys = NULL;
417135446Strhodes	if (voptions != NULL)
418135446Strhodes		(void)cfg_map_get(voptions, "trusted-keys", &keys);
419135446Strhodes	if (keys == NULL)
420135446Strhodes		(void)cfg_map_get(config, "trusted-keys", &keys);
421135446Strhodes
422135446Strhodes	for (element = cfg_list_first(keys);
423135446Strhodes	     element != NULL;
424135446Strhodes	     element = cfg_list_next(element))
425135446Strhodes	{
426135446Strhodes		keylist = cfg_listelt_value(element);
427135446Strhodes		for (element2 = cfg_list_first(keylist);
428135446Strhodes		     element2 != NULL;
429135446Strhodes		     element2 = cfg_list_next(element2))
430135446Strhodes		{
431135446Strhodes			key = cfg_listelt_value(element2);
432135446Strhodes			CHECK(configure_view_dnsseckey(vconfig, key,
433135446Strhodes						       keytable, mctx));
434135446Strhodes		}
435135446Strhodes	}
436135446Strhodes
437135446Strhodes	dns_keytable_detach(target);
438135446Strhodes	*target = keytable; /* Transfer ownership. */
439135446Strhodes	keytable = NULL;
440135446Strhodes	result = ISC_R_SUCCESS;
441135446Strhodes
442135446Strhodes cleanup:
443135446Strhodes	return (result);
444135446Strhodes}
445135446Strhodes
446135446Strhodesstatic isc_result_t
447165071Sdougbmustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver)
448135446Strhodes{
449165071Sdougb	const cfg_listelt_t *element;
450165071Sdougb	const cfg_obj_t *obj;
451135446Strhodes	const char *str;
452135446Strhodes	dns_fixedname_t fixed;
453135446Strhodes	dns_name_t *name;
454135446Strhodes	isc_boolean_t value;
455135446Strhodes	isc_result_t result;
456135446Strhodes	isc_buffer_t b;
457135446Strhodes
458135446Strhodes	dns_fixedname_init(&fixed);
459135446Strhodes	name = dns_fixedname_name(&fixed);
460135446Strhodes	for (element = cfg_list_first(mbs);
461135446Strhodes	     element != NULL;
462135446Strhodes	     element = cfg_list_next(element))
463135446Strhodes	{
464135446Strhodes		obj = cfg_listelt_value(element);
465135446Strhodes		str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
466135446Strhodes		isc_buffer_init(&b, str, strlen(str));
467135446Strhodes		isc_buffer_add(&b, strlen(str));
468135446Strhodes		CHECK(dns_name_fromtext(name, &b, dns_rootname,
469135446Strhodes					ISC_FALSE, NULL));
470135446Strhodes		value = cfg_obj_asboolean(cfg_tuple_get(obj, "value"));
471135446Strhodes		CHECK(dns_resolver_setmustbesecure(resolver, name, value));
472135446Strhodes	}
473135446Strhodes
474135446Strhodes	result = ISC_R_SUCCESS;
475135446Strhodes
476135446Strhodes cleanup:
477135446Strhodes	return (result);
478135446Strhodes}
479135446Strhodes
480170222Sdougb/*%
481135446Strhodes * Get a dispatch appropriate for the resolver of a given view.
482135446Strhodes */
483135446Strhodesstatic isc_result_t
484165071Sdougbget_view_querysource_dispatch(const cfg_obj_t **maps,
485135446Strhodes			      int af, dns_dispatch_t **dispatchp)
486135446Strhodes{
487135446Strhodes	isc_result_t result;
488135446Strhodes	dns_dispatch_t *disp;
489135446Strhodes	isc_sockaddr_t sa;
490135446Strhodes	unsigned int attrs, attrmask;
491165071Sdougb	const cfg_obj_t *obj = NULL;
492135446Strhodes
493135446Strhodes	/*
494135446Strhodes	 * Make compiler happy.
495135446Strhodes	 */
496135446Strhodes	result = ISC_R_FAILURE;
497135446Strhodes
498135446Strhodes	switch (af) {
499135446Strhodes	case AF_INET:
500135446Strhodes		result = ns_config_get(maps, "query-source", &obj);
501135446Strhodes		INSIST(result == ISC_R_SUCCESS);
502135446Strhodes		break;
503135446Strhodes	case AF_INET6:
504135446Strhodes		result = ns_config_get(maps, "query-source-v6", &obj);
505135446Strhodes		INSIST(result == ISC_R_SUCCESS);
506135446Strhodes		break;
507135446Strhodes	default:
508135446Strhodes		INSIST(0);
509135446Strhodes	}
510135446Strhodes
511135446Strhodes	sa = *(cfg_obj_assockaddr(obj));
512135446Strhodes	INSIST(isc_sockaddr_pf(&sa) == af);
513135446Strhodes
514135446Strhodes	/*
515135446Strhodes	 * If we don't support this address family, we're done!
516135446Strhodes	 */
517135446Strhodes	switch (af) {
518135446Strhodes	case AF_INET:
519135446Strhodes		result = isc_net_probeipv4();
520135446Strhodes		break;
521135446Strhodes	case AF_INET6:
522135446Strhodes		result = isc_net_probeipv6();
523135446Strhodes		break;
524135446Strhodes	default:
525135446Strhodes		INSIST(0);
526135446Strhodes	}
527135446Strhodes	if (result != ISC_R_SUCCESS)
528135446Strhodes		return (ISC_R_SUCCESS);
529135446Strhodes
530135446Strhodes	/*
531135446Strhodes	 * Try to find a dispatcher that we can share.
532135446Strhodes	 */
533135446Strhodes	attrs = 0;
534135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
535135446Strhodes	switch (af) {
536135446Strhodes	case AF_INET:
537135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
538135446Strhodes		break;
539135446Strhodes	case AF_INET6:
540135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
541135446Strhodes		break;
542135446Strhodes	}
543180477Sdougb
544180477Sdougb	if (isc_sockaddr_getport(&sa) != 0) {
545180477Sdougb		INSIST(obj != NULL);
546180477Sdougb		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_INFO,
547180477Sdougb			    "using specific query-source port suppresses port "
548180477Sdougb			    "randomization and can be insecure.");
549180477Sdougb	}
550180477Sdougb
551135446Strhodes	attrmask = 0;
552135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP;
553135446Strhodes	attrmask |= DNS_DISPATCHATTR_TCP;
554135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4;
555135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV6;
556135446Strhodes
557135446Strhodes	disp = NULL;
558135446Strhodes	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
559135446Strhodes				     ns_g_taskmgr, &sa, 4096,
560180477Sdougb				     1024, 32768, 16411, 16433,
561135446Strhodes				     attrs, attrmask, &disp);
562135446Strhodes	if (result != ISC_R_SUCCESS) {
563135446Strhodes		isc_sockaddr_t any;
564135446Strhodes		char buf[ISC_SOCKADDR_FORMATSIZE];
565135446Strhodes
566135446Strhodes		switch (af) {
567135446Strhodes		case AF_INET:
568135446Strhodes			isc_sockaddr_any(&any);
569135446Strhodes			break;
570135446Strhodes		case AF_INET6:
571135446Strhodes			isc_sockaddr_any6(&any);
572135446Strhodes			break;
573135446Strhodes		}
574135446Strhodes		if (isc_sockaddr_equal(&sa, &any))
575135446Strhodes			return (ISC_R_SUCCESS);
576135446Strhodes		isc_sockaddr_format(&sa, buf, sizeof(buf));
577135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
578135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
579135446Strhodes			      "could not get query source dispatcher (%s)",
580135446Strhodes			      buf);
581135446Strhodes		return (result);
582135446Strhodes	}
583135446Strhodes
584135446Strhodes	*dispatchp = disp;
585135446Strhodes
586135446Strhodes	return (ISC_R_SUCCESS);
587135446Strhodes}
588135446Strhodes
589135446Strhodesstatic isc_result_t
590165071Sdougbconfigure_order(dns_order_t *order, const cfg_obj_t *ent) {
591135446Strhodes	dns_rdataclass_t rdclass;
592135446Strhodes	dns_rdatatype_t rdtype;
593165071Sdougb	const cfg_obj_t *obj;
594135446Strhodes	dns_fixedname_t fixed;
595135446Strhodes	unsigned int mode = 0;
596135446Strhodes	const char *str;
597135446Strhodes	isc_buffer_t b;
598135446Strhodes	isc_result_t result;
599143731Sdougb	isc_boolean_t addroot;
600135446Strhodes
601135446Strhodes	result = ns_config_getclass(cfg_tuple_get(ent, "class"),
602135446Strhodes				    dns_rdataclass_any, &rdclass);
603135446Strhodes	if (result != ISC_R_SUCCESS)
604135446Strhodes		return (result);
605135446Strhodes
606135446Strhodes	result = ns_config_gettype(cfg_tuple_get(ent, "type"),
607135446Strhodes				   dns_rdatatype_any, &rdtype);
608135446Strhodes	if (result != ISC_R_SUCCESS)
609135446Strhodes		return (result);
610135446Strhodes
611135446Strhodes	obj = cfg_tuple_get(ent, "name");
612135446Strhodes	if (cfg_obj_isstring(obj))
613135446Strhodes		str = cfg_obj_asstring(obj);
614135446Strhodes	else
615135446Strhodes		str = "*";
616143731Sdougb	addroot = ISC_TF(strcmp(str, "*") == 0);
617135446Strhodes	isc_buffer_init(&b, str, strlen(str));
618135446Strhodes	isc_buffer_add(&b, strlen(str));
619135446Strhodes	dns_fixedname_init(&fixed);
620135446Strhodes	result = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
621143731Sdougb				   dns_rootname, ISC_FALSE, NULL);
622135446Strhodes	if (result != ISC_R_SUCCESS)
623135446Strhodes		return (result);
624135446Strhodes
625135446Strhodes	obj = cfg_tuple_get(ent, "ordering");
626135446Strhodes	INSIST(cfg_obj_isstring(obj));
627135446Strhodes	str = cfg_obj_asstring(obj);
628135446Strhodes	if (!strcasecmp(str, "fixed"))
629135446Strhodes		mode = DNS_RDATASETATTR_FIXEDORDER;
630135446Strhodes	else if (!strcasecmp(str, "random"))
631135446Strhodes		mode = DNS_RDATASETATTR_RANDOMIZE;
632135446Strhodes	else if (!strcasecmp(str, "cyclic"))
633135446Strhodes		mode = 0;
634135446Strhodes	else
635135446Strhodes		INSIST(0);
636135446Strhodes
637143731Sdougb	/*
638143731Sdougb	 * "*" should match everything including the root (BIND 8 compat).
639143731Sdougb	 * As dns_name_matcheswildcard(".", "*.") returns FALSE add a
640165071Sdougb	 * explicit entry for "." when the name is "*".
641143731Sdougb	 */
642143731Sdougb	if (addroot) {
643143731Sdougb		result = dns_order_add(order, dns_rootname,
644143731Sdougb				       rdtype, rdclass, mode);
645143731Sdougb		if (result != ISC_R_SUCCESS)
646143731Sdougb			return (result);
647143731Sdougb	}
648143731Sdougb
649135446Strhodes	return (dns_order_add(order, dns_fixedname_name(&fixed),
650135446Strhodes			      rdtype, rdclass, mode));
651135446Strhodes}
652135446Strhodes
653135446Strhodesstatic isc_result_t
654165071Sdougbconfigure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
655135446Strhodes	isc_netaddr_t na;
656135446Strhodes	dns_peer_t *peer;
657165071Sdougb	const cfg_obj_t *obj;
658165071Sdougb	const char *str;
659135446Strhodes	isc_result_t result;
660170222Sdougb	unsigned int prefixlen;
661135446Strhodes
662170222Sdougb	cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen);
663135446Strhodes
664135446Strhodes	peer = NULL;
665135446Strhodes	result = dns_peer_new(mctx, &na, &peer);
666135446Strhodes	if (result != ISC_R_SUCCESS)
667135446Strhodes		return (result);
668135446Strhodes
669135446Strhodes	obj = NULL;
670135446Strhodes	(void)cfg_map_get(cpeer, "bogus", &obj);
671135446Strhodes	if (obj != NULL)
672135446Strhodes		CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj)));
673135446Strhodes
674135446Strhodes	obj = NULL;
675135446Strhodes	(void)cfg_map_get(cpeer, "provide-ixfr", &obj);
676135446Strhodes	if (obj != NULL)
677135446Strhodes		CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
678135446Strhodes
679135446Strhodes	obj = NULL;
680135446Strhodes	(void)cfg_map_get(cpeer, "request-ixfr", &obj);
681135446Strhodes	if (obj != NULL)
682135446Strhodes		CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj)));
683135446Strhodes
684135446Strhodes	obj = NULL;
685135446Strhodes	(void)cfg_map_get(cpeer, "edns", &obj);
686135446Strhodes	if (obj != NULL)
687135446Strhodes		CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj)));
688135446Strhodes
689135446Strhodes	obj = NULL;
690170222Sdougb	(void)cfg_map_get(cpeer, "edns-udp-size", &obj);
691170222Sdougb	if (obj != NULL) {
692170222Sdougb		isc_uint32_t udpsize = cfg_obj_asuint32(obj);
693170222Sdougb		if (udpsize < 512)
694170222Sdougb			udpsize = 512;
695170222Sdougb		if (udpsize > 4096)
696170222Sdougb			udpsize = 4096;
697170222Sdougb		CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize));
698170222Sdougb	}
699170222Sdougb
700170222Sdougb	obj = NULL;
701170222Sdougb	(void)cfg_map_get(cpeer, "max-udp-size", &obj);
702170222Sdougb	if (obj != NULL) {
703170222Sdougb		isc_uint32_t udpsize = cfg_obj_asuint32(obj);
704170222Sdougb		if (udpsize < 512)
705170222Sdougb			udpsize = 512;
706170222Sdougb		if (udpsize > 4096)
707170222Sdougb			udpsize = 4096;
708170222Sdougb		CHECK(dns_peer_setmaxudp(peer, (isc_uint16_t)udpsize));
709170222Sdougb	}
710170222Sdougb
711170222Sdougb	obj = NULL;
712135446Strhodes	(void)cfg_map_get(cpeer, "transfers", &obj);
713135446Strhodes	if (obj != NULL)
714135446Strhodes		CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj)));
715135446Strhodes
716135446Strhodes	obj = NULL;
717135446Strhodes	(void)cfg_map_get(cpeer, "transfer-format", &obj);
718135446Strhodes	if (obj != NULL) {
719135446Strhodes		str = cfg_obj_asstring(obj);
720135446Strhodes		if (strcasecmp(str, "many-answers") == 0)
721135446Strhodes			CHECK(dns_peer_settransferformat(peer,
722135446Strhodes							 dns_many_answers));
723135446Strhodes		else if (strcasecmp(str, "one-answer") == 0)
724135446Strhodes			CHECK(dns_peer_settransferformat(peer,
725135446Strhodes							 dns_one_answer));
726135446Strhodes		else
727135446Strhodes			INSIST(0);
728135446Strhodes	}
729135446Strhodes
730135446Strhodes	obj = NULL;
731135446Strhodes	(void)cfg_map_get(cpeer, "keys", &obj);
732135446Strhodes	if (obj != NULL) {
733135446Strhodes		result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj));
734135446Strhodes		if (result != ISC_R_SUCCESS)
735135446Strhodes			goto cleanup;
736135446Strhodes	}
737135446Strhodes
738135446Strhodes	obj = NULL;
739170222Sdougb	if (na.family == AF_INET)
740135446Strhodes		(void)cfg_map_get(cpeer, "transfer-source", &obj);
741135446Strhodes	else
742135446Strhodes		(void)cfg_map_get(cpeer, "transfer-source-v6", &obj);
743135446Strhodes	if (obj != NULL) {
744135446Strhodes		result = dns_peer_settransfersource(peer,
745135446Strhodes						    cfg_obj_assockaddr(obj));
746135446Strhodes		if (result != ISC_R_SUCCESS)
747135446Strhodes			goto cleanup;
748170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
749135446Strhodes	}
750170222Sdougb
751170222Sdougb	obj = NULL;
752170222Sdougb	if (na.family == AF_INET)
753170222Sdougb		(void)cfg_map_get(cpeer, "notify-source", &obj);
754170222Sdougb	else
755170222Sdougb		(void)cfg_map_get(cpeer, "notify-source-v6", &obj);
756170222Sdougb	if (obj != NULL) {
757170222Sdougb		result = dns_peer_setnotifysource(peer,
758170222Sdougb						  cfg_obj_assockaddr(obj));
759170222Sdougb		if (result != ISC_R_SUCCESS)
760170222Sdougb			goto cleanup;
761170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
762170222Sdougb	}
763170222Sdougb
764170222Sdougb	obj = NULL;
765170222Sdougb	if (na.family == AF_INET)
766170222Sdougb		(void)cfg_map_get(cpeer, "query-source", &obj);
767170222Sdougb	else
768170222Sdougb		(void)cfg_map_get(cpeer, "query-source-v6", &obj);
769170222Sdougb	if (obj != NULL) {
770170222Sdougb		result = dns_peer_setquerysource(peer,
771170222Sdougb						 cfg_obj_assockaddr(obj));
772170222Sdougb		if (result != ISC_R_SUCCESS)
773170222Sdougb			goto cleanup;
774170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
775170222Sdougb	}
776170222Sdougb
777135446Strhodes	*peerp = peer;
778135446Strhodes	return (ISC_R_SUCCESS);
779135446Strhodes
780135446Strhodes cleanup:
781135446Strhodes	dns_peer_detach(&peer);
782135446Strhodes	return (result);
783135446Strhodes}
784135446Strhodes
785135446Strhodesstatic isc_result_t
786165071Sdougbdisable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
787135446Strhodes	isc_result_t result;
788165071Sdougb	const cfg_obj_t *algorithms;
789165071Sdougb	const cfg_listelt_t *element;
790135446Strhodes	const char *str;
791135446Strhodes	dns_fixedname_t fixed;
792135446Strhodes	dns_name_t *name;
793135446Strhodes	isc_buffer_t b;
794135446Strhodes
795135446Strhodes	dns_fixedname_init(&fixed);
796135446Strhodes	name = dns_fixedname_name(&fixed);
797135446Strhodes	str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
798135446Strhodes	isc_buffer_init(&b, str, strlen(str));
799135446Strhodes	isc_buffer_add(&b, strlen(str));
800135446Strhodes	CHECK(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL));
801135446Strhodes
802135446Strhodes	algorithms = cfg_tuple_get(disabled, "algorithms");
803135446Strhodes	for (element = cfg_list_first(algorithms);
804135446Strhodes	     element != NULL;
805135446Strhodes	     element = cfg_list_next(element))
806135446Strhodes	{
807135446Strhodes		isc_textregion_t r;
808135446Strhodes		dns_secalg_t alg;
809135446Strhodes
810165071Sdougb		DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
811135446Strhodes		r.length = strlen(r.base);
812135446Strhodes
813135446Strhodes		result = dns_secalg_fromtext(&alg, &r);
814135446Strhodes		if (result != ISC_R_SUCCESS) {
815135446Strhodes			isc_uint8_t ui;
816135446Strhodes			result = isc_parse_uint8(&ui, r.base, 10);
817135446Strhodes			alg = ui;
818135446Strhodes		}
819135446Strhodes		if (result != ISC_R_SUCCESS) {
820135446Strhodes			cfg_obj_log(cfg_listelt_value(element),
821135446Strhodes				    ns_g_lctx, ISC_LOG_ERROR,
822135446Strhodes				    "invalid algorithm");
823135446Strhodes			CHECK(result);
824135446Strhodes		}
825135446Strhodes		CHECK(dns_resolver_disable_algorithm(resolver, name, alg));
826135446Strhodes	}
827135446Strhodes cleanup:
828135446Strhodes	return (result);
829135446Strhodes}
830135446Strhodes
831170222Sdougbstatic isc_boolean_t
832170222Sdougbon_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) {
833170222Sdougb	const cfg_listelt_t *element;
834170222Sdougb	dns_fixedname_t fixed;
835170222Sdougb	dns_name_t *name;
836170222Sdougb	isc_result_t result;
837170222Sdougb	const cfg_obj_t *value;
838170222Sdougb	const char *str;
839170222Sdougb	isc_buffer_t b;
840170222Sdougb
841170222Sdougb	dns_fixedname_init(&fixed);
842170222Sdougb	name = dns_fixedname_name(&fixed);
843170222Sdougb
844170222Sdougb	for (element = cfg_list_first(disablelist);
845170222Sdougb	     element != NULL;
846170222Sdougb	     element = cfg_list_next(element))
847170222Sdougb	{
848170222Sdougb		value = cfg_listelt_value(element);
849170222Sdougb		str = cfg_obj_asstring(value);
850170222Sdougb		isc_buffer_init(&b, str, strlen(str));
851170222Sdougb		isc_buffer_add(&b, strlen(str));
852170222Sdougb		result = dns_name_fromtext(name, &b, dns_rootname,
853170222Sdougb					   ISC_TRUE, NULL);
854170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
855170222Sdougb		if (dns_name_equal(name, zonename))
856170222Sdougb			return (ISC_TRUE);
857170222Sdougb	}
858170222Sdougb	return (ISC_FALSE);
859170222Sdougb}
860170222Sdougb
861170222Sdougbstatic void
862170222Sdougbcheck_dbtype(dns_zone_t **zonep, unsigned int dbtypec, const char **dbargv,
863170222Sdougb	     isc_mem_t *mctx)
864170222Sdougb{
865170222Sdougb	char **argv = NULL;
866170222Sdougb	unsigned int i;
867170222Sdougb	isc_result_t result;
868170222Sdougb
869170222Sdougb	result = dns_zone_getdbtype(*zonep, &argv, mctx);
870170222Sdougb	if (result != ISC_R_SUCCESS) {
871170222Sdougb		dns_zone_detach(zonep);
872170222Sdougb		return;
873170222Sdougb	}
874170222Sdougb
875170222Sdougb	/*
876170222Sdougb	 * Check that all the arguments match.
877170222Sdougb	 */
878170222Sdougb	for (i = 0; i < dbtypec; i++)
879170222Sdougb		if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) {
880170222Sdougb			dns_zone_detach(zonep);
881170222Sdougb			break;
882170222Sdougb		}
883170222Sdougb
884170222Sdougb	/*
885170222Sdougb	 * Check that there are not extra arguments.
886170222Sdougb	 */
887170222Sdougb	if (i == dbtypec && argv[i] != NULL)
888170222Sdougb		dns_zone_detach(zonep);
889170222Sdougb	isc_mem_free(mctx, argv);
890170222Sdougb}
891170222Sdougb
892170222Sdougb
893135446Strhodes/*
894135446Strhodes * Configure 'view' according to 'vconfig', taking defaults from 'config'
895135446Strhodes * where values are missing in 'vconfig'.
896135446Strhodes *
897135446Strhodes * When configuring the default view, 'vconfig' will be NULL and the
898135446Strhodes * global defaults in 'config' used exclusively.
899135446Strhodes */
900135446Strhodesstatic isc_result_t
901165071Sdougbconfigure_view(dns_view_t *view, const cfg_obj_t *config,
902170222Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx,
903170222Sdougb	       cfg_aclconfctx_t *actx, isc_boolean_t need_hints)
904135446Strhodes{
905165071Sdougb	const cfg_obj_t *maps[4];
906165071Sdougb	const cfg_obj_t *cfgmaps[3];
907165071Sdougb	const cfg_obj_t *options = NULL;
908165071Sdougb	const cfg_obj_t *voptions = NULL;
909165071Sdougb	const cfg_obj_t *forwardtype;
910165071Sdougb	const cfg_obj_t *forwarders;
911165071Sdougb	const cfg_obj_t *alternates;
912165071Sdougb	const cfg_obj_t *zonelist;
913170222Sdougb#ifdef DLZ
914170222Sdougb 	const cfg_obj_t *dlz;
915170222Sdougb 	unsigned int dlzargc;
916170222Sdougb 	char **dlzargv;
917170222Sdougb#endif
918165071Sdougb	const cfg_obj_t *disabled;
919165071Sdougb	const cfg_obj_t *obj;
920165071Sdougb	const cfg_listelt_t *element;
921135446Strhodes	in_port_t port;
922135446Strhodes	dns_cache_t *cache = NULL;
923135446Strhodes	isc_result_t result;
924135446Strhodes	isc_uint32_t max_adb_size;
925135446Strhodes	isc_uint32_t max_cache_size;
926170222Sdougb	isc_uint32_t max_acache_size;
927135446Strhodes	isc_uint32_t lame_ttl;
928135446Strhodes	dns_tsig_keyring_t *ring;
929135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
930135446Strhodes	isc_mem_t *cmctx;
931135446Strhodes	dns_dispatch_t *dispatch4 = NULL;
932135446Strhodes	dns_dispatch_t *dispatch6 = NULL;
933135446Strhodes	isc_boolean_t reused_cache = ISC_FALSE;
934135446Strhodes	int i;
935135446Strhodes	const char *str;
936135446Strhodes	dns_order_t *order = NULL;
937135446Strhodes	isc_uint32_t udpsize;
938135446Strhodes	unsigned int check = 0;
939170222Sdougb	dns_zone_t *zone = NULL;
940170222Sdougb	isc_uint32_t max_clients_per_query;
941170222Sdougb	const char *sep = ": view ";
942170222Sdougb	const char *viewname = view->name;
943170222Sdougb	const char *forview = " for view ";
944170222Sdougb	isc_boolean_t rfc1918;
945170222Sdougb	isc_boolean_t empty_zones_enable;
946170222Sdougb	const cfg_obj_t *disablelist = NULL;
947135446Strhodes
948135446Strhodes	REQUIRE(DNS_VIEW_VALID(view));
949135446Strhodes
950135446Strhodes	cmctx = NULL;
951135446Strhodes
952135446Strhodes	if (config != NULL)
953135446Strhodes		(void)cfg_map_get(config, "options", &options);
954135446Strhodes
955135446Strhodes	i = 0;
956135446Strhodes	if (vconfig != NULL) {
957135446Strhodes		voptions = cfg_tuple_get(vconfig, "options");
958135446Strhodes		maps[i++] = voptions;
959135446Strhodes	}
960135446Strhodes	if (options != NULL)
961135446Strhodes		maps[i++] = options;
962135446Strhodes	maps[i++] = ns_g_defaults;
963135446Strhodes	maps[i] = NULL;
964135446Strhodes
965135446Strhodes	i = 0;
966135446Strhodes	if (voptions != NULL)
967135446Strhodes		cfgmaps[i++] = voptions;
968135446Strhodes	if (config != NULL)
969135446Strhodes		cfgmaps[i++] = config;
970135446Strhodes	cfgmaps[i] = NULL;
971135446Strhodes
972170222Sdougb	if (!strcmp(viewname, "_default")) {
973170222Sdougb		sep = "";
974170222Sdougb		viewname = "";
975170222Sdougb		forview = "";
976170222Sdougb	}
977170222Sdougb
978135446Strhodes	/*
979135446Strhodes	 * Set the view's port number for outgoing queries.
980135446Strhodes	 */
981135446Strhodes	CHECKM(ns_config_getport(config, &port), "port");
982135446Strhodes	dns_view_setdstport(view, port);
983135446Strhodes
984135446Strhodes	/*
985170222Sdougb	 * Create additional cache for this view and zones under the view
986170222Sdougb	 * if explicitly enabled.
987170222Sdougb	 * XXX950 default to on.
988170222Sdougb	 */
989170222Sdougb	obj = NULL;
990170222Sdougb	(void)ns_config_get(maps, "acache-enable", &obj);
991170222Sdougb	if (obj != NULL && cfg_obj_asboolean(obj)) {
992170222Sdougb		cmctx = NULL;
993170222Sdougb		CHECK(isc_mem_create(0, 0, &cmctx));
994170222Sdougb		CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr,
995170222Sdougb					ns_g_timermgr));
996170222Sdougb		isc_mem_detach(&cmctx);
997170222Sdougb	}
998170222Sdougb	if (view->acache != NULL) {
999170222Sdougb		obj = NULL;
1000170222Sdougb		result = ns_config_get(maps, "acache-cleaning-interval", &obj);
1001170222Sdougb		INSIST(result == ISC_R_SUCCESS);
1002170222Sdougb		dns_acache_setcleaninginterval(view->acache,
1003170222Sdougb					       cfg_obj_asuint32(obj) * 60);
1004170222Sdougb
1005170222Sdougb		obj = NULL;
1006170222Sdougb		result = ns_config_get(maps, "max-acache-size", &obj);
1007170222Sdougb		INSIST(result == ISC_R_SUCCESS);
1008170222Sdougb		if (cfg_obj_isstring(obj)) {
1009170222Sdougb			str = cfg_obj_asstring(obj);
1010170222Sdougb			INSIST(strcasecmp(str, "unlimited") == 0);
1011170222Sdougb			max_acache_size = ISC_UINT32_MAX;
1012170222Sdougb		} else {
1013170222Sdougb			isc_resourcevalue_t value;
1014170222Sdougb
1015170222Sdougb			value = cfg_obj_asuint64(obj);
1016170222Sdougb			if (value > ISC_UINT32_MAX) {
1017170222Sdougb				cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
1018170222Sdougb					    "'max-acache-size "
1019170222Sdougb					    "%" ISC_PRINT_QUADFORMAT
1020170222Sdougb					    "d' is too large",
1021170222Sdougb					    value);
1022170222Sdougb				result = ISC_R_RANGE;
1023170222Sdougb				goto cleanup;
1024170222Sdougb			}
1025170222Sdougb			max_acache_size = (isc_uint32_t)value;
1026170222Sdougb		}
1027170222Sdougb		dns_acache_setcachesize(view->acache, max_acache_size);
1028170222Sdougb	}
1029170222Sdougb
1030170222Sdougb	/*
1031135446Strhodes	 * Configure the zones.
1032135446Strhodes	 */
1033135446Strhodes	zonelist = NULL;
1034135446Strhodes	if (voptions != NULL)
1035135446Strhodes		(void)cfg_map_get(voptions, "zone", &zonelist);
1036135446Strhodes	else
1037135446Strhodes		(void)cfg_map_get(config, "zone", &zonelist);
1038135446Strhodes	for (element = cfg_list_first(zonelist);
1039135446Strhodes	     element != NULL;
1040135446Strhodes	     element = cfg_list_next(element))
1041135446Strhodes	{
1042165071Sdougb		const cfg_obj_t *zconfig = cfg_listelt_value(element);
1043135446Strhodes		CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
1044135446Strhodes				     actx));
1045135446Strhodes	}
1046135446Strhodes
1047170222Sdougb#ifdef DLZ
1048135446Strhodes	/*
1049170222Sdougb	 * Create Dynamically Loadable Zone driver.
1050170222Sdougb	 */
1051170222Sdougb	dlz = NULL;
1052170222Sdougb	if (voptions != NULL)
1053170222Sdougb		(void)cfg_map_get(voptions, "dlz", &dlz);
1054170222Sdougb	else
1055170222Sdougb		(void)cfg_map_get(config, "dlz", &dlz);
1056170222Sdougb
1057170222Sdougb	obj = NULL;
1058170222Sdougb	if (dlz != NULL) {
1059170222Sdougb		(void)cfg_map_get(cfg_tuple_get(dlz, "options"),
1060170222Sdougb				  "database", &obj);
1061170222Sdougb		if (obj != NULL) {
1062170222Sdougb			char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
1063170222Sdougb			if (s == NULL) {
1064170222Sdougb				result = ISC_R_NOMEMORY;
1065170222Sdougb				goto cleanup;
1066170222Sdougb			}
1067170222Sdougb
1068170222Sdougb			result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv);
1069170222Sdougb			if (result != ISC_R_SUCCESS) {
1070170222Sdougb				isc_mem_free(mctx, s);
1071170222Sdougb				goto cleanup;
1072170222Sdougb			}
1073170222Sdougb
1074170222Sdougb			obj = cfg_tuple_get(dlz, "name");
1075170222Sdougb			result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
1076170222Sdougb					       dlzargv[0], dlzargc, dlzargv,
1077170222Sdougb					       &view->dlzdatabase);
1078170222Sdougb			isc_mem_free(mctx, s);
1079170222Sdougb			isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
1080170222Sdougb			if (result != ISC_R_SUCCESS)
1081170222Sdougb				goto cleanup;
1082170222Sdougb		}
1083170222Sdougb	}
1084170222Sdougb#endif
1085170222Sdougb
1086170222Sdougb	/*
1087135446Strhodes	 * Configure the view's cache.  Try to reuse an existing
1088135446Strhodes	 * cache if possible, otherwise create a new cache.
1089135446Strhodes	 * Note that the ADB is not preserved in either case.
1090135446Strhodes	 *
1091135446Strhodes	 * XXX Determining when it is safe to reuse a cache is
1092135446Strhodes	 * tricky.  When the view's configuration changes, the cached
1093135446Strhodes	 * data may become invalid because it reflects our old
1094135446Strhodes	 * view of the world.  As more view attributes become
1095135446Strhodes	 * configurable, we will have to add code here to check
1096135446Strhodes	 * whether they have changed in ways that could
1097135446Strhodes	 * invalidate the cache.
1098135446Strhodes	 */
1099135446Strhodes	result = dns_viewlist_find(&ns_g_server->viewlist,
1100135446Strhodes				   view->name, view->rdclass,
1101135446Strhodes				   &pview);
1102135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
1103135446Strhodes		goto cleanup;
1104135446Strhodes	if (pview != NULL) {
1105135446Strhodes		INSIST(pview->cache != NULL);
1106135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1107135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3),
1108135446Strhodes			      "reusing existing cache");
1109135446Strhodes		reused_cache = ISC_TRUE;
1110135446Strhodes		dns_cache_attach(pview->cache, &cache);
1111135446Strhodes		dns_view_detach(&pview);
1112135446Strhodes	} else {
1113135446Strhodes		CHECK(isc_mem_create(0, 0, &cmctx));
1114135446Strhodes		CHECK(dns_cache_create(cmctx, ns_g_taskmgr, ns_g_timermgr,
1115135446Strhodes				       view->rdclass, "rbt", 0, NULL, &cache));
1116135446Strhodes	}
1117135446Strhodes	dns_view_setcache(view, cache);
1118135446Strhodes
1119135446Strhodes	/*
1120135446Strhodes	 * cache-file cannot be inherited if views are present, but this
1121135446Strhodes	 * should be caught by the configuration checking stage.
1122135446Strhodes	 */
1123135446Strhodes	obj = NULL;
1124135446Strhodes	result = ns_config_get(maps, "cache-file", &obj);
1125135446Strhodes	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
1126135446Strhodes		CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
1127135446Strhodes		if (!reused_cache)
1128135446Strhodes			CHECK(dns_cache_load(cache));
1129135446Strhodes	}
1130135446Strhodes
1131135446Strhodes	obj = NULL;
1132135446Strhodes	result = ns_config_get(maps, "cleaning-interval", &obj);
1133135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1134135446Strhodes	dns_cache_setcleaninginterval(cache, cfg_obj_asuint32(obj) * 60);
1135135446Strhodes
1136135446Strhodes	obj = NULL;
1137135446Strhodes	result = ns_config_get(maps, "max-cache-size", &obj);
1138135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1139135446Strhodes	if (cfg_obj_isstring(obj)) {
1140135446Strhodes		str = cfg_obj_asstring(obj);
1141135446Strhodes		INSIST(strcasecmp(str, "unlimited") == 0);
1142135446Strhodes		max_cache_size = ISC_UINT32_MAX;
1143135446Strhodes	} else {
1144135446Strhodes		isc_resourcevalue_t value;
1145135446Strhodes		value = cfg_obj_asuint64(obj);
1146135446Strhodes		if (value > ISC_UINT32_MAX) {
1147135446Strhodes			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
1148135446Strhodes				    "'max-cache-size "
1149135446Strhodes				    "%" ISC_PRINT_QUADFORMAT "d' is too large",
1150135446Strhodes				    value);
1151135446Strhodes			result = ISC_R_RANGE;
1152135446Strhodes			goto cleanup;
1153135446Strhodes		}
1154135446Strhodes		max_cache_size = (isc_uint32_t)value;
1155135446Strhodes	}
1156135446Strhodes	dns_cache_setcachesize(cache, max_cache_size);
1157135446Strhodes
1158135446Strhodes	dns_cache_detach(&cache);
1159135446Strhodes
1160135446Strhodes	/*
1161135446Strhodes	 * Check-names.
1162135446Strhodes	 */
1163135446Strhodes	obj = NULL;
1164135446Strhodes	result = ns_checknames_get(maps, "response", &obj);
1165135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1166135446Strhodes
1167135446Strhodes	str = cfg_obj_asstring(obj);
1168135446Strhodes	if (strcasecmp(str, "fail") == 0) {
1169135446Strhodes		check = DNS_RESOLVER_CHECKNAMES |
1170135446Strhodes			DNS_RESOLVER_CHECKNAMESFAIL;
1171135446Strhodes		view->checknames = ISC_TRUE;
1172135446Strhodes	} else if (strcasecmp(str, "warn") == 0) {
1173135446Strhodes		check = DNS_RESOLVER_CHECKNAMES;
1174135446Strhodes		view->checknames = ISC_FALSE;
1175135446Strhodes	} else if (strcasecmp(str, "ignore") == 0) {
1176135446Strhodes		check = 0;
1177135446Strhodes		view->checknames = ISC_FALSE;
1178135446Strhodes	} else
1179135446Strhodes		INSIST(0);
1180135446Strhodes
1181135446Strhodes	/*
1182135446Strhodes	 * Resolver.
1183135446Strhodes	 *
1184135446Strhodes	 * XXXRTH  Hardwired number of tasks.
1185135446Strhodes	 */
1186135446Strhodes	CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4));
1187135446Strhodes	CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6));
1188135446Strhodes	if (dispatch4 == NULL && dispatch6 == NULL) {
1189135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
1190135446Strhodes				 "unable to obtain neither an IPv4 nor"
1191135446Strhodes				 " an IPv6 dispatch");
1192135446Strhodes		result = ISC_R_UNEXPECTED;
1193135446Strhodes		goto cleanup;
1194135446Strhodes	}
1195135446Strhodes	CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
1196135446Strhodes				      ns_g_socketmgr, ns_g_timermgr,
1197135446Strhodes				      check, ns_g_dispatchmgr,
1198135446Strhodes				      dispatch4, dispatch6));
1199135446Strhodes
1200135446Strhodes	/*
1201135446Strhodes	 * Set the ADB cache size to 1/8th of the max-cache-size.
1202135446Strhodes	 */
1203135446Strhodes	max_adb_size = 0;
1204135446Strhodes	if (max_cache_size != 0) {
1205135446Strhodes		max_adb_size = max_cache_size / 8;
1206135446Strhodes		if (max_adb_size == 0)
1207135446Strhodes			max_adb_size = 1;	/* Force minimum. */
1208135446Strhodes	}
1209135446Strhodes	dns_adb_setadbsize(view->adb, max_adb_size);
1210135446Strhodes
1211135446Strhodes	/*
1212135446Strhodes	 * Set resolver's lame-ttl.
1213135446Strhodes	 */
1214135446Strhodes	obj = NULL;
1215135446Strhodes	result = ns_config_get(maps, "lame-ttl", &obj);
1216135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1217135446Strhodes	lame_ttl = cfg_obj_asuint32(obj);
1218135446Strhodes	if (lame_ttl > 1800)
1219135446Strhodes		lame_ttl = 1800;
1220135446Strhodes	dns_resolver_setlamettl(view->resolver, lame_ttl);
1221170222Sdougb
1222170222Sdougb	obj = NULL;
1223170222Sdougb	result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
1224170222Sdougb	INSIST(result == ISC_R_SUCCESS);
1225170222Sdougb	dns_resolver_setzeronosoattl(view->resolver, cfg_obj_asboolean(obj));
1226135446Strhodes
1227135446Strhodes	/*
1228135446Strhodes	 * Set the resolver's EDNS UDP size.
1229135446Strhodes	 */
1230135446Strhodes	obj = NULL;
1231135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
1232135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1233135446Strhodes	udpsize = cfg_obj_asuint32(obj);
1234135446Strhodes	if (udpsize < 512)
1235135446Strhodes		udpsize = 512;
1236135446Strhodes	if (udpsize > 4096)
1237135446Strhodes		udpsize = 4096;
1238135446Strhodes	dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize);
1239135446Strhodes
1240135446Strhodes	/*
1241170222Sdougb	 * Set the maximum UDP response size.
1242170222Sdougb	 */
1243170222Sdougb	obj = NULL;
1244170222Sdougb	result = ns_config_get(maps, "max-udp-size", &obj);
1245170222Sdougb	INSIST(result == ISC_R_SUCCESS);
1246170222Sdougb	udpsize = cfg_obj_asuint32(obj);
1247170222Sdougb	if (udpsize < 512)
1248170222Sdougb		udpsize = 512;
1249170222Sdougb	if (udpsize > 4096)
1250170222Sdougb		udpsize = 4096;
1251170222Sdougb	view->maxudp = udpsize;
1252170222Sdougb
1253170222Sdougb	/*
1254135446Strhodes	 * Set supported DNSSEC algorithms.
1255135446Strhodes	 */
1256135446Strhodes	dns_resolver_reset_algorithms(view->resolver);
1257135446Strhodes	disabled = NULL;
1258135446Strhodes	(void)ns_config_get(maps, "disable-algorithms", &disabled);
1259135446Strhodes	if (disabled != NULL) {
1260135446Strhodes		for (element = cfg_list_first(disabled);
1261135446Strhodes		     element != NULL;
1262135446Strhodes		     element = cfg_list_next(element))
1263135446Strhodes			CHECK(disable_algorithms(cfg_listelt_value(element),
1264135446Strhodes						 view->resolver));
1265135446Strhodes	}
1266135446Strhodes
1267135446Strhodes	/*
1268135446Strhodes	 * A global or view "forwarders" option, if present,
1269135446Strhodes	 * creates an entry for "." in the forwarding table.
1270135446Strhodes	 */
1271135446Strhodes	forwardtype = NULL;
1272135446Strhodes	forwarders = NULL;
1273135446Strhodes	(void)ns_config_get(maps, "forward", &forwardtype);
1274135446Strhodes	(void)ns_config_get(maps, "forwarders", &forwarders);
1275135446Strhodes	if (forwarders != NULL)
1276135446Strhodes		CHECK(configure_forward(config, view, dns_rootname,
1277135446Strhodes					forwarders, forwardtype));
1278135446Strhodes
1279135446Strhodes	/*
1280135446Strhodes	 * Dual Stack Servers.
1281135446Strhodes	 */
1282135446Strhodes	alternates = NULL;
1283135446Strhodes	(void)ns_config_get(maps, "dual-stack-servers", &alternates);
1284135446Strhodes	if (alternates != NULL)
1285135446Strhodes		CHECK(configure_alternates(config, view, alternates));
1286135446Strhodes
1287135446Strhodes	/*
1288135446Strhodes	 * We have default hints for class IN if we need them.
1289135446Strhodes	 */
1290135446Strhodes	if (view->rdclass == dns_rdataclass_in && view->hints == NULL)
1291135446Strhodes		dns_view_sethints(view, ns_g_server->in_roothints);
1292135446Strhodes
1293135446Strhodes	/*
1294135446Strhodes	 * If we still have no hints, this is a non-IN view with no
1295135446Strhodes	 * "hints zone" configured.  Issue a warning, except if this
1296135446Strhodes	 * is a root server.  Root servers never need to consult
1297135446Strhodes	 * their hints, so it's no point requiring users to configure
1298135446Strhodes	 * them.
1299135446Strhodes	 */
1300135446Strhodes	if (view->hints == NULL) {
1301135446Strhodes		dns_zone_t *rootzone = NULL;
1302135446Strhodes		(void)dns_view_findzone(view, dns_rootname, &rootzone);
1303135446Strhodes		if (rootzone != NULL) {
1304135446Strhodes			dns_zone_detach(&rootzone);
1305135446Strhodes			need_hints = ISC_FALSE;
1306135446Strhodes		}
1307135446Strhodes		if (need_hints)
1308135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1309135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
1310135446Strhodes				      "no root hints for view '%s'",
1311135446Strhodes				      view->name);
1312135446Strhodes	}
1313135446Strhodes
1314135446Strhodes	/*
1315135446Strhodes	 * Configure the view's TSIG keys.
1316135446Strhodes	 */
1317135446Strhodes	ring = NULL;
1318135446Strhodes	CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
1319135446Strhodes	dns_view_setkeyring(view, ring);
1320135446Strhodes
1321135446Strhodes	/*
1322135446Strhodes	 * Configure the view's peer list.
1323135446Strhodes	 */
1324135446Strhodes	{
1325165071Sdougb		const cfg_obj_t *peers = NULL;
1326165071Sdougb		const cfg_listelt_t *element;
1327135446Strhodes		dns_peerlist_t *newpeers = NULL;
1328135446Strhodes
1329135446Strhodes		(void)ns_config_get(cfgmaps, "server", &peers);
1330135446Strhodes		CHECK(dns_peerlist_new(mctx, &newpeers));
1331135446Strhodes		for (element = cfg_list_first(peers);
1332135446Strhodes		     element != NULL;
1333135446Strhodes		     element = cfg_list_next(element))
1334135446Strhodes		{
1335165071Sdougb			const cfg_obj_t *cpeer = cfg_listelt_value(element);
1336135446Strhodes			dns_peer_t *peer;
1337135446Strhodes
1338135446Strhodes			CHECK(configure_peer(cpeer, mctx, &peer));
1339135446Strhodes			dns_peerlist_addpeer(newpeers, peer);
1340135446Strhodes			dns_peer_detach(&peer);
1341135446Strhodes		}
1342135446Strhodes		dns_peerlist_detach(&view->peers);
1343135446Strhodes		view->peers = newpeers; /* Transfer ownership. */
1344135446Strhodes	}
1345135446Strhodes
1346135446Strhodes	/*
1347135446Strhodes	 *	Configure the views rrset-order.
1348135446Strhodes	 */
1349135446Strhodes	{
1350165071Sdougb		const cfg_obj_t *rrsetorder = NULL;
1351165071Sdougb		const cfg_listelt_t *element;
1352135446Strhodes
1353135446Strhodes		(void)ns_config_get(maps, "rrset-order", &rrsetorder);
1354135446Strhodes		CHECK(dns_order_create(mctx, &order));
1355135446Strhodes		for (element = cfg_list_first(rrsetorder);
1356135446Strhodes		     element != NULL;
1357135446Strhodes		     element = cfg_list_next(element))
1358135446Strhodes		{
1359165071Sdougb			const cfg_obj_t *ent = cfg_listelt_value(element);
1360135446Strhodes
1361135446Strhodes			CHECK(configure_order(order, ent));
1362135446Strhodes		}
1363135446Strhodes		if (view->order != NULL)
1364135446Strhodes			dns_order_detach(&view->order);
1365135446Strhodes		dns_order_attach(order, &view->order);
1366135446Strhodes		dns_order_detach(&order);
1367135446Strhodes	}
1368135446Strhodes	/*
1369135446Strhodes	 * Copy the aclenv object.
1370135446Strhodes	 */
1371135446Strhodes	dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv);
1372135446Strhodes
1373135446Strhodes	/*
1374135446Strhodes	 * Configure the "match-clients" and "match-destinations" ACL.
1375135446Strhodes	 */
1376135446Strhodes	CHECK(configure_view_acl(vconfig, config, "match-clients", actx,
1377135446Strhodes				 ns_g_mctx, &view->matchclients));
1378135446Strhodes	CHECK(configure_view_acl(vconfig, config, "match-destinations", actx,
1379135446Strhodes				 ns_g_mctx, &view->matchdestinations));
1380135446Strhodes
1381135446Strhodes	/*
1382135446Strhodes	 * Configure the "match-recursive-only" option.
1383135446Strhodes	 */
1384135446Strhodes	obj = NULL;
1385165071Sdougb	(void)ns_config_get(maps, "match-recursive-only", &obj);
1386135446Strhodes	if (obj != NULL && cfg_obj_asboolean(obj))
1387135446Strhodes		view->matchrecursiveonly = ISC_TRUE;
1388135446Strhodes	else
1389135446Strhodes		view->matchrecursiveonly = ISC_FALSE;
1390135446Strhodes
1391135446Strhodes	/*
1392135446Strhodes	 * Configure other configurable data.
1393135446Strhodes	 */
1394135446Strhodes	obj = NULL;
1395135446Strhodes	result = ns_config_get(maps, "recursion", &obj);
1396135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1397135446Strhodes	view->recursion = cfg_obj_asboolean(obj);
1398135446Strhodes
1399135446Strhodes	obj = NULL;
1400135446Strhodes	result = ns_config_get(maps, "auth-nxdomain", &obj);
1401135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1402135446Strhodes	view->auth_nxdomain = cfg_obj_asboolean(obj);
1403135446Strhodes
1404135446Strhodes	obj = NULL;
1405135446Strhodes	result = ns_config_get(maps, "minimal-responses", &obj);
1406135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1407135446Strhodes	view->minimalresponses = cfg_obj_asboolean(obj);
1408135446Strhodes
1409135446Strhodes	obj = NULL;
1410135446Strhodes	result = ns_config_get(maps, "transfer-format", &obj);
1411135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1412135446Strhodes	str = cfg_obj_asstring(obj);
1413135446Strhodes	if (strcasecmp(str, "many-answers") == 0)
1414135446Strhodes		view->transfer_format = dns_many_answers;
1415135446Strhodes	else if (strcasecmp(str, "one-answer") == 0)
1416135446Strhodes		view->transfer_format = dns_one_answer;
1417135446Strhodes	else
1418135446Strhodes		INSIST(0);
1419135446Strhodes
1420135446Strhodes	/*
1421135446Strhodes	 * Set sources where additional data and CNAME/DNAME
1422135446Strhodes	 * targets for authoritative answers may be found.
1423135446Strhodes	 */
1424135446Strhodes	obj = NULL;
1425135446Strhodes	result = ns_config_get(maps, "additional-from-auth", &obj);
1426135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1427135446Strhodes	view->additionalfromauth = cfg_obj_asboolean(obj);
1428135446Strhodes	if (view->recursion && ! view->additionalfromauth) {
1429135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
1430135446Strhodes			    "'additional-from-auth no' is only supported "
1431135446Strhodes			    "with 'recursion no'");
1432135446Strhodes		view->additionalfromauth = ISC_TRUE;
1433135446Strhodes	}
1434135446Strhodes
1435135446Strhodes	obj = NULL;
1436135446Strhodes	result = ns_config_get(maps, "additional-from-cache", &obj);
1437135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1438135446Strhodes	view->additionalfromcache = cfg_obj_asboolean(obj);
1439135446Strhodes	if (view->recursion && ! view->additionalfromcache) {
1440135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
1441135446Strhodes			    "'additional-from-cache no' is only supported "
1442135446Strhodes			    "with 'recursion no'");
1443135446Strhodes		view->additionalfromcache = ISC_TRUE;
1444135446Strhodes	}
1445135446Strhodes
1446171577Sdougb	/*
1447171577Sdougb	 * Set "allow-query-cache" and "allow-recursion" acls if
1448171577Sdougb	 * configured in named.conf.
1449171577Sdougb	 */
1450170222Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query-cache",
1451135446Strhodes				 actx, ns_g_mctx, &view->queryacl));
1452135446Strhodes
1453135446Strhodes	if (strcmp(view->name, "_bind") != 0)
1454135446Strhodes		CHECK(configure_view_acl(vconfig, config, "allow-recursion",
1455135446Strhodes					 actx, ns_g_mctx, &view->recursionacl));
1456135446Strhodes
1457135446Strhodes	/*
1458135446Strhodes	 * Warning if both "recursion no;" and allow-recursion are active
1459135446Strhodes	 * except for "allow-recursion { none; };".
1460135446Strhodes	 */
1461135446Strhodes	if (!view->recursion && view->recursionacl != NULL &&
1462135446Strhodes	    (view->recursionacl->length != 1 ||
1463135446Strhodes	     view->recursionacl->elements[0].type != dns_aclelementtype_any ||
1464170222Sdougb	     view->recursionacl->elements[0].negative != ISC_TRUE))
1465135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1466135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
1467135446Strhodes			      "both \"recursion no;\" and \"allow-recursion\" "
1468135446Strhodes			      "active%s%s", forview, viewname);
1469135446Strhodes
1470170222Sdougb	/*
1471171577Sdougb	 * "allow-query-cache" inherits from "allow-recursion" if set,
1472171577Sdougb	 * otherwise from "allow-query" if set.
1473171577Sdougb	 * "allow-recursion" inherits from "allow-query-cache" if set,
1474171577Sdougb	 * otherwise from "allow-query" if set.
1475170222Sdougb	 */
1476171577Sdougb	if (view->queryacl == NULL && view->recursionacl != NULL)
1477171577Sdougb		dns_acl_attach(view->recursionacl, &view->queryacl);
1478171577Sdougb	if (view->queryacl == NULL)
1479171577Sdougb		CHECK(configure_view_acl(vconfig, config, "allow-query",
1480171577Sdougb					 actx, ns_g_mctx, &view->queryacl));
1481171577Sdougb	if (view->recursionacl == NULL && view->queryacl != NULL)
1482171577Sdougb		dns_acl_attach(view->queryacl, &view->recursionacl);
1483171577Sdougb
1484171577Sdougb	/*
1485171577Sdougb	 * Set default "allow-recursion" and "allow-query-cache" acls.
1486171577Sdougb	 */
1487170222Sdougb	if (view->recursionacl == NULL && view->recursion)
1488171577Sdougb		CHECK(configure_view_acl(NULL, ns_g_config, "allow-recursion",
1489170222Sdougb					 actx, ns_g_mctx, &view->recursionacl));
1490171577Sdougb	if (view->queryacl == NULL)
1491171577Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
1492171577Sdougb					 "allow-query-cache", actx,
1493171577Sdougb					 ns_g_mctx, &view->queryacl));
1494170222Sdougb
1495135446Strhodes	CHECK(configure_view_acl(vconfig, config, "sortlist",
1496135446Strhodes				 actx, ns_g_mctx, &view->sortlist));
1497135446Strhodes
1498135446Strhodes	obj = NULL;
1499135446Strhodes	result = ns_config_get(maps, "request-ixfr", &obj);
1500135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1501135446Strhodes	view->requestixfr = cfg_obj_asboolean(obj);
1502135446Strhodes
1503135446Strhodes	obj = NULL;
1504135446Strhodes	result = ns_config_get(maps, "provide-ixfr", &obj);
1505135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1506135446Strhodes	view->provideixfr = cfg_obj_asboolean(obj);
1507170222Sdougb
1508170222Sdougb	obj = NULL;
1509170222Sdougb	result = ns_config_get(maps, "max-clients-per-query", &obj);
1510170222Sdougb	INSIST(result == ISC_R_SUCCESS);
1511170222Sdougb	max_clients_per_query = cfg_obj_asuint32(obj);
1512170222Sdougb
1513170222Sdougb	obj = NULL;
1514170222Sdougb	result = ns_config_get(maps, "clients-per-query", &obj);
1515170222Sdougb	INSIST(result == ISC_R_SUCCESS);
1516170222Sdougb	dns_resolver_setclientsperquery(view->resolver,
1517170222Sdougb					cfg_obj_asuint32(obj),
1518170222Sdougb					max_clients_per_query);
1519135446Strhodes
1520135446Strhodes	obj = NULL;
1521135446Strhodes	result = ns_config_get(maps, "dnssec-enable", &obj);
1522135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1523135446Strhodes	view->enablednssec = cfg_obj_asboolean(obj);
1524135446Strhodes
1525135446Strhodes	obj = NULL;
1526170222Sdougb	result = ns_config_get(maps, "dnssec-accept-expired", &obj);
1527170222Sdougb	INSIST(result == ISC_R_SUCCESS);
1528170222Sdougb	view->acceptexpired = cfg_obj_asboolean(obj);
1529170222Sdougb
1530170222Sdougb	obj = NULL;
1531170222Sdougb	result = ns_config_get(maps, "dnssec-validation", &obj);
1532170222Sdougb	INSIST(result == ISC_R_SUCCESS);
1533170222Sdougb	view->enablevalidation = cfg_obj_asboolean(obj);
1534170222Sdougb
1535170222Sdougb	obj = NULL;
1536135446Strhodes	result = ns_config_get(maps, "dnssec-lookaside", &obj);
1537135446Strhodes	if (result == ISC_R_SUCCESS) {
1538135446Strhodes		for (element = cfg_list_first(obj);
1539135446Strhodes		     element != NULL;
1540135446Strhodes		     element = cfg_list_next(element))
1541135446Strhodes		{
1542135446Strhodes			const char *str;
1543135446Strhodes			isc_buffer_t b;
1544135446Strhodes			dns_name_t *dlv;
1545135446Strhodes
1546135446Strhodes			obj = cfg_listelt_value(element);
1547135446Strhodes#if 0
1548135446Strhodes			dns_fixedname_t fixed;
1549135446Strhodes			dns_name_t *name;
1550135446Strhodes
1551135446Strhodes			/*
1552135446Strhodes			 * When we support multiple dnssec-lookaside
1553135446Strhodes			 * entries this is how to find the domain to be
1554135446Strhodes			 * checked. XXXMPA
1555135446Strhodes			 */
1556135446Strhodes			dns_fixedname_init(&fixed);
1557135446Strhodes			name = dns_fixedname_name(&fixed);
1558135446Strhodes			str = cfg_obj_asstring(cfg_tuple_get(obj,
1559135446Strhodes							     "domain"));
1560135446Strhodes			isc_buffer_init(&b, str, strlen(str));
1561135446Strhodes			isc_buffer_add(&b, strlen(str));
1562135446Strhodes			CHECK(dns_name_fromtext(name, &b, dns_rootname,
1563135446Strhodes						ISC_TRUE, NULL));
1564135446Strhodes#endif
1565135446Strhodes			str = cfg_obj_asstring(cfg_tuple_get(obj,
1566135446Strhodes							     "trust-anchor"));
1567135446Strhodes			isc_buffer_init(&b, str, strlen(str));
1568135446Strhodes			isc_buffer_add(&b, strlen(str));
1569135446Strhodes			dlv = dns_fixedname_name(&view->dlv_fixed);
1570135446Strhodes			CHECK(dns_name_fromtext(dlv, &b, dns_rootname,
1571135446Strhodes						ISC_TRUE, NULL));
1572135446Strhodes			view->dlv = dns_fixedname_name(&view->dlv_fixed);
1573135446Strhodes		}
1574135446Strhodes	} else
1575135446Strhodes		view->dlv = NULL;
1576135446Strhodes
1577135446Strhodes	/*
1578135446Strhodes	 * For now, there is only one kind of trusted keys, the
1579135446Strhodes	 * "security roots".
1580135446Strhodes	 */
1581170222Sdougb	CHECK(configure_view_dnsseckeys(vconfig, config, mctx,
1582170222Sdougb					&view->secroots));
1583170222Sdougb	dns_resolver_resetmustbesecure(view->resolver);
1584170222Sdougb	obj = NULL;
1585170222Sdougb	result = ns_config_get(maps, "dnssec-must-be-secure", &obj);
1586170222Sdougb	if (result == ISC_R_SUCCESS)
1587170222Sdougb		CHECK(mustbesecure(obj, view->resolver));
1588135446Strhodes
1589135446Strhodes	obj = NULL;
1590135446Strhodes	result = ns_config_get(maps, "max-cache-ttl", &obj);
1591135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1592135446Strhodes	view->maxcachettl = cfg_obj_asuint32(obj);
1593135446Strhodes
1594135446Strhodes	obj = NULL;
1595135446Strhodes	result = ns_config_get(maps, "max-ncache-ttl", &obj);
1596135446Strhodes	INSIST(result == ISC_R_SUCCESS);
1597135446Strhodes	view->maxncachettl = cfg_obj_asuint32(obj);
1598135446Strhodes	if (view->maxncachettl > 7 * 24 * 3600)
1599135446Strhodes		view->maxncachettl = 7 * 24 * 3600;
1600135446Strhodes
1601135446Strhodes	obj = NULL;
1602135446Strhodes	result = ns_config_get(maps, "preferred-glue", &obj);
1603135446Strhodes	if (result == ISC_R_SUCCESS) {
1604135446Strhodes		str = cfg_obj_asstring(obj);
1605135446Strhodes		if (strcasecmp(str, "a") == 0)
1606135446Strhodes			view->preferred_glue = dns_rdatatype_a;
1607135446Strhodes		else if (strcasecmp(str, "aaaa") == 0)
1608135446Strhodes			view->preferred_glue = dns_rdatatype_aaaa;
1609135446Strhodes		else
1610135446Strhodes			view->preferred_glue = 0;
1611135446Strhodes	} else
1612135446Strhodes		view->preferred_glue = 0;
1613135446Strhodes
1614135446Strhodes	obj = NULL;
1615135446Strhodes	result = ns_config_get(maps, "root-delegation-only", &obj);
1616135446Strhodes	if (result == ISC_R_SUCCESS) {
1617135446Strhodes		dns_view_setrootdelonly(view, ISC_TRUE);
1618135446Strhodes		if (!cfg_obj_isvoid(obj)) {
1619135446Strhodes			dns_fixedname_t fixed;
1620135446Strhodes			dns_name_t *name;
1621135446Strhodes			isc_buffer_t b;
1622165071Sdougb			const char *str;
1623165071Sdougb			const cfg_obj_t *exclude;
1624135446Strhodes
1625135446Strhodes			dns_fixedname_init(&fixed);
1626135446Strhodes			name = dns_fixedname_name(&fixed);
1627135446Strhodes			for (element = cfg_list_first(obj);
1628135446Strhodes			     element != NULL;
1629135446Strhodes			     element = cfg_list_next(element)) {
1630135446Strhodes				exclude = cfg_listelt_value(element);
1631135446Strhodes				str = cfg_obj_asstring(exclude);
1632135446Strhodes				isc_buffer_init(&b, str, strlen(str));
1633135446Strhodes				isc_buffer_add(&b, strlen(str));
1634135446Strhodes				CHECK(dns_name_fromtext(name, &b, dns_rootname,
1635135446Strhodes							ISC_FALSE, NULL));
1636135446Strhodes				CHECK(dns_view_excludedelegationonly(view,
1637135446Strhodes								     name));
1638135446Strhodes			}
1639135446Strhodes		}
1640135446Strhodes	} else
1641135446Strhodes		dns_view_setrootdelonly(view, ISC_FALSE);
1642135446Strhodes
1643170222Sdougb	/*
1644170222Sdougb	 * Setup automatic empty zones.  If recursion is off then
1645170222Sdougb	 * they are disabled by default.
1646170222Sdougb	 */
1647170222Sdougb	obj = NULL;
1648170222Sdougb	(void)ns_config_get(maps, "empty-zones-enable", &obj);
1649170222Sdougb	(void)ns_config_get(maps, "disable-empty-zone", &disablelist);
1650170222Sdougb	if (obj == NULL && disablelist == NULL &&
1651170222Sdougb	    view->rdclass == dns_rdataclass_in) {
1652170222Sdougb		rfc1918 = ISC_FALSE;
1653170222Sdougb		empty_zones_enable = view->recursion;
1654170222Sdougb	} else if (view->rdclass == dns_rdataclass_in) {
1655170222Sdougb		rfc1918 = ISC_TRUE;
1656170222Sdougb		if (obj != NULL)
1657170222Sdougb			empty_zones_enable = cfg_obj_asboolean(obj);
1658170222Sdougb		else
1659170222Sdougb			empty_zones_enable = view->recursion;
1660170222Sdougb	} else {
1661170222Sdougb		rfc1918 = ISC_FALSE;
1662170222Sdougb		empty_zones_enable = ISC_FALSE;
1663170222Sdougb	}
1664170222Sdougb	if (empty_zones_enable) {
1665170222Sdougb		const char *empty;
1666170222Sdougb		int empty_zone = 0;
1667170222Sdougb		dns_fixedname_t fixed;
1668170222Sdougb		dns_name_t *name;
1669170222Sdougb		isc_buffer_t buffer;
1670170222Sdougb		const char *str;
1671170222Sdougb		char server[DNS_NAME_FORMATSIZE + 1];
1672170222Sdougb		char contact[DNS_NAME_FORMATSIZE + 1];
1673170222Sdougb		isc_boolean_t logit;
1674170222Sdougb		const char *empty_dbtype[4] =
1675170222Sdougb				    { "_builtin", "empty", NULL, NULL };
1676170222Sdougb		int empty_dbtypec = 4;
1677170222Sdougb
1678170222Sdougb		dns_fixedname_init(&fixed);
1679170222Sdougb		name = dns_fixedname_name(&fixed);
1680170222Sdougb
1681170222Sdougb		obj = NULL;
1682170222Sdougb		result = ns_config_get(maps, "empty-server", &obj);
1683170222Sdougb		if (result == ISC_R_SUCCESS) {
1684170222Sdougb			str = cfg_obj_asstring(obj);
1685170222Sdougb			isc_buffer_init(&buffer, str, strlen(str));
1686170222Sdougb			isc_buffer_add(&buffer, strlen(str));
1687170222Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
1688170222Sdougb						ISC_FALSE, NULL));
1689170222Sdougb			isc_buffer_init(&buffer, server, sizeof(server) - 1);
1690170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
1691170222Sdougb			server[isc_buffer_usedlength(&buffer)] = 0;
1692170222Sdougb			empty_dbtype[2] = server;
1693170222Sdougb		} else
1694170222Sdougb			empty_dbtype[2] = "@";
1695170222Sdougb
1696170222Sdougb		obj = NULL;
1697170222Sdougb		result = ns_config_get(maps, "empty-contact", &obj);
1698170222Sdougb		if (result == ISC_R_SUCCESS) {
1699170222Sdougb			str = cfg_obj_asstring(obj);
1700170222Sdougb			isc_buffer_init(&buffer, str, strlen(str));
1701170222Sdougb			isc_buffer_add(&buffer, strlen(str));
1702170222Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
1703170222Sdougb						ISC_FALSE, NULL));
1704170222Sdougb			isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
1705170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
1706170222Sdougb			contact[isc_buffer_usedlength(&buffer)] = 0;
1707170222Sdougb			empty_dbtype[3] = contact;
1708170222Sdougb		} else
1709170222Sdougb			empty_dbtype[3] = ".";
1710170222Sdougb
1711170222Sdougb		logit = ISC_TRUE;
1712170222Sdougb		for (empty = empty_zones[empty_zone].zone;
1713170222Sdougb		     empty != NULL;
1714170222Sdougb		     empty = empty_zones[++empty_zone].zone)
1715170222Sdougb		{
1716170222Sdougb			dns_forwarders_t *forwarders = NULL;
1717170222Sdougb			dns_view_t *pview = NULL;
1718170222Sdougb
1719170222Sdougb			isc_buffer_init(&buffer, empty, strlen(empty));
1720170222Sdougb			isc_buffer_add(&buffer, strlen(empty));
1721170222Sdougb			/*
1722170222Sdougb			 * Look for zone on drop list.
1723170222Sdougb			 */
1724170222Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
1725170222Sdougb						ISC_FALSE, NULL));
1726170222Sdougb			if (disablelist != NULL &&
1727170222Sdougb			    on_disable_list(disablelist, name))
1728170222Sdougb				continue;
1729170222Sdougb
1730170222Sdougb			/*
1731170222Sdougb			 * This zone already exists.
1732170222Sdougb			 */
1733170222Sdougb			(void)dns_view_findzone(view, name, &zone);
1734170222Sdougb			if (zone != NULL) {
1735170222Sdougb				dns_zone_detach(&zone);
1736170222Sdougb				continue;
1737170222Sdougb			}
1738170222Sdougb
1739170222Sdougb			/*
1740170222Sdougb			 * If we would forward this name don't add a
1741170222Sdougb			 * empty zone for it.
1742170222Sdougb			 */
1743170222Sdougb			result = dns_fwdtable_find(view->fwdtable, name,
1744170222Sdougb						   &forwarders);
1745170222Sdougb			if (result == ISC_R_SUCCESS &&
1746170222Sdougb			    forwarders->fwdpolicy == dns_fwdpolicy_only)
1747170222Sdougb				continue;
1748170222Sdougb
1749170222Sdougb			if (!rfc1918 && empty_zones[empty_zone].rfc1918) {
1750170222Sdougb				if (logit) {
1751170222Sdougb					isc_log_write(ns_g_lctx,
1752170222Sdougb						      NS_LOGCATEGORY_GENERAL,
1753170222Sdougb						      NS_LOGMODULE_SERVER,
1754170222Sdougb						      ISC_LOG_WARNING,
1755170222Sdougb					              "Warning%s%s: "
1756170222Sdougb						      "'empty-zones-enable/"
1757170222Sdougb						      "disable-empty-zone' "
1758170222Sdougb						      "not set: disabling "
1759170222Sdougb						      "RFC 1918 empty zones",
1760170222Sdougb						      sep, viewname);
1761170222Sdougb					logit = ISC_FALSE;
1762170222Sdougb				}
1763170222Sdougb				continue;
1764170222Sdougb			}
1765170222Sdougb
1766170222Sdougb			/*
1767170222Sdougb			 * See if we can re-use a existing zone.
1768170222Sdougb			 */
1769170222Sdougb			result = dns_viewlist_find(&ns_g_server->viewlist,
1770170222Sdougb						   view->name, view->rdclass,
1771170222Sdougb						   &pview);
1772170222Sdougb			if (result != ISC_R_NOTFOUND &&
1773170222Sdougb			    result != ISC_R_SUCCESS)
1774170222Sdougb				goto cleanup;
1775170222Sdougb
1776170222Sdougb			if (pview != NULL) {
1777170222Sdougb				(void)dns_view_findzone(pview, name, &zone);
1778170222Sdougb				dns_view_detach(&pview);
1779170222Sdougb				if (zone != NULL)
1780170222Sdougb					check_dbtype(&zone, empty_dbtypec,
1781170222Sdougb						     empty_dbtype, mctx);
1782170222Sdougb				if (zone != NULL) {
1783170222Sdougb					dns_zone_setview(zone, view);
1784174187Sdougb					CHECK(dns_view_addzone(view, zone));
1785170222Sdougb					dns_zone_detach(&zone);
1786170222Sdougb					continue;
1787170222Sdougb				}
1788170222Sdougb			}
1789170222Sdougb
1790170222Sdougb			CHECK(dns_zone_create(&zone, mctx));
1791170222Sdougb			CHECK(dns_zone_setorigin(zone, name));
1792170222Sdougb			dns_zone_setview(zone, view);
1793170222Sdougb			CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
1794170222Sdougb			dns_zone_setclass(zone, view->rdclass);
1795170222Sdougb			dns_zone_settype(zone, dns_zone_master);
1796170222Sdougb			CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
1797170222Sdougb					 	 empty_dbtype));
1798170222Sdougb			if (view->queryacl != NULL)
1799170222Sdougb				dns_zone_setqueryacl(zone, view->queryacl);
1800170222Sdougb			dns_zone_setdialup(zone, dns_dialuptype_no);
1801170222Sdougb			dns_zone_setnotifytype(zone, dns_notifytype_no);
1802170222Sdougb			dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS,
1803170222Sdougb					   ISC_TRUE);
1804170222Sdougb			CHECK(dns_view_addzone(view, zone));
1805170222Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1806170222Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
1807170222Sdougb				      "automatic empty zone%s%s: %s",
1808170222Sdougb				      sep, viewname,  empty);
1809170222Sdougb			dns_zone_detach(&zone);
1810170222Sdougb		}
1811170222Sdougb	}
1812170222Sdougb
1813135446Strhodes	result = ISC_R_SUCCESS;
1814135446Strhodes
1815135446Strhodes cleanup:
1816170222Sdougb	if (zone != NULL)
1817170222Sdougb		dns_zone_detach(&zone);
1818135446Strhodes	if (dispatch4 != NULL)
1819135446Strhodes		dns_dispatch_detach(&dispatch4);
1820135446Strhodes	if (dispatch6 != NULL)
1821135446Strhodes		dns_dispatch_detach(&dispatch6);
1822135446Strhodes	if (order != NULL)
1823135446Strhodes		dns_order_detach(&order);
1824135446Strhodes	if (cmctx != NULL)
1825135446Strhodes		isc_mem_detach(&cmctx);
1826135446Strhodes
1827135446Strhodes	if (cache != NULL)
1828135446Strhodes		dns_cache_detach(&cache);
1829135446Strhodes
1830135446Strhodes	return (result);
1831135446Strhodes}
1832135446Strhodes
1833135446Strhodesstatic isc_result_t
1834135446Strhodesconfigure_hints(dns_view_t *view, const char *filename) {
1835135446Strhodes	isc_result_t result;
1836135446Strhodes	dns_db_t *db;
1837135446Strhodes
1838135446Strhodes	db = NULL;
1839135446Strhodes	result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
1840135446Strhodes	if (result == ISC_R_SUCCESS) {
1841135446Strhodes		dns_view_sethints(view, db);
1842135446Strhodes		dns_db_detach(&db);
1843135446Strhodes	}
1844135446Strhodes
1845135446Strhodes	return (result);
1846135446Strhodes}
1847135446Strhodes
1848135446Strhodesstatic isc_result_t
1849165071Sdougbconfigure_alternates(const cfg_obj_t *config, dns_view_t *view,
1850165071Sdougb		     const cfg_obj_t *alternates)
1851135446Strhodes{
1852165071Sdougb	const cfg_obj_t *portobj;
1853165071Sdougb	const cfg_obj_t *addresses;
1854165071Sdougb	const cfg_listelt_t *element;
1855135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
1856135446Strhodes	in_port_t port;
1857135446Strhodes
1858135446Strhodes	/*
1859135446Strhodes	 * Determine which port to send requests to.
1860135446Strhodes	 */
1861135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
1862135446Strhodes		port = ns_g_port;
1863135446Strhodes	else
1864135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
1865135446Strhodes
1866135446Strhodes	if (alternates != NULL) {
1867135446Strhodes		portobj = cfg_tuple_get(alternates, "port");
1868135446Strhodes		if (cfg_obj_isuint32(portobj)) {
1869135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
1870135446Strhodes			if (val > ISC_UINT16_MAX) {
1871135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
1872135446Strhodes					    "port '%u' out of range", val);
1873135446Strhodes				return (ISC_R_RANGE);
1874135446Strhodes			}
1875135446Strhodes			port = (in_port_t) val;
1876135446Strhodes		}
1877135446Strhodes	}
1878135446Strhodes
1879135446Strhodes	addresses = NULL;
1880135446Strhodes	if (alternates != NULL)
1881135446Strhodes		addresses = cfg_tuple_get(alternates, "addresses");
1882135446Strhodes
1883135446Strhodes	for (element = cfg_list_first(addresses);
1884135446Strhodes	     element != NULL;
1885135446Strhodes	     element = cfg_list_next(element))
1886135446Strhodes	{
1887165071Sdougb		const cfg_obj_t *alternate = cfg_listelt_value(element);
1888135446Strhodes		isc_sockaddr_t sa;
1889135446Strhodes
1890135446Strhodes		if (!cfg_obj_issockaddr(alternate)) {
1891135446Strhodes			dns_fixedname_t fixed;
1892135446Strhodes			dns_name_t *name;
1893165071Sdougb			const char *str = cfg_obj_asstring(cfg_tuple_get(
1894165071Sdougb							   alternate, "name"));
1895135446Strhodes			isc_buffer_t buffer;
1896135446Strhodes			in_port_t myport = port;
1897135446Strhodes
1898135446Strhodes			isc_buffer_init(&buffer, str, strlen(str));
1899135446Strhodes			isc_buffer_add(&buffer, strlen(str));
1900135446Strhodes			dns_fixedname_init(&fixed);
1901135446Strhodes			name = dns_fixedname_name(&fixed);
1902135446Strhodes			CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
1903135446Strhodes						ISC_FALSE, NULL));
1904135446Strhodes
1905135446Strhodes			portobj = cfg_tuple_get(alternate, "port");
1906135446Strhodes			if (cfg_obj_isuint32(portobj)) {
1907135446Strhodes				isc_uint32_t val = cfg_obj_asuint32(portobj);
1908135446Strhodes				if (val > ISC_UINT16_MAX) {
1909135446Strhodes					cfg_obj_log(portobj, ns_g_lctx,
1910135446Strhodes						    ISC_LOG_ERROR,
1911135446Strhodes						    "port '%u' out of range",
1912135446Strhodes						     val);
1913135446Strhodes					return (ISC_R_RANGE);
1914135446Strhodes				}
1915135446Strhodes				myport = (in_port_t) val;
1916135446Strhodes			}
1917135446Strhodes			CHECK(dns_resolver_addalternate(view->resolver, NULL,
1918135446Strhodes							name, myport));
1919135446Strhodes			continue;
1920135446Strhodes		}
1921135446Strhodes
1922135446Strhodes		sa = *cfg_obj_assockaddr(alternate);
1923135446Strhodes		if (isc_sockaddr_getport(&sa) == 0)
1924135446Strhodes			isc_sockaddr_setport(&sa, port);
1925135446Strhodes		CHECK(dns_resolver_addalternate(view->resolver, &sa,
1926135446Strhodes						NULL, 0));
1927135446Strhodes	}
1928135446Strhodes
1929135446Strhodes cleanup:
1930135446Strhodes	return (result);
1931135446Strhodes}
1932135446Strhodes
1933135446Strhodesstatic isc_result_t
1934165071Sdougbconfigure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
1935165071Sdougb		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype)
1936135446Strhodes{
1937165071Sdougb	const cfg_obj_t *portobj;
1938165071Sdougb	const cfg_obj_t *faddresses;
1939165071Sdougb	const cfg_listelt_t *element;
1940135446Strhodes	dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
1941135446Strhodes	isc_sockaddrlist_t addresses;
1942135446Strhodes	isc_sockaddr_t *sa;
1943135446Strhodes	isc_result_t result;
1944135446Strhodes	in_port_t port;
1945135446Strhodes
1946135446Strhodes	/*
1947135446Strhodes	 * Determine which port to send forwarded requests to.
1948135446Strhodes	 */
1949135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
1950135446Strhodes		port = ns_g_port;
1951135446Strhodes	else
1952135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
1953135446Strhodes
1954135446Strhodes	if (forwarders != NULL) {
1955135446Strhodes		portobj = cfg_tuple_get(forwarders, "port");
1956135446Strhodes		if (cfg_obj_isuint32(portobj)) {
1957135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
1958135446Strhodes			if (val > ISC_UINT16_MAX) {
1959135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
1960135446Strhodes					    "port '%u' out of range", val);
1961135446Strhodes				return (ISC_R_RANGE);
1962135446Strhodes			}
1963135446Strhodes			port = (in_port_t) val;
1964135446Strhodes		}
1965135446Strhodes	}
1966135446Strhodes
1967135446Strhodes	faddresses = NULL;
1968135446Strhodes	if (forwarders != NULL)
1969135446Strhodes		faddresses = cfg_tuple_get(forwarders, "addresses");
1970135446Strhodes
1971135446Strhodes	ISC_LIST_INIT(addresses);
1972135446Strhodes
1973135446Strhodes	for (element = cfg_list_first(faddresses);
1974135446Strhodes	     element != NULL;
1975135446Strhodes	     element = cfg_list_next(element))
1976135446Strhodes	{
1977165071Sdougb		const cfg_obj_t *forwarder = cfg_listelt_value(element);
1978135446Strhodes		sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t));
1979135446Strhodes		if (sa == NULL) {
1980135446Strhodes			result = ISC_R_NOMEMORY;
1981135446Strhodes			goto cleanup;
1982135446Strhodes		}
1983135446Strhodes		*sa = *cfg_obj_assockaddr(forwarder);
1984135446Strhodes		if (isc_sockaddr_getport(sa) == 0)
1985135446Strhodes			isc_sockaddr_setport(sa, port);
1986135446Strhodes		ISC_LINK_INIT(sa, link);
1987135446Strhodes		ISC_LIST_APPEND(addresses, sa, link);
1988135446Strhodes	}
1989135446Strhodes
1990135446Strhodes	if (ISC_LIST_EMPTY(addresses)) {
1991135446Strhodes		if (forwardtype != NULL)
1992135446Strhodes			cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
1993135446Strhodes				    "no forwarders seen; disabling "
1994135446Strhodes				    "forwarding");
1995135446Strhodes		fwdpolicy = dns_fwdpolicy_none;
1996135446Strhodes	} else {
1997135446Strhodes		if (forwardtype == NULL)
1998135446Strhodes			fwdpolicy = dns_fwdpolicy_first;
1999135446Strhodes		else {
2000165071Sdougb			const char *forwardstr = cfg_obj_asstring(forwardtype);
2001135446Strhodes			if (strcasecmp(forwardstr, "first") == 0)
2002135446Strhodes				fwdpolicy = dns_fwdpolicy_first;
2003135446Strhodes			else if (strcasecmp(forwardstr, "only") == 0)
2004135446Strhodes				fwdpolicy = dns_fwdpolicy_only;
2005135446Strhodes			else
2006135446Strhodes				INSIST(0);
2007135446Strhodes		}
2008135446Strhodes	}
2009135446Strhodes
2010135446Strhodes	result = dns_fwdtable_add(view->fwdtable, origin, &addresses,
2011135446Strhodes				  fwdpolicy);
2012135446Strhodes	if (result != ISC_R_SUCCESS) {
2013135446Strhodes		char namebuf[DNS_NAME_FORMATSIZE];
2014135446Strhodes		dns_name_format(origin, namebuf, sizeof(namebuf));
2015135446Strhodes		cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
2016135446Strhodes			    "could not set up forwarding for domain '%s': %s",
2017135446Strhodes			    namebuf, isc_result_totext(result));
2018135446Strhodes		goto cleanup;
2019135446Strhodes	}
2020135446Strhodes
2021135446Strhodes	result = ISC_R_SUCCESS;
2022135446Strhodes
2023135446Strhodes cleanup:
2024135446Strhodes
2025135446Strhodes	while (!ISC_LIST_EMPTY(addresses)) {
2026135446Strhodes		sa = ISC_LIST_HEAD(addresses);
2027135446Strhodes		ISC_LIST_UNLINK(addresses, sa, link);
2028135446Strhodes		isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t));
2029135446Strhodes	}
2030135446Strhodes
2031135446Strhodes	return (result);
2032135446Strhodes}
2033135446Strhodes
2034135446Strhodes/*
2035135446Strhodes * Create a new view and add it to the list.
2036135446Strhodes *
2037135446Strhodes * If 'vconfig' is NULL, create the default view.
2038135446Strhodes *
2039135446Strhodes * The view created is attached to '*viewp'.
2040135446Strhodes */
2041135446Strhodesstatic isc_result_t
2042165071Sdougbcreate_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
2043165071Sdougb	    dns_view_t **viewp)
2044165071Sdougb{
2045135446Strhodes	isc_result_t result;
2046135446Strhodes	const char *viewname;
2047135446Strhodes	dns_rdataclass_t viewclass;
2048135446Strhodes	dns_view_t *view = NULL;
2049135446Strhodes
2050135446Strhodes	if (vconfig != NULL) {
2051165071Sdougb		const cfg_obj_t *classobj = NULL;
2052135446Strhodes
2053135446Strhodes		viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
2054135446Strhodes		classobj = cfg_tuple_get(vconfig, "class");
2055135446Strhodes		result = ns_config_getclass(classobj, dns_rdataclass_in,
2056135446Strhodes					    &viewclass);
2057135446Strhodes	} else {
2058135446Strhodes		viewname = "_default";
2059135446Strhodes		viewclass = dns_rdataclass_in;
2060135446Strhodes	}
2061135446Strhodes	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
2062135446Strhodes	if (result == ISC_R_SUCCESS)
2063135446Strhodes		return (ISC_R_EXISTS);
2064135446Strhodes	if (result != ISC_R_NOTFOUND)
2065135446Strhodes		return (result);
2066135446Strhodes	INSIST(view == NULL);
2067135446Strhodes
2068135446Strhodes	result = dns_view_create(ns_g_mctx, viewclass, viewname, &view);
2069135446Strhodes	if (result != ISC_R_SUCCESS)
2070135446Strhodes		return (result);
2071135446Strhodes
2072135446Strhodes	ISC_LIST_APPEND(*viewlist, view, link);
2073135446Strhodes	dns_view_attach(view, viewp);
2074135446Strhodes	return (ISC_R_SUCCESS);
2075135446Strhodes}
2076135446Strhodes
2077135446Strhodes/*
2078135446Strhodes * Configure or reconfigure a zone.
2079135446Strhodes */
2080135446Strhodesstatic isc_result_t
2081165071Sdougbconfigure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
2082165071Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
2083170222Sdougb	       cfg_aclconfctx_t *aclconf)
2084135446Strhodes{
2085135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
2086135446Strhodes	dns_zone_t *zone = NULL;	/* New or reused zone */
2087135446Strhodes	dns_zone_t *dupzone = NULL;
2088165071Sdougb	const cfg_obj_t *options = NULL;
2089165071Sdougb	const cfg_obj_t *zoptions = NULL;
2090165071Sdougb	const cfg_obj_t *typeobj = NULL;
2091165071Sdougb	const cfg_obj_t *forwarders = NULL;
2092165071Sdougb	const cfg_obj_t *forwardtype = NULL;
2093165071Sdougb	const cfg_obj_t *only = NULL;
2094135446Strhodes	isc_result_t result;
2095135446Strhodes	isc_result_t tresult;
2096135446Strhodes	isc_buffer_t buffer;
2097135446Strhodes	dns_fixedname_t fixorigin;
2098135446Strhodes	dns_name_t *origin;
2099135446Strhodes	const char *zname;
2100135446Strhodes	dns_rdataclass_t zclass;
2101135446Strhodes	const char *ztypestr;
2102135446Strhodes
2103135446Strhodes	options = NULL;
2104135446Strhodes	(void)cfg_map_get(config, "options", &options);
2105135446Strhodes
2106135446Strhodes	zoptions = cfg_tuple_get(zconfig, "options");
2107135446Strhodes
2108135446Strhodes	/*
2109135446Strhodes	 * Get the zone origin as a dns_name_t.
2110135446Strhodes	 */
2111135446Strhodes	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
2112135446Strhodes	isc_buffer_init(&buffer, zname, strlen(zname));
2113135446Strhodes	isc_buffer_add(&buffer, strlen(zname));
2114135446Strhodes	dns_fixedname_init(&fixorigin);
2115135446Strhodes	CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin),
2116135446Strhodes				&buffer, dns_rootname, ISC_FALSE, NULL));
2117135446Strhodes	origin = dns_fixedname_name(&fixorigin);
2118135446Strhodes
2119135446Strhodes	CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
2120135446Strhodes				 view->rdclass, &zclass));
2121135446Strhodes	if (zclass != view->rdclass) {
2122135446Strhodes		const char *vname = NULL;
2123135446Strhodes		if (vconfig != NULL)
2124135446Strhodes			vname = cfg_obj_asstring(cfg_tuple_get(vconfig,
2125135446Strhodes							       "name"));
2126135446Strhodes		else
2127135446Strhodes			vname = "<default view>";
2128135446Strhodes
2129135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2130135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
2131135446Strhodes			      "zone '%s': wrong class for view '%s'",
2132135446Strhodes			      zname, vname);
2133135446Strhodes		result = ISC_R_FAILURE;
2134135446Strhodes		goto cleanup;
2135135446Strhodes	}
2136135446Strhodes
2137135446Strhodes	(void)cfg_map_get(zoptions, "type", &typeobj);
2138135446Strhodes	if (typeobj == NULL) {
2139135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
2140135446Strhodes			    "zone '%s' 'type' not specified", zname);
2141135446Strhodes		return (ISC_R_FAILURE);
2142135446Strhodes	}
2143135446Strhodes	ztypestr = cfg_obj_asstring(typeobj);
2144135446Strhodes
2145135446Strhodes	/*
2146135446Strhodes	 * "hints zones" aren't zones.  If we've got one,
2147135446Strhodes	 * configure it and return.
2148135446Strhodes	 */
2149135446Strhodes	if (strcasecmp(ztypestr, "hint") == 0) {
2150165071Sdougb		const cfg_obj_t *fileobj = NULL;
2151135446Strhodes		if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) {
2152135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2153135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
2154135446Strhodes				      "zone '%s': 'file' not specified",
2155135446Strhodes				      zname);
2156135446Strhodes			result = ISC_R_FAILURE;
2157135446Strhodes			goto cleanup;
2158135446Strhodes		}
2159135446Strhodes		if (dns_name_equal(origin, dns_rootname)) {
2160165071Sdougb			const char *hintsfile = cfg_obj_asstring(fileobj);
2161135446Strhodes
2162135446Strhodes			result = configure_hints(view, hintsfile);
2163135446Strhodes			if (result != ISC_R_SUCCESS) {
2164135446Strhodes				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2165135446Strhodes					      NS_LOGMODULE_SERVER,
2166135446Strhodes					      ISC_LOG_ERROR,
2167135446Strhodes					      "could not configure root hints "
2168135446Strhodes					      "from '%s': %s", hintsfile,
2169135446Strhodes					      isc_result_totext(result));
2170135446Strhodes				goto cleanup;
2171135446Strhodes			}
2172135446Strhodes			/*
2173135446Strhodes			 * Hint zones may also refer to delegation only points.
2174135446Strhodes			 */
2175135446Strhodes			only = NULL;
2176135446Strhodes			tresult = cfg_map_get(zoptions, "delegation-only",
2177135446Strhodes					      &only);
2178135446Strhodes			if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only))
2179135446Strhodes				CHECK(dns_view_adddelegationonly(view, origin));
2180135446Strhodes		} else {
2181135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2182135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
2183135446Strhodes				      "ignoring non-root hint zone '%s'",
2184135446Strhodes				      zname);
2185135446Strhodes			result = ISC_R_SUCCESS;
2186135446Strhodes		}
2187135446Strhodes		/* Skip ordinary zone processing. */
2188135446Strhodes		goto cleanup;
2189135446Strhodes	}
2190135446Strhodes
2191135446Strhodes	/*
2192135446Strhodes	 * "forward zones" aren't zones either.  Translate this syntax into
2193135446Strhodes	 * the appropriate selective forwarding configuration and return.
2194135446Strhodes	 */
2195135446Strhodes	if (strcasecmp(ztypestr, "forward") == 0) {
2196135446Strhodes		forwardtype = NULL;
2197135446Strhodes		forwarders = NULL;
2198135446Strhodes
2199135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
2200135446Strhodes		(void)cfg_map_get(zoptions, "forwarders", &forwarders);
2201135446Strhodes		result = configure_forward(config, view, origin, forwarders,
2202135446Strhodes					   forwardtype);
2203135446Strhodes		goto cleanup;
2204135446Strhodes	}
2205135446Strhodes
2206135446Strhodes	/*
2207135446Strhodes	 * "delegation-only zones" aren't zones either.
2208135446Strhodes	 */
2209135446Strhodes	if (strcasecmp(ztypestr, "delegation-only") == 0) {
2210135446Strhodes		result = dns_view_adddelegationonly(view, origin);
2211135446Strhodes		goto cleanup;
2212135446Strhodes	}
2213135446Strhodes
2214135446Strhodes	/*
2215135446Strhodes	 * Check for duplicates in the new zone table.
2216135446Strhodes	 */
2217135446Strhodes	result = dns_view_findzone(view, origin, &dupzone);
2218135446Strhodes	if (result == ISC_R_SUCCESS) {
2219135446Strhodes		/*
2220135446Strhodes		 * We already have this zone!
2221135446Strhodes		 */
2222135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
2223135446Strhodes			    "zone '%s' already exists", zname);
2224135446Strhodes		dns_zone_detach(&dupzone);
2225135446Strhodes		result = ISC_R_EXISTS;
2226135446Strhodes		goto cleanup;
2227135446Strhodes	}
2228135446Strhodes	INSIST(dupzone == NULL);
2229135446Strhodes
2230135446Strhodes	/*
2231135446Strhodes	 * See if we can reuse an existing zone.  This is
2232135446Strhodes	 * only possible if all of these are true:
2233135446Strhodes	 *   - The zone's view exists
2234135446Strhodes	 *   - A zone with the right name exists in the view
2235135446Strhodes	 *   - The zone is compatible with the config
2236135446Strhodes	 *     options (e.g., an existing master zone cannot
2237135446Strhodes	 *     be reused if the options specify a slave zone)
2238135446Strhodes	 */
2239135446Strhodes	result = dns_viewlist_find(&ns_g_server->viewlist,
2240135446Strhodes				   view->name, view->rdclass,
2241135446Strhodes				   &pview);
2242135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
2243135446Strhodes		goto cleanup;
2244135446Strhodes	if (pview != NULL)
2245135446Strhodes		result = dns_view_findzone(pview, origin, &zone);
2246135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
2247135446Strhodes		goto cleanup;
2248170222Sdougb	if (zone != NULL && !ns_zone_reusable(zone, zconfig))
2249170222Sdougb		dns_zone_detach(&zone);
2250135446Strhodes
2251135446Strhodes	if (zone != NULL) {
2252135446Strhodes		/*
2253135446Strhodes		 * We found a reusable zone.  Make it use the
2254135446Strhodes		 * new view.
2255135446Strhodes		 */
2256135446Strhodes		dns_zone_setview(zone, view);
2257170222Sdougb		if (view->acache != NULL)
2258170222Sdougb			dns_zone_setacache(zone, view->acache);
2259135446Strhodes	} else {
2260135446Strhodes		/*
2261135446Strhodes		 * We cannot reuse an existing zone, we have
2262135446Strhodes		 * to create a new one.
2263135446Strhodes		 */
2264135446Strhodes		CHECK(dns_zone_create(&zone, mctx));
2265135446Strhodes		CHECK(dns_zone_setorigin(zone, origin));
2266135446Strhodes		dns_zone_setview(zone, view);
2267170222Sdougb		if (view->acache != NULL)
2268170222Sdougb			dns_zone_setacache(zone, view->acache);
2269135446Strhodes		CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
2270135446Strhodes	}
2271135446Strhodes
2272135446Strhodes	/*
2273135446Strhodes	 * If the zone contains a 'forwarders' statement, configure
2274135446Strhodes	 * selective forwarding.
2275135446Strhodes	 */
2276135446Strhodes	forwarders = NULL;
2277135446Strhodes	if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS)
2278135446Strhodes	{
2279135446Strhodes		forwardtype = NULL;
2280135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
2281135446Strhodes		CHECK(configure_forward(config, view, origin, forwarders,
2282135446Strhodes					forwardtype));
2283135446Strhodes	}
2284135446Strhodes
2285135446Strhodes	/*
2286135446Strhodes	 * Stub and forward zones may also refer to delegation only points.
2287135446Strhodes	 */
2288135446Strhodes	only = NULL;
2289135446Strhodes	if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS)
2290135446Strhodes	{
2291135446Strhodes		if (cfg_obj_asboolean(only))
2292135446Strhodes			CHECK(dns_view_adddelegationonly(view, origin));
2293135446Strhodes	}
2294135446Strhodes
2295135446Strhodes	/*
2296135446Strhodes	 * Configure the zone.
2297135446Strhodes	 */
2298135446Strhodes	CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone));
2299135446Strhodes
2300135446Strhodes	/*
2301135446Strhodes	 * Add the zone to its view in the new view list.
2302135446Strhodes	 */
2303135446Strhodes	CHECK(dns_view_addzone(view, zone));
2304135446Strhodes
2305135446Strhodes cleanup:
2306135446Strhodes	if (zone != NULL)
2307135446Strhodes		dns_zone_detach(&zone);
2308135446Strhodes	if (pview != NULL)
2309135446Strhodes		dns_view_detach(&pview);
2310135446Strhodes
2311135446Strhodes	return (result);
2312135446Strhodes}
2313135446Strhodes
2314135446Strhodes/*
2315135446Strhodes * Configure a single server quota.
2316135446Strhodes */
2317135446Strhodesstatic void
2318165071Sdougbconfigure_server_quota(const cfg_obj_t **maps, const char *name,
2319165071Sdougb		       isc_quota_t *quota)
2320135446Strhodes{
2321165071Sdougb	const cfg_obj_t *obj = NULL;
2322135446Strhodes	isc_result_t result;
2323135446Strhodes
2324135446Strhodes	result = ns_config_get(maps, name, &obj);
2325135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2326153816Sdougb	isc_quota_max(quota, cfg_obj_asuint32(obj));
2327135446Strhodes}
2328135446Strhodes
2329135446Strhodes/*
2330135446Strhodes * This function is called as soon as the 'directory' statement has been
2331135446Strhodes * parsed.  This can be extended to support other options if necessary.
2332135446Strhodes */
2333135446Strhodesstatic isc_result_t
2334165071Sdougbdirectory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
2335135446Strhodes	isc_result_t result;
2336165071Sdougb	const char *directory;
2337135446Strhodes
2338135446Strhodes	REQUIRE(strcasecmp("directory", clausename) == 0);
2339135446Strhodes
2340135446Strhodes	UNUSED(arg);
2341135446Strhodes	UNUSED(clausename);
2342135446Strhodes
2343135446Strhodes	/*
2344135446Strhodes	 * Change directory.
2345135446Strhodes	 */
2346135446Strhodes	directory = cfg_obj_asstring(obj);
2347135446Strhodes
2348135446Strhodes	if (! isc_file_ischdiridempotent(directory))
2349135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
2350135446Strhodes			    "option 'directory' contains relative path '%s'",
2351135446Strhodes			    directory);
2352135446Strhodes
2353135446Strhodes	result = isc_dir_chdir(directory);
2354135446Strhodes	if (result != ISC_R_SUCCESS) {
2355135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
2356135446Strhodes			    "change directory to '%s' failed: %s",
2357135446Strhodes			    directory, isc_result_totext(result));
2358135446Strhodes		return (result);
2359135446Strhodes	}
2360135446Strhodes
2361135446Strhodes	return (ISC_R_SUCCESS);
2362135446Strhodes}
2363135446Strhodes
2364135446Strhodesstatic void
2365135446Strhodesscan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
2366135446Strhodes	isc_boolean_t match_mapped = server->aclenv.match_mapped;
2367135446Strhodes
2368135446Strhodes	ns_interfacemgr_scan(server->interfacemgr, verbose);
2369135446Strhodes	/*
2370135446Strhodes	 * Update the "localhost" and "localnets" ACLs to match the
2371135446Strhodes	 * current set of network interfaces.
2372135446Strhodes	 */
2373135446Strhodes	dns_aclenv_copy(&server->aclenv,
2374135446Strhodes			ns_interfacemgr_getaclenv(server->interfacemgr));
2375135446Strhodes
2376135446Strhodes	server->aclenv.match_mapped = match_mapped;
2377135446Strhodes}
2378135446Strhodes
2379135446Strhodesstatic isc_result_t
2380180477Sdougbadd_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
2381180477Sdougb	      isc_boolean_t wcardport_ok)
2382180477Sdougb{
2383135446Strhodes	ns_listenelt_t *lelt = NULL;
2384135446Strhodes	dns_acl_t *src_acl = NULL;
2385135446Strhodes	dns_aclelement_t aelt;
2386135446Strhodes	isc_result_t result;
2387135446Strhodes	isc_sockaddr_t any_sa6;
2388135446Strhodes
2389135446Strhodes	REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
2390135446Strhodes
2391135446Strhodes	isc_sockaddr_any6(&any_sa6);
2392180477Sdougb	if (!isc_sockaddr_equal(&any_sa6, addr) &&
2393180477Sdougb	    (wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
2394135446Strhodes		aelt.type = dns_aclelementtype_ipprefix;
2395135446Strhodes		aelt.negative = ISC_FALSE;
2396135446Strhodes		aelt.u.ip_prefix.prefixlen = 128;
2397135446Strhodes		isc_netaddr_fromin6(&aelt.u.ip_prefix.address,
2398135446Strhodes				    &addr->type.sin6.sin6_addr);
2399135446Strhodes
2400135446Strhodes		result = dns_acl_create(mctx, 1, &src_acl);
2401135446Strhodes		if (result != ISC_R_SUCCESS)
2402135446Strhodes			return (result);
2403135446Strhodes		result = dns_acl_appendelement(src_acl, &aelt);
2404135446Strhodes		if (result != ISC_R_SUCCESS)
2405135446Strhodes			goto clean;
2406135446Strhodes
2407135446Strhodes		result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr),
2408135446Strhodes					     src_acl, &lelt);
2409135446Strhodes		if (result != ISC_R_SUCCESS)
2410135446Strhodes			goto clean;
2411135446Strhodes		ISC_LIST_APPEND(list->elts, lelt, link);
2412135446Strhodes	}
2413135446Strhodes
2414135446Strhodes	return (ISC_R_SUCCESS);
2415135446Strhodes
2416135446Strhodes clean:
2417135446Strhodes	INSIST(lelt == NULL);
2418165071Sdougb	dns_acl_detach(&src_acl);
2419135446Strhodes
2420135446Strhodes	return (result);
2421135446Strhodes}
2422135446Strhodes
2423135446Strhodes/*
2424135446Strhodes * Make a list of xxx-source addresses and call ns_interfacemgr_adjust()
2425135446Strhodes * to update the listening interfaces accordingly.
2426135446Strhodes * We currently only consider IPv6, because this only affects IPv6 wildcard
2427135446Strhodes * sockets.
2428135446Strhodes */
2429135446Strhodesstatic void
2430135446Strhodesadjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
2431135446Strhodes	isc_result_t result;
2432135446Strhodes	ns_listenlist_t *list = NULL;
2433135446Strhodes	dns_view_t *view;
2434135446Strhodes	dns_zone_t *zone, *next;
2435135446Strhodes	isc_sockaddr_t addr, *addrp;
2436135446Strhodes
2437135446Strhodes	result = ns_listenlist_create(mctx, &list);
2438135446Strhodes	if (result != ISC_R_SUCCESS)
2439135446Strhodes		return;
2440135446Strhodes
2441135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
2442135446Strhodes	     view != NULL;
2443135446Strhodes	     view = ISC_LIST_NEXT(view, link)) {
2444135446Strhodes		dns_dispatch_t *dispatch6;
2445135446Strhodes
2446135446Strhodes		dispatch6 = dns_resolver_dispatchv6(view->resolver);
2447143731Sdougb		if (dispatch6 == NULL)
2448143731Sdougb			continue;
2449135446Strhodes		result = dns_dispatch_getlocaladdress(dispatch6, &addr);
2450135446Strhodes		if (result != ISC_R_SUCCESS)
2451135446Strhodes			goto fail;
2452180477Sdougb
2453180477Sdougb		/*
2454180477Sdougb		 * We always add non-wildcard address regardless of whether
2455180477Sdougb		 * the port is 'any' (the fourth arg is TRUE): if the port is
2456180477Sdougb		 * specific, we need to add it since it may conflict with a
2457180477Sdougb		 * listening interface; if it's zero, we'll dynamically open
2458180477Sdougb		 * query ports, and some of them may override an existing
2459180477Sdougb		 * wildcard IPv6 port.
2460180477Sdougb		 */
2461180477Sdougb		result = add_listenelt(mctx, list, &addr, ISC_TRUE);
2462135446Strhodes		if (result != ISC_R_SUCCESS)
2463135446Strhodes			goto fail;
2464135446Strhodes	}
2465135446Strhodes
2466135446Strhodes	zone = NULL;
2467135446Strhodes	for (result = dns_zone_first(server->zonemgr, &zone);
2468135446Strhodes	     result == ISC_R_SUCCESS;
2469135446Strhodes	     next = NULL, result = dns_zone_next(zone, &next), zone = next) {
2470135446Strhodes		dns_view_t *zoneview;
2471135446Strhodes
2472135446Strhodes		/*
2473135446Strhodes		 * At this point the zone list may contain a stale zone
2474135446Strhodes		 * just removed from the configuration.  To see the validity,
2475135446Strhodes		 * check if the corresponding view is in our current view list.
2476153816Sdougb		 * There may also be old zones that are still in the process
2477153816Sdougb		 * of shutting down and have detached from their old view
2478153816Sdougb		 * (zoneview == NULL).
2479135446Strhodes		 */
2480135446Strhodes		zoneview = dns_zone_getview(zone);
2481153816Sdougb		if (zoneview == NULL)
2482153816Sdougb			continue;
2483135446Strhodes		for (view = ISC_LIST_HEAD(server->viewlist);
2484135446Strhodes		     view != NULL && view != zoneview;
2485135446Strhodes		     view = ISC_LIST_NEXT(view, link))
2486135446Strhodes			;
2487135446Strhodes		if (view == NULL)
2488135446Strhodes			continue;
2489135446Strhodes
2490135446Strhodes		addrp = dns_zone_getnotifysrc6(zone);
2491180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
2492135446Strhodes		if (result != ISC_R_SUCCESS)
2493135446Strhodes			goto fail;
2494135446Strhodes
2495135446Strhodes		addrp = dns_zone_getxfrsource6(zone);
2496180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
2497135446Strhodes		if (result != ISC_R_SUCCESS)
2498135446Strhodes			goto fail;
2499135446Strhodes	}
2500135446Strhodes
2501135446Strhodes	ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE);
2502135446Strhodes
2503135446Strhodes clean:
2504135446Strhodes	ns_listenlist_detach(&list);
2505135446Strhodes	return;
2506135446Strhodes
2507135446Strhodes fail:
2508135446Strhodes	/*
2509135446Strhodes	 * Even when we failed the procedure, most of other interfaces
2510135446Strhodes	 * should work correctly.  We therefore just warn it.
2511135446Strhodes	 */
2512135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2513135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
2514135446Strhodes		      "could not adjust the listen-on list; "
2515135446Strhodes		      "some interfaces may not work");
2516135446Strhodes	goto clean;
2517135446Strhodes}
2518135446Strhodes
2519135446Strhodes/*
2520135446Strhodes * This event callback is invoked to do periodic network
2521135446Strhodes * interface scanning.
2522135446Strhodes */
2523135446Strhodesstatic void
2524135446Strhodesinterface_timer_tick(isc_task_t *task, isc_event_t *event) {
2525135446Strhodes	isc_result_t result;
2526135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
2527135446Strhodes	INSIST(task == server->task);
2528135446Strhodes	UNUSED(task);
2529135446Strhodes	isc_event_free(&event);
2530135446Strhodes	/*
2531135446Strhodes	 * XXX should scan interfaces unlocked and get exclusive access
2532135446Strhodes	 * only to replace ACLs.
2533135446Strhodes	 */
2534135446Strhodes	result = isc_task_beginexclusive(server->task);
2535135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
2536135446Strhodes	scan_interfaces(server, ISC_FALSE);
2537135446Strhodes	isc_task_endexclusive(server->task);
2538135446Strhodes}
2539135446Strhodes
2540135446Strhodesstatic void
2541135446Strhodesheartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
2542135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
2543135446Strhodes	dns_view_t *view;
2544135446Strhodes
2545135446Strhodes	UNUSED(task);
2546135446Strhodes	isc_event_free(&event);
2547135446Strhodes	view = ISC_LIST_HEAD(server->viewlist);
2548135446Strhodes	while (view != NULL) {
2549135446Strhodes		dns_view_dialup(view);
2550135446Strhodes		view = ISC_LIST_NEXT(view, link);
2551135446Strhodes	}
2552135446Strhodes}
2553135446Strhodes
2554170222Sdougbstatic void
2555170222Sdougbpps_timer_tick(isc_task_t *task, isc_event_t *event) {
2556170222Sdougb	static unsigned int oldrequests = 0;
2557170222Sdougb	unsigned int requests = ns_client_requests;
2558170222Sdougb
2559170222Sdougb	UNUSED(task);
2560170222Sdougb	isc_event_free(&event);
2561170222Sdougb
2562170222Sdougb	/*
2563170222Sdougb	 * Don't worry about wrapping as the overflow result will be right.
2564170222Sdougb	 */
2565170222Sdougb	dns_pps = (requests - oldrequests) / 1200;
2566170222Sdougb	oldrequests = requests;
2567170222Sdougb}
2568170222Sdougb
2569135446Strhodes/*
2570135446Strhodes * Replace the current value of '*field', a dynamically allocated
2571135446Strhodes * string or NULL, with a dynamically allocated copy of the
2572135446Strhodes * null-terminated string pointed to by 'value', or NULL.
2573135446Strhodes */
2574135446Strhodesstatic isc_result_t
2575135446Strhodessetstring(ns_server_t *server, char **field, const char *value) {
2576135446Strhodes	char *copy;
2577135446Strhodes
2578135446Strhodes	if (value != NULL) {
2579135446Strhodes		copy = isc_mem_strdup(server->mctx, value);
2580135446Strhodes		if (copy == NULL)
2581135446Strhodes			return (ISC_R_NOMEMORY);
2582135446Strhodes	} else {
2583135446Strhodes		copy = NULL;
2584135446Strhodes	}
2585135446Strhodes
2586135446Strhodes	if (*field != NULL)
2587135446Strhodes		isc_mem_free(server->mctx, *field);
2588135446Strhodes
2589135446Strhodes	*field = copy;
2590135446Strhodes	return (ISC_R_SUCCESS);
2591135446Strhodes}
2592135446Strhodes
2593135446Strhodes/*
2594135446Strhodes * Replace the current value of '*field', a dynamically allocated
2595135446Strhodes * string or NULL, with another dynamically allocated string
2596135446Strhodes * or NULL if whether 'obj' is a string or void value, respectively.
2597135446Strhodes */
2598135446Strhodesstatic isc_result_t
2599165071Sdougbsetoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) {
2600135446Strhodes	if (cfg_obj_isvoid(obj))
2601135446Strhodes		return (setstring(server, field, NULL));
2602135446Strhodes	else
2603135446Strhodes		return (setstring(server, field, cfg_obj_asstring(obj)));
2604135446Strhodes}
2605135446Strhodes
2606135446Strhodesstatic void
2607165071Sdougbset_limit(const cfg_obj_t **maps, const char *configname,
2608165071Sdougb	  const char *description, isc_resource_t resourceid,
2609165071Sdougb	  isc_resourcevalue_t defaultvalue)
2610135446Strhodes{
2611165071Sdougb	const cfg_obj_t *obj = NULL;
2612165071Sdougb	const char *resource;
2613135446Strhodes	isc_resourcevalue_t value;
2614135446Strhodes	isc_result_t result;
2615135446Strhodes
2616135446Strhodes	if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS)
2617135446Strhodes		return;
2618135446Strhodes
2619135446Strhodes	if (cfg_obj_isstring(obj)) {
2620135446Strhodes		resource = cfg_obj_asstring(obj);
2621135446Strhodes		if (strcasecmp(resource, "unlimited") == 0)
2622135446Strhodes			value = ISC_RESOURCE_UNLIMITED;
2623135446Strhodes		else {
2624135446Strhodes			INSIST(strcasecmp(resource, "default") == 0);
2625135446Strhodes			value = defaultvalue;
2626135446Strhodes		}
2627135446Strhodes	} else
2628135446Strhodes		value = cfg_obj_asuint64(obj);
2629135446Strhodes
2630135446Strhodes	result = isc_resource_setlimit(resourceid, value);
2631135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
2632135446Strhodes		      result == ISC_R_SUCCESS ?
2633135446Strhodes		      	ISC_LOG_DEBUG(3) : ISC_LOG_WARNING,
2634135446Strhodes		      "set maximum %s to %" ISC_PRINT_QUADFORMAT "d: %s",
2635135446Strhodes		      description, value, isc_result_totext(result));
2636135446Strhodes}
2637135446Strhodes
2638135446Strhodes#define SETLIMIT(cfgvar, resource, description) \
2639135446Strhodes	set_limit(maps, cfgvar, description, isc_resource_ ## resource, \
2640135446Strhodes		  ns_g_init ## resource)
2641135446Strhodes
2642135446Strhodesstatic void
2643165071Sdougbset_limits(const cfg_obj_t **maps) {
2644135446Strhodes	SETLIMIT("stacksize", stacksize, "stack size");
2645135446Strhodes	SETLIMIT("datasize", datasize, "data size");
2646135446Strhodes	SETLIMIT("coresize", coresize, "core size");
2647135446Strhodes	SETLIMIT("files", openfiles, "open files");
2648135446Strhodes}
2649135446Strhodes
2650135446Strhodesstatic isc_result_t
2651135446Strhodesportlist_fromconf(dns_portlist_t *portlist, unsigned int family,
2652165071Sdougb		  const cfg_obj_t *ports)
2653135446Strhodes{
2654165071Sdougb	const cfg_listelt_t *element;
2655135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
2656135446Strhodes
2657135446Strhodes	for (element = cfg_list_first(ports);
2658135446Strhodes	     element != NULL;
2659135446Strhodes	     element = cfg_list_next(element)) {
2660165071Sdougb		const cfg_obj_t *obj = cfg_listelt_value(element);
2661135446Strhodes		in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
2662135446Strhodes
2663135446Strhodes		result = dns_portlist_add(portlist, family, port);
2664135446Strhodes		if (result != ISC_R_SUCCESS)
2665135446Strhodes			break;
2666135446Strhodes	}
2667135446Strhodes	return (result);
2668135446Strhodes}
2669135446Strhodes
2670135446Strhodesstatic isc_result_t
2671170222Sdougbremoved(dns_zone_t *zone, void *uap) {
2672170222Sdougb	const char *type;
2673170222Sdougb
2674170222Sdougb        if (dns_zone_getview(zone) != uap)
2675170222Sdougb		return (ISC_R_SUCCESS);
2676170222Sdougb
2677170222Sdougb	switch (dns_zone_gettype(zone)) {
2678170222Sdougb	case dns_zone_master:
2679170222Sdougb		type = "master";
2680170222Sdougb		break;
2681170222Sdougb	case dns_zone_slave:
2682170222Sdougb		type = "slave";
2683170222Sdougb		break;
2684170222Sdougb	case dns_zone_stub:
2685170222Sdougb		type = "stub";
2686170222Sdougb		break;
2687170222Sdougb	default:
2688170222Sdougb		type = "other";
2689170222Sdougb		break;
2690170222Sdougb	}
2691170222Sdougb	dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type);
2692170222Sdougb	return (ISC_R_SUCCESS);
2693170222Sdougb}
2694170222Sdougb
2695170222Sdougbstatic isc_result_t
2696135446Strhodesload_configuration(const char *filename, ns_server_t *server,
2697135446Strhodes		   isc_boolean_t first_time)
2698135446Strhodes{
2699182645Sdougb	cfg_aclconfctx_t aclconfctx;
2700182645Sdougb	cfg_obj_t *config;
2701135446Strhodes	cfg_parser_t *parser = NULL;
2702182645Sdougb	const cfg_listelt_t *element;
2703182645Sdougb	const cfg_obj_t *builtin_views;
2704182645Sdougb	const cfg_obj_t *maps[3];
2705182645Sdougb	const cfg_obj_t *obj;
2706165071Sdougb	const cfg_obj_t *options;
2707182645Sdougb	const cfg_obj_t *v4ports, *v6ports;
2708165071Sdougb	const cfg_obj_t *views;
2709135446Strhodes	dns_view_t *view = NULL;
2710135446Strhodes	dns_view_t *view_next;
2711182645Sdougb	dns_viewlist_t tmpviewlist;
2712135446Strhodes	dns_viewlist_t viewlist;
2713182645Sdougb	in_port_t listen_port;
2714182645Sdougb	int i;
2715182645Sdougb	isc_interval_t interval;
2716182645Sdougb	isc_resourcevalue_t files;
2717182645Sdougb	isc_result_t result;
2718182645Sdougb	isc_uint32_t heartbeat_interval;
2719135446Strhodes	isc_uint32_t interface_interval;
2720182645Sdougb	isc_uint32_t reserved;
2721135446Strhodes	isc_uint32_t udpsize;
2722135446Strhodes
2723170222Sdougb	cfg_aclconfctx_init(&aclconfctx);
2724135446Strhodes	ISC_LIST_INIT(viewlist);
2725135446Strhodes
2726135446Strhodes	/* Ensure exclusive access to configuration data. */
2727135446Strhodes	result = isc_task_beginexclusive(server->task);
2728135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
2729135446Strhodes
2730135446Strhodes	/*
2731135446Strhodes	 * Parse the global default pseudo-config file.
2732135446Strhodes	 */
2733135446Strhodes	if (first_time) {
2734135446Strhodes		CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config));
2735135446Strhodes		RUNTIME_CHECK(cfg_map_get(ns_g_config, "options",
2736135446Strhodes					  &ns_g_defaults) ==
2737135446Strhodes			      ISC_R_SUCCESS);
2738135446Strhodes	}
2739135446Strhodes
2740135446Strhodes	/*
2741135446Strhodes	 * Parse the configuration file using the new config code.
2742135446Strhodes	 */
2743135446Strhodes	result = ISC_R_FAILURE;
2744135446Strhodes	config = NULL;
2745135446Strhodes
2746135446Strhodes	/*
2747135446Strhodes	 * Unless this is lwresd with the -C option, parse the config file.
2748135446Strhodes	 */
2749135446Strhodes	if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) {
2750135446Strhodes		isc_log_write(ns_g_lctx,
2751135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
2752135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
2753135446Strhodes			      filename);
2754135446Strhodes		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &parser));
2755135446Strhodes		cfg_parser_setcallback(parser, directory_callback, NULL);
2756135446Strhodes		result = cfg_parse_file(parser, filename, &cfg_type_namedconf,
2757135446Strhodes					&config);
2758135446Strhodes	}
2759135446Strhodes
2760135446Strhodes	/*
2761135446Strhodes	 * If this is lwresd with the -C option, or lwresd with no -C or -c
2762135446Strhodes	 * option where the above parsing failed, parse resolv.conf.
2763135446Strhodes	 */
2764135446Strhodes	if (ns_g_lwresdonly &&
2765135446Strhodes	    (lwresd_g_useresolvconf ||
2766135446Strhodes	     (!ns_g_conffileset && result == ISC_R_FILENOTFOUND)))
2767135446Strhodes	{
2768135446Strhodes		isc_log_write(ns_g_lctx,
2769135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
2770135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
2771135446Strhodes			      lwresd_g_resolvconffile);
2772135446Strhodes		if (parser != NULL)
2773135446Strhodes			cfg_parser_destroy(&parser);
2774135446Strhodes		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &parser));
2775135446Strhodes		result = ns_lwresd_parseeresolvconf(ns_g_mctx, parser,
2776135446Strhodes						    &config);
2777135446Strhodes	}
2778135446Strhodes	CHECK(result);
2779135446Strhodes
2780135446Strhodes	/*
2781135446Strhodes	 * Check the validity of the configuration.
2782135446Strhodes	 */
2783135446Strhodes	CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx));
2784135446Strhodes
2785135446Strhodes	/*
2786135446Strhodes	 * Fill in the maps array, used for resolving defaults.
2787135446Strhodes	 */
2788135446Strhodes	i = 0;
2789135446Strhodes	options = NULL;
2790135446Strhodes	result = cfg_map_get(config, "options", &options);
2791135446Strhodes	if (result == ISC_R_SUCCESS)
2792135446Strhodes		maps[i++] = options;
2793135446Strhodes	maps[i++] = ns_g_defaults;
2794135446Strhodes	maps[i++] = NULL;
2795135446Strhodes
2796135446Strhodes	/*
2797135446Strhodes	 * Set process limits, which (usually) needs to be done as root.
2798135446Strhodes	 */
2799135446Strhodes	set_limits(maps);
2800135446Strhodes
2801135446Strhodes	/*
2802182645Sdougb	 * Sanity check on "files" limit.
2803182645Sdougb	 */
2804182645Sdougb	result = isc_resource_curlimit(isc_resource_openfiles, &files);
2805182645Sdougb	if (result == ISC_R_SUCCESS && files < FD_SETSIZE) {
2806182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2807182645Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
2808182645Sdougb			      "the 'files' limit (%" ISC_PRINT_QUADFORMAT "u) "
2809182645Sdougb			      "is less than FD_SETSIZE (%d), increase "
2810182645Sdougb			      "'files' in named.conf or recompile with a "
2811182645Sdougb			      "smaller FD_SETSIZE.", files, FD_SETSIZE);
2812182645Sdougb		if (files > FD_SETSIZE)
2813182645Sdougb			files = FD_SETSIZE;
2814182645Sdougb	} else
2815182645Sdougb		files = FD_SETSIZE;
2816182645Sdougb
2817182645Sdougb	/*
2818182645Sdougb	 * Set the number of socket reserved for TCP, stdio etc.
2819182645Sdougb	 */
2820182645Sdougb	obj = NULL;
2821182645Sdougb	result = ns_config_get(maps, "reserved-sockets", &obj);
2822182645Sdougb	INSIST(result == ISC_R_SUCCESS);
2823182645Sdougb	reserved = cfg_obj_asuint32(obj);
2824182645Sdougb	if (files < 128U)			/* Prevent underflow. */
2825182645Sdougb		reserved = 0;
2826182645Sdougb	else if (reserved > files - 128U)	/* Mimimum UDP space. */
2827182645Sdougb		reserved = files - 128;
2828182645Sdougb	if (reserved < 128U)			/* Mimimum TCP/stdio space. */
2829182645Sdougb		reserved = 128;
2830182645Sdougb	if (reserved + 128U > files) {
2831182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2832182645Sdougb                              NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
2833182645Sdougb			      "less than 128 UDP sockets available after "
2834182645Sdougb			      "applying 'reserved-sockets' and 'files'");
2835182645Sdougb	}
2836182645Sdougb	isc__socketmgr_setreserved(ns_g_socketmgr, reserved);
2837182645Sdougb
2838182645Sdougb	/*
2839135446Strhodes	 * Configure various server options.
2840135446Strhodes	 */
2841135446Strhodes	configure_server_quota(maps, "transfers-out", &server->xfroutquota);
2842135446Strhodes	configure_server_quota(maps, "tcp-clients", &server->tcpquota);
2843135446Strhodes	configure_server_quota(maps, "recursive-clients",
2844135446Strhodes			       &server->recursionquota);
2845153816Sdougb	if (server->recursionquota.max > 1000)
2846153816Sdougb		isc_quota_soft(&server->recursionquota,
2847153816Sdougb			       server->recursionquota.max - 100);
2848153816Sdougb	else
2849153816Sdougb		isc_quota_soft(&server->recursionquota, 0);
2850135446Strhodes
2851135446Strhodes	CHECK(configure_view_acl(NULL, config, "blackhole", &aclconfctx,
2852135446Strhodes				 ns_g_mctx, &server->blackholeacl));
2853135446Strhodes	if (server->blackholeacl != NULL)
2854135446Strhodes		dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
2855135446Strhodes					     server->blackholeacl);
2856135446Strhodes
2857135446Strhodes	obj = NULL;
2858135446Strhodes	result = ns_config_get(maps, "match-mapped-addresses", &obj);
2859135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2860135446Strhodes	server->aclenv.match_mapped = cfg_obj_asboolean(obj);
2861135446Strhodes
2862135446Strhodes	v4ports = NULL;
2863135446Strhodes	v6ports = NULL;
2864135446Strhodes	(void)ns_config_get(maps, "avoid-v4-udp-ports", &v4ports);
2865135446Strhodes	(void)ns_config_get(maps, "avoid-v6-udp-ports", &v6ports);
2866135446Strhodes	if (v4ports != NULL || v6ports != NULL) {
2867135446Strhodes		dns_portlist_t *portlist = NULL;
2868135446Strhodes		result = dns_portlist_create(ns_g_mctx, &portlist);
2869135446Strhodes		if (result == ISC_R_SUCCESS && v4ports != NULL)
2870135446Strhodes			result = portlist_fromconf(portlist, AF_INET, v4ports);
2871135446Strhodes		if (result == ISC_R_SUCCESS && v6ports != NULL)
2872135446Strhodes			portlist_fromconf(portlist, AF_INET6, v6ports);
2873135446Strhodes		if (result == ISC_R_SUCCESS)
2874135446Strhodes			dns_dispatchmgr_setblackportlist(ns_g_dispatchmgr, portlist);
2875135446Strhodes		if (portlist != NULL)
2876135446Strhodes			dns_portlist_detach(&portlist);
2877135446Strhodes		CHECK(result);
2878135446Strhodes	} else
2879135446Strhodes		dns_dispatchmgr_setblackportlist(ns_g_dispatchmgr, NULL);
2880135446Strhodes
2881135446Strhodes	/*
2882135446Strhodes	 * Set the EDNS UDP size when we don't match a view.
2883135446Strhodes	 */
2884135446Strhodes	obj = NULL;
2885135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
2886135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2887135446Strhodes	udpsize = cfg_obj_asuint32(obj);
2888135446Strhodes	if (udpsize < 512)
2889135446Strhodes		udpsize = 512;
2890135446Strhodes	if (udpsize > 4096)
2891135446Strhodes		udpsize = 4096;
2892135446Strhodes	ns_g_udpsize = (isc_uint16_t)udpsize;
2893135446Strhodes
2894135446Strhodes	/*
2895135446Strhodes	 * Configure the zone manager.
2896135446Strhodes	 */
2897135446Strhodes	obj = NULL;
2898135446Strhodes	result = ns_config_get(maps, "transfers-in", &obj);
2899135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2900135446Strhodes	dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj));
2901135446Strhodes
2902135446Strhodes	obj = NULL;
2903135446Strhodes	result = ns_config_get(maps, "transfers-per-ns", &obj);
2904135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2905135446Strhodes	dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj));
2906135446Strhodes
2907135446Strhodes	obj = NULL;
2908135446Strhodes	result = ns_config_get(maps, "serial-query-rate", &obj);
2909135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2910135446Strhodes	dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj));
2911135446Strhodes
2912135446Strhodes	/*
2913135446Strhodes	 * Determine which port to use for listening for incoming connections.
2914135446Strhodes	 */
2915135446Strhodes	if (ns_g_port != 0)
2916135446Strhodes		listen_port = ns_g_port;
2917135446Strhodes	else
2918135446Strhodes		CHECKM(ns_config_getport(config, &listen_port), "port");
2919135446Strhodes
2920135446Strhodes	/*
2921135446Strhodes	 * Find the listen queue depth.
2922135446Strhodes	 */
2923135446Strhodes	obj = NULL;
2924135446Strhodes	result = ns_config_get(maps, "tcp-listen-queue", &obj);
2925135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2926135446Strhodes	ns_g_listen = cfg_obj_asuint32(obj);
2927135446Strhodes	if (ns_g_listen < 3)
2928135446Strhodes		ns_g_listen = 3;
2929135446Strhodes
2930135446Strhodes	/*
2931135446Strhodes	 * Configure the interface manager according to the "listen-on"
2932135446Strhodes	 * statement.
2933135446Strhodes	 */
2934135446Strhodes	{
2935165071Sdougb		const cfg_obj_t *clistenon = NULL;
2936135446Strhodes		ns_listenlist_t *listenon = NULL;
2937135446Strhodes
2938135446Strhodes		clistenon = NULL;
2939135446Strhodes		/*
2940135446Strhodes		 * Even though listen-on is present in the default
2941135446Strhodes		 * configuration, we can't use it here, since it isn't
2942135446Strhodes		 * used if we're in lwresd mode.  This way is easier.
2943135446Strhodes		 */
2944135446Strhodes		if (options != NULL)
2945135446Strhodes			(void)cfg_map_get(options, "listen-on", &clistenon);
2946135446Strhodes		if (clistenon != NULL) {
2947135446Strhodes			result = ns_listenlist_fromconfig(clistenon,
2948135446Strhodes							  config,
2949135446Strhodes							  &aclconfctx,
2950135446Strhodes							  ns_g_mctx,
2951135446Strhodes							  &listenon);
2952135446Strhodes		} else if (!ns_g_lwresdonly) {
2953135446Strhodes			/*
2954135446Strhodes			 * Not specified, use default.
2955135446Strhodes			 */
2956135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
2957135446Strhodes						    ISC_TRUE, &listenon));
2958135446Strhodes		}
2959135446Strhodes		if (listenon != NULL) {
2960135446Strhodes			ns_interfacemgr_setlistenon4(server->interfacemgr,
2961135446Strhodes						     listenon);
2962135446Strhodes			ns_listenlist_detach(&listenon);
2963135446Strhodes		}
2964135446Strhodes	}
2965135446Strhodes	/*
2966135446Strhodes	 * Ditto for IPv6.
2967135446Strhodes	 */
2968135446Strhodes	{
2969165071Sdougb		const cfg_obj_t *clistenon = NULL;
2970135446Strhodes		ns_listenlist_t *listenon = NULL;
2971135446Strhodes
2972135446Strhodes		if (options != NULL)
2973135446Strhodes			(void)cfg_map_get(options, "listen-on-v6", &clistenon);
2974135446Strhodes		if (clistenon != NULL) {
2975135446Strhodes			result = ns_listenlist_fromconfig(clistenon,
2976135446Strhodes							  config,
2977135446Strhodes							  &aclconfctx,
2978135446Strhodes							  ns_g_mctx,
2979135446Strhodes							  &listenon);
2980135446Strhodes		} else if (!ns_g_lwresdonly) {
2981135446Strhodes			/*
2982135446Strhodes			 * Not specified, use default.
2983135446Strhodes			 */
2984135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
2985135446Strhodes						    ISC_FALSE, &listenon));
2986135446Strhodes		}
2987135446Strhodes		if (listenon != NULL) {
2988135446Strhodes			ns_interfacemgr_setlistenon6(server->interfacemgr,
2989135446Strhodes						     listenon);
2990135446Strhodes			ns_listenlist_detach(&listenon);
2991135446Strhodes		}
2992135446Strhodes	}
2993135446Strhodes
2994135446Strhodes	/*
2995135446Strhodes	 * Rescan the interface list to pick up changes in the
2996135446Strhodes	 * listen-on option.  It's important that we do this before we try
2997135446Strhodes	 * to configure the query source, since the dispatcher we use might
2998135446Strhodes	 * be shared with an interface.
2999135446Strhodes	 */
3000135446Strhodes	scan_interfaces(server, ISC_TRUE);
3001135446Strhodes
3002135446Strhodes	/*
3003135446Strhodes	 * Arrange for further interface scanning to occur periodically
3004135446Strhodes	 * as specified by the "interface-interval" option.
3005135446Strhodes	 */
3006135446Strhodes	obj = NULL;
3007135446Strhodes	result = ns_config_get(maps, "interface-interval", &obj);
3008135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3009135446Strhodes	interface_interval = cfg_obj_asuint32(obj) * 60;
3010135446Strhodes	if (interface_interval == 0) {
3011135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
3012135446Strhodes				      isc_timertype_inactive,
3013135446Strhodes				      NULL, NULL, ISC_TRUE));
3014135446Strhodes	} else if (server->interface_interval != interface_interval) {
3015135446Strhodes		isc_interval_set(&interval, interface_interval, 0);
3016135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
3017135446Strhodes				      isc_timertype_ticker,
3018135446Strhodes				      NULL, &interval, ISC_FALSE));
3019135446Strhodes	}
3020135446Strhodes	server->interface_interval = interface_interval;
3021135446Strhodes
3022135446Strhodes	/*
3023135446Strhodes	 * Configure the dialup heartbeat timer.
3024135446Strhodes	 */
3025135446Strhodes	obj = NULL;
3026135446Strhodes	result = ns_config_get(maps, "heartbeat-interval", &obj);
3027135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3028135446Strhodes	heartbeat_interval = cfg_obj_asuint32(obj) * 60;
3029135446Strhodes	if (heartbeat_interval == 0) {
3030135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
3031135446Strhodes				      isc_timertype_inactive,
3032135446Strhodes				      NULL, NULL, ISC_TRUE));
3033135446Strhodes	} else if (server->heartbeat_interval != heartbeat_interval) {
3034135446Strhodes		isc_interval_set(&interval, heartbeat_interval, 0);
3035135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
3036135446Strhodes				      isc_timertype_ticker,
3037135446Strhodes				      NULL, &interval, ISC_FALSE));
3038135446Strhodes	}
3039135446Strhodes	server->heartbeat_interval = heartbeat_interval;
3040170222Sdougb
3041170222Sdougb	isc_interval_set(&interval, 1200, 0);
3042170222Sdougb	CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
3043170222Sdougb			      &interval, ISC_FALSE));
3044135446Strhodes
3045135446Strhodes	/*
3046135446Strhodes	 * Configure and freeze all explicit views.  Explicit
3047135446Strhodes	 * views that have zones were already created at parsing
3048135446Strhodes	 * time, but views with no zones must be created here.
3049135446Strhodes	 */
3050135446Strhodes	views = NULL;
3051135446Strhodes	(void)cfg_map_get(config, "view", &views);
3052135446Strhodes	for (element = cfg_list_first(views);
3053135446Strhodes	     element != NULL;
3054135446Strhodes	     element = cfg_list_next(element))
3055135446Strhodes	{
3056165071Sdougb		const cfg_obj_t *vconfig = cfg_listelt_value(element);
3057135446Strhodes		view = NULL;
3058135446Strhodes
3059135446Strhodes		CHECK(create_view(vconfig, &viewlist, &view));
3060135446Strhodes		INSIST(view != NULL);
3061135446Strhodes		CHECK(configure_view(view, config, vconfig,
3062135446Strhodes				     ns_g_mctx, &aclconfctx, ISC_TRUE));
3063135446Strhodes		dns_view_freeze(view);
3064135446Strhodes		dns_view_detach(&view);
3065135446Strhodes	}
3066135446Strhodes
3067135446Strhodes	/*
3068135446Strhodes	 * Make sure we have a default view if and only if there
3069135446Strhodes	 * were no explicit views.
3070135446Strhodes	 */
3071135446Strhodes	if (views == NULL) {
3072135446Strhodes		/*
3073135446Strhodes		 * No explicit views; there ought to be a default view.
3074135446Strhodes		 * There may already be one created as a side effect
3075135446Strhodes		 * of zone statements, or we may have to create one.
3076135446Strhodes		 * In either case, we need to configure and freeze it.
3077135446Strhodes		 */
3078135446Strhodes		CHECK(create_view(NULL, &viewlist, &view));
3079135446Strhodes		CHECK(configure_view(view, config, NULL, ns_g_mctx,
3080135446Strhodes				     &aclconfctx, ISC_TRUE));
3081135446Strhodes		dns_view_freeze(view);
3082135446Strhodes		dns_view_detach(&view);
3083135446Strhodes	}
3084135446Strhodes
3085135446Strhodes	/*
3086135446Strhodes	 * Create (or recreate) the built-in views.  Currently
3087135446Strhodes	 * there is only one, the _bind view.
3088135446Strhodes	 */
3089135446Strhodes	builtin_views = NULL;
3090135446Strhodes	RUNTIME_CHECK(cfg_map_get(ns_g_config, "view",
3091135446Strhodes				  &builtin_views) == ISC_R_SUCCESS);
3092135446Strhodes	for (element = cfg_list_first(builtin_views);
3093135446Strhodes	     element != NULL;
3094135446Strhodes	     element = cfg_list_next(element))
3095135446Strhodes	{
3096165071Sdougb		const cfg_obj_t *vconfig = cfg_listelt_value(element);
3097135446Strhodes		CHECK(create_view(vconfig, &viewlist, &view));
3098135446Strhodes		CHECK(configure_view(view, config, vconfig, ns_g_mctx,
3099135446Strhodes				     &aclconfctx, ISC_FALSE));
3100135446Strhodes		dns_view_freeze(view);
3101135446Strhodes		dns_view_detach(&view);
3102135446Strhodes		view = NULL;
3103135446Strhodes	}
3104135446Strhodes
3105135446Strhodes	/*
3106135446Strhodes	 * Swap our new view list with the production one.
3107135446Strhodes	 */
3108135446Strhodes	tmpviewlist = server->viewlist;
3109135446Strhodes	server->viewlist = viewlist;
3110135446Strhodes	viewlist = tmpviewlist;
3111135446Strhodes
3112135446Strhodes	/*
3113135446Strhodes	 * Load the TKEY information from the configuration.
3114135446Strhodes	 */
3115135446Strhodes	if (options != NULL) {
3116135446Strhodes		dns_tkeyctx_t *t = NULL;
3117135446Strhodes		CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy,
3118135446Strhodes					     &t),
3119135446Strhodes		       "configuring TKEY");
3120135446Strhodes		if (server->tkeyctx != NULL)
3121135446Strhodes			dns_tkeyctx_destroy(&server->tkeyctx);
3122135446Strhodes		server->tkeyctx = t;
3123135446Strhodes	}
3124135446Strhodes
3125135446Strhodes	/*
3126135446Strhodes	 * Bind the control port(s).
3127135446Strhodes	 */
3128135446Strhodes	CHECKM(ns_controls_configure(ns_g_server->controls, config,
3129135446Strhodes				     &aclconfctx),
3130135446Strhodes	       "binding control channel(s)");
3131135446Strhodes
3132135446Strhodes	/*
3133135446Strhodes	 * Bind the lwresd port(s).
3134135446Strhodes	 */
3135135446Strhodes	CHECKM(ns_lwresd_configure(ns_g_mctx, config),
3136135446Strhodes	       "binding lightweight resolver ports");
3137135446Strhodes
3138135446Strhodes	/*
3139135446Strhodes	 * Open the source of entropy.
3140135446Strhodes	 */
3141135446Strhodes	if (first_time) {
3142135446Strhodes		obj = NULL;
3143135446Strhodes		result = ns_config_get(maps, "random-device", &obj);
3144135446Strhodes		if (result != ISC_R_SUCCESS) {
3145135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3146135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
3147135446Strhodes				      "no source of entropy found");
3148135446Strhodes		} else {
3149135446Strhodes			const char *randomdev = cfg_obj_asstring(obj);
3150135446Strhodes			result = isc_entropy_createfilesource(ns_g_entropy,
3151135446Strhodes							      randomdev);
3152135446Strhodes			if (result != ISC_R_SUCCESS)
3153135446Strhodes				isc_log_write(ns_g_lctx,
3154135446Strhodes					      NS_LOGCATEGORY_GENERAL,
3155135446Strhodes					      NS_LOGMODULE_SERVER,
3156135446Strhodes					      ISC_LOG_INFO,
3157135446Strhodes					      "could not open entropy source "
3158135446Strhodes					      "%s: %s",
3159135446Strhodes					      randomdev,
3160135446Strhodes					      isc_result_totext(result));
3161135446Strhodes#ifdef PATH_RANDOMDEV
3162135446Strhodes			if (ns_g_fallbackentropy != NULL) {
3163135446Strhodes				if (result != ISC_R_SUCCESS) {
3164135446Strhodes					isc_log_write(ns_g_lctx,
3165135446Strhodes						      NS_LOGCATEGORY_GENERAL,
3166135446Strhodes						      NS_LOGMODULE_SERVER,
3167135446Strhodes						      ISC_LOG_INFO,
3168135446Strhodes						      "using pre-chroot entropy source "
3169135446Strhodes						      "%s",
3170135446Strhodes						      PATH_RANDOMDEV);
3171135446Strhodes					isc_entropy_detach(&ns_g_entropy);
3172135446Strhodes					isc_entropy_attach(ns_g_fallbackentropy,
3173135446Strhodes							   &ns_g_entropy);
3174135446Strhodes				}
3175135446Strhodes				isc_entropy_detach(&ns_g_fallbackentropy);
3176135446Strhodes			}
3177135446Strhodes#endif
3178135446Strhodes		}
3179135446Strhodes	}
3180135446Strhodes
3181135446Strhodes	/*
3182135446Strhodes	 * Relinquish root privileges.
3183135446Strhodes	 */
3184135446Strhodes	if (first_time)
3185135446Strhodes		ns_os_changeuser();
3186135446Strhodes
3187135446Strhodes	/*
3188135446Strhodes	 * Configure the logging system.
3189135446Strhodes	 *
3190135446Strhodes	 * Do this after changing UID to make sure that any log
3191135446Strhodes	 * files specified in named.conf get created by the
3192135446Strhodes	 * unprivileged user, not root.
3193135446Strhodes	 */
3194135446Strhodes	if (ns_g_logstderr) {
3195135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3196135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
3197135446Strhodes			      "ignoring config file logging "
3198135446Strhodes			      "statement due to -g option");
3199135446Strhodes	} else {
3200165071Sdougb		const cfg_obj_t *logobj = NULL;
3201135446Strhodes		isc_logconfig_t *logc = NULL;
3202135446Strhodes
3203135446Strhodes		CHECKM(isc_logconfig_create(ns_g_lctx, &logc),
3204135446Strhodes		       "creating new logging configuration");
3205135446Strhodes
3206135446Strhodes		logobj = NULL;
3207135446Strhodes		(void)cfg_map_get(config, "logging", &logobj);
3208135446Strhodes		if (logobj != NULL) {
3209135446Strhodes			CHECKM(ns_log_configure(logc, logobj),
3210135446Strhodes			       "configuring logging");
3211135446Strhodes		} else {
3212135446Strhodes			CHECKM(ns_log_setdefaultchannels(logc),
3213135446Strhodes			       "setting up default logging channels");
3214135446Strhodes			CHECKM(ns_log_setunmatchedcategory(logc),
3215135446Strhodes			       "setting up default 'category unmatched'");
3216135446Strhodes			CHECKM(ns_log_setdefaultcategory(logc),
3217135446Strhodes			       "setting up default 'category default'");
3218135446Strhodes		}
3219135446Strhodes
3220135446Strhodes		result = isc_logconfig_use(ns_g_lctx, logc);
3221135446Strhodes		if (result != ISC_R_SUCCESS) {
3222135446Strhodes			isc_logconfig_destroy(&logc);
3223135446Strhodes			CHECKM(result, "installing logging configuration");
3224135446Strhodes		}
3225135446Strhodes
3226135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3227135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
3228135446Strhodes			      "now using logging configuration from "
3229135446Strhodes			      "config file");
3230135446Strhodes	}
3231135446Strhodes
3232135446Strhodes	/*
3233135446Strhodes	 * Set the default value of the query logging flag depending
3234135446Strhodes	 * whether a "queries" category has been defined.  This is
3235135446Strhodes	 * a disgusting hack, but we need to do this for BIND 8
3236135446Strhodes	 * compatibility.
3237135446Strhodes	 */
3238135446Strhodes	if (first_time) {
3239165071Sdougb		const cfg_obj_t *logobj = NULL;
3240165071Sdougb		const cfg_obj_t *categories = NULL;
3241135446Strhodes
3242135446Strhodes		obj = NULL;
3243135446Strhodes		if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) {
3244135446Strhodes			server->log_queries = cfg_obj_asboolean(obj);
3245135446Strhodes		} else {
3246135446Strhodes
3247135446Strhodes			(void)cfg_map_get(config, "logging", &logobj);
3248135446Strhodes			if (logobj != NULL)
3249135446Strhodes				(void)cfg_map_get(logobj, "category",
3250135446Strhodes						  &categories);
3251135446Strhodes			if (categories != NULL) {
3252165071Sdougb				const cfg_listelt_t *element;
3253135446Strhodes				for (element = cfg_list_first(categories);
3254135446Strhodes				     element != NULL;
3255135446Strhodes				     element = cfg_list_next(element))
3256135446Strhodes				{
3257165071Sdougb					const cfg_obj_t *catobj;
3258165071Sdougb					const char *str;
3259135446Strhodes
3260135446Strhodes					obj = cfg_listelt_value(element);
3261135446Strhodes					catobj = cfg_tuple_get(obj, "name");
3262135446Strhodes					str = cfg_obj_asstring(catobj);
3263135446Strhodes					if (strcasecmp(str, "queries") == 0)
3264135446Strhodes						server->log_queries = ISC_TRUE;
3265135446Strhodes				}
3266135446Strhodes			}
3267135446Strhodes		}
3268135446Strhodes	}
3269135446Strhodes
3270135446Strhodes	obj = NULL;
3271135446Strhodes	if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS)
3272135446Strhodes		if (cfg_obj_isvoid(obj))
3273135446Strhodes			ns_os_writepidfile(NULL, first_time);
3274135446Strhodes		else
3275135446Strhodes			ns_os_writepidfile(cfg_obj_asstring(obj), first_time);
3276135446Strhodes	else if (ns_g_lwresdonly)
3277135446Strhodes		ns_os_writepidfile(lwresd_g_defaultpidfile, first_time);
3278135446Strhodes	else
3279135446Strhodes		ns_os_writepidfile(ns_g_defaultpidfile, first_time);
3280135446Strhodes
3281135446Strhodes	obj = NULL;
3282135446Strhodes	if (options != NULL &&
3283135446Strhodes	    cfg_map_get(options, "memstatistics-file", &obj) == ISC_R_SUCCESS)
3284135446Strhodes		ns_main_setmemstats(cfg_obj_asstring(obj));
3285135446Strhodes	else
3286135446Strhodes		ns_main_setmemstats(NULL);
3287135446Strhodes
3288135446Strhodes	obj = NULL;
3289135446Strhodes	result = ns_config_get(maps, "statistics-file", &obj);
3290135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3291135446Strhodes	CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)),
3292135446Strhodes	       "strdup");
3293135446Strhodes
3294135446Strhodes	obj = NULL;
3295135446Strhodes	result = ns_config_get(maps, "dump-file", &obj);
3296135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3297135446Strhodes	CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)),
3298135446Strhodes	       "strdup");
3299135446Strhodes
3300135446Strhodes	obj = NULL;
3301135446Strhodes	result = ns_config_get(maps, "recursing-file", &obj);
3302135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3303135446Strhodes	CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)),
3304135446Strhodes	       "strdup");
3305135446Strhodes
3306135446Strhodes	obj = NULL;
3307135446Strhodes	result = ns_config_get(maps, "version", &obj);
3308135446Strhodes	if (result == ISC_R_SUCCESS) {
3309135446Strhodes		CHECKM(setoptstring(server, &server->version, obj), "strdup");
3310135446Strhodes		server->version_set = ISC_TRUE;
3311135446Strhodes	} else {
3312135446Strhodes		server->version_set = ISC_FALSE;
3313135446Strhodes	}
3314135446Strhodes
3315135446Strhodes	obj = NULL;
3316135446Strhodes	result = ns_config_get(maps, "hostname", &obj);
3317135446Strhodes	if (result == ISC_R_SUCCESS) {
3318135446Strhodes		CHECKM(setoptstring(server, &server->hostname, obj), "strdup");
3319135446Strhodes		server->hostname_set = ISC_TRUE;
3320135446Strhodes	} else {
3321135446Strhodes		server->hostname_set = ISC_FALSE;
3322135446Strhodes	}
3323135446Strhodes
3324135446Strhodes	obj = NULL;
3325135446Strhodes	result = ns_config_get(maps, "server-id", &obj);
3326135446Strhodes	server->server_usehostname = ISC_FALSE;
3327135446Strhodes	if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
3328135446Strhodes		server->server_usehostname = ISC_TRUE;
3329135446Strhodes	} else if (result == ISC_R_SUCCESS) {
3330135446Strhodes		CHECKM(setoptstring(server, &server->server_id, obj), "strdup");
3331135446Strhodes	} else {
3332170222Sdougb		result = setstring(server, &server->server_id, NULL);
3333135446Strhodes		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3334135446Strhodes	}
3335135446Strhodes
3336135446Strhodes	obj = NULL;
3337135446Strhodes	result = ns_config_get(maps, "flush-zones-on-shutdown", &obj);
3338135446Strhodes	if (result == ISC_R_SUCCESS) {
3339135446Strhodes		server->flushonshutdown = cfg_obj_asboolean(obj);
3340135446Strhodes	} else {
3341135446Strhodes		server->flushonshutdown = ISC_FALSE;
3342135446Strhodes	}
3343135446Strhodes
3344135446Strhodes	result = ISC_R_SUCCESS;
3345135446Strhodes
3346135446Strhodes cleanup:
3347170222Sdougb	cfg_aclconfctx_destroy(&aclconfctx);
3348135446Strhodes
3349135446Strhodes	if (parser != NULL) {
3350135446Strhodes		if (config != NULL)
3351135446Strhodes			cfg_obj_destroy(parser, &config);
3352135446Strhodes		cfg_parser_destroy(&parser);
3353135446Strhodes	}
3354135446Strhodes
3355135446Strhodes	if (view != NULL)
3356135446Strhodes		dns_view_detach(&view);
3357135446Strhodes
3358135446Strhodes	/*
3359135446Strhodes	 * This cleans up either the old production view list
3360135446Strhodes	 * or our temporary list depending on whether they
3361135446Strhodes	 * were swapped above or not.
3362135446Strhodes	 */
3363135446Strhodes	for (view = ISC_LIST_HEAD(viewlist);
3364135446Strhodes	     view != NULL;
3365135446Strhodes	     view = view_next) {
3366135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
3367135446Strhodes		ISC_LIST_UNLINK(viewlist, view, link);
3368170222Sdougb		if (result == ISC_R_SUCCESS &&
3369170222Sdougb		    strcmp(view->name, "_bind") != 0)
3370170222Sdougb			(void)dns_zt_apply(view->zonetable, ISC_FALSE,
3371170222Sdougb					   removed, view);
3372135446Strhodes		dns_view_detach(&view);
3373135446Strhodes	}
3374135446Strhodes
3375135446Strhodes	/*
3376135446Strhodes	 * Adjust the listening interfaces in accordance with the source
3377135446Strhodes	 * addresses specified in views and zones.
3378135446Strhodes	 */
3379135446Strhodes	if (isc_net_probeipv6() == ISC_R_SUCCESS)
3380135446Strhodes		adjust_interfaces(server, ns_g_mctx);
3381135446Strhodes
3382135446Strhodes	/* Relinquish exclusive access to configuration data. */
3383135446Strhodes	isc_task_endexclusive(server->task);
3384135446Strhodes
3385135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
3386135446Strhodes		      ISC_LOG_DEBUG(1), "load_configuration: %s",
3387135446Strhodes		      isc_result_totext(result));
3388135446Strhodes
3389135446Strhodes	return (result);
3390135446Strhodes}
3391135446Strhodes
3392135446Strhodesstatic isc_result_t
3393135446Strhodesload_zones(ns_server_t *server, isc_boolean_t stop) {
3394135446Strhodes	isc_result_t result;
3395135446Strhodes	dns_view_t *view;
3396135446Strhodes
3397135446Strhodes	result = isc_task_beginexclusive(server->task);
3398135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3399135446Strhodes
3400135446Strhodes	/*
3401135446Strhodes	 * Load zone data from disk.
3402135446Strhodes	 */
3403135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
3404135446Strhodes	     view != NULL;
3405135446Strhodes	     view = ISC_LIST_NEXT(view, link))
3406135446Strhodes	{
3407135446Strhodes		CHECK(dns_view_load(view, stop));
3408135446Strhodes	}
3409135446Strhodes
3410135446Strhodes	/*
3411135446Strhodes	 * Force zone maintenance.  Do this after loading
3412135446Strhodes	 * so that we know when we need to force AXFR of
3413135446Strhodes	 * slave zones whose master files are missing.
3414135446Strhodes	 */
3415135446Strhodes	CHECK(dns_zonemgr_forcemaint(server->zonemgr));
3416135446Strhodes cleanup:
3417135446Strhodes	isc_task_endexclusive(server->task);
3418135446Strhodes	return (result);
3419135446Strhodes}
3420135446Strhodes
3421135446Strhodesstatic isc_result_t
3422135446Strhodesload_new_zones(ns_server_t *server, isc_boolean_t stop) {
3423135446Strhodes	isc_result_t result;
3424135446Strhodes	dns_view_t *view;
3425135446Strhodes
3426135446Strhodes	result = isc_task_beginexclusive(server->task);
3427135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3428135446Strhodes
3429135446Strhodes	/*
3430135446Strhodes	 * Load zone data from disk.
3431135446Strhodes	 */
3432135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
3433135446Strhodes	     view != NULL;
3434135446Strhodes	     view = ISC_LIST_NEXT(view, link))
3435135446Strhodes	{
3436135446Strhodes		CHECK(dns_view_loadnew(view, stop));
3437135446Strhodes	}
3438135446Strhodes	/*
3439135446Strhodes	 * Force zone maintenance.  Do this after loading
3440135446Strhodes	 * so that we know when we need to force AXFR of
3441135446Strhodes	 * slave zones whose master files are missing.
3442135446Strhodes	 */
3443135446Strhodes	dns_zonemgr_resumexfrs(server->zonemgr);
3444135446Strhodes cleanup:
3445135446Strhodes	isc_task_endexclusive(server->task);
3446135446Strhodes	return (result);
3447135446Strhodes}
3448135446Strhodes
3449135446Strhodesstatic void
3450135446Strhodesrun_server(isc_task_t *task, isc_event_t *event) {
3451135446Strhodes	isc_result_t result;
3452135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
3453135446Strhodes
3454143731Sdougb	INSIST(task == server->task);
3455135446Strhodes
3456135446Strhodes	isc_event_free(&event);
3457135446Strhodes
3458135446Strhodes	CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy,
3459135446Strhodes					  &ns_g_dispatchmgr),
3460135446Strhodes		   "creating dispatch manager");
3461135446Strhodes
3462135446Strhodes	CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr,
3463135446Strhodes					  ns_g_socketmgr, ns_g_dispatchmgr,
3464135446Strhodes					  &server->interfacemgr),
3465135446Strhodes		   "creating interface manager");
3466135446Strhodes
3467135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
3468135446Strhodes				    NULL, NULL, server->task,
3469135446Strhodes				    interface_timer_tick,
3470135446Strhodes				    server, &server->interface_timer),
3471135446Strhodes		   "creating interface timer");
3472135446Strhodes
3473135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
3474135446Strhodes				    NULL, NULL, server->task,
3475135446Strhodes				    heartbeat_timer_tick,
3476135446Strhodes				    server, &server->heartbeat_timer),
3477135446Strhodes		   "creating heartbeat timer");
3478135446Strhodes
3479170222Sdougb	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
3480170222Sdougb				    NULL, NULL, server->task, pps_timer_tick,
3481170222Sdougb				    server, &server->pps_timer),
3482170222Sdougb		   "creating pps timer");
3483170222Sdougb
3484135446Strhodes	CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser),
3485135446Strhodes		   "creating default configuration parser");
3486135446Strhodes
3487135446Strhodes	if (ns_g_lwresdonly)
3488135446Strhodes		CHECKFATAL(load_configuration(lwresd_g_conffile, server,
3489135446Strhodes					      ISC_TRUE),
3490135446Strhodes			   "loading configuration");
3491135446Strhodes	else
3492135446Strhodes		CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE),
3493135446Strhodes			   "loading configuration");
3494135446Strhodes
3495135446Strhodes	isc_hash_init();
3496135446Strhodes
3497143731Sdougb	CHECKFATAL(load_zones(server, ISC_FALSE), "loading zones");
3498135446Strhodes
3499143731Sdougb	ns_os_started();
3500135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
3501143731Sdougb		      ISC_LOG_NOTICE, "running");
3502135446Strhodes}
3503135446Strhodes
3504135446Strhodesvoid
3505135446Strhodesns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) {
3506135446Strhodes
3507135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
3508135446Strhodes
3509135446Strhodes	server->flushonshutdown = flush;
3510135446Strhodes}
3511135446Strhodes
3512135446Strhodesstatic void
3513135446Strhodesshutdown_server(isc_task_t *task, isc_event_t *event) {
3514135446Strhodes	isc_result_t result;
3515135446Strhodes	dns_view_t *view, *view_next;
3516135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
3517135446Strhodes	isc_boolean_t flush = server->flushonshutdown;
3518135446Strhodes
3519135446Strhodes	UNUSED(task);
3520135446Strhodes	INSIST(task == server->task);
3521135446Strhodes
3522135446Strhodes	result = isc_task_beginexclusive(server->task);
3523135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3524135446Strhodes
3525135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
3526135446Strhodes		      ISC_LOG_INFO, "shutting down%s",
3527135446Strhodes		      flush ? ": flushing changes" : "");
3528135446Strhodes
3529135446Strhodes	ns_controls_shutdown(server->controls);
3530135446Strhodes	end_reserved_dispatches(server, ISC_TRUE);
3531135446Strhodes
3532135446Strhodes	cfg_obj_destroy(ns_g_parser, &ns_g_config);
3533135446Strhodes	cfg_parser_destroy(&ns_g_parser);
3534135446Strhodes
3535135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
3536135446Strhodes	     view != NULL;
3537135446Strhodes	     view = view_next) {
3538135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
3539135446Strhodes		ISC_LIST_UNLINK(server->viewlist, view, link);
3540135446Strhodes		if (flush)
3541135446Strhodes			dns_view_flushanddetach(&view);
3542135446Strhodes		else
3543135446Strhodes			dns_view_detach(&view);
3544135446Strhodes	}
3545135446Strhodes
3546135446Strhodes	isc_timer_detach(&server->interface_timer);
3547135446Strhodes	isc_timer_detach(&server->heartbeat_timer);
3548170222Sdougb	isc_timer_detach(&server->pps_timer);
3549135446Strhodes
3550135446Strhodes	ns_interfacemgr_shutdown(server->interfacemgr);
3551135446Strhodes	ns_interfacemgr_detach(&server->interfacemgr);
3552135446Strhodes
3553135446Strhodes	dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
3554135446Strhodes
3555135446Strhodes	dns_zonemgr_shutdown(server->zonemgr);
3556135446Strhodes
3557135446Strhodes	if (server->blackholeacl != NULL)
3558135446Strhodes		dns_acl_detach(&server->blackholeacl);
3559135446Strhodes
3560135446Strhodes	dns_db_detach(&server->in_roothints);
3561135446Strhodes
3562135446Strhodes	isc_task_endexclusive(server->task);
3563135446Strhodes
3564135446Strhodes	isc_task_detach(&server->task);
3565135446Strhodes
3566135446Strhodes	isc_event_free(&event);
3567135446Strhodes}
3568135446Strhodes
3569135446Strhodesvoid
3570135446Strhodesns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
3571135446Strhodes	isc_result_t result;
3572135446Strhodes
3573135446Strhodes	ns_server_t *server = isc_mem_get(mctx, sizeof(*server));
3574135446Strhodes	if (server == NULL)
3575135446Strhodes		fatal("allocating server object", ISC_R_NOMEMORY);
3576135446Strhodes
3577135446Strhodes	server->mctx = mctx;
3578135446Strhodes	server->task = NULL;
3579135446Strhodes
3580135446Strhodes	/* Initialize configuration data with default values. */
3581135446Strhodes
3582135446Strhodes	result = isc_quota_init(&server->xfroutquota, 10);
3583135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3584135446Strhodes	result = isc_quota_init(&server->tcpquota, 10);
3585135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3586135446Strhodes	result = isc_quota_init(&server->recursionquota, 100);
3587135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3588135446Strhodes
3589135446Strhodes	result = dns_aclenv_init(mctx, &server->aclenv);
3590135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3591135446Strhodes
3592135446Strhodes	/* Initialize server data structures. */
3593135446Strhodes	server->zonemgr = NULL;
3594135446Strhodes	server->interfacemgr = NULL;
3595135446Strhodes	ISC_LIST_INIT(server->viewlist);
3596135446Strhodes	server->in_roothints = NULL;
3597135446Strhodes	server->blackholeacl = NULL;
3598135446Strhodes
3599135446Strhodes	CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
3600135446Strhodes				     &server->in_roothints),
3601135446Strhodes		   "setting up root hints");
3602135446Strhodes
3603135446Strhodes	CHECKFATAL(isc_mutex_init(&server->reload_event_lock),
3604135446Strhodes		   "initializing reload event lock");
3605135446Strhodes	server->reload_event =
3606135446Strhodes		isc_event_allocate(ns_g_mctx, server,
3607135446Strhodes				   NS_EVENT_RELOAD,
3608135446Strhodes				   ns_server_reload,
3609135446Strhodes				   server,
3610135446Strhodes				   sizeof(isc_event_t));
3611135446Strhodes	CHECKFATAL(server->reload_event == NULL ?
3612135446Strhodes		   ISC_R_NOMEMORY : ISC_R_SUCCESS,
3613135446Strhodes		   "allocating reload event");
3614135446Strhodes
3615135446Strhodes	CHECKFATAL(dst_lib_init(ns_g_mctx, ns_g_entropy, ISC_ENTROPY_GOODONLY),
3616135446Strhodes		   "initializing DST");
3617135446Strhodes
3618135446Strhodes	server->tkeyctx = NULL;
3619135446Strhodes	CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
3620135446Strhodes				      &server->tkeyctx),
3621135446Strhodes		   "creating TKEY context");
3622135446Strhodes
3623135446Strhodes	/*
3624135446Strhodes	 * Setup the server task, which is responsible for coordinating
3625135446Strhodes	 * startup and shutdown of the server.
3626135446Strhodes	 */
3627135446Strhodes	CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
3628135446Strhodes		   "creating server task");
3629135446Strhodes	isc_task_setname(server->task, "server", server);
3630135446Strhodes	CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
3631135446Strhodes		   "isc_task_onshutdown");
3632135446Strhodes	CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),
3633135446Strhodes		   "isc_app_onrun");
3634135446Strhodes
3635135446Strhodes	server->interface_timer = NULL;
3636135446Strhodes	server->heartbeat_timer = NULL;
3637170222Sdougb	server->pps_timer = NULL;
3638135446Strhodes
3639135446Strhodes	server->interface_interval = 0;
3640135446Strhodes	server->heartbeat_interval = 0;
3641135446Strhodes
3642135446Strhodes	CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr,
3643135446Strhodes				      ns_g_socketmgr, &server->zonemgr),
3644135446Strhodes		   "dns_zonemgr_create");
3645135446Strhodes
3646135446Strhodes	server->statsfile = isc_mem_strdup(server->mctx, "named.stats");
3647135446Strhodes	CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
3648135446Strhodes		   "isc_mem_strdup");
3649135446Strhodes	server->querystats = NULL;
3650135446Strhodes
3651135446Strhodes	server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db");
3652135446Strhodes	CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
3653135446Strhodes		   "isc_mem_strdup");
3654135446Strhodes
3655135446Strhodes	server->recfile = isc_mem_strdup(server->mctx, "named.recursing");
3656135446Strhodes	CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
3657135446Strhodes		   "isc_mem_strdup");
3658135446Strhodes
3659135446Strhodes	server->hostname_set = ISC_FALSE;
3660135446Strhodes	server->hostname = NULL;
3661135446Strhodes	server->version_set = ISC_FALSE;
3662135446Strhodes	server->version = NULL;
3663135446Strhodes	server->server_usehostname = ISC_FALSE;
3664135446Strhodes	server->server_id = NULL;
3665135446Strhodes
3666135446Strhodes	CHECKFATAL(dns_stats_alloccounters(ns_g_mctx, &server->querystats),
3667135446Strhodes		   "dns_stats_alloccounters");
3668135446Strhodes
3669135446Strhodes	server->flushonshutdown = ISC_FALSE;
3670135446Strhodes	server->log_queries = ISC_FALSE;
3671135446Strhodes
3672135446Strhodes	server->controls = NULL;
3673135446Strhodes	CHECKFATAL(ns_controls_create(server, &server->controls),
3674135446Strhodes		   "ns_controls_create");
3675135446Strhodes	server->dispatchgen = 0;
3676135446Strhodes	ISC_LIST_INIT(server->dispatches);
3677135446Strhodes
3678135446Strhodes	server->magic = NS_SERVER_MAGIC;
3679135446Strhodes	*serverp = server;
3680135446Strhodes}
3681135446Strhodes
3682135446Strhodesvoid
3683135446Strhodesns_server_destroy(ns_server_t **serverp) {
3684135446Strhodes	ns_server_t *server = *serverp;
3685135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
3686135446Strhodes
3687135446Strhodes	ns_controls_destroy(&server->controls);
3688135446Strhodes
3689135446Strhodes	dns_stats_freecounters(server->mctx, &server->querystats);
3690135446Strhodes
3691135446Strhodes	isc_mem_free(server->mctx, server->statsfile);
3692135446Strhodes	isc_mem_free(server->mctx, server->dumpfile);
3693135446Strhodes	isc_mem_free(server->mctx, server->recfile);
3694135446Strhodes
3695135446Strhodes	if (server->version != NULL)
3696135446Strhodes		isc_mem_free(server->mctx, server->version);
3697135446Strhodes	if (server->hostname != NULL)
3698135446Strhodes		isc_mem_free(server->mctx, server->hostname);
3699135446Strhodes	if (server->server_id != NULL)
3700135446Strhodes		isc_mem_free(server->mctx, server->server_id);
3701135446Strhodes
3702135446Strhodes	dns_zonemgr_detach(&server->zonemgr);
3703135446Strhodes
3704135446Strhodes	if (server->tkeyctx != NULL)
3705135446Strhodes		dns_tkeyctx_destroy(&server->tkeyctx);
3706135446Strhodes
3707135446Strhodes	dst_lib_destroy();
3708135446Strhodes
3709135446Strhodes	isc_event_free(&server->reload_event);
3710135446Strhodes
3711135446Strhodes	INSIST(ISC_LIST_EMPTY(server->viewlist));
3712135446Strhodes
3713135446Strhodes	dns_aclenv_destroy(&server->aclenv);
3714135446Strhodes
3715135446Strhodes	isc_quota_destroy(&server->recursionquota);
3716135446Strhodes	isc_quota_destroy(&server->tcpquota);
3717135446Strhodes	isc_quota_destroy(&server->xfroutquota);
3718135446Strhodes
3719135446Strhodes	server->magic = 0;
3720135446Strhodes	isc_mem_put(server->mctx, server, sizeof(*server));
3721135446Strhodes	*serverp = NULL;
3722135446Strhodes}
3723135446Strhodes
3724135446Strhodesstatic void
3725135446Strhodesfatal(const char *msg, isc_result_t result) {
3726135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
3727135446Strhodes		      ISC_LOG_CRITICAL, "%s: %s", msg,
3728135446Strhodes		      isc_result_totext(result));
3729135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
3730135446Strhodes		      ISC_LOG_CRITICAL, "exiting (due to fatal error)");
3731135446Strhodes	exit(1);
3732135446Strhodes}
3733135446Strhodes
3734135446Strhodesstatic void
3735135446Strhodesstart_reserved_dispatches(ns_server_t *server) {
3736135446Strhodes
3737135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
3738135446Strhodes
3739135446Strhodes	server->dispatchgen++;
3740135446Strhodes}
3741135446Strhodes
3742135446Strhodesstatic void
3743135446Strhodesend_reserved_dispatches(ns_server_t *server, isc_boolean_t all) {
3744135446Strhodes	ns_dispatch_t *dispatch, *nextdispatch;
3745135446Strhodes
3746135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
3747135446Strhodes
3748135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
3749135446Strhodes	     dispatch != NULL;
3750135446Strhodes	     dispatch = nextdispatch) {
3751135446Strhodes		nextdispatch = ISC_LIST_NEXT(dispatch, link);
3752135446Strhodes		if (!all && server->dispatchgen == dispatch-> dispatchgen)
3753135446Strhodes			continue;
3754135446Strhodes		ISC_LIST_UNLINK(server->dispatches, dispatch, link);
3755135446Strhodes		dns_dispatch_detach(&dispatch->dispatch);
3756135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
3757135446Strhodes	}
3758135446Strhodes}
3759135446Strhodes
3760135446Strhodesvoid
3761165071Sdougbns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) {
3762135446Strhodes	ns_dispatch_t *dispatch;
3763135446Strhodes	in_port_t port;
3764135446Strhodes	char addrbuf[ISC_SOCKADDR_FORMATSIZE];
3765135446Strhodes	isc_result_t result;
3766135446Strhodes	unsigned int attrs, attrmask;
3767135446Strhodes
3768135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
3769135446Strhodes
3770135446Strhodes	port = isc_sockaddr_getport(addr);
3771135446Strhodes	if (port == 0 || port >= 1024)
3772135446Strhodes		return;
3773135446Strhodes
3774135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
3775135446Strhodes	     dispatch != NULL;
3776135446Strhodes	     dispatch = ISC_LIST_NEXT(dispatch, link)) {
3777135446Strhodes		if (isc_sockaddr_equal(&dispatch->addr, addr))
3778135446Strhodes			break;
3779135446Strhodes	}
3780135446Strhodes	if (dispatch != NULL) {
3781135446Strhodes		dispatch->dispatchgen = server->dispatchgen;
3782135446Strhodes		return;
3783135446Strhodes	}
3784135446Strhodes
3785135446Strhodes	dispatch = isc_mem_get(server->mctx, sizeof(*dispatch));
3786135446Strhodes	if (dispatch == NULL) {
3787135446Strhodes		result = ISC_R_NOMEMORY;
3788135446Strhodes		goto cleanup;
3789135446Strhodes	}
3790135446Strhodes
3791135446Strhodes	dispatch->addr = *addr;
3792135446Strhodes	dispatch->dispatchgen = server->dispatchgen;
3793135446Strhodes	dispatch->dispatch = NULL;
3794135446Strhodes
3795135446Strhodes	attrs = 0;
3796135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
3797135446Strhodes	switch (isc_sockaddr_pf(addr)) {
3798135446Strhodes	case AF_INET:
3799135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
3800135446Strhodes		break;
3801135446Strhodes	case AF_INET6:
3802135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
3803135446Strhodes		break;
3804135446Strhodes	default:
3805135446Strhodes		result = ISC_R_NOTIMPLEMENTED;
3806135446Strhodes		goto cleanup;
3807135446Strhodes	}
3808135446Strhodes	attrmask = 0;
3809135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP;
3810135446Strhodes	attrmask |= DNS_DISPATCHATTR_TCP;
3811135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4;
3812135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV6;
3813135446Strhodes
3814135446Strhodes	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
3815135446Strhodes				     ns_g_taskmgr, &dispatch->addr, 4096,
3816135446Strhodes				     1000, 32768, 16411, 16433,
3817135446Strhodes				     attrs, attrmask, &dispatch->dispatch);
3818135446Strhodes	if (result != ISC_R_SUCCESS)
3819135446Strhodes		goto cleanup;
3820135446Strhodes
3821135446Strhodes	ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link);
3822135446Strhodes
3823135446Strhodes	return;
3824135446Strhodes
3825135446Strhodes cleanup:
3826135446Strhodes	if (dispatch != NULL)
3827135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
3828135446Strhodes	isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
3829135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3830135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
3831135446Strhodes		      "unable to create dispatch for reserved port %s: %s",
3832135446Strhodes		      addrbuf, isc_result_totext(result));
3833135446Strhodes}
3834135446Strhodes
3835135446Strhodes
3836135446Strhodesstatic isc_result_t
3837135446Strhodesloadconfig(ns_server_t *server) {
3838135446Strhodes	isc_result_t result;
3839135446Strhodes	start_reserved_dispatches(server);
3840135446Strhodes	result = load_configuration(ns_g_lwresdonly ?
3841135446Strhodes				    lwresd_g_conffile : ns_g_conffile,
3842143731Sdougb				    server, ISC_FALSE);
3843135446Strhodes	if (result == ISC_R_SUCCESS)
3844135446Strhodes		end_reserved_dispatches(server, ISC_FALSE);
3845135446Strhodes	else
3846135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3847135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3848135446Strhodes			      "reloading configuration failed: %s",
3849135446Strhodes			      isc_result_totext(result));
3850135446Strhodes	return (result);
3851135446Strhodes}
3852135446Strhodes
3853135446Strhodesstatic isc_result_t
3854135446Strhodesreload(ns_server_t *server) {
3855135446Strhodes	isc_result_t result;
3856135446Strhodes	CHECK(loadconfig(server));
3857135446Strhodes
3858135446Strhodes	result = load_zones(server, ISC_FALSE);
3859135446Strhodes	if (result != ISC_R_SUCCESS) {
3860135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3861135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3862135446Strhodes			      "reloading zones failed: %s",
3863135446Strhodes			      isc_result_totext(result));
3864135446Strhodes	}
3865135446Strhodes cleanup:
3866135446Strhodes	return (result);
3867135446Strhodes}
3868135446Strhodes
3869135446Strhodesstatic void
3870135446Strhodesreconfig(ns_server_t *server) {
3871135446Strhodes	isc_result_t result;
3872135446Strhodes	CHECK(loadconfig(server));
3873135446Strhodes
3874135446Strhodes	result = load_new_zones(server, ISC_FALSE);
3875135446Strhodes	if (result != ISC_R_SUCCESS) {
3876135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3877135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3878135446Strhodes			      "loading new zones failed: %s",
3879135446Strhodes			      isc_result_totext(result));
3880135446Strhodes	}
3881135446Strhodes cleanup: ;
3882135446Strhodes}
3883135446Strhodes
3884135446Strhodes/*
3885135446Strhodes * Handle a reload event (from SIGHUP).
3886135446Strhodes */
3887135446Strhodesstatic void
3888135446Strhodesns_server_reload(isc_task_t *task, isc_event_t *event) {
3889135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
3890135446Strhodes
3891135446Strhodes	INSIST(task = server->task);
3892135446Strhodes	UNUSED(task);
3893135446Strhodes
3894135446Strhodes	(void)reload(server);
3895135446Strhodes
3896135446Strhodes	LOCK(&server->reload_event_lock);
3897135446Strhodes	INSIST(server->reload_event == NULL);
3898135446Strhodes	server->reload_event = event;
3899135446Strhodes	UNLOCK(&server->reload_event_lock);
3900135446Strhodes}
3901135446Strhodes
3902135446Strhodesvoid
3903135446Strhodesns_server_reloadwanted(ns_server_t *server) {
3904135446Strhodes	LOCK(&server->reload_event_lock);
3905135446Strhodes	if (server->reload_event != NULL)
3906135446Strhodes		isc_task_send(server->task, &server->reload_event);
3907135446Strhodes	UNLOCK(&server->reload_event_lock);
3908135446Strhodes}
3909135446Strhodes
3910135446Strhodesstatic char *
3911135446Strhodesnext_token(char **stringp, const char *delim) {
3912135446Strhodes	char *res;
3913135446Strhodes
3914135446Strhodes	do {
3915135446Strhodes		res = strsep(stringp, delim);
3916135446Strhodes		if (res == NULL)
3917135446Strhodes			break;
3918135446Strhodes	} while (*res == '\0');
3919135446Strhodes	return (res);
3920135446Strhodes}
3921135446Strhodes
3922135446Strhodes/*
3923135446Strhodes * Find the zone specified in the control channel command 'args',
3924135446Strhodes * if any.  If a zone is specified, point '*zonep' at it, otherwise
3925135446Strhodes * set '*zonep' to NULL.
3926135446Strhodes */
3927135446Strhodesstatic isc_result_t
3928135446Strhodeszone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep) {
3929135446Strhodes	char *input, *ptr;
3930135446Strhodes	const char *zonetxt;
3931135446Strhodes	char *classtxt;
3932135446Strhodes	const char *viewtxt = NULL;
3933135446Strhodes	dns_fixedname_t name;
3934135446Strhodes	isc_result_t result;
3935135446Strhodes	isc_buffer_t buf;
3936135446Strhodes	dns_view_t *view = NULL;
3937135446Strhodes	dns_rdataclass_t rdclass;
3938135446Strhodes
3939135446Strhodes	REQUIRE(zonep != NULL && *zonep == NULL);
3940135446Strhodes
3941135446Strhodes	input = args;
3942135446Strhodes
3943135446Strhodes	/* Skip the command name. */
3944135446Strhodes	ptr = next_token(&input, " \t");
3945135446Strhodes	if (ptr == NULL)
3946135446Strhodes		return (ISC_R_UNEXPECTEDEND);
3947135446Strhodes
3948135446Strhodes	/* Look for the zone name. */
3949135446Strhodes	zonetxt = next_token(&input, " \t");
3950135446Strhodes	if (zonetxt == NULL)
3951135446Strhodes		return (ISC_R_SUCCESS);
3952135446Strhodes
3953135446Strhodes	/* Look for the optional class name. */
3954135446Strhodes	classtxt = next_token(&input, " \t");
3955135446Strhodes	if (classtxt != NULL) {
3956135446Strhodes		/* Look for the optional view name. */
3957135446Strhodes		viewtxt = next_token(&input, " \t");
3958135446Strhodes	}
3959135446Strhodes
3960135446Strhodes	isc_buffer_init(&buf, zonetxt, strlen(zonetxt));
3961135446Strhodes	isc_buffer_add(&buf, strlen(zonetxt));
3962135446Strhodes	dns_fixedname_init(&name);
3963135446Strhodes	result = dns_name_fromtext(dns_fixedname_name(&name),
3964135446Strhodes				   &buf, dns_rootname, ISC_FALSE, NULL);
3965135446Strhodes	if (result != ISC_R_SUCCESS)
3966135446Strhodes		goto fail1;
3967135446Strhodes
3968135446Strhodes	if (classtxt != NULL) {
3969135446Strhodes		isc_textregion_t r;
3970135446Strhodes		r.base = classtxt;
3971135446Strhodes		r.length = strlen(classtxt);
3972135446Strhodes		result = dns_rdataclass_fromtext(&rdclass, &r);
3973135446Strhodes		if (result != ISC_R_SUCCESS)
3974135446Strhodes			goto fail1;
3975135446Strhodes	} else {
3976135446Strhodes		rdclass = dns_rdataclass_in;
3977135446Strhodes	}
3978135446Strhodes
3979135446Strhodes	if (viewtxt == NULL)
3980135446Strhodes		viewtxt = "_default";
3981135446Strhodes	result = dns_viewlist_find(&server->viewlist, viewtxt,
3982135446Strhodes				   rdclass, &view);
3983135446Strhodes	if (result != ISC_R_SUCCESS)
3984135446Strhodes		goto fail1;
3985135446Strhodes
3986135446Strhodes	result = dns_zt_find(view->zonetable, dns_fixedname_name(&name),
3987135446Strhodes			     0, NULL, zonep);
3988135446Strhodes	/* Partial match? */
3989135446Strhodes	if (result != ISC_R_SUCCESS && *zonep != NULL)
3990135446Strhodes		dns_zone_detach(zonep);
3991135446Strhodes	dns_view_detach(&view);
3992135446Strhodes fail1:
3993135446Strhodes	return (result);
3994135446Strhodes}
3995135446Strhodes
3996135446Strhodes/*
3997135446Strhodes * Act on a "retransfer" command from the command channel.
3998135446Strhodes */
3999135446Strhodesisc_result_t
4000135446Strhodesns_server_retransfercommand(ns_server_t *server, char *args) {
4001135446Strhodes	isc_result_t result;
4002135446Strhodes	dns_zone_t *zone = NULL;
4003135446Strhodes	dns_zonetype_t type;
4004135446Strhodes
4005135446Strhodes	result = zone_from_args(server, args, &zone);
4006135446Strhodes	if (result != ISC_R_SUCCESS)
4007135446Strhodes		return (result);
4008135446Strhodes	if (zone == NULL)
4009135446Strhodes		return (ISC_R_UNEXPECTEDEND);
4010135446Strhodes	type = dns_zone_gettype(zone);
4011135446Strhodes	if (type == dns_zone_slave || type == dns_zone_stub)
4012135446Strhodes		dns_zone_forcereload(zone);
4013135446Strhodes	else
4014135446Strhodes		result = ISC_R_NOTFOUND;
4015135446Strhodes	dns_zone_detach(&zone);
4016135446Strhodes	return (result);
4017135446Strhodes}
4018135446Strhodes
4019135446Strhodes/*
4020135446Strhodes * Act on a "reload" command from the command channel.
4021135446Strhodes */
4022135446Strhodesisc_result_t
4023135446Strhodesns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
4024135446Strhodes	isc_result_t result;
4025135446Strhodes	dns_zone_t *zone = NULL;
4026135446Strhodes	dns_zonetype_t type;
4027135446Strhodes	const char *msg = NULL;
4028135446Strhodes
4029135446Strhodes	result = zone_from_args(server, args, &zone);
4030135446Strhodes	if (result != ISC_R_SUCCESS)
4031135446Strhodes		return (result);
4032135446Strhodes	if (zone == NULL) {
4033135446Strhodes		result = reload(server);
4034135446Strhodes		if (result == ISC_R_SUCCESS)
4035135446Strhodes			msg = "server reload successful";
4036135446Strhodes	} else {
4037135446Strhodes		type = dns_zone_gettype(zone);
4038135446Strhodes		if (type == dns_zone_slave || type == dns_zone_stub) {
4039135446Strhodes			dns_zone_refresh(zone);
4040174187Sdougb			dns_zone_detach(&zone);
4041135446Strhodes			msg = "zone refresh queued";
4042135446Strhodes		} else {
4043135446Strhodes			result = dns_zone_load(zone);
4044135446Strhodes			dns_zone_detach(&zone);
4045135446Strhodes			switch (result) {
4046135446Strhodes			case ISC_R_SUCCESS:
4047135446Strhodes				 msg = "zone reload successful";
4048135446Strhodes				 break;
4049135446Strhodes			case DNS_R_CONTINUE:
4050135446Strhodes				msg = "zone reload queued";
4051135446Strhodes				result = ISC_R_SUCCESS;
4052135446Strhodes				break;
4053135446Strhodes			case DNS_R_UPTODATE:
4054135446Strhodes				msg = "zone reload up-to-date";
4055135446Strhodes				result = ISC_R_SUCCESS;
4056135446Strhodes				break;
4057135446Strhodes			default:
4058135446Strhodes				/* failure message will be generated by rndc */
4059135446Strhodes				break;
4060135446Strhodes			}
4061135446Strhodes		}
4062135446Strhodes	}
4063135446Strhodes	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
4064135446Strhodes		isc_buffer_putmem(text, (const unsigned char *)msg,
4065135446Strhodes				  strlen(msg) + 1);
4066135446Strhodes	return (result);
4067135446Strhodes}
4068135446Strhodes
4069135446Strhodes/*
4070135446Strhodes * Act on a "reconfig" command from the command channel.
4071135446Strhodes */
4072135446Strhodesisc_result_t
4073135446Strhodesns_server_reconfigcommand(ns_server_t *server, char *args) {
4074135446Strhodes	UNUSED(args);
4075135446Strhodes
4076135446Strhodes	reconfig(server);
4077135446Strhodes	return (ISC_R_SUCCESS);
4078135446Strhodes}
4079135446Strhodes
4080135446Strhodes/*
4081170222Sdougb * Act on a "notify" command from the command channel.
4082170222Sdougb */
4083170222Sdougbisc_result_t
4084170222Sdougbns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) {
4085170222Sdougb	isc_result_t result;
4086170222Sdougb	dns_zone_t *zone = NULL;
4087170222Sdougb	const unsigned char msg[] = "zone notify queued";
4088170222Sdougb
4089170222Sdougb	result = zone_from_args(server, args, &zone);
4090170222Sdougb	if (result != ISC_R_SUCCESS)
4091170222Sdougb		return (result);
4092170222Sdougb	if (zone == NULL)
4093170222Sdougb		return (ISC_R_UNEXPECTEDEND);
4094170222Sdougb
4095170222Sdougb	dns_zone_notify(zone);
4096170222Sdougb	dns_zone_detach(&zone);
4097170222Sdougb	if (sizeof(msg) <= isc_buffer_availablelength(text))
4098170222Sdougb		isc_buffer_putmem(text, msg, sizeof(msg));
4099170222Sdougb
4100170222Sdougb	return (ISC_R_SUCCESS);
4101170222Sdougb}
4102170222Sdougb
4103170222Sdougb/*
4104135446Strhodes * Act on a "refresh" command from the command channel.
4105135446Strhodes */
4106135446Strhodesisc_result_t
4107135446Strhodesns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
4108135446Strhodes	isc_result_t result;
4109135446Strhodes	dns_zone_t *zone = NULL;
4110165071Sdougb	const unsigned char msg1[] = "zone refresh queued";
4111165071Sdougb	const unsigned char msg2[] = "not a slave or stub zone";
4112165071Sdougb	dns_zonetype_t type;
4113135446Strhodes
4114135446Strhodes	result = zone_from_args(server, args, &zone);
4115135446Strhodes	if (result != ISC_R_SUCCESS)
4116135446Strhodes		return (result);
4117135446Strhodes	if (zone == NULL)
4118135446Strhodes		return (ISC_R_UNEXPECTEDEND);
4119165071Sdougb
4120165071Sdougb	type = dns_zone_gettype(zone);
4121165071Sdougb	if (type == dns_zone_slave || type == dns_zone_stub) {
4122165071Sdougb		dns_zone_refresh(zone);
4123165071Sdougb		dns_zone_detach(&zone);
4124165071Sdougb		if (sizeof(msg1) <= isc_buffer_availablelength(text))
4125165071Sdougb			isc_buffer_putmem(text, msg1, sizeof(msg1));
4126165071Sdougb		return (ISC_R_SUCCESS);
4127165071Sdougb	}
4128165071Sdougb
4129135446Strhodes	dns_zone_detach(&zone);
4130165071Sdougb	if (sizeof(msg2) <= isc_buffer_availablelength(text))
4131165071Sdougb		isc_buffer_putmem(text, msg2, sizeof(msg2));
4132165071Sdougb	return (ISC_R_FAILURE);
4133135446Strhodes}
4134135446Strhodes
4135135446Strhodesisc_result_t
4136135446Strhodesns_server_togglequerylog(ns_server_t *server) {
4137135446Strhodes	server->log_queries = server->log_queries ? ISC_FALSE : ISC_TRUE;
4138135446Strhodes
4139135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4140135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4141135446Strhodes		      "query logging is now %s",
4142135446Strhodes		      server->log_queries ? "on" : "off");
4143135446Strhodes	return (ISC_R_SUCCESS);
4144135446Strhodes}
4145135446Strhodes
4146135446Strhodesstatic isc_result_t
4147165071Sdougbns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
4148170222Sdougb			 cfg_aclconfctx_t *actx,
4149135446Strhodes			 isc_mem_t *mctx, ns_listenlist_t **target)
4150135446Strhodes{
4151135446Strhodes	isc_result_t result;
4152165071Sdougb	const cfg_listelt_t *element;
4153135446Strhodes	ns_listenlist_t *dlist = NULL;
4154135446Strhodes
4155135446Strhodes	REQUIRE(target != NULL && *target == NULL);
4156135446Strhodes
4157135446Strhodes	result = ns_listenlist_create(mctx, &dlist);
4158135446Strhodes	if (result != ISC_R_SUCCESS)
4159135446Strhodes		return (result);
4160135446Strhodes
4161135446Strhodes	for (element = cfg_list_first(listenlist);
4162135446Strhodes	     element != NULL;
4163135446Strhodes	     element = cfg_list_next(element))
4164135446Strhodes	{
4165135446Strhodes		ns_listenelt_t *delt = NULL;
4166165071Sdougb		const cfg_obj_t *listener = cfg_listelt_value(element);
4167135446Strhodes		result = ns_listenelt_fromconfig(listener, config, actx,
4168135446Strhodes						 mctx, &delt);
4169135446Strhodes		if (result != ISC_R_SUCCESS)
4170135446Strhodes			goto cleanup;
4171135446Strhodes		ISC_LIST_APPEND(dlist->elts, delt, link);
4172135446Strhodes	}
4173135446Strhodes	*target = dlist;
4174135446Strhodes	return (ISC_R_SUCCESS);
4175135446Strhodes
4176135446Strhodes cleanup:
4177135446Strhodes	ns_listenlist_detach(&dlist);
4178135446Strhodes	return (result);
4179135446Strhodes}
4180135446Strhodes
4181135446Strhodes/*
4182135446Strhodes * Create a listen list from the corresponding configuration
4183135446Strhodes * data structure.
4184135446Strhodes */
4185135446Strhodesstatic isc_result_t
4186165071Sdougbns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
4187170222Sdougb			cfg_aclconfctx_t *actx,
4188135446Strhodes			isc_mem_t *mctx, ns_listenelt_t **target)
4189135446Strhodes{
4190135446Strhodes	isc_result_t result;
4191165071Sdougb	const cfg_obj_t *portobj;
4192135446Strhodes	in_port_t port;
4193135446Strhodes	ns_listenelt_t *delt = NULL;
4194135446Strhodes	REQUIRE(target != NULL && *target == NULL);
4195135446Strhodes
4196135446Strhodes	portobj = cfg_tuple_get(listener, "port");
4197135446Strhodes	if (!cfg_obj_isuint32(portobj)) {
4198135446Strhodes		if (ns_g_port != 0) {
4199135446Strhodes			port = ns_g_port;
4200135446Strhodes		} else {
4201135446Strhodes			result = ns_config_getport(config, &port);
4202135446Strhodes			if (result != ISC_R_SUCCESS)
4203135446Strhodes				return (result);
4204135446Strhodes		}
4205135446Strhodes	} else {
4206135446Strhodes		if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
4207135446Strhodes			cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
4208135446Strhodes				    "port value '%u' is out of range",
4209135446Strhodes				    cfg_obj_asuint32(portobj));
4210135446Strhodes			return (ISC_R_RANGE);
4211135446Strhodes		}
4212135446Strhodes		port = (in_port_t)cfg_obj_asuint32(portobj);
4213135446Strhodes	}
4214135446Strhodes
4215135446Strhodes	result = ns_listenelt_create(mctx, port, NULL, &delt);
4216135446Strhodes	if (result != ISC_R_SUCCESS)
4217135446Strhodes		return (result);
4218135446Strhodes
4219170222Sdougb	result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
4220170222Sdougb				   config, ns_g_lctx, actx, mctx, &delt->acl);
4221135446Strhodes	if (result != ISC_R_SUCCESS) {
4222135446Strhodes		ns_listenelt_destroy(delt);
4223135446Strhodes		return (result);
4224135446Strhodes	}
4225135446Strhodes	*target = delt;
4226135446Strhodes	return (ISC_R_SUCCESS);
4227135446Strhodes}
4228135446Strhodes
4229135446Strhodesisc_result_t
4230135446Strhodesns_server_dumpstats(ns_server_t *server) {
4231135446Strhodes	isc_result_t result;
4232135446Strhodes	dns_zone_t *zone, *next;
4233135446Strhodes	isc_stdtime_t now;
4234135446Strhodes	FILE *fp = NULL;
4235135446Strhodes	int i;
4236135446Strhodes	int ncounters;
4237135446Strhodes
4238135446Strhodes	isc_stdtime_get(&now);
4239135446Strhodes
4240135446Strhodes	CHECKMF(isc_stdio_open(server->statsfile, "a", &fp),
4241135446Strhodes		"could not open statistics dump file", server->statsfile);
4242135446Strhodes
4243135446Strhodes	ncounters = DNS_STATS_NCOUNTERS;
4244135446Strhodes	fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now);
4245135446Strhodes
4246135446Strhodes	for (i = 0; i < ncounters; i++)
4247135446Strhodes		fprintf(fp, "%s %" ISC_PRINT_QUADFORMAT "u\n",
4248135446Strhodes			dns_statscounter_names[i],
4249135446Strhodes			server->querystats[i]);
4250135446Strhodes
4251135446Strhodes	zone = NULL;
4252135446Strhodes	for (result = dns_zone_first(server->zonemgr, &zone);
4253135446Strhodes	     result == ISC_R_SUCCESS;
4254135446Strhodes	     next = NULL, result = dns_zone_next(zone, &next), zone = next)
4255135446Strhodes	{
4256135446Strhodes		isc_uint64_t *zonestats = dns_zone_getstatscounters(zone);
4257135446Strhodes		if (zonestats != NULL) {
4258135446Strhodes			char zonename[DNS_NAME_FORMATSIZE];
4259135446Strhodes			dns_view_t *view;
4260135446Strhodes			char *viewname;
4261135446Strhodes
4262135446Strhodes			dns_name_format(dns_zone_getorigin(zone),
4263135446Strhodes					zonename, sizeof(zonename));
4264135446Strhodes			view = dns_zone_getview(zone);
4265135446Strhodes			viewname = view->name;
4266135446Strhodes			for (i = 0; i < ncounters; i++) {
4267135446Strhodes				fprintf(fp, "%s %" ISC_PRINT_QUADFORMAT
4268135446Strhodes					"u %s",
4269135446Strhodes					dns_statscounter_names[i],
4270135446Strhodes					zonestats[i],
4271135446Strhodes					zonename);
4272135446Strhodes				if (strcmp(viewname, "_default") != 0)
4273135446Strhodes					fprintf(fp, " %s", viewname);
4274135446Strhodes				fprintf(fp, "\n");
4275135446Strhodes			}
4276135446Strhodes		}
4277135446Strhodes	}
4278135446Strhodes	if (result == ISC_R_NOMORE)
4279135446Strhodes		result = ISC_R_SUCCESS;
4280135446Strhodes	CHECK(result);
4281135446Strhodes
4282135446Strhodes	fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now);
4283135446Strhodes
4284135446Strhodes cleanup:
4285135446Strhodes	if (fp != NULL)
4286135446Strhodes		(void)isc_stdio_close(fp);
4287135446Strhodes	return (result);
4288135446Strhodes}
4289135446Strhodes
4290135446Strhodesstatic isc_result_t
4291135446Strhodesadd_zone_tolist(dns_zone_t *zone, void *uap) {
4292135446Strhodes	struct dumpcontext *dctx = uap;
4293135446Strhodes	struct zonelistentry *zle;
4294135446Strhodes
4295135446Strhodes	zle = isc_mem_get(dctx->mctx, sizeof *zle);
4296135446Strhodes	if (zle ==  NULL)
4297135446Strhodes		return (ISC_R_NOMEMORY);
4298135446Strhodes	zle->zone = NULL;
4299135446Strhodes	dns_zone_attach(zone, &zle->zone);
4300135446Strhodes	ISC_LINK_INIT(zle, link);
4301135446Strhodes	ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link);
4302135446Strhodes	return (ISC_R_SUCCESS);
4303135446Strhodes}
4304135446Strhodes
4305135446Strhodesstatic isc_result_t
4306135446Strhodesadd_view_tolist(struct dumpcontext *dctx, dns_view_t *view) {
4307135446Strhodes	struct viewlistentry *vle;
4308135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
4309135446Strhodes
4310153816Sdougb	/*
4311153816Sdougb	 * Prevent duplicate views.
4312153816Sdougb	 */
4313153816Sdougb	for (vle = ISC_LIST_HEAD(dctx->viewlist);
4314153816Sdougb	     vle != NULL;
4315153816Sdougb	     vle = ISC_LIST_NEXT(vle, link))
4316153816Sdougb		if (vle->view == view)
4317153816Sdougb			return (ISC_R_SUCCESS);
4318153816Sdougb
4319135446Strhodes	vle = isc_mem_get(dctx->mctx, sizeof *vle);
4320135446Strhodes	if (vle == NULL)
4321135446Strhodes		return (ISC_R_NOMEMORY);
4322135446Strhodes	vle->view = NULL;
4323135446Strhodes	dns_view_attach(view, &vle->view);
4324135446Strhodes	ISC_LINK_INIT(vle, link);
4325135446Strhodes	ISC_LIST_INIT(vle->zonelist);
4326135446Strhodes	ISC_LIST_APPEND(dctx->viewlist, vle, link);
4327135446Strhodes	if (dctx->dumpzones)
4328135446Strhodes		result = dns_zt_apply(view->zonetable, ISC_TRUE,
4329135446Strhodes				      add_zone_tolist, dctx);
4330135446Strhodes	return (result);
4331135446Strhodes}
4332135446Strhodes
4333135446Strhodesstatic void
4334135446Strhodesdumpcontext_destroy(struct dumpcontext *dctx) {
4335135446Strhodes	struct viewlistentry *vle;
4336135446Strhodes	struct zonelistentry *zle;
4337135446Strhodes
4338135446Strhodes	vle = ISC_LIST_HEAD(dctx->viewlist);
4339135446Strhodes	while (vle != NULL) {
4340135446Strhodes		ISC_LIST_UNLINK(dctx->viewlist, vle, link);
4341135446Strhodes		zle = ISC_LIST_HEAD(vle->zonelist);
4342135446Strhodes		while (zle != NULL) {
4343135446Strhodes			ISC_LIST_UNLINK(vle->zonelist, zle, link);
4344135446Strhodes			dns_zone_detach(&zle->zone);
4345135446Strhodes			isc_mem_put(dctx->mctx, zle, sizeof *zle);
4346135446Strhodes			zle = ISC_LIST_HEAD(vle->zonelist);
4347135446Strhodes		}
4348135446Strhodes		dns_view_detach(&vle->view);
4349135446Strhodes		isc_mem_put(dctx->mctx, vle, sizeof *vle);
4350135446Strhodes		vle = ISC_LIST_HEAD(dctx->viewlist);
4351135446Strhodes	}
4352135446Strhodes	if (dctx->version != NULL)
4353135446Strhodes		dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
4354135446Strhodes	if (dctx->db != NULL)
4355135446Strhodes		dns_db_detach(&dctx->db);
4356135446Strhodes	if (dctx->cache != NULL)
4357135446Strhodes		dns_db_detach(&dctx->cache);
4358135446Strhodes	if (dctx->task != NULL)
4359135446Strhodes		isc_task_detach(&dctx->task);
4360135446Strhodes	if (dctx->fp != NULL)
4361135446Strhodes		(void)isc_stdio_close(dctx->fp);
4362135446Strhodes	if (dctx->mdctx != NULL)
4363135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
4364135446Strhodes	isc_mem_put(dctx->mctx, dctx, sizeof *dctx);
4365135446Strhodes}
4366135446Strhodes
4367135446Strhodesstatic void
4368135446Strhodesdumpdone(void *arg, isc_result_t result) {
4369135446Strhodes	struct dumpcontext *dctx = arg;
4370135446Strhodes	char buf[1024+32];
4371135446Strhodes	const dns_master_style_t *style;
4372135446Strhodes
4373135446Strhodes	if (result != ISC_R_SUCCESS)
4374135446Strhodes		goto cleanup;
4375135446Strhodes	if (dctx->mdctx != NULL)
4376135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
4377135446Strhodes	if (dctx->view == NULL) {
4378135446Strhodes		dctx->view = ISC_LIST_HEAD(dctx->viewlist);
4379135446Strhodes		if (dctx->view == NULL)
4380135446Strhodes			goto done;
4381135446Strhodes		INSIST(dctx->zone == NULL);
4382153816Sdougb	} else
4383153816Sdougb		goto resume;
4384135446Strhodes nextview:
4385135446Strhodes	fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
4386153816Sdougb resume:
4387135446Strhodes	if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache) {
4388135446Strhodes		style = &dns_master_style_cache;
4389135446Strhodes		/* start cache dump */
4390135446Strhodes		if (dctx->view->view->cachedb != NULL)
4391135446Strhodes			dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
4392135446Strhodes		if (dctx->cache != NULL) {
4393135446Strhodes
4394135446Strhodes			fprintf(dctx->fp, ";\n; Cache dump of view '%s'\n;\n",
4395135446Strhodes				dctx->view->view->name);
4396135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
4397135446Strhodes							    dctx->cache, NULL,
4398135446Strhodes							    style, dctx->fp,
4399135446Strhodes							    dctx->task,
4400135446Strhodes							    dumpdone, dctx,
4401135446Strhodes							    &dctx->mdctx);
4402135446Strhodes			if (result == DNS_R_CONTINUE)
4403135446Strhodes				return;
4404135446Strhodes			if (result == ISC_R_NOTIMPLEMENTED)
4405135446Strhodes				fprintf(dctx->fp, "; %s\n",
4406135446Strhodes					dns_result_totext(result));
4407135446Strhodes			else if (result != ISC_R_SUCCESS)
4408135446Strhodes				goto cleanup;
4409135446Strhodes		}
4410135446Strhodes	}
4411135446Strhodes	if (dctx->cache != NULL) {
4412135446Strhodes		dns_adb_dump(dctx->view->view->adb, dctx->fp);
4413135446Strhodes		dns_db_detach(&dctx->cache);
4414135446Strhodes	}
4415135446Strhodes	if (dctx->dumpzones) {
4416135446Strhodes		style = &dns_master_style_full;
4417135446Strhodes nextzone:
4418135446Strhodes		if (dctx->version != NULL)
4419135446Strhodes			dns_db_closeversion(dctx->db, &dctx->version,
4420135446Strhodes					    ISC_FALSE);
4421135446Strhodes		if (dctx->db != NULL)
4422135446Strhodes			dns_db_detach(&dctx->db);
4423135446Strhodes		if (dctx->zone == NULL)
4424135446Strhodes			dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist);
4425135446Strhodes		else
4426135446Strhodes			dctx->zone = ISC_LIST_NEXT(dctx->zone, link);
4427135446Strhodes		if (dctx->zone != NULL) {
4428135446Strhodes			/* start zone dump */
4429135446Strhodes			dns_zone_name(dctx->zone->zone, buf, sizeof(buf));
4430135446Strhodes			fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf);
4431135446Strhodes			result = dns_zone_getdb(dctx->zone->zone, &dctx->db);
4432135446Strhodes			if (result != ISC_R_SUCCESS) {
4433135446Strhodes				fprintf(dctx->fp, "; %s\n",
4434135446Strhodes					dns_result_totext(result));
4435135446Strhodes				goto nextzone;
4436135446Strhodes			}
4437135446Strhodes			dns_db_currentversion(dctx->db, &dctx->version);
4438135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
4439135446Strhodes							    dctx->db,
4440135446Strhodes							    dctx->version,
4441135446Strhodes							    style, dctx->fp,
4442135446Strhodes							    dctx->task,
4443135446Strhodes							    dumpdone, dctx,
4444135446Strhodes							    &dctx->mdctx);
4445135446Strhodes			if (result == DNS_R_CONTINUE)
4446135446Strhodes				return;
4447153816Sdougb			if (result == ISC_R_NOTIMPLEMENTED) {
4448135446Strhodes				fprintf(dctx->fp, "; %s\n",
4449135446Strhodes					dns_result_totext(result));
4450153816Sdougb				result = ISC_R_SUCCESS;
4451153816Sdougb				goto nextzone;
4452153816Sdougb			}
4453135446Strhodes			if (result != ISC_R_SUCCESS)
4454135446Strhodes				goto cleanup;
4455135446Strhodes		}
4456135446Strhodes	}
4457135446Strhodes	if (dctx->view != NULL)
4458135446Strhodes		dctx->view = ISC_LIST_NEXT(dctx->view, link);
4459135446Strhodes	if (dctx->view != NULL)
4460135446Strhodes		goto nextview;
4461135446Strhodes done:
4462135446Strhodes	fprintf(dctx->fp, "; Dump complete\n");
4463135446Strhodes	result = isc_stdio_flush(dctx->fp);
4464135446Strhodes	if (result == ISC_R_SUCCESS)
4465135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4466135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4467135446Strhodes			      "dumpdb complete");
4468135446Strhodes cleanup:
4469135446Strhodes	if (result != ISC_R_SUCCESS)
4470135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4471135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4472135446Strhodes			      "dumpdb failed: %s", dns_result_totext(result));
4473135446Strhodes	dumpcontext_destroy(dctx);
4474135446Strhodes}
4475135446Strhodes
4476135446Strhodesisc_result_t
4477135446Strhodesns_server_dumpdb(ns_server_t *server, char *args) {
4478135446Strhodes	struct dumpcontext *dctx = NULL;
4479135446Strhodes	dns_view_t *view;
4480135446Strhodes	isc_result_t result;
4481135446Strhodes	char *ptr;
4482135446Strhodes	const char *sep;
4483135446Strhodes
4484165071Sdougb	/* Skip the command name. */
4485165071Sdougb	ptr = next_token(&args, " \t");
4486165071Sdougb	if (ptr == NULL)
4487165071Sdougb		return (ISC_R_UNEXPECTEDEND);
4488165071Sdougb
4489135446Strhodes	dctx = isc_mem_get(server->mctx, sizeof(*dctx));
4490135446Strhodes	if (dctx == NULL)
4491135446Strhodes		return (ISC_R_NOMEMORY);
4492135446Strhodes
4493135446Strhodes	dctx->mctx = server->mctx;
4494135446Strhodes	dctx->dumpcache = ISC_TRUE;
4495135446Strhodes	dctx->dumpzones = ISC_FALSE;
4496135446Strhodes	dctx->fp = NULL;
4497135446Strhodes	ISC_LIST_INIT(dctx->viewlist);
4498135446Strhodes	dctx->view = NULL;
4499135446Strhodes	dctx->zone = NULL;
4500135446Strhodes	dctx->cache = NULL;
4501135446Strhodes	dctx->mdctx = NULL;
4502135446Strhodes	dctx->db = NULL;
4503135446Strhodes	dctx->cache = NULL;
4504135446Strhodes	dctx->task = NULL;
4505135446Strhodes	dctx->version = NULL;
4506135446Strhodes	isc_task_attach(server->task, &dctx->task);
4507135446Strhodes
4508135446Strhodes	CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp),
4509135446Strhodes		"could not open dump file", server->dumpfile);
4510135446Strhodes
4511135446Strhodes	sep = (args == NULL) ? "" : ": ";
4512135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4513135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4514135446Strhodes		      "dumpdb started%s%s", sep, (args != NULL) ? args : "");
4515135446Strhodes
4516135446Strhodes	ptr = next_token(&args, " \t");
4517135446Strhodes	if (ptr != NULL && strcmp(ptr, "-all") == 0) {
4518135446Strhodes		dctx->dumpzones = ISC_TRUE;
4519135446Strhodes		dctx->dumpcache = ISC_TRUE;
4520135446Strhodes		ptr = next_token(&args, " \t");
4521135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
4522135446Strhodes		dctx->dumpzones = ISC_FALSE;
4523135446Strhodes		dctx->dumpcache = ISC_TRUE;
4524135446Strhodes		ptr = next_token(&args, " \t");
4525135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
4526135446Strhodes		dctx->dumpzones = ISC_TRUE;
4527135446Strhodes		dctx->dumpcache = ISC_FALSE;
4528135446Strhodes		ptr = next_token(&args, " \t");
4529135446Strhodes	}
4530135446Strhodes
4531153816Sdougb nextview:
4532135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
4533135446Strhodes	     view != NULL;
4534135446Strhodes	     view = ISC_LIST_NEXT(view, link))
4535135446Strhodes	{
4536135446Strhodes		if (ptr != NULL && strcmp(view->name, ptr) != 0)
4537135446Strhodes			continue;
4538135446Strhodes		CHECK(add_view_tolist(dctx, view));
4539135446Strhodes	}
4540153816Sdougb	if (ptr != NULL) {
4541153816Sdougb		ptr = next_token(&args, " \t");
4542153816Sdougb		if (ptr != NULL)
4543153816Sdougb			goto nextview;
4544153816Sdougb	}
4545135446Strhodes	dumpdone(dctx, ISC_R_SUCCESS);
4546135446Strhodes	return (ISC_R_SUCCESS);
4547135446Strhodes
4548135446Strhodes cleanup:
4549135446Strhodes	if (dctx != NULL)
4550135446Strhodes		dumpcontext_destroy(dctx);
4551135446Strhodes	return (result);
4552135446Strhodes}
4553135446Strhodes
4554135446Strhodesisc_result_t
4555135446Strhodesns_server_dumprecursing(ns_server_t *server) {
4556135446Strhodes	FILE *fp = NULL;
4557135446Strhodes	isc_result_t result;
4558135446Strhodes
4559135446Strhodes	CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
4560135446Strhodes		"could not open dump file", server->recfile);
4561135446Strhodes	fprintf(fp,";\n; Recursing Queries\n;\n");
4562135446Strhodes	ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
4563135446Strhodes	fprintf(fp, "; Dump complete\n");
4564135446Strhodes
4565135446Strhodes cleanup:
4566135446Strhodes	if (fp != NULL)
4567135446Strhodes		result = isc_stdio_close(fp);
4568135446Strhodes	return (result);
4569135446Strhodes}
4570135446Strhodes
4571135446Strhodesisc_result_t
4572135446Strhodesns_server_setdebuglevel(ns_server_t *server, char *args) {
4573135446Strhodes	char *ptr;
4574135446Strhodes	char *levelstr;
4575135446Strhodes	char *endp;
4576135446Strhodes	long newlevel;
4577135446Strhodes
4578135446Strhodes	UNUSED(server);
4579135446Strhodes
4580135446Strhodes	/* Skip the command name. */
4581135446Strhodes	ptr = next_token(&args, " \t");
4582135446Strhodes	if (ptr == NULL)
4583135446Strhodes		return (ISC_R_UNEXPECTEDEND);
4584135446Strhodes
4585135446Strhodes	/* Look for the new level name. */
4586135446Strhodes	levelstr = next_token(&args, " \t");
4587135446Strhodes	if (levelstr == NULL) {
4588135446Strhodes		if (ns_g_debuglevel < 99)
4589135446Strhodes			ns_g_debuglevel++;
4590135446Strhodes	} else {
4591135446Strhodes		newlevel = strtol(levelstr, &endp, 10);
4592135446Strhodes		if (*endp != '\0' || newlevel < 0 || newlevel > 99)
4593135446Strhodes			return (ISC_R_RANGE);
4594135446Strhodes		ns_g_debuglevel = (unsigned int)newlevel;
4595135446Strhodes	}
4596135446Strhodes	isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
4597135446Strhodes	return (ISC_R_SUCCESS);
4598135446Strhodes}
4599135446Strhodes
4600135446Strhodesisc_result_t
4601170222Sdougbns_server_validation(ns_server_t *server, char *args) {
4602170222Sdougb	char *ptr, *viewname;
4603170222Sdougb	dns_view_t *view;
4604170222Sdougb	isc_boolean_t changed = ISC_FALSE;
4605170222Sdougb	isc_result_t result;
4606170222Sdougb	isc_boolean_t enable;
4607170222Sdougb
4608170222Sdougb	/* Skip the command name. */
4609170222Sdougb	ptr = next_token(&args, " \t");
4610170222Sdougb	if (ptr == NULL)
4611170222Sdougb		return (ISC_R_UNEXPECTEDEND);
4612170222Sdougb
4613170222Sdougb	/* Find out what we are to do. */
4614170222Sdougb	ptr = next_token(&args, " \t");
4615170222Sdougb	if (ptr == NULL)
4616170222Sdougb		return (ISC_R_UNEXPECTEDEND);
4617170222Sdougb
4618170222Sdougb	if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
4619170222Sdougb	    !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
4620170222Sdougb		enable = ISC_TRUE;
4621170222Sdougb	else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
4622170222Sdougb		 !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
4623170222Sdougb		enable = ISC_FALSE;
4624170222Sdougb	else
4625170222Sdougb		return (DNS_R_SYNTAX);
4626170222Sdougb
4627170222Sdougb	/* Look for the view name. */
4628170222Sdougb	viewname = next_token(&args, " \t");
4629170222Sdougb
4630170222Sdougb	result = isc_task_beginexclusive(server->task);
4631170222Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
4632170222Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
4633170222Sdougb	     view != NULL;
4634170222Sdougb	     view = ISC_LIST_NEXT(view, link))
4635170222Sdougb	{
4636170222Sdougb		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
4637170222Sdougb			continue;
4638170222Sdougb		result = dns_view_flushcache(view);
4639170222Sdougb		if (result != ISC_R_SUCCESS)
4640170222Sdougb			goto out;
4641170222Sdougb		view->enablevalidation = enable;
4642170222Sdougb		changed = ISC_TRUE;
4643170222Sdougb	}
4644170222Sdougb	if (changed)
4645170222Sdougb		result = ISC_R_SUCCESS;
4646170222Sdougb	else
4647170222Sdougb		result = ISC_R_FAILURE;
4648170222Sdougb out:
4649170222Sdougb	isc_task_endexclusive(server->task);
4650170222Sdougb	return (result);
4651170222Sdougb}
4652170222Sdougb
4653170222Sdougbisc_result_t
4654135446Strhodesns_server_flushcache(ns_server_t *server, char *args) {
4655135446Strhodes	char *ptr, *viewname;
4656135446Strhodes	dns_view_t *view;
4657174187Sdougb	isc_boolean_t flushed;
4658174187Sdougb	isc_boolean_t found;
4659135446Strhodes	isc_result_t result;
4660135446Strhodes
4661135446Strhodes	/* Skip the command name. */
4662135446Strhodes	ptr = next_token(&args, " \t");
4663135446Strhodes	if (ptr == NULL)
4664135446Strhodes		return (ISC_R_UNEXPECTEDEND);
4665135446Strhodes
4666135446Strhodes	/* Look for the view name. */
4667135446Strhodes	viewname = next_token(&args, " \t");
4668135446Strhodes
4669135446Strhodes	result = isc_task_beginexclusive(server->task);
4670135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
4671174187Sdougb	flushed = ISC_TRUE;
4672174187Sdougb	found = ISC_FALSE;
4673135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
4674135446Strhodes	     view != NULL;
4675135446Strhodes	     view = ISC_LIST_NEXT(view, link))
4676135446Strhodes	{
4677135446Strhodes		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
4678135446Strhodes			continue;
4679174187Sdougb		found = ISC_TRUE;
4680135446Strhodes		result = dns_view_flushcache(view);
4681135446Strhodes		if (result != ISC_R_SUCCESS)
4682174187Sdougb			flushed = ISC_FALSE;
4683135446Strhodes	}
4684174187Sdougb	if (flushed && found) {
4685135446Strhodes		result = ISC_R_SUCCESS;
4686174187Sdougb	} else {
4687174187Sdougb		if (!found)
4688174187Sdougb			result = ISC_R_NOTFOUND;
4689174187Sdougb		else
4690174187Sdougb			result = ISC_R_FAILURE;
4691174187Sdougb	}
4692135446Strhodes	isc_task_endexclusive(server->task);
4693135446Strhodes	return (result);
4694135446Strhodes}
4695135446Strhodes
4696135446Strhodesisc_result_t
4697135446Strhodesns_server_flushname(ns_server_t *server, char *args) {
4698135446Strhodes	char *ptr, *target, *viewname;
4699135446Strhodes	dns_view_t *view;
4700174187Sdougb	isc_boolean_t flushed;
4701174187Sdougb	isc_boolean_t found;
4702135446Strhodes	isc_result_t result;
4703135446Strhodes	isc_buffer_t b;
4704135446Strhodes	dns_fixedname_t fixed;
4705135446Strhodes	dns_name_t *name;
4706135446Strhodes
4707135446Strhodes	/* Skip the command name. */
4708135446Strhodes	ptr = next_token(&args, " \t");
4709135446Strhodes	if (ptr == NULL)
4710135446Strhodes		return (ISC_R_UNEXPECTEDEND);
4711135446Strhodes
4712135446Strhodes	/* Find the domain name to flush. */
4713135446Strhodes	target = next_token(&args, " \t");
4714135446Strhodes	if (target == NULL)
4715135446Strhodes		return (ISC_R_UNEXPECTEDEND);
4716135446Strhodes
4717135446Strhodes	isc_buffer_init(&b, target, strlen(target));
4718135446Strhodes	isc_buffer_add(&b, strlen(target));
4719135446Strhodes	dns_fixedname_init(&fixed);
4720135446Strhodes	name = dns_fixedname_name(&fixed);
4721135446Strhodes	result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
4722135446Strhodes	if (result != ISC_R_SUCCESS)
4723135446Strhodes		return (result);
4724135446Strhodes
4725135446Strhodes	/* Look for the view name. */
4726135446Strhodes	viewname = next_token(&args, " \t");
4727135446Strhodes
4728135446Strhodes	result = isc_task_beginexclusive(server->task);
4729135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
4730135446Strhodes	flushed = ISC_TRUE;
4731174187Sdougb	found = ISC_FALSE;
4732135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
4733135446Strhodes	     view != NULL;
4734135446Strhodes	     view = ISC_LIST_NEXT(view, link))
4735135446Strhodes	{
4736135446Strhodes		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
4737135446Strhodes			continue;
4738174187Sdougb		found = ISC_TRUE;
4739135446Strhodes		result = dns_view_flushname(view, name);
4740135446Strhodes		if (result != ISC_R_SUCCESS)
4741135446Strhodes			flushed = ISC_FALSE;
4742135446Strhodes	}
4743174187Sdougb	if (flushed && found)
4744135446Strhodes		result = ISC_R_SUCCESS;
4745174187Sdougb	else if (!found)
4746174187Sdougb		result = ISC_R_NOTFOUND;
4747135446Strhodes	else
4748135446Strhodes		result = ISC_R_FAILURE;
4749135446Strhodes	isc_task_endexclusive(server->task);
4750135446Strhodes	return (result);
4751135446Strhodes}
4752135446Strhodes
4753135446Strhodesisc_result_t
4754135446Strhodesns_server_status(ns_server_t *server, isc_buffer_t *text) {
4755135446Strhodes	int zonecount, xferrunning, xferdeferred, soaqueries;
4756135446Strhodes	unsigned int n;
4757135446Strhodes
4758135446Strhodes	zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY);
4759135446Strhodes	xferrunning = dns_zonemgr_getcount(server->zonemgr,
4760135446Strhodes					   DNS_ZONESTATE_XFERRUNNING);
4761135446Strhodes	xferdeferred = dns_zonemgr_getcount(server->zonemgr,
4762135446Strhodes					    DNS_ZONESTATE_XFERDEFERRED);
4763135446Strhodes	soaqueries = dns_zonemgr_getcount(server->zonemgr,
4764135446Strhodes					  DNS_ZONESTATE_SOAQUERY);
4765135446Strhodes	n = snprintf((char *)isc_buffer_used(text),
4766135446Strhodes		     isc_buffer_availablelength(text),
4767135446Strhodes		     "number of zones: %u\n"
4768135446Strhodes		     "debug level: %d\n"
4769135446Strhodes		     "xfers running: %u\n"
4770135446Strhodes		     "xfers deferred: %u\n"
4771135446Strhodes		     "soa queries in progress: %u\n"
4772135446Strhodes		     "query logging is %s\n"
4773170222Sdougb		     "recursive clients: %d/%d/%d\n"
4774135446Strhodes		     "tcp clients: %d/%d\n"
4775135446Strhodes		     "server is up and running",
4776135446Strhodes		     zonecount, ns_g_debuglevel, xferrunning, xferdeferred,
4777135446Strhodes		     soaqueries, server->log_queries ? "ON" : "OFF",
4778170222Sdougb		     server->recursionquota.used, server->recursionquota.soft,
4779170222Sdougb		     server->recursionquota.max,
4780135446Strhodes		     server->tcpquota.used, server->tcpquota.max);
4781135446Strhodes	if (n >= isc_buffer_availablelength(text))
4782135446Strhodes		return (ISC_R_NOSPACE);
4783135446Strhodes	isc_buffer_add(text, n);
4784135446Strhodes	return (ISC_R_SUCCESS);
4785135446Strhodes}
4786135446Strhodes
4787135446Strhodes/*
4788170222Sdougb * Act on a "freeze" or "thaw" command from the command channel.
4789135446Strhodes */
4790135446Strhodesisc_result_t
4791135446Strhodesns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args) {
4792170222Sdougb	isc_result_t result, tresult;
4793135446Strhodes	dns_zone_t *zone = NULL;
4794135446Strhodes	dns_zonetype_t type;
4795135446Strhodes	char classstr[DNS_RDATACLASS_FORMATSIZE];
4796135446Strhodes	char zonename[DNS_NAME_FORMATSIZE];
4797135446Strhodes	dns_view_t *view;
4798135446Strhodes	char *journal;
4799135446Strhodes	const char *vname, *sep;
4800135446Strhodes	isc_boolean_t frozen;
4801135446Strhodes
4802135446Strhodes	result = zone_from_args(server, args, &zone);
4803135446Strhodes	if (result != ISC_R_SUCCESS)
4804135446Strhodes		return (result);
4805170222Sdougb	if (zone == NULL) {
4806170222Sdougb		result = isc_task_beginexclusive(server->task);
4807170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
4808170222Sdougb		tresult = ISC_R_SUCCESS;
4809170222Sdougb	        for (view = ISC_LIST_HEAD(server->viewlist);
4810170222Sdougb		     view != NULL;
4811170222Sdougb		     view = ISC_LIST_NEXT(view, link)) {
4812170222Sdougb			result = dns_view_freezezones(view, freeze);
4813170222Sdougb			if (result != ISC_R_SUCCESS &&
4814170222Sdougb			    tresult == ISC_R_SUCCESS)
4815170222Sdougb				tresult = result;
4816170222Sdougb		}
4817170222Sdougb		isc_task_endexclusive(server->task);
4818170222Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4819170222Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4820170222Sdougb			      "%s all zones: %s",
4821170222Sdougb			      freeze ? "freezing" : "thawing",
4822170222Sdougb			      isc_result_totext(tresult));
4823170222Sdougb		return (tresult);
4824170222Sdougb	}
4825135446Strhodes	type = dns_zone_gettype(zone);
4826135446Strhodes	if (type != dns_zone_master) {
4827135446Strhodes		dns_zone_detach(&zone);
4828135446Strhodes		return (ISC_R_NOTFOUND);
4829135446Strhodes	}
4830135446Strhodes
4831135446Strhodes	frozen = dns_zone_getupdatedisabled(zone);
4832135446Strhodes	if (freeze) {
4833135446Strhodes		if (frozen)
4834135446Strhodes			result = DNS_R_FROZEN;
4835135446Strhodes		if (result == ISC_R_SUCCESS)
4836135446Strhodes			result = dns_zone_flush(zone);
4837135446Strhodes		if (result == ISC_R_SUCCESS) {
4838135446Strhodes			journal = dns_zone_getjournal(zone);
4839135446Strhodes			if (journal != NULL)
4840135446Strhodes				(void)isc_file_remove(journal);
4841135446Strhodes		}
4842135446Strhodes	} else {
4843135446Strhodes		if (frozen) {
4844135446Strhodes			result = dns_zone_load(zone);
4845135446Strhodes			if (result == DNS_R_CONTINUE ||
4846135446Strhodes			    result == DNS_R_UPTODATE)
4847135446Strhodes				result = ISC_R_SUCCESS;
4848135446Strhodes		}
4849135446Strhodes	}
4850135446Strhodes	if (result == ISC_R_SUCCESS)
4851135446Strhodes		dns_zone_setupdatedisabled(zone, freeze);
4852135446Strhodes
4853135446Strhodes	view = dns_zone_getview(zone);
4854135446Strhodes	if (strcmp(view->name, "_bind") == 0 ||
4855135446Strhodes	    strcmp(view->name, "_default") == 0)
4856135446Strhodes	{
4857135446Strhodes		vname = "";
4858135446Strhodes		sep = "";
4859135446Strhodes	} else {
4860135446Strhodes		vname = view->name;
4861135446Strhodes		sep = " ";
4862135446Strhodes	}
4863135446Strhodes	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
4864135446Strhodes			      sizeof(classstr));
4865135446Strhodes	dns_name_format(dns_zone_getorigin(zone),
4866135446Strhodes			zonename, sizeof(zonename));
4867135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4868135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4869135446Strhodes		      "%s zone '%s/%s'%s%s: %s",
4870170222Sdougb		      freeze ? "freezing" : "thawing",
4871135446Strhodes		      zonename, classstr, sep, vname,
4872135446Strhodes		      isc_result_totext(result));
4873135446Strhodes	dns_zone_detach(&zone);
4874135446Strhodes	return (result);
4875135446Strhodes}
4876153816Sdougb
4877153816Sdougb#ifdef HAVE_LIBSCF
4878153816Sdougb/*
4879153816Sdougb * This function adds a message for rndc to echo if named
4880153816Sdougb * is managed by smf and is also running chroot.
4881153816Sdougb */
4882153816Sdougbisc_result_t
4883153816Sdougbns_smf_add_message(isc_buffer_t *text) {
4884153816Sdougb	unsigned int n;
4885153816Sdougb
4886153816Sdougb	n = snprintf((char *)isc_buffer_used(text),
4887153816Sdougb		isc_buffer_availablelength(text),
4888153816Sdougb		"use svcadm(1M) to manage named");
4889153816Sdougb	if (n >= isc_buffer_availablelength(text))
4890153816Sdougb		return (ISC_R_NOSPACE);
4891153816Sdougb	isc_buffer_add(text, n);
4892153816Sdougb	return (ISC_R_SUCCESS);
4893153816Sdougb}
4894153816Sdougb#endif /* HAVE_LIBSCF */
4895