server.c revision 245163
1135446Strhodes/*
2234010Sdougb * Copyright (C) 2004-2012  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
18234010Sdougb/* $Id: server.c,v 1.599.8.19 2012/02/22 00:33:32 each Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <stdlib.h>
25186462Sdougb#include <unistd.h>
26224092Sdougb#include <limits.h>
27224092Sdougb#include <ctype.h>
28224092Sdougb#include <sys/types.h>
29224092Sdougb#include <sys/stat.h>
30135446Strhodes
31135446Strhodes#include <isc/app.h>
32135446Strhodes#include <isc/base64.h>
33135446Strhodes#include <isc/dir.h>
34135446Strhodes#include <isc/entropy.h>
35135446Strhodes#include <isc/file.h>
36135446Strhodes#include <isc/hash.h>
37193149Sdougb#include <isc/httpd.h>
38135446Strhodes#include <isc/lex.h>
39135446Strhodes#include <isc/parseint.h>
40186462Sdougb#include <isc/portset.h>
41135446Strhodes#include <isc/print.h>
42135446Strhodes#include <isc/resource.h>
43224092Sdougb#include <isc/sha2.h>
44186462Sdougb#include <isc/socket.h>
45224092Sdougb#include <isc/stat.h>
46193149Sdougb#include <isc/stats.h>
47135446Strhodes#include <isc/stdio.h>
48135446Strhodes#include <isc/string.h>
49135446Strhodes#include <isc/task.h>
50135446Strhodes#include <isc/timer.h>
51135446Strhodes#include <isc/util.h>
52193149Sdougb#include <isc/xml.h>
53135446Strhodes
54135446Strhodes#include <isccfg/namedconf.h>
55135446Strhodes
56135446Strhodes#include <bind9/check.h>
57135446Strhodes
58170222Sdougb#include <dns/acache.h>
59135446Strhodes#include <dns/adb.h>
60135446Strhodes#include <dns/cache.h>
61135446Strhodes#include <dns/db.h>
62135446Strhodes#include <dns/dispatch.h>
63170222Sdougb#include <dns/dlz.h>
64224092Sdougb#include <dns/dns64.h>
65135446Strhodes#include <dns/forward.h>
66135446Strhodes#include <dns/journal.h>
67135446Strhodes#include <dns/keytable.h>
68224092Sdougb#include <dns/keyvalues.h>
69170222Sdougb#include <dns/lib.h>
70135446Strhodes#include <dns/master.h>
71135446Strhodes#include <dns/masterdump.h>
72135446Strhodes#include <dns/order.h>
73135446Strhodes#include <dns/peer.h>
74135446Strhodes#include <dns/portlist.h>
75193149Sdougb#include <dns/rbt.h>
76135446Strhodes#include <dns/rdataclass.h>
77135446Strhodes#include <dns/rdataset.h>
78135446Strhodes#include <dns/rdatastruct.h>
79135446Strhodes#include <dns/resolver.h>
80135446Strhodes#include <dns/rootns.h>
81135446Strhodes#include <dns/secalg.h>
82135446Strhodes#include <dns/stats.h>
83135446Strhodes#include <dns/tkey.h>
84193149Sdougb#include <dns/tsig.h>
85135446Strhodes#include <dns/view.h>
86135446Strhodes#include <dns/zone.h>
87135446Strhodes#include <dns/zt.h>
88135446Strhodes
89135446Strhodes#include <dst/dst.h>
90135446Strhodes#include <dst/result.h>
91135446Strhodes
92135446Strhodes#include <named/client.h>
93135446Strhodes#include <named/config.h>
94135446Strhodes#include <named/control.h>
95135446Strhodes#include <named/interfacemgr.h>
96135446Strhodes#include <named/log.h>
97135446Strhodes#include <named/logconf.h>
98135446Strhodes#include <named/lwresd.h>
99135446Strhodes#include <named/main.h>
100135446Strhodes#include <named/os.h>
101135446Strhodes#include <named/server.h>
102193149Sdougb#include <named/statschannel.h>
103135446Strhodes#include <named/tkeyconf.h>
104135446Strhodes#include <named/tsigconf.h>
105135446Strhodes#include <named/zoneconf.h>
106153816Sdougb#ifdef HAVE_LIBSCF
107153816Sdougb#include <named/ns_smf_globals.h>
108153816Sdougb#include <stdlib.h>
109153816Sdougb#endif
110135446Strhodes
111224092Sdougb#ifndef PATH_MAX
112224092Sdougb#define PATH_MAX 1024
113224092Sdougb#endif
114224092Sdougb
115170222Sdougb/*%
116135446Strhodes * Check an operation for failure.  Assumes that the function
117135446Strhodes * using it has a 'result' variable and a 'cleanup' label.
118135446Strhodes */
119135446Strhodes#define CHECK(op) \
120193149Sdougb	do { result = (op);					 \
121193149Sdougb	       if (result != ISC_R_SUCCESS) goto cleanup;	 \
122135446Strhodes	} while (0)
123135446Strhodes
124135446Strhodes#define CHECKM(op, msg) \
125193149Sdougb	do { result = (op);					  \
126135446Strhodes	       if (result != ISC_R_SUCCESS) {			  \
127135446Strhodes			isc_log_write(ns_g_lctx,		  \
128135446Strhodes				      NS_LOGCATEGORY_GENERAL,	  \
129135446Strhodes				      NS_LOGMODULE_SERVER,	  \
130135446Strhodes				      ISC_LOG_ERROR,		  \
131135446Strhodes				      "%s: %s", msg,		  \
132135446Strhodes				      isc_result_totext(result)); \
133135446Strhodes			goto cleanup;				  \
134135446Strhodes		}						  \
135135446Strhodes	} while (0)						  \
136135446Strhodes
137135446Strhodes#define CHECKMF(op, msg, file) \
138193149Sdougb	do { result = (op);					  \
139135446Strhodes	       if (result != ISC_R_SUCCESS) {			  \
140135446Strhodes			isc_log_write(ns_g_lctx,		  \
141135446Strhodes				      NS_LOGCATEGORY_GENERAL,	  \
142135446Strhodes				      NS_LOGMODULE_SERVER,	  \
143135446Strhodes				      ISC_LOG_ERROR,		  \
144135446Strhodes				      "%s '%s': %s", msg, file,	  \
145135446Strhodes				      isc_result_totext(result)); \
146135446Strhodes			goto cleanup;				  \
147135446Strhodes		}						  \
148135446Strhodes	} while (0)						  \
149135446Strhodes
150135446Strhodes#define CHECKFATAL(op, msg) \
151193149Sdougb	do { result = (op);					  \
152135446Strhodes	       if (result != ISC_R_SUCCESS)			  \
153135446Strhodes			fatal(msg, result);			  \
154135446Strhodes	} while (0)						  \
155135446Strhodes
156224092Sdougb/*%
157224092Sdougb * Maximum ADB size for views that share a cache.  Use this limit to suppress
158224092Sdougb * the total of memory footprint, which should be the main reason for sharing
159224092Sdougb * a cache.  Only effective when a finite max-cache-size is specified.
160224092Sdougb * This is currently defined to be 8MB.
161224092Sdougb */
162224092Sdougb#define MAX_ADB_SIZE_FOR_CACHESHARE	8388608
163224092Sdougb
164135446Strhodesstruct ns_dispatch {
165135446Strhodes	isc_sockaddr_t			addr;
166135446Strhodes	unsigned int			dispatchgen;
167135446Strhodes	dns_dispatch_t			*dispatch;
168135446Strhodes	ISC_LINK(struct ns_dispatch)	link;
169135446Strhodes};
170135446Strhodes
171224092Sdougbstruct ns_cache {
172224092Sdougb	dns_cache_t			*cache;
173224092Sdougb	dns_view_t			*primaryview;
174224092Sdougb	isc_boolean_t			needflush;
175224092Sdougb	isc_boolean_t			adbsizeadjusted;
176224092Sdougb	ISC_LINK(ns_cache_t)		link;
177224092Sdougb};
178224092Sdougb
179135446Strhodesstruct dumpcontext {
180135446Strhodes	isc_mem_t			*mctx;
181135446Strhodes	isc_boolean_t			dumpcache;
182135446Strhodes	isc_boolean_t			dumpzones;
183135446Strhodes	FILE				*fp;
184135446Strhodes	ISC_LIST(struct viewlistentry)	viewlist;
185135446Strhodes	struct viewlistentry		*view;
186135446Strhodes	struct zonelistentry		*zone;
187135446Strhodes	dns_dumpctx_t			*mdctx;
188135446Strhodes	dns_db_t			*db;
189135446Strhodes	dns_db_t			*cache;
190135446Strhodes	isc_task_t			*task;
191135446Strhodes	dns_dbversion_t			*version;
192135446Strhodes};
193135446Strhodes
194135446Strhodesstruct viewlistentry {
195135446Strhodes	dns_view_t			*view;
196135446Strhodes	ISC_LINK(struct viewlistentry)	link;
197135446Strhodes	ISC_LIST(struct zonelistentry)	zonelist;
198135446Strhodes};
199135446Strhodes
200135446Strhodesstruct zonelistentry {
201135446Strhodes	dns_zone_t			*zone;
202135446Strhodes	ISC_LINK(struct zonelistentry)	link;
203135446Strhodes};
204135446Strhodes
205224092Sdougb/*%
206224092Sdougb * Configuration context to retain for each view that allows
207225361Sdougb * new zones to be added at runtime.
208224092Sdougb */
209224092Sdougbstruct cfg_context {
210224092Sdougb	isc_mem_t *			mctx;
211225361Sdougb	cfg_parser_t *			parser;
212224092Sdougb	cfg_obj_t *			config;
213225361Sdougb	cfg_parser_t *			nzparser;
214225361Sdougb	cfg_obj_t *			nzconfig;
215225361Sdougb	cfg_aclconfctx_t *		actx;
216224092Sdougb};
217224092Sdougb
218170222Sdougb/*
219170222Sdougb * These zones should not leak onto the Internet.
220170222Sdougb */
221170222Sdougbstatic const struct {
222170222Sdougb	const char	*zone;
223170222Sdougb	isc_boolean_t	rfc1918;
224170222Sdougb} empty_zones[] = {
225170222Sdougb	/* RFC 1918 */
226170222Sdougb	{ "10.IN-ADDR.ARPA", ISC_TRUE },
227170222Sdougb	{ "16.172.IN-ADDR.ARPA", ISC_TRUE },
228170222Sdougb	{ "17.172.IN-ADDR.ARPA", ISC_TRUE },
229170222Sdougb	{ "18.172.IN-ADDR.ARPA", ISC_TRUE },
230170222Sdougb	{ "19.172.IN-ADDR.ARPA", ISC_TRUE },
231170222Sdougb	{ "20.172.IN-ADDR.ARPA", ISC_TRUE },
232170222Sdougb	{ "21.172.IN-ADDR.ARPA", ISC_TRUE },
233170222Sdougb	{ "22.172.IN-ADDR.ARPA", ISC_TRUE },
234170222Sdougb	{ "23.172.IN-ADDR.ARPA", ISC_TRUE },
235170222Sdougb	{ "24.172.IN-ADDR.ARPA", ISC_TRUE },
236170222Sdougb	{ "25.172.IN-ADDR.ARPA", ISC_TRUE },
237170222Sdougb	{ "26.172.IN-ADDR.ARPA", ISC_TRUE },
238170222Sdougb	{ "27.172.IN-ADDR.ARPA", ISC_TRUE },
239170222Sdougb	{ "28.172.IN-ADDR.ARPA", ISC_TRUE },
240170222Sdougb	{ "29.172.IN-ADDR.ARPA", ISC_TRUE },
241170222Sdougb	{ "30.172.IN-ADDR.ARPA", ISC_TRUE },
242170222Sdougb	{ "31.172.IN-ADDR.ARPA", ISC_TRUE },
243170222Sdougb	{ "168.192.IN-ADDR.ARPA", ISC_TRUE },
244170222Sdougb
245218384Sdougb	/* RFC 5735 and RFC 5737 */
246186462Sdougb	{ "0.IN-ADDR.ARPA", ISC_FALSE },	/* THIS NETWORK */
247170222Sdougb	{ "127.IN-ADDR.ARPA", ISC_FALSE },	/* LOOPBACK */
248170222Sdougb	{ "254.169.IN-ADDR.ARPA", ISC_FALSE },	/* LINK LOCAL */
249170222Sdougb	{ "2.0.192.IN-ADDR.ARPA", ISC_FALSE },	/* TEST NET */
250218384Sdougb	{ "100.51.198.IN-ADDR.ARPA", ISC_FALSE },	/* TEST NET 2 */
251218384Sdougb	{ "113.0.203.IN-ADDR.ARPA", ISC_FALSE },	/* TEST NET 3 */
252170222Sdougb	{ "255.255.255.255.IN-ADDR.ARPA", ISC_FALSE },	/* BROADCAST */
253170222Sdougb
254170222Sdougb	/* Local IPv6 Unicast Addresses */
255170222Sdougb	{ "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 },
256170222Sdougb	{ "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 },
257193149Sdougb	/* LOCALLY ASSIGNED LOCAL ADDRESS SCOPE */
258170222Sdougb	{ "D.F.IP6.ARPA", ISC_FALSE },
259170222Sdougb	{ "8.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
260170222Sdougb	{ "9.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
261170222Sdougb	{ "A.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
262170222Sdougb	{ "B.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
263170222Sdougb
264218384Sdougb	/* Example Prefix, RFC 3849. */
265218384Sdougb	{ "8.B.D.0.1.0.0.2.IP6.ARPA", ISC_FALSE },
266218384Sdougb
267170222Sdougb	{ NULL, ISC_FALSE }
268170222Sdougb};
269170222Sdougb
270224092SdougbISC_PLATFORM_NORETURN_PRE static void
271224092Sdougbfatal(const char *msg, isc_result_t result) ISC_PLATFORM_NORETURN_POST;
272135446Strhodes
273135446Strhodesstatic void
274135446Strhodesns_server_reload(isc_task_t *task, isc_event_t *event);
275135446Strhodes
276135446Strhodesstatic isc_result_t
277165071Sdougbns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
278170222Sdougb			cfg_aclconfctx_t *actx,
279135446Strhodes			isc_mem_t *mctx, ns_listenelt_t **target);
280135446Strhodesstatic isc_result_t
281165071Sdougbns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
282170222Sdougb			 cfg_aclconfctx_t *actx,
283135446Strhodes			 isc_mem_t *mctx, ns_listenlist_t **target);
284135446Strhodes
285135446Strhodesstatic isc_result_t
286165071Sdougbconfigure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
287165071Sdougb		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype);
288135446Strhodes
289135446Strhodesstatic isc_result_t
290165071Sdougbconfigure_alternates(const cfg_obj_t *config, dns_view_t *view,
291165071Sdougb		     const cfg_obj_t *alternates);
292135446Strhodes
293135446Strhodesstatic isc_result_t
294165071Sdougbconfigure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
295165071Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
296224092Sdougb	       cfg_aclconfctx_t *aclconf, isc_boolean_t added);
297135446Strhodes
298224092Sdougbstatic isc_result_t
299224092Sdougbadd_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
300224092Sdougb
301135446Strhodesstatic void
302135446Strhodesend_reserved_dispatches(ns_server_t *server, isc_boolean_t all);
303135446Strhodes
304224092Sdougbstatic void
305225361Sdougbnewzone_cfgctx_destroy(void **cfgp);
306224092Sdougb
307170222Sdougb/*%
308193149Sdougb * Configure a single view ACL at '*aclp'.  Get its configuration from
309193149Sdougb * 'vconfig' (for per-view configuration) and maybe from 'config'
310135446Strhodes */
311135446Strhodesstatic isc_result_t
312165071Sdougbconfigure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
313224092Sdougb		   const char *aclname, const char *acltuplename,
314224092Sdougb		   cfg_aclconfctx_t *actx, isc_mem_t *mctx, dns_acl_t **aclp)
315135446Strhodes{
316135446Strhodes	isc_result_t result;
317165071Sdougb	const cfg_obj_t *maps[3];
318165071Sdougb	const cfg_obj_t *aclobj = NULL;
319135446Strhodes	int i = 0;
320135446Strhodes
321135446Strhodes	if (*aclp != NULL)
322135446Strhodes		dns_acl_detach(aclp);
323135446Strhodes	if (vconfig != NULL)
324135446Strhodes		maps[i++] = cfg_tuple_get(vconfig, "options");
325135446Strhodes	if (config != NULL) {
326165071Sdougb		const cfg_obj_t *options = NULL;
327135446Strhodes		(void)cfg_map_get(config, "options", &options);
328135446Strhodes		if (options != NULL)
329135446Strhodes			maps[i++] = options;
330135446Strhodes	}
331135446Strhodes	maps[i] = NULL;
332135446Strhodes
333165071Sdougb	(void)ns_config_get(maps, aclname, &aclobj);
334135446Strhodes	if (aclobj == NULL)
335135446Strhodes		/*
336193149Sdougb		 * No value available.	*aclp == NULL.
337135446Strhodes		 */
338135446Strhodes		return (ISC_R_SUCCESS);
339135446Strhodes
340224092Sdougb	if (acltuplename != NULL) {
341224092Sdougb		/*
342224092Sdougb		 * If the ACL is given in an optional tuple, retrieve it.
343224092Sdougb		 * The parser should have ensured that a valid object be
344224092Sdougb		 * returned.
345224092Sdougb		 */
346224092Sdougb		aclobj = cfg_tuple_get(aclobj, acltuplename);
347224092Sdougb	}
348224092Sdougb
349170222Sdougb	result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
350193149Sdougb				    actx, mctx, 0, aclp);
351135446Strhodes
352135446Strhodes	return (result);
353135446Strhodes}
354135446Strhodes
355193149Sdougb/*%
356193149Sdougb * Configure a sortlist at '*aclp'.  Essentially the same as
357193149Sdougb * configure_view_acl() except it calls cfg_acl_fromconfig with a
358193149Sdougb * nest_level value of 2.
359193149Sdougb */
360135446Strhodesstatic isc_result_t
361193149Sdougbconfigure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config,
362193149Sdougb			cfg_aclconfctx_t *actx, isc_mem_t *mctx,
363193149Sdougb			dns_acl_t **aclp)
364193149Sdougb{
365193149Sdougb	isc_result_t result;
366193149Sdougb	const cfg_obj_t *maps[3];
367193149Sdougb	const cfg_obj_t *aclobj = NULL;
368193149Sdougb	int i = 0;
369193149Sdougb
370193149Sdougb	if (*aclp != NULL)
371193149Sdougb		dns_acl_detach(aclp);
372193149Sdougb	if (vconfig != NULL)
373193149Sdougb		maps[i++] = cfg_tuple_get(vconfig, "options");
374193149Sdougb	if (config != NULL) {
375193149Sdougb		const cfg_obj_t *options = NULL;
376193149Sdougb		(void)cfg_map_get(config, "options", &options);
377193149Sdougb		if (options != NULL)
378193149Sdougb			maps[i++] = options;
379193149Sdougb	}
380193149Sdougb	maps[i] = NULL;
381193149Sdougb
382193149Sdougb	(void)ns_config_get(maps, "sortlist", &aclobj);
383193149Sdougb	if (aclobj == NULL)
384193149Sdougb		return (ISC_R_SUCCESS);
385193149Sdougb
386193149Sdougb	/*
387193149Sdougb	 * Use a nest level of 3 for the "top level" of the sortlist;
388193149Sdougb	 * this means each entry in the top three levels will be stored
389193149Sdougb	 * as lists of separate, nested ACLs, rather than merged together
390193149Sdougb	 * into IP tables as is usually done with ACLs.
391193149Sdougb	 */
392193149Sdougb	result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
393193149Sdougb				    actx, mctx, 3, aclp);
394193149Sdougb
395193149Sdougb	return (result);
396193149Sdougb}
397193149Sdougb
398193149Sdougbstatic isc_result_t
399224092Sdougbconfigure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
400224092Sdougb			 const char *confname, const char *conftuplename,
401224092Sdougb			 isc_mem_t *mctx, dns_rbt_t **rbtp)
402135446Strhodes{
403224092Sdougb	isc_result_t result;
404224092Sdougb	const cfg_obj_t *maps[3];
405224092Sdougb	const cfg_obj_t *obj = NULL;
406224092Sdougb	const cfg_listelt_t *element;
407224092Sdougb	int i = 0;
408224092Sdougb	dns_fixedname_t fixed;
409224092Sdougb	dns_name_t *name;
410224092Sdougb	isc_buffer_t b;
411224092Sdougb	const char *str;
412224092Sdougb	const cfg_obj_t *nameobj;
413224092Sdougb
414224092Sdougb	if (*rbtp != NULL)
415224092Sdougb		dns_rbt_destroy(rbtp);
416224092Sdougb	if (vconfig != NULL)
417224092Sdougb		maps[i++] = cfg_tuple_get(vconfig, "options");
418224092Sdougb	if (config != NULL) {
419224092Sdougb		const cfg_obj_t *options = NULL;
420224092Sdougb		(void)cfg_map_get(config, "options", &options);
421224092Sdougb		if (options != NULL)
422224092Sdougb			maps[i++] = options;
423224092Sdougb	}
424224092Sdougb	maps[i] = NULL;
425224092Sdougb
426224092Sdougb	(void)ns_config_get(maps, confname, &obj);
427224092Sdougb	if (obj == NULL)
428224092Sdougb		/*
429224092Sdougb		 * No value available.	*rbtp == NULL.
430224092Sdougb		 */
431224092Sdougb		return (ISC_R_SUCCESS);
432224092Sdougb
433224092Sdougb	if (conftuplename != NULL) {
434224092Sdougb		obj = cfg_tuple_get(obj, conftuplename);
435224092Sdougb		if (cfg_obj_isvoid(obj))
436224092Sdougb			return (ISC_R_SUCCESS);
437224092Sdougb	}
438224092Sdougb
439224092Sdougb	result = dns_rbt_create(mctx, NULL, NULL, rbtp);
440224092Sdougb	if (result != ISC_R_SUCCESS)
441224092Sdougb		return (result);
442224092Sdougb
443224092Sdougb	dns_fixedname_init(&fixed);
444224092Sdougb	name = dns_fixedname_name(&fixed);
445224092Sdougb	for (element = cfg_list_first(obj);
446224092Sdougb	     element != NULL;
447224092Sdougb	     element = cfg_list_next(element)) {
448224092Sdougb		nameobj = cfg_listelt_value(element);
449224092Sdougb		str = cfg_obj_asstring(nameobj);
450224092Sdougb		isc_buffer_init(&b, str, strlen(str));
451224092Sdougb		isc_buffer_add(&b, strlen(str));
452224092Sdougb		CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
453224092Sdougb		/*
454224092Sdougb		 * We don't need the node data, but need to set dummy data to
455224092Sdougb		 * avoid a partial match with an empty node.  For example, if
456224092Sdougb		 * we have foo.example.com and bar.example.com, we'd get a match
457224092Sdougb		 * for baz.example.com, which is not the expected result.
458224092Sdougb		 * We simply use (void *)1 as the dummy data.
459224092Sdougb		 */
460224092Sdougb		result = dns_rbt_addname(*rbtp, name, (void *)1);
461224092Sdougb		if (result != ISC_R_SUCCESS) {
462224092Sdougb			cfg_obj_log(nameobj, ns_g_lctx, ISC_LOG_ERROR,
463224092Sdougb				    "failed to add %s for %s: %s",
464224092Sdougb				    str, confname, isc_result_totext(result));
465224092Sdougb			goto cleanup;
466224092Sdougb		}
467224092Sdougb
468224092Sdougb	}
469224092Sdougb
470224092Sdougb	return (result);
471224092Sdougb
472224092Sdougb  cleanup:
473224092Sdougb	dns_rbt_destroy(rbtp);
474224092Sdougb	return (result);
475224092Sdougb
476224092Sdougb}
477224092Sdougb
478224092Sdougbstatic isc_result_t
479224092Sdougbdstkey_fromconfig(const cfg_obj_t *vconfig, const cfg_obj_t *key,
480224092Sdougb		  isc_boolean_t managed, dst_key_t **target, isc_mem_t *mctx)
481224092Sdougb{
482135446Strhodes	dns_rdataclass_t viewclass;
483135446Strhodes	dns_rdata_dnskey_t keystruct;
484135446Strhodes	isc_uint32_t flags, proto, alg;
485165071Sdougb	const char *keystr, *keynamestr;
486135446Strhodes	unsigned char keydata[4096];
487135446Strhodes	isc_buffer_t keydatabuf;
488135446Strhodes	unsigned char rrdata[4096];
489135446Strhodes	isc_buffer_t rrdatabuf;
490135446Strhodes	isc_region_t r;
491135446Strhodes	dns_fixedname_t fkeyname;
492135446Strhodes	dns_name_t *keyname;
493135446Strhodes	isc_buffer_t namebuf;
494135446Strhodes	isc_result_t result;
495135446Strhodes	dst_key_t *dstkey = NULL;
496135446Strhodes
497224092Sdougb	INSIST(target != NULL && *target == NULL);
498224092Sdougb
499135446Strhodes	flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags"));
500135446Strhodes	proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol"));
501135446Strhodes	alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm"));
502135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
503135446Strhodes	keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
504135446Strhodes
505224092Sdougb	if (managed) {
506224092Sdougb		const char *initmethod;
507224092Sdougb		initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init"));
508224092Sdougb
509224092Sdougb		if (strcasecmp(initmethod, "initial-key") != 0) {
510224092Sdougb			cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
511224092Sdougb				    "managed key '%s': "
512224092Sdougb				    "invalid initialization method '%s'",
513224092Sdougb				    keynamestr, initmethod);
514224092Sdougb			result = ISC_R_FAILURE;
515224092Sdougb			goto cleanup;
516224092Sdougb		}
517224092Sdougb	}
518224092Sdougb
519135446Strhodes	if (vconfig == NULL)
520135446Strhodes		viewclass = dns_rdataclass_in;
521135446Strhodes	else {
522165071Sdougb		const cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class");
523135446Strhodes		CHECK(ns_config_getclass(classobj, dns_rdataclass_in,
524135446Strhodes					 &viewclass));
525135446Strhodes	}
526135446Strhodes	keystruct.common.rdclass = viewclass;
527135446Strhodes	keystruct.common.rdtype = dns_rdatatype_dnskey;
528135446Strhodes	/*
529135446Strhodes	 * The key data in keystruct is not dynamically allocated.
530135446Strhodes	 */
531135446Strhodes	keystruct.mctx = NULL;
532135446Strhodes
533135446Strhodes	ISC_LINK_INIT(&keystruct.common, link);
534135446Strhodes
535135446Strhodes	if (flags > 0xffff)
536135446Strhodes		CHECKM(ISC_R_RANGE, "key flags");
537135446Strhodes	if (proto > 0xff)
538135446Strhodes		CHECKM(ISC_R_RANGE, "key protocol");
539135446Strhodes	if (alg > 0xff)
540135446Strhodes		CHECKM(ISC_R_RANGE, "key algorithm");
541135446Strhodes	keystruct.flags = (isc_uint16_t)flags;
542135446Strhodes	keystruct.protocol = (isc_uint8_t)proto;
543135446Strhodes	keystruct.algorithm = (isc_uint8_t)alg;
544135446Strhodes
545135446Strhodes	isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
546135446Strhodes	isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
547135446Strhodes
548135446Strhodes	keystr = cfg_obj_asstring(cfg_tuple_get(key, "key"));
549135446Strhodes	CHECK(isc_base64_decodestring(keystr, &keydatabuf));
550135446Strhodes	isc_buffer_usedregion(&keydatabuf, &r);
551135446Strhodes	keystruct.datalen = r.length;
552135446Strhodes	keystruct.data = r.base;
553135446Strhodes
554170222Sdougb	if ((keystruct.algorithm == DST_ALG_RSASHA1 ||
555170222Sdougb	     keystruct.algorithm == DST_ALG_RSAMD5) &&
556170222Sdougb	    r.length > 1 && r.base[0] == 1 && r.base[1] == 3)
557170222Sdougb		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
558224092Sdougb			    "%s key '%s' has a weak exponent",
559224092Sdougb			    managed ? "managed" : "trusted",
560170222Sdougb			    keynamestr);
561170222Sdougb
562135446Strhodes	CHECK(dns_rdata_fromstruct(NULL,
563135446Strhodes				   keystruct.common.rdclass,
564135446Strhodes				   keystruct.common.rdtype,
565135446Strhodes				   &keystruct, &rrdatabuf));
566135446Strhodes	dns_fixedname_init(&fkeyname);
567135446Strhodes	isc_buffer_init(&namebuf, keynamestr, strlen(keynamestr));
568135446Strhodes	isc_buffer_add(&namebuf, strlen(keynamestr));
569224092Sdougb	CHECK(dns_name_fromtext(keyname, &namebuf, dns_rootname, 0, NULL));
570135446Strhodes	CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf,
571135446Strhodes			      mctx, &dstkey));
572135446Strhodes
573224092Sdougb	*target = dstkey;
574135446Strhodes	return (ISC_R_SUCCESS);
575135446Strhodes
576135446Strhodes cleanup:
577135446Strhodes	if (result == DST_R_NOCRYPTO) {
578135446Strhodes		cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
579224092Sdougb			    "ignoring %s key for '%s': no crypto support",
580224092Sdougb			    managed ? "managed" : "trusted",
581135446Strhodes			    keynamestr);
582224092Sdougb	} else if (result == DST_R_UNSUPPORTEDALG) {
583224092Sdougb		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
584224092Sdougb			    "skipping %s key for '%s': %s",
585224092Sdougb			    managed ? "managed" : "trusted",
586224092Sdougb			    keynamestr, isc_result_totext(result));
587135446Strhodes	} else {
588135446Strhodes		cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
589224092Sdougb			    "configuring %s key for '%s': %s",
590224092Sdougb			    managed ? "managed" : "trusted",
591135446Strhodes			    keynamestr, isc_result_totext(result));
592135446Strhodes		result = ISC_R_FAILURE;
593135446Strhodes	}
594135446Strhodes
595135446Strhodes	if (dstkey != NULL)
596135446Strhodes		dst_key_free(&dstkey);
597135446Strhodes
598135446Strhodes	return (result);
599135446Strhodes}
600135446Strhodes
601224092Sdougbstatic isc_result_t
602224092Sdougbload_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig,
603224092Sdougb	       dns_view_t *view, isc_boolean_t managed,
604224092Sdougb	       dns_name_t *keyname, isc_mem_t *mctx)
605224092Sdougb{
606224092Sdougb	const cfg_listelt_t *elt, *elt2;
607224092Sdougb	const cfg_obj_t *key, *keylist;
608224092Sdougb	dst_key_t *dstkey = NULL;
609224092Sdougb	isc_result_t result;
610224092Sdougb	dns_keytable_t *secroots = NULL;
611224092Sdougb
612224092Sdougb	CHECK(dns_view_getsecroots(view, &secroots));
613224092Sdougb
614224092Sdougb	for (elt = cfg_list_first(keys);
615224092Sdougb	     elt != NULL;
616224092Sdougb	     elt = cfg_list_next(elt)) {
617224092Sdougb		keylist = cfg_listelt_value(elt);
618224092Sdougb
619224092Sdougb		for (elt2 = cfg_list_first(keylist);
620224092Sdougb		     elt2 != NULL;
621224092Sdougb		     elt2 = cfg_list_next(elt2)) {
622224092Sdougb			key = cfg_listelt_value(elt2);
623224092Sdougb			result = dstkey_fromconfig(vconfig, key, managed,
624224092Sdougb						   &dstkey, mctx);
625224092Sdougb			if (result ==  DST_R_UNSUPPORTEDALG) {
626224092Sdougb				result = ISC_R_SUCCESS;
627224092Sdougb				continue;
628224092Sdougb			}
629224092Sdougb			if (result != ISC_R_SUCCESS)
630224092Sdougb				goto cleanup;
631224092Sdougb
632224092Sdougb			/*
633224092Sdougb			 * If keyname was specified, we only add that key.
634224092Sdougb			 */
635224092Sdougb			if (keyname != NULL &&
636224092Sdougb			    !dns_name_equal(keyname, dst_key_name(dstkey)))
637224092Sdougb			{
638224092Sdougb				dst_key_free(&dstkey);
639224092Sdougb				continue;
640224092Sdougb			}
641224092Sdougb
642224092Sdougb			CHECK(dns_keytable_add(secroots, managed, &dstkey));
643224092Sdougb		}
644224092Sdougb	}
645224092Sdougb
646224092Sdougb cleanup:
647224092Sdougb	if (dstkey != NULL)
648224092Sdougb		dst_key_free(&dstkey);
649224092Sdougb	if (secroots != NULL)
650224092Sdougb		dns_keytable_detach(&secroots);
651224092Sdougb	if (result == DST_R_NOCRYPTO)
652224092Sdougb		result = ISC_R_SUCCESS;
653224092Sdougb	return (result);
654224092Sdougb}
655224092Sdougb
656170222Sdougb/*%
657224092Sdougb * Configure DNSSEC keys for a view.
658135446Strhodes *
659135446Strhodes * The per-view configuration values and the server-global defaults are read
660224092Sdougb * from 'vconfig' and 'config'.
661135446Strhodes */
662135446Strhodesstatic isc_result_t
663224092Sdougbconfigure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig,
664224092Sdougb			  const cfg_obj_t *config, const cfg_obj_t *bindkeys,
665224092Sdougb			  isc_boolean_t auto_dlv, isc_boolean_t auto_root,
666224092Sdougb			  isc_mem_t *mctx)
667135446Strhodes{
668224092Sdougb	isc_result_t result = ISC_R_SUCCESS;
669224092Sdougb	const cfg_obj_t *view_keys = NULL;
670224092Sdougb	const cfg_obj_t *global_keys = NULL;
671224092Sdougb	const cfg_obj_t *view_managed_keys = NULL;
672224092Sdougb	const cfg_obj_t *global_managed_keys = NULL;
673224092Sdougb	const cfg_obj_t *maps[4];
674165071Sdougb	const cfg_obj_t *voptions = NULL;
675224092Sdougb	const cfg_obj_t *options = NULL;
676224092Sdougb	const cfg_obj_t *obj = NULL;
677224092Sdougb	const char *directory;
678224092Sdougb	int i = 0;
679135446Strhodes
680224092Sdougb	/* We don't need trust anchors for the _bind view */
681224092Sdougb	if (strcmp(view->name, "_bind") == 0 &&
682224092Sdougb	    view->rdclass == dns_rdataclass_chaos) {
683224092Sdougb		return (ISC_R_SUCCESS);
684224092Sdougb	}
685135446Strhodes
686224092Sdougb	if (vconfig != NULL) {
687135446Strhodes		voptions = cfg_tuple_get(vconfig, "options");
688224092Sdougb		if (voptions != NULL) {
689224092Sdougb			(void) cfg_map_get(voptions, "trusted-keys",
690224092Sdougb					   &view_keys);
691224092Sdougb			(void) cfg_map_get(voptions, "managed-keys",
692224092Sdougb					   &view_managed_keys);
693224092Sdougb			maps[i++] = voptions;
694224092Sdougb		}
695224092Sdougb	}
696135446Strhodes
697224092Sdougb	if (config != NULL) {
698224092Sdougb		(void)cfg_map_get(config, "trusted-keys", &global_keys);
699224092Sdougb		(void)cfg_map_get(config, "managed-keys", &global_managed_keys);
700224092Sdougb		(void)cfg_map_get(config, "options", &options);
701224092Sdougb		if (options != NULL) {
702224092Sdougb			maps[i++] = options;
703224092Sdougb		}
704224092Sdougb	}
705135446Strhodes
706224092Sdougb	maps[i++] = ns_g_defaults;
707224092Sdougb	maps[i] = NULL;
708224092Sdougb
709224092Sdougb	result = dns_view_initsecroots(view, mctx);
710224092Sdougb	if (result != ISC_R_SUCCESS) {
711224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
712224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
713224092Sdougb			      "couldn't create keytable");
714224092Sdougb		return (ISC_R_UNEXPECTED);
715224092Sdougb	}
716224092Sdougb
717224092Sdougb	if (auto_dlv && view->rdclass == dns_rdataclass_in) {
718224092Sdougb		const cfg_obj_t *builtin_keys = NULL;
719224092Sdougb		const cfg_obj_t *builtin_managed_keys = NULL;
720224092Sdougb
721224092Sdougb		isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
722224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
723224092Sdougb			      "using built-in DLV key for view %s",
724224092Sdougb			      view->name);
725224092Sdougb
726224092Sdougb		/*
727224092Sdougb		 * If bind.keys exists, it overrides the managed-keys
728224092Sdougb		 * clause hard-coded in ns_g_config.
729224092Sdougb		 */
730224092Sdougb		if (bindkeys != NULL) {
731224092Sdougb			(void)cfg_map_get(bindkeys, "trusted-keys",
732224092Sdougb					  &builtin_keys);
733224092Sdougb			(void)cfg_map_get(bindkeys, "managed-keys",
734224092Sdougb					  &builtin_managed_keys);
735224092Sdougb		} else {
736224092Sdougb			(void)cfg_map_get(ns_g_config, "trusted-keys",
737224092Sdougb					  &builtin_keys);
738224092Sdougb			(void)cfg_map_get(ns_g_config, "managed-keys",
739224092Sdougb					  &builtin_managed_keys);
740135446Strhodes		}
741224092Sdougb
742224092Sdougb		if (builtin_keys != NULL)
743224092Sdougb			CHECK(load_view_keys(builtin_keys, vconfig, view,
744224092Sdougb					     ISC_FALSE, view->dlv, mctx));
745224092Sdougb		if (builtin_managed_keys != NULL)
746224092Sdougb			CHECK(load_view_keys(builtin_managed_keys, vconfig,
747224092Sdougb					     view, ISC_TRUE, view->dlv, mctx));
748135446Strhodes	}
749135446Strhodes
750224092Sdougb	if (auto_root && view->rdclass == dns_rdataclass_in) {
751224092Sdougb		const cfg_obj_t *builtin_keys = NULL;
752224092Sdougb		const cfg_obj_t *builtin_managed_keys = NULL;
753186462Sdougb
754224092Sdougb		isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
755224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
756224092Sdougb			      "using built-in root key for view %s",
757224092Sdougb			      view->name);
758224092Sdougb
759224092Sdougb		/*
760224092Sdougb		 * If bind.keys exists, it overrides the managed-keys
761224092Sdougb		 * clause hard-coded in ns_g_config.
762224092Sdougb		 */
763224092Sdougb		if (bindkeys != NULL) {
764224092Sdougb			(void)cfg_map_get(bindkeys, "trusted-keys",
765224092Sdougb					  &builtin_keys);
766224092Sdougb			(void)cfg_map_get(bindkeys, "managed-keys",
767224092Sdougb					  &builtin_managed_keys);
768224092Sdougb		} else {
769224092Sdougb			(void)cfg_map_get(ns_g_config, "trusted-keys",
770224092Sdougb					  &builtin_keys);
771224092Sdougb			(void)cfg_map_get(ns_g_config, "managed-keys",
772224092Sdougb					  &builtin_managed_keys);
773224092Sdougb		}
774224092Sdougb
775224092Sdougb		if (builtin_keys != NULL)
776224092Sdougb			CHECK(load_view_keys(builtin_keys, vconfig, view,
777224092Sdougb					     ISC_FALSE, dns_rootname, mctx));
778224092Sdougb		if (builtin_managed_keys != NULL)
779224092Sdougb			CHECK(load_view_keys(builtin_managed_keys, vconfig,
780224092Sdougb					     view, ISC_TRUE, dns_rootname,
781224092Sdougb					     mctx));
782224092Sdougb	}
783224092Sdougb
784224092Sdougb	CHECK(load_view_keys(view_keys, vconfig, view, ISC_FALSE,
785224092Sdougb			     NULL, mctx));
786224092Sdougb	CHECK(load_view_keys(view_managed_keys, vconfig, view, ISC_TRUE,
787224092Sdougb			     NULL, mctx));
788224092Sdougb
789224092Sdougb	if (view->rdclass == dns_rdataclass_in) {
790224092Sdougb		CHECK(load_view_keys(global_keys, vconfig, view, ISC_FALSE,
791224092Sdougb				     NULL, mctx));
792224092Sdougb		CHECK(load_view_keys(global_managed_keys, vconfig, view,
793224092Sdougb				     ISC_TRUE, NULL, mctx));
794224092Sdougb	}
795224092Sdougb
796224092Sdougb	/*
797224092Sdougb	 * Add key zone for managed-keys.
798224092Sdougb	 */
799224092Sdougb	obj = NULL;
800224092Sdougb	(void)ns_config_get(maps, "managed-keys-directory", &obj);
801224092Sdougb	directory = obj != NULL ? cfg_obj_asstring(obj) : NULL;
802224092Sdougb	CHECK(add_keydata_zone(view, directory, ns_g_mctx));
803224092Sdougb
804224092Sdougb  cleanup:
805135446Strhodes	return (result);
806135446Strhodes}
807135446Strhodes
808135446Strhodesstatic isc_result_t
809224092Sdougbmustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver) {
810165071Sdougb	const cfg_listelt_t *element;
811165071Sdougb	const cfg_obj_t *obj;
812135446Strhodes	const char *str;
813135446Strhodes	dns_fixedname_t fixed;
814135446Strhodes	dns_name_t *name;
815135446Strhodes	isc_boolean_t value;
816135446Strhodes	isc_result_t result;
817135446Strhodes	isc_buffer_t b;
818186462Sdougb
819135446Strhodes	dns_fixedname_init(&fixed);
820135446Strhodes	name = dns_fixedname_name(&fixed);
821135446Strhodes	for (element = cfg_list_first(mbs);
822135446Strhodes	     element != NULL;
823135446Strhodes	     element = cfg_list_next(element))
824135446Strhodes	{
825135446Strhodes		obj = cfg_listelt_value(element);
826135446Strhodes		str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
827135446Strhodes		isc_buffer_init(&b, str, strlen(str));
828135446Strhodes		isc_buffer_add(&b, strlen(str));
829224092Sdougb		CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
830135446Strhodes		value = cfg_obj_asboolean(cfg_tuple_get(obj, "value"));
831135446Strhodes		CHECK(dns_resolver_setmustbesecure(resolver, name, value));
832135446Strhodes	}
833135446Strhodes
834135446Strhodes	result = ISC_R_SUCCESS;
835186462Sdougb
836135446Strhodes cleanup:
837135446Strhodes	return (result);
838135446Strhodes}
839135446Strhodes
840170222Sdougb/*%
841135446Strhodes * Get a dispatch appropriate for the resolver of a given view.
842135446Strhodes */
843135446Strhodesstatic isc_result_t
844165071Sdougbget_view_querysource_dispatch(const cfg_obj_t **maps,
845186462Sdougb			      int af, dns_dispatch_t **dispatchp,
846186462Sdougb			      isc_boolean_t is_firstview)
847135446Strhodes{
848225361Sdougb	isc_result_t result = ISC_R_FAILURE;
849135446Strhodes	dns_dispatch_t *disp;
850135446Strhodes	isc_sockaddr_t sa;
851135446Strhodes	unsigned int attrs, attrmask;
852165071Sdougb	const cfg_obj_t *obj = NULL;
853186462Sdougb	unsigned int maxdispatchbuffers;
854135446Strhodes
855135446Strhodes	switch (af) {
856135446Strhodes	case AF_INET:
857135446Strhodes		result = ns_config_get(maps, "query-source", &obj);
858135446Strhodes		INSIST(result == ISC_R_SUCCESS);
859135446Strhodes		break;
860135446Strhodes	case AF_INET6:
861135446Strhodes		result = ns_config_get(maps, "query-source-v6", &obj);
862135446Strhodes		INSIST(result == ISC_R_SUCCESS);
863135446Strhodes		break;
864135446Strhodes	default:
865135446Strhodes		INSIST(0);
866135446Strhodes	}
867135446Strhodes
868135446Strhodes	sa = *(cfg_obj_assockaddr(obj));
869135446Strhodes	INSIST(isc_sockaddr_pf(&sa) == af);
870135446Strhodes
871135446Strhodes	/*
872135446Strhodes	 * If we don't support this address family, we're done!
873135446Strhodes	 */
874135446Strhodes	switch (af) {
875135446Strhodes	case AF_INET:
876135446Strhodes		result = isc_net_probeipv4();
877135446Strhodes		break;
878135446Strhodes	case AF_INET6:
879135446Strhodes		result = isc_net_probeipv6();
880135446Strhodes		break;
881135446Strhodes	default:
882135446Strhodes		INSIST(0);
883135446Strhodes	}
884135446Strhodes	if (result != ISC_R_SUCCESS)
885135446Strhodes		return (ISC_R_SUCCESS);
886135446Strhodes
887135446Strhodes	/*
888135446Strhodes	 * Try to find a dispatcher that we can share.
889135446Strhodes	 */
890135446Strhodes	attrs = 0;
891135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
892135446Strhodes	switch (af) {
893135446Strhodes	case AF_INET:
894135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
895135446Strhodes		break;
896135446Strhodes	case AF_INET6:
897135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
898135446Strhodes		break;
899135446Strhodes	}
900186462Sdougb	if (isc_sockaddr_getport(&sa) == 0) {
901186462Sdougb		attrs |= DNS_DISPATCHATTR_EXCLUSIVE;
902186462Sdougb		maxdispatchbuffers = 4096;
903186462Sdougb	} else {
904180477Sdougb		INSIST(obj != NULL);
905186462Sdougb		if (is_firstview) {
906186462Sdougb			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_INFO,
907186462Sdougb				    "using specific query-source port "
908186462Sdougb				    "suppresses port randomization and can be "
909186462Sdougb				    "insecure.");
910186462Sdougb		}
911186462Sdougb		maxdispatchbuffers = 1000;
912180477Sdougb	}
913180477Sdougb
914135446Strhodes	attrmask = 0;
915135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP;
916135446Strhodes	attrmask |= DNS_DISPATCHATTR_TCP;
917135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4;
918135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV6;
919135446Strhodes
920135446Strhodes	disp = NULL;
921135446Strhodes	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
922135446Strhodes				     ns_g_taskmgr, &sa, 4096,
923186462Sdougb				     maxdispatchbuffers, 32768, 16411, 16433,
924135446Strhodes				     attrs, attrmask, &disp);
925135446Strhodes	if (result != ISC_R_SUCCESS) {
926135446Strhodes		isc_sockaddr_t any;
927135446Strhodes		char buf[ISC_SOCKADDR_FORMATSIZE];
928135446Strhodes
929135446Strhodes		switch (af) {
930135446Strhodes		case AF_INET:
931135446Strhodes			isc_sockaddr_any(&any);
932135446Strhodes			break;
933135446Strhodes		case AF_INET6:
934135446Strhodes			isc_sockaddr_any6(&any);
935135446Strhodes			break;
936135446Strhodes		}
937135446Strhodes		if (isc_sockaddr_equal(&sa, &any))
938135446Strhodes			return (ISC_R_SUCCESS);
939135446Strhodes		isc_sockaddr_format(&sa, buf, sizeof(buf));
940135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
941135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
942135446Strhodes			      "could not get query source dispatcher (%s)",
943135446Strhodes			      buf);
944135446Strhodes		return (result);
945135446Strhodes	}
946135446Strhodes
947135446Strhodes	*dispatchp = disp;
948135446Strhodes
949135446Strhodes	return (ISC_R_SUCCESS);
950135446Strhodes}
951135446Strhodes
952135446Strhodesstatic isc_result_t
953165071Sdougbconfigure_order(dns_order_t *order, const cfg_obj_t *ent) {
954135446Strhodes	dns_rdataclass_t rdclass;
955135446Strhodes	dns_rdatatype_t rdtype;
956165071Sdougb	const cfg_obj_t *obj;
957135446Strhodes	dns_fixedname_t fixed;
958135446Strhodes	unsigned int mode = 0;
959135446Strhodes	const char *str;
960135446Strhodes	isc_buffer_t b;
961135446Strhodes	isc_result_t result;
962143731Sdougb	isc_boolean_t addroot;
963135446Strhodes
964135446Strhodes	result = ns_config_getclass(cfg_tuple_get(ent, "class"),
965135446Strhodes				    dns_rdataclass_any, &rdclass);
966135446Strhodes	if (result != ISC_R_SUCCESS)
967135446Strhodes		return (result);
968135446Strhodes
969135446Strhodes	result = ns_config_gettype(cfg_tuple_get(ent, "type"),
970135446Strhodes				   dns_rdatatype_any, &rdtype);
971135446Strhodes	if (result != ISC_R_SUCCESS)
972135446Strhodes		return (result);
973135446Strhodes
974135446Strhodes	obj = cfg_tuple_get(ent, "name");
975186462Sdougb	if (cfg_obj_isstring(obj))
976135446Strhodes		str = cfg_obj_asstring(obj);
977135446Strhodes	else
978135446Strhodes		str = "*";
979143731Sdougb	addroot = ISC_TF(strcmp(str, "*") == 0);
980135446Strhodes	isc_buffer_init(&b, str, strlen(str));
981135446Strhodes	isc_buffer_add(&b, strlen(str));
982135446Strhodes	dns_fixedname_init(&fixed);
983135446Strhodes	result = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
984224092Sdougb				   dns_rootname, 0, NULL);
985135446Strhodes	if (result != ISC_R_SUCCESS)
986135446Strhodes		return (result);
987135446Strhodes
988135446Strhodes	obj = cfg_tuple_get(ent, "ordering");
989135446Strhodes	INSIST(cfg_obj_isstring(obj));
990135446Strhodes	str = cfg_obj_asstring(obj);
991135446Strhodes	if (!strcasecmp(str, "fixed"))
992135446Strhodes		mode = DNS_RDATASETATTR_FIXEDORDER;
993135446Strhodes	else if (!strcasecmp(str, "random"))
994135446Strhodes		mode = DNS_RDATASETATTR_RANDOMIZE;
995135446Strhodes	else if (!strcasecmp(str, "cyclic"))
996135446Strhodes		mode = 0;
997135446Strhodes	else
998135446Strhodes		INSIST(0);
999135446Strhodes
1000143731Sdougb	/*
1001143731Sdougb	 * "*" should match everything including the root (BIND 8 compat).
1002143731Sdougb	 * As dns_name_matcheswildcard(".", "*.") returns FALSE add a
1003165071Sdougb	 * explicit entry for "." when the name is "*".
1004143731Sdougb	 */
1005143731Sdougb	if (addroot) {
1006143731Sdougb		result = dns_order_add(order, dns_rootname,
1007143731Sdougb				       rdtype, rdclass, mode);
1008143731Sdougb		if (result != ISC_R_SUCCESS)
1009143731Sdougb			return (result);
1010143731Sdougb	}
1011143731Sdougb
1012135446Strhodes	return (dns_order_add(order, dns_fixedname_name(&fixed),
1013135446Strhodes			      rdtype, rdclass, mode));
1014135446Strhodes}
1015135446Strhodes
1016135446Strhodesstatic isc_result_t
1017165071Sdougbconfigure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
1018135446Strhodes	isc_netaddr_t na;
1019135446Strhodes	dns_peer_t *peer;
1020165071Sdougb	const cfg_obj_t *obj;
1021165071Sdougb	const char *str;
1022135446Strhodes	isc_result_t result;
1023170222Sdougb	unsigned int prefixlen;
1024135446Strhodes
1025170222Sdougb	cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen);
1026135446Strhodes
1027135446Strhodes	peer = NULL;
1028186462Sdougb	result = dns_peer_newprefix(mctx, &na, prefixlen, &peer);
1029135446Strhodes	if (result != ISC_R_SUCCESS)
1030135446Strhodes		return (result);
1031135446Strhodes
1032135446Strhodes	obj = NULL;
1033135446Strhodes	(void)cfg_map_get(cpeer, "bogus", &obj);
1034135446Strhodes	if (obj != NULL)
1035135446Strhodes		CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj)));
1036135446Strhodes
1037135446Strhodes	obj = NULL;
1038135446Strhodes	(void)cfg_map_get(cpeer, "provide-ixfr", &obj);
1039135446Strhodes	if (obj != NULL)
1040135446Strhodes		CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
1041135446Strhodes
1042135446Strhodes	obj = NULL;
1043135446Strhodes	(void)cfg_map_get(cpeer, "request-ixfr", &obj);
1044135446Strhodes	if (obj != NULL)
1045135446Strhodes		CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj)));
1046135446Strhodes
1047135446Strhodes	obj = NULL;
1048193149Sdougb	(void)cfg_map_get(cpeer, "request-nsid", &obj);
1049193149Sdougb	if (obj != NULL)
1050193149Sdougb		CHECK(dns_peer_setrequestnsid(peer, cfg_obj_asboolean(obj)));
1051193149Sdougb
1052193149Sdougb	obj = NULL;
1053135446Strhodes	(void)cfg_map_get(cpeer, "edns", &obj);
1054135446Strhodes	if (obj != NULL)
1055135446Strhodes		CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj)));
1056135446Strhodes
1057135446Strhodes	obj = NULL;
1058170222Sdougb	(void)cfg_map_get(cpeer, "edns-udp-size", &obj);
1059170222Sdougb	if (obj != NULL) {
1060170222Sdougb		isc_uint32_t udpsize = cfg_obj_asuint32(obj);
1061170222Sdougb		if (udpsize < 512)
1062170222Sdougb			udpsize = 512;
1063170222Sdougb		if (udpsize > 4096)
1064170222Sdougb			udpsize = 4096;
1065170222Sdougb		CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize));
1066170222Sdougb	}
1067170222Sdougb
1068170222Sdougb	obj = NULL;
1069170222Sdougb	(void)cfg_map_get(cpeer, "max-udp-size", &obj);
1070170222Sdougb	if (obj != NULL) {
1071170222Sdougb		isc_uint32_t udpsize = cfg_obj_asuint32(obj);
1072170222Sdougb		if (udpsize < 512)
1073170222Sdougb			udpsize = 512;
1074170222Sdougb		if (udpsize > 4096)
1075170222Sdougb			udpsize = 4096;
1076170222Sdougb		CHECK(dns_peer_setmaxudp(peer, (isc_uint16_t)udpsize));
1077170222Sdougb	}
1078170222Sdougb
1079170222Sdougb	obj = NULL;
1080135446Strhodes	(void)cfg_map_get(cpeer, "transfers", &obj);
1081135446Strhodes	if (obj != NULL)
1082135446Strhodes		CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj)));
1083135446Strhodes
1084135446Strhodes	obj = NULL;
1085135446Strhodes	(void)cfg_map_get(cpeer, "transfer-format", &obj);
1086135446Strhodes	if (obj != NULL) {
1087135446Strhodes		str = cfg_obj_asstring(obj);
1088135446Strhodes		if (strcasecmp(str, "many-answers") == 0)
1089135446Strhodes			CHECK(dns_peer_settransferformat(peer,
1090135446Strhodes							 dns_many_answers));
1091135446Strhodes		else if (strcasecmp(str, "one-answer") == 0)
1092135446Strhodes			CHECK(dns_peer_settransferformat(peer,
1093135446Strhodes							 dns_one_answer));
1094135446Strhodes		else
1095135446Strhodes			INSIST(0);
1096135446Strhodes	}
1097135446Strhodes
1098135446Strhodes	obj = NULL;
1099135446Strhodes	(void)cfg_map_get(cpeer, "keys", &obj);
1100135446Strhodes	if (obj != NULL) {
1101135446Strhodes		result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj));
1102135446Strhodes		if (result != ISC_R_SUCCESS)
1103135446Strhodes			goto cleanup;
1104135446Strhodes	}
1105135446Strhodes
1106135446Strhodes	obj = NULL;
1107170222Sdougb	if (na.family == AF_INET)
1108135446Strhodes		(void)cfg_map_get(cpeer, "transfer-source", &obj);
1109135446Strhodes	else
1110135446Strhodes		(void)cfg_map_get(cpeer, "transfer-source-v6", &obj);
1111135446Strhodes	if (obj != NULL) {
1112135446Strhodes		result = dns_peer_settransfersource(peer,
1113135446Strhodes						    cfg_obj_assockaddr(obj));
1114135446Strhodes		if (result != ISC_R_SUCCESS)
1115135446Strhodes			goto cleanup;
1116170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
1117135446Strhodes	}
1118170222Sdougb
1119170222Sdougb	obj = NULL;
1120170222Sdougb	if (na.family == AF_INET)
1121170222Sdougb		(void)cfg_map_get(cpeer, "notify-source", &obj);
1122170222Sdougb	else
1123170222Sdougb		(void)cfg_map_get(cpeer, "notify-source-v6", &obj);
1124170222Sdougb	if (obj != NULL) {
1125170222Sdougb		result = dns_peer_setnotifysource(peer,
1126170222Sdougb						  cfg_obj_assockaddr(obj));
1127170222Sdougb		if (result != ISC_R_SUCCESS)
1128170222Sdougb			goto cleanup;
1129170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
1130170222Sdougb	}
1131170222Sdougb
1132170222Sdougb	obj = NULL;
1133170222Sdougb	if (na.family == AF_INET)
1134170222Sdougb		(void)cfg_map_get(cpeer, "query-source", &obj);
1135170222Sdougb	else
1136170222Sdougb		(void)cfg_map_get(cpeer, "query-source-v6", &obj);
1137170222Sdougb	if (obj != NULL) {
1138170222Sdougb		result = dns_peer_setquerysource(peer,
1139170222Sdougb						 cfg_obj_assockaddr(obj));
1140170222Sdougb		if (result != ISC_R_SUCCESS)
1141170222Sdougb			goto cleanup;
1142170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
1143170222Sdougb	}
1144170222Sdougb
1145135446Strhodes	*peerp = peer;
1146135446Strhodes	return (ISC_R_SUCCESS);
1147135446Strhodes
1148135446Strhodes cleanup:
1149135446Strhodes	dns_peer_detach(&peer);
1150135446Strhodes	return (result);
1151135446Strhodes}
1152135446Strhodes
1153135446Strhodesstatic isc_result_t
1154165071Sdougbdisable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
1155135446Strhodes	isc_result_t result;
1156165071Sdougb	const cfg_obj_t *algorithms;
1157165071Sdougb	const cfg_listelt_t *element;
1158135446Strhodes	const char *str;
1159135446Strhodes	dns_fixedname_t fixed;
1160135446Strhodes	dns_name_t *name;
1161135446Strhodes	isc_buffer_t b;
1162135446Strhodes
1163135446Strhodes	dns_fixedname_init(&fixed);
1164135446Strhodes	name = dns_fixedname_name(&fixed);
1165135446Strhodes	str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
1166135446Strhodes	isc_buffer_init(&b, str, strlen(str));
1167135446Strhodes	isc_buffer_add(&b, strlen(str));
1168224092Sdougb	CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
1169135446Strhodes
1170135446Strhodes	algorithms = cfg_tuple_get(disabled, "algorithms");
1171135446Strhodes	for (element = cfg_list_first(algorithms);
1172135446Strhodes	     element != NULL;
1173135446Strhodes	     element = cfg_list_next(element))
1174135446Strhodes	{
1175135446Strhodes		isc_textregion_t r;
1176135446Strhodes		dns_secalg_t alg;
1177135446Strhodes
1178165071Sdougb		DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
1179135446Strhodes		r.length = strlen(r.base);
1180135446Strhodes
1181135446Strhodes		result = dns_secalg_fromtext(&alg, &r);
1182135446Strhodes		if (result != ISC_R_SUCCESS) {
1183135446Strhodes			isc_uint8_t ui;
1184135446Strhodes			result = isc_parse_uint8(&ui, r.base, 10);
1185135446Strhodes			alg = ui;
1186135446Strhodes		}
1187135446Strhodes		if (result != ISC_R_SUCCESS) {
1188135446Strhodes			cfg_obj_log(cfg_listelt_value(element),
1189135446Strhodes				    ns_g_lctx, ISC_LOG_ERROR,
1190135446Strhodes				    "invalid algorithm");
1191135446Strhodes			CHECK(result);
1192135446Strhodes		}
1193135446Strhodes		CHECK(dns_resolver_disable_algorithm(resolver, name, alg));
1194135446Strhodes	}
1195135446Strhodes cleanup:
1196135446Strhodes	return (result);
1197135446Strhodes}
1198135446Strhodes
1199170222Sdougbstatic isc_boolean_t
1200170222Sdougbon_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) {
1201170222Sdougb	const cfg_listelt_t *element;
1202170222Sdougb	dns_fixedname_t fixed;
1203170222Sdougb	dns_name_t *name;
1204170222Sdougb	isc_result_t result;
1205170222Sdougb	const cfg_obj_t *value;
1206170222Sdougb	const char *str;
1207170222Sdougb	isc_buffer_t b;
1208170222Sdougb
1209170222Sdougb	dns_fixedname_init(&fixed);
1210170222Sdougb	name = dns_fixedname_name(&fixed);
1211186462Sdougb
1212170222Sdougb	for (element = cfg_list_first(disablelist);
1213170222Sdougb	     element != NULL;
1214170222Sdougb	     element = cfg_list_next(element))
1215170222Sdougb	{
1216170222Sdougb		value = cfg_listelt_value(element);
1217170222Sdougb		str = cfg_obj_asstring(value);
1218170222Sdougb		isc_buffer_init(&b, str, strlen(str));
1219170222Sdougb		isc_buffer_add(&b, strlen(str));
1220170222Sdougb		result = dns_name_fromtext(name, &b, dns_rootname,
1221224092Sdougb					   0, NULL);
1222170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
1223170222Sdougb		if (dns_name_equal(name, zonename))
1224170222Sdougb			return (ISC_TRUE);
1225170222Sdougb	}
1226170222Sdougb	return (ISC_FALSE);
1227170222Sdougb}
1228170222Sdougb
1229170222Sdougbstatic void
1230170222Sdougbcheck_dbtype(dns_zone_t **zonep, unsigned int dbtypec, const char **dbargv,
1231170222Sdougb	     isc_mem_t *mctx)
1232170222Sdougb{
1233170222Sdougb	char **argv = NULL;
1234170222Sdougb	unsigned int i;
1235170222Sdougb	isc_result_t result;
1236170222Sdougb
1237170222Sdougb	result = dns_zone_getdbtype(*zonep, &argv, mctx);
1238170222Sdougb	if (result != ISC_R_SUCCESS) {
1239170222Sdougb		dns_zone_detach(zonep);
1240170222Sdougb		return;
1241170222Sdougb	}
1242170222Sdougb
1243170222Sdougb	/*
1244170222Sdougb	 * Check that all the arguments match.
1245170222Sdougb	 */
1246170222Sdougb	for (i = 0; i < dbtypec; i++)
1247170222Sdougb		if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) {
1248170222Sdougb			dns_zone_detach(zonep);
1249170222Sdougb			break;
1250170222Sdougb		}
1251170222Sdougb
1252170222Sdougb	/*
1253170222Sdougb	 * Check that there are not extra arguments.
1254170222Sdougb	 */
1255170222Sdougb	if (i == dbtypec && argv[i] != NULL)
1256170222Sdougb		dns_zone_detach(zonep);
1257170222Sdougb	isc_mem_free(mctx, argv);
1258170222Sdougb}
1259170222Sdougb
1260193149Sdougbstatic isc_result_t
1261193149Sdougbsetquerystats(dns_zone_t *zone, isc_mem_t *mctx, isc_boolean_t on) {
1262193149Sdougb	isc_result_t result;
1263193149Sdougb	isc_stats_t *zoneqrystats;
1264170222Sdougb
1265193149Sdougb	zoneqrystats = NULL;
1266193149Sdougb	if (on) {
1267193149Sdougb		result = isc_stats_create(mctx, &zoneqrystats,
1268193149Sdougb					  dns_nsstatscounter_max);
1269193149Sdougb		if (result != ISC_R_SUCCESS)
1270193149Sdougb			return (result);
1271193149Sdougb	}
1272193149Sdougb	dns_zone_setrequeststats(zone, zoneqrystats);
1273193149Sdougb	if (zoneqrystats != NULL)
1274193149Sdougb		isc_stats_detach(&zoneqrystats);
1275193149Sdougb
1276193149Sdougb	return (ISC_R_SUCCESS);
1277193149Sdougb}
1278193149Sdougb
1279224092Sdougbstatic ns_cache_t *
1280224092Sdougbcachelist_find(ns_cachelist_t *cachelist, const char *cachename) {
1281224092Sdougb	ns_cache_t *nsc;
1282224092Sdougb
1283224092Sdougb	for (nsc = ISC_LIST_HEAD(*cachelist);
1284224092Sdougb	     nsc != NULL;
1285224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
1286224092Sdougb		if (strcmp(dns_cache_getname(nsc->cache), cachename) == 0)
1287224092Sdougb			return (nsc);
1288224092Sdougb	}
1289224092Sdougb
1290224092Sdougb	return (NULL);
1291224092Sdougb}
1292224092Sdougb
1293193149Sdougbstatic isc_boolean_t
1294193149Sdougbcache_reusable(dns_view_t *originview, dns_view_t *view,
1295193149Sdougb	       isc_boolean_t new_zero_no_soattl)
1296193149Sdougb{
1297193149Sdougb	if (originview->checknames != view->checknames ||
1298193149Sdougb	    dns_resolver_getzeronosoattl(originview->resolver) !=
1299193149Sdougb	    new_zero_no_soattl ||
1300193149Sdougb	    originview->acceptexpired != view->acceptexpired ||
1301193149Sdougb	    originview->enablevalidation != view->enablevalidation ||
1302193149Sdougb	    originview->maxcachettl != view->maxcachettl ||
1303193149Sdougb	    originview->maxncachettl != view->maxncachettl) {
1304193149Sdougb		return (ISC_FALSE);
1305193149Sdougb	}
1306193149Sdougb
1307193149Sdougb	return (ISC_TRUE);
1308193149Sdougb}
1309193149Sdougb
1310224092Sdougbstatic isc_boolean_t
1311224092Sdougbcache_sharable(dns_view_t *originview, dns_view_t *view,
1312224092Sdougb	       isc_boolean_t new_zero_no_soattl,
1313224092Sdougb	       unsigned int new_cleaning_interval,
1314224092Sdougb	       isc_uint32_t new_max_cache_size)
1315224092Sdougb{
1316224092Sdougb	/*
1317224092Sdougb	 * If the cache cannot even reused for the same view, it cannot be
1318224092Sdougb	 * shared with other views.
1319224092Sdougb	 */
1320224092Sdougb	if (!cache_reusable(originview, view, new_zero_no_soattl))
1321224092Sdougb		return (ISC_FALSE);
1322224092Sdougb
1323224092Sdougb	/*
1324224092Sdougb	 * Check other cache related parameters that must be consistent among
1325224092Sdougb	 * the sharing views.
1326224092Sdougb	 */
1327224092Sdougb	if (dns_cache_getcleaninginterval(originview->cache) !=
1328224092Sdougb	    new_cleaning_interval ||
1329224092Sdougb	    dns_cache_getcachesize(originview->cache) != new_max_cache_size) {
1330224092Sdougb		return (ISC_FALSE);
1331224092Sdougb	}
1332224092Sdougb
1333224092Sdougb	return (ISC_TRUE);
1334224092Sdougb}
1335224092Sdougb
1336135446Strhodes/*
1337224092Sdougb * Callback from DLZ configure when the driver sets up a writeable zone
1338224092Sdougb */
1339224092Sdougbstatic isc_result_t
1340224092Sdougbdlzconfigure_callback(dns_view_t *view, dns_zone_t *zone) {
1341224092Sdougb	dns_name_t *origin = dns_zone_getorigin(zone);
1342224092Sdougb	dns_rdataclass_t zclass = view->rdclass;
1343224092Sdougb	isc_result_t result;
1344224092Sdougb
1345224092Sdougb	result = dns_zonemgr_managezone(ns_g_server->zonemgr, zone);
1346224092Sdougb	if (result != ISC_R_SUCCESS)
1347224092Sdougb		return result;
1348224092Sdougb	dns_zone_setstats(zone, ns_g_server->zonestats);
1349224092Sdougb
1350224092Sdougb	return ns_zone_configure_writeable_dlz(view->dlzdatabase,
1351224092Sdougb					       zone, zclass, origin);
1352224092Sdougb}
1353224092Sdougb
1354224092Sdougbstatic isc_result_t
1355224092Sdougbdns64_reverse(dns_view_t *view, isc_mem_t *mctx, isc_netaddr_t *na,
1356224092Sdougb	      unsigned int prefixlen, const char *server,
1357224092Sdougb	      const char *contact)
1358224092Sdougb{
1359224092Sdougb	char *cp;
1360224092Sdougb	char reverse[48+sizeof("ip6.arpa.")];
1361236374Sdougb	const char *dns64_dbtype[4] = { "_dns64", "dns64", ".", "." };
1362224092Sdougb	const char *sep = ": view ";
1363224092Sdougb	const char *viewname = view->name;
1364224092Sdougb	const unsigned char *s6;
1365224092Sdougb	dns_fixedname_t fixed;
1366224092Sdougb	dns_name_t *name;
1367224092Sdougb	dns_zone_t *zone = NULL;
1368224092Sdougb	int dns64_dbtypec = 4;
1369224092Sdougb	isc_buffer_t b;
1370224092Sdougb	isc_result_t result;
1371224092Sdougb
1372224092Sdougb	REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 ||
1373224092Sdougb		prefixlen == 56 || prefixlen == 64 || prefixlen == 96);
1374224092Sdougb
1375224092Sdougb	if (!strcmp(viewname, "_default")) {
1376224092Sdougb		sep = "";
1377224092Sdougb		viewname = "";
1378224092Sdougb	}
1379224092Sdougb
1380224092Sdougb	/*
1381224092Sdougb	 * Construct the reverse name of the zone.
1382224092Sdougb	 */
1383224092Sdougb	cp = reverse;
1384224092Sdougb	s6 = na->type.in6.s6_addr;
1385224092Sdougb	while (prefixlen > 0) {
1386224092Sdougb		prefixlen -= 8;
1387224092Sdougb		sprintf(cp, "%x.%x.", s6[prefixlen/8] & 0xf,
1388224092Sdougb			(s6[prefixlen/8] >> 4) & 0xf);
1389224092Sdougb		cp += 4;
1390224092Sdougb	}
1391224092Sdougb	strcat(cp, "ip6.arpa.");
1392224092Sdougb
1393224092Sdougb	/*
1394224092Sdougb	 * Create the actual zone.
1395224092Sdougb	 */
1396224092Sdougb	if (server != NULL)
1397224092Sdougb		dns64_dbtype[2] = server;
1398224092Sdougb	if (contact != NULL)
1399224092Sdougb		dns64_dbtype[3] = contact;
1400224092Sdougb	dns_fixedname_init(&fixed);
1401224092Sdougb	name = dns_fixedname_name(&fixed);
1402224092Sdougb	isc_buffer_init(&b, reverse, strlen(reverse));
1403224092Sdougb	isc_buffer_add(&b, strlen(reverse));
1404224092Sdougb	CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
1405224092Sdougb	CHECK(dns_zone_create(&zone, mctx));
1406224092Sdougb	CHECK(dns_zone_setorigin(zone, name));
1407224092Sdougb	dns_zone_setview(zone, view);
1408224092Sdougb	CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
1409224092Sdougb	dns_zone_setclass(zone, view->rdclass);
1410224092Sdougb	dns_zone_settype(zone, dns_zone_master);
1411224092Sdougb	dns_zone_setstats(zone, ns_g_server->zonestats);
1412224092Sdougb	CHECK(dns_zone_setdbtype(zone, dns64_dbtypec, dns64_dbtype));
1413224092Sdougb	if (view->queryacl != NULL)
1414224092Sdougb		dns_zone_setqueryacl(zone, view->queryacl);
1415224092Sdougb	if (view->queryonacl != NULL)
1416224092Sdougb		dns_zone_setqueryonacl(zone, view->queryonacl);
1417224092Sdougb	dns_zone_setdialup(zone, dns_dialuptype_no);
1418224092Sdougb	dns_zone_setnotifytype(zone, dns_notifytype_no);
1419224092Sdougb	dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE);
1420224092Sdougb	CHECK(setquerystats(zone, mctx, ISC_FALSE));	/* XXXMPA */
1421224092Sdougb	CHECK(dns_view_addzone(view, zone));
1422224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
1423224092Sdougb		      ISC_LOG_INFO, "dns64 reverse zone%s%s: %s", sep,
1424224092Sdougb		      viewname, reverse);
1425224092Sdougb
1426224092Sdougbcleanup:
1427224092Sdougb	if (zone != NULL)
1428224092Sdougb		dns_zone_detach(&zone);
1429224092Sdougb	return (result);
1430224092Sdougb}
1431224092Sdougb
1432224092Sdougbstatic isc_result_t
1433245163Serwinconfigure_rpz(dns_view_t *view, const cfg_listelt_t *element,
1434245163Serwin	      isc_boolean_t recursive_only_def, dns_ttl_t ttl_def)
1435245163Serwin{
1436245163Serwin	const cfg_obj_t *rpz_obj, *policy_obj, *obj;
1437224092Sdougb	const char *str;
1438224092Sdougb	dns_rpz_zone_t *old, *new;
1439224092Sdougb	dns_zone_t *zone = NULL;
1440224092Sdougb	isc_result_t result;
1441224092Sdougb
1442224092Sdougb	new = isc_mem_get(view->mctx, sizeof(*new));
1443224092Sdougb	if (new == NULL) {
1444224092Sdougb		result = ISC_R_NOMEMORY;
1445224092Sdougb		goto cleanup;
1446224092Sdougb	}
1447224092Sdougb
1448224092Sdougb	memset(new, 0, sizeof(*new));
1449245163Serwin	dns_name_init(&new->origin, NULL);
1450224092Sdougb	dns_name_init(&new->nsdname, NULL);
1451224092Sdougb	dns_name_init(&new->cname, NULL);
1452245163Serwin	dns_name_init(&new->passthru, NULL);
1453224092Sdougb	ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link);
1454224092Sdougb
1455224092Sdougb	rpz_obj = cfg_listelt_value(element);
1456224092Sdougb	policy_obj = cfg_tuple_get(rpz_obj, "policy");
1457224092Sdougb	if (cfg_obj_isvoid(policy_obj)) {
1458224092Sdougb		new->policy = DNS_RPZ_POLICY_GIVEN;
1459224092Sdougb	} else {
1460245163Serwin		str = cfg_obj_asstring(cfg_tuple_get(policy_obj,
1461245163Serwin						     "policy name"));
1462224092Sdougb		new->policy = dns_rpz_str2policy(str);
1463224092Sdougb		INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
1464224092Sdougb	}
1465224092Sdougb
1466245163Serwin	obj = cfg_tuple_get(rpz_obj, "recursive-only");
1467245163Serwin	if (cfg_obj_isvoid(obj)) {
1468245163Serwin		new->recursive_only = recursive_only_def;
1469245163Serwin	} else {
1470245163Serwin		new->recursive_only = cfg_obj_asboolean(obj);
1471245163Serwin	}
1472245163Serwin	if (!new->recursive_only)
1473245163Serwin		view->rpz_recursive_only = ISC_FALSE;
1474245163Serwin
1475245163Serwin	obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
1476245163Serwin	if (cfg_obj_isuint32(obj)) {
1477245163Serwin		new->max_policy_ttl = cfg_obj_asuint32(obj);
1478245163Serwin	} else {
1479245163Serwin		new->max_policy_ttl = ttl_def;
1480245163Serwin	}
1481245163Serwin
1482245163Serwin	str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name"));
1483245163Serwin	result = dns_name_fromstring(&new->origin, str, DNS_NAME_DOWNCASE,
1484245163Serwin				     view->mctx);
1485224092Sdougb	if (result != ISC_R_SUCCESS) {
1486224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1487224092Sdougb			    "invalid zone '%s'", str);
1488224092Sdougb		goto cleanup;
1489224092Sdougb	}
1490224092Sdougb
1491224092Sdougb	result = dns_name_fromstring2(&new->nsdname, DNS_RPZ_NSDNAME_ZONE,
1492245163Serwin				      &new->origin, DNS_NAME_DOWNCASE,
1493245163Serwin				      view->mctx);
1494224092Sdougb	if (result != ISC_R_SUCCESS) {
1495224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1496224092Sdougb			    "invalid zone '%s'", str);
1497224092Sdougb		goto cleanup;
1498224092Sdougb	}
1499224092Sdougb
1500245163Serwin	result = dns_name_fromstring(&new->passthru, DNS_RPZ_PASSTHRU_ZONE,
1501245163Serwin				     DNS_NAME_DOWNCASE, view->mctx);
1502245163Serwin	if (result != ISC_R_SUCCESS) {
1503245163Serwin		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1504245163Serwin			    "invalid zone '%s'", str);
1505245163Serwin		goto cleanup;
1506245163Serwin	}
1507224092Sdougb
1508224092Sdougb	result = dns_view_findzone(view, &new->origin, &zone);
1509224092Sdougb	if (result != ISC_R_SUCCESS) {
1510224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1511224092Sdougb			    "unknown zone '%s'", str);
1512224092Sdougb		goto cleanup;
1513224092Sdougb	}
1514224092Sdougb	if (dns_zone_gettype(zone) != dns_zone_master &&
1515224092Sdougb	    dns_zone_gettype(zone) != dns_zone_slave) {
1516224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1517224092Sdougb			     "zone '%s' is neither master nor slave", str);
1518224092Sdougb		dns_zone_detach(&zone);
1519224092Sdougb		result = DNS_R_NOTMASTER;
1520224092Sdougb		goto cleanup;
1521224092Sdougb	}
1522224092Sdougb	dns_zone_detach(&zone);
1523224092Sdougb
1524224092Sdougb	for (old = ISC_LIST_HEAD(view->rpz_zones);
1525224092Sdougb	     old != new;
1526224092Sdougb	     old = ISC_LIST_NEXT(old, link)) {
1527224092Sdougb		++new->num;
1528224092Sdougb		if (dns_name_equal(&old->origin, &new->origin)) {
1529224092Sdougb			cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1530224092Sdougb				    "duplicate '%s'", str);
1531224092Sdougb			result = DNS_R_DUPLICATE;
1532224092Sdougb			goto cleanup;
1533224092Sdougb		}
1534224092Sdougb	}
1535224092Sdougb
1536224092Sdougb	if (new->policy == DNS_RPZ_POLICY_CNAME) {
1537245163Serwin		str = cfg_obj_asstring(cfg_tuple_get(policy_obj, "cname"));
1538245163Serwin		result = dns_name_fromstring(&new->cname, str,
1539245163Serwin					     DNS_NAME_DOWNCASE, view->mctx);
1540224092Sdougb		if (result != ISC_R_SUCCESS) {
1541224092Sdougb			cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1542224092Sdougb				    "invalid cname '%s'", str);
1543224092Sdougb			goto cleanup;
1544224092Sdougb		}
1545224092Sdougb	}
1546224092Sdougb
1547224092Sdougb	return (ISC_R_SUCCESS);
1548224092Sdougb
1549224092Sdougb cleanup:
1550224092Sdougb	dns_rpz_view_destroy(view);
1551224092Sdougb	return (result);
1552224092Sdougb}
1553224092Sdougb
1554224092Sdougb/*
1555135446Strhodes * Configure 'view' according to 'vconfig', taking defaults from 'config'
1556135446Strhodes * where values are missing in 'vconfig'.
1557135446Strhodes *
1558135446Strhodes * When configuring the default view, 'vconfig' will be NULL and the
1559135446Strhodes * global defaults in 'config' used exclusively.
1560135446Strhodes */
1561135446Strhodesstatic isc_result_t
1562225361Sdougbconfigure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
1563224092Sdougb	       ns_cachelist_t *cachelist, const cfg_obj_t *bindkeys,
1564224092Sdougb	       isc_mem_t *mctx, cfg_aclconfctx_t *actx,
1565224092Sdougb	       isc_boolean_t need_hints)
1566135446Strhodes{
1567165071Sdougb	const cfg_obj_t *maps[4];
1568165071Sdougb	const cfg_obj_t *cfgmaps[3];
1569224092Sdougb	const cfg_obj_t *optionmaps[3];
1570165071Sdougb	const cfg_obj_t *options = NULL;
1571165071Sdougb	const cfg_obj_t *voptions = NULL;
1572165071Sdougb	const cfg_obj_t *forwardtype;
1573165071Sdougb	const cfg_obj_t *forwarders;
1574165071Sdougb	const cfg_obj_t *alternates;
1575165071Sdougb	const cfg_obj_t *zonelist;
1576186462Sdougb	const cfg_obj_t *dlz;
1577186462Sdougb	unsigned int dlzargc;
1578186462Sdougb	char **dlzargv;
1579165071Sdougb	const cfg_obj_t *disabled;
1580165071Sdougb	const cfg_obj_t *obj;
1581165071Sdougb	const cfg_listelt_t *element;
1582135446Strhodes	in_port_t port;
1583135446Strhodes	dns_cache_t *cache = NULL;
1584135446Strhodes	isc_result_t result;
1585135446Strhodes	isc_uint32_t max_adb_size;
1586224092Sdougb	unsigned int cleaning_interval;
1587135446Strhodes	isc_uint32_t max_cache_size;
1588170222Sdougb	isc_uint32_t max_acache_size;
1589135446Strhodes	isc_uint32_t lame_ttl;
1590224092Sdougb	dns_tsig_keyring_t *ring = NULL;
1591135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
1592225361Sdougb	isc_mem_t *cmctx = NULL, *hmctx = NULL;
1593135446Strhodes	dns_dispatch_t *dispatch4 = NULL;
1594135446Strhodes	dns_dispatch_t *dispatch6 = NULL;
1595135446Strhodes	isc_boolean_t reused_cache = ISC_FALSE;
1596224092Sdougb	isc_boolean_t shared_cache = ISC_FALSE;
1597224092Sdougb	int i = 0, j = 0, k = 0;
1598135446Strhodes	const char *str;
1599224092Sdougb	const char *cachename = NULL;
1600135446Strhodes	dns_order_t *order = NULL;
1601135446Strhodes	isc_uint32_t udpsize;
1602193149Sdougb	unsigned int resopts = 0;
1603170222Sdougb	dns_zone_t *zone = NULL;
1604170222Sdougb	isc_uint32_t max_clients_per_query;
1605170222Sdougb	const char *sep = ": view ";
1606170222Sdougb	const char *viewname = view->name;
1607170222Sdougb	const char *forview = " for view ";
1608170222Sdougb	isc_boolean_t rfc1918;
1609170222Sdougb	isc_boolean_t empty_zones_enable;
1610170222Sdougb	const cfg_obj_t *disablelist = NULL;
1611193149Sdougb	isc_stats_t *resstats = NULL;
1612193149Sdougb	dns_stats_t *resquerystats = NULL;
1613224092Sdougb	isc_boolean_t auto_dlv = ISC_FALSE;
1614224092Sdougb	isc_boolean_t auto_root = ISC_FALSE;
1615224092Sdougb	ns_cache_t *nsc;
1616193149Sdougb	isc_boolean_t zero_no_soattl;
1617224092Sdougb	dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
1618224092Sdougb	unsigned int query_timeout;
1619225361Sdougb	struct cfg_context *nzctx;
1620135446Strhodes
1621135446Strhodes	REQUIRE(DNS_VIEW_VALID(view));
1622135446Strhodes
1623135446Strhodes	if (config != NULL)
1624135446Strhodes		(void)cfg_map_get(config, "options", &options);
1625135446Strhodes
1626224092Sdougb	/*
1627224092Sdougb	 * maps: view options, options, defaults
1628224092Sdougb	 * cfgmaps: view options, config
1629224092Sdougb	 * optionmaps: view options, options
1630224092Sdougb	 */
1631135446Strhodes	if (vconfig != NULL) {
1632135446Strhodes		voptions = cfg_tuple_get(vconfig, "options");
1633135446Strhodes		maps[i++] = voptions;
1634224092Sdougb		optionmaps[j++] = voptions;
1635224092Sdougb		cfgmaps[k++] = voptions;
1636135446Strhodes	}
1637224092Sdougb	if (options != NULL) {
1638135446Strhodes		maps[i++] = options;
1639224092Sdougb		optionmaps[j++] = options;
1640224092Sdougb	}
1641224092Sdougb
1642135446Strhodes	maps[i++] = ns_g_defaults;
1643135446Strhodes	maps[i] = NULL;
1644224092Sdougb	optionmaps[j] = NULL;
1645135446Strhodes	if (config != NULL)
1646224092Sdougb		cfgmaps[k++] = config;
1647224092Sdougb	cfgmaps[k] = NULL;
1648135446Strhodes
1649170222Sdougb	if (!strcmp(viewname, "_default")) {
1650170222Sdougb		sep = "";
1651170222Sdougb		viewname = "";
1652170222Sdougb		forview = "";
1653225361Sdougb		POST(forview);
1654170222Sdougb	}
1655170222Sdougb
1656135446Strhodes	/*
1657135446Strhodes	 * Set the view's port number for outgoing queries.
1658135446Strhodes	 */
1659135446Strhodes	CHECKM(ns_config_getport(config, &port), "port");
1660135446Strhodes	dns_view_setdstport(view, port);
1661135446Strhodes
1662135446Strhodes	/*
1663170222Sdougb	 * Create additional cache for this view and zones under the view
1664170222Sdougb	 * if explicitly enabled.
1665170222Sdougb	 * XXX950 default to on.
1666170222Sdougb	 */
1667170222Sdougb	obj = NULL;
1668170222Sdougb	(void)ns_config_get(maps, "acache-enable", &obj);
1669170222Sdougb	if (obj != NULL && cfg_obj_asboolean(obj)) {
1670170222Sdougb		cmctx = NULL;
1671170222Sdougb		CHECK(isc_mem_create(0, 0, &cmctx));
1672170222Sdougb		CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr,
1673170222Sdougb					ns_g_timermgr));
1674193149Sdougb		isc_mem_setname(cmctx, "acache", NULL);
1675170222Sdougb		isc_mem_detach(&cmctx);
1676170222Sdougb	}
1677170222Sdougb	if (view->acache != NULL) {
1678170222Sdougb		obj = NULL;
1679170222Sdougb		result = ns_config_get(maps, "acache-cleaning-interval", &obj);
1680170222Sdougb		INSIST(result == ISC_R_SUCCESS);
1681170222Sdougb		dns_acache_setcleaninginterval(view->acache,
1682170222Sdougb					       cfg_obj_asuint32(obj) * 60);
1683170222Sdougb
1684170222Sdougb		obj = NULL;
1685170222Sdougb		result = ns_config_get(maps, "max-acache-size", &obj);
1686170222Sdougb		INSIST(result == ISC_R_SUCCESS);
1687170222Sdougb		if (cfg_obj_isstring(obj)) {
1688170222Sdougb			str = cfg_obj_asstring(obj);
1689170222Sdougb			INSIST(strcasecmp(str, "unlimited") == 0);
1690170222Sdougb			max_acache_size = ISC_UINT32_MAX;
1691170222Sdougb		} else {
1692170222Sdougb			isc_resourcevalue_t value;
1693170222Sdougb
1694170222Sdougb			value = cfg_obj_asuint64(obj);
1695170222Sdougb			if (value > ISC_UINT32_MAX) {
1696170222Sdougb				cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
1697170222Sdougb					    "'max-acache-size "
1698170222Sdougb					    "%" ISC_PRINT_QUADFORMAT
1699170222Sdougb					    "d' is too large",
1700170222Sdougb					    value);
1701170222Sdougb				result = ISC_R_RANGE;
1702170222Sdougb				goto cleanup;
1703170222Sdougb			}
1704170222Sdougb			max_acache_size = (isc_uint32_t)value;
1705170222Sdougb		}
1706170222Sdougb		dns_acache_setcachesize(view->acache, max_acache_size);
1707170222Sdougb	}
1708170222Sdougb
1709224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query", NULL, actx,
1710216175Sdougb				 ns_g_mctx, &view->queryacl));
1711216175Sdougb	if (view->queryacl == NULL) {
1712224092Sdougb		CHECK(configure_view_acl(NULL, ns_g_config, "allow-query",
1713224092Sdougb					 NULL, actx, ns_g_mctx,
1714224092Sdougb					 &view->queryacl));
1715216175Sdougb	}
1716216175Sdougb
1717170222Sdougb	/*
1718135446Strhodes	 * Configure the zones.
1719135446Strhodes	 */
1720135446Strhodes	zonelist = NULL;
1721135446Strhodes	if (voptions != NULL)
1722135446Strhodes		(void)cfg_map_get(voptions, "zone", &zonelist);
1723135446Strhodes	else
1724135446Strhodes		(void)cfg_map_get(config, "zone", &zonelist);
1725225361Sdougb
1726225361Sdougb	/*
1727225361Sdougb	 * Load zone configuration
1728225361Sdougb	 */
1729135446Strhodes	for (element = cfg_list_first(zonelist);
1730135446Strhodes	     element != NULL;
1731135446Strhodes	     element = cfg_list_next(element))
1732135446Strhodes	{
1733165071Sdougb		const cfg_obj_t *zconfig = cfg_listelt_value(element);
1734135446Strhodes		CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
1735224092Sdougb				     actx, ISC_FALSE));
1736135446Strhodes	}
1737135446Strhodes
1738224092Sdougb	/*
1739224092Sdougb	 * If we're allowing added zones, then load zone configuration
1740224092Sdougb	 * from the newzone file for zones that were added during previous
1741224092Sdougb	 * runs.
1742224092Sdougb	 */
1743225361Sdougb	nzctx = view->new_zone_config;
1744225361Sdougb	if (nzctx != NULL && nzctx->nzconfig != NULL) {
1745224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1746224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
1747224092Sdougb			      "loading additional zones for view '%s'",
1748224092Sdougb			      view->name);
1749224092Sdougb
1750225361Sdougb		zonelist = NULL;
1751225361Sdougb		cfg_map_get(nzctx->nzconfig, "zone", &zonelist);
1752225361Sdougb
1753225361Sdougb		for (element = cfg_list_first(zonelist);
1754225361Sdougb		     element != NULL;
1755225361Sdougb		     element = cfg_list_next(element))
1756225361Sdougb		{
1757225361Sdougb			const cfg_obj_t *zconfig = cfg_listelt_value(element);
1758225361Sdougb			CHECK(configure_zone(config, zconfig, vconfig,
1759225361Sdougb					     mctx, view, actx,
1760225361Sdougb					     ISC_TRUE));
1761224092Sdougb		}
1762224092Sdougb	}
1763224092Sdougb
1764135446Strhodes	/*
1765170222Sdougb	 * Create Dynamically Loadable Zone driver.
1766170222Sdougb	 */
1767170222Sdougb	dlz = NULL;
1768170222Sdougb	if (voptions != NULL)
1769170222Sdougb		(void)cfg_map_get(voptions, "dlz", &dlz);
1770170222Sdougb	else
1771170222Sdougb		(void)cfg_map_get(config, "dlz", &dlz);
1772170222Sdougb
1773170222Sdougb	obj = NULL;
1774170222Sdougb	if (dlz != NULL) {
1775170222Sdougb		(void)cfg_map_get(cfg_tuple_get(dlz, "options"),
1776170222Sdougb				  "database", &obj);
1777170222Sdougb		if (obj != NULL) {
1778170222Sdougb			char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
1779170222Sdougb			if (s == NULL) {
1780170222Sdougb				result = ISC_R_NOMEMORY;
1781170222Sdougb				goto cleanup;
1782170222Sdougb			}
1783186462Sdougb
1784170222Sdougb			result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv);
1785170222Sdougb			if (result != ISC_R_SUCCESS) {
1786170222Sdougb				isc_mem_free(mctx, s);
1787170222Sdougb				goto cleanup;
1788170222Sdougb			}
1789170222Sdougb
1790170222Sdougb			obj = cfg_tuple_get(dlz, "name");
1791170222Sdougb			result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
1792170222Sdougb					       dlzargv[0], dlzargc, dlzargv,
1793170222Sdougb					       &view->dlzdatabase);
1794170222Sdougb			isc_mem_free(mctx, s);
1795170222Sdougb			isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
1796170222Sdougb			if (result != ISC_R_SUCCESS)
1797170222Sdougb				goto cleanup;
1798224092Sdougb
1799224092Sdougb			/*
1800224092Sdougb			 * If the dlz backend supports configuration,
1801224092Sdougb			 * then call its configure method now.
1802224092Sdougb			 */
1803224092Sdougb			result = dns_dlzconfigure(view, dlzconfigure_callback);
1804224092Sdougb			if (result != ISC_R_SUCCESS)
1805224092Sdougb				goto cleanup;
1806170222Sdougb		}
1807170222Sdougb	}
1808170222Sdougb
1809170222Sdougb	/*
1810193149Sdougb	 * Obtain configuration parameters that affect the decision of whether
1811193149Sdougb	 * we can reuse/share an existing cache.
1812193149Sdougb	 */
1813224092Sdougb	obj = NULL;
1814224092Sdougb	result = ns_config_get(maps, "cleaning-interval", &obj);
1815224092Sdougb	INSIST(result == ISC_R_SUCCESS);
1816224092Sdougb	cleaning_interval = cfg_obj_asuint32(obj) * 60;
1817224092Sdougb
1818224092Sdougb	obj = NULL;
1819224092Sdougb	result = ns_config_get(maps, "max-cache-size", &obj);
1820224092Sdougb	INSIST(result == ISC_R_SUCCESS);
1821224092Sdougb	if (cfg_obj_isstring(obj)) {
1822224092Sdougb		str = cfg_obj_asstring(obj);
1823224092Sdougb		INSIST(strcasecmp(str, "unlimited") == 0);
1824224092Sdougb		max_cache_size = ISC_UINT32_MAX;
1825224092Sdougb	} else {
1826224092Sdougb		isc_resourcevalue_t value;
1827224092Sdougb		value = cfg_obj_asuint64(obj);
1828224092Sdougb		if (value > ISC_UINT32_MAX) {
1829224092Sdougb			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
1830224092Sdougb				    "'max-cache-size "
1831224092Sdougb				    "%" ISC_PRINT_QUADFORMAT "d' is too large",
1832224092Sdougb				    value);
1833224092Sdougb			result = ISC_R_RANGE;
1834224092Sdougb			goto cleanup;
1835224092Sdougb		}
1836224092Sdougb		max_cache_size = (isc_uint32_t)value;
1837224092Sdougb	}
1838224092Sdougb
1839193149Sdougb	/* Check-names. */
1840193149Sdougb	obj = NULL;
1841193149Sdougb	result = ns_checknames_get(maps, "response", &obj);
1842193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1843193149Sdougb
1844193149Sdougb	str = cfg_obj_asstring(obj);
1845193149Sdougb	if (strcasecmp(str, "fail") == 0) {
1846193149Sdougb		resopts |= DNS_RESOLVER_CHECKNAMES |
1847193149Sdougb			DNS_RESOLVER_CHECKNAMESFAIL;
1848193149Sdougb		view->checknames = ISC_TRUE;
1849193149Sdougb	} else if (strcasecmp(str, "warn") == 0) {
1850193149Sdougb		resopts |= DNS_RESOLVER_CHECKNAMES;
1851193149Sdougb		view->checknames = ISC_FALSE;
1852193149Sdougb	} else if (strcasecmp(str, "ignore") == 0) {
1853193149Sdougb		view->checknames = ISC_FALSE;
1854193149Sdougb	} else
1855193149Sdougb		INSIST(0);
1856193149Sdougb
1857193149Sdougb	obj = NULL;
1858193149Sdougb	result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
1859193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1860193149Sdougb	zero_no_soattl = cfg_obj_asboolean(obj);
1861193149Sdougb
1862193149Sdougb	obj = NULL;
1863224092Sdougb	result = ns_config_get(maps, "dns64", &obj);
1864224092Sdougb	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") &&
1865224092Sdougb	    strcmp(view->name, "_meta")) {
1866224092Sdougb		const cfg_listelt_t *element;
1867224092Sdougb		isc_netaddr_t na, suffix, *sp;
1868224092Sdougb		unsigned int prefixlen;
1869224092Sdougb		const char *server, *contact;
1870224092Sdougb		const cfg_obj_t *myobj;
1871224092Sdougb
1872224092Sdougb		myobj = NULL;
1873224092Sdougb		result = ns_config_get(maps, "dns64-server", &myobj);
1874224092Sdougb		if (result == ISC_R_SUCCESS)
1875224092Sdougb			server = cfg_obj_asstring(myobj);
1876224092Sdougb		else
1877224092Sdougb			server = NULL;
1878224092Sdougb
1879224092Sdougb		myobj = NULL;
1880224092Sdougb		result = ns_config_get(maps, "dns64-contact", &myobj);
1881224092Sdougb		if (result == ISC_R_SUCCESS)
1882224092Sdougb			contact = cfg_obj_asstring(myobj);
1883224092Sdougb		else
1884224092Sdougb			contact = NULL;
1885224092Sdougb
1886224092Sdougb		for (element = cfg_list_first(obj);
1887224092Sdougb		     element != NULL;
1888224092Sdougb		     element = cfg_list_next(element))
1889224092Sdougb		{
1890224092Sdougb			const cfg_obj_t *map = cfg_listelt_value(element);
1891224092Sdougb			dns_dns64_t *dns64 = NULL;
1892224092Sdougb			unsigned int dns64options = 0;
1893224092Sdougb
1894224092Sdougb			cfg_obj_asnetprefix(cfg_map_getname(map), &na,
1895224092Sdougb					    &prefixlen);
1896224092Sdougb
1897224092Sdougb			obj = NULL;
1898224092Sdougb			(void)cfg_map_get(map, "suffix", &obj);
1899224092Sdougb			if (obj != NULL) {
1900224092Sdougb				sp = &suffix;
1901224092Sdougb				isc_netaddr_fromsockaddr(sp,
1902224092Sdougb						      cfg_obj_assockaddr(obj));
1903224092Sdougb			} else
1904224092Sdougb				sp = NULL;
1905224092Sdougb
1906224092Sdougb			clients = mapped = excluded = NULL;
1907224092Sdougb			obj = NULL;
1908224092Sdougb			(void)cfg_map_get(map, "clients", &obj);
1909224092Sdougb			if (obj != NULL) {
1910224092Sdougb				result = cfg_acl_fromconfig(obj, config,
1911224092Sdougb							    ns_g_lctx, actx,
1912224092Sdougb							    mctx, 0, &clients);
1913224092Sdougb				if (result != ISC_R_SUCCESS)
1914224092Sdougb					goto cleanup;
1915224092Sdougb			}
1916224092Sdougb			obj = NULL;
1917224092Sdougb			(void)cfg_map_get(map, "mapped", &obj);
1918224092Sdougb			if (obj != NULL) {
1919224092Sdougb				result = cfg_acl_fromconfig(obj, config,
1920224092Sdougb							    ns_g_lctx, actx,
1921224092Sdougb							    mctx, 0, &mapped);
1922224092Sdougb				if (result != ISC_R_SUCCESS)
1923224092Sdougb					goto cleanup;
1924224092Sdougb			}
1925224092Sdougb			obj = NULL;
1926224092Sdougb			(void)cfg_map_get(map, "exclude", &obj);
1927224092Sdougb			if (obj != NULL) {
1928224092Sdougb				result = cfg_acl_fromconfig(obj, config,
1929224092Sdougb							    ns_g_lctx, actx,
1930224092Sdougb							    mctx, 0, &excluded);
1931224092Sdougb				if (result != ISC_R_SUCCESS)
1932224092Sdougb					goto cleanup;
1933224092Sdougb			}
1934224092Sdougb
1935224092Sdougb			obj = NULL;
1936224092Sdougb			(void)cfg_map_get(map, "recursive-only", &obj);
1937224092Sdougb			if (obj != NULL && cfg_obj_asboolean(obj))
1938224092Sdougb				dns64options |= DNS_DNS64_RECURSIVE_ONLY;
1939224092Sdougb
1940224092Sdougb			obj = NULL;
1941224092Sdougb			(void)cfg_map_get(map, "break-dnssec", &obj);
1942224092Sdougb			if (obj != NULL && cfg_obj_asboolean(obj))
1943224092Sdougb				dns64options |= DNS_DNS64_BREAK_DNSSEC;
1944224092Sdougb
1945224092Sdougb			result = dns_dns64_create(mctx, &na, prefixlen, sp,
1946224092Sdougb						  clients, mapped, excluded,
1947224092Sdougb						  dns64options, &dns64);
1948224092Sdougb			if (result != ISC_R_SUCCESS)
1949224092Sdougb				goto cleanup;
1950224092Sdougb			dns_dns64_append(&view->dns64, dns64);
1951224092Sdougb			view->dns64cnt++;
1952224092Sdougb			result = dns64_reverse(view, mctx, &na, prefixlen,
1953224092Sdougb					       server, contact);
1954224092Sdougb			if (result != ISC_R_SUCCESS)
1955224092Sdougb				goto cleanup;
1956224092Sdougb			if (clients != NULL)
1957224092Sdougb				dns_acl_detach(&clients);
1958224092Sdougb			if (mapped != NULL)
1959224092Sdougb				dns_acl_detach(&mapped);
1960224092Sdougb			if (excluded != NULL)
1961224092Sdougb				dns_acl_detach(&excluded);
1962224092Sdougb		}
1963224092Sdougb	}
1964224092Sdougb
1965224092Sdougb	obj = NULL;
1966193149Sdougb	result = ns_config_get(maps, "dnssec-accept-expired", &obj);
1967193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1968193149Sdougb	view->acceptexpired = cfg_obj_asboolean(obj);
1969193149Sdougb
1970193149Sdougb	obj = NULL;
1971193149Sdougb	result = ns_config_get(maps, "dnssec-validation", &obj);
1972193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1973224092Sdougb	if (cfg_obj_isboolean(obj)) {
1974224092Sdougb		view->enablevalidation = cfg_obj_asboolean(obj);
1975224092Sdougb	} else {
1976224092Sdougb		/* If dnssec-validation is not boolean, it must be "auto" */
1977224092Sdougb		view->enablevalidation = ISC_TRUE;
1978224092Sdougb		auto_root = ISC_TRUE;
1979224092Sdougb	}
1980193149Sdougb
1981193149Sdougb	obj = NULL;
1982193149Sdougb	result = ns_config_get(maps, "max-cache-ttl", &obj);
1983193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1984193149Sdougb	view->maxcachettl = cfg_obj_asuint32(obj);
1985193149Sdougb
1986193149Sdougb	obj = NULL;
1987193149Sdougb	result = ns_config_get(maps, "max-ncache-ttl", &obj);
1988193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1989193149Sdougb	view->maxncachettl = cfg_obj_asuint32(obj);
1990193149Sdougb	if (view->maxncachettl > 7 * 24 * 3600)
1991193149Sdougb		view->maxncachettl = 7 * 24 * 3600;
1992193149Sdougb
1993193149Sdougb	/*
1994224092Sdougb	 * Configure the view's cache.
1995135446Strhodes	 *
1996224092Sdougb	 * First, check to see if there are any attach-cache options.  If yes,
1997224092Sdougb	 * attempt to lookup an existing cache at attach it to the view.  If
1998224092Sdougb	 * there is not one, then try to reuse an existing cache if possible;
1999224092Sdougb	 * otherwise create a new cache.
2000224092Sdougb	 *
2001224092Sdougb	 * Note that the ADB is not preserved or shared in either case.
2002224092Sdougb	 *
2003224092Sdougb	 * When a matching view is found, the associated statistics are also
2004224092Sdougb	 * retrieved and reused.
2005224092Sdougb	 *
2006224092Sdougb	 * XXX Determining when it is safe to reuse or share a cache is tricky.
2007193149Sdougb	 * When the view's configuration changes, the cached data may become
2008193149Sdougb	 * invalid because it reflects our old view of the world.  We check
2009224092Sdougb	 * some of the configuration parameters that could invalidate the cache
2010224092Sdougb	 * or otherwise make it unsharable, but there are other configuration
2011224092Sdougb	 * options that should be checked.  For example, if a view uses a
2012224092Sdougb	 * forwarder, changes in the forwarder configuration may invalidate
2013224092Sdougb	 * the cache.  At the moment, it's the administrator's responsibility to
2014224092Sdougb	 * ensure these configuration options don't invalidate reusing/sharing.
2015135446Strhodes	 */
2016224092Sdougb	obj = NULL;
2017224092Sdougb	result = ns_config_get(maps, "attach-cache", &obj);
2018224092Sdougb	if (result == ISC_R_SUCCESS)
2019224092Sdougb		cachename = cfg_obj_asstring(obj);
2020224092Sdougb	else
2021224092Sdougb		cachename = view->name;
2022224092Sdougb	cache = NULL;
2023224092Sdougb	nsc = cachelist_find(cachelist, cachename);
2024224092Sdougb	if (nsc != NULL) {
2025224092Sdougb		if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
2026224092Sdougb				    cleaning_interval, max_cache_size)) {
2027193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2028224092Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
2029224092Sdougb				      "views %s and %s can't share the cache "
2030193149Sdougb				      "due to configuration parameter mismatch",
2031224092Sdougb				      nsc->primaryview->name, view->name);
2032224092Sdougb			result = ISC_R_FAILURE;
2033224092Sdougb			goto cleanup;
2034193149Sdougb		}
2035224092Sdougb		dns_cache_attach(nsc->cache, &cache);
2036224092Sdougb		shared_cache = ISC_TRUE;
2037224092Sdougb	} else {
2038224092Sdougb		if (strcmp(cachename, view->name) == 0) {
2039224092Sdougb			result = dns_viewlist_find(&ns_g_server->viewlist,
2040224092Sdougb						   cachename, view->rdclass,
2041224092Sdougb						   &pview);
2042224092Sdougb			if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
2043224092Sdougb				goto cleanup;
2044224092Sdougb			if (pview != NULL) {
2045224092Sdougb				if (!cache_reusable(pview, view,
2046224092Sdougb						    zero_no_soattl)) {
2047224092Sdougb					isc_log_write(ns_g_lctx,
2048224092Sdougb						      NS_LOGCATEGORY_GENERAL,
2049224092Sdougb						      NS_LOGMODULE_SERVER,
2050224092Sdougb						      ISC_LOG_DEBUG(1),
2051224092Sdougb						      "cache cannot be reused "
2052224092Sdougb						      "for view %s due to "
2053224092Sdougb						      "configuration parameter "
2054224092Sdougb						      "mismatch", view->name);
2055224092Sdougb				} else {
2056224092Sdougb					INSIST(pview->cache != NULL);
2057224092Sdougb					isc_log_write(ns_g_lctx,
2058224092Sdougb						      NS_LOGCATEGORY_GENERAL,
2059224092Sdougb						      NS_LOGMODULE_SERVER,
2060224092Sdougb						      ISC_LOG_DEBUG(3),
2061224092Sdougb						      "reusing existing cache");
2062224092Sdougb					reused_cache = ISC_TRUE;
2063224092Sdougb					dns_cache_attach(pview->cache, &cache);
2064224092Sdougb				}
2065224092Sdougb				dns_view_getresstats(pview, &resstats);
2066224092Sdougb				dns_view_getresquerystats(pview,
2067224092Sdougb							  &resquerystats);
2068224092Sdougb				dns_view_detach(&pview);
2069224092Sdougb			}
2070224092Sdougb		}
2071224092Sdougb		if (cache == NULL) {
2072224092Sdougb			/*
2073224092Sdougb			 * Create a cache with the desired name.  This normally
2074224092Sdougb			 * equals the view name, but may also be a forward
2075224092Sdougb			 * reference to a view that share the cache with this
2076224092Sdougb			 * view but is not yet configured.  If it is not the
2077224092Sdougb			 * view name but not a forward reference either, then it
2078224092Sdougb			 * is simply a named cache that is not shared.
2079225361Sdougb			 *
2080225361Sdougb			 * We use two separate memory contexts for the
2081225361Sdougb			 * cache, for the main cache memory and the heap
2082225361Sdougb			 * memory.
2083224092Sdougb			 */
2084224092Sdougb			CHECK(isc_mem_create(0, 0, &cmctx));
2085224092Sdougb			isc_mem_setname(cmctx, "cache", NULL);
2086225361Sdougb			CHECK(isc_mem_create(0, 0, &hmctx));
2087225361Sdougb			isc_mem_setname(hmctx, "cache_heap", NULL);
2088225361Sdougb			CHECK(dns_cache_create3(cmctx, hmctx, ns_g_taskmgr,
2089224092Sdougb						ns_g_timermgr, view->rdclass,
2090224092Sdougb						cachename, "rbt", 0, NULL,
2091224092Sdougb						&cache));
2092225361Sdougb			isc_mem_detach(&cmctx);
2093225361Sdougb			isc_mem_detach(&hmctx);
2094224092Sdougb		}
2095224092Sdougb		nsc = isc_mem_get(mctx, sizeof(*nsc));
2096224092Sdougb		if (nsc == NULL) {
2097224092Sdougb			result = ISC_R_NOMEMORY;
2098224092Sdougb			goto cleanup;
2099224092Sdougb		}
2100224092Sdougb		nsc->cache = NULL;
2101224092Sdougb		dns_cache_attach(cache, &nsc->cache);
2102224092Sdougb		nsc->primaryview = view;
2103224092Sdougb		nsc->needflush = ISC_FALSE;
2104224092Sdougb		nsc->adbsizeadjusted = ISC_FALSE;
2105224092Sdougb		ISC_LINK_INIT(nsc, link);
2106224092Sdougb		ISC_LIST_APPEND(*cachelist, nsc, link);
2107193149Sdougb	}
2108224092Sdougb	dns_view_setcache2(view, cache, shared_cache);
2109135446Strhodes
2110135446Strhodes	/*
2111135446Strhodes	 * cache-file cannot be inherited if views are present, but this
2112135446Strhodes	 * should be caught by the configuration checking stage.
2113135446Strhodes	 */
2114135446Strhodes	obj = NULL;
2115135446Strhodes	result = ns_config_get(maps, "cache-file", &obj);
2116135446Strhodes	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
2117135446Strhodes		CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
2118224092Sdougb		if (!reused_cache && !shared_cache)
2119135446Strhodes			CHECK(dns_cache_load(cache));
2120135446Strhodes	}
2121135446Strhodes
2122224092Sdougb	dns_cache_setcleaninginterval(cache, cleaning_interval);
2123135446Strhodes	dns_cache_setcachesize(cache, max_cache_size);
2124135446Strhodes
2125135446Strhodes	dns_cache_detach(&cache);
2126135446Strhodes
2127135446Strhodes	/*
2128135446Strhodes	 * Resolver.
2129135446Strhodes	 *
2130135446Strhodes	 * XXXRTH  Hardwired number of tasks.
2131135446Strhodes	 */
2132186462Sdougb	CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4,
2133186462Sdougb					    ISC_TF(ISC_LIST_PREV(view, link)
2134186462Sdougb						   == NULL)));
2135186462Sdougb	CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6,
2136186462Sdougb					    ISC_TF(ISC_LIST_PREV(view, link)
2137186462Sdougb						   == NULL)));
2138135446Strhodes	if (dispatch4 == NULL && dispatch6 == NULL) {
2139135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
2140135446Strhodes				 "unable to obtain neither an IPv4 nor"
2141135446Strhodes				 " an IPv6 dispatch");
2142135446Strhodes		result = ISC_R_UNEXPECTED;
2143135446Strhodes		goto cleanup;
2144135446Strhodes	}
2145135446Strhodes	CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
2146135446Strhodes				      ns_g_socketmgr, ns_g_timermgr,
2147193149Sdougb				      resopts, ns_g_dispatchmgr,
2148135446Strhodes				      dispatch4, dispatch6));
2149135446Strhodes
2150193149Sdougb	if (resstats == NULL) {
2151193149Sdougb		CHECK(isc_stats_create(mctx, &resstats,
2152193149Sdougb				       dns_resstatscounter_max));
2153193149Sdougb	}
2154193149Sdougb	dns_view_setresstats(view, resstats);
2155193149Sdougb	if (resquerystats == NULL)
2156193149Sdougb		CHECK(dns_rdatatypestats_create(mctx, &resquerystats));
2157193149Sdougb	dns_view_setresquerystats(view, resquerystats);
2158193149Sdougb
2159135446Strhodes	/*
2160224092Sdougb	 * Set the ADB cache size to 1/8th of the max-cache-size or
2161224092Sdougb	 * MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared.
2162135446Strhodes	 */
2163135446Strhodes	max_adb_size = 0;
2164135446Strhodes	if (max_cache_size != 0) {
2165135446Strhodes		max_adb_size = max_cache_size / 8;
2166135446Strhodes		if (max_adb_size == 0)
2167135446Strhodes			max_adb_size = 1;	/* Force minimum. */
2168224092Sdougb		if (view != nsc->primaryview &&
2169224092Sdougb		    max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE) {
2170224092Sdougb			max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE;
2171224092Sdougb			if (!nsc->adbsizeadjusted) {
2172224092Sdougb				dns_adb_setadbsize(nsc->primaryview->adb,
2173224092Sdougb						   MAX_ADB_SIZE_FOR_CACHESHARE);
2174224092Sdougb				nsc->adbsizeadjusted = ISC_TRUE;
2175224092Sdougb			}
2176224092Sdougb		}
2177135446Strhodes	}
2178135446Strhodes	dns_adb_setadbsize(view->adb, max_adb_size);
2179135446Strhodes
2180135446Strhodes	/*
2181135446Strhodes	 * Set resolver's lame-ttl.
2182135446Strhodes	 */
2183135446Strhodes	obj = NULL;
2184135446Strhodes	result = ns_config_get(maps, "lame-ttl", &obj);
2185135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2186135446Strhodes	lame_ttl = cfg_obj_asuint32(obj);
2187135446Strhodes	if (lame_ttl > 1800)
2188135446Strhodes		lame_ttl = 1800;
2189135446Strhodes	dns_resolver_setlamettl(view->resolver, lame_ttl);
2190170222Sdougb
2191135446Strhodes	/*
2192224092Sdougb	 * Set the resolver's query timeout.
2193224092Sdougb	 */
2194224092Sdougb	obj = NULL;
2195224092Sdougb	result = ns_config_get(maps, "resolver-query-timeout", &obj);
2196224092Sdougb	INSIST(result == ISC_R_SUCCESS);
2197224092Sdougb	query_timeout = cfg_obj_asuint32(obj);
2198224092Sdougb	dns_resolver_settimeout(view->resolver, query_timeout);
2199224092Sdougb
2200224092Sdougb	/* Specify whether to use 0-TTL for negative response for SOA query */
2201224092Sdougb	dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl);
2202224092Sdougb
2203224092Sdougb	/*
2204135446Strhodes	 * Set the resolver's EDNS UDP size.
2205135446Strhodes	 */
2206135446Strhodes	obj = NULL;
2207135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
2208135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2209135446Strhodes	udpsize = cfg_obj_asuint32(obj);
2210135446Strhodes	if (udpsize < 512)
2211135446Strhodes		udpsize = 512;
2212135446Strhodes	if (udpsize > 4096)
2213135446Strhodes		udpsize = 4096;
2214135446Strhodes	dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize);
2215186462Sdougb
2216135446Strhodes	/*
2217170222Sdougb	 * Set the maximum UDP response size.
2218170222Sdougb	 */
2219170222Sdougb	obj = NULL;
2220170222Sdougb	result = ns_config_get(maps, "max-udp-size", &obj);
2221170222Sdougb	INSIST(result == ISC_R_SUCCESS);
2222170222Sdougb	udpsize = cfg_obj_asuint32(obj);
2223170222Sdougb	if (udpsize < 512)
2224170222Sdougb		udpsize = 512;
2225170222Sdougb	if (udpsize > 4096)
2226170222Sdougb		udpsize = 4096;
2227170222Sdougb	view->maxudp = udpsize;
2228170222Sdougb
2229170222Sdougb	/*
2230135446Strhodes	 * Set supported DNSSEC algorithms.
2231135446Strhodes	 */
2232135446Strhodes	dns_resolver_reset_algorithms(view->resolver);
2233135446Strhodes	disabled = NULL;
2234135446Strhodes	(void)ns_config_get(maps, "disable-algorithms", &disabled);
2235135446Strhodes	if (disabled != NULL) {
2236135446Strhodes		for (element = cfg_list_first(disabled);
2237135446Strhodes		     element != NULL;
2238135446Strhodes		     element = cfg_list_next(element))
2239135446Strhodes			CHECK(disable_algorithms(cfg_listelt_value(element),
2240135446Strhodes						 view->resolver));
2241135446Strhodes	}
2242135446Strhodes
2243135446Strhodes	/*
2244135446Strhodes	 * A global or view "forwarders" option, if present,
2245135446Strhodes	 * creates an entry for "." in the forwarding table.
2246135446Strhodes	 */
2247135446Strhodes	forwardtype = NULL;
2248135446Strhodes	forwarders = NULL;
2249135446Strhodes	(void)ns_config_get(maps, "forward", &forwardtype);
2250135446Strhodes	(void)ns_config_get(maps, "forwarders", &forwarders);
2251135446Strhodes	if (forwarders != NULL)
2252186462Sdougb		CHECK(configure_forward(config, view, dns_rootname,
2253135446Strhodes					forwarders, forwardtype));
2254135446Strhodes
2255135446Strhodes	/*
2256135446Strhodes	 * Dual Stack Servers.
2257135446Strhodes	 */
2258135446Strhodes	alternates = NULL;
2259135446Strhodes	(void)ns_config_get(maps, "dual-stack-servers", &alternates);
2260135446Strhodes	if (alternates != NULL)
2261135446Strhodes		CHECK(configure_alternates(config, view, alternates));
2262135446Strhodes
2263135446Strhodes	/*
2264135446Strhodes	 * We have default hints for class IN if we need them.
2265135446Strhodes	 */
2266135446Strhodes	if (view->rdclass == dns_rdataclass_in && view->hints == NULL)
2267135446Strhodes		dns_view_sethints(view, ns_g_server->in_roothints);
2268135446Strhodes
2269135446Strhodes	/*
2270135446Strhodes	 * If we still have no hints, this is a non-IN view with no
2271135446Strhodes	 * "hints zone" configured.  Issue a warning, except if this
2272186462Sdougb	 * is a root server.  Root servers never need to consult
2273135446Strhodes	 * their hints, so it's no point requiring users to configure
2274135446Strhodes	 * them.
2275135446Strhodes	 */
2276135446Strhodes	if (view->hints == NULL) {
2277135446Strhodes		dns_zone_t *rootzone = NULL;
2278135446Strhodes		(void)dns_view_findzone(view, dns_rootname, &rootzone);
2279135446Strhodes		if (rootzone != NULL) {
2280135446Strhodes			dns_zone_detach(&rootzone);
2281135446Strhodes			need_hints = ISC_FALSE;
2282135446Strhodes		}
2283135446Strhodes		if (need_hints)
2284135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2285135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
2286135446Strhodes				      "no root hints for view '%s'",
2287135446Strhodes				      view->name);
2288135446Strhodes	}
2289135446Strhodes
2290135446Strhodes	/*
2291135446Strhodes	 * Configure the view's TSIG keys.
2292135446Strhodes	 */
2293135446Strhodes	CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
2294224092Sdougb	if (ns_g_server->sessionkey != NULL) {
2295224092Sdougb		CHECK(dns_tsigkeyring_add(ring, ns_g_server->session_keyname,
2296224092Sdougb					  ns_g_server->sessionkey));
2297224092Sdougb	}
2298135446Strhodes	dns_view_setkeyring(view, ring);
2299224092Sdougb	dns_tsigkeyring_detach(&ring);
2300135446Strhodes
2301135446Strhodes	/*
2302224092Sdougb	 * See if we can re-use a dynamic key ring.
2303224092Sdougb	 */
2304224092Sdougb	result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
2305224092Sdougb				   view->rdclass, &pview);
2306224092Sdougb	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
2307224092Sdougb		goto cleanup;
2308224092Sdougb	if (pview != NULL) {
2309224092Sdougb		dns_view_getdynamickeyring(pview, &ring);
2310224092Sdougb		if (ring != NULL)
2311224092Sdougb			dns_view_setdynamickeyring(view, ring);
2312224092Sdougb		dns_tsigkeyring_detach(&ring);
2313224092Sdougb		dns_view_detach(&pview);
2314224092Sdougb	} else
2315224092Sdougb		dns_view_restorekeyring(view);
2316224092Sdougb
2317224092Sdougb	/*
2318135446Strhodes	 * Configure the view's peer list.
2319135446Strhodes	 */
2320135446Strhodes	{
2321165071Sdougb		const cfg_obj_t *peers = NULL;
2322165071Sdougb		const cfg_listelt_t *element;
2323135446Strhodes		dns_peerlist_t *newpeers = NULL;
2324135446Strhodes
2325135446Strhodes		(void)ns_config_get(cfgmaps, "server", &peers);
2326135446Strhodes		CHECK(dns_peerlist_new(mctx, &newpeers));
2327135446Strhodes		for (element = cfg_list_first(peers);
2328135446Strhodes		     element != NULL;
2329135446Strhodes		     element = cfg_list_next(element))
2330135446Strhodes		{
2331165071Sdougb			const cfg_obj_t *cpeer = cfg_listelt_value(element);
2332135446Strhodes			dns_peer_t *peer;
2333135446Strhodes
2334135446Strhodes			CHECK(configure_peer(cpeer, mctx, &peer));
2335135446Strhodes			dns_peerlist_addpeer(newpeers, peer);
2336135446Strhodes			dns_peer_detach(&peer);
2337135446Strhodes		}
2338135446Strhodes		dns_peerlist_detach(&view->peers);
2339135446Strhodes		view->peers = newpeers; /* Transfer ownership. */
2340135446Strhodes	}
2341135446Strhodes
2342135446Strhodes	/*
2343135446Strhodes	 *	Configure the views rrset-order.
2344135446Strhodes	 */
2345135446Strhodes	{
2346165071Sdougb		const cfg_obj_t *rrsetorder = NULL;
2347165071Sdougb		const cfg_listelt_t *element;
2348135446Strhodes
2349135446Strhodes		(void)ns_config_get(maps, "rrset-order", &rrsetorder);
2350135446Strhodes		CHECK(dns_order_create(mctx, &order));
2351135446Strhodes		for (element = cfg_list_first(rrsetorder);
2352135446Strhodes		     element != NULL;
2353135446Strhodes		     element = cfg_list_next(element))
2354135446Strhodes		{
2355165071Sdougb			const cfg_obj_t *ent = cfg_listelt_value(element);
2356135446Strhodes
2357135446Strhodes			CHECK(configure_order(order, ent));
2358135446Strhodes		}
2359135446Strhodes		if (view->order != NULL)
2360135446Strhodes			dns_order_detach(&view->order);
2361135446Strhodes		dns_order_attach(order, &view->order);
2362135446Strhodes		dns_order_detach(&order);
2363135446Strhodes	}
2364135446Strhodes	/*
2365135446Strhodes	 * Copy the aclenv object.
2366135446Strhodes	 */
2367135446Strhodes	dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv);
2368135446Strhodes
2369135446Strhodes	/*
2370135446Strhodes	 * Configure the "match-clients" and "match-destinations" ACL.
2371135446Strhodes	 */
2372224092Sdougb	CHECK(configure_view_acl(vconfig, config, "match-clients", NULL, actx,
2373135446Strhodes				 ns_g_mctx, &view->matchclients));
2374224092Sdougb	CHECK(configure_view_acl(vconfig, config, "match-destinations", NULL,
2375224092Sdougb				 actx, ns_g_mctx, &view->matchdestinations));
2376135446Strhodes
2377135446Strhodes	/*
2378135446Strhodes	 * Configure the "match-recursive-only" option.
2379135446Strhodes	 */
2380135446Strhodes	obj = NULL;
2381165071Sdougb	(void)ns_config_get(maps, "match-recursive-only", &obj);
2382135446Strhodes	if (obj != NULL && cfg_obj_asboolean(obj))
2383135446Strhodes		view->matchrecursiveonly = ISC_TRUE;
2384135446Strhodes	else
2385135446Strhodes		view->matchrecursiveonly = ISC_FALSE;
2386135446Strhodes
2387135446Strhodes	/*
2388135446Strhodes	 * Configure other configurable data.
2389135446Strhodes	 */
2390135446Strhodes	obj = NULL;
2391135446Strhodes	result = ns_config_get(maps, "recursion", &obj);
2392135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2393135446Strhodes	view->recursion = cfg_obj_asboolean(obj);
2394135446Strhodes
2395135446Strhodes	obj = NULL;
2396135446Strhodes	result = ns_config_get(maps, "auth-nxdomain", &obj);
2397135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2398135446Strhodes	view->auth_nxdomain = cfg_obj_asboolean(obj);
2399135446Strhodes
2400135446Strhodes	obj = NULL;
2401135446Strhodes	result = ns_config_get(maps, "minimal-responses", &obj);
2402135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2403135446Strhodes	view->minimalresponses = cfg_obj_asboolean(obj);
2404135446Strhodes
2405135446Strhodes	obj = NULL;
2406135446Strhodes	result = ns_config_get(maps, "transfer-format", &obj);
2407135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2408135446Strhodes	str = cfg_obj_asstring(obj);
2409135446Strhodes	if (strcasecmp(str, "many-answers") == 0)
2410135446Strhodes		view->transfer_format = dns_many_answers;
2411135446Strhodes	else if (strcasecmp(str, "one-answer") == 0)
2412135446Strhodes		view->transfer_format = dns_one_answer;
2413135446Strhodes	else
2414135446Strhodes		INSIST(0);
2415186462Sdougb
2416135446Strhodes	/*
2417135446Strhodes	 * Set sources where additional data and CNAME/DNAME
2418135446Strhodes	 * targets for authoritative answers may be found.
2419135446Strhodes	 */
2420135446Strhodes	obj = NULL;
2421135446Strhodes	result = ns_config_get(maps, "additional-from-auth", &obj);
2422135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2423135446Strhodes	view->additionalfromauth = cfg_obj_asboolean(obj);
2424135446Strhodes	if (view->recursion && ! view->additionalfromauth) {
2425135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
2426135446Strhodes			    "'additional-from-auth no' is only supported "
2427135446Strhodes			    "with 'recursion no'");
2428135446Strhodes		view->additionalfromauth = ISC_TRUE;
2429135446Strhodes	}
2430135446Strhodes
2431135446Strhodes	obj = NULL;
2432135446Strhodes	result = ns_config_get(maps, "additional-from-cache", &obj);
2433135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2434135446Strhodes	view->additionalfromcache = cfg_obj_asboolean(obj);
2435135446Strhodes	if (view->recursion && ! view->additionalfromcache) {
2436135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
2437135446Strhodes			    "'additional-from-cache no' is only supported "
2438135446Strhodes			    "with 'recursion no'");
2439135446Strhodes		view->additionalfromcache = ISC_TRUE;
2440135446Strhodes	}
2441135446Strhodes
2442171577Sdougb	/*
2443193149Sdougb	 * Set "allow-query-cache", "allow-query-cache-on",
2444193149Sdougb	 * "allow-recursion", and "allow-recursion-on" acls if
2445171577Sdougb	 * configured in named.conf.
2446171577Sdougb	 */
2447224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL,
2448216175Sdougb				 actx, ns_g_mctx, &view->cacheacl));
2449224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL,
2450216175Sdougb				 actx, ns_g_mctx, &view->cacheonacl));
2451216175Sdougb	if (view->cacheonacl == NULL)
2452193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2453224092Sdougb					 "allow-query-cache-on", NULL, actx,
2454216175Sdougb					 ns_g_mctx, &view->cacheonacl));
2455193149Sdougb	if (strcmp(view->name, "_bind") != 0) {
2456135446Strhodes		CHECK(configure_view_acl(vconfig, config, "allow-recursion",
2457224092Sdougb					 NULL, actx, ns_g_mctx,
2458193149Sdougb					 &view->recursionacl));
2459193149Sdougb		CHECK(configure_view_acl(vconfig, config, "allow-recursion-on",
2460224092Sdougb					 NULL, actx, ns_g_mctx,
2461193149Sdougb					 &view->recursiononacl));
2462193149Sdougb	}
2463135446Strhodes
2464135446Strhodes	/*
2465171577Sdougb	 * "allow-query-cache" inherits from "allow-recursion" if set,
2466171577Sdougb	 * otherwise from "allow-query" if set.
2467171577Sdougb	 * "allow-recursion" inherits from "allow-query-cache" if set,
2468171577Sdougb	 * otherwise from "allow-query" if set.
2469170222Sdougb	 */
2470216175Sdougb	if (view->cacheacl == NULL && view->recursionacl != NULL)
2471216175Sdougb		dns_acl_attach(view->recursionacl, &view->cacheacl);
2472224092Sdougb	/*
2473224092Sdougb	 * XXXEACH: This call to configure_view_acl() is redundant.  We
2474224092Sdougb	 * are leaving it as it is because we are making a minimal change
2475224092Sdougb	 * for a patch release.  In the future this should be changed to
2476224092Sdougb	 * dns_acl_attach(view->queryacl, &view->cacheacl).
2477224092Sdougb	 */
2478216175Sdougb	if (view->cacheacl == NULL && view->recursion)
2479224092Sdougb		CHECK(configure_view_acl(vconfig, config, "allow-query", NULL,
2480216175Sdougb					 actx, ns_g_mctx, &view->cacheacl));
2481193149Sdougb	if (view->recursion &&
2482216175Sdougb	    view->recursionacl == NULL && view->cacheacl != NULL)
2483216175Sdougb		dns_acl_attach(view->cacheacl, &view->recursionacl);
2484171577Sdougb
2485171577Sdougb	/*
2486193149Sdougb	 * Set default "allow-recursion", "allow-recursion-on" and
2487193149Sdougb	 * "allow-query-cache" acls.
2488171577Sdougb	 */
2489170222Sdougb	if (view->recursionacl == NULL && view->recursion)
2490171577Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2491224092Sdougb					 "allow-recursion", NULL,
2492193149Sdougb					 actx, ns_g_mctx,
2493193149Sdougb					 &view->recursionacl));
2494193149Sdougb	if (view->recursiononacl == NULL && view->recursion)
2495193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2496224092Sdougb					 "allow-recursion-on", NULL,
2497193149Sdougb					 actx, ns_g_mctx,
2498193149Sdougb					 &view->recursiononacl));
2499216175Sdougb	if (view->cacheacl == NULL) {
2500193149Sdougb		if (view->recursion)
2501193149Sdougb			CHECK(configure_view_acl(NULL, ns_g_config,
2502224092Sdougb						 "allow-query-cache", NULL,
2503224092Sdougb						 actx, ns_g_mctx,
2504224092Sdougb						 &view->cacheacl));
2505216175Sdougb		else
2506224092Sdougb			CHECK(dns_acl_none(mctx, &view->cacheacl));
2507193149Sdougb	}
2508170222Sdougb
2509193149Sdougb	/*
2510224092Sdougb	 * Filter setting on addresses in the answer section.
2511224092Sdougb	 */
2512224092Sdougb	CHECK(configure_view_acl(vconfig, config, "deny-answer-addresses",
2513224092Sdougb				 "acl", actx, ns_g_mctx, &view->denyansweracl));
2514224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses",
2515224092Sdougb				       "except-from", ns_g_mctx,
2516224092Sdougb				       &view->answeracl_exclude));
2517224092Sdougb
2518224092Sdougb	/*
2519224092Sdougb	 * Filter setting on names (CNAME/DNAME targets) in the answer section.
2520224092Sdougb	 */
2521224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
2522224092Sdougb				       "name", ns_g_mctx,
2523224092Sdougb				       &view->denyanswernames));
2524224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
2525224092Sdougb				       "except-from", ns_g_mctx,
2526224092Sdougb				       &view->answernames_exclude));
2527224092Sdougb
2528224092Sdougb	/*
2529193149Sdougb	 * Configure sortlist, if set
2530193149Sdougb	 */
2531193149Sdougb	CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx,
2532193149Sdougb				      &view->sortlist));
2533135446Strhodes
2534193149Sdougb	/*
2535193149Sdougb	 * Configure default allow-transfer, allow-notify, allow-update
2536193149Sdougb	 * and allow-update-forwarding ACLs, if set, so they can be
2537193149Sdougb	 * inherited by zones.
2538193149Sdougb	 */
2539193149Sdougb	if (view->notifyacl == NULL)
2540193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2541224092Sdougb					 "allow-notify", NULL, actx,
2542193149Sdougb					 ns_g_mctx, &view->notifyacl));
2543193149Sdougb	if (view->transferacl == NULL)
2544193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2545224092Sdougb					 "allow-transfer", NULL, actx,
2546193149Sdougb					 ns_g_mctx, &view->transferacl));
2547193149Sdougb	if (view->updateacl == NULL)
2548193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2549224092Sdougb					 "allow-update", NULL, actx,
2550193149Sdougb					 ns_g_mctx, &view->updateacl));
2551193149Sdougb	if (view->upfwdacl == NULL)
2552193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2553224092Sdougb					 "allow-update-forwarding", NULL, actx,
2554193149Sdougb					 ns_g_mctx, &view->upfwdacl));
2555193149Sdougb
2556135446Strhodes	obj = NULL;
2557135446Strhodes	result = ns_config_get(maps, "request-ixfr", &obj);
2558135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2559135446Strhodes	view->requestixfr = cfg_obj_asboolean(obj);
2560135446Strhodes
2561135446Strhodes	obj = NULL;
2562135446Strhodes	result = ns_config_get(maps, "provide-ixfr", &obj);
2563135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2564135446Strhodes	view->provideixfr = cfg_obj_asboolean(obj);
2565170222Sdougb
2566170222Sdougb	obj = NULL;
2567193149Sdougb	result = ns_config_get(maps, "request-nsid", &obj);
2568193149Sdougb	INSIST(result == ISC_R_SUCCESS);
2569193149Sdougb	view->requestnsid = cfg_obj_asboolean(obj);
2570193149Sdougb
2571193149Sdougb	obj = NULL;
2572170222Sdougb	result = ns_config_get(maps, "max-clients-per-query", &obj);
2573170222Sdougb	INSIST(result == ISC_R_SUCCESS);
2574170222Sdougb	max_clients_per_query = cfg_obj_asuint32(obj);
2575170222Sdougb
2576170222Sdougb	obj = NULL;
2577170222Sdougb	result = ns_config_get(maps, "clients-per-query", &obj);
2578170222Sdougb	INSIST(result == ISC_R_SUCCESS);
2579170222Sdougb	dns_resolver_setclientsperquery(view->resolver,
2580170222Sdougb					cfg_obj_asuint32(obj),
2581170222Sdougb					max_clients_per_query);
2582186462Sdougb
2583224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
2584135446Strhodes	obj = NULL;
2585224092Sdougb	result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
2586224092Sdougb	INSIST(result == ISC_R_SUCCESS);
2587224092Sdougb	if (cfg_obj_isboolean(obj)) {
2588224092Sdougb		if (cfg_obj_asboolean(obj))
2589224092Sdougb			view->v4_aaaa = dns_v4_aaaa_filter;
2590224092Sdougb		else
2591224092Sdougb			view->v4_aaaa = dns_v4_aaaa_ok;
2592224092Sdougb	} else {
2593224092Sdougb		const char *v4_aaaastr = cfg_obj_asstring(obj);
2594224092Sdougb		if (strcasecmp(v4_aaaastr, "break-dnssec") == 0)
2595224092Sdougb			view->v4_aaaa = dns_v4_aaaa_break_dnssec;
2596224092Sdougb		else
2597224092Sdougb			INSIST(0);
2598224092Sdougb	}
2599224092Sdougb	CHECK(configure_view_acl(vconfig, config, "filter-aaaa", NULL,
2600224092Sdougb				 actx, ns_g_mctx, &view->v4_aaaa_acl));
2601224092Sdougb#endif
2602224092Sdougb
2603224092Sdougb	obj = NULL;
2604135446Strhodes	result = ns_config_get(maps, "dnssec-enable", &obj);
2605135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2606135446Strhodes	view->enablednssec = cfg_obj_asboolean(obj);
2607135446Strhodes
2608135446Strhodes	obj = NULL;
2609224092Sdougb	result = ns_config_get(optionmaps, "dnssec-lookaside", &obj);
2610135446Strhodes	if (result == ISC_R_SUCCESS) {
2611224092Sdougb		/* If set to "auto", use the version from the defaults */
2612224092Sdougb		const cfg_obj_t *dlvobj;
2613234010Sdougb		const char *dom;
2614224092Sdougb		dlvobj = cfg_listelt_value(cfg_list_first(obj));
2615234010Sdougb		dom = cfg_obj_asstring(cfg_tuple_get(dlvobj, "domain"));
2616234010Sdougb		if (cfg_obj_isvoid(cfg_tuple_get(dlvobj, "trust-anchor"))) {
2617234010Sdougb			/* If "no", skip; if "auto", use global default */
2618234010Sdougb			if (!strcasecmp(dom, "no"))
2619234010Sdougb				result = ISC_R_NOTFOUND;
2620234010Sdougb			else if (!strcasecmp(dom, "auto")) {
2621234010Sdougb				auto_dlv = ISC_TRUE;
2622234010Sdougb				obj = NULL;
2623234010Sdougb				result = cfg_map_get(ns_g_defaults,
2624234010Sdougb						     "dnssec-lookaside", &obj);
2625234010Sdougb			}
2626224092Sdougb		}
2627224092Sdougb	}
2628224092Sdougb
2629224092Sdougb	if (result == ISC_R_SUCCESS) {
2630135446Strhodes		for (element = cfg_list_first(obj);
2631135446Strhodes		     element != NULL;
2632135446Strhodes		     element = cfg_list_next(element))
2633135446Strhodes		{
2634135446Strhodes			const char *str;
2635135446Strhodes			isc_buffer_t b;
2636135446Strhodes			dns_name_t *dlv;
2637135446Strhodes
2638135446Strhodes			obj = cfg_listelt_value(element);
2639135446Strhodes			str = cfg_obj_asstring(cfg_tuple_get(obj,
2640135446Strhodes							     "trust-anchor"));
2641135446Strhodes			isc_buffer_init(&b, str, strlen(str));
2642135446Strhodes			isc_buffer_add(&b, strlen(str));
2643135446Strhodes			dlv = dns_fixedname_name(&view->dlv_fixed);
2644135446Strhodes			CHECK(dns_name_fromtext(dlv, &b, dns_rootname,
2645224092Sdougb						DNS_NAME_DOWNCASE, NULL));
2646135446Strhodes			view->dlv = dns_fixedname_name(&view->dlv_fixed);
2647135446Strhodes		}
2648135446Strhodes	} else
2649135446Strhodes		view->dlv = NULL;
2650135446Strhodes
2651135446Strhodes	/*
2652135446Strhodes	 * For now, there is only one kind of trusted keys, the
2653135446Strhodes	 * "security roots".
2654135446Strhodes	 */
2655224092Sdougb	CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys,
2656224092Sdougb					auto_dlv, auto_root, mctx));
2657170222Sdougb	dns_resolver_resetmustbesecure(view->resolver);
2658170222Sdougb	obj = NULL;
2659170222Sdougb	result = ns_config_get(maps, "dnssec-must-be-secure", &obj);
2660170222Sdougb	if (result == ISC_R_SUCCESS)
2661170222Sdougb		CHECK(mustbesecure(obj, view->resolver));
2662135446Strhodes
2663135446Strhodes	obj = NULL;
2664135446Strhodes	result = ns_config_get(maps, "preferred-glue", &obj);
2665135446Strhodes	if (result == ISC_R_SUCCESS) {
2666135446Strhodes		str = cfg_obj_asstring(obj);
2667135446Strhodes		if (strcasecmp(str, "a") == 0)
2668135446Strhodes			view->preferred_glue = dns_rdatatype_a;
2669135446Strhodes		else if (strcasecmp(str, "aaaa") == 0)
2670135446Strhodes			view->preferred_glue = dns_rdatatype_aaaa;
2671135446Strhodes		else
2672135446Strhodes			view->preferred_glue = 0;
2673135446Strhodes	} else
2674135446Strhodes		view->preferred_glue = 0;
2675135446Strhodes
2676135446Strhodes	obj = NULL;
2677135446Strhodes	result = ns_config_get(maps, "root-delegation-only", &obj);
2678135446Strhodes	if (result == ISC_R_SUCCESS) {
2679135446Strhodes		dns_view_setrootdelonly(view, ISC_TRUE);
2680135446Strhodes		if (!cfg_obj_isvoid(obj)) {
2681135446Strhodes			dns_fixedname_t fixed;
2682135446Strhodes			dns_name_t *name;
2683135446Strhodes			isc_buffer_t b;
2684165071Sdougb			const char *str;
2685165071Sdougb			const cfg_obj_t *exclude;
2686135446Strhodes
2687135446Strhodes			dns_fixedname_init(&fixed);
2688135446Strhodes			name = dns_fixedname_name(&fixed);
2689135446Strhodes			for (element = cfg_list_first(obj);
2690135446Strhodes			     element != NULL;
2691135446Strhodes			     element = cfg_list_next(element)) {
2692135446Strhodes				exclude = cfg_listelt_value(element);
2693135446Strhodes				str = cfg_obj_asstring(exclude);
2694135446Strhodes				isc_buffer_init(&b, str, strlen(str));
2695135446Strhodes				isc_buffer_add(&b, strlen(str));
2696135446Strhodes				CHECK(dns_name_fromtext(name, &b, dns_rootname,
2697224092Sdougb							0, NULL));
2698135446Strhodes				CHECK(dns_view_excludedelegationonly(view,
2699135446Strhodes								     name));
2700135446Strhodes			}
2701135446Strhodes		}
2702135446Strhodes	} else
2703135446Strhodes		dns_view_setrootdelonly(view, ISC_FALSE);
2704135446Strhodes
2705170222Sdougb	/*
2706170222Sdougb	 * Setup automatic empty zones.  If recursion is off then
2707170222Sdougb	 * they are disabled by default.
2708170222Sdougb	 */
2709170222Sdougb	obj = NULL;
2710170222Sdougb	(void)ns_config_get(maps, "empty-zones-enable", &obj);
2711170222Sdougb	(void)ns_config_get(maps, "disable-empty-zone", &disablelist);
2712170222Sdougb	if (obj == NULL && disablelist == NULL &&
2713170222Sdougb	    view->rdclass == dns_rdataclass_in) {
2714170222Sdougb		rfc1918 = ISC_FALSE;
2715170222Sdougb		empty_zones_enable = view->recursion;
2716170222Sdougb	} else if (view->rdclass == dns_rdataclass_in) {
2717170222Sdougb		rfc1918 = ISC_TRUE;
2718170222Sdougb		if (obj != NULL)
2719170222Sdougb			empty_zones_enable = cfg_obj_asboolean(obj);
2720170222Sdougb		else
2721170222Sdougb			empty_zones_enable = view->recursion;
2722170222Sdougb	} else {
2723170222Sdougb		rfc1918 = ISC_FALSE;
2724170222Sdougb		empty_zones_enable = ISC_FALSE;
2725170222Sdougb	}
2726234010Sdougb	if (empty_zones_enable && !lwresd_g_useresolvconf) {
2727170222Sdougb		const char *empty;
2728170222Sdougb		int empty_zone = 0;
2729170222Sdougb		dns_fixedname_t fixed;
2730170222Sdougb		dns_name_t *name;
2731170222Sdougb		isc_buffer_t buffer;
2732170222Sdougb		const char *str;
2733170222Sdougb		char server[DNS_NAME_FORMATSIZE + 1];
2734170222Sdougb		char contact[DNS_NAME_FORMATSIZE + 1];
2735170222Sdougb		isc_boolean_t logit;
2736170222Sdougb		const char *empty_dbtype[4] =
2737170222Sdougb				    { "_builtin", "empty", NULL, NULL };
2738170222Sdougb		int empty_dbtypec = 4;
2739193149Sdougb		isc_boolean_t zonestats_on;
2740170222Sdougb
2741170222Sdougb		dns_fixedname_init(&fixed);
2742170222Sdougb		name = dns_fixedname_name(&fixed);
2743170222Sdougb
2744170222Sdougb		obj = NULL;
2745170222Sdougb		result = ns_config_get(maps, "empty-server", &obj);
2746170222Sdougb		if (result == ISC_R_SUCCESS) {
2747170222Sdougb			str = cfg_obj_asstring(obj);
2748170222Sdougb			isc_buffer_init(&buffer, str, strlen(str));
2749170222Sdougb			isc_buffer_add(&buffer, strlen(str));
2750224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
2751224092Sdougb						NULL));
2752170222Sdougb			isc_buffer_init(&buffer, server, sizeof(server) - 1);
2753170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
2754170222Sdougb			server[isc_buffer_usedlength(&buffer)] = 0;
2755170222Sdougb			empty_dbtype[2] = server;
2756170222Sdougb		} else
2757170222Sdougb			empty_dbtype[2] = "@";
2758170222Sdougb
2759170222Sdougb		obj = NULL;
2760170222Sdougb		result = ns_config_get(maps, "empty-contact", &obj);
2761170222Sdougb		if (result == ISC_R_SUCCESS) {
2762170222Sdougb			str = cfg_obj_asstring(obj);
2763170222Sdougb			isc_buffer_init(&buffer, str, strlen(str));
2764170222Sdougb			isc_buffer_add(&buffer, strlen(str));
2765224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
2766224092Sdougb						NULL));
2767170222Sdougb			isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
2768170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
2769170222Sdougb			contact[isc_buffer_usedlength(&buffer)] = 0;
2770170222Sdougb			empty_dbtype[3] = contact;
2771170222Sdougb		} else
2772170222Sdougb			empty_dbtype[3] = ".";
2773170222Sdougb
2774193149Sdougb		obj = NULL;
2775193149Sdougb		result = ns_config_get(maps, "zone-statistics", &obj);
2776193149Sdougb		INSIST(result == ISC_R_SUCCESS);
2777193149Sdougb		zonestats_on = cfg_obj_asboolean(obj);
2778193149Sdougb
2779170222Sdougb		logit = ISC_TRUE;
2780170222Sdougb		for (empty = empty_zones[empty_zone].zone;
2781170222Sdougb		     empty != NULL;
2782170222Sdougb		     empty = empty_zones[++empty_zone].zone)
2783170222Sdougb		{
2784170222Sdougb			dns_forwarders_t *forwarders = NULL;
2785170222Sdougb			dns_view_t *pview = NULL;
2786170222Sdougb
2787170222Sdougb			isc_buffer_init(&buffer, empty, strlen(empty));
2788170222Sdougb			isc_buffer_add(&buffer, strlen(empty));
2789170222Sdougb			/*
2790170222Sdougb			 * Look for zone on drop list.
2791170222Sdougb			 */
2792224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
2793224092Sdougb						NULL));
2794170222Sdougb			if (disablelist != NULL &&
2795170222Sdougb			    on_disable_list(disablelist, name))
2796170222Sdougb				continue;
2797170222Sdougb
2798170222Sdougb			/*
2799170222Sdougb			 * This zone already exists.
2800170222Sdougb			 */
2801170222Sdougb			(void)dns_view_findzone(view, name, &zone);
2802170222Sdougb			if (zone != NULL) {
2803193149Sdougb				CHECK(setquerystats(zone, mctx, zonestats_on));
2804170222Sdougb				dns_zone_detach(&zone);
2805170222Sdougb				continue;
2806170222Sdougb			}
2807170222Sdougb
2808170222Sdougb			/*
2809170222Sdougb			 * If we would forward this name don't add a
2810170222Sdougb			 * empty zone for it.
2811170222Sdougb			 */
2812170222Sdougb			result = dns_fwdtable_find(view->fwdtable, name,
2813170222Sdougb						   &forwarders);
2814170222Sdougb			if (result == ISC_R_SUCCESS &&
2815170222Sdougb			    forwarders->fwdpolicy == dns_fwdpolicy_only)
2816170222Sdougb				continue;
2817186462Sdougb
2818170222Sdougb			if (!rfc1918 && empty_zones[empty_zone].rfc1918) {
2819170222Sdougb				if (logit) {
2820170222Sdougb					isc_log_write(ns_g_lctx,
2821170222Sdougb						      NS_LOGCATEGORY_GENERAL,
2822170222Sdougb						      NS_LOGMODULE_SERVER,
2823170222Sdougb						      ISC_LOG_WARNING,
2824186462Sdougb						      "Warning%s%s: "
2825170222Sdougb						      "'empty-zones-enable/"
2826170222Sdougb						      "disable-empty-zone' "
2827170222Sdougb						      "not set: disabling "
2828170222Sdougb						      "RFC 1918 empty zones",
2829170222Sdougb						      sep, viewname);
2830170222Sdougb					logit = ISC_FALSE;
2831170222Sdougb				}
2832170222Sdougb				continue;
2833170222Sdougb			}
2834170222Sdougb
2835170222Sdougb			/*
2836170222Sdougb			 * See if we can re-use a existing zone.
2837170222Sdougb			 */
2838170222Sdougb			result = dns_viewlist_find(&ns_g_server->viewlist,
2839170222Sdougb						   view->name, view->rdclass,
2840170222Sdougb						   &pview);
2841170222Sdougb			if (result != ISC_R_NOTFOUND &&
2842170222Sdougb			    result != ISC_R_SUCCESS)
2843170222Sdougb				goto cleanup;
2844170222Sdougb
2845170222Sdougb			if (pview != NULL) {
2846170222Sdougb				(void)dns_view_findzone(pview, name, &zone);
2847170222Sdougb				dns_view_detach(&pview);
2848170222Sdougb				if (zone != NULL)
2849170222Sdougb					check_dbtype(&zone, empty_dbtypec,
2850170222Sdougb						     empty_dbtype, mctx);
2851170222Sdougb				if (zone != NULL) {
2852170222Sdougb					dns_zone_setview(zone, view);
2853174187Sdougb					CHECK(dns_view_addzone(view, zone));
2854193149Sdougb					CHECK(setquerystats(zone, mctx,
2855193149Sdougb							    zonestats_on));
2856170222Sdougb					dns_zone_detach(&zone);
2857170222Sdougb					continue;
2858170222Sdougb				}
2859170222Sdougb			}
2860170222Sdougb
2861170222Sdougb			CHECK(dns_zone_create(&zone, mctx));
2862170222Sdougb			CHECK(dns_zone_setorigin(zone, name));
2863170222Sdougb			dns_zone_setview(zone, view);
2864234010Sdougb			CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr,
2865234010Sdougb						     zone));
2866170222Sdougb			dns_zone_setclass(zone, view->rdclass);
2867170222Sdougb			dns_zone_settype(zone, dns_zone_master);
2868193149Sdougb			dns_zone_setstats(zone, ns_g_server->zonestats);
2869170222Sdougb			CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
2870186462Sdougb						 empty_dbtype));
2871170222Sdougb			if (view->queryacl != NULL)
2872170222Sdougb				dns_zone_setqueryacl(zone, view->queryacl);
2873193149Sdougb			if (view->queryonacl != NULL)
2874193149Sdougb				dns_zone_setqueryonacl(zone, view->queryonacl);
2875170222Sdougb			dns_zone_setdialup(zone, dns_dialuptype_no);
2876170222Sdougb			dns_zone_setnotifytype(zone, dns_notifytype_no);
2877170222Sdougb			dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS,
2878170222Sdougb					   ISC_TRUE);
2879193149Sdougb			CHECK(setquerystats(zone, mctx, zonestats_on));
2880170222Sdougb			CHECK(dns_view_addzone(view, zone));
2881170222Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2882170222Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
2883170222Sdougb				      "automatic empty zone%s%s: %s",
2884170222Sdougb				      sep, viewname,  empty);
2885170222Sdougb			dns_zone_detach(&zone);
2886170222Sdougb		}
2887170222Sdougb	}
2888186462Sdougb
2889224092Sdougb	/*
2890224092Sdougb	 * Make the list of response policy zone names for views that
2891224092Sdougb	 * are used for real lookups and so care about hints.
2892224092Sdougb	 */
2893245163Serwin	obj = NULL;
2894245163Serwin	if (view->rdclass == dns_rdataclass_in && need_hints &&
2895245163Serwin	    ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
2896245163Serwin		const cfg_obj_t *recursive_only_obj;
2897245163Serwin		const cfg_obj_t *break_dnssec_obj, *ttl_obj;
2898245163Serwin		isc_boolean_t recursive_only_def;
2899245163Serwin		dns_ttl_t ttl_def;
2900225361Sdougb
2901245163Serwin		recursive_only_obj = cfg_tuple_get(obj, "recursive-only");
2902245163Serwin		if (!cfg_obj_isvoid(recursive_only_obj) &&
2903245163Serwin		    !cfg_obj_asboolean(recursive_only_obj))
2904245163Serwin			recursive_only_def = ISC_FALSE;
2905245163Serwin		else
2906245163Serwin			recursive_only_def = ISC_TRUE;
2907245163Serwin
2908245163Serwin		break_dnssec_obj = cfg_tuple_get(obj, "break-dnssec");
2909245163Serwin		if (!cfg_obj_isvoid(break_dnssec_obj) &&
2910245163Serwin		    cfg_obj_asboolean(break_dnssec_obj))
2911245163Serwin			view->rpz_break_dnssec = ISC_TRUE;
2912245163Serwin		else
2913245163Serwin			view->rpz_break_dnssec = ISC_FALSE;
2914245163Serwin
2915245163Serwin		ttl_obj = cfg_tuple_get(obj, "max-policy-ttl");
2916245163Serwin		if (cfg_obj_isuint32(ttl_obj))
2917245163Serwin			ttl_def = cfg_obj_asuint32(ttl_obj);
2918245163Serwin		else
2919245163Serwin			ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
2920245163Serwin
2921245163Serwin		for (element = cfg_list_first(cfg_tuple_get(obj, "zone list"));
2922224092Sdougb		     element != NULL;
2923224092Sdougb		     element = cfg_list_next(element)) {
2924245163Serwin			result = configure_rpz(view, element,
2925245163Serwin					       recursive_only_def, ttl_def);
2926224092Sdougb			if (result != ISC_R_SUCCESS)
2927224092Sdougb				goto cleanup;
2928224092Sdougb			dns_rpz_set_need(ISC_TRUE);
2929224092Sdougb		}
2930224092Sdougb	}
2931224092Sdougb
2932135446Strhodes	result = ISC_R_SUCCESS;
2933135446Strhodes
2934135446Strhodes cleanup:
2935224092Sdougb	if (clients != NULL)
2936224092Sdougb		dns_acl_detach(&clients);
2937224092Sdougb	if (mapped != NULL)
2938224092Sdougb		dns_acl_detach(&mapped);
2939224092Sdougb	if (excluded != NULL)
2940224092Sdougb		dns_acl_detach(&excluded);
2941224092Sdougb	if (ring != NULL)
2942224092Sdougb		dns_tsigkeyring_detach(&ring);
2943170222Sdougb	if (zone != NULL)
2944170222Sdougb		dns_zone_detach(&zone);
2945135446Strhodes	if (dispatch4 != NULL)
2946135446Strhodes		dns_dispatch_detach(&dispatch4);
2947135446Strhodes	if (dispatch6 != NULL)
2948135446Strhodes		dns_dispatch_detach(&dispatch6);
2949193149Sdougb	if (resstats != NULL)
2950193149Sdougb		isc_stats_detach(&resstats);
2951193149Sdougb	if (resquerystats != NULL)
2952193149Sdougb		dns_stats_detach(&resquerystats);
2953135446Strhodes	if (order != NULL)
2954135446Strhodes		dns_order_detach(&order);
2955135446Strhodes	if (cmctx != NULL)
2956135446Strhodes		isc_mem_detach(&cmctx);
2957225361Sdougb	if (hmctx != NULL)
2958225361Sdougb		isc_mem_detach(&hmctx);
2959135446Strhodes
2960135446Strhodes	if (cache != NULL)
2961135446Strhodes		dns_cache_detach(&cache);
2962135446Strhodes
2963135446Strhodes	return (result);
2964135446Strhodes}
2965135446Strhodes
2966135446Strhodesstatic isc_result_t
2967135446Strhodesconfigure_hints(dns_view_t *view, const char *filename) {
2968135446Strhodes	isc_result_t result;
2969135446Strhodes	dns_db_t *db;
2970135446Strhodes
2971135446Strhodes	db = NULL;
2972135446Strhodes	result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
2973135446Strhodes	if (result == ISC_R_SUCCESS) {
2974135446Strhodes		dns_view_sethints(view, db);
2975135446Strhodes		dns_db_detach(&db);
2976135446Strhodes	}
2977135446Strhodes
2978135446Strhodes	return (result);
2979135446Strhodes}
2980135446Strhodes
2981135446Strhodesstatic isc_result_t
2982165071Sdougbconfigure_alternates(const cfg_obj_t *config, dns_view_t *view,
2983165071Sdougb		     const cfg_obj_t *alternates)
2984135446Strhodes{
2985165071Sdougb	const cfg_obj_t *portobj;
2986165071Sdougb	const cfg_obj_t *addresses;
2987165071Sdougb	const cfg_listelt_t *element;
2988135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
2989135446Strhodes	in_port_t port;
2990135446Strhodes
2991135446Strhodes	/*
2992135446Strhodes	 * Determine which port to send requests to.
2993135446Strhodes	 */
2994135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
2995135446Strhodes		port = ns_g_port;
2996135446Strhodes	else
2997135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
2998135446Strhodes
2999135446Strhodes	if (alternates != NULL) {
3000135446Strhodes		portobj = cfg_tuple_get(alternates, "port");
3001135446Strhodes		if (cfg_obj_isuint32(portobj)) {
3002135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
3003135446Strhodes			if (val > ISC_UINT16_MAX) {
3004135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
3005135446Strhodes					    "port '%u' out of range", val);
3006135446Strhodes				return (ISC_R_RANGE);
3007135446Strhodes			}
3008135446Strhodes			port = (in_port_t) val;
3009135446Strhodes		}
3010135446Strhodes	}
3011135446Strhodes
3012135446Strhodes	addresses = NULL;
3013135446Strhodes	if (alternates != NULL)
3014135446Strhodes		addresses = cfg_tuple_get(alternates, "addresses");
3015135446Strhodes
3016135446Strhodes	for (element = cfg_list_first(addresses);
3017135446Strhodes	     element != NULL;
3018135446Strhodes	     element = cfg_list_next(element))
3019135446Strhodes	{
3020165071Sdougb		const cfg_obj_t *alternate = cfg_listelt_value(element);
3021135446Strhodes		isc_sockaddr_t sa;
3022135446Strhodes
3023135446Strhodes		if (!cfg_obj_issockaddr(alternate)) {
3024135446Strhodes			dns_fixedname_t fixed;
3025135446Strhodes			dns_name_t *name;
3026165071Sdougb			const char *str = cfg_obj_asstring(cfg_tuple_get(
3027165071Sdougb							   alternate, "name"));
3028135446Strhodes			isc_buffer_t buffer;
3029135446Strhodes			in_port_t myport = port;
3030135446Strhodes
3031135446Strhodes			isc_buffer_init(&buffer, str, strlen(str));
3032135446Strhodes			isc_buffer_add(&buffer, strlen(str));
3033135446Strhodes			dns_fixedname_init(&fixed);
3034135446Strhodes			name = dns_fixedname_name(&fixed);
3035224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3036224092Sdougb						NULL));
3037135446Strhodes
3038135446Strhodes			portobj = cfg_tuple_get(alternate, "port");
3039135446Strhodes			if (cfg_obj_isuint32(portobj)) {
3040135446Strhodes				isc_uint32_t val = cfg_obj_asuint32(portobj);
3041135446Strhodes				if (val > ISC_UINT16_MAX) {
3042135446Strhodes					cfg_obj_log(portobj, ns_g_lctx,
3043135446Strhodes						    ISC_LOG_ERROR,
3044135446Strhodes						    "port '%u' out of range",
3045135446Strhodes						     val);
3046135446Strhodes					return (ISC_R_RANGE);
3047135446Strhodes				}
3048135446Strhodes				myport = (in_port_t) val;
3049135446Strhodes			}
3050135446Strhodes			CHECK(dns_resolver_addalternate(view->resolver, NULL,
3051135446Strhodes							name, myport));
3052135446Strhodes			continue;
3053135446Strhodes		}
3054135446Strhodes
3055135446Strhodes		sa = *cfg_obj_assockaddr(alternate);
3056135446Strhodes		if (isc_sockaddr_getport(&sa) == 0)
3057135446Strhodes			isc_sockaddr_setport(&sa, port);
3058135446Strhodes		CHECK(dns_resolver_addalternate(view->resolver, &sa,
3059135446Strhodes						NULL, 0));
3060135446Strhodes	}
3061135446Strhodes
3062135446Strhodes cleanup:
3063135446Strhodes	return (result);
3064135446Strhodes}
3065135446Strhodes
3066135446Strhodesstatic isc_result_t
3067165071Sdougbconfigure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
3068165071Sdougb		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype)
3069135446Strhodes{
3070165071Sdougb	const cfg_obj_t *portobj;
3071165071Sdougb	const cfg_obj_t *faddresses;
3072165071Sdougb	const cfg_listelt_t *element;
3073135446Strhodes	dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
3074135446Strhodes	isc_sockaddrlist_t addresses;
3075135446Strhodes	isc_sockaddr_t *sa;
3076135446Strhodes	isc_result_t result;
3077135446Strhodes	in_port_t port;
3078135446Strhodes
3079193149Sdougb	ISC_LIST_INIT(addresses);
3080193149Sdougb
3081135446Strhodes	/*
3082135446Strhodes	 * Determine which port to send forwarded requests to.
3083135446Strhodes	 */
3084135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
3085135446Strhodes		port = ns_g_port;
3086135446Strhodes	else
3087135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
3088135446Strhodes
3089135446Strhodes	if (forwarders != NULL) {
3090135446Strhodes		portobj = cfg_tuple_get(forwarders, "port");
3091135446Strhodes		if (cfg_obj_isuint32(portobj)) {
3092135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
3093135446Strhodes			if (val > ISC_UINT16_MAX) {
3094135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
3095135446Strhodes					    "port '%u' out of range", val);
3096135446Strhodes				return (ISC_R_RANGE);
3097135446Strhodes			}
3098135446Strhodes			port = (in_port_t) val;
3099135446Strhodes		}
3100135446Strhodes	}
3101135446Strhodes
3102135446Strhodes	faddresses = NULL;
3103135446Strhodes	if (forwarders != NULL)
3104135446Strhodes		faddresses = cfg_tuple_get(forwarders, "addresses");
3105135446Strhodes
3106135446Strhodes	for (element = cfg_list_first(faddresses);
3107135446Strhodes	     element != NULL;
3108135446Strhodes	     element = cfg_list_next(element))
3109135446Strhodes	{
3110165071Sdougb		const cfg_obj_t *forwarder = cfg_listelt_value(element);
3111135446Strhodes		sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t));
3112135446Strhodes		if (sa == NULL) {
3113135446Strhodes			result = ISC_R_NOMEMORY;
3114135446Strhodes			goto cleanup;
3115135446Strhodes		}
3116135446Strhodes		*sa = *cfg_obj_assockaddr(forwarder);
3117135446Strhodes		if (isc_sockaddr_getport(sa) == 0)
3118135446Strhodes			isc_sockaddr_setport(sa, port);
3119135446Strhodes		ISC_LINK_INIT(sa, link);
3120135446Strhodes		ISC_LIST_APPEND(addresses, sa, link);
3121135446Strhodes	}
3122135446Strhodes
3123135446Strhodes	if (ISC_LIST_EMPTY(addresses)) {
3124135446Strhodes		if (forwardtype != NULL)
3125135446Strhodes			cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
3126135446Strhodes				    "no forwarders seen; disabling "
3127135446Strhodes				    "forwarding");
3128135446Strhodes		fwdpolicy = dns_fwdpolicy_none;
3129135446Strhodes	} else {
3130135446Strhodes		if (forwardtype == NULL)
3131135446Strhodes			fwdpolicy = dns_fwdpolicy_first;
3132135446Strhodes		else {
3133165071Sdougb			const char *forwardstr = cfg_obj_asstring(forwardtype);
3134135446Strhodes			if (strcasecmp(forwardstr, "first") == 0)
3135135446Strhodes				fwdpolicy = dns_fwdpolicy_first;
3136135446Strhodes			else if (strcasecmp(forwardstr, "only") == 0)
3137135446Strhodes				fwdpolicy = dns_fwdpolicy_only;
3138135446Strhodes			else
3139135446Strhodes				INSIST(0);
3140135446Strhodes		}
3141135446Strhodes	}
3142135446Strhodes
3143135446Strhodes	result = dns_fwdtable_add(view->fwdtable, origin, &addresses,
3144135446Strhodes				  fwdpolicy);
3145135446Strhodes	if (result != ISC_R_SUCCESS) {
3146135446Strhodes		char namebuf[DNS_NAME_FORMATSIZE];
3147135446Strhodes		dns_name_format(origin, namebuf, sizeof(namebuf));
3148135446Strhodes		cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
3149135446Strhodes			    "could not set up forwarding for domain '%s': %s",
3150135446Strhodes			    namebuf, isc_result_totext(result));
3151135446Strhodes		goto cleanup;
3152135446Strhodes	}
3153135446Strhodes
3154135446Strhodes	result = ISC_R_SUCCESS;
3155135446Strhodes
3156135446Strhodes cleanup:
3157135446Strhodes
3158135446Strhodes	while (!ISC_LIST_EMPTY(addresses)) {
3159135446Strhodes		sa = ISC_LIST_HEAD(addresses);
3160135446Strhodes		ISC_LIST_UNLINK(addresses, sa, link);
3161135446Strhodes		isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t));
3162135446Strhodes	}
3163135446Strhodes
3164135446Strhodes	return (result);
3165135446Strhodes}
3166135446Strhodes
3167135446Strhodesstatic isc_result_t
3168225361Sdougbget_viewinfo(const cfg_obj_t *vconfig, const char **namep,
3169225361Sdougb	     dns_rdataclass_t *classp)
3170165071Sdougb{
3171225361Sdougb	isc_result_t result = ISC_R_SUCCESS;
3172135446Strhodes	const char *viewname;
3173135446Strhodes	dns_rdataclass_t viewclass;
3174135446Strhodes
3175225361Sdougb	REQUIRE(namep != NULL && *namep == NULL);
3176225361Sdougb	REQUIRE(classp != NULL);
3177225361Sdougb
3178135446Strhodes	if (vconfig != NULL) {
3179165071Sdougb		const cfg_obj_t *classobj = NULL;
3180135446Strhodes
3181135446Strhodes		viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
3182135446Strhodes		classobj = cfg_tuple_get(vconfig, "class");
3183135446Strhodes		result = ns_config_getclass(classobj, dns_rdataclass_in,
3184135446Strhodes					    &viewclass);
3185135446Strhodes	} else {
3186135446Strhodes		viewname = "_default";
3187135446Strhodes		viewclass = dns_rdataclass_in;
3188135446Strhodes	}
3189225361Sdougb
3190225361Sdougb	*namep = viewname;
3191225361Sdougb	*classp = viewclass;
3192225361Sdougb
3193225361Sdougb	return (result);
3194225361Sdougb}
3195225361Sdougb
3196225361Sdougb/*
3197225361Sdougb * Find a view based on its configuration info and attach to it.
3198225361Sdougb *
3199225361Sdougb * If 'vconfig' is NULL, attach to the default view.
3200225361Sdougb */
3201225361Sdougbstatic isc_result_t
3202225361Sdougbfind_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
3203225361Sdougb	  dns_view_t **viewp)
3204225361Sdougb{
3205225361Sdougb	isc_result_t result;
3206225361Sdougb	const char *viewname = NULL;
3207225361Sdougb	dns_rdataclass_t viewclass;
3208225361Sdougb	dns_view_t *view = NULL;
3209225361Sdougb
3210225361Sdougb	result = get_viewinfo(vconfig, &viewname, &viewclass);
3211225361Sdougb	if (result != ISC_R_SUCCESS)
3212225361Sdougb		return (result);
3213225361Sdougb
3214135446Strhodes	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
3215225361Sdougb	if (result != ISC_R_SUCCESS)
3216225361Sdougb		return (result);
3217225361Sdougb
3218225361Sdougb	*viewp = view;
3219225361Sdougb	return (ISC_R_SUCCESS);
3220225361Sdougb}
3221225361Sdougb
3222225361Sdougb/*
3223225361Sdougb * Create a new view and add it to the list.
3224225361Sdougb *
3225225361Sdougb * If 'vconfig' is NULL, create the default view.
3226225361Sdougb *
3227225361Sdougb * The view created is attached to '*viewp'.
3228225361Sdougb */
3229225361Sdougbstatic isc_result_t
3230225361Sdougbcreate_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
3231225361Sdougb	    dns_view_t **viewp)
3232225361Sdougb{
3233225361Sdougb	isc_result_t result;
3234225361Sdougb	const char *viewname = NULL;
3235225361Sdougb	dns_rdataclass_t viewclass;
3236225361Sdougb	dns_view_t *view = NULL;
3237225361Sdougb
3238225361Sdougb	result = get_viewinfo(vconfig, &viewname, &viewclass);
3239225361Sdougb	if (result != ISC_R_SUCCESS)
3240225361Sdougb		return (result);
3241225361Sdougb
3242225361Sdougb	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
3243135446Strhodes	if (result == ISC_R_SUCCESS)
3244135446Strhodes		return (ISC_R_EXISTS);
3245135446Strhodes	if (result != ISC_R_NOTFOUND)
3246135446Strhodes		return (result);
3247135446Strhodes	INSIST(view == NULL);
3248135446Strhodes
3249135446Strhodes	result = dns_view_create(ns_g_mctx, viewclass, viewname, &view);
3250135446Strhodes	if (result != ISC_R_SUCCESS)
3251135446Strhodes		return (result);
3252135446Strhodes
3253135446Strhodes	ISC_LIST_APPEND(*viewlist, view, link);
3254135446Strhodes	dns_view_attach(view, viewp);
3255135446Strhodes	return (ISC_R_SUCCESS);
3256135446Strhodes}
3257135446Strhodes
3258135446Strhodes/*
3259135446Strhodes * Configure or reconfigure a zone.
3260135446Strhodes */
3261135446Strhodesstatic isc_result_t
3262165071Sdougbconfigure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
3263165071Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
3264224092Sdougb	       cfg_aclconfctx_t *aclconf, isc_boolean_t added)
3265135446Strhodes{
3266135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
3267135446Strhodes	dns_zone_t *zone = NULL;	/* New or reused zone */
3268135446Strhodes	dns_zone_t *dupzone = NULL;
3269165071Sdougb	const cfg_obj_t *options = NULL;
3270165071Sdougb	const cfg_obj_t *zoptions = NULL;
3271165071Sdougb	const cfg_obj_t *typeobj = NULL;
3272165071Sdougb	const cfg_obj_t *forwarders = NULL;
3273165071Sdougb	const cfg_obj_t *forwardtype = NULL;
3274165071Sdougb	const cfg_obj_t *only = NULL;
3275135446Strhodes	isc_result_t result;
3276135446Strhodes	isc_result_t tresult;
3277135446Strhodes	isc_buffer_t buffer;
3278135446Strhodes	dns_fixedname_t fixorigin;
3279135446Strhodes	dns_name_t *origin;
3280135446Strhodes	const char *zname;
3281135446Strhodes	dns_rdataclass_t zclass;
3282135446Strhodes	const char *ztypestr;
3283135446Strhodes
3284135446Strhodes	options = NULL;
3285135446Strhodes	(void)cfg_map_get(config, "options", &options);
3286135446Strhodes
3287135446Strhodes	zoptions = cfg_tuple_get(zconfig, "options");
3288135446Strhodes
3289135446Strhodes	/*
3290135446Strhodes	 * Get the zone origin as a dns_name_t.
3291135446Strhodes	 */
3292135446Strhodes	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
3293135446Strhodes	isc_buffer_init(&buffer, zname, strlen(zname));
3294135446Strhodes	isc_buffer_add(&buffer, strlen(zname));
3295135446Strhodes	dns_fixedname_init(&fixorigin);
3296135446Strhodes	CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin),
3297224092Sdougb				&buffer, dns_rootname, 0, NULL));
3298135446Strhodes	origin = dns_fixedname_name(&fixorigin);
3299135446Strhodes
3300135446Strhodes	CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
3301135446Strhodes				 view->rdclass, &zclass));
3302135446Strhodes	if (zclass != view->rdclass) {
3303135446Strhodes		const char *vname = NULL;
3304135446Strhodes		if (vconfig != NULL)
3305135446Strhodes			vname = cfg_obj_asstring(cfg_tuple_get(vconfig,
3306135446Strhodes							       "name"));
3307135446Strhodes		else
3308135446Strhodes			vname = "<default view>";
3309186462Sdougb
3310135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3311135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3312135446Strhodes			      "zone '%s': wrong class for view '%s'",
3313135446Strhodes			      zname, vname);
3314135446Strhodes		result = ISC_R_FAILURE;
3315135446Strhodes		goto cleanup;
3316135446Strhodes	}
3317135446Strhodes
3318135446Strhodes	(void)cfg_map_get(zoptions, "type", &typeobj);
3319135446Strhodes	if (typeobj == NULL) {
3320135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3321135446Strhodes			    "zone '%s' 'type' not specified", zname);
3322135446Strhodes		return (ISC_R_FAILURE);
3323135446Strhodes	}
3324135446Strhodes	ztypestr = cfg_obj_asstring(typeobj);
3325135446Strhodes
3326135446Strhodes	/*
3327224092Sdougb	 * "hints zones" aren't zones.	If we've got one,
3328135446Strhodes	 * configure it and return.
3329135446Strhodes	 */
3330135446Strhodes	if (strcasecmp(ztypestr, "hint") == 0) {
3331165071Sdougb		const cfg_obj_t *fileobj = NULL;
3332135446Strhodes		if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) {
3333135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3334135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3335135446Strhodes				      "zone '%s': 'file' not specified",
3336135446Strhodes				      zname);
3337135446Strhodes			result = ISC_R_FAILURE;
3338135446Strhodes			goto cleanup;
3339135446Strhodes		}
3340135446Strhodes		if (dns_name_equal(origin, dns_rootname)) {
3341165071Sdougb			const char *hintsfile = cfg_obj_asstring(fileobj);
3342135446Strhodes
3343135446Strhodes			result = configure_hints(view, hintsfile);
3344135446Strhodes			if (result != ISC_R_SUCCESS) {
3345135446Strhodes				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3346135446Strhodes					      NS_LOGMODULE_SERVER,
3347135446Strhodes					      ISC_LOG_ERROR,
3348135446Strhodes					      "could not configure root hints "
3349135446Strhodes					      "from '%s': %s", hintsfile,
3350135446Strhodes					      isc_result_totext(result));
3351135446Strhodes				goto cleanup;
3352135446Strhodes			}
3353135446Strhodes			/*
3354135446Strhodes			 * Hint zones may also refer to delegation only points.
3355135446Strhodes			 */
3356135446Strhodes			only = NULL;
3357135446Strhodes			tresult = cfg_map_get(zoptions, "delegation-only",
3358135446Strhodes					      &only);
3359135446Strhodes			if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only))
3360135446Strhodes				CHECK(dns_view_adddelegationonly(view, origin));
3361135446Strhodes		} else {
3362135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3363135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
3364135446Strhodes				      "ignoring non-root hint zone '%s'",
3365135446Strhodes				      zname);
3366135446Strhodes			result = ISC_R_SUCCESS;
3367135446Strhodes		}
3368135446Strhodes		/* Skip ordinary zone processing. */
3369135446Strhodes		goto cleanup;
3370135446Strhodes	}
3371135446Strhodes
3372135446Strhodes	/*
3373135446Strhodes	 * "forward zones" aren't zones either.  Translate this syntax into
3374135446Strhodes	 * the appropriate selective forwarding configuration and return.
3375135446Strhodes	 */
3376135446Strhodes	if (strcasecmp(ztypestr, "forward") == 0) {
3377135446Strhodes		forwardtype = NULL;
3378135446Strhodes		forwarders = NULL;
3379135446Strhodes
3380135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
3381135446Strhodes		(void)cfg_map_get(zoptions, "forwarders", &forwarders);
3382135446Strhodes		result = configure_forward(config, view, origin, forwarders,
3383135446Strhodes					   forwardtype);
3384135446Strhodes		goto cleanup;
3385135446Strhodes	}
3386135446Strhodes
3387135446Strhodes	/*
3388135446Strhodes	 * "delegation-only zones" aren't zones either.
3389135446Strhodes	 */
3390135446Strhodes	if (strcasecmp(ztypestr, "delegation-only") == 0) {
3391135446Strhodes		result = dns_view_adddelegationonly(view, origin);
3392135446Strhodes		goto cleanup;
3393135446Strhodes	}
3394135446Strhodes
3395135446Strhodes	/*
3396135446Strhodes	 * Check for duplicates in the new zone table.
3397135446Strhodes	 */
3398135446Strhodes	result = dns_view_findzone(view, origin, &dupzone);
3399135446Strhodes	if (result == ISC_R_SUCCESS) {
3400135446Strhodes		/*
3401135446Strhodes		 * We already have this zone!
3402135446Strhodes		 */
3403135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3404135446Strhodes			    "zone '%s' already exists", zname);
3405135446Strhodes		dns_zone_detach(&dupzone);
3406135446Strhodes		result = ISC_R_EXISTS;
3407135446Strhodes		goto cleanup;
3408135446Strhodes	}
3409135446Strhodes	INSIST(dupzone == NULL);
3410135446Strhodes
3411135446Strhodes	/*
3412135446Strhodes	 * See if we can reuse an existing zone.  This is
3413135446Strhodes	 * only possible if all of these are true:
3414135446Strhodes	 *   - The zone's view exists
3415135446Strhodes	 *   - A zone with the right name exists in the view
3416135446Strhodes	 *   - The zone is compatible with the config
3417135446Strhodes	 *     options (e.g., an existing master zone cannot
3418135446Strhodes	 *     be reused if the options specify a slave zone)
3419135446Strhodes	 */
3420135446Strhodes	result = dns_viewlist_find(&ns_g_server->viewlist,
3421135446Strhodes				   view->name, view->rdclass,
3422135446Strhodes				   &pview);
3423135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3424135446Strhodes		goto cleanup;
3425135446Strhodes	if (pview != NULL)
3426135446Strhodes		result = dns_view_findzone(pview, origin, &zone);
3427135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3428135446Strhodes		goto cleanup;
3429170222Sdougb	if (zone != NULL && !ns_zone_reusable(zone, zconfig))
3430170222Sdougb		dns_zone_detach(&zone);
3431135446Strhodes
3432135446Strhodes	if (zone != NULL) {
3433135446Strhodes		/*
3434135446Strhodes		 * We found a reusable zone.  Make it use the
3435135446Strhodes		 * new view.
3436135446Strhodes		 */
3437135446Strhodes		dns_zone_setview(zone, view);
3438170222Sdougb		if (view->acache != NULL)
3439170222Sdougb			dns_zone_setacache(zone, view->acache);
3440135446Strhodes	} else {
3441135446Strhodes		/*
3442135446Strhodes		 * We cannot reuse an existing zone, we have
3443135446Strhodes		 * to create a new one.
3444135446Strhodes		 */
3445135446Strhodes		CHECK(dns_zone_create(&zone, mctx));
3446135446Strhodes		CHECK(dns_zone_setorigin(zone, origin));
3447135446Strhodes		dns_zone_setview(zone, view);
3448170222Sdougb		if (view->acache != NULL)
3449170222Sdougb			dns_zone_setacache(zone, view->acache);
3450135446Strhodes		CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
3451193149Sdougb		dns_zone_setstats(zone, ns_g_server->zonestats);
3452135446Strhodes	}
3453135446Strhodes
3454135446Strhodes	/*
3455135446Strhodes	 * If the zone contains a 'forwarders' statement, configure
3456135446Strhodes	 * selective forwarding.
3457135446Strhodes	 */
3458135446Strhodes	forwarders = NULL;
3459135446Strhodes	if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS)
3460135446Strhodes	{
3461135446Strhodes		forwardtype = NULL;
3462135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
3463135446Strhodes		CHECK(configure_forward(config, view, origin, forwarders,
3464135446Strhodes					forwardtype));
3465135446Strhodes	}
3466135446Strhodes
3467135446Strhodes	/*
3468135446Strhodes	 * Stub and forward zones may also refer to delegation only points.
3469135446Strhodes	 */
3470135446Strhodes	only = NULL;
3471135446Strhodes	if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS)
3472135446Strhodes	{
3473135446Strhodes		if (cfg_obj_asboolean(only))
3474135446Strhodes			CHECK(dns_view_adddelegationonly(view, origin));
3475135446Strhodes	}
3476135446Strhodes
3477135446Strhodes	/*
3478224092Sdougb	 * Mark whether the zone was originally added at runtime or not
3479224092Sdougb	 */
3480224092Sdougb	dns_zone_setadded(zone, added);
3481224092Sdougb
3482224092Sdougb	/*
3483135446Strhodes	 * Configure the zone.
3484135446Strhodes	 */
3485135446Strhodes	CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone));
3486135446Strhodes
3487135446Strhodes	/*
3488135446Strhodes	 * Add the zone to its view in the new view list.
3489135446Strhodes	 */
3490135446Strhodes	CHECK(dns_view_addzone(view, zone));
3491135446Strhodes
3492234010Sdougb	/*
3493234010Sdougb	 * Ensure that zone keys are reloaded on reconfig
3494234010Sdougb	 */
3495234010Sdougb	if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0)
3496234010Sdougb		dns_zone_rekey(zone, ISC_FALSE);
3497234010Sdougb
3498135446Strhodes cleanup:
3499135446Strhodes	if (zone != NULL)
3500135446Strhodes		dns_zone_detach(&zone);
3501135446Strhodes	if (pview != NULL)
3502135446Strhodes		dns_view_detach(&pview);
3503135446Strhodes
3504135446Strhodes	return (result);
3505135446Strhodes}
3506135446Strhodes
3507135446Strhodes/*
3508224092Sdougb * Configure built-in zone for storing managed-key data.
3509224092Sdougb */
3510224092Sdougb
3511224092Sdougb#define KEYZONE "managed-keys.bind"
3512224092Sdougb#define MKEYS ".mkeys"
3513224092Sdougb
3514224092Sdougbstatic isc_result_t
3515224092Sdougbadd_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
3516224092Sdougb	isc_result_t result;
3517224092Sdougb	dns_view_t *pview = NULL;
3518224092Sdougb	dns_zone_t *zone = NULL;
3519224092Sdougb	dns_acl_t *none = NULL;
3520224092Sdougb	char filename[PATH_MAX];
3521224092Sdougb	char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(MKEYS)];
3522224092Sdougb	int n;
3523224092Sdougb
3524224092Sdougb	REQUIRE(view != NULL);
3525224092Sdougb
3526224092Sdougb	/* See if we can re-use an existing keydata zone. */
3527224092Sdougb	result = dns_viewlist_find(&ns_g_server->viewlist,
3528224092Sdougb				   view->name, view->rdclass,
3529224092Sdougb				   &pview);
3530224092Sdougb	if (result != ISC_R_NOTFOUND &&
3531224092Sdougb	    result != ISC_R_SUCCESS)
3532224092Sdougb		return (result);
3533224092Sdougb
3534224092Sdougb	if (pview != NULL && pview->managed_keys != NULL) {
3535224092Sdougb		dns_zone_attach(pview->managed_keys, &view->managed_keys);
3536224092Sdougb		dns_zone_setview(pview->managed_keys, view);
3537224092Sdougb		dns_view_detach(&pview);
3538234010Sdougb		dns_zone_synckeyzone(view->managed_keys);
3539224092Sdougb		return (ISC_R_SUCCESS);
3540224092Sdougb	}
3541224092Sdougb
3542224092Sdougb	/* No existing keydata zone was found; create one */
3543224092Sdougb	CHECK(dns_zone_create(&zone, mctx));
3544224092Sdougb	CHECK(dns_zone_setorigin(zone, dns_rootname));
3545224092Sdougb
3546224092Sdougb	isc_sha256_data((void *)view->name, strlen(view->name), buffer);
3547224092Sdougb	strcat(buffer, MKEYS);
3548224092Sdougb	n = snprintf(filename, sizeof(filename), "%s%s%s",
3549224092Sdougb		     directory ? directory : "", directory ? "/" : "",
3550224092Sdougb		     strcmp(view->name, "_default") == 0 ? KEYZONE : buffer);
3551224092Sdougb	if (n < 0 || (size_t)n >= sizeof(filename)) {
3552224092Sdougb		result = (n < 0) ? ISC_R_FAILURE : ISC_R_NOSPACE;
3553224092Sdougb		goto cleanup;
3554224092Sdougb	}
3555224092Sdougb	CHECK(dns_zone_setfile(zone, filename));
3556224092Sdougb
3557224092Sdougb	dns_zone_setview(zone, view);
3558224092Sdougb	dns_zone_settype(zone, dns_zone_key);
3559224092Sdougb	dns_zone_setclass(zone, view->rdclass);
3560224092Sdougb
3561224092Sdougb	CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
3562224092Sdougb
3563224092Sdougb	if (view->acache != NULL)
3564224092Sdougb		dns_zone_setacache(zone, view->acache);
3565224092Sdougb
3566224092Sdougb	CHECK(dns_acl_none(mctx, &none));
3567224092Sdougb	dns_zone_setqueryacl(zone, none);
3568224092Sdougb	dns_zone_setqueryonacl(zone, none);
3569224092Sdougb	dns_acl_detach(&none);
3570224092Sdougb
3571224092Sdougb	dns_zone_setdialup(zone, dns_dialuptype_no);
3572224092Sdougb	dns_zone_setnotifytype(zone, dns_notifytype_no);
3573224092Sdougb	dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE);
3574224092Sdougb	dns_zone_setjournalsize(zone, 0);
3575224092Sdougb
3576224092Sdougb	dns_zone_setstats(zone, ns_g_server->zonestats);
3577224092Sdougb	CHECK(setquerystats(zone, mctx, ISC_FALSE));
3578224092Sdougb
3579224092Sdougb	if (view->managed_keys != NULL)
3580224092Sdougb		dns_zone_detach(&view->managed_keys);
3581224092Sdougb	dns_zone_attach(zone, &view->managed_keys);
3582224092Sdougb
3583224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3584224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
3585224092Sdougb		      "set up managed keys zone for view %s, file '%s'",
3586224092Sdougb		      view->name, filename);
3587224092Sdougb
3588224092Sdougbcleanup:
3589224092Sdougb	if (zone != NULL)
3590224092Sdougb		dns_zone_detach(&zone);
3591224092Sdougb	if (none != NULL)
3592224092Sdougb		dns_acl_detach(&none);
3593224092Sdougb
3594224092Sdougb	return (result);
3595224092Sdougb}
3596224092Sdougb
3597224092Sdougb/*
3598135446Strhodes * Configure a single server quota.
3599135446Strhodes */
3600135446Strhodesstatic void
3601165071Sdougbconfigure_server_quota(const cfg_obj_t **maps, const char *name,
3602165071Sdougb		       isc_quota_t *quota)
3603135446Strhodes{
3604165071Sdougb	const cfg_obj_t *obj = NULL;
3605135446Strhodes	isc_result_t result;
3606135446Strhodes
3607135446Strhodes	result = ns_config_get(maps, name, &obj);
3608135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3609153816Sdougb	isc_quota_max(quota, cfg_obj_asuint32(obj));
3610135446Strhodes}
3611135446Strhodes
3612135446Strhodes/*
3613135446Strhodes * This function is called as soon as the 'directory' statement has been
3614135446Strhodes * parsed.  This can be extended to support other options if necessary.
3615135446Strhodes */
3616135446Strhodesstatic isc_result_t
3617165071Sdougbdirectory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
3618135446Strhodes	isc_result_t result;
3619165071Sdougb	const char *directory;
3620135446Strhodes
3621135446Strhodes	REQUIRE(strcasecmp("directory", clausename) == 0);
3622135446Strhodes
3623135446Strhodes	UNUSED(arg);
3624135446Strhodes	UNUSED(clausename);
3625135446Strhodes
3626135446Strhodes	/*
3627135446Strhodes	 * Change directory.
3628135446Strhodes	 */
3629135446Strhodes	directory = cfg_obj_asstring(obj);
3630135446Strhodes
3631135446Strhodes	if (! isc_file_ischdiridempotent(directory))
3632135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
3633135446Strhodes			    "option 'directory' contains relative path '%s'",
3634135446Strhodes			    directory);
3635135446Strhodes
3636135446Strhodes	result = isc_dir_chdir(directory);
3637135446Strhodes	if (result != ISC_R_SUCCESS) {
3638135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
3639135446Strhodes			    "change directory to '%s' failed: %s",
3640135446Strhodes			    directory, isc_result_totext(result));
3641135446Strhodes		return (result);
3642135446Strhodes	}
3643135446Strhodes
3644135446Strhodes	return (ISC_R_SUCCESS);
3645135446Strhodes}
3646135446Strhodes
3647135446Strhodesstatic void
3648135446Strhodesscan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
3649135446Strhodes	isc_boolean_t match_mapped = server->aclenv.match_mapped;
3650135446Strhodes
3651135446Strhodes	ns_interfacemgr_scan(server->interfacemgr, verbose);
3652135446Strhodes	/*
3653135446Strhodes	 * Update the "localhost" and "localnets" ACLs to match the
3654135446Strhodes	 * current set of network interfaces.
3655135446Strhodes	 */
3656135446Strhodes	dns_aclenv_copy(&server->aclenv,
3657135446Strhodes			ns_interfacemgr_getaclenv(server->interfacemgr));
3658135446Strhodes
3659135446Strhodes	server->aclenv.match_mapped = match_mapped;
3660135446Strhodes}
3661135446Strhodes
3662135446Strhodesstatic isc_result_t
3663180477Sdougbadd_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
3664180477Sdougb	      isc_boolean_t wcardport_ok)
3665180477Sdougb{
3666135446Strhodes	ns_listenelt_t *lelt = NULL;
3667135446Strhodes	dns_acl_t *src_acl = NULL;
3668135446Strhodes	isc_result_t result;
3669135446Strhodes	isc_sockaddr_t any_sa6;
3670193149Sdougb	isc_netaddr_t netaddr;
3671135446Strhodes
3672135446Strhodes	REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
3673135446Strhodes
3674135446Strhodes	isc_sockaddr_any6(&any_sa6);
3675180477Sdougb	if (!isc_sockaddr_equal(&any_sa6, addr) &&
3676180477Sdougb	    (wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
3677193149Sdougb		isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr);
3678135446Strhodes
3679193149Sdougb		result = dns_acl_create(mctx, 0, &src_acl);
3680135446Strhodes		if (result != ISC_R_SUCCESS)
3681135446Strhodes			return (result);
3682193149Sdougb
3683193149Sdougb		result = dns_iptable_addprefix(src_acl->iptable,
3684193149Sdougb					       &netaddr, 128, ISC_TRUE);
3685135446Strhodes		if (result != ISC_R_SUCCESS)
3686135446Strhodes			goto clean;
3687135446Strhodes
3688135446Strhodes		result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr),
3689135446Strhodes					     src_acl, &lelt);
3690135446Strhodes		if (result != ISC_R_SUCCESS)
3691135446Strhodes			goto clean;
3692135446Strhodes		ISC_LIST_APPEND(list->elts, lelt, link);
3693135446Strhodes	}
3694135446Strhodes
3695135446Strhodes	return (ISC_R_SUCCESS);
3696135446Strhodes
3697135446Strhodes clean:
3698135446Strhodes	INSIST(lelt == NULL);
3699165071Sdougb	dns_acl_detach(&src_acl);
3700135446Strhodes
3701135446Strhodes	return (result);
3702135446Strhodes}
3703135446Strhodes
3704135446Strhodes/*
3705135446Strhodes * Make a list of xxx-source addresses and call ns_interfacemgr_adjust()
3706135446Strhodes * to update the listening interfaces accordingly.
3707135446Strhodes * We currently only consider IPv6, because this only affects IPv6 wildcard
3708135446Strhodes * sockets.
3709135446Strhodes */
3710135446Strhodesstatic void
3711135446Strhodesadjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
3712135446Strhodes	isc_result_t result;
3713135446Strhodes	ns_listenlist_t *list = NULL;
3714135446Strhodes	dns_view_t *view;
3715135446Strhodes	dns_zone_t *zone, *next;
3716135446Strhodes	isc_sockaddr_t addr, *addrp;
3717135446Strhodes
3718135446Strhodes	result = ns_listenlist_create(mctx, &list);
3719135446Strhodes	if (result != ISC_R_SUCCESS)
3720135446Strhodes		return;
3721135446Strhodes
3722135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
3723135446Strhodes	     view != NULL;
3724135446Strhodes	     view = ISC_LIST_NEXT(view, link)) {
3725135446Strhodes		dns_dispatch_t *dispatch6;
3726135446Strhodes
3727135446Strhodes		dispatch6 = dns_resolver_dispatchv6(view->resolver);
3728143731Sdougb		if (dispatch6 == NULL)
3729143731Sdougb			continue;
3730135446Strhodes		result = dns_dispatch_getlocaladdress(dispatch6, &addr);
3731135446Strhodes		if (result != ISC_R_SUCCESS)
3732135446Strhodes			goto fail;
3733180477Sdougb
3734180477Sdougb		/*
3735180477Sdougb		 * We always add non-wildcard address regardless of whether
3736180477Sdougb		 * the port is 'any' (the fourth arg is TRUE): if the port is
3737180477Sdougb		 * specific, we need to add it since it may conflict with a
3738180477Sdougb		 * listening interface; if it's zero, we'll dynamically open
3739180477Sdougb		 * query ports, and some of them may override an existing
3740180477Sdougb		 * wildcard IPv6 port.
3741180477Sdougb		 */
3742180477Sdougb		result = add_listenelt(mctx, list, &addr, ISC_TRUE);
3743135446Strhodes		if (result != ISC_R_SUCCESS)
3744135446Strhodes			goto fail;
3745135446Strhodes	}
3746135446Strhodes
3747135446Strhodes	zone = NULL;
3748135446Strhodes	for (result = dns_zone_first(server->zonemgr, &zone);
3749135446Strhodes	     result == ISC_R_SUCCESS;
3750135446Strhodes	     next = NULL, result = dns_zone_next(zone, &next), zone = next) {
3751135446Strhodes		dns_view_t *zoneview;
3752135446Strhodes
3753135446Strhodes		/*
3754135446Strhodes		 * At this point the zone list may contain a stale zone
3755135446Strhodes		 * just removed from the configuration.  To see the validity,
3756135446Strhodes		 * check if the corresponding view is in our current view list.
3757153816Sdougb		 * There may also be old zones that are still in the process
3758153816Sdougb		 * of shutting down and have detached from their old view
3759153816Sdougb		 * (zoneview == NULL).
3760135446Strhodes		 */
3761135446Strhodes		zoneview = dns_zone_getview(zone);
3762153816Sdougb		if (zoneview == NULL)
3763153816Sdougb			continue;
3764135446Strhodes		for (view = ISC_LIST_HEAD(server->viewlist);
3765135446Strhodes		     view != NULL && view != zoneview;
3766135446Strhodes		     view = ISC_LIST_NEXT(view, link))
3767135446Strhodes			;
3768135446Strhodes		if (view == NULL)
3769135446Strhodes			continue;
3770135446Strhodes
3771135446Strhodes		addrp = dns_zone_getnotifysrc6(zone);
3772180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
3773135446Strhodes		if (result != ISC_R_SUCCESS)
3774135446Strhodes			goto fail;
3775135446Strhodes
3776135446Strhodes		addrp = dns_zone_getxfrsource6(zone);
3777180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
3778135446Strhodes		if (result != ISC_R_SUCCESS)
3779135446Strhodes			goto fail;
3780135446Strhodes	}
3781135446Strhodes
3782135446Strhodes	ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE);
3783186462Sdougb
3784135446Strhodes clean:
3785135446Strhodes	ns_listenlist_detach(&list);
3786135446Strhodes	return;
3787135446Strhodes
3788135446Strhodes fail:
3789135446Strhodes	/*
3790135446Strhodes	 * Even when we failed the procedure, most of other interfaces
3791135446Strhodes	 * should work correctly.  We therefore just warn it.
3792135446Strhodes	 */
3793135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3794135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
3795135446Strhodes		      "could not adjust the listen-on list; "
3796135446Strhodes		      "some interfaces may not work");
3797135446Strhodes	goto clean;
3798135446Strhodes}
3799135446Strhodes
3800135446Strhodes/*
3801135446Strhodes * This event callback is invoked to do periodic network
3802135446Strhodes * interface scanning.
3803135446Strhodes */
3804135446Strhodesstatic void
3805135446Strhodesinterface_timer_tick(isc_task_t *task, isc_event_t *event) {
3806135446Strhodes	isc_result_t result;
3807135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
3808135446Strhodes	INSIST(task == server->task);
3809135446Strhodes	UNUSED(task);
3810135446Strhodes	isc_event_free(&event);
3811135446Strhodes	/*
3812135446Strhodes	 * XXX should scan interfaces unlocked and get exclusive access
3813135446Strhodes	 * only to replace ACLs.
3814135446Strhodes	 */
3815135446Strhodes	result = isc_task_beginexclusive(server->task);
3816135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3817135446Strhodes	scan_interfaces(server, ISC_FALSE);
3818135446Strhodes	isc_task_endexclusive(server->task);
3819135446Strhodes}
3820135446Strhodes
3821135446Strhodesstatic void
3822135446Strhodesheartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
3823135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
3824135446Strhodes	dns_view_t *view;
3825135446Strhodes
3826135446Strhodes	UNUSED(task);
3827135446Strhodes	isc_event_free(&event);
3828135446Strhodes	view = ISC_LIST_HEAD(server->viewlist);
3829135446Strhodes	while (view != NULL) {
3830135446Strhodes		dns_view_dialup(view);
3831135446Strhodes		view = ISC_LIST_NEXT(view, link);
3832135446Strhodes	}
3833135446Strhodes}
3834135446Strhodes
3835170222Sdougbstatic void
3836170222Sdougbpps_timer_tick(isc_task_t *task, isc_event_t *event) {
3837170222Sdougb	static unsigned int oldrequests = 0;
3838170222Sdougb	unsigned int requests = ns_client_requests;
3839170222Sdougb
3840170222Sdougb	UNUSED(task);
3841170222Sdougb	isc_event_free(&event);
3842170222Sdougb
3843170222Sdougb	/*
3844170222Sdougb	 * Don't worry about wrapping as the overflow result will be right.
3845170222Sdougb	 */
3846170222Sdougb	dns_pps = (requests - oldrequests) / 1200;
3847170222Sdougb	oldrequests = requests;
3848170222Sdougb}
3849170222Sdougb
3850135446Strhodes/*
3851135446Strhodes * Replace the current value of '*field', a dynamically allocated
3852135446Strhodes * string or NULL, with a dynamically allocated copy of the
3853135446Strhodes * null-terminated string pointed to by 'value', or NULL.
3854135446Strhodes */
3855135446Strhodesstatic isc_result_t
3856135446Strhodessetstring(ns_server_t *server, char **field, const char *value) {
3857135446Strhodes	char *copy;
3858135446Strhodes
3859135446Strhodes	if (value != NULL) {
3860135446Strhodes		copy = isc_mem_strdup(server->mctx, value);
3861135446Strhodes		if (copy == NULL)
3862135446Strhodes			return (ISC_R_NOMEMORY);
3863135446Strhodes	} else {
3864135446Strhodes		copy = NULL;
3865135446Strhodes	}
3866135446Strhodes
3867135446Strhodes	if (*field != NULL)
3868135446Strhodes		isc_mem_free(server->mctx, *field);
3869135446Strhodes
3870135446Strhodes	*field = copy;
3871135446Strhodes	return (ISC_R_SUCCESS);
3872186462Sdougb}
3873135446Strhodes
3874135446Strhodes/*
3875135446Strhodes * Replace the current value of '*field', a dynamically allocated
3876135446Strhodes * string or NULL, with another dynamically allocated string
3877135446Strhodes * or NULL if whether 'obj' is a string or void value, respectively.
3878135446Strhodes */
3879135446Strhodesstatic isc_result_t
3880165071Sdougbsetoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) {
3881135446Strhodes	if (cfg_obj_isvoid(obj))
3882135446Strhodes		return (setstring(server, field, NULL));
3883135446Strhodes	else
3884135446Strhodes		return (setstring(server, field, cfg_obj_asstring(obj)));
3885135446Strhodes}
3886135446Strhodes
3887135446Strhodesstatic void
3888165071Sdougbset_limit(const cfg_obj_t **maps, const char *configname,
3889165071Sdougb	  const char *description, isc_resource_t resourceid,
3890165071Sdougb	  isc_resourcevalue_t defaultvalue)
3891135446Strhodes{
3892165071Sdougb	const cfg_obj_t *obj = NULL;
3893165071Sdougb	const char *resource;
3894135446Strhodes	isc_resourcevalue_t value;
3895135446Strhodes	isc_result_t result;
3896135446Strhodes
3897135446Strhodes	if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS)
3898135446Strhodes		return;
3899135446Strhodes
3900135446Strhodes	if (cfg_obj_isstring(obj)) {
3901135446Strhodes		resource = cfg_obj_asstring(obj);
3902135446Strhodes		if (strcasecmp(resource, "unlimited") == 0)
3903135446Strhodes			value = ISC_RESOURCE_UNLIMITED;
3904135446Strhodes		else {
3905135446Strhodes			INSIST(strcasecmp(resource, "default") == 0);
3906135446Strhodes			value = defaultvalue;
3907135446Strhodes		}
3908135446Strhodes	} else
3909135446Strhodes		value = cfg_obj_asuint64(obj);
3910135446Strhodes
3911135446Strhodes	result = isc_resource_setlimit(resourceid, value);
3912135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
3913135446Strhodes		      result == ISC_R_SUCCESS ?
3914186462Sdougb			ISC_LOG_DEBUG(3) : ISC_LOG_WARNING,
3915204619Sdougb		      "set maximum %s to %" ISC_PRINT_QUADFORMAT "u: %s",
3916135446Strhodes		      description, value, isc_result_totext(result));
3917135446Strhodes}
3918135446Strhodes
3919135446Strhodes#define SETLIMIT(cfgvar, resource, description) \
3920135446Strhodes	set_limit(maps, cfgvar, description, isc_resource_ ## resource, \
3921135446Strhodes		  ns_g_init ## resource)
3922135446Strhodes
3923135446Strhodesstatic void
3924165071Sdougbset_limits(const cfg_obj_t **maps) {
3925135446Strhodes	SETLIMIT("stacksize", stacksize, "stack size");
3926135446Strhodes	SETLIMIT("datasize", datasize, "data size");
3927135446Strhodes	SETLIMIT("coresize", coresize, "core size");
3928135446Strhodes	SETLIMIT("files", openfiles, "open files");
3929135446Strhodes}
3930135446Strhodes
3931186462Sdougbstatic void
3932186462Sdougbportset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports,
3933186462Sdougb		 isc_boolean_t positive)
3934135446Strhodes{
3935165071Sdougb	const cfg_listelt_t *element;
3936135446Strhodes
3937135446Strhodes	for (element = cfg_list_first(ports);
3938135446Strhodes	     element != NULL;
3939135446Strhodes	     element = cfg_list_next(element)) {
3940165071Sdougb		const cfg_obj_t *obj = cfg_listelt_value(element);
3941186462Sdougb
3942186462Sdougb		if (cfg_obj_isuint32(obj)) {
3943186462Sdougb			in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
3944186462Sdougb
3945186462Sdougb			if (positive)
3946186462Sdougb				isc_portset_add(portset, port);
3947186462Sdougb			else
3948186462Sdougb				isc_portset_remove(portset, port);
3949186462Sdougb		} else {
3950186462Sdougb			const cfg_obj_t *obj_loport, *obj_hiport;
3951186462Sdougb			in_port_t loport, hiport;
3952186462Sdougb
3953186462Sdougb			obj_loport = cfg_tuple_get(obj, "loport");
3954186462Sdougb			loport = (in_port_t)cfg_obj_asuint32(obj_loport);
3955186462Sdougb			obj_hiport = cfg_tuple_get(obj, "hiport");
3956186462Sdougb			hiport = (in_port_t)cfg_obj_asuint32(obj_hiport);
3957186462Sdougb
3958186462Sdougb			if (positive)
3959186462Sdougb				isc_portset_addrange(portset, loport, hiport);
3960186462Sdougb			else {
3961186462Sdougb				isc_portset_removerange(portset, loport,
3962186462Sdougb							hiport);
3963186462Sdougb			}
3964186462Sdougb		}
3965135446Strhodes	}
3966135446Strhodes}
3967135446Strhodes
3968135446Strhodesstatic isc_result_t
3969170222Sdougbremoved(dns_zone_t *zone, void *uap) {
3970170222Sdougb	const char *type;
3971170222Sdougb
3972186462Sdougb	if (dns_zone_getview(zone) != uap)
3973170222Sdougb		return (ISC_R_SUCCESS);
3974170222Sdougb
3975170222Sdougb	switch (dns_zone_gettype(zone)) {
3976170222Sdougb	case dns_zone_master:
3977170222Sdougb		type = "master";
3978170222Sdougb		break;
3979170222Sdougb	case dns_zone_slave:
3980170222Sdougb		type = "slave";
3981170222Sdougb		break;
3982170222Sdougb	case dns_zone_stub:
3983170222Sdougb		type = "stub";
3984170222Sdougb		break;
3985170222Sdougb	default:
3986170222Sdougb		type = "other";
3987170222Sdougb		break;
3988170222Sdougb	}
3989170222Sdougb	dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type);
3990170222Sdougb	return (ISC_R_SUCCESS);
3991170222Sdougb}
3992170222Sdougb
3993224092Sdougbstatic void
3994224092Sdougbcleanup_session_key(ns_server_t *server, isc_mem_t *mctx) {
3995224092Sdougb	if (server->session_keyfile != NULL) {
3996224092Sdougb		isc_file_remove(server->session_keyfile);
3997224092Sdougb		isc_mem_free(mctx, server->session_keyfile);
3998224092Sdougb		server->session_keyfile = NULL;
3999224092Sdougb	}
4000224092Sdougb
4001224092Sdougb	if (server->session_keyname != NULL) {
4002224092Sdougb		if (dns_name_dynamic(server->session_keyname))
4003224092Sdougb			dns_name_free(server->session_keyname, mctx);
4004224092Sdougb		isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t));
4005224092Sdougb		server->session_keyname = NULL;
4006224092Sdougb	}
4007224092Sdougb
4008224092Sdougb	if (server->sessionkey != NULL)
4009224092Sdougb		dns_tsigkey_detach(&server->sessionkey);
4010224092Sdougb
4011224092Sdougb	server->session_keyalg = DST_ALG_UNKNOWN;
4012224092Sdougb	server->session_keybits = 0;
4013224092Sdougb}
4014224092Sdougb
4015170222Sdougbstatic isc_result_t
4016224092Sdougbgenerate_session_key(const char *filename, const char *keynamestr,
4017224092Sdougb		     dns_name_t *keyname, const char *algstr,
4018224092Sdougb		     dns_name_t *algname, unsigned int algtype,
4019224092Sdougb		     isc_uint16_t bits, isc_mem_t *mctx,
4020224092Sdougb		     dns_tsigkey_t **tsigkeyp)
4021224092Sdougb{
4022224092Sdougb	isc_result_t result = ISC_R_SUCCESS;
4023224092Sdougb	dst_key_t *key = NULL;
4024224092Sdougb	isc_buffer_t key_txtbuffer;
4025224092Sdougb	isc_buffer_t key_rawbuffer;
4026224092Sdougb	char key_txtsecret[256];
4027224092Sdougb	char key_rawsecret[64];
4028224092Sdougb	isc_region_t key_rawregion;
4029224092Sdougb	isc_stdtime_t now;
4030224092Sdougb	dns_tsigkey_t *tsigkey = NULL;
4031224092Sdougb	FILE *fp = NULL;
4032224092Sdougb
4033224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4034224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4035224092Sdougb		      "generating session key for dynamic DNS");
4036224092Sdougb
4037224092Sdougb	/* generate key */
4038224092Sdougb	result = dst_key_generate(keyname, algtype, bits, 1, 0,
4039224092Sdougb				  DNS_KEYPROTO_ANY, dns_rdataclass_in,
4040224092Sdougb				  mctx, &key);
4041224092Sdougb	if (result != ISC_R_SUCCESS)
4042224092Sdougb		return (result);
4043224092Sdougb
4044224092Sdougb	/*
4045224092Sdougb	 * Dump the key to the buffer for later use.  Should be done before
4046224092Sdougb	 * we transfer the ownership of key to tsigkey.
4047224092Sdougb	 */
4048224092Sdougb	isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));
4049224092Sdougb	CHECK(dst_key_tobuffer(key, &key_rawbuffer));
4050224092Sdougb
4051224092Sdougb	isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);
4052224092Sdougb	isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret));
4053224092Sdougb	CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer));
4054224092Sdougb
4055224092Sdougb	/* Store the key in tsigkey. */
4056224092Sdougb	isc_stdtime_get(&now);
4057224092Sdougb	CHECK(dns_tsigkey_createfromkey(dst_key_name(key), algname, key,
4058224092Sdougb					ISC_FALSE, NULL, now, now, mctx, NULL,
4059224092Sdougb					&tsigkey));
4060224092Sdougb
4061224092Sdougb	/* Dump the key to the key file. */
4062224092Sdougb	fp = ns_os_openfile(filename, S_IRUSR|S_IWUSR, ISC_TRUE);
4063224092Sdougb	if (fp == NULL) {
4064224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4065224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4066224092Sdougb			      "could not create %s", filename);
4067224092Sdougb		result = ISC_R_NOPERM;
4068224092Sdougb		goto cleanup;
4069224092Sdougb	}
4070224092Sdougb
4071224092Sdougb	fprintf(fp, "key \"%s\" {\n"
4072224092Sdougb		"\talgorithm %s;\n"
4073224092Sdougb		"\tsecret \"%.*s\";\n};\n", keynamestr, algstr,
4074224092Sdougb		(int) isc_buffer_usedlength(&key_txtbuffer),
4075224092Sdougb		(char*) isc_buffer_base(&key_txtbuffer));
4076224092Sdougb
4077224092Sdougb	RUNTIME_CHECK(isc_stdio_flush(fp) == ISC_R_SUCCESS);
4078224092Sdougb	RUNTIME_CHECK(isc_stdio_close(fp) == ISC_R_SUCCESS);
4079224092Sdougb
4080224092Sdougb	dst_key_free(&key);
4081224092Sdougb
4082224092Sdougb	*tsigkeyp = tsigkey;
4083224092Sdougb
4084224092Sdougb	return (ISC_R_SUCCESS);
4085224092Sdougb
4086224092Sdougb  cleanup:
4087224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4088224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4089224092Sdougb		      "failed to generate session key "
4090224092Sdougb		      "for dynamic DNS: %s", isc_result_totext(result));
4091224092Sdougb	if (tsigkey != NULL)
4092224092Sdougb		dns_tsigkey_detach(&tsigkey);
4093224092Sdougb	if (key != NULL)
4094224092Sdougb		dst_key_free(&key);
4095224092Sdougb
4096224092Sdougb	return (result);
4097224092Sdougb}
4098224092Sdougb
4099224092Sdougbstatic isc_result_t
4100224092Sdougbconfigure_session_key(const cfg_obj_t **maps, ns_server_t *server,
4101224092Sdougb		      isc_mem_t *mctx)
4102224092Sdougb{
4103224092Sdougb	const char *keyfile, *keynamestr, *algstr;
4104224092Sdougb	unsigned int algtype;
4105224092Sdougb	dns_fixedname_t fname;
4106224092Sdougb	dns_name_t *keyname, *algname;
4107224092Sdougb	isc_buffer_t buffer;
4108224092Sdougb	isc_uint16_t bits;
4109224092Sdougb	const cfg_obj_t *obj;
4110224092Sdougb	isc_boolean_t need_deleteold = ISC_FALSE;
4111224092Sdougb	isc_boolean_t need_createnew = ISC_FALSE;
4112224092Sdougb	isc_result_t result;
4113224092Sdougb
4114224092Sdougb	obj = NULL;
4115224092Sdougb	result = ns_config_get(maps, "session-keyfile", &obj);
4116224092Sdougb	if (result == ISC_R_SUCCESS) {
4117224092Sdougb		if (cfg_obj_isvoid(obj))
4118224092Sdougb			keyfile = NULL; /* disable it */
4119224092Sdougb		else
4120224092Sdougb			keyfile = cfg_obj_asstring(obj);
4121224092Sdougb	} else
4122224092Sdougb		keyfile = ns_g_defaultsessionkeyfile;
4123224092Sdougb
4124224092Sdougb	obj = NULL;
4125224092Sdougb	result = ns_config_get(maps, "session-keyname", &obj);
4126224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4127224092Sdougb	keynamestr = cfg_obj_asstring(obj);
4128224092Sdougb	dns_fixedname_init(&fname);
4129224092Sdougb	isc_buffer_init(&buffer, keynamestr, strlen(keynamestr));
4130224092Sdougb	isc_buffer_add(&buffer, strlen(keynamestr));
4131224092Sdougb	keyname = dns_fixedname_name(&fname);
4132224092Sdougb	result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL);
4133224092Sdougb	if (result != ISC_R_SUCCESS)
4134224092Sdougb		return (result);
4135224092Sdougb
4136224092Sdougb	obj = NULL;
4137224092Sdougb	result = ns_config_get(maps, "session-keyalg", &obj);
4138224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4139224092Sdougb	algstr = cfg_obj_asstring(obj);
4140224092Sdougb	algname = NULL;
4141224092Sdougb	result = ns_config_getkeyalgorithm2(algstr, &algname, &algtype, &bits);
4142224092Sdougb	if (result != ISC_R_SUCCESS) {
4143224092Sdougb		const char *s = " (keeping current key)";
4144224092Sdougb
4145224092Sdougb		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "session-keyalg: "
4146224092Sdougb			    "unsupported or unknown algorithm '%s'%s",
4147224092Sdougb			    algstr,
4148224092Sdougb			    server->session_keyfile != NULL ? s : "");
4149224092Sdougb		return (result);
4150224092Sdougb	}
4151224092Sdougb
4152224092Sdougb	/* See if we need to (re)generate a new key. */
4153224092Sdougb	if (keyfile == NULL) {
4154224092Sdougb		if (server->session_keyfile != NULL)
4155224092Sdougb			need_deleteold = ISC_TRUE;
4156224092Sdougb	} else if (server->session_keyfile == NULL)
4157224092Sdougb		need_createnew = ISC_TRUE;
4158224092Sdougb	else if (strcmp(keyfile, server->session_keyfile) != 0 ||
4159224092Sdougb		 !dns_name_equal(server->session_keyname, keyname) ||
4160224092Sdougb		 server->session_keyalg != algtype ||
4161224092Sdougb		 server->session_keybits != bits) {
4162224092Sdougb		need_deleteold = ISC_TRUE;
4163224092Sdougb		need_createnew = ISC_TRUE;
4164224092Sdougb	}
4165224092Sdougb
4166224092Sdougb	if (need_deleteold) {
4167224092Sdougb		INSIST(server->session_keyfile != NULL);
4168224092Sdougb		INSIST(server->session_keyname != NULL);
4169224092Sdougb		INSIST(server->sessionkey != NULL);
4170224092Sdougb
4171224092Sdougb		cleanup_session_key(server, mctx);
4172224092Sdougb	}
4173224092Sdougb
4174224092Sdougb	if (need_createnew) {
4175224092Sdougb		INSIST(server->sessionkey == NULL);
4176224092Sdougb		INSIST(server->session_keyfile == NULL);
4177224092Sdougb		INSIST(server->session_keyname == NULL);
4178224092Sdougb		INSIST(server->session_keyalg == DST_ALG_UNKNOWN);
4179224092Sdougb		INSIST(server->session_keybits == 0);
4180224092Sdougb
4181224092Sdougb		server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t));
4182224092Sdougb		if (server->session_keyname == NULL)
4183224092Sdougb			goto cleanup;
4184224092Sdougb		dns_name_init(server->session_keyname, NULL);
4185224092Sdougb		CHECK(dns_name_dup(keyname, mctx, server->session_keyname));
4186224092Sdougb
4187224092Sdougb		server->session_keyfile = isc_mem_strdup(mctx, keyfile);
4188224092Sdougb		if (server->session_keyfile == NULL)
4189224092Sdougb			goto cleanup;
4190224092Sdougb
4191224092Sdougb		server->session_keyalg = algtype;
4192224092Sdougb		server->session_keybits = bits;
4193224092Sdougb
4194224092Sdougb		CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr,
4195224092Sdougb					   algname, algtype, bits, mctx,
4196224092Sdougb					   &server->sessionkey));
4197224092Sdougb	}
4198224092Sdougb
4199224092Sdougb	return (result);
4200224092Sdougb
4201224092Sdougb  cleanup:
4202224092Sdougb	cleanup_session_key(server, mctx);
4203224092Sdougb	return (result);
4204224092Sdougb}
4205224092Sdougb
4206224092Sdougbstatic isc_result_t
4207225361Sdougbsetup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
4208225361Sdougb	       cfg_parser_t *parser, cfg_aclconfctx_t *actx)
4209225361Sdougb{
4210225361Sdougb	isc_result_t result = ISC_R_SUCCESS;
4211225361Sdougb	isc_boolean_t allow = ISC_FALSE;
4212225361Sdougb	struct cfg_context *nzcfg = NULL;
4213225361Sdougb	cfg_parser_t *nzparser = NULL;
4214225361Sdougb	cfg_obj_t *nzconfig = NULL;
4215225361Sdougb	const cfg_obj_t *maps[4];
4216225361Sdougb	const cfg_obj_t *options = NULL, *voptions = NULL;
4217225361Sdougb	const cfg_obj_t *nz = NULL;
4218225361Sdougb	int i = 0;
4219225361Sdougb
4220225361Sdougb	REQUIRE (config != NULL);
4221225361Sdougb
4222225361Sdougb	if (vconfig != NULL)
4223225361Sdougb		voptions = cfg_tuple_get(vconfig, "options");
4224225361Sdougb	if (voptions != NULL)
4225225361Sdougb		maps[i++] = voptions;
4226225361Sdougb	result = cfg_map_get(config, "options", &options);
4227225361Sdougb	if (result == ISC_R_SUCCESS)
4228225361Sdougb		maps[i++] = options;
4229225361Sdougb	maps[i++] = ns_g_defaults;
4230225361Sdougb	maps[i] = NULL;
4231225361Sdougb
4232225361Sdougb	result = ns_config_get(maps, "allow-new-zones", &nz);
4233225361Sdougb	if (result == ISC_R_SUCCESS)
4234225361Sdougb		allow = cfg_obj_asboolean(nz);
4235225361Sdougb
4236225361Sdougb	if (!allow) {
4237225361Sdougb		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
4238225361Sdougb		return (ISC_R_SUCCESS);
4239225361Sdougb	}
4240225361Sdougb
4241225361Sdougb	nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg));
4242225361Sdougb	if (nzcfg == NULL) {
4243225361Sdougb		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
4244225361Sdougb		return (ISC_R_NOMEMORY);
4245225361Sdougb	}
4246225361Sdougb
4247225361Sdougb	dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy);
4248225361Sdougb
4249225361Sdougb	memset(nzcfg, 0, sizeof(*nzcfg));
4250225361Sdougb	isc_mem_attach(view->mctx, &nzcfg->mctx);
4251225361Sdougb	cfg_obj_attach(config, &nzcfg->config);
4252225361Sdougb	cfg_parser_attach(parser, &nzcfg->parser);
4253225361Sdougb	cfg_aclconfctx_attach(actx, &nzcfg->actx);
4254225361Sdougb
4255225361Sdougb	/*
4256225361Sdougb	 * Attempt to create a parser and parse the newzones
4257225361Sdougb	 * file.  If successful, preserve both; otherwise leave
4258225361Sdougb	 * them NULL.
4259225361Sdougb	 */
4260225361Sdougb	result = cfg_parser_create(view->mctx, ns_g_lctx, &nzparser);
4261225361Sdougb	if (result == ISC_R_SUCCESS)
4262225361Sdougb		result = cfg_parse_file(nzparser, view->new_zone_file,
4263225361Sdougb					&cfg_type_newzones, &nzconfig);
4264225361Sdougb	if (result == ISC_R_SUCCESS) {
4265225361Sdougb		cfg_parser_attach(nzparser, &nzcfg->nzparser);
4266225361Sdougb		cfg_obj_attach(nzconfig, &nzcfg->nzconfig);
4267225361Sdougb	}
4268225361Sdougb
4269225361Sdougb	if (nzparser != NULL) {
4270225361Sdougb		if (nzconfig != NULL)
4271225361Sdougb			cfg_obj_destroy(nzparser, &nzconfig);
4272225361Sdougb		cfg_parser_destroy(&nzparser);
4273225361Sdougb	}
4274225361Sdougb
4275225361Sdougb	return (ISC_R_SUCCESS);
4276225361Sdougb}
4277225361Sdougb
4278225361Sdougbstatic int
4279225361Sdougbcount_zones(const cfg_obj_t *conf) {
4280225361Sdougb	const cfg_obj_t *zonelist = NULL;
4281225361Sdougb	const cfg_listelt_t *element;
4282225361Sdougb	int n = 0;
4283225361Sdougb
4284225361Sdougb	REQUIRE(conf != NULL);
4285225361Sdougb
4286225361Sdougb	cfg_map_get(conf, "zone", &zonelist);
4287225361Sdougb	for (element = cfg_list_first(zonelist);
4288225361Sdougb	     element != NULL;
4289225361Sdougb	     element = cfg_list_next(element))
4290225361Sdougb		n++;
4291225361Sdougb
4292225361Sdougb	return (n);
4293225361Sdougb}
4294225361Sdougb
4295225361Sdougbstatic isc_result_t
4296135446Strhodesload_configuration(const char *filename, ns_server_t *server,
4297135446Strhodes		   isc_boolean_t first_time)
4298135446Strhodes{
4299224092Sdougb	cfg_obj_t *config = NULL, *bindkeys = NULL;
4300224092Sdougb	cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL;
4301182645Sdougb	const cfg_listelt_t *element;
4302182645Sdougb	const cfg_obj_t *builtin_views;
4303182645Sdougb	const cfg_obj_t *maps[3];
4304182645Sdougb	const cfg_obj_t *obj;
4305165071Sdougb	const cfg_obj_t *options;
4306186462Sdougb	const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports;
4307165071Sdougb	const cfg_obj_t *views;
4308135446Strhodes	dns_view_t *view = NULL;
4309135446Strhodes	dns_view_t *view_next;
4310182645Sdougb	dns_viewlist_t tmpviewlist;
4311224092Sdougb	dns_viewlist_t viewlist, builtin_viewlist;
4312186462Sdougb	in_port_t listen_port, udpport_low, udpport_high;
4313182645Sdougb	int i;
4314182645Sdougb	isc_interval_t interval;
4315186462Sdougb	isc_portset_t *v4portset = NULL;
4316186462Sdougb	isc_portset_t *v6portset = NULL;
4317186462Sdougb	isc_resourcevalue_t nfiles;
4318182645Sdougb	isc_result_t result;
4319182645Sdougb	isc_uint32_t heartbeat_interval;
4320135446Strhodes	isc_uint32_t interface_interval;
4321182645Sdougb	isc_uint32_t reserved;
4322135446Strhodes	isc_uint32_t udpsize;
4323224092Sdougb	ns_cachelist_t cachelist, tmpcachelist;
4324186462Sdougb	unsigned int maxsocks;
4325224092Sdougb	ns_cache_t *nsc;
4326225361Sdougb	struct cfg_context *nzctx;
4327225361Sdougb	int num_zones = 0;
4328234010Sdougb	isc_boolean_t exclusive = ISC_FALSE;
4329135446Strhodes
4330135446Strhodes	ISC_LIST_INIT(viewlist);
4331224092Sdougb	ISC_LIST_INIT(builtin_viewlist);
4332224092Sdougb	ISC_LIST_INIT(cachelist);
4333135446Strhodes
4334225361Sdougb	/* Create the ACL configuration context */
4335225361Sdougb	if (ns_g_aclconfctx != NULL)
4336225361Sdougb		cfg_aclconfctx_detach(&ns_g_aclconfctx);
4337225361Sdougb	CHECK(cfg_aclconfctx_create(ns_g_mctx, &ns_g_aclconfctx));
4338225361Sdougb
4339135446Strhodes	/*
4340135446Strhodes	 * Parse the global default pseudo-config file.
4341135446Strhodes	 */
4342135446Strhodes	if (first_time) {
4343135446Strhodes		CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config));
4344135446Strhodes		RUNTIME_CHECK(cfg_map_get(ns_g_config, "options",
4345224092Sdougb					  &ns_g_defaults) == ISC_R_SUCCESS);
4346135446Strhodes	}
4347135446Strhodes
4348135446Strhodes	/*
4349135446Strhodes	 * Parse the configuration file using the new config code.
4350135446Strhodes	 */
4351135446Strhodes	result = ISC_R_FAILURE;
4352135446Strhodes	config = NULL;
4353135446Strhodes
4354135446Strhodes	/*
4355135446Strhodes	 * Unless this is lwresd with the -C option, parse the config file.
4356135446Strhodes	 */
4357135446Strhodes	if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) {
4358135446Strhodes		isc_log_write(ns_g_lctx,
4359135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4360135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
4361135446Strhodes			      filename);
4362224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
4363224092Sdougb		cfg_parser_setcallback(conf_parser, directory_callback, NULL);
4364224092Sdougb		result = cfg_parse_file(conf_parser, filename,
4365224092Sdougb					&cfg_type_namedconf, &config);
4366135446Strhodes	}
4367135446Strhodes
4368135446Strhodes	/*
4369135446Strhodes	 * If this is lwresd with the -C option, or lwresd with no -C or -c
4370135446Strhodes	 * option where the above parsing failed, parse resolv.conf.
4371135446Strhodes	 */
4372135446Strhodes	if (ns_g_lwresdonly &&
4373135446Strhodes	    (lwresd_g_useresolvconf ||
4374135446Strhodes	     (!ns_g_conffileset && result == ISC_R_FILENOTFOUND)))
4375135446Strhodes	{
4376135446Strhodes		isc_log_write(ns_g_lctx,
4377135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4378135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
4379135446Strhodes			      lwresd_g_resolvconffile);
4380224092Sdougb		if (conf_parser != NULL)
4381224092Sdougb			cfg_parser_destroy(&conf_parser);
4382224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
4383224092Sdougb		result = ns_lwresd_parseeresolvconf(ns_g_mctx, conf_parser,
4384135446Strhodes						    &config);
4385135446Strhodes	}
4386135446Strhodes	CHECK(result);
4387135446Strhodes
4388135446Strhodes	/*
4389135446Strhodes	 * Check the validity of the configuration.
4390135446Strhodes	 */
4391135446Strhodes	CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx));
4392135446Strhodes
4393135446Strhodes	/*
4394135446Strhodes	 * Fill in the maps array, used for resolving defaults.
4395135446Strhodes	 */
4396135446Strhodes	i = 0;
4397135446Strhodes	options = NULL;
4398135446Strhodes	result = cfg_map_get(config, "options", &options);
4399135446Strhodes	if (result == ISC_R_SUCCESS)
4400135446Strhodes		maps[i++] = options;
4401135446Strhodes	maps[i++] = ns_g_defaults;
4402225361Sdougb	maps[i] = NULL;
4403135446Strhodes
4404135446Strhodes	/*
4405224092Sdougb	 * If bind.keys exists, load it.  If "dnssec-lookaside auto"
4406224092Sdougb	 * is turned on, the keys found there will be used as default
4407224092Sdougb	 * trust anchors.
4408224092Sdougb	 */
4409224092Sdougb	obj = NULL;
4410224092Sdougb	result = ns_config_get(maps, "bindkeys-file", &obj);
4411224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4412224092Sdougb	CHECKM(setstring(server, &server->bindkeysfile,
4413224092Sdougb	       cfg_obj_asstring(obj)), "strdup");
4414224092Sdougb
4415224092Sdougb	if (access(server->bindkeysfile, R_OK) == 0) {
4416224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4417224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4418224092Sdougb			      "reading built-in trusted "
4419224092Sdougb			      "keys from file '%s'", server->bindkeysfile);
4420224092Sdougb
4421224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx,
4422224092Sdougb					&bindkeys_parser));
4423224092Sdougb
4424224092Sdougb		result = cfg_parse_file(bindkeys_parser, server->bindkeysfile,
4425224092Sdougb					&cfg_type_bindkeys, &bindkeys);
4426224092Sdougb		CHECK(result);
4427224092Sdougb	}
4428224092Sdougb
4429234010Sdougb	/* Ensure exclusive access to configuration data. */
4430234010Sdougb	if (!exclusive) {
4431234010Sdougb		result = isc_task_beginexclusive(server->task);
4432234010Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
4433234010Sdougb		exclusive = ISC_TRUE;
4434234010Sdougb	}
4435234010Sdougb
4436224092Sdougb	/*
4437135446Strhodes	 * Set process limits, which (usually) needs to be done as root.
4438135446Strhodes	 */
4439135446Strhodes	set_limits(maps);
4440135446Strhodes
4441135446Strhodes	/*
4442186462Sdougb	 * Check if max number of open sockets that the system allows is
4443224092Sdougb	 * sufficiently large.	Failing this condition is not necessarily fatal,
4444186462Sdougb	 * but may cause subsequent runtime failures for a busy recursive
4445186462Sdougb	 * server.
4446182645Sdougb	 */
4447186462Sdougb	result = isc_socketmgr_getmaxsockets(ns_g_socketmgr, &maxsocks);
4448186462Sdougb	if (result != ISC_R_SUCCESS)
4449186462Sdougb		maxsocks = 0;
4450186462Sdougb	result = isc_resource_getcurlimit(isc_resource_openfiles, &nfiles);
4451186462Sdougb	if (result == ISC_R_SUCCESS && (isc_resourcevalue_t)maxsocks > nfiles) {
4452182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4453182645Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
4454186462Sdougb			      "max open files (%" ISC_PRINT_QUADFORMAT "u)"
4455186462Sdougb			      " is smaller than max sockets (%u)",
4456186462Sdougb			      nfiles, maxsocks);
4457186462Sdougb	}
4458182645Sdougb
4459182645Sdougb	/*
4460182645Sdougb	 * Set the number of socket reserved for TCP, stdio etc.
4461182645Sdougb	 */
4462182645Sdougb	obj = NULL;
4463182645Sdougb	result = ns_config_get(maps, "reserved-sockets", &obj);
4464182645Sdougb	INSIST(result == ISC_R_SUCCESS);
4465182645Sdougb	reserved = cfg_obj_asuint32(obj);
4466186462Sdougb	if (maxsocks != 0) {
4467186462Sdougb		if (maxsocks < 128U)			/* Prevent underflow. */
4468186462Sdougb			reserved = 0;
4469186462Sdougb		else if (reserved > maxsocks - 128U)	/* Minimum UDP space. */
4470186462Sdougb			reserved = maxsocks - 128;
4471186462Sdougb	}
4472186462Sdougb	/* Minimum TCP/stdio space. */
4473186462Sdougb	if (reserved < 128U)
4474182645Sdougb		reserved = 128;
4475186462Sdougb	if (reserved + 128U > maxsocks && maxsocks != 0) {
4476182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4477186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
4478182645Sdougb			      "less than 128 UDP sockets available after "
4479186462Sdougb			      "applying 'reserved-sockets' and 'maxsockets'");
4480182645Sdougb	}
4481182645Sdougb	isc__socketmgr_setreserved(ns_g_socketmgr, reserved);
4482186462Sdougb
4483182645Sdougb	/*
4484135446Strhodes	 * Configure various server options.
4485135446Strhodes	 */
4486135446Strhodes	configure_server_quota(maps, "transfers-out", &server->xfroutquota);
4487135446Strhodes	configure_server_quota(maps, "tcp-clients", &server->tcpquota);
4488135446Strhodes	configure_server_quota(maps, "recursive-clients",
4489135446Strhodes			       &server->recursionquota);
4490153816Sdougb	if (server->recursionquota.max > 1000)
4491153816Sdougb		isc_quota_soft(&server->recursionquota,
4492153816Sdougb			       server->recursionquota.max - 100);
4493153816Sdougb	else
4494153816Sdougb		isc_quota_soft(&server->recursionquota, 0);
4495135446Strhodes
4496225361Sdougb	CHECK(configure_view_acl(NULL, config, "blackhole", NULL,
4497225361Sdougb				 ns_g_aclconfctx, ns_g_mctx,
4498225361Sdougb				 &server->blackholeacl));
4499135446Strhodes	if (server->blackholeacl != NULL)
4500135446Strhodes		dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
4501135446Strhodes					     server->blackholeacl);
4502135446Strhodes
4503135446Strhodes	obj = NULL;
4504135446Strhodes	result = ns_config_get(maps, "match-mapped-addresses", &obj);
4505135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4506135446Strhodes	server->aclenv.match_mapped = cfg_obj_asboolean(obj);
4507135446Strhodes
4508225361Sdougb	CHECKM(ns_statschannels_configure(ns_g_server, config, ns_g_aclconfctx),
4509193149Sdougb	       "configuring statistics server(s)");
4510193149Sdougb
4511186462Sdougb	/*
4512186462Sdougb	 * Configure sets of UDP query source ports.
4513186462Sdougb	 */
4514186462Sdougb	CHECKM(isc_portset_create(ns_g_mctx, &v4portset),
4515186462Sdougb	       "creating UDP port set");
4516186462Sdougb	CHECKM(isc_portset_create(ns_g_mctx, &v6portset),
4517186462Sdougb	       "creating UDP port set");
4518135446Strhodes
4519186462Sdougb	usev4ports = NULL;
4520186462Sdougb	usev6ports = NULL;
4521186462Sdougb	avoidv4ports = NULL;
4522186462Sdougb	avoidv6ports = NULL;
4523186462Sdougb
4524186462Sdougb	(void)ns_config_get(maps, "use-v4-udp-ports", &usev4ports);
4525186462Sdougb	if (usev4ports != NULL)
4526186462Sdougb		portset_fromconf(v4portset, usev4ports, ISC_TRUE);
4527186462Sdougb	else {
4528186462Sdougb		CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low,
4529186462Sdougb					       &udpport_high),
4530186462Sdougb		       "get the default UDP/IPv4 port range");
4531186462Sdougb		if (udpport_low == udpport_high)
4532186462Sdougb			isc_portset_add(v4portset, udpport_low);
4533186462Sdougb		else {
4534186462Sdougb			isc_portset_addrange(v4portset, udpport_low,
4535186462Sdougb					     udpport_high);
4536186462Sdougb		}
4537186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4538186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4539186462Sdougb			      "using default UDP/IPv4 port range: [%d, %d]",
4540186462Sdougb			      udpport_low, udpport_high);
4541186462Sdougb	}
4542186462Sdougb	(void)ns_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports);
4543186462Sdougb	if (avoidv4ports != NULL)
4544186462Sdougb		portset_fromconf(v4portset, avoidv4ports, ISC_FALSE);
4545186462Sdougb
4546186462Sdougb	(void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports);
4547186462Sdougb	if (usev6ports != NULL)
4548186462Sdougb		portset_fromconf(v6portset, usev6ports, ISC_TRUE);
4549186462Sdougb	else {
4550186462Sdougb		CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low,
4551186462Sdougb					       &udpport_high),
4552186462Sdougb		       "get the default UDP/IPv6 port range");
4553186462Sdougb		if (udpport_low == udpport_high)
4554186462Sdougb			isc_portset_add(v6portset, udpport_low);
4555186462Sdougb		else {
4556186462Sdougb			isc_portset_addrange(v6portset, udpport_low,
4557186462Sdougb					     udpport_high);
4558186462Sdougb		}
4559186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4560186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4561186462Sdougb			      "using default UDP/IPv6 port range: [%d, %d]",
4562186462Sdougb			      udpport_low, udpport_high);
4563186462Sdougb	}
4564186462Sdougb	(void)ns_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports);
4565186462Sdougb	if (avoidv6ports != NULL)
4566186462Sdougb		portset_fromconf(v6portset, avoidv6ports, ISC_FALSE);
4567186462Sdougb
4568186462Sdougb	dns_dispatchmgr_setavailports(ns_g_dispatchmgr, v4portset, v6portset);
4569186462Sdougb
4570135446Strhodes	/*
4571135446Strhodes	 * Set the EDNS UDP size when we don't match a view.
4572135446Strhodes	 */
4573135446Strhodes	obj = NULL;
4574135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
4575135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4576135446Strhodes	udpsize = cfg_obj_asuint32(obj);
4577135446Strhodes	if (udpsize < 512)
4578135446Strhodes		udpsize = 512;
4579135446Strhodes	if (udpsize > 4096)
4580135446Strhodes		udpsize = 4096;
4581135446Strhodes	ns_g_udpsize = (isc_uint16_t)udpsize;
4582135446Strhodes
4583135446Strhodes	/*
4584135446Strhodes	 * Configure the zone manager.
4585135446Strhodes	 */
4586135446Strhodes	obj = NULL;
4587135446Strhodes	result = ns_config_get(maps, "transfers-in", &obj);
4588135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4589135446Strhodes	dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj));
4590135446Strhodes
4591135446Strhodes	obj = NULL;
4592135446Strhodes	result = ns_config_get(maps, "transfers-per-ns", &obj);
4593135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4594135446Strhodes	dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj));
4595135446Strhodes
4596135446Strhodes	obj = NULL;
4597135446Strhodes	result = ns_config_get(maps, "serial-query-rate", &obj);
4598135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4599135446Strhodes	dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj));
4600135446Strhodes
4601135446Strhodes	/*
4602135446Strhodes	 * Determine which port to use for listening for incoming connections.
4603135446Strhodes	 */
4604135446Strhodes	if (ns_g_port != 0)
4605135446Strhodes		listen_port = ns_g_port;
4606135446Strhodes	else
4607135446Strhodes		CHECKM(ns_config_getport(config, &listen_port), "port");
4608135446Strhodes
4609135446Strhodes	/*
4610135446Strhodes	 * Find the listen queue depth.
4611135446Strhodes	 */
4612135446Strhodes	obj = NULL;
4613135446Strhodes	result = ns_config_get(maps, "tcp-listen-queue", &obj);
4614135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4615135446Strhodes	ns_g_listen = cfg_obj_asuint32(obj);
4616135446Strhodes	if (ns_g_listen < 3)
4617135446Strhodes		ns_g_listen = 3;
4618135446Strhodes
4619135446Strhodes	/*
4620135446Strhodes	 * Configure the interface manager according to the "listen-on"
4621135446Strhodes	 * statement.
4622135446Strhodes	 */
4623135446Strhodes	{
4624165071Sdougb		const cfg_obj_t *clistenon = NULL;
4625135446Strhodes		ns_listenlist_t *listenon = NULL;
4626135446Strhodes
4627135446Strhodes		clistenon = NULL;
4628135446Strhodes		/*
4629135446Strhodes		 * Even though listen-on is present in the default
4630135446Strhodes		 * configuration, we can't use it here, since it isn't
4631135446Strhodes		 * used if we're in lwresd mode.  This way is easier.
4632135446Strhodes		 */
4633135446Strhodes		if (options != NULL)
4634135446Strhodes			(void)cfg_map_get(options, "listen-on", &clistenon);
4635135446Strhodes		if (clistenon != NULL) {
4636225361Sdougb			/* check return code? */
4637225361Sdougb			(void)ns_listenlist_fromconfig(clistenon, config,
4638225361Sdougb						       ns_g_aclconfctx,
4639225361Sdougb						       ns_g_mctx, &listenon);
4640135446Strhodes		} else if (!ns_g_lwresdonly) {
4641135446Strhodes			/*
4642135446Strhodes			 * Not specified, use default.
4643135446Strhodes			 */
4644135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
4645135446Strhodes						    ISC_TRUE, &listenon));
4646135446Strhodes		}
4647135446Strhodes		if (listenon != NULL) {
4648135446Strhodes			ns_interfacemgr_setlistenon4(server->interfacemgr,
4649135446Strhodes						     listenon);
4650135446Strhodes			ns_listenlist_detach(&listenon);
4651135446Strhodes		}
4652135446Strhodes	}
4653135446Strhodes	/*
4654135446Strhodes	 * Ditto for IPv6.
4655135446Strhodes	 */
4656135446Strhodes	{
4657165071Sdougb		const cfg_obj_t *clistenon = NULL;
4658135446Strhodes		ns_listenlist_t *listenon = NULL;
4659135446Strhodes
4660135446Strhodes		if (options != NULL)
4661135446Strhodes			(void)cfg_map_get(options, "listen-on-v6", &clistenon);
4662135446Strhodes		if (clistenon != NULL) {
4663225361Sdougb			/* check return code? */
4664225361Sdougb			(void)ns_listenlist_fromconfig(clistenon, config,
4665225361Sdougb						       ns_g_aclconfctx,
4666225361Sdougb						       ns_g_mctx, &listenon);
4667135446Strhodes		} else if (!ns_g_lwresdonly) {
4668193149Sdougb			isc_boolean_t enable;
4669135446Strhodes			/*
4670135446Strhodes			 * Not specified, use default.
4671135446Strhodes			 */
4672193149Sdougb			enable = ISC_TF(isc_net_probeipv4() != ISC_R_SUCCESS);
4673135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
4674193149Sdougb						    enable, &listenon));
4675135446Strhodes		}
4676135446Strhodes		if (listenon != NULL) {
4677135446Strhodes			ns_interfacemgr_setlistenon6(server->interfacemgr,
4678135446Strhodes						     listenon);
4679135446Strhodes			ns_listenlist_detach(&listenon);
4680135446Strhodes		}
4681135446Strhodes	}
4682135446Strhodes
4683135446Strhodes	/*
4684135446Strhodes	 * Rescan the interface list to pick up changes in the
4685135446Strhodes	 * listen-on option.  It's important that we do this before we try
4686135446Strhodes	 * to configure the query source, since the dispatcher we use might
4687135446Strhodes	 * be shared with an interface.
4688135446Strhodes	 */
4689135446Strhodes	scan_interfaces(server, ISC_TRUE);
4690135446Strhodes
4691135446Strhodes	/*
4692135446Strhodes	 * Arrange for further interface scanning to occur periodically
4693135446Strhodes	 * as specified by the "interface-interval" option.
4694135446Strhodes	 */
4695135446Strhodes	obj = NULL;
4696135446Strhodes	result = ns_config_get(maps, "interface-interval", &obj);
4697135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4698135446Strhodes	interface_interval = cfg_obj_asuint32(obj) * 60;
4699135446Strhodes	if (interface_interval == 0) {
4700135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
4701135446Strhodes				      isc_timertype_inactive,
4702135446Strhodes				      NULL, NULL, ISC_TRUE));
4703135446Strhodes	} else if (server->interface_interval != interface_interval) {
4704135446Strhodes		isc_interval_set(&interval, interface_interval, 0);
4705135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
4706135446Strhodes				      isc_timertype_ticker,
4707135446Strhodes				      NULL, &interval, ISC_FALSE));
4708135446Strhodes	}
4709135446Strhodes	server->interface_interval = interface_interval;
4710135446Strhodes
4711135446Strhodes	/*
4712135446Strhodes	 * Configure the dialup heartbeat timer.
4713135446Strhodes	 */
4714135446Strhodes	obj = NULL;
4715135446Strhodes	result = ns_config_get(maps, "heartbeat-interval", &obj);
4716135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4717135446Strhodes	heartbeat_interval = cfg_obj_asuint32(obj) * 60;
4718135446Strhodes	if (heartbeat_interval == 0) {
4719135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
4720135446Strhodes				      isc_timertype_inactive,
4721135446Strhodes				      NULL, NULL, ISC_TRUE));
4722135446Strhodes	} else if (server->heartbeat_interval != heartbeat_interval) {
4723135446Strhodes		isc_interval_set(&interval, heartbeat_interval, 0);
4724135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
4725135446Strhodes				      isc_timertype_ticker,
4726135446Strhodes				      NULL, &interval, ISC_FALSE));
4727135446Strhodes	}
4728135446Strhodes	server->heartbeat_interval = heartbeat_interval;
4729186462Sdougb
4730170222Sdougb	isc_interval_set(&interval, 1200, 0);
4731170222Sdougb	CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
4732170222Sdougb			      &interval, ISC_FALSE));
4733135446Strhodes
4734135446Strhodes	/*
4735224092Sdougb	 * Write the PID file.
4736224092Sdougb	 */
4737224092Sdougb	obj = NULL;
4738224092Sdougb	if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS)
4739224092Sdougb		if (cfg_obj_isvoid(obj))
4740224092Sdougb			ns_os_writepidfile(NULL, first_time);
4741224092Sdougb		else
4742224092Sdougb			ns_os_writepidfile(cfg_obj_asstring(obj), first_time);
4743224092Sdougb	else if (ns_g_lwresdonly)
4744224092Sdougb		ns_os_writepidfile(lwresd_g_defaultpidfile, first_time);
4745224092Sdougb	else
4746224092Sdougb		ns_os_writepidfile(ns_g_defaultpidfile, first_time);
4747224092Sdougb
4748224092Sdougb	/*
4749224092Sdougb	 * Configure the server-wide session key.  This must be done before
4750224092Sdougb	 * configure views because zone configuration may need to know
4751224092Sdougb	 * session-keyname.
4752224092Sdougb	 *
4753224092Sdougb	 * Failure of session key generation isn't fatal at this time; if it
4754224092Sdougb	 * turns out that a session key is really needed but doesn't exist,
4755224092Sdougb	 * we'll treat it as a fatal error then.
4756224092Sdougb	 */
4757224092Sdougb	(void)configure_session_key(maps, server, ns_g_mctx);
4758224092Sdougb
4759225361Sdougb	views = NULL;
4760225361Sdougb	(void)cfg_map_get(config, "view", &views);
4761225361Sdougb
4762224092Sdougb	/*
4763225361Sdougb	 * Create the views and count all the configured zones in
4764225361Sdougb	 * order to correctly size the zone manager's task table.
4765225361Sdougb	 * (We only count zones for configured views; the built-in
4766225361Sdougb	 * "bind" view can be ignored as it only adds a negligible
4767225361Sdougb	 * number of zones.)
4768225361Sdougb	 *
4769225361Sdougb	 * If we're allowing new zones, we need to be able to find the
4770225361Sdougb	 * new zone file and count those as well.  So we setup the new
4771225361Sdougb	 * zone configuration context, but otherwise view configuration
4772225361Sdougb	 * waits until after the zone manager's task list has been sized.
4773225361Sdougb	 */
4774225361Sdougb	for (element = cfg_list_first(views);
4775225361Sdougb	     element != NULL;
4776225361Sdougb	     element = cfg_list_next(element))
4777225361Sdougb	{
4778225361Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
4779225361Sdougb		const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options");
4780225361Sdougb		view = NULL;
4781225361Sdougb
4782225361Sdougb		CHECK(create_view(vconfig, &viewlist, &view));
4783225361Sdougb		INSIST(view != NULL);
4784225361Sdougb
4785225361Sdougb		num_zones += count_zones(voptions);
4786225361Sdougb		CHECK(setup_newzones(view, config, vconfig, conf_parser,
4787225361Sdougb				     ns_g_aclconfctx));
4788225361Sdougb
4789225361Sdougb		nzctx = view->new_zone_config;
4790225361Sdougb		if (nzctx != NULL && nzctx->nzconfig != NULL)
4791225361Sdougb			num_zones += count_zones(nzctx->nzconfig);
4792225361Sdougb
4793225361Sdougb		dns_view_detach(&view);
4794225361Sdougb	}
4795225361Sdougb
4796225361Sdougb	/*
4797225361Sdougb	 * If there were no explicit views then we do the default
4798225361Sdougb	 * view here.
4799225361Sdougb	 */
4800225361Sdougb	if (views == NULL) {
4801225361Sdougb		CHECK(create_view(NULL, &viewlist, &view));
4802225361Sdougb		INSIST(view != NULL);
4803225361Sdougb
4804225361Sdougb		num_zones = count_zones(config);
4805225361Sdougb
4806225361Sdougb		CHECK(setup_newzones(view, config, NULL,  conf_parser,
4807225361Sdougb				     ns_g_aclconfctx));
4808225361Sdougb
4809225361Sdougb		nzctx = view->new_zone_config;
4810225361Sdougb		if (nzctx != NULL && nzctx->nzconfig != NULL)
4811225361Sdougb			num_zones += count_zones(nzctx->nzconfig);
4812225361Sdougb
4813225361Sdougb		dns_view_detach(&view);
4814225361Sdougb	}
4815225361Sdougb
4816225361Sdougb	/*
4817225361Sdougb	 * Zones have been counted; set the zone manager task pool size.
4818225361Sdougb	 */
4819225361Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4820225361Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4821225361Sdougb		      "sizing zone task pool based on %d zones", num_zones);
4822225361Sdougb	CHECK(dns_zonemgr_setsize(ns_g_server->zonemgr, num_zones));
4823225361Sdougb
4824225361Sdougb	/*
4825135446Strhodes	 * Configure and freeze all explicit views.  Explicit
4826135446Strhodes	 * views that have zones were already created at parsing
4827135446Strhodes	 * time, but views with no zones must be created here.
4828135446Strhodes	 */
4829135446Strhodes	for (element = cfg_list_first(views);
4830135446Strhodes	     element != NULL;
4831135446Strhodes	     element = cfg_list_next(element))
4832135446Strhodes	{
4833224092Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
4834225361Sdougb
4835135446Strhodes		view = NULL;
4836225361Sdougb		CHECK(find_view(vconfig, &viewlist, &view));
4837225361Sdougb		CHECK(configure_view(view, config, vconfig,
4838225361Sdougb				     &cachelist, bindkeys, ns_g_mctx,
4839225361Sdougb				     ns_g_aclconfctx, ISC_TRUE));
4840135446Strhodes		dns_view_freeze(view);
4841135446Strhodes		dns_view_detach(&view);
4842135446Strhodes	}
4843135446Strhodes
4844135446Strhodes	/*
4845135446Strhodes	 * Make sure we have a default view if and only if there
4846135446Strhodes	 * were no explicit views.
4847135446Strhodes	 */
4848135446Strhodes	if (views == NULL) {
4849225361Sdougb		view = NULL;
4850225361Sdougb		CHECK(find_view(NULL, &viewlist, &view));
4851225361Sdougb		CHECK(configure_view(view, config, NULL,
4852224092Sdougb				     &cachelist, bindkeys,
4853225361Sdougb				     ns_g_mctx, ns_g_aclconfctx, ISC_TRUE));
4854135446Strhodes		dns_view_freeze(view);
4855135446Strhodes		dns_view_detach(&view);
4856135446Strhodes	}
4857135446Strhodes
4858135446Strhodes	/*
4859224092Sdougb	 * Create (or recreate) the built-in views.
4860135446Strhodes	 */
4861135446Strhodes	builtin_views = NULL;
4862135446Strhodes	RUNTIME_CHECK(cfg_map_get(ns_g_config, "view",
4863135446Strhodes				  &builtin_views) == ISC_R_SUCCESS);
4864135446Strhodes	for (element = cfg_list_first(builtin_views);
4865135446Strhodes	     element != NULL;
4866135446Strhodes	     element = cfg_list_next(element))
4867135446Strhodes	{
4868224092Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
4869224092Sdougb
4870224092Sdougb		CHECK(create_view(vconfig, &builtin_viewlist, &view));
4871225361Sdougb		CHECK(configure_view(view, config, vconfig,
4872224092Sdougb				     &cachelist, bindkeys,
4873225361Sdougb				     ns_g_mctx, ns_g_aclconfctx, ISC_FALSE));
4874135446Strhodes		dns_view_freeze(view);
4875135446Strhodes		dns_view_detach(&view);
4876135446Strhodes		view = NULL;
4877135446Strhodes	}
4878135446Strhodes
4879224092Sdougb	/* Now combine the two viewlists into one */
4880224092Sdougb	ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link);
4881224092Sdougb
4882224092Sdougb	/* Swap our new view list with the production one. */
4883135446Strhodes	tmpviewlist = server->viewlist;
4884135446Strhodes	server->viewlist = viewlist;
4885135446Strhodes	viewlist = tmpviewlist;
4886135446Strhodes
4887224092Sdougb	/* Make the view list available to each of the views */
4888224092Sdougb	view = ISC_LIST_HEAD(server->viewlist);
4889224092Sdougb	while (view != NULL) {
4890224092Sdougb		view->viewlist = &server->viewlist;
4891224092Sdougb		view = ISC_LIST_NEXT(view, link);
4892224092Sdougb	}
4893224092Sdougb
4894224092Sdougb	/* Swap our new cache list with the production one. */
4895224092Sdougb	tmpcachelist = server->cachelist;
4896224092Sdougb	server->cachelist = cachelist;
4897224092Sdougb	cachelist = tmpcachelist;
4898224092Sdougb
4899224092Sdougb	/* Load the TKEY information from the configuration. */
4900135446Strhodes	if (options != NULL) {
4901135446Strhodes		dns_tkeyctx_t *t = NULL;
4902135446Strhodes		CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy,
4903135446Strhodes					     &t),
4904135446Strhodes		       "configuring TKEY");
4905135446Strhodes		if (server->tkeyctx != NULL)
4906135446Strhodes			dns_tkeyctx_destroy(&server->tkeyctx);
4907135446Strhodes		server->tkeyctx = t;
4908135446Strhodes	}
4909135446Strhodes
4910135446Strhodes	/*
4911135446Strhodes	 * Bind the control port(s).
4912135446Strhodes	 */
4913135446Strhodes	CHECKM(ns_controls_configure(ns_g_server->controls, config,
4914225361Sdougb				     ns_g_aclconfctx),
4915135446Strhodes	       "binding control channel(s)");
4916135446Strhodes
4917135446Strhodes	/*
4918135446Strhodes	 * Bind the lwresd port(s).
4919135446Strhodes	 */
4920135446Strhodes	CHECKM(ns_lwresd_configure(ns_g_mctx, config),
4921135446Strhodes	       "binding lightweight resolver ports");
4922135446Strhodes
4923135446Strhodes	/*
4924135446Strhodes	 * Open the source of entropy.
4925135446Strhodes	 */
4926135446Strhodes	if (first_time) {
4927135446Strhodes		obj = NULL;
4928135446Strhodes		result = ns_config_get(maps, "random-device", &obj);
4929135446Strhodes		if (result != ISC_R_SUCCESS) {
4930135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4931135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4932135446Strhodes				      "no source of entropy found");
4933135446Strhodes		} else {
4934135446Strhodes			const char *randomdev = cfg_obj_asstring(obj);
4935135446Strhodes			result = isc_entropy_createfilesource(ns_g_entropy,
4936135446Strhodes							      randomdev);
4937135446Strhodes			if (result != ISC_R_SUCCESS)
4938135446Strhodes				isc_log_write(ns_g_lctx,
4939135446Strhodes					      NS_LOGCATEGORY_GENERAL,
4940135446Strhodes					      NS_LOGMODULE_SERVER,
4941135446Strhodes					      ISC_LOG_INFO,
4942135446Strhodes					      "could not open entropy source "
4943135446Strhodes					      "%s: %s",
4944135446Strhodes					      randomdev,
4945135446Strhodes					      isc_result_totext(result));
4946135446Strhodes#ifdef PATH_RANDOMDEV
4947135446Strhodes			if (ns_g_fallbackentropy != NULL) {
4948135446Strhodes				if (result != ISC_R_SUCCESS) {
4949135446Strhodes					isc_log_write(ns_g_lctx,
4950135446Strhodes						      NS_LOGCATEGORY_GENERAL,
4951135446Strhodes						      NS_LOGMODULE_SERVER,
4952135446Strhodes						      ISC_LOG_INFO,
4953135446Strhodes						      "using pre-chroot entropy source "
4954135446Strhodes						      "%s",
4955135446Strhodes						      PATH_RANDOMDEV);
4956135446Strhodes					isc_entropy_detach(&ns_g_entropy);
4957135446Strhodes					isc_entropy_attach(ns_g_fallbackentropy,
4958135446Strhodes							   &ns_g_entropy);
4959135446Strhodes				}
4960135446Strhodes				isc_entropy_detach(&ns_g_fallbackentropy);
4961135446Strhodes			}
4962135446Strhodes#endif
4963135446Strhodes		}
4964135446Strhodes	}
4965135446Strhodes
4966135446Strhodes	/*
4967135446Strhodes	 * Relinquish root privileges.
4968135446Strhodes	 */
4969135446Strhodes	if (first_time)
4970135446Strhodes		ns_os_changeuser();
4971135446Strhodes
4972135446Strhodes	/*
4973186462Sdougb	 * Check that the working directory is writable.
4974186462Sdougb	 */
4975186462Sdougb	if (access(".", W_OK) != 0) {
4976186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4977186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4978186462Sdougb			      "the working directory is not writable");
4979186462Sdougb	}
4980186462Sdougb
4981186462Sdougb	/*
4982135446Strhodes	 * Configure the logging system.
4983135446Strhodes	 *
4984135446Strhodes	 * Do this after changing UID to make sure that any log
4985135446Strhodes	 * files specified in named.conf get created by the
4986135446Strhodes	 * unprivileged user, not root.
4987135446Strhodes	 */
4988135446Strhodes	if (ns_g_logstderr) {
4989135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4990135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4991135446Strhodes			      "ignoring config file logging "
4992135446Strhodes			      "statement due to -g option");
4993135446Strhodes	} else {
4994165071Sdougb		const cfg_obj_t *logobj = NULL;
4995135446Strhodes		isc_logconfig_t *logc = NULL;
4996135446Strhodes
4997135446Strhodes		CHECKM(isc_logconfig_create(ns_g_lctx, &logc),
4998135446Strhodes		       "creating new logging configuration");
4999135446Strhodes
5000135446Strhodes		logobj = NULL;
5001135446Strhodes		(void)cfg_map_get(config, "logging", &logobj);
5002135446Strhodes		if (logobj != NULL) {
5003135446Strhodes			CHECKM(ns_log_configure(logc, logobj),
5004135446Strhodes			       "configuring logging");
5005135446Strhodes		} else {
5006135446Strhodes			CHECKM(ns_log_setdefaultchannels(logc),
5007135446Strhodes			       "setting up default logging channels");
5008135446Strhodes			CHECKM(ns_log_setunmatchedcategory(logc),
5009135446Strhodes			       "setting up default 'category unmatched'");
5010135446Strhodes			CHECKM(ns_log_setdefaultcategory(logc),
5011135446Strhodes			       "setting up default 'category default'");
5012135446Strhodes		}
5013135446Strhodes
5014135446Strhodes		result = isc_logconfig_use(ns_g_lctx, logc);
5015135446Strhodes		if (result != ISC_R_SUCCESS) {
5016135446Strhodes			isc_logconfig_destroy(&logc);
5017135446Strhodes			CHECKM(result, "installing logging configuration");
5018135446Strhodes		}
5019135446Strhodes
5020135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5021135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
5022135446Strhodes			      "now using logging configuration from "
5023135446Strhodes			      "config file");
5024135446Strhodes	}
5025135446Strhodes
5026135446Strhodes	/*
5027135446Strhodes	 * Set the default value of the query logging flag depending
5028135446Strhodes	 * whether a "queries" category has been defined.  This is
5029135446Strhodes	 * a disgusting hack, but we need to do this for BIND 8
5030135446Strhodes	 * compatibility.
5031135446Strhodes	 */
5032135446Strhodes	if (first_time) {
5033165071Sdougb		const cfg_obj_t *logobj = NULL;
5034165071Sdougb		const cfg_obj_t *categories = NULL;
5035135446Strhodes
5036135446Strhodes		obj = NULL;
5037135446Strhodes		if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) {
5038135446Strhodes			server->log_queries = cfg_obj_asboolean(obj);
5039135446Strhodes		} else {
5040135446Strhodes
5041135446Strhodes			(void)cfg_map_get(config, "logging", &logobj);
5042135446Strhodes			if (logobj != NULL)
5043135446Strhodes				(void)cfg_map_get(logobj, "category",
5044135446Strhodes						  &categories);
5045135446Strhodes			if (categories != NULL) {
5046165071Sdougb				const cfg_listelt_t *element;
5047135446Strhodes				for (element = cfg_list_first(categories);
5048135446Strhodes				     element != NULL;
5049135446Strhodes				     element = cfg_list_next(element))
5050135446Strhodes				{
5051165071Sdougb					const cfg_obj_t *catobj;
5052165071Sdougb					const char *str;
5053135446Strhodes
5054135446Strhodes					obj = cfg_listelt_value(element);
5055135446Strhodes					catobj = cfg_tuple_get(obj, "name");
5056135446Strhodes					str = cfg_obj_asstring(catobj);
5057135446Strhodes					if (strcasecmp(str, "queries") == 0)
5058135446Strhodes						server->log_queries = ISC_TRUE;
5059135446Strhodes				}
5060135446Strhodes			}
5061135446Strhodes		}
5062135446Strhodes	}
5063135446Strhodes
5064186462Sdougb
5065135446Strhodes	obj = NULL;
5066135446Strhodes	if (options != NULL &&
5067193149Sdougb	    cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS)
5068193149Sdougb		ns_g_memstatistics = cfg_obj_asboolean(obj);
5069193149Sdougb	else
5070193149Sdougb		ns_g_memstatistics =
5071193149Sdougb			ISC_TF((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0);
5072193149Sdougb
5073193149Sdougb	obj = NULL;
5074193149Sdougb	if (ns_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS)
5075135446Strhodes		ns_main_setmemstats(cfg_obj_asstring(obj));
5076193149Sdougb	else if (ns_g_memstatistics)
5077193149Sdougb		ns_main_setmemstats("named.memstats");
5078135446Strhodes	else
5079135446Strhodes		ns_main_setmemstats(NULL);
5080135446Strhodes
5081135446Strhodes	obj = NULL;
5082135446Strhodes	result = ns_config_get(maps, "statistics-file", &obj);
5083135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5084135446Strhodes	CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)),
5085135446Strhodes	       "strdup");
5086135446Strhodes
5087135446Strhodes	obj = NULL;
5088135446Strhodes	result = ns_config_get(maps, "dump-file", &obj);
5089135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5090135446Strhodes	CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)),
5091135446Strhodes	       "strdup");
5092135446Strhodes
5093135446Strhodes	obj = NULL;
5094224092Sdougb	result = ns_config_get(maps, "secroots-file", &obj);
5095224092Sdougb	INSIST(result == ISC_R_SUCCESS);
5096224092Sdougb	CHECKM(setstring(server, &server->secrootsfile, cfg_obj_asstring(obj)),
5097224092Sdougb	       "strdup");
5098224092Sdougb
5099224092Sdougb	obj = NULL;
5100135446Strhodes	result = ns_config_get(maps, "recursing-file", &obj);
5101135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5102135446Strhodes	CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)),
5103135446Strhodes	       "strdup");
5104135446Strhodes
5105135446Strhodes	obj = NULL;
5106135446Strhodes	result = ns_config_get(maps, "version", &obj);
5107135446Strhodes	if (result == ISC_R_SUCCESS) {
5108135446Strhodes		CHECKM(setoptstring(server, &server->version, obj), "strdup");
5109135446Strhodes		server->version_set = ISC_TRUE;
5110135446Strhodes	} else {
5111135446Strhodes		server->version_set = ISC_FALSE;
5112135446Strhodes	}
5113135446Strhodes
5114135446Strhodes	obj = NULL;
5115135446Strhodes	result = ns_config_get(maps, "hostname", &obj);
5116135446Strhodes	if (result == ISC_R_SUCCESS) {
5117135446Strhodes		CHECKM(setoptstring(server, &server->hostname, obj), "strdup");
5118135446Strhodes		server->hostname_set = ISC_TRUE;
5119135446Strhodes	} else {
5120135446Strhodes		server->hostname_set = ISC_FALSE;
5121135446Strhodes	}
5122135446Strhodes
5123135446Strhodes	obj = NULL;
5124135446Strhodes	result = ns_config_get(maps, "server-id", &obj);
5125135446Strhodes	server->server_usehostname = ISC_FALSE;
5126135446Strhodes	if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
5127193149Sdougb		/* The parser translates "hostname" to ISC_TRUE */
5128193149Sdougb		server->server_usehostname = cfg_obj_asboolean(obj);
5129193149Sdougb		result = setstring(server, &server->server_id, NULL);
5130193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5131135446Strhodes	} else if (result == ISC_R_SUCCESS) {
5132193149Sdougb		/* Found a quoted string */
5133135446Strhodes		CHECKM(setoptstring(server, &server->server_id, obj), "strdup");
5134135446Strhodes	} else {
5135170222Sdougb		result = setstring(server, &server->server_id, NULL);
5136135446Strhodes		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5137135446Strhodes	}
5138135446Strhodes
5139135446Strhodes	obj = NULL;
5140135446Strhodes	result = ns_config_get(maps, "flush-zones-on-shutdown", &obj);
5141135446Strhodes	if (result == ISC_R_SUCCESS) {
5142135446Strhodes		server->flushonshutdown = cfg_obj_asboolean(obj);
5143135446Strhodes	} else {
5144135446Strhodes		server->flushonshutdown = ISC_FALSE;
5145135446Strhodes	}
5146135446Strhodes
5147135446Strhodes	result = ISC_R_SUCCESS;
5148135446Strhodes
5149135446Strhodes cleanup:
5150186462Sdougb	if (v4portset != NULL)
5151186462Sdougb		isc_portset_destroy(ns_g_mctx, &v4portset);
5152186462Sdougb
5153186462Sdougb	if (v6portset != NULL)
5154186462Sdougb		isc_portset_destroy(ns_g_mctx, &v6portset);
5155186462Sdougb
5156224092Sdougb	if (conf_parser != NULL) {
5157135446Strhodes		if (config != NULL)
5158224092Sdougb			cfg_obj_destroy(conf_parser, &config);
5159224092Sdougb		cfg_parser_destroy(&conf_parser);
5160135446Strhodes	}
5161135446Strhodes
5162224092Sdougb	if (bindkeys_parser != NULL) {
5163224092Sdougb		if (bindkeys  != NULL)
5164224092Sdougb			cfg_obj_destroy(bindkeys_parser, &bindkeys);
5165224092Sdougb		cfg_parser_destroy(&bindkeys_parser);
5166224092Sdougb	}
5167224092Sdougb
5168135446Strhodes	if (view != NULL)
5169135446Strhodes		dns_view_detach(&view);
5170135446Strhodes
5171135446Strhodes	/*
5172135446Strhodes	 * This cleans up either the old production view list
5173135446Strhodes	 * or our temporary list depending on whether they
5174135446Strhodes	 * were swapped above or not.
5175135446Strhodes	 */
5176135446Strhodes	for (view = ISC_LIST_HEAD(viewlist);
5177135446Strhodes	     view != NULL;
5178135446Strhodes	     view = view_next) {
5179135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
5180135446Strhodes		ISC_LIST_UNLINK(viewlist, view, link);
5181170222Sdougb		if (result == ISC_R_SUCCESS &&
5182170222Sdougb		    strcmp(view->name, "_bind") != 0)
5183170222Sdougb			(void)dns_zt_apply(view->zonetable, ISC_FALSE,
5184170222Sdougb					   removed, view);
5185135446Strhodes		dns_view_detach(&view);
5186135446Strhodes	}
5187135446Strhodes
5188224092Sdougb	/* Same cleanup for cache list. */
5189224092Sdougb	while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) {
5190224092Sdougb		ISC_LIST_UNLINK(cachelist, nsc, link);
5191224092Sdougb		dns_cache_detach(&nsc->cache);
5192224092Sdougb		isc_mem_put(server->mctx, nsc, sizeof(*nsc));
5193224092Sdougb	}
5194224092Sdougb
5195135446Strhodes	/*
5196135446Strhodes	 * Adjust the listening interfaces in accordance with the source
5197135446Strhodes	 * addresses specified in views and zones.
5198135446Strhodes	 */
5199135446Strhodes	if (isc_net_probeipv6() == ISC_R_SUCCESS)
5200135446Strhodes		adjust_interfaces(server, ns_g_mctx);
5201135446Strhodes
5202135446Strhodes	/* Relinquish exclusive access to configuration data. */
5203234010Sdougb	if (exclusive)
5204234010Sdougb		isc_task_endexclusive(server->task);
5205135446Strhodes
5206135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5207135446Strhodes		      ISC_LOG_DEBUG(1), "load_configuration: %s",
5208135446Strhodes		      isc_result_totext(result));
5209135446Strhodes
5210135446Strhodes	return (result);
5211135446Strhodes}
5212135446Strhodes
5213135446Strhodesstatic isc_result_t
5214135446Strhodesload_zones(ns_server_t *server, isc_boolean_t stop) {
5215135446Strhodes	isc_result_t result;
5216135446Strhodes	dns_view_t *view;
5217135446Strhodes
5218135446Strhodes	result = isc_task_beginexclusive(server->task);
5219135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5220135446Strhodes
5221135446Strhodes	/*
5222135446Strhodes	 * Load zone data from disk.
5223135446Strhodes	 */
5224135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5225135446Strhodes	     view != NULL;
5226135446Strhodes	     view = ISC_LIST_NEXT(view, link))
5227135446Strhodes	{
5228135446Strhodes		CHECK(dns_view_load(view, stop));
5229224092Sdougb		if (view->managed_keys != NULL)
5230224092Sdougb			CHECK(dns_zone_load(view->managed_keys));
5231135446Strhodes	}
5232135446Strhodes
5233135446Strhodes	/*
5234135446Strhodes	 * Force zone maintenance.  Do this after loading
5235135446Strhodes	 * so that we know when we need to force AXFR of
5236135446Strhodes	 * slave zones whose master files are missing.
5237135446Strhodes	 */
5238135446Strhodes	CHECK(dns_zonemgr_forcemaint(server->zonemgr));
5239135446Strhodes cleanup:
5240186462Sdougb	isc_task_endexclusive(server->task);
5241135446Strhodes	return (result);
5242135446Strhodes}
5243135446Strhodes
5244135446Strhodesstatic isc_result_t
5245135446Strhodesload_new_zones(ns_server_t *server, isc_boolean_t stop) {
5246135446Strhodes	isc_result_t result;
5247135446Strhodes	dns_view_t *view;
5248135446Strhodes
5249135446Strhodes	result = isc_task_beginexclusive(server->task);
5250135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5251135446Strhodes
5252135446Strhodes	/*
5253135446Strhodes	 * Load zone data from disk.
5254135446Strhodes	 */
5255135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5256135446Strhodes	     view != NULL;
5257135446Strhodes	     view = ISC_LIST_NEXT(view, link))
5258135446Strhodes	{
5259135446Strhodes		CHECK(dns_view_loadnew(view, stop));
5260224092Sdougb
5261224092Sdougb		/* Load managed-keys data */
5262224092Sdougb		if (view->managed_keys != NULL)
5263224092Sdougb			CHECK(dns_zone_loadnew(view->managed_keys));
5264135446Strhodes	}
5265224092Sdougb
5266135446Strhodes	/*
5267224092Sdougb	 * Resume zone XFRs.
5268135446Strhodes	 */
5269135446Strhodes	dns_zonemgr_resumexfrs(server->zonemgr);
5270135446Strhodes cleanup:
5271186462Sdougb	isc_task_endexclusive(server->task);
5272135446Strhodes	return (result);
5273135446Strhodes}
5274135446Strhodes
5275135446Strhodesstatic void
5276135446Strhodesrun_server(isc_task_t *task, isc_event_t *event) {
5277135446Strhodes	isc_result_t result;
5278135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
5279135446Strhodes
5280143731Sdougb	INSIST(task == server->task);
5281135446Strhodes
5282135446Strhodes	isc_event_free(&event);
5283135446Strhodes
5284135446Strhodes	CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy,
5285135446Strhodes					  &ns_g_dispatchmgr),
5286135446Strhodes		   "creating dispatch manager");
5287135446Strhodes
5288193149Sdougb	dns_dispatchmgr_setstats(ns_g_dispatchmgr, server->resolverstats);
5289193149Sdougb
5290135446Strhodes	CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr,
5291135446Strhodes					  ns_g_socketmgr, ns_g_dispatchmgr,
5292135446Strhodes					  &server->interfacemgr),
5293135446Strhodes		   "creating interface manager");
5294135446Strhodes
5295135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5296135446Strhodes				    NULL, NULL, server->task,
5297135446Strhodes				    interface_timer_tick,
5298135446Strhodes				    server, &server->interface_timer),
5299135446Strhodes		   "creating interface timer");
5300135446Strhodes
5301135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5302135446Strhodes				    NULL, NULL, server->task,
5303135446Strhodes				    heartbeat_timer_tick,
5304135446Strhodes				    server, &server->heartbeat_timer),
5305135446Strhodes		   "creating heartbeat timer");
5306135446Strhodes
5307170222Sdougb	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5308170222Sdougb				    NULL, NULL, server->task, pps_timer_tick,
5309170222Sdougb				    server, &server->pps_timer),
5310170222Sdougb		   "creating pps timer");
5311170222Sdougb
5312135446Strhodes	CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser),
5313135446Strhodes		   "creating default configuration parser");
5314135446Strhodes
5315135446Strhodes	if (ns_g_lwresdonly)
5316135446Strhodes		CHECKFATAL(load_configuration(lwresd_g_conffile, server,
5317135446Strhodes					      ISC_TRUE),
5318135446Strhodes			   "loading configuration");
5319135446Strhodes	else
5320135446Strhodes		CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE),
5321135446Strhodes			   "loading configuration");
5322135446Strhodes
5323135446Strhodes	isc_hash_init();
5324135446Strhodes
5325143731Sdougb	CHECKFATAL(load_zones(server, ISC_FALSE), "loading zones");
5326135446Strhodes
5327143731Sdougb	ns_os_started();
5328135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5329143731Sdougb		      ISC_LOG_NOTICE, "running");
5330135446Strhodes}
5331135446Strhodes
5332186462Sdougbvoid
5333135446Strhodesns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) {
5334135446Strhodes
5335135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5336135446Strhodes
5337135446Strhodes	server->flushonshutdown = flush;
5338135446Strhodes}
5339135446Strhodes
5340135446Strhodesstatic void
5341135446Strhodesshutdown_server(isc_task_t *task, isc_event_t *event) {
5342135446Strhodes	isc_result_t result;
5343135446Strhodes	dns_view_t *view, *view_next;
5344135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
5345135446Strhodes	isc_boolean_t flush = server->flushonshutdown;
5346224092Sdougb	ns_cache_t *nsc;
5347135446Strhodes
5348135446Strhodes	UNUSED(task);
5349135446Strhodes	INSIST(task == server->task);
5350135446Strhodes
5351135446Strhodes	result = isc_task_beginexclusive(server->task);
5352135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5353135446Strhodes
5354135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5355135446Strhodes		      ISC_LOG_INFO, "shutting down%s",
5356135446Strhodes		      flush ? ": flushing changes" : "");
5357135446Strhodes
5358193149Sdougb	ns_statschannels_shutdown(server);
5359135446Strhodes	ns_controls_shutdown(server->controls);
5360135446Strhodes	end_reserved_dispatches(server, ISC_TRUE);
5361224092Sdougb	cleanup_session_key(server, server->mctx);
5362135446Strhodes
5363225361Sdougb	if (ns_g_aclconfctx != NULL)
5364225361Sdougb		cfg_aclconfctx_detach(&ns_g_aclconfctx);
5365225361Sdougb
5366135446Strhodes	cfg_obj_destroy(ns_g_parser, &ns_g_config);
5367135446Strhodes	cfg_parser_destroy(&ns_g_parser);
5368135446Strhodes
5369135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5370135446Strhodes	     view != NULL;
5371135446Strhodes	     view = view_next) {
5372135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
5373135446Strhodes		ISC_LIST_UNLINK(server->viewlist, view, link);
5374135446Strhodes		if (flush)
5375135446Strhodes			dns_view_flushanddetach(&view);
5376135446Strhodes		else
5377135446Strhodes			dns_view_detach(&view);
5378135446Strhodes	}
5379135446Strhodes
5380224092Sdougb	while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) {
5381224092Sdougb		ISC_LIST_UNLINK(server->cachelist, nsc, link);
5382224092Sdougb		dns_cache_detach(&nsc->cache);
5383224092Sdougb		isc_mem_put(server->mctx, nsc, sizeof(*nsc));
5384224092Sdougb	}
5385224092Sdougb
5386135446Strhodes	isc_timer_detach(&server->interface_timer);
5387135446Strhodes	isc_timer_detach(&server->heartbeat_timer);
5388170222Sdougb	isc_timer_detach(&server->pps_timer);
5389135446Strhodes
5390135446Strhodes	ns_interfacemgr_shutdown(server->interfacemgr);
5391135446Strhodes	ns_interfacemgr_detach(&server->interfacemgr);
5392135446Strhodes
5393135446Strhodes	dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
5394135446Strhodes
5395135446Strhodes	dns_zonemgr_shutdown(server->zonemgr);
5396135446Strhodes
5397224092Sdougb	if (ns_g_sessionkey != NULL) {
5398224092Sdougb		dns_tsigkey_detach(&ns_g_sessionkey);
5399224092Sdougb		dns_name_free(&ns_g_sessionkeyname, server->mctx);
5400224092Sdougb	}
5401224092Sdougb
5402135446Strhodes	if (server->blackholeacl != NULL)
5403135446Strhodes		dns_acl_detach(&server->blackholeacl);
5404135446Strhodes
5405135446Strhodes	dns_db_detach(&server->in_roothints);
5406135446Strhodes
5407135446Strhodes	isc_task_endexclusive(server->task);
5408135446Strhodes
5409135446Strhodes	isc_task_detach(&server->task);
5410135446Strhodes
5411135446Strhodes	isc_event_free(&event);
5412135446Strhodes}
5413135446Strhodes
5414135446Strhodesvoid
5415135446Strhodesns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
5416135446Strhodes	isc_result_t result;
5417225361Sdougb	ns_server_t *server = isc_mem_get(mctx, sizeof(*server));
5418135446Strhodes
5419135446Strhodes	if (server == NULL)
5420135446Strhodes		fatal("allocating server object", ISC_R_NOMEMORY);
5421135446Strhodes
5422135446Strhodes	server->mctx = mctx;
5423135446Strhodes	server->task = NULL;
5424135446Strhodes
5425135446Strhodes	/* Initialize configuration data with default values. */
5426135446Strhodes
5427135446Strhodes	result = isc_quota_init(&server->xfroutquota, 10);
5428135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5429135446Strhodes	result = isc_quota_init(&server->tcpquota, 10);
5430135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5431135446Strhodes	result = isc_quota_init(&server->recursionquota, 100);
5432135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5433135446Strhodes
5434135446Strhodes	result = dns_aclenv_init(mctx, &server->aclenv);
5435135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5436135446Strhodes
5437135446Strhodes	/* Initialize server data structures. */
5438135446Strhodes	server->zonemgr = NULL;
5439135446Strhodes	server->interfacemgr = NULL;
5440135446Strhodes	ISC_LIST_INIT(server->viewlist);
5441135446Strhodes	server->in_roothints = NULL;
5442135446Strhodes	server->blackholeacl = NULL;
5443135446Strhodes
5444135446Strhodes	CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
5445135446Strhodes				     &server->in_roothints),
5446135446Strhodes		   "setting up root hints");
5447135446Strhodes
5448135446Strhodes	CHECKFATAL(isc_mutex_init(&server->reload_event_lock),
5449135446Strhodes		   "initializing reload event lock");
5450135446Strhodes	server->reload_event =
5451135446Strhodes		isc_event_allocate(ns_g_mctx, server,
5452135446Strhodes				   NS_EVENT_RELOAD,
5453135446Strhodes				   ns_server_reload,
5454135446Strhodes				   server,
5455135446Strhodes				   sizeof(isc_event_t));
5456135446Strhodes	CHECKFATAL(server->reload_event == NULL ?
5457135446Strhodes		   ISC_R_NOMEMORY : ISC_R_SUCCESS,
5458135446Strhodes		   "allocating reload event");
5459135446Strhodes
5460224092Sdougb	CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy,
5461224092Sdougb				 ns_g_engine, ISC_ENTROPY_GOODONLY),
5462135446Strhodes		   "initializing DST");
5463135446Strhodes
5464135446Strhodes	server->tkeyctx = NULL;
5465135446Strhodes	CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
5466135446Strhodes				      &server->tkeyctx),
5467135446Strhodes		   "creating TKEY context");
5468135446Strhodes
5469135446Strhodes	/*
5470135446Strhodes	 * Setup the server task, which is responsible for coordinating
5471245163Serwin	 * startup and shutdown of the server, as well as all exclusive
5472245163Serwin	 * tasks.
5473135446Strhodes	 */
5474135446Strhodes	CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
5475135446Strhodes		   "creating server task");
5476135446Strhodes	isc_task_setname(server->task, "server", server);
5477245163Serwin	isc_taskmgr_setexcltask(ns_g_taskmgr, server->task);
5478135446Strhodes	CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
5479135446Strhodes		   "isc_task_onshutdown");
5480135446Strhodes	CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),
5481135446Strhodes		   "isc_app_onrun");
5482135446Strhodes
5483135446Strhodes	server->interface_timer = NULL;
5484135446Strhodes	server->heartbeat_timer = NULL;
5485170222Sdougb	server->pps_timer = NULL;
5486186462Sdougb
5487135446Strhodes	server->interface_interval = 0;
5488135446Strhodes	server->heartbeat_interval = 0;
5489135446Strhodes
5490135446Strhodes	CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr,
5491135446Strhodes				      ns_g_socketmgr, &server->zonemgr),
5492135446Strhodes		   "dns_zonemgr_create");
5493225361Sdougb	CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000),
5494225361Sdougb		   "dns_zonemgr_setsize");
5495135446Strhodes
5496135446Strhodes	server->statsfile = isc_mem_strdup(server->mctx, "named.stats");
5497135446Strhodes	CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
5498135446Strhodes		   "isc_mem_strdup");
5499193149Sdougb	server->nsstats = NULL;
5500193149Sdougb	server->rcvquerystats = NULL;
5501193149Sdougb	server->opcodestats = NULL;
5502193149Sdougb	server->zonestats = NULL;
5503193149Sdougb	server->resolverstats = NULL;
5504193149Sdougb	server->sockstats = NULL;
5505193149Sdougb	CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats,
5506193149Sdougb				    isc_sockstatscounter_max),
5507193149Sdougb		   "isc_stats_create");
5508193149Sdougb	isc_socketmgr_setstats(ns_g_socketmgr, server->sockstats);
5509135446Strhodes
5510224092Sdougb	server->bindkeysfile = isc_mem_strdup(server->mctx, "bind.keys");
5511224092Sdougb	CHECKFATAL(server->bindkeysfile == NULL ? ISC_R_NOMEMORY :
5512224092Sdougb						  ISC_R_SUCCESS,
5513224092Sdougb		   "isc_mem_strdup");
5514224092Sdougb
5515135446Strhodes	server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db");
5516135446Strhodes	CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
5517135446Strhodes		   "isc_mem_strdup");
5518135446Strhodes
5519224092Sdougb	server->secrootsfile = isc_mem_strdup(server->mctx, "named.secroots");
5520224092Sdougb	CHECKFATAL(server->secrootsfile == NULL ? ISC_R_NOMEMORY :
5521224092Sdougb						  ISC_R_SUCCESS,
5522224092Sdougb		   "isc_mem_strdup");
5523224092Sdougb
5524135446Strhodes	server->recfile = isc_mem_strdup(server->mctx, "named.recursing");
5525135446Strhodes	CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
5526135446Strhodes		   "isc_mem_strdup");
5527135446Strhodes
5528135446Strhodes	server->hostname_set = ISC_FALSE;
5529135446Strhodes	server->hostname = NULL;
5530186462Sdougb	server->version_set = ISC_FALSE;
5531135446Strhodes	server->version = NULL;
5532135446Strhodes	server->server_usehostname = ISC_FALSE;
5533135446Strhodes	server->server_id = NULL;
5534135446Strhodes
5535193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->nsstats,
5536193149Sdougb				    dns_nsstatscounter_max),
5537193149Sdougb		   "dns_stats_create (server)");
5538135446Strhodes
5539193149Sdougb	CHECKFATAL(dns_rdatatypestats_create(ns_g_mctx,
5540193149Sdougb					     &server->rcvquerystats),
5541193149Sdougb		   "dns_stats_create (rcvquery)");
5542193149Sdougb
5543193149Sdougb	CHECKFATAL(dns_opcodestats_create(ns_g_mctx, &server->opcodestats),
5544193149Sdougb		   "dns_stats_create (opcode)");
5545193149Sdougb
5546193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->zonestats,
5547193149Sdougb				    dns_zonestatscounter_max),
5548193149Sdougb		   "dns_stats_create (zone)");
5549193149Sdougb
5550193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->resolverstats,
5551193149Sdougb				    dns_resstatscounter_max),
5552193149Sdougb		   "dns_stats_create (resolver)");
5553193149Sdougb
5554135446Strhodes	server->flushonshutdown = ISC_FALSE;
5555135446Strhodes	server->log_queries = ISC_FALSE;
5556135446Strhodes
5557135446Strhodes	server->controls = NULL;
5558135446Strhodes	CHECKFATAL(ns_controls_create(server, &server->controls),
5559135446Strhodes		   "ns_controls_create");
5560135446Strhodes	server->dispatchgen = 0;
5561135446Strhodes	ISC_LIST_INIT(server->dispatches);
5562135446Strhodes
5563193149Sdougb	ISC_LIST_INIT(server->statschannels);
5564193149Sdougb
5565224092Sdougb	ISC_LIST_INIT(server->cachelist);
5566224092Sdougb
5567224092Sdougb	server->sessionkey = NULL;
5568224092Sdougb	server->session_keyfile = NULL;
5569224092Sdougb	server->session_keyname = NULL;
5570224092Sdougb	server->session_keyalg = DST_ALG_UNKNOWN;
5571224092Sdougb	server->session_keybits = 0;
5572224092Sdougb
5573135446Strhodes	server->magic = NS_SERVER_MAGIC;
5574135446Strhodes	*serverp = server;
5575135446Strhodes}
5576135446Strhodes
5577135446Strhodesvoid
5578135446Strhodesns_server_destroy(ns_server_t **serverp) {
5579135446Strhodes	ns_server_t *server = *serverp;
5580135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5581135446Strhodes
5582135446Strhodes	ns_controls_destroy(&server->controls);
5583135446Strhodes
5584193149Sdougb	isc_stats_detach(&server->nsstats);
5585193149Sdougb	dns_stats_detach(&server->rcvquerystats);
5586193149Sdougb	dns_stats_detach(&server->opcodestats);
5587193149Sdougb	isc_stats_detach(&server->zonestats);
5588193149Sdougb	isc_stats_detach(&server->resolverstats);
5589193149Sdougb	isc_stats_detach(&server->sockstats);
5590135446Strhodes
5591135446Strhodes	isc_mem_free(server->mctx, server->statsfile);
5592224092Sdougb	isc_mem_free(server->mctx, server->bindkeysfile);
5593135446Strhodes	isc_mem_free(server->mctx, server->dumpfile);
5594224092Sdougb	isc_mem_free(server->mctx, server->secrootsfile);
5595135446Strhodes	isc_mem_free(server->mctx, server->recfile);
5596135446Strhodes
5597135446Strhodes	if (server->version != NULL)
5598135446Strhodes		isc_mem_free(server->mctx, server->version);
5599135446Strhodes	if (server->hostname != NULL)
5600135446Strhodes		isc_mem_free(server->mctx, server->hostname);
5601135446Strhodes	if (server->server_id != NULL)
5602135446Strhodes		isc_mem_free(server->mctx, server->server_id);
5603135446Strhodes
5604225361Sdougb	if (server->zonemgr != NULL)
5605225361Sdougb		dns_zonemgr_detach(&server->zonemgr);
5606135446Strhodes
5607135446Strhodes	if (server->tkeyctx != NULL)
5608135446Strhodes		dns_tkeyctx_destroy(&server->tkeyctx);
5609135446Strhodes
5610135446Strhodes	dst_lib_destroy();
5611135446Strhodes
5612135446Strhodes	isc_event_free(&server->reload_event);
5613135446Strhodes
5614135446Strhodes	INSIST(ISC_LIST_EMPTY(server->viewlist));
5615224092Sdougb	INSIST(ISC_LIST_EMPTY(server->cachelist));
5616135446Strhodes
5617135446Strhodes	dns_aclenv_destroy(&server->aclenv);
5618135446Strhodes
5619135446Strhodes	isc_quota_destroy(&server->recursionquota);
5620135446Strhodes	isc_quota_destroy(&server->tcpquota);
5621135446Strhodes	isc_quota_destroy(&server->xfroutquota);
5622135446Strhodes
5623135446Strhodes	server->magic = 0;
5624135446Strhodes	isc_mem_put(server->mctx, server, sizeof(*server));
5625135446Strhodes	*serverp = NULL;
5626135446Strhodes}
5627135446Strhodes
5628135446Strhodesstatic void
5629135446Strhodesfatal(const char *msg, isc_result_t result) {
5630135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5631135446Strhodes		      ISC_LOG_CRITICAL, "%s: %s", msg,
5632135446Strhodes		      isc_result_totext(result));
5633135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5634135446Strhodes		      ISC_LOG_CRITICAL, "exiting (due to fatal error)");
5635135446Strhodes	exit(1);
5636135446Strhodes}
5637135446Strhodes
5638135446Strhodesstatic void
5639135446Strhodesstart_reserved_dispatches(ns_server_t *server) {
5640135446Strhodes
5641135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5642135446Strhodes
5643135446Strhodes	server->dispatchgen++;
5644135446Strhodes}
5645135446Strhodes
5646135446Strhodesstatic void
5647135446Strhodesend_reserved_dispatches(ns_server_t *server, isc_boolean_t all) {
5648135446Strhodes	ns_dispatch_t *dispatch, *nextdispatch;
5649135446Strhodes
5650135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5651135446Strhodes
5652135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
5653135446Strhodes	     dispatch != NULL;
5654135446Strhodes	     dispatch = nextdispatch) {
5655135446Strhodes		nextdispatch = ISC_LIST_NEXT(dispatch, link);
5656135446Strhodes		if (!all && server->dispatchgen == dispatch-> dispatchgen)
5657135446Strhodes			continue;
5658135446Strhodes		ISC_LIST_UNLINK(server->dispatches, dispatch, link);
5659135446Strhodes		dns_dispatch_detach(&dispatch->dispatch);
5660135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
5661135446Strhodes	}
5662135446Strhodes}
5663135446Strhodes
5664135446Strhodesvoid
5665165071Sdougbns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) {
5666135446Strhodes	ns_dispatch_t *dispatch;
5667135446Strhodes	in_port_t port;
5668135446Strhodes	char addrbuf[ISC_SOCKADDR_FORMATSIZE];
5669135446Strhodes	isc_result_t result;
5670135446Strhodes	unsigned int attrs, attrmask;
5671135446Strhodes
5672135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5673135446Strhodes
5674135446Strhodes	port = isc_sockaddr_getport(addr);
5675135446Strhodes	if (port == 0 || port >= 1024)
5676135446Strhodes		return;
5677135446Strhodes
5678135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
5679135446Strhodes	     dispatch != NULL;
5680135446Strhodes	     dispatch = ISC_LIST_NEXT(dispatch, link)) {
5681135446Strhodes		if (isc_sockaddr_equal(&dispatch->addr, addr))
5682135446Strhodes			break;
5683135446Strhodes	}
5684135446Strhodes	if (dispatch != NULL) {
5685135446Strhodes		dispatch->dispatchgen = server->dispatchgen;
5686135446Strhodes		return;
5687135446Strhodes	}
5688135446Strhodes
5689135446Strhodes	dispatch = isc_mem_get(server->mctx, sizeof(*dispatch));
5690135446Strhodes	if (dispatch == NULL) {
5691135446Strhodes		result = ISC_R_NOMEMORY;
5692135446Strhodes		goto cleanup;
5693135446Strhodes	}
5694135446Strhodes
5695135446Strhodes	dispatch->addr = *addr;
5696135446Strhodes	dispatch->dispatchgen = server->dispatchgen;
5697135446Strhodes	dispatch->dispatch = NULL;
5698135446Strhodes
5699135446Strhodes	attrs = 0;
5700135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
5701135446Strhodes	switch (isc_sockaddr_pf(addr)) {
5702135446Strhodes	case AF_INET:
5703135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
5704135446Strhodes		break;
5705135446Strhodes	case AF_INET6:
5706135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
5707135446Strhodes		break;
5708135446Strhodes	default:
5709135446Strhodes		result = ISC_R_NOTIMPLEMENTED;
5710135446Strhodes		goto cleanup;
5711135446Strhodes	}
5712135446Strhodes	attrmask = 0;
5713135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP;
5714135446Strhodes	attrmask |= DNS_DISPATCHATTR_TCP;
5715135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4;
5716135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV6;
5717135446Strhodes
5718135446Strhodes	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
5719135446Strhodes				     ns_g_taskmgr, &dispatch->addr, 4096,
5720135446Strhodes				     1000, 32768, 16411, 16433,
5721186462Sdougb				     attrs, attrmask, &dispatch->dispatch);
5722135446Strhodes	if (result != ISC_R_SUCCESS)
5723135446Strhodes		goto cleanup;
5724135446Strhodes
5725135446Strhodes	ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link);
5726135446Strhodes
5727135446Strhodes	return;
5728135446Strhodes
5729135446Strhodes cleanup:
5730135446Strhodes	if (dispatch != NULL)
5731135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
5732135446Strhodes	isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
5733135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5734135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
5735135446Strhodes		      "unable to create dispatch for reserved port %s: %s",
5736135446Strhodes		      addrbuf, isc_result_totext(result));
5737135446Strhodes}
5738135446Strhodes
5739135446Strhodes
5740135446Strhodesstatic isc_result_t
5741135446Strhodesloadconfig(ns_server_t *server) {
5742135446Strhodes	isc_result_t result;
5743135446Strhodes	start_reserved_dispatches(server);
5744135446Strhodes	result = load_configuration(ns_g_lwresdonly ?
5745135446Strhodes				    lwresd_g_conffile : ns_g_conffile,
5746143731Sdougb				    server, ISC_FALSE);
5747193149Sdougb	if (result == ISC_R_SUCCESS) {
5748135446Strhodes		end_reserved_dispatches(server, ISC_FALSE);
5749135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5750193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5751193149Sdougb			      "reloading configuration succeeded");
5752193149Sdougb	} else {
5753193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5754135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
5755135446Strhodes			      "reloading configuration failed: %s",
5756135446Strhodes			      isc_result_totext(result));
5757193149Sdougb	}
5758135446Strhodes	return (result);
5759135446Strhodes}
5760135446Strhodes
5761135446Strhodesstatic isc_result_t
5762135446Strhodesreload(ns_server_t *server) {
5763135446Strhodes	isc_result_t result;
5764135446Strhodes	CHECK(loadconfig(server));
5765135446Strhodes
5766135446Strhodes	result = load_zones(server, ISC_FALSE);
5767193149Sdougb	if (result == ISC_R_SUCCESS)
5768135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5769193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5770193149Sdougb			      "reloading zones succeeded");
5771193149Sdougb	else
5772193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5773135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
5774135446Strhodes			      "reloading zones failed: %s",
5775135446Strhodes			      isc_result_totext(result));
5776193149Sdougb
5777135446Strhodes cleanup:
5778135446Strhodes	return (result);
5779135446Strhodes}
5780135446Strhodes
5781135446Strhodesstatic void
5782135446Strhodesreconfig(ns_server_t *server) {
5783135446Strhodes	isc_result_t result;
5784135446Strhodes	CHECK(loadconfig(server));
5785135446Strhodes
5786135446Strhodes	result = load_new_zones(server, ISC_FALSE);
5787193149Sdougb	if (result == ISC_R_SUCCESS)
5788135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5789193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5790193149Sdougb			      "any newly configured zones are now loaded");
5791193149Sdougb	else
5792193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5793135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
5794135446Strhodes			      "loading new zones failed: %s",
5795135446Strhodes			      isc_result_totext(result));
5796193149Sdougb
5797135446Strhodes cleanup: ;
5798135446Strhodes}
5799135446Strhodes
5800135446Strhodes/*
5801135446Strhodes * Handle a reload event (from SIGHUP).
5802135446Strhodes */
5803135446Strhodesstatic void
5804135446Strhodesns_server_reload(isc_task_t *task, isc_event_t *event) {
5805135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
5806135446Strhodes
5807135446Strhodes	INSIST(task = server->task);
5808135446Strhodes	UNUSED(task);
5809135446Strhodes
5810193149Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5811193149Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5812193149Sdougb		      "received SIGHUP signal to reload zones");
5813135446Strhodes	(void)reload(server);
5814135446Strhodes
5815135446Strhodes	LOCK(&server->reload_event_lock);
5816135446Strhodes	INSIST(server->reload_event == NULL);
5817135446Strhodes	server->reload_event = event;
5818135446Strhodes	UNLOCK(&server->reload_event_lock);
5819135446Strhodes}
5820135446Strhodes
5821135446Strhodesvoid
5822135446Strhodesns_server_reloadwanted(ns_server_t *server) {
5823135446Strhodes	LOCK(&server->reload_event_lock);
5824135446Strhodes	if (server->reload_event != NULL)
5825135446Strhodes		isc_task_send(server->task, &server->reload_event);
5826135446Strhodes	UNLOCK(&server->reload_event_lock);
5827135446Strhodes}
5828135446Strhodes
5829135446Strhodesstatic char *
5830135446Strhodesnext_token(char **stringp, const char *delim) {
5831135446Strhodes	char *res;
5832135446Strhodes
5833135446Strhodes	do {
5834135446Strhodes		res = strsep(stringp, delim);
5835135446Strhodes		if (res == NULL)
5836135446Strhodes			break;
5837135446Strhodes	} while (*res == '\0');
5838135446Strhodes	return (res);
5839186462Sdougb}
5840135446Strhodes
5841135446Strhodes/*
5842135446Strhodes * Find the zone specified in the control channel command 'args',
5843135446Strhodes * if any.  If a zone is specified, point '*zonep' at it, otherwise
5844135446Strhodes * set '*zonep' to NULL.
5845135446Strhodes */
5846135446Strhodesstatic isc_result_t
5847224092Sdougbzone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep,
5848224092Sdougb	       const char **zonename)
5849224092Sdougb{
5850135446Strhodes	char *input, *ptr;
5851135446Strhodes	const char *zonetxt;
5852135446Strhodes	char *classtxt;
5853135446Strhodes	const char *viewtxt = NULL;
5854135446Strhodes	dns_fixedname_t name;
5855135446Strhodes	isc_result_t result;
5856135446Strhodes	isc_buffer_t buf;
5857135446Strhodes	dns_view_t *view = NULL;
5858135446Strhodes	dns_rdataclass_t rdclass;
5859135446Strhodes
5860135446Strhodes	REQUIRE(zonep != NULL && *zonep == NULL);
5861135446Strhodes
5862135446Strhodes	input = args;
5863135446Strhodes
5864135446Strhodes	/* Skip the command name. */
5865135446Strhodes	ptr = next_token(&input, " \t");
5866135446Strhodes	if (ptr == NULL)
5867135446Strhodes		return (ISC_R_UNEXPECTEDEND);
5868135446Strhodes
5869135446Strhodes	/* Look for the zone name. */
5870135446Strhodes	zonetxt = next_token(&input, " \t");
5871135446Strhodes	if (zonetxt == NULL)
5872135446Strhodes		return (ISC_R_SUCCESS);
5873224092Sdougb	if (zonename)
5874224092Sdougb		*zonename = zonetxt;
5875135446Strhodes
5876135446Strhodes	/* Look for the optional class name. */
5877135446Strhodes	classtxt = next_token(&input, " \t");
5878135446Strhodes	if (classtxt != NULL) {
5879135446Strhodes		/* Look for the optional view name. */
5880135446Strhodes		viewtxt = next_token(&input, " \t");
5881135446Strhodes	}
5882135446Strhodes
5883135446Strhodes	isc_buffer_init(&buf, zonetxt, strlen(zonetxt));
5884135446Strhodes	isc_buffer_add(&buf, strlen(zonetxt));
5885135446Strhodes	dns_fixedname_init(&name);
5886135446Strhodes	result = dns_name_fromtext(dns_fixedname_name(&name),
5887224092Sdougb				   &buf, dns_rootname, 0, NULL);
5888135446Strhodes	if (result != ISC_R_SUCCESS)
5889135446Strhodes		goto fail1;
5890135446Strhodes
5891135446Strhodes	if (classtxt != NULL) {
5892135446Strhodes		isc_textregion_t r;
5893135446Strhodes		r.base = classtxt;
5894135446Strhodes		r.length = strlen(classtxt);
5895135446Strhodes		result = dns_rdataclass_fromtext(&rdclass, &r);
5896135446Strhodes		if (result != ISC_R_SUCCESS)
5897135446Strhodes			goto fail1;
5898193149Sdougb	} else
5899193149Sdougb		rdclass = dns_rdataclass_in;
5900193149Sdougb
5901193149Sdougb	if (viewtxt == NULL) {
5902193149Sdougb		result = dns_viewlist_findzone(&server->viewlist,
5903193149Sdougb					       dns_fixedname_name(&name),
5904193149Sdougb					       ISC_TF(classtxt == NULL),
5905193149Sdougb					       rdclass, zonep);
5906135446Strhodes	} else {
5907193149Sdougb		result = dns_viewlist_find(&server->viewlist, viewtxt,
5908193149Sdougb					   rdclass, &view);
5909193149Sdougb		if (result != ISC_R_SUCCESS)
5910193149Sdougb			goto fail1;
5911193149Sdougb
5912193149Sdougb		result = dns_zt_find(view->zonetable, dns_fixedname_name(&name),
5913193149Sdougb				     0, NULL, zonep);
5914193149Sdougb		dns_view_detach(&view);
5915135446Strhodes	}
5916186462Sdougb
5917135446Strhodes	/* Partial match? */
5918135446Strhodes	if (result != ISC_R_SUCCESS && *zonep != NULL)
5919135446Strhodes		dns_zone_detach(zonep);
5920204619Sdougb	if (result == DNS_R_PARTIALMATCH)
5921204619Sdougb		result = ISC_R_NOTFOUND;
5922135446Strhodes fail1:
5923135446Strhodes	return (result);
5924135446Strhodes}
5925135446Strhodes
5926135446Strhodes/*
5927135446Strhodes * Act on a "retransfer" command from the command channel.
5928135446Strhodes */
5929135446Strhodesisc_result_t
5930135446Strhodesns_server_retransfercommand(ns_server_t *server, char *args) {
5931135446Strhodes	isc_result_t result;
5932135446Strhodes	dns_zone_t *zone = NULL;
5933135446Strhodes	dns_zonetype_t type;
5934186462Sdougb
5935224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
5936135446Strhodes	if (result != ISC_R_SUCCESS)
5937135446Strhodes		return (result);
5938135446Strhodes	if (zone == NULL)
5939135446Strhodes		return (ISC_R_UNEXPECTEDEND);
5940135446Strhodes	type = dns_zone_gettype(zone);
5941135446Strhodes	if (type == dns_zone_slave || type == dns_zone_stub)
5942135446Strhodes		dns_zone_forcereload(zone);
5943135446Strhodes	else
5944135446Strhodes		result = ISC_R_NOTFOUND;
5945135446Strhodes	dns_zone_detach(&zone);
5946135446Strhodes	return (result);
5947186462Sdougb}
5948135446Strhodes
5949135446Strhodes/*
5950135446Strhodes * Act on a "reload" command from the command channel.
5951135446Strhodes */
5952135446Strhodesisc_result_t
5953135446Strhodesns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
5954135446Strhodes	isc_result_t result;
5955135446Strhodes	dns_zone_t *zone = NULL;
5956135446Strhodes	dns_zonetype_t type;
5957135446Strhodes	const char *msg = NULL;
5958186462Sdougb
5959224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
5960135446Strhodes	if (result != ISC_R_SUCCESS)
5961135446Strhodes		return (result);
5962135446Strhodes	if (zone == NULL) {
5963135446Strhodes		result = reload(server);
5964135446Strhodes		if (result == ISC_R_SUCCESS)
5965135446Strhodes			msg = "server reload successful";
5966135446Strhodes	} else {
5967135446Strhodes		type = dns_zone_gettype(zone);
5968135446Strhodes		if (type == dns_zone_slave || type == dns_zone_stub) {
5969135446Strhodes			dns_zone_refresh(zone);
5970174187Sdougb			dns_zone_detach(&zone);
5971135446Strhodes			msg = "zone refresh queued";
5972135446Strhodes		} else {
5973135446Strhodes			result = dns_zone_load(zone);
5974135446Strhodes			dns_zone_detach(&zone);
5975186462Sdougb			switch (result) {
5976135446Strhodes			case ISC_R_SUCCESS:
5977135446Strhodes				 msg = "zone reload successful";
5978135446Strhodes				 break;
5979135446Strhodes			case DNS_R_CONTINUE:
5980135446Strhodes				msg = "zone reload queued";
5981135446Strhodes				result = ISC_R_SUCCESS;
5982135446Strhodes				break;
5983135446Strhodes			case DNS_R_UPTODATE:
5984135446Strhodes				msg = "zone reload up-to-date";
5985135446Strhodes				result = ISC_R_SUCCESS;
5986135446Strhodes				break;
5987135446Strhodes			default:
5988135446Strhodes				/* failure message will be generated by rndc */
5989135446Strhodes				break;
5990135446Strhodes			}
5991135446Strhodes		}
5992135446Strhodes	}
5993135446Strhodes	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
5994135446Strhodes		isc_buffer_putmem(text, (const unsigned char *)msg,
5995135446Strhodes				  strlen(msg) + 1);
5996135446Strhodes	return (result);
5997186462Sdougb}
5998135446Strhodes
5999135446Strhodes/*
6000135446Strhodes * Act on a "reconfig" command from the command channel.
6001135446Strhodes */
6002135446Strhodesisc_result_t
6003135446Strhodesns_server_reconfigcommand(ns_server_t *server, char *args) {
6004135446Strhodes	UNUSED(args);
6005135446Strhodes
6006135446Strhodes	reconfig(server);
6007135446Strhodes	return (ISC_R_SUCCESS);
6008135446Strhodes}
6009135446Strhodes
6010135446Strhodes/*
6011170222Sdougb * Act on a "notify" command from the command channel.
6012170222Sdougb */
6013170222Sdougbisc_result_t
6014170222Sdougbns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6015170222Sdougb	isc_result_t result;
6016170222Sdougb	dns_zone_t *zone = NULL;
6017170222Sdougb	const unsigned char msg[] = "zone notify queued";
6018170222Sdougb
6019224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
6020170222Sdougb	if (result != ISC_R_SUCCESS)
6021170222Sdougb		return (result);
6022170222Sdougb	if (zone == NULL)
6023170222Sdougb		return (ISC_R_UNEXPECTEDEND);
6024186462Sdougb
6025170222Sdougb	dns_zone_notify(zone);
6026170222Sdougb	dns_zone_detach(&zone);
6027170222Sdougb	if (sizeof(msg) <= isc_buffer_availablelength(text))
6028170222Sdougb		isc_buffer_putmem(text, msg, sizeof(msg));
6029170222Sdougb
6030170222Sdougb	return (ISC_R_SUCCESS);
6031186462Sdougb}
6032170222Sdougb
6033170222Sdougb/*
6034135446Strhodes * Act on a "refresh" command from the command channel.
6035135446Strhodes */
6036135446Strhodesisc_result_t
6037135446Strhodesns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6038135446Strhodes	isc_result_t result;
6039135446Strhodes	dns_zone_t *zone = NULL;
6040165071Sdougb	const unsigned char msg1[] = "zone refresh queued";
6041165071Sdougb	const unsigned char msg2[] = "not a slave or stub zone";
6042165071Sdougb	dns_zonetype_t type;
6043135446Strhodes
6044224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
6045135446Strhodes	if (result != ISC_R_SUCCESS)
6046135446Strhodes		return (result);
6047135446Strhodes	if (zone == NULL)
6048135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6049165071Sdougb
6050165071Sdougb	type = dns_zone_gettype(zone);
6051165071Sdougb	if (type == dns_zone_slave || type == dns_zone_stub) {
6052165071Sdougb		dns_zone_refresh(zone);
6053165071Sdougb		dns_zone_detach(&zone);
6054165071Sdougb		if (sizeof(msg1) <= isc_buffer_availablelength(text))
6055165071Sdougb			isc_buffer_putmem(text, msg1, sizeof(msg1));
6056165071Sdougb		return (ISC_R_SUCCESS);
6057165071Sdougb	}
6058186462Sdougb
6059135446Strhodes	dns_zone_detach(&zone);
6060165071Sdougb	if (sizeof(msg2) <= isc_buffer_availablelength(text))
6061165071Sdougb		isc_buffer_putmem(text, msg2, sizeof(msg2));
6062165071Sdougb	return (ISC_R_FAILURE);
6063186462Sdougb}
6064135446Strhodes
6065135446Strhodesisc_result_t
6066135446Strhodesns_server_togglequerylog(ns_server_t *server) {
6067135446Strhodes	server->log_queries = server->log_queries ? ISC_FALSE : ISC_TRUE;
6068186462Sdougb
6069135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6070135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6071135446Strhodes		      "query logging is now %s",
6072135446Strhodes		      server->log_queries ? "on" : "off");
6073135446Strhodes	return (ISC_R_SUCCESS);
6074135446Strhodes}
6075135446Strhodes
6076135446Strhodesstatic isc_result_t
6077165071Sdougbns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
6078170222Sdougb			 cfg_aclconfctx_t *actx,
6079135446Strhodes			 isc_mem_t *mctx, ns_listenlist_t **target)
6080135446Strhodes{
6081135446Strhodes	isc_result_t result;
6082165071Sdougb	const cfg_listelt_t *element;
6083135446Strhodes	ns_listenlist_t *dlist = NULL;
6084135446Strhodes
6085135446Strhodes	REQUIRE(target != NULL && *target == NULL);
6086135446Strhodes
6087135446Strhodes	result = ns_listenlist_create(mctx, &dlist);
6088135446Strhodes	if (result != ISC_R_SUCCESS)
6089135446Strhodes		return (result);
6090135446Strhodes
6091135446Strhodes	for (element = cfg_list_first(listenlist);
6092135446Strhodes	     element != NULL;
6093135446Strhodes	     element = cfg_list_next(element))
6094135446Strhodes	{
6095135446Strhodes		ns_listenelt_t *delt = NULL;
6096165071Sdougb		const cfg_obj_t *listener = cfg_listelt_value(element);
6097135446Strhodes		result = ns_listenelt_fromconfig(listener, config, actx,
6098135446Strhodes						 mctx, &delt);
6099135446Strhodes		if (result != ISC_R_SUCCESS)
6100135446Strhodes			goto cleanup;
6101135446Strhodes		ISC_LIST_APPEND(dlist->elts, delt, link);
6102135446Strhodes	}
6103135446Strhodes	*target = dlist;
6104135446Strhodes	return (ISC_R_SUCCESS);
6105135446Strhodes
6106135446Strhodes cleanup:
6107135446Strhodes	ns_listenlist_detach(&dlist);
6108135446Strhodes	return (result);
6109135446Strhodes}
6110135446Strhodes
6111135446Strhodes/*
6112135446Strhodes * Create a listen list from the corresponding configuration
6113135446Strhodes * data structure.
6114135446Strhodes */
6115135446Strhodesstatic isc_result_t
6116165071Sdougbns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
6117170222Sdougb			cfg_aclconfctx_t *actx,
6118135446Strhodes			isc_mem_t *mctx, ns_listenelt_t **target)
6119135446Strhodes{
6120135446Strhodes	isc_result_t result;
6121165071Sdougb	const cfg_obj_t *portobj;
6122135446Strhodes	in_port_t port;
6123135446Strhodes	ns_listenelt_t *delt = NULL;
6124135446Strhodes	REQUIRE(target != NULL && *target == NULL);
6125135446Strhodes
6126135446Strhodes	portobj = cfg_tuple_get(listener, "port");
6127135446Strhodes	if (!cfg_obj_isuint32(portobj)) {
6128135446Strhodes		if (ns_g_port != 0) {
6129135446Strhodes			port = ns_g_port;
6130135446Strhodes		} else {
6131135446Strhodes			result = ns_config_getport(config, &port);
6132135446Strhodes			if (result != ISC_R_SUCCESS)
6133135446Strhodes				return (result);
6134135446Strhodes		}
6135135446Strhodes	} else {
6136135446Strhodes		if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
6137135446Strhodes			cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
6138135446Strhodes				    "port value '%u' is out of range",
6139135446Strhodes				    cfg_obj_asuint32(portobj));
6140135446Strhodes			return (ISC_R_RANGE);
6141135446Strhodes		}
6142135446Strhodes		port = (in_port_t)cfg_obj_asuint32(portobj);
6143135446Strhodes	}
6144135446Strhodes
6145135446Strhodes	result = ns_listenelt_create(mctx, port, NULL, &delt);
6146135446Strhodes	if (result != ISC_R_SUCCESS)
6147135446Strhodes		return (result);
6148135446Strhodes
6149170222Sdougb	result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
6150193149Sdougb				   config, ns_g_lctx, actx, mctx, 0,
6151193149Sdougb				   &delt->acl);
6152135446Strhodes	if (result != ISC_R_SUCCESS) {
6153135446Strhodes		ns_listenelt_destroy(delt);
6154135446Strhodes		return (result);
6155135446Strhodes	}
6156135446Strhodes	*target = delt;
6157135446Strhodes	return (ISC_R_SUCCESS);
6158135446Strhodes}
6159135446Strhodes
6160135446Strhodesisc_result_t
6161135446Strhodesns_server_dumpstats(ns_server_t *server) {
6162135446Strhodes	isc_result_t result;
6163135446Strhodes	FILE *fp = NULL;
6164135446Strhodes
6165135446Strhodes	CHECKMF(isc_stdio_open(server->statsfile, "a", &fp),
6166135446Strhodes		"could not open statistics dump file", server->statsfile);
6167186462Sdougb
6168193149Sdougb	result = ns_stats_dump(server, fp);
6169186462Sdougb
6170135446Strhodes cleanup:
6171135446Strhodes	if (fp != NULL)
6172135446Strhodes		(void)isc_stdio_close(fp);
6173193149Sdougb	if (result == ISC_R_SUCCESS)
6174193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6175193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6176193149Sdougb			      "dumpstats complete");
6177193149Sdougb	else
6178193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6179193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6180193149Sdougb			      "dumpstats failed: %s",
6181193149Sdougb			      dns_result_totext(result));
6182135446Strhodes	return (result);
6183135446Strhodes}
6184135446Strhodes
6185135446Strhodesstatic isc_result_t
6186135446Strhodesadd_zone_tolist(dns_zone_t *zone, void *uap) {
6187135446Strhodes	struct dumpcontext *dctx = uap;
6188135446Strhodes	struct zonelistentry *zle;
6189135446Strhodes
6190135446Strhodes	zle = isc_mem_get(dctx->mctx, sizeof *zle);
6191135446Strhodes	if (zle ==  NULL)
6192135446Strhodes		return (ISC_R_NOMEMORY);
6193135446Strhodes	zle->zone = NULL;
6194135446Strhodes	dns_zone_attach(zone, &zle->zone);
6195135446Strhodes	ISC_LINK_INIT(zle, link);
6196135446Strhodes	ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link);
6197135446Strhodes	return (ISC_R_SUCCESS);
6198135446Strhodes}
6199135446Strhodes
6200135446Strhodesstatic isc_result_t
6201135446Strhodesadd_view_tolist(struct dumpcontext *dctx, dns_view_t *view) {
6202135446Strhodes	struct viewlistentry *vle;
6203135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
6204186462Sdougb
6205153816Sdougb	/*
6206153816Sdougb	 * Prevent duplicate views.
6207153816Sdougb	 */
6208153816Sdougb	for (vle = ISC_LIST_HEAD(dctx->viewlist);
6209153816Sdougb	     vle != NULL;
6210153816Sdougb	     vle = ISC_LIST_NEXT(vle, link))
6211153816Sdougb		if (vle->view == view)
6212153816Sdougb			return (ISC_R_SUCCESS);
6213153816Sdougb
6214135446Strhodes	vle = isc_mem_get(dctx->mctx, sizeof *vle);
6215135446Strhodes	if (vle == NULL)
6216135446Strhodes		return (ISC_R_NOMEMORY);
6217135446Strhodes	vle->view = NULL;
6218135446Strhodes	dns_view_attach(view, &vle->view);
6219135446Strhodes	ISC_LINK_INIT(vle, link);
6220135446Strhodes	ISC_LIST_INIT(vle->zonelist);
6221135446Strhodes	ISC_LIST_APPEND(dctx->viewlist, vle, link);
6222135446Strhodes	if (dctx->dumpzones)
6223135446Strhodes		result = dns_zt_apply(view->zonetable, ISC_TRUE,
6224135446Strhodes				      add_zone_tolist, dctx);
6225135446Strhodes	return (result);
6226135446Strhodes}
6227135446Strhodes
6228135446Strhodesstatic void
6229135446Strhodesdumpcontext_destroy(struct dumpcontext *dctx) {
6230135446Strhodes	struct viewlistentry *vle;
6231135446Strhodes	struct zonelistentry *zle;
6232135446Strhodes
6233135446Strhodes	vle = ISC_LIST_HEAD(dctx->viewlist);
6234135446Strhodes	while (vle != NULL) {
6235135446Strhodes		ISC_LIST_UNLINK(dctx->viewlist, vle, link);
6236135446Strhodes		zle = ISC_LIST_HEAD(vle->zonelist);
6237135446Strhodes		while (zle != NULL) {
6238135446Strhodes			ISC_LIST_UNLINK(vle->zonelist, zle, link);
6239135446Strhodes			dns_zone_detach(&zle->zone);
6240135446Strhodes			isc_mem_put(dctx->mctx, zle, sizeof *zle);
6241135446Strhodes			zle = ISC_LIST_HEAD(vle->zonelist);
6242135446Strhodes		}
6243135446Strhodes		dns_view_detach(&vle->view);
6244135446Strhodes		isc_mem_put(dctx->mctx, vle, sizeof *vle);
6245135446Strhodes		vle = ISC_LIST_HEAD(dctx->viewlist);
6246135446Strhodes	}
6247135446Strhodes	if (dctx->version != NULL)
6248135446Strhodes		dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
6249135446Strhodes	if (dctx->db != NULL)
6250135446Strhodes		dns_db_detach(&dctx->db);
6251135446Strhodes	if (dctx->cache != NULL)
6252135446Strhodes		dns_db_detach(&dctx->cache);
6253135446Strhodes	if (dctx->task != NULL)
6254135446Strhodes		isc_task_detach(&dctx->task);
6255135446Strhodes	if (dctx->fp != NULL)
6256135446Strhodes		(void)isc_stdio_close(dctx->fp);
6257135446Strhodes	if (dctx->mdctx != NULL)
6258135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
6259135446Strhodes	isc_mem_put(dctx->mctx, dctx, sizeof *dctx);
6260135446Strhodes}
6261135446Strhodes
6262135446Strhodesstatic void
6263135446Strhodesdumpdone(void *arg, isc_result_t result) {
6264135446Strhodes	struct dumpcontext *dctx = arg;
6265135446Strhodes	char buf[1024+32];
6266135446Strhodes	const dns_master_style_t *style;
6267186462Sdougb
6268135446Strhodes	if (result != ISC_R_SUCCESS)
6269135446Strhodes		goto cleanup;
6270135446Strhodes	if (dctx->mdctx != NULL)
6271135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
6272135446Strhodes	if (dctx->view == NULL) {
6273135446Strhodes		dctx->view = ISC_LIST_HEAD(dctx->viewlist);
6274135446Strhodes		if (dctx->view == NULL)
6275135446Strhodes			goto done;
6276135446Strhodes		INSIST(dctx->zone == NULL);
6277153816Sdougb	} else
6278153816Sdougb		goto resume;
6279135446Strhodes nextview:
6280135446Strhodes	fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
6281153816Sdougb resume:
6282224092Sdougb	if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) {
6283224092Sdougb		fprintf(dctx->fp,
6284224092Sdougb			";\n; Cache of view '%s' is shared as '%s'\n",
6285224092Sdougb			dctx->view->view->name,
6286224092Sdougb			dns_cache_getname(dctx->view->view->cache));
6287224092Sdougb	} else if (dctx->zone == NULL && dctx->cache == NULL &&
6288224092Sdougb		   dctx->dumpcache)
6289224092Sdougb	{
6290135446Strhodes		style = &dns_master_style_cache;
6291135446Strhodes		/* start cache dump */
6292135446Strhodes		if (dctx->view->view->cachedb != NULL)
6293135446Strhodes			dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
6294135446Strhodes		if (dctx->cache != NULL) {
6295224092Sdougb			fprintf(dctx->fp,
6296224092Sdougb				";\n; Cache dump of view '%s' (cache %s)\n;\n",
6297224092Sdougb				dctx->view->view->name,
6298224092Sdougb				dns_cache_getname(dctx->view->view->cache));
6299135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
6300135446Strhodes							    dctx->cache, NULL,
6301135446Strhodes							    style, dctx->fp,
6302135446Strhodes							    dctx->task,
6303135446Strhodes							    dumpdone, dctx,
6304135446Strhodes							    &dctx->mdctx);
6305135446Strhodes			if (result == DNS_R_CONTINUE)
6306135446Strhodes				return;
6307135446Strhodes			if (result == ISC_R_NOTIMPLEMENTED)
6308135446Strhodes				fprintf(dctx->fp, "; %s\n",
6309135446Strhodes					dns_result_totext(result));
6310135446Strhodes			else if (result != ISC_R_SUCCESS)
6311135446Strhodes				goto cleanup;
6312135446Strhodes		}
6313135446Strhodes	}
6314135446Strhodes	if (dctx->cache != NULL) {
6315135446Strhodes		dns_adb_dump(dctx->view->view->adb, dctx->fp);
6316205292Sdougb		dns_resolver_printbadcache(dctx->view->view->resolver,
6317205292Sdougb					   dctx->fp);
6318135446Strhodes		dns_db_detach(&dctx->cache);
6319135446Strhodes	}
6320135446Strhodes	if (dctx->dumpzones) {
6321135446Strhodes		style = &dns_master_style_full;
6322135446Strhodes nextzone:
6323135446Strhodes		if (dctx->version != NULL)
6324135446Strhodes			dns_db_closeversion(dctx->db, &dctx->version,
6325135446Strhodes					    ISC_FALSE);
6326135446Strhodes		if (dctx->db != NULL)
6327135446Strhodes			dns_db_detach(&dctx->db);
6328135446Strhodes		if (dctx->zone == NULL)
6329135446Strhodes			dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist);
6330135446Strhodes		else
6331135446Strhodes			dctx->zone = ISC_LIST_NEXT(dctx->zone, link);
6332135446Strhodes		if (dctx->zone != NULL) {
6333135446Strhodes			/* start zone dump */
6334135446Strhodes			dns_zone_name(dctx->zone->zone, buf, sizeof(buf));
6335135446Strhodes			fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf);
6336135446Strhodes			result = dns_zone_getdb(dctx->zone->zone, &dctx->db);
6337135446Strhodes			if (result != ISC_R_SUCCESS) {
6338135446Strhodes				fprintf(dctx->fp, "; %s\n",
6339135446Strhodes					dns_result_totext(result));
6340135446Strhodes				goto nextzone;
6341135446Strhodes			}
6342135446Strhodes			dns_db_currentversion(dctx->db, &dctx->version);
6343135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
6344135446Strhodes							    dctx->db,
6345135446Strhodes							    dctx->version,
6346135446Strhodes							    style, dctx->fp,
6347135446Strhodes							    dctx->task,
6348135446Strhodes							    dumpdone, dctx,
6349135446Strhodes							    &dctx->mdctx);
6350135446Strhodes			if (result == DNS_R_CONTINUE)
6351135446Strhodes				return;
6352153816Sdougb			if (result == ISC_R_NOTIMPLEMENTED) {
6353135446Strhodes				fprintf(dctx->fp, "; %s\n",
6354135446Strhodes					dns_result_totext(result));
6355153816Sdougb				result = ISC_R_SUCCESS;
6356225361Sdougb				POST(result);
6357153816Sdougb				goto nextzone;
6358153816Sdougb			}
6359135446Strhodes			if (result != ISC_R_SUCCESS)
6360135446Strhodes				goto cleanup;
6361135446Strhodes		}
6362135446Strhodes	}
6363135446Strhodes	if (dctx->view != NULL)
6364135446Strhodes		dctx->view = ISC_LIST_NEXT(dctx->view, link);
6365135446Strhodes	if (dctx->view != NULL)
6366135446Strhodes		goto nextview;
6367135446Strhodes done:
6368135446Strhodes	fprintf(dctx->fp, "; Dump complete\n");
6369135446Strhodes	result = isc_stdio_flush(dctx->fp);
6370135446Strhodes	if (result == ISC_R_SUCCESS)
6371135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6372135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6373135446Strhodes			      "dumpdb complete");
6374135446Strhodes cleanup:
6375135446Strhodes	if (result != ISC_R_SUCCESS)
6376135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6377193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6378135446Strhodes			      "dumpdb failed: %s", dns_result_totext(result));
6379135446Strhodes	dumpcontext_destroy(dctx);
6380135446Strhodes}
6381135446Strhodes
6382135446Strhodesisc_result_t
6383135446Strhodesns_server_dumpdb(ns_server_t *server, char *args) {
6384135446Strhodes	struct dumpcontext *dctx = NULL;
6385135446Strhodes	dns_view_t *view;
6386135446Strhodes	isc_result_t result;
6387135446Strhodes	char *ptr;
6388135446Strhodes	const char *sep;
6389135446Strhodes
6390165071Sdougb	/* Skip the command name. */
6391165071Sdougb	ptr = next_token(&args, " \t");
6392165071Sdougb	if (ptr == NULL)
6393165071Sdougb		return (ISC_R_UNEXPECTEDEND);
6394165071Sdougb
6395135446Strhodes	dctx = isc_mem_get(server->mctx, sizeof(*dctx));
6396135446Strhodes	if (dctx == NULL)
6397135446Strhodes		return (ISC_R_NOMEMORY);
6398135446Strhodes
6399135446Strhodes	dctx->mctx = server->mctx;
6400135446Strhodes	dctx->dumpcache = ISC_TRUE;
6401135446Strhodes	dctx->dumpzones = ISC_FALSE;
6402135446Strhodes	dctx->fp = NULL;
6403135446Strhodes	ISC_LIST_INIT(dctx->viewlist);
6404135446Strhodes	dctx->view = NULL;
6405135446Strhodes	dctx->zone = NULL;
6406135446Strhodes	dctx->cache = NULL;
6407135446Strhodes	dctx->mdctx = NULL;
6408135446Strhodes	dctx->db = NULL;
6409135446Strhodes	dctx->cache = NULL;
6410135446Strhodes	dctx->task = NULL;
6411135446Strhodes	dctx->version = NULL;
6412135446Strhodes	isc_task_attach(server->task, &dctx->task);
6413135446Strhodes
6414135446Strhodes	CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp),
6415135446Strhodes		"could not open dump file", server->dumpfile);
6416135446Strhodes
6417135446Strhodes	sep = (args == NULL) ? "" : ": ";
6418135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6419135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6420135446Strhodes		      "dumpdb started%s%s", sep, (args != NULL) ? args : "");
6421135446Strhodes
6422135446Strhodes	ptr = next_token(&args, " \t");
6423135446Strhodes	if (ptr != NULL && strcmp(ptr, "-all") == 0) {
6424135446Strhodes		dctx->dumpzones = ISC_TRUE;
6425135446Strhodes		dctx->dumpcache = ISC_TRUE;
6426135446Strhodes		ptr = next_token(&args, " \t");
6427135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
6428135446Strhodes		dctx->dumpzones = ISC_FALSE;
6429135446Strhodes		dctx->dumpcache = ISC_TRUE;
6430135446Strhodes		ptr = next_token(&args, " \t");
6431135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
6432135446Strhodes		dctx->dumpzones = ISC_TRUE;
6433135446Strhodes		dctx->dumpcache = ISC_FALSE;
6434135446Strhodes		ptr = next_token(&args, " \t");
6435186462Sdougb	}
6436135446Strhodes
6437153816Sdougb nextview:
6438135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
6439135446Strhodes	     view != NULL;
6440135446Strhodes	     view = ISC_LIST_NEXT(view, link))
6441135446Strhodes	{
6442135446Strhodes		if (ptr != NULL && strcmp(view->name, ptr) != 0)
6443135446Strhodes			continue;
6444135446Strhodes		CHECK(add_view_tolist(dctx, view));
6445135446Strhodes	}
6446153816Sdougb	if (ptr != NULL) {
6447153816Sdougb		ptr = next_token(&args, " \t");
6448153816Sdougb		if (ptr != NULL)
6449153816Sdougb			goto nextview;
6450153816Sdougb	}
6451135446Strhodes	dumpdone(dctx, ISC_R_SUCCESS);
6452135446Strhodes	return (ISC_R_SUCCESS);
6453135446Strhodes
6454135446Strhodes cleanup:
6455135446Strhodes	if (dctx != NULL)
6456135446Strhodes		dumpcontext_destroy(dctx);
6457135446Strhodes	return (result);
6458135446Strhodes}
6459135446Strhodes
6460135446Strhodesisc_result_t
6461224092Sdougbns_server_dumpsecroots(ns_server_t *server, char *args) {
6462224092Sdougb	dns_view_t *view;
6463224092Sdougb	dns_keytable_t *secroots = NULL;
6464224092Sdougb	isc_result_t result;
6465224092Sdougb	char *ptr;
6466224092Sdougb	FILE *fp = NULL;
6467224092Sdougb	isc_time_t now;
6468224092Sdougb	char tbuf[64];
6469224092Sdougb
6470224092Sdougb	/* Skip the command name. */
6471224092Sdougb	ptr = next_token(&args, " \t");
6472224092Sdougb	if (ptr == NULL)
6473224092Sdougb		return (ISC_R_UNEXPECTEDEND);
6474224092Sdougb	ptr = next_token(&args, " \t");
6475224092Sdougb
6476224092Sdougb	CHECKMF(isc_stdio_open(server->secrootsfile, "w", &fp),
6477224092Sdougb		"could not open secroots dump file", server->secrootsfile);
6478224092Sdougb	TIME_NOW(&now);
6479224092Sdougb	isc_time_formattimestamp(&now, tbuf, sizeof(tbuf));
6480224092Sdougb	fprintf(fp, "%s\n", tbuf);
6481224092Sdougb
6482225361Sdougb	do {
6483225361Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
6484225361Sdougb		     view != NULL;
6485225361Sdougb		     view = ISC_LIST_NEXT(view, link))
6486225361Sdougb		{
6487225361Sdougb			if (ptr != NULL && strcmp(view->name, ptr) != 0)
6488225361Sdougb				continue;
6489225361Sdougb			if (secroots != NULL)
6490225361Sdougb				dns_keytable_detach(&secroots);
6491225361Sdougb			result = dns_view_getsecroots(view, &secroots);
6492225361Sdougb			if (result == ISC_R_NOTFOUND) {
6493225361Sdougb				result = ISC_R_SUCCESS;
6494225361Sdougb				continue;
6495225361Sdougb			}
6496225361Sdougb			fprintf(fp, "\n Start view %s\n\n", view->name);
6497225361Sdougb			result = dns_keytable_dump(secroots, fp);
6498225361Sdougb			if (result != ISC_R_SUCCESS)
6499225361Sdougb				fprintf(fp, " dumpsecroots failed: %s\n",
6500225361Sdougb					isc_result_totext(result));
6501224092Sdougb		}
6502224092Sdougb		if (ptr != NULL)
6503225361Sdougb			ptr = next_token(&args, " \t");
6504225361Sdougb	} while (ptr != NULL);
6505224092Sdougb
6506224092Sdougb cleanup:
6507224092Sdougb	if (secroots != NULL)
6508224092Sdougb		dns_keytable_detach(&secroots);
6509224092Sdougb	if (fp != NULL)
6510224092Sdougb		(void)isc_stdio_close(fp);
6511224092Sdougb	if (result == ISC_R_SUCCESS)
6512224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6513224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6514224092Sdougb			      "dumpsecroots complete");
6515224092Sdougb	else
6516224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6517224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6518224092Sdougb			      "dumpsecroots failed: %s",
6519224092Sdougb			      dns_result_totext(result));
6520224092Sdougb	return (result);
6521224092Sdougb}
6522224092Sdougb
6523224092Sdougbisc_result_t
6524135446Strhodesns_server_dumprecursing(ns_server_t *server) {
6525135446Strhodes	FILE *fp = NULL;
6526135446Strhodes	isc_result_t result;
6527135446Strhodes
6528135446Strhodes	CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
6529135446Strhodes		"could not open dump file", server->recfile);
6530135446Strhodes	fprintf(fp,";\n; Recursing Queries\n;\n");
6531135446Strhodes	ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
6532135446Strhodes	fprintf(fp, "; Dump complete\n");
6533135446Strhodes
6534135446Strhodes cleanup:
6535135446Strhodes	if (fp != NULL)
6536135446Strhodes		result = isc_stdio_close(fp);
6537193149Sdougb	if (result == ISC_R_SUCCESS)
6538193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6539193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6540193149Sdougb			      "dumprecursing complete");
6541193149Sdougb	else
6542193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6543193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6544193149Sdougb			      "dumprecursing failed: %s",
6545193149Sdougb			      dns_result_totext(result));
6546135446Strhodes	return (result);
6547135446Strhodes}
6548135446Strhodes
6549135446Strhodesisc_result_t
6550135446Strhodesns_server_setdebuglevel(ns_server_t *server, char *args) {
6551135446Strhodes	char *ptr;
6552135446Strhodes	char *levelstr;
6553135446Strhodes	char *endp;
6554135446Strhodes	long newlevel;
6555135446Strhodes
6556135446Strhodes	UNUSED(server);
6557135446Strhodes
6558135446Strhodes	/* Skip the command name. */
6559135446Strhodes	ptr = next_token(&args, " \t");
6560135446Strhodes	if (ptr == NULL)
6561135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6562135446Strhodes
6563135446Strhodes	/* Look for the new level name. */
6564135446Strhodes	levelstr = next_token(&args, " \t");
6565135446Strhodes	if (levelstr == NULL) {
6566135446Strhodes		if (ns_g_debuglevel < 99)
6567135446Strhodes			ns_g_debuglevel++;
6568135446Strhodes	} else {
6569135446Strhodes		newlevel = strtol(levelstr, &endp, 10);
6570135446Strhodes		if (*endp != '\0' || newlevel < 0 || newlevel > 99)
6571135446Strhodes			return (ISC_R_RANGE);
6572135446Strhodes		ns_g_debuglevel = (unsigned int)newlevel;
6573135446Strhodes	}
6574135446Strhodes	isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
6575193149Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6576193149Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6577193149Sdougb		      "debug level is now %d", ns_g_debuglevel);
6578135446Strhodes	return (ISC_R_SUCCESS);
6579135446Strhodes}
6580135446Strhodes
6581135446Strhodesisc_result_t
6582170222Sdougbns_server_validation(ns_server_t *server, char *args) {
6583170222Sdougb	char *ptr, *viewname;
6584170222Sdougb	dns_view_t *view;
6585170222Sdougb	isc_boolean_t changed = ISC_FALSE;
6586170222Sdougb	isc_result_t result;
6587170222Sdougb	isc_boolean_t enable;
6588170222Sdougb
6589170222Sdougb	/* Skip the command name. */
6590170222Sdougb	ptr = next_token(&args, " \t");
6591170222Sdougb	if (ptr == NULL)
6592170222Sdougb		return (ISC_R_UNEXPECTEDEND);
6593170222Sdougb
6594170222Sdougb	/* Find out what we are to do. */
6595170222Sdougb	ptr = next_token(&args, " \t");
6596170222Sdougb	if (ptr == NULL)
6597170222Sdougb		return (ISC_R_UNEXPECTEDEND);
6598170222Sdougb
6599170222Sdougb	if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
6600170222Sdougb	    !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
6601170222Sdougb		enable = ISC_TRUE;
6602170222Sdougb	else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
6603170222Sdougb		 !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
6604170222Sdougb		enable = ISC_FALSE;
6605170222Sdougb	else
6606170222Sdougb		return (DNS_R_SYNTAX);
6607170222Sdougb
6608170222Sdougb	/* Look for the view name. */
6609170222Sdougb	viewname = next_token(&args, " \t");
6610170222Sdougb
6611170222Sdougb	result = isc_task_beginexclusive(server->task);
6612170222Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6613170222Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
6614170222Sdougb	     view != NULL;
6615170222Sdougb	     view = ISC_LIST_NEXT(view, link))
6616170222Sdougb	{
6617170222Sdougb		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
6618170222Sdougb			continue;
6619170222Sdougb		result = dns_view_flushcache(view);
6620170222Sdougb		if (result != ISC_R_SUCCESS)
6621170222Sdougb			goto out;
6622170222Sdougb		view->enablevalidation = enable;
6623170222Sdougb		changed = ISC_TRUE;
6624170222Sdougb	}
6625170222Sdougb	if (changed)
6626170222Sdougb		result = ISC_R_SUCCESS;
6627170222Sdougb	else
6628170222Sdougb		result = ISC_R_FAILURE;
6629170222Sdougb out:
6630186462Sdougb	isc_task_endexclusive(server->task);
6631170222Sdougb	return (result);
6632170222Sdougb}
6633170222Sdougb
6634170222Sdougbisc_result_t
6635135446Strhodesns_server_flushcache(ns_server_t *server, char *args) {
6636135446Strhodes	char *ptr, *viewname;
6637135446Strhodes	dns_view_t *view;
6638174187Sdougb	isc_boolean_t flushed;
6639174187Sdougb	isc_boolean_t found;
6640135446Strhodes	isc_result_t result;
6641224092Sdougb	ns_cache_t *nsc;
6642135446Strhodes
6643135446Strhodes	/* Skip the command name. */
6644135446Strhodes	ptr = next_token(&args, " \t");
6645135446Strhodes	if (ptr == NULL)
6646135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6647135446Strhodes
6648135446Strhodes	/* Look for the view name. */
6649135446Strhodes	viewname = next_token(&args, " \t");
6650135446Strhodes
6651135446Strhodes	result = isc_task_beginexclusive(server->task);
6652135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6653174187Sdougb	flushed = ISC_TRUE;
6654174187Sdougb	found = ISC_FALSE;
6655224092Sdougb
6656224092Sdougb	/*
6657224092Sdougb	 * Flushing a cache is tricky when caches are shared by multiple views.
6658224092Sdougb	 * We first identify which caches should be flushed in the local cache
6659224092Sdougb	 * list, flush these caches, and then update other views that refer to
6660224092Sdougb	 * the flushed cache DB.
6661224092Sdougb	 */
6662224092Sdougb	if (viewname != NULL) {
6663224092Sdougb		/*
6664224092Sdougb		 * Mark caches that need to be flushed.  This is an O(#view^2)
6665224092Sdougb		 * operation in the very worst case, but should be normally
6666224092Sdougb		 * much more lightweight because only a few (most typically just
6667224092Sdougb		 * one) views will match.
6668224092Sdougb		 */
6669224092Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
6670224092Sdougb		     view != NULL;
6671224092Sdougb		     view = ISC_LIST_NEXT(view, link))
6672224092Sdougb		{
6673224092Sdougb			if (strcasecmp(viewname, view->name) != 0)
6674224092Sdougb				continue;
6675224092Sdougb			found = ISC_TRUE;
6676224092Sdougb			for (nsc = ISC_LIST_HEAD(server->cachelist);
6677224092Sdougb			     nsc != NULL;
6678224092Sdougb			     nsc = ISC_LIST_NEXT(nsc, link)) {
6679224092Sdougb				if (nsc->cache == view->cache)
6680224092Sdougb					break;
6681224092Sdougb			}
6682224092Sdougb			INSIST(nsc != NULL);
6683224092Sdougb			nsc->needflush = ISC_TRUE;
6684224092Sdougb		}
6685224092Sdougb	} else
6686224092Sdougb		found = ISC_TRUE;
6687224092Sdougb
6688224092Sdougb	/* Perform flush */
6689224092Sdougb	for (nsc = ISC_LIST_HEAD(server->cachelist);
6690224092Sdougb	     nsc != NULL;
6691224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
6692224092Sdougb		if (viewname != NULL && !nsc->needflush)
6693135446Strhodes			continue;
6694224092Sdougb		nsc->needflush = ISC_TRUE;
6695224092Sdougb		result = dns_view_flushcache2(nsc->primaryview, ISC_FALSE);
6696193149Sdougb		if (result != ISC_R_SUCCESS) {
6697174187Sdougb			flushed = ISC_FALSE;
6698193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6699193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6700193149Sdougb				      "flushing cache in view '%s' failed: %s",
6701224092Sdougb				      nsc->primaryview->name,
6702224092Sdougb				      isc_result_totext(result));
6703193149Sdougb		}
6704135446Strhodes	}
6705224092Sdougb
6706224092Sdougb	/*
6707224092Sdougb	 * Fix up views that share a flushed cache: let the views update the
6708224092Sdougb	 * cache DB they're referring to.  This could also be an expensive
6709224092Sdougb	 * operation, but should typically be marginal: the inner loop is only
6710224092Sdougb	 * necessary for views that share a cache, and if there are many such
6711224092Sdougb	 * views the number of shared cache should normally be small.
6712224092Sdougb	 * A worst case is that we have n views and n/2 caches, each shared by
6713224092Sdougb	 * two views.  Then this will be a O(n^2/4) operation.
6714224092Sdougb	 */
6715224092Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
6716224092Sdougb	     view != NULL;
6717224092Sdougb	     view = ISC_LIST_NEXT(view, link))
6718224092Sdougb	{
6719224092Sdougb		if (!dns_view_iscacheshared(view))
6720224092Sdougb			continue;
6721224092Sdougb		for (nsc = ISC_LIST_HEAD(server->cachelist);
6722224092Sdougb		     nsc != NULL;
6723224092Sdougb		     nsc = ISC_LIST_NEXT(nsc, link)) {
6724224092Sdougb			if (!nsc->needflush || nsc->cache != view->cache)
6725224092Sdougb				continue;
6726224092Sdougb			result = dns_view_flushcache2(view, ISC_TRUE);
6727224092Sdougb			if (result != ISC_R_SUCCESS) {
6728224092Sdougb				flushed = ISC_FALSE;
6729224092Sdougb				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6730224092Sdougb					      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6731224092Sdougb					      "fixing cache in view '%s' "
6732224092Sdougb					      "failed: %s", view->name,
6733224092Sdougb					      isc_result_totext(result));
6734224092Sdougb			}
6735224092Sdougb		}
6736224092Sdougb	}
6737224092Sdougb
6738224092Sdougb	/* Cleanup the cache list. */
6739224092Sdougb	for (nsc = ISC_LIST_HEAD(server->cachelist);
6740224092Sdougb	     nsc != NULL;
6741224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
6742224092Sdougb		nsc->needflush = ISC_FALSE;
6743224092Sdougb	}
6744224092Sdougb
6745174187Sdougb	if (flushed && found) {
6746193149Sdougb		if (viewname != NULL)
6747193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6748193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6749193149Sdougb				      "flushing cache in view '%s' succeeded",
6750193149Sdougb				      viewname);
6751193149Sdougb		else
6752193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6753193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6754193149Sdougb				      "flushing caches in all views succeeded");
6755135446Strhodes		result = ISC_R_SUCCESS;
6756174187Sdougb	} else {
6757193149Sdougb		if (!found) {
6758193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6759193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6760193149Sdougb				      "flushing cache in view '%s' failed: "
6761193149Sdougb				      "view not found", viewname);
6762174187Sdougb			result = ISC_R_NOTFOUND;
6763193149Sdougb		} else
6764174187Sdougb			result = ISC_R_FAILURE;
6765174187Sdougb	}
6766186462Sdougb	isc_task_endexclusive(server->task);
6767135446Strhodes	return (result);
6768135446Strhodes}
6769135446Strhodes
6770135446Strhodesisc_result_t
6771135446Strhodesns_server_flushname(ns_server_t *server, char *args) {
6772135446Strhodes	char *ptr, *target, *viewname;
6773135446Strhodes	dns_view_t *view;
6774174187Sdougb	isc_boolean_t flushed;
6775174187Sdougb	isc_boolean_t found;
6776135446Strhodes	isc_result_t result;
6777135446Strhodes	isc_buffer_t b;
6778135446Strhodes	dns_fixedname_t fixed;
6779135446Strhodes	dns_name_t *name;
6780135446Strhodes
6781135446Strhodes	/* Skip the command name. */
6782135446Strhodes	ptr = next_token(&args, " \t");
6783135446Strhodes	if (ptr == NULL)
6784135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6785135446Strhodes
6786135446Strhodes	/* Find the domain name to flush. */
6787135446Strhodes	target = next_token(&args, " \t");
6788135446Strhodes	if (target == NULL)
6789135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6790135446Strhodes
6791135446Strhodes	isc_buffer_init(&b, target, strlen(target));
6792135446Strhodes	isc_buffer_add(&b, strlen(target));
6793135446Strhodes	dns_fixedname_init(&fixed);
6794135446Strhodes	name = dns_fixedname_name(&fixed);
6795224092Sdougb	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
6796135446Strhodes	if (result != ISC_R_SUCCESS)
6797135446Strhodes		return (result);
6798135446Strhodes
6799135446Strhodes	/* Look for the view name. */
6800135446Strhodes	viewname = next_token(&args, " \t");
6801135446Strhodes
6802135446Strhodes	result = isc_task_beginexclusive(server->task);
6803135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6804135446Strhodes	flushed = ISC_TRUE;
6805174187Sdougb	found = ISC_FALSE;
6806135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
6807135446Strhodes	     view != NULL;
6808135446Strhodes	     view = ISC_LIST_NEXT(view, link))
6809135446Strhodes	{
6810135446Strhodes		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
6811135446Strhodes			continue;
6812174187Sdougb		found = ISC_TRUE;
6813224092Sdougb		/*
6814224092Sdougb		 * It's a little inefficient to try flushing name for all views
6815224092Sdougb		 * if some of the views share a single cache.  But since the
6816224092Sdougb		 * operation is lightweight we prefer simplicity here.
6817224092Sdougb		 */
6818135446Strhodes		result = dns_view_flushname(view, name);
6819193149Sdougb		if (result != ISC_R_SUCCESS) {
6820135446Strhodes			flushed = ISC_FALSE;
6821193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6822193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6823193149Sdougb				      "flushing name '%s' in cache view '%s' "
6824193149Sdougb				      "failed: %s", target, view->name,
6825193149Sdougb				      isc_result_totext(result));
6826193149Sdougb		}
6827135446Strhodes	}
6828193149Sdougb	if (flushed && found) {
6829193149Sdougb		if (viewname != NULL)
6830193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6831193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6832193149Sdougb				      "flushing name '%s' in cache view '%s' "
6833193149Sdougb				      "succeeded", target, viewname);
6834193149Sdougb		else
6835193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6836193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6837193149Sdougb				      "flushing name '%s' in all cache views "
6838193149Sdougb				      "succeeded", target);
6839135446Strhodes		result = ISC_R_SUCCESS;
6840193149Sdougb	} else {
6841193149Sdougb		if (!found)
6842193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6843193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6844193149Sdougb				      "flushing name '%s' in cache view '%s' "
6845193149Sdougb				      "failed: view not found", target,
6846193149Sdougb				      viewname);
6847135446Strhodes		result = ISC_R_FAILURE;
6848193149Sdougb	}
6849186462Sdougb	isc_task_endexclusive(server->task);
6850135446Strhodes	return (result);
6851135446Strhodes}
6852135446Strhodes
6853135446Strhodesisc_result_t
6854135446Strhodesns_server_status(ns_server_t *server, isc_buffer_t *text) {
6855135446Strhodes	int zonecount, xferrunning, xferdeferred, soaqueries;
6856135446Strhodes	unsigned int n;
6857193149Sdougb	const char *ob = "", *cb = "", *alt = "";
6858135446Strhodes
6859193149Sdougb	if (ns_g_server->version_set) {
6860193149Sdougb		ob = " (";
6861193149Sdougb		cb = ")";
6862193149Sdougb		if (ns_g_server->version == NULL)
6863193149Sdougb			alt = "version.bind/txt/ch disabled";
6864193149Sdougb		else
6865193149Sdougb			alt = ns_g_server->version;
6866193149Sdougb	}
6867135446Strhodes	zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY);
6868135446Strhodes	xferrunning = dns_zonemgr_getcount(server->zonemgr,
6869135446Strhodes					   DNS_ZONESTATE_XFERRUNNING);
6870135446Strhodes	xferdeferred = dns_zonemgr_getcount(server->zonemgr,
6871135446Strhodes					    DNS_ZONESTATE_XFERDEFERRED);
6872135446Strhodes	soaqueries = dns_zonemgr_getcount(server->zonemgr,
6873135446Strhodes					  DNS_ZONESTATE_SOAQUERY);
6874193149Sdougb
6875135446Strhodes	n = snprintf((char *)isc_buffer_used(text),
6876135446Strhodes		     isc_buffer_availablelength(text),
6877193149Sdougb		     "version: %s%s%s%s\n"
6878193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
6879193149Sdougb		     "CPUs found: %u\n"
6880193149Sdougb		     "worker threads: %u\n"
6881193149Sdougb#endif
6882135446Strhodes		     "number of zones: %u\n"
6883135446Strhodes		     "debug level: %d\n"
6884135446Strhodes		     "xfers running: %u\n"
6885135446Strhodes		     "xfers deferred: %u\n"
6886135446Strhodes		     "soa queries in progress: %u\n"
6887135446Strhodes		     "query logging is %s\n"
6888170222Sdougb		     "recursive clients: %d/%d/%d\n"
6889135446Strhodes		     "tcp clients: %d/%d\n"
6890135446Strhodes		     "server is up and running",
6891193149Sdougb		     ns_g_version, ob, alt, cb,
6892193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
6893193149Sdougb		     ns_g_cpus_detected, ns_g_cpus,
6894193149Sdougb#endif
6895135446Strhodes		     zonecount, ns_g_debuglevel, xferrunning, xferdeferred,
6896135446Strhodes		     soaqueries, server->log_queries ? "ON" : "OFF",
6897170222Sdougb		     server->recursionquota.used, server->recursionquota.soft,
6898170222Sdougb		     server->recursionquota.max,
6899135446Strhodes		     server->tcpquota.used, server->tcpquota.max);
6900135446Strhodes	if (n >= isc_buffer_availablelength(text))
6901135446Strhodes		return (ISC_R_NOSPACE);
6902135446Strhodes	isc_buffer_add(text, n);
6903135446Strhodes	return (ISC_R_SUCCESS);
6904135446Strhodes}
6905135446Strhodes
6906193149Sdougbstatic isc_result_t
6907193149Sdougbdelete_keynames(dns_tsig_keyring_t *ring, char *target,
6908193149Sdougb		unsigned int *foundkeys)
6909193149Sdougb{
6910193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
6911193149Sdougb	isc_result_t result;
6912193149Sdougb	dns_rbtnodechain_t chain;
6913193149Sdougb	dns_name_t foundname;
6914193149Sdougb	dns_fixedname_t fixedorigin;
6915193149Sdougb	dns_name_t *origin;
6916193149Sdougb	dns_rbtnode_t *node;
6917193149Sdougb	dns_tsigkey_t *tkey;
6918193149Sdougb
6919193149Sdougb	dns_name_init(&foundname, NULL);
6920193149Sdougb	dns_fixedname_init(&fixedorigin);
6921193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
6922193149Sdougb
6923193149Sdougb again:
6924193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
6925193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
6926193149Sdougb					origin);
6927193149Sdougb	if (result == ISC_R_NOTFOUND) {
6928193149Sdougb		dns_rbtnodechain_invalidate(&chain);
6929193149Sdougb		return (ISC_R_SUCCESS);
6930193149Sdougb	}
6931193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
6932193149Sdougb		dns_rbtnodechain_invalidate(&chain);
6933193149Sdougb		return (result);
6934193149Sdougb	}
6935193149Sdougb
6936193149Sdougb	for (;;) {
6937193149Sdougb		node = NULL;
6938193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
6939193149Sdougb		tkey = node->data;
6940193149Sdougb
6941193149Sdougb		if (tkey != NULL) {
6942193149Sdougb			if (!tkey->generated)
6943193149Sdougb				goto nextkey;
6944193149Sdougb
6945193149Sdougb			dns_name_format(&tkey->name, namestr, sizeof(namestr));
6946193149Sdougb			if (strcmp(namestr, target) == 0) {
6947193149Sdougb				(*foundkeys)++;
6948193149Sdougb				dns_rbtnodechain_invalidate(&chain);
6949193149Sdougb				(void)dns_rbt_deletename(ring->keys,
6950193149Sdougb							 &tkey->name,
6951193149Sdougb							 ISC_FALSE);
6952193149Sdougb				goto again;
6953193149Sdougb			}
6954193149Sdougb		}
6955193149Sdougb
6956193149Sdougb	nextkey:
6957193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname, origin);
6958193149Sdougb		if (result == ISC_R_NOMORE)
6959193149Sdougb			break;
6960193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
6961193149Sdougb			dns_rbtnodechain_invalidate(&chain);
6962193149Sdougb			return (result);
6963193149Sdougb		}
6964193149Sdougb	}
6965193149Sdougb
6966193149Sdougb	return (ISC_R_SUCCESS);
6967193149Sdougb}
6968193149Sdougb
6969193149Sdougbisc_result_t
6970193149Sdougbns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) {
6971193149Sdougb	isc_result_t result;
6972193149Sdougb	unsigned int n;
6973193149Sdougb	dns_view_t *view;
6974193149Sdougb	unsigned int foundkeys = 0;
6975193149Sdougb	char *target;
6976193149Sdougb	char *viewname;
6977193149Sdougb
6978193149Sdougb	(void)next_token(&command, " \t");  /* skip command name */
6979193149Sdougb	target = next_token(&command, " \t");
6980193149Sdougb	if (target == NULL)
6981193149Sdougb		return (ISC_R_UNEXPECTEDEND);
6982193149Sdougb	viewname = next_token(&command, " \t");
6983193149Sdougb
6984193149Sdougb	result = isc_task_beginexclusive(server->task);
6985193149Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6986193149Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
6987193149Sdougb	     view != NULL;
6988193149Sdougb	     view = ISC_LIST_NEXT(view, link)) {
6989193149Sdougb		if (viewname == NULL || strcmp(view->name, viewname) == 0) {
6990193149Sdougb			RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write);
6991193149Sdougb			result = delete_keynames(view->dynamickeys, target,
6992193149Sdougb						 &foundkeys);
6993193149Sdougb			RWUNLOCK(&view->dynamickeys->lock,
6994193149Sdougb				 isc_rwlocktype_write);
6995193149Sdougb			if (result != ISC_R_SUCCESS) {
6996193149Sdougb				isc_task_endexclusive(server->task);
6997193149Sdougb				return (result);
6998193149Sdougb			}
6999193149Sdougb		}
7000193149Sdougb	}
7001193149Sdougb	isc_task_endexclusive(server->task);
7002193149Sdougb
7003193149Sdougb	n = snprintf((char *)isc_buffer_used(text),
7004193149Sdougb		     isc_buffer_availablelength(text),
7005193149Sdougb		     "%d tsig keys deleted.\n", foundkeys);
7006218384Sdougb	if (n >= isc_buffer_availablelength(text))
7007193149Sdougb		return (ISC_R_NOSPACE);
7008193149Sdougb	isc_buffer_add(text, n);
7009193149Sdougb
7010193149Sdougb	return (ISC_R_SUCCESS);
7011193149Sdougb}
7012193149Sdougb
7013193149Sdougbstatic isc_result_t
7014193149Sdougblist_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text,
7015193149Sdougb	     unsigned int *foundkeys)
7016193149Sdougb{
7017193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
7018193149Sdougb	char creatorstr[DNS_NAME_FORMATSIZE];
7019193149Sdougb	isc_result_t result;
7020193149Sdougb	dns_rbtnodechain_t chain;
7021193149Sdougb	dns_name_t foundname;
7022193149Sdougb	dns_fixedname_t fixedorigin;
7023193149Sdougb	dns_name_t *origin;
7024193149Sdougb	dns_rbtnode_t *node;
7025193149Sdougb	dns_tsigkey_t *tkey;
7026193149Sdougb	unsigned int n;
7027193149Sdougb	const char *viewname;
7028193149Sdougb
7029193149Sdougb	if (view != NULL)
7030193149Sdougb		viewname = view->name;
7031193149Sdougb	else
7032193149Sdougb		viewname = "(global)";
7033193149Sdougb
7034193149Sdougb	dns_name_init(&foundname, NULL);
7035193149Sdougb	dns_fixedname_init(&fixedorigin);
7036193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
7037193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
7038193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
7039193149Sdougb					origin);
7040193149Sdougb	if (result == ISC_R_NOTFOUND) {
7041193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7042193149Sdougb		return (ISC_R_SUCCESS);
7043193149Sdougb	}
7044193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7045193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7046193149Sdougb		return (result);
7047193149Sdougb	}
7048193149Sdougb
7049193149Sdougb	for (;;) {
7050193149Sdougb		node = NULL;
7051193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
7052193149Sdougb		tkey = node->data;
7053193149Sdougb
7054193149Sdougb		if (tkey != NULL) {
7055193149Sdougb			(*foundkeys)++;
7056193149Sdougb			dns_name_format(&tkey->name, namestr, sizeof(namestr));
7057193149Sdougb			if (tkey->generated) {
7058193149Sdougb				dns_name_format(tkey->creator, creatorstr,
7059193149Sdougb						sizeof(creatorstr));
7060193149Sdougb				n = snprintf((char *)isc_buffer_used(text),
7061193149Sdougb					     isc_buffer_availablelength(text),
7062193149Sdougb					     "view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n",
7063193149Sdougb					     viewname, namestr, creatorstr);
7064193149Sdougb			} else {
7065193149Sdougb				n = snprintf((char *)isc_buffer_used(text),
7066193149Sdougb					     isc_buffer_availablelength(text),
7067193149Sdougb					     "view \"%s\"; type \"static\"; key \"%s\";\n",
7068193149Sdougb					     viewname, namestr);
7069193149Sdougb			}
7070193149Sdougb			if (n >= isc_buffer_availablelength(text)) {
7071193149Sdougb				dns_rbtnodechain_invalidate(&chain);
7072193149Sdougb				return (ISC_R_NOSPACE);
7073193149Sdougb			}
7074193149Sdougb			isc_buffer_add(text, n);
7075193149Sdougb		}
7076193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname, origin);
7077193149Sdougb		if (result == ISC_R_NOMORE)
7078193149Sdougb			break;
7079193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7080193149Sdougb			dns_rbtnodechain_invalidate(&chain);
7081193149Sdougb			return (result);
7082193149Sdougb		}
7083193149Sdougb	}
7084193149Sdougb
7085193149Sdougb	return (ISC_R_SUCCESS);
7086193149Sdougb}
7087193149Sdougb
7088193149Sdougbisc_result_t
7089193149Sdougbns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) {
7090193149Sdougb	isc_result_t result;
7091193149Sdougb	unsigned int n;
7092193149Sdougb	dns_view_t *view;
7093193149Sdougb	unsigned int foundkeys = 0;
7094193149Sdougb
7095193149Sdougb	result = isc_task_beginexclusive(server->task);
7096193149Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7097193149Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7098193149Sdougb	     view != NULL;
7099193149Sdougb	     view = ISC_LIST_NEXT(view, link)) {
7100193149Sdougb		RWLOCK(&view->statickeys->lock, isc_rwlocktype_read);
7101193149Sdougb		result = list_keynames(view, view->statickeys, text,
7102193149Sdougb				       &foundkeys);
7103193149Sdougb		RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read);
7104193149Sdougb		if (result != ISC_R_SUCCESS) {
7105193149Sdougb			isc_task_endexclusive(server->task);
7106193149Sdougb			return (result);
7107193149Sdougb		}
7108193149Sdougb		RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
7109193149Sdougb		result = list_keynames(view, view->dynamickeys, text,
7110193149Sdougb				       &foundkeys);
7111193149Sdougb		RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
7112193149Sdougb		if (result != ISC_R_SUCCESS) {
7113193149Sdougb			isc_task_endexclusive(server->task);
7114193149Sdougb			return (result);
7115193149Sdougb		}
7116193149Sdougb	}
7117193149Sdougb	isc_task_endexclusive(server->task);
7118193149Sdougb
7119193149Sdougb	if (foundkeys == 0) {
7120193149Sdougb		n = snprintf((char *)isc_buffer_used(text),
7121193149Sdougb			     isc_buffer_availablelength(text),
7122193149Sdougb			     "no tsig keys found.\n");
7123218384Sdougb		if (n >= isc_buffer_availablelength(text))
7124193149Sdougb			return (ISC_R_NOSPACE);
7125193149Sdougb		isc_buffer_add(text, n);
7126193149Sdougb	}
7127193149Sdougb
7128193149Sdougb	return (ISC_R_SUCCESS);
7129193149Sdougb}
7130193149Sdougb
7131135446Strhodes/*
7132224092Sdougb * Act on a "sign" or "loadkeys" command from the command channel.
7133224092Sdougb */
7134224092Sdougbisc_result_t
7135224092Sdougbns_server_rekey(ns_server_t *server, char *args) {
7136224092Sdougb	isc_result_t result;
7137224092Sdougb	dns_zone_t *zone = NULL;
7138224092Sdougb	dns_zonetype_t type;
7139224092Sdougb	isc_uint16_t keyopts;
7140224092Sdougb	isc_boolean_t fullsign = ISC_FALSE;
7141224092Sdougb
7142224092Sdougb	if (strncasecmp(args, NS_COMMAND_SIGN, strlen(NS_COMMAND_SIGN)) == 0)
7143224092Sdougb	    fullsign = ISC_TRUE;
7144224092Sdougb
7145224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
7146224092Sdougb	if (result != ISC_R_SUCCESS)
7147224092Sdougb		return (result);
7148224092Sdougb	if (zone == NULL)
7149224092Sdougb		return (ISC_R_UNEXPECTEDEND);   /* XXX: or do all zones? */
7150224092Sdougb
7151224092Sdougb	type = dns_zone_gettype(zone);
7152224092Sdougb	if (type != dns_zone_master) {
7153224092Sdougb		dns_zone_detach(&zone);
7154224092Sdougb		return (DNS_R_NOTMASTER);
7155224092Sdougb	}
7156224092Sdougb
7157224092Sdougb	keyopts = dns_zone_getkeyopts(zone);
7158224092Sdougb
7159224092Sdougb	/* "rndc loadkeys" requires "auto-dnssec maintain". */
7160224092Sdougb	if ((keyopts & DNS_ZONEKEY_ALLOW) == 0)
7161224092Sdougb		result = ISC_R_NOPERM;
7162224092Sdougb	else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign)
7163224092Sdougb		result = ISC_R_NOPERM;
7164224092Sdougb	else
7165224092Sdougb		dns_zone_rekey(zone, fullsign);
7166224092Sdougb
7167224092Sdougb	dns_zone_detach(&zone);
7168224092Sdougb	return (result);
7169224092Sdougb}
7170224092Sdougb
7171224092Sdougb/*
7172170222Sdougb * Act on a "freeze" or "thaw" command from the command channel.
7173135446Strhodes */
7174135446Strhodesisc_result_t
7175204619Sdougbns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args,
7176204619Sdougb		 isc_buffer_t *text)
7177204619Sdougb{
7178170222Sdougb	isc_result_t result, tresult;
7179135446Strhodes	dns_zone_t *zone = NULL;
7180135446Strhodes	dns_zonetype_t type;
7181135446Strhodes	char classstr[DNS_RDATACLASS_FORMATSIZE];
7182135446Strhodes	char zonename[DNS_NAME_FORMATSIZE];
7183135446Strhodes	dns_view_t *view;
7184135446Strhodes	char *journal;
7185135446Strhodes	const char *vname, *sep;
7186135446Strhodes	isc_boolean_t frozen;
7187204619Sdougb	const char *msg = NULL;
7188186462Sdougb
7189224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
7190135446Strhodes	if (result != ISC_R_SUCCESS)
7191135446Strhodes		return (result);
7192170222Sdougb	if (zone == NULL) {
7193170222Sdougb		result = isc_task_beginexclusive(server->task);
7194170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
7195170222Sdougb		tresult = ISC_R_SUCCESS;
7196186462Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
7197170222Sdougb		     view != NULL;
7198170222Sdougb		     view = ISC_LIST_NEXT(view, link)) {
7199170222Sdougb			result = dns_view_freezezones(view, freeze);
7200170222Sdougb			if (result != ISC_R_SUCCESS &&
7201170222Sdougb			    tresult == ISC_R_SUCCESS)
7202170222Sdougb				tresult = result;
7203170222Sdougb		}
7204170222Sdougb		isc_task_endexclusive(server->task);
7205170222Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7206170222Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7207170222Sdougb			      "%s all zones: %s",
7208170222Sdougb			      freeze ? "freezing" : "thawing",
7209170222Sdougb			      isc_result_totext(tresult));
7210170222Sdougb		return (tresult);
7211170222Sdougb	}
7212135446Strhodes	type = dns_zone_gettype(zone);
7213135446Strhodes	if (type != dns_zone_master) {
7214135446Strhodes		dns_zone_detach(&zone);
7215224092Sdougb		return (DNS_R_NOTMASTER);
7216135446Strhodes	}
7217135446Strhodes
7218204619Sdougb	result = isc_task_beginexclusive(server->task);
7219204619Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7220135446Strhodes	frozen = dns_zone_getupdatedisabled(zone);
7221135446Strhodes	if (freeze) {
7222204619Sdougb		if (frozen) {
7223204619Sdougb			msg = "WARNING: The zone was already frozen.\n"
7224204619Sdougb			      "Someone else may be editing it or "
7225204619Sdougb			      "it may still be re-loading.";
7226135446Strhodes			result = DNS_R_FROZEN;
7227204619Sdougb		}
7228204619Sdougb		if (result == ISC_R_SUCCESS) {
7229135446Strhodes			result = dns_zone_flush(zone);
7230204619Sdougb			if (result != ISC_R_SUCCESS)
7231204619Sdougb				msg = "Flushing the zone updates to "
7232204619Sdougb				      "disk failed.";
7233204619Sdougb		}
7234135446Strhodes		if (result == ISC_R_SUCCESS) {
7235135446Strhodes			journal = dns_zone_getjournal(zone);
7236135446Strhodes			if (journal != NULL)
7237135446Strhodes				(void)isc_file_remove(journal);
7238135446Strhodes		}
7239204619Sdougb		if (result == ISC_R_SUCCESS)
7240204619Sdougb			dns_zone_setupdatedisabled(zone, freeze);
7241135446Strhodes	} else {
7242135446Strhodes		if (frozen) {
7243204619Sdougb			result = dns_zone_loadandthaw(zone);
7244204619Sdougb			switch (result) {
7245204619Sdougb			case ISC_R_SUCCESS:
7246204619Sdougb			case DNS_R_UPTODATE:
7247204619Sdougb				msg = "The zone reload and thaw was "
7248204619Sdougb				      "successful.";
7249135446Strhodes				result = ISC_R_SUCCESS;
7250204619Sdougb				break;
7251204619Sdougb			case DNS_R_CONTINUE:
7252204619Sdougb				msg = "A zone reload and thaw was started.\n"
7253204619Sdougb				      "Check the logs to see the result.";
7254204619Sdougb				result = ISC_R_SUCCESS;
7255204619Sdougb				break;
7256204619Sdougb			}
7257135446Strhodes		}
7258135446Strhodes	}
7259204619Sdougb	isc_task_endexclusive(server->task);
7260135446Strhodes
7261204619Sdougb	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
7262204619Sdougb		isc_buffer_putmem(text, (const unsigned char *)msg,
7263204619Sdougb				  strlen(msg) + 1);
7264204619Sdougb
7265135446Strhodes	view = dns_zone_getview(zone);
7266224092Sdougb	if (strcmp(view->name, "_default") == 0 ||
7267224092Sdougb	    strcmp(view->name, "_bind") == 0)
7268135446Strhodes	{
7269135446Strhodes		vname = "";
7270135446Strhodes		sep = "";
7271135446Strhodes	} else {
7272135446Strhodes		vname = view->name;
7273135446Strhodes		sep = " ";
7274135446Strhodes	}
7275135446Strhodes	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
7276135446Strhodes			      sizeof(classstr));
7277135446Strhodes	dns_name_format(dns_zone_getorigin(zone),
7278135446Strhodes			zonename, sizeof(zonename));
7279135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7280135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7281135446Strhodes		      "%s zone '%s/%s'%s%s: %s",
7282170222Sdougb		      freeze ? "freezing" : "thawing",
7283135446Strhodes		      zonename, classstr, sep, vname,
7284135446Strhodes		      isc_result_totext(result));
7285135446Strhodes	dns_zone_detach(&zone);
7286135446Strhodes	return (result);
7287135446Strhodes}
7288153816Sdougb
7289153816Sdougb#ifdef HAVE_LIBSCF
7290153816Sdougb/*
7291153816Sdougb * This function adds a message for rndc to echo if named
7292153816Sdougb * is managed by smf and is also running chroot.
7293153816Sdougb */
7294153816Sdougbisc_result_t
7295153816Sdougbns_smf_add_message(isc_buffer_t *text) {
7296153816Sdougb	unsigned int n;
7297153816Sdougb
7298153816Sdougb	n = snprintf((char *)isc_buffer_used(text),
7299153816Sdougb		isc_buffer_availablelength(text),
7300153816Sdougb		"use svcadm(1M) to manage named");
7301153816Sdougb	if (n >= isc_buffer_availablelength(text))
7302153816Sdougb		return (ISC_R_NOSPACE);
7303153816Sdougb	isc_buffer_add(text, n);
7304153816Sdougb	return (ISC_R_SUCCESS);
7305153816Sdougb}
7306153816Sdougb#endif /* HAVE_LIBSCF */
7307224092Sdougb
7308224092Sdougb/*
7309224092Sdougb * Act on an "addzone" command from the command channel.
7310224092Sdougb */
7311224092Sdougbisc_result_t
7312224092Sdougbns_server_add_zone(ns_server_t *server, char *args) {
7313224092Sdougb	isc_result_t	     result;
7314224092Sdougb	isc_buffer_t	     argbuf;
7315224092Sdougb	size_t		     arglen;
7316224092Sdougb	cfg_parser_t	    *parser = NULL;
7317224092Sdougb	cfg_obj_t	    *config = NULL;
7318224092Sdougb	const cfg_obj_t	    *vconfig = NULL;
7319224092Sdougb	const cfg_obj_t	    *views = NULL;
7320224092Sdougb	const cfg_obj_t     *parms = NULL;
7321224092Sdougb	const cfg_obj_t     *obj = NULL;
7322224092Sdougb	const cfg_listelt_t *element;
7323224092Sdougb	const char	    *zonename;
7324224092Sdougb	const char	    *classname = NULL;
7325224092Sdougb	const char	    *argp;
7326224092Sdougb	const char	    *viewname = NULL;
7327224092Sdougb	dns_rdataclass_t     rdclass;
7328224092Sdougb	dns_view_t	    *view = 0;
7329224092Sdougb	isc_buffer_t	     buf, *nbuf = NULL;
7330224092Sdougb	dns_name_t	     dnsname;
7331224092Sdougb	dns_zone_t	    *zone = NULL;
7332224092Sdougb	FILE		    *fp = NULL;
7333224092Sdougb	struct cfg_context  *cfg = NULL;
7334224092Sdougb
7335224092Sdougb	/* Try to parse the argument string */
7336224092Sdougb	arglen = strlen(args);
7337224092Sdougb	isc_buffer_init(&argbuf, args, arglen);
7338224092Sdougb	isc_buffer_add(&argbuf, strlen(args));
7339224092Sdougb	CHECK(cfg_parser_create(server->mctx, ns_g_lctx, &parser));
7340224092Sdougb	CHECK(cfg_parse_buffer(parser, &argbuf, &cfg_type_addzoneconf,
7341224092Sdougb			       &config));
7342224092Sdougb	CHECK(cfg_map_get(config, "addzone", &parms));
7343224092Sdougb
7344224092Sdougb	zonename = cfg_obj_asstring(cfg_tuple_get(parms, "name"));
7345224092Sdougb	isc_buffer_init(&buf, zonename, strlen(zonename));
7346224092Sdougb	isc_buffer_add(&buf, strlen(zonename));
7347224092Sdougb	dns_name_init(&dnsname, NULL);
7348224092Sdougb	isc_buffer_allocate(server->mctx, &nbuf, 256);
7349224092Sdougb	dns_name_setbuffer(&dnsname, nbuf);
7350224092Sdougb	CHECK(dns_name_fromtext(&dnsname, &buf, dns_rootname, ISC_FALSE, NULL));
7351224092Sdougb
7352224092Sdougb	/* Make sense of optional class argument */
7353224092Sdougb	obj = cfg_tuple_get(parms, "class");
7354224092Sdougb	CHECK(ns_config_getclass(obj, dns_rdataclass_in, &rdclass));
7355224092Sdougb	if (rdclass != dns_rdataclass_in && obj)
7356224092Sdougb		classname = cfg_obj_asstring(obj);
7357224092Sdougb
7358224092Sdougb	/* Make sense of optional view argument */
7359224092Sdougb	obj = cfg_tuple_get(parms, "view");
7360224092Sdougb	if (obj && cfg_obj_isstring(obj))
7361224092Sdougb		viewname = cfg_obj_asstring(obj);
7362224092Sdougb	if (viewname == NULL || *viewname == '\0')
7363224092Sdougb		viewname = "_default";
7364224092Sdougb	CHECK(dns_viewlist_find(&server->viewlist, viewname, rdclass, &view));
7365224092Sdougb
7366224092Sdougb	/* Are we accepting new zones? */
7367224092Sdougb	if (view->new_zone_file == NULL) {
7368224092Sdougb		result = ISC_R_NOPERM;
7369224092Sdougb		goto cleanup;
7370224092Sdougb	}
7371224092Sdougb
7372224092Sdougb	cfg = (struct cfg_context *) view->new_zone_config;
7373224092Sdougb	if (cfg == NULL) {
7374224092Sdougb		result = ISC_R_FAILURE;
7375224092Sdougb		goto cleanup;
7376224092Sdougb	}
7377224092Sdougb
7378224092Sdougb	/* Zone shouldn't already exist */
7379224092Sdougb	result = dns_zt_find(view->zonetable, &dnsname, 0, NULL, &zone);
7380224092Sdougb	if (result == ISC_R_SUCCESS) {
7381224092Sdougb		result = ISC_R_EXISTS;
7382224092Sdougb		goto cleanup;
7383224092Sdougb	} else if (result == DNS_R_PARTIALMATCH) {
7384224092Sdougb		/* Create our sub-zone anyway */
7385224092Sdougb		dns_zone_detach(&zone);
7386224092Sdougb		zone = NULL;
7387224092Sdougb	}
7388224092Sdougb	else if (result != ISC_R_NOTFOUND)
7389224092Sdougb		goto cleanup;
7390224092Sdougb
7391224092Sdougb	/* Find the view statement */
7392224092Sdougb	cfg_map_get(cfg->config, "view", &views);
7393224092Sdougb	for (element = cfg_list_first(views);
7394224092Sdougb	     element != NULL;
7395224092Sdougb	     element = cfg_list_next(element))
7396224092Sdougb	{
7397224092Sdougb		const char *vname;
7398224092Sdougb		vconfig = cfg_listelt_value(element);
7399224092Sdougb		vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
7400224092Sdougb		if (vname && !strcasecmp(vname, viewname))
7401224092Sdougb			break;
7402224092Sdougb		vconfig = NULL;
7403224092Sdougb	}
7404224092Sdougb
7405224092Sdougb	/* Open save file for write configuration */
7406224092Sdougb	CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
7407224092Sdougb
7408224092Sdougb	/* Mark view unfrozen so that zone can be added */
7409234010Sdougb	isc_task_beginexclusive(server->task);
7410224092Sdougb	dns_view_thaw(view);
7411224092Sdougb	result = configure_zone(cfg->config, parms, vconfig,
7412225361Sdougb				server->mctx, view, cfg->actx, ISC_FALSE);
7413224092Sdougb	dns_view_freeze(view);
7414234010Sdougb	isc_task_endexclusive(server->task);
7415234010Sdougb	if (result != ISC_R_SUCCESS)
7416224092Sdougb		goto cleanup;
7417224092Sdougb
7418224092Sdougb	/* Is it there yet? */
7419224092Sdougb	CHECK(dns_zt_find(view->zonetable, &dnsname, 0, NULL, &zone));
7420224092Sdougb
7421224092Sdougb	/*
7422224092Sdougb	 * Load the zone from the master file.  If this fails, we'll
7423224092Sdougb	 * need to undo the configuration we've done already.
7424224092Sdougb	 */
7425224092Sdougb	result = dns_zone_loadnew(zone);
7426224092Sdougb	if (result != ISC_R_SUCCESS) {
7427224092Sdougb		dns_db_t *dbp = NULL;
7428224092Sdougb
7429224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7430224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7431224092Sdougb			      "addzone failed; reverting.");
7432224092Sdougb
7433224092Sdougb		/* If the zone loaded partially, unload it */
7434224092Sdougb		if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
7435224092Sdougb			dns_db_detach(&dbp);
7436224092Sdougb			dns_zone_unload(zone);
7437224092Sdougb		}
7438224092Sdougb
7439224092Sdougb		/* Remove the zone from the zone table */
7440224092Sdougb		dns_zt_unmount(view->zonetable, zone);
7441224092Sdougb		goto cleanup;
7442224092Sdougb	}
7443224092Sdougb
7444224092Sdougb	/* Flag the zone as having been added at runtime */
7445224092Sdougb	dns_zone_setadded(zone, ISC_TRUE);
7446224092Sdougb
7447224092Sdougb	/* Emit just the zone name from args */
7448224092Sdougb	CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
7449224092Sdougb	CHECK(isc_stdio_write(zonename, strlen(zonename), 1, fp, NULL));
7450224092Sdougb	CHECK(isc_stdio_write(" ", 1, 1, fp, NULL));
7451224092Sdougb
7452224092Sdougb	/* Classname, if not default */
7453224092Sdougb	if (classname != NULL && *classname != '\0') {
7454224092Sdougb		CHECK(isc_stdio_write(classname, strlen(classname), 1, fp,
7455224092Sdougb				      NULL));
7456224092Sdougb		CHECK(isc_stdio_write(" ", 1, 1, fp, NULL));
7457224092Sdougb	}
7458224092Sdougb
7459224092Sdougb	/* Find beginning of option block from args */
7460224092Sdougb	for (argp = args; *argp; argp++, arglen--) {
7461224092Sdougb		if (*argp == '{') {	/* Assume matching '}' */
7462224092Sdougb			/* Add that to our file */
7463224092Sdougb			CHECK(isc_stdio_write(argp, arglen, 1, fp, NULL));
7464224092Sdougb
7465224092Sdougb			/* Make sure we end with a LF */
7466224092Sdougb			if (argp[arglen-1] != '\n') {
7467224092Sdougb				CHECK(isc_stdio_write("\n", 1, 1, fp, NULL));
7468224092Sdougb			}
7469224092Sdougb			break;
7470224092Sdougb		}
7471224092Sdougb	}
7472224092Sdougb
7473224092Sdougb	CHECK(isc_stdio_close(fp));
7474224092Sdougb	fp = NULL;
7475224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7476224092Sdougb				  NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7477224092Sdougb				  "zone %s added to view %s via addzone",
7478224092Sdougb				  zonename, viewname);
7479224092Sdougb
7480224092Sdougb	result = ISC_R_SUCCESS;
7481224092Sdougb
7482224092Sdougb cleanup:
7483224092Sdougb	if (fp != NULL)
7484224092Sdougb		isc_stdio_close(fp);
7485224092Sdougb	if (parser != NULL) {
7486224092Sdougb		if (config != NULL)
7487224092Sdougb			cfg_obj_destroy(parser, &config);
7488224092Sdougb		cfg_parser_destroy(&parser);
7489224092Sdougb	}
7490224092Sdougb	if (zone != NULL)
7491224092Sdougb		dns_zone_detach(&zone);
7492224092Sdougb	if (view != NULL)
7493224092Sdougb		dns_view_detach(&view);
7494224092Sdougb	if (nbuf != NULL)
7495224092Sdougb		isc_buffer_free(&nbuf);
7496224092Sdougb
7497224092Sdougb	return (result);
7498224092Sdougb}
7499224092Sdougb
7500224092Sdougb/*
7501224092Sdougb * Act on a "delzone" command from the command channel.
7502224092Sdougb */
7503224092Sdougbisc_result_t
7504224092Sdougbns_server_del_zone(ns_server_t *server, char *args) {
7505224092Sdougb	isc_result_t	       result;
7506224092Sdougb	dns_zone_t	      *zone = NULL;
7507224092Sdougb	dns_view_t	      *view = NULL;
7508224092Sdougb	dns_db_t	      *dbp = NULL;
7509224092Sdougb	const char	      *filename = NULL;
7510224092Sdougb	char		      *tmpname = NULL;
7511224092Sdougb	char		       buf[1024];
7512224092Sdougb	const char	      *zonename = NULL;
7513224092Sdougb	size_t		       znamelen = 0;
7514224092Sdougb	FILE		      *ifp = NULL, *ofp = NULL;
7515224092Sdougb
7516224092Sdougb	/* Parse parameters */
7517224092Sdougb	CHECK(zone_from_args(server, args, &zone, &zonename));
7518224092Sdougb	if (result != ISC_R_SUCCESS)
7519224092Sdougb		return (result);
7520224092Sdougb	if (zone == NULL) {
7521224092Sdougb		result = ISC_R_UNEXPECTEDEND;
7522224092Sdougb		goto cleanup;
7523224092Sdougb	}
7524224092Sdougb
7525224092Sdougb	/*
7526224092Sdougb	 * Was this zone originally added at runtime?
7527224092Sdougb	 * If not, we can't delete it now.
7528224092Sdougb	 */
7529224092Sdougb	if (!dns_zone_getadded(zone)) {
7530224092Sdougb		result = ISC_R_NOPERM;
7531224092Sdougb		goto cleanup;
7532224092Sdougb	}
7533224092Sdougb
7534224092Sdougb	if (zonename != NULL)
7535224092Sdougb		znamelen = strlen(zonename);
7536224092Sdougb
7537224092Sdougb	/* Dig out configuration for this zone */
7538224092Sdougb	view = dns_zone_getview(zone);
7539224092Sdougb	filename = view->new_zone_file;
7540224092Sdougb	if (filename == NULL) {
7541224092Sdougb		/* No adding zones in this view */
7542224092Sdougb		result = ISC_R_FAILURE;
7543224092Sdougb		goto cleanup;
7544224092Sdougb	}
7545224092Sdougb
7546224092Sdougb	/* Rewrite zone list */
7547224092Sdougb	result = isc_stdio_open(filename, "r", &ifp);
7548224092Sdougb	if (ifp != NULL && result == ISC_R_SUCCESS) {
7549224092Sdougb		char *found = NULL, *p = NULL;
7550224092Sdougb		size_t n;
7551224092Sdougb
7552224092Sdougb		/* Create a temporary file */
7553224092Sdougb		CHECK(isc_string_printf(buf, 1023, "%s.%ld", filename,
7554224092Sdougb					(long)getpid()));
7555224092Sdougb		if (!(tmpname = isc_mem_strdup(server->mctx, buf))) {
7556224092Sdougb			result = ISC_R_NOMEMORY;
7557224092Sdougb			goto cleanup;
7558224092Sdougb		}
7559224092Sdougb		CHECK(isc_stdio_open(tmpname, "w", &ofp));
7560224092Sdougb
7561224092Sdougb		/* Look for the entry for that zone */
7562224092Sdougb		while (fgets(buf, 1024, ifp)) {
7563224092Sdougb			/* A 'zone' line */
7564224092Sdougb			if (strncasecmp(buf, "zone", 4)) {
7565224092Sdougb				fputs(buf, ofp);
7566224092Sdougb				continue;
7567224092Sdougb			}
7568224092Sdougb			p = buf+4;
7569224092Sdougb
7570224092Sdougb			/* Locate a name */
7571224092Sdougb			while (*p &&
7572224092Sdougb			       ((*p == '"') || isspace((unsigned char)*p)))
7573224092Sdougb				p++;
7574224092Sdougb
7575224092Sdougb			/* Is that the zone we're looking for */
7576224092Sdougb			if (strncasecmp(p, zonename, znamelen)) {
7577224092Sdougb				fputs(buf, ofp);
7578224092Sdougb				continue;
7579224092Sdougb			}
7580224092Sdougb
7581224092Sdougb			/* And nothing else? */
7582224092Sdougb			p += znamelen;
7583224092Sdougb			if (isspace((unsigned char)*p) ||
7584224092Sdougb			    *p == '"' || *p == '{') {
7585224092Sdougb				/* This must be the entry */
7586224092Sdougb				found = p;
7587224092Sdougb				break;
7588224092Sdougb			}
7589224092Sdougb
7590224092Sdougb			/* Spit it out, keep looking */
7591224092Sdougb			fputs(buf, ofp);
7592224092Sdougb		}
7593224092Sdougb
7594224092Sdougb		/* Skip over an option block (matching # of braces) */
7595224092Sdougb		if (found) {
7596224092Sdougb			int obrace = 0, cbrace = 0;
7597224092Sdougb			for (;;) {
7598224092Sdougb				while (*p) {
7599224092Sdougb					if (*p == '{') obrace++;
7600224092Sdougb					if (*p == '}') cbrace++;
7601224092Sdougb					p++;
7602224092Sdougb				}
7603224092Sdougb				if (obrace && (obrace == cbrace))
7604224092Sdougb					break;
7605224092Sdougb				if (!fgets(buf, 1024, ifp))
7606224092Sdougb					break;
7607224092Sdougb				p = buf;
7608224092Sdougb			}
7609224092Sdougb
7610224092Sdougb			/* Just spool the remainder of the file out */
7611224092Sdougb			result = isc_stdio_read(buf, 1, 1024, ifp, &n);
7612224092Sdougb			while (n > 0U) {
7613224092Sdougb				if (result == ISC_R_EOF)
7614224092Sdougb					result = ISC_R_SUCCESS;
7615224092Sdougb				CHECK(result);
7616224092Sdougb				isc_stdio_write(buf, 1, n, ofp, NULL);
7617224092Sdougb				result = isc_stdio_read(buf, 1, 1024, ifp, &n);
7618224092Sdougb			}
7619224092Sdougb
7620224092Sdougb			/* Move temporary into place */
7621224092Sdougb			CHECK(isc_file_rename(tmpname, view->new_zone_file));
7622224092Sdougb		} else {
7623224092Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7624224092Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
7625224092Sdougb				      "deleted zone %s was missing from "
7626224092Sdougb				      "new zone file", zonename);
7627224092Sdougb			goto cleanup;
7628224092Sdougb		}
7629224092Sdougb	}
7630224092Sdougb
7631224092Sdougb	/* Stop answering for this zone */
7632224092Sdougb	if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
7633224092Sdougb		dns_db_detach(&dbp);
7634224092Sdougb		dns_zone_unload(zone);
7635224092Sdougb	}
7636224092Sdougb
7637224092Sdougb	CHECK(dns_zt_unmount(view->zonetable, zone));
7638224092Sdougb
7639224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7640224092Sdougb				  NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7641224092Sdougb				  "zone %s removed via delzone", zonename);
7642224092Sdougb
7643224092Sdougb	result = ISC_R_SUCCESS;
7644224092Sdougb
7645224092Sdougb cleanup:
7646224092Sdougb	if (ifp != NULL)
7647224092Sdougb		isc_stdio_close(ifp);
7648224092Sdougb	if (ofp != NULL) {
7649224092Sdougb		isc_stdio_close(ofp);
7650224092Sdougb		isc_file_remove(tmpname);
7651224092Sdougb	}
7652224092Sdougb	if (tmpname != NULL)
7653224092Sdougb		isc_mem_free(server->mctx, tmpname);
7654224092Sdougb	if (zone != NULL)
7655224092Sdougb		dns_zone_detach(&zone);
7656224092Sdougb
7657224092Sdougb	return (result);
7658224092Sdougb}
7659224092Sdougb
7660224092Sdougbstatic void
7661225361Sdougbnewzone_cfgctx_destroy(void **cfgp) {
7662224092Sdougb	struct cfg_context *cfg;
7663224092Sdougb
7664224092Sdougb	REQUIRE(cfgp != NULL && *cfgp != NULL);
7665225361Sdougb
7666224092Sdougb	cfg = *cfgp;
7667224092Sdougb
7668225361Sdougb	if (cfg->actx != NULL)
7669225361Sdougb		cfg_aclconfctx_detach(&cfg->actx);
7670225361Sdougb
7671224092Sdougb	if (cfg->parser != NULL) {
7672224092Sdougb		if (cfg->config != NULL)
7673224092Sdougb			cfg_obj_destroy(cfg->parser, &cfg->config);
7674224092Sdougb		cfg_parser_destroy(&cfg->parser);
7675224092Sdougb	}
7676225361Sdougb	if (cfg->nzparser != NULL) {
7677225361Sdougb		if (cfg->nzconfig != NULL)
7678225361Sdougb			cfg_obj_destroy(cfg->nzparser, &cfg->nzconfig);
7679225361Sdougb		cfg_parser_destroy(&cfg->nzparser);
7680225361Sdougb	}
7681224092Sdougb
7682225361Sdougb	isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg));
7683224092Sdougb	*cfgp = NULL;
7684224092Sdougb}
7685