server.c revision 234010
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.")];
1361224092Sdougb	const char *dns64_dbtype[4] = { "_builtin", "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
1433224092Sdougbconfigure_rpz(dns_view_t *view, const cfg_listelt_t *element) {
1434224092Sdougb	const cfg_obj_t *rpz_obj, *policy_obj;
1435224092Sdougb	const char *str;
1436224092Sdougb	dns_fixedname_t fixed;
1437224092Sdougb	dns_name_t *origin;
1438224092Sdougb	dns_rpz_zone_t *old, *new;
1439224092Sdougb	dns_zone_t *zone = NULL;
1440224092Sdougb	isc_result_t result;
1441224092Sdougb	unsigned int l1, l2;
1442224092Sdougb
1443224092Sdougb	new = isc_mem_get(view->mctx, sizeof(*new));
1444224092Sdougb	if (new == NULL) {
1445224092Sdougb		result = ISC_R_NOMEMORY;
1446224092Sdougb		goto cleanup;
1447224092Sdougb	}
1448224092Sdougb
1449224092Sdougb	memset(new, 0, sizeof(*new));
1450224092Sdougb	dns_name_init(&new->nsdname, NULL);
1451224092Sdougb	dns_name_init(&new->origin, NULL);
1452224092Sdougb	dns_name_init(&new->cname, 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 {
1460224092Sdougb		str = cfg_obj_asstring(policy_obj);
1461224092Sdougb		new->policy = dns_rpz_str2policy(str);
1462224092Sdougb		INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
1463224092Sdougb	}
1464224092Sdougb
1465224092Sdougb	dns_fixedname_init(&fixed);
1466224092Sdougb	origin = dns_fixedname_name(&fixed);
1467224092Sdougb	str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "name"));
1468224092Sdougb	result = dns_name_fromstring(origin, str, DNS_NAME_DOWNCASE, NULL);
1469224092Sdougb	if (result != ISC_R_SUCCESS) {
1470224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1471224092Sdougb			    "invalid zone '%s'", str);
1472224092Sdougb		goto cleanup;
1473224092Sdougb	}
1474224092Sdougb
1475224092Sdougb	result = dns_name_fromstring2(&new->nsdname, DNS_RPZ_NSDNAME_ZONE,
1476224092Sdougb				      origin, DNS_NAME_DOWNCASE, view->mctx);
1477224092Sdougb	if (result != ISC_R_SUCCESS) {
1478224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1479224092Sdougb			    "invalid zone '%s'", str);
1480224092Sdougb		goto cleanup;
1481224092Sdougb	}
1482224092Sdougb
1483224092Sdougb	/*
1484224092Sdougb	 * The origin is part of 'nsdname' so we don't need to keep it
1485224092Sdougb	 * seperately.
1486224092Sdougb	 */
1487224092Sdougb	l1 = dns_name_countlabels(&new->nsdname);
1488224092Sdougb	l2 = dns_name_countlabels(origin);
1489224092Sdougb	dns_name_getlabelsequence(&new->nsdname, l1 - l2, l2, &new->origin);
1490224092Sdougb
1491224092Sdougb	/*
1492224092Sdougb	 * Are we configured to with the reponse policy zone?
1493224092Sdougb	 */
1494224092Sdougb	result = dns_view_findzone(view, &new->origin, &zone);
1495224092Sdougb	if (result != ISC_R_SUCCESS) {
1496224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1497224092Sdougb			    "unknown zone '%s'", str);
1498224092Sdougb		goto cleanup;
1499224092Sdougb	}
1500224092Sdougb
1501224092Sdougb	if (dns_zone_gettype(zone) != dns_zone_master &&
1502224092Sdougb	    dns_zone_gettype(zone) != dns_zone_slave) {
1503224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1504224092Sdougb			     "zone '%s' is neither master nor slave", str);
1505224092Sdougb		dns_zone_detach(&zone);
1506224092Sdougb		result = DNS_R_NOTMASTER;
1507224092Sdougb		goto cleanup;
1508224092Sdougb	}
1509224092Sdougb	dns_zone_detach(&zone);
1510224092Sdougb
1511224092Sdougb	for (old = ISC_LIST_HEAD(view->rpz_zones);
1512224092Sdougb	     old != new;
1513224092Sdougb	     old = ISC_LIST_NEXT(old, link)) {
1514224092Sdougb		++new->num;
1515224092Sdougb		if (dns_name_equal(&old->origin, &new->origin)) {
1516224092Sdougb			cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1517224092Sdougb				    "duplicate '%s'", str);
1518224092Sdougb			result = DNS_R_DUPLICATE;
1519224092Sdougb			goto cleanup;
1520224092Sdougb		}
1521224092Sdougb	}
1522224092Sdougb
1523224092Sdougb	if (new->policy == DNS_RPZ_POLICY_CNAME) {
1524224092Sdougb		str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "cname"));
1525224092Sdougb		result = dns_name_fromstring(&new->cname, str, 0, view->mctx);
1526224092Sdougb		if (result != ISC_R_SUCCESS) {
1527224092Sdougb			cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1528224092Sdougb				    "invalid cname '%s'", str);
1529224092Sdougb			goto cleanup;
1530224092Sdougb		}
1531224092Sdougb	}
1532224092Sdougb
1533224092Sdougb	return (ISC_R_SUCCESS);
1534224092Sdougb
1535224092Sdougb cleanup:
1536224092Sdougb	dns_rpz_view_destroy(view);
1537224092Sdougb	return (result);
1538224092Sdougb}
1539224092Sdougb
1540224092Sdougb/*
1541135446Strhodes * Configure 'view' according to 'vconfig', taking defaults from 'config'
1542135446Strhodes * where values are missing in 'vconfig'.
1543135446Strhodes *
1544135446Strhodes * When configuring the default view, 'vconfig' will be NULL and the
1545135446Strhodes * global defaults in 'config' used exclusively.
1546135446Strhodes */
1547135446Strhodesstatic isc_result_t
1548225361Sdougbconfigure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
1549224092Sdougb	       ns_cachelist_t *cachelist, const cfg_obj_t *bindkeys,
1550224092Sdougb	       isc_mem_t *mctx, cfg_aclconfctx_t *actx,
1551224092Sdougb	       isc_boolean_t need_hints)
1552135446Strhodes{
1553165071Sdougb	const cfg_obj_t *maps[4];
1554165071Sdougb	const cfg_obj_t *cfgmaps[3];
1555224092Sdougb	const cfg_obj_t *optionmaps[3];
1556165071Sdougb	const cfg_obj_t *options = NULL;
1557165071Sdougb	const cfg_obj_t *voptions = NULL;
1558165071Sdougb	const cfg_obj_t *forwardtype;
1559165071Sdougb	const cfg_obj_t *forwarders;
1560165071Sdougb	const cfg_obj_t *alternates;
1561165071Sdougb	const cfg_obj_t *zonelist;
1562186462Sdougb	const cfg_obj_t *dlz;
1563186462Sdougb	unsigned int dlzargc;
1564186462Sdougb	char **dlzargv;
1565165071Sdougb	const cfg_obj_t *disabled;
1566165071Sdougb	const cfg_obj_t *obj;
1567165071Sdougb	const cfg_listelt_t *element;
1568135446Strhodes	in_port_t port;
1569135446Strhodes	dns_cache_t *cache = NULL;
1570135446Strhodes	isc_result_t result;
1571135446Strhodes	isc_uint32_t max_adb_size;
1572224092Sdougb	unsigned int cleaning_interval;
1573135446Strhodes	isc_uint32_t max_cache_size;
1574170222Sdougb	isc_uint32_t max_acache_size;
1575135446Strhodes	isc_uint32_t lame_ttl;
1576224092Sdougb	dns_tsig_keyring_t *ring = NULL;
1577135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
1578225361Sdougb	isc_mem_t *cmctx = NULL, *hmctx = NULL;
1579135446Strhodes	dns_dispatch_t *dispatch4 = NULL;
1580135446Strhodes	dns_dispatch_t *dispatch6 = NULL;
1581135446Strhodes	isc_boolean_t reused_cache = ISC_FALSE;
1582224092Sdougb	isc_boolean_t shared_cache = ISC_FALSE;
1583224092Sdougb	int i = 0, j = 0, k = 0;
1584135446Strhodes	const char *str;
1585224092Sdougb	const char *cachename = NULL;
1586135446Strhodes	dns_order_t *order = NULL;
1587135446Strhodes	isc_uint32_t udpsize;
1588193149Sdougb	unsigned int resopts = 0;
1589170222Sdougb	dns_zone_t *zone = NULL;
1590170222Sdougb	isc_uint32_t max_clients_per_query;
1591170222Sdougb	const char *sep = ": view ";
1592170222Sdougb	const char *viewname = view->name;
1593170222Sdougb	const char *forview = " for view ";
1594170222Sdougb	isc_boolean_t rfc1918;
1595170222Sdougb	isc_boolean_t empty_zones_enable;
1596170222Sdougb	const cfg_obj_t *disablelist = NULL;
1597193149Sdougb	isc_stats_t *resstats = NULL;
1598193149Sdougb	dns_stats_t *resquerystats = NULL;
1599224092Sdougb	isc_boolean_t auto_dlv = ISC_FALSE;
1600224092Sdougb	isc_boolean_t auto_root = ISC_FALSE;
1601224092Sdougb	ns_cache_t *nsc;
1602193149Sdougb	isc_boolean_t zero_no_soattl;
1603224092Sdougb	dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
1604224092Sdougb	unsigned int query_timeout;
1605225361Sdougb	struct cfg_context *nzctx;
1606135446Strhodes
1607135446Strhodes	REQUIRE(DNS_VIEW_VALID(view));
1608135446Strhodes
1609135446Strhodes	if (config != NULL)
1610135446Strhodes		(void)cfg_map_get(config, "options", &options);
1611135446Strhodes
1612224092Sdougb	/*
1613224092Sdougb	 * maps: view options, options, defaults
1614224092Sdougb	 * cfgmaps: view options, config
1615224092Sdougb	 * optionmaps: view options, options
1616224092Sdougb	 */
1617135446Strhodes	if (vconfig != NULL) {
1618135446Strhodes		voptions = cfg_tuple_get(vconfig, "options");
1619135446Strhodes		maps[i++] = voptions;
1620224092Sdougb		optionmaps[j++] = voptions;
1621224092Sdougb		cfgmaps[k++] = voptions;
1622135446Strhodes	}
1623224092Sdougb	if (options != NULL) {
1624135446Strhodes		maps[i++] = options;
1625224092Sdougb		optionmaps[j++] = options;
1626224092Sdougb	}
1627224092Sdougb
1628135446Strhodes	maps[i++] = ns_g_defaults;
1629135446Strhodes	maps[i] = NULL;
1630224092Sdougb	optionmaps[j] = NULL;
1631135446Strhodes	if (config != NULL)
1632224092Sdougb		cfgmaps[k++] = config;
1633224092Sdougb	cfgmaps[k] = NULL;
1634135446Strhodes
1635170222Sdougb	if (!strcmp(viewname, "_default")) {
1636170222Sdougb		sep = "";
1637170222Sdougb		viewname = "";
1638170222Sdougb		forview = "";
1639225361Sdougb		POST(forview);
1640170222Sdougb	}
1641170222Sdougb
1642135446Strhodes	/*
1643135446Strhodes	 * Set the view's port number for outgoing queries.
1644135446Strhodes	 */
1645135446Strhodes	CHECKM(ns_config_getport(config, &port), "port");
1646135446Strhodes	dns_view_setdstport(view, port);
1647135446Strhodes
1648135446Strhodes	/*
1649170222Sdougb	 * Create additional cache for this view and zones under the view
1650170222Sdougb	 * if explicitly enabled.
1651170222Sdougb	 * XXX950 default to on.
1652170222Sdougb	 */
1653170222Sdougb	obj = NULL;
1654170222Sdougb	(void)ns_config_get(maps, "acache-enable", &obj);
1655170222Sdougb	if (obj != NULL && cfg_obj_asboolean(obj)) {
1656170222Sdougb		cmctx = NULL;
1657170222Sdougb		CHECK(isc_mem_create(0, 0, &cmctx));
1658170222Sdougb		CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr,
1659170222Sdougb					ns_g_timermgr));
1660193149Sdougb		isc_mem_setname(cmctx, "acache", NULL);
1661170222Sdougb		isc_mem_detach(&cmctx);
1662170222Sdougb	}
1663170222Sdougb	if (view->acache != NULL) {
1664170222Sdougb		obj = NULL;
1665170222Sdougb		result = ns_config_get(maps, "acache-cleaning-interval", &obj);
1666170222Sdougb		INSIST(result == ISC_R_SUCCESS);
1667170222Sdougb		dns_acache_setcleaninginterval(view->acache,
1668170222Sdougb					       cfg_obj_asuint32(obj) * 60);
1669170222Sdougb
1670170222Sdougb		obj = NULL;
1671170222Sdougb		result = ns_config_get(maps, "max-acache-size", &obj);
1672170222Sdougb		INSIST(result == ISC_R_SUCCESS);
1673170222Sdougb		if (cfg_obj_isstring(obj)) {
1674170222Sdougb			str = cfg_obj_asstring(obj);
1675170222Sdougb			INSIST(strcasecmp(str, "unlimited") == 0);
1676170222Sdougb			max_acache_size = ISC_UINT32_MAX;
1677170222Sdougb		} else {
1678170222Sdougb			isc_resourcevalue_t value;
1679170222Sdougb
1680170222Sdougb			value = cfg_obj_asuint64(obj);
1681170222Sdougb			if (value > ISC_UINT32_MAX) {
1682170222Sdougb				cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
1683170222Sdougb					    "'max-acache-size "
1684170222Sdougb					    "%" ISC_PRINT_QUADFORMAT
1685170222Sdougb					    "d' is too large",
1686170222Sdougb					    value);
1687170222Sdougb				result = ISC_R_RANGE;
1688170222Sdougb				goto cleanup;
1689170222Sdougb			}
1690170222Sdougb			max_acache_size = (isc_uint32_t)value;
1691170222Sdougb		}
1692170222Sdougb		dns_acache_setcachesize(view->acache, max_acache_size);
1693170222Sdougb	}
1694170222Sdougb
1695224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query", NULL, actx,
1696216175Sdougb				 ns_g_mctx, &view->queryacl));
1697216175Sdougb	if (view->queryacl == NULL) {
1698224092Sdougb		CHECK(configure_view_acl(NULL, ns_g_config, "allow-query",
1699224092Sdougb					 NULL, actx, ns_g_mctx,
1700224092Sdougb					 &view->queryacl));
1701216175Sdougb	}
1702216175Sdougb
1703170222Sdougb	/*
1704135446Strhodes	 * Configure the zones.
1705135446Strhodes	 */
1706135446Strhodes	zonelist = NULL;
1707135446Strhodes	if (voptions != NULL)
1708135446Strhodes		(void)cfg_map_get(voptions, "zone", &zonelist);
1709135446Strhodes	else
1710135446Strhodes		(void)cfg_map_get(config, "zone", &zonelist);
1711225361Sdougb
1712225361Sdougb	/*
1713225361Sdougb	 * Load zone configuration
1714225361Sdougb	 */
1715135446Strhodes	for (element = cfg_list_first(zonelist);
1716135446Strhodes	     element != NULL;
1717135446Strhodes	     element = cfg_list_next(element))
1718135446Strhodes	{
1719165071Sdougb		const cfg_obj_t *zconfig = cfg_listelt_value(element);
1720135446Strhodes		CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
1721224092Sdougb				     actx, ISC_FALSE));
1722135446Strhodes	}
1723135446Strhodes
1724224092Sdougb	/*
1725224092Sdougb	 * If we're allowing added zones, then load zone configuration
1726224092Sdougb	 * from the newzone file for zones that were added during previous
1727224092Sdougb	 * runs.
1728224092Sdougb	 */
1729225361Sdougb	nzctx = view->new_zone_config;
1730225361Sdougb	if (nzctx != NULL && nzctx->nzconfig != NULL) {
1731224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1732224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
1733224092Sdougb			      "loading additional zones for view '%s'",
1734224092Sdougb			      view->name);
1735224092Sdougb
1736225361Sdougb		zonelist = NULL;
1737225361Sdougb		cfg_map_get(nzctx->nzconfig, "zone", &zonelist);
1738225361Sdougb
1739225361Sdougb		for (element = cfg_list_first(zonelist);
1740225361Sdougb		     element != NULL;
1741225361Sdougb		     element = cfg_list_next(element))
1742225361Sdougb		{
1743225361Sdougb			const cfg_obj_t *zconfig = cfg_listelt_value(element);
1744225361Sdougb			CHECK(configure_zone(config, zconfig, vconfig,
1745225361Sdougb					     mctx, view, actx,
1746225361Sdougb					     ISC_TRUE));
1747224092Sdougb		}
1748224092Sdougb	}
1749224092Sdougb
1750135446Strhodes	/*
1751170222Sdougb	 * Create Dynamically Loadable Zone driver.
1752170222Sdougb	 */
1753170222Sdougb	dlz = NULL;
1754170222Sdougb	if (voptions != NULL)
1755170222Sdougb		(void)cfg_map_get(voptions, "dlz", &dlz);
1756170222Sdougb	else
1757170222Sdougb		(void)cfg_map_get(config, "dlz", &dlz);
1758170222Sdougb
1759170222Sdougb	obj = NULL;
1760170222Sdougb	if (dlz != NULL) {
1761170222Sdougb		(void)cfg_map_get(cfg_tuple_get(dlz, "options"),
1762170222Sdougb				  "database", &obj);
1763170222Sdougb		if (obj != NULL) {
1764170222Sdougb			char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
1765170222Sdougb			if (s == NULL) {
1766170222Sdougb				result = ISC_R_NOMEMORY;
1767170222Sdougb				goto cleanup;
1768170222Sdougb			}
1769186462Sdougb
1770170222Sdougb			result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv);
1771170222Sdougb			if (result != ISC_R_SUCCESS) {
1772170222Sdougb				isc_mem_free(mctx, s);
1773170222Sdougb				goto cleanup;
1774170222Sdougb			}
1775170222Sdougb
1776170222Sdougb			obj = cfg_tuple_get(dlz, "name");
1777170222Sdougb			result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
1778170222Sdougb					       dlzargv[0], dlzargc, dlzargv,
1779170222Sdougb					       &view->dlzdatabase);
1780170222Sdougb			isc_mem_free(mctx, s);
1781170222Sdougb			isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
1782170222Sdougb			if (result != ISC_R_SUCCESS)
1783170222Sdougb				goto cleanup;
1784224092Sdougb
1785224092Sdougb			/*
1786224092Sdougb			 * If the dlz backend supports configuration,
1787224092Sdougb			 * then call its configure method now.
1788224092Sdougb			 */
1789224092Sdougb			result = dns_dlzconfigure(view, dlzconfigure_callback);
1790224092Sdougb			if (result != ISC_R_SUCCESS)
1791224092Sdougb				goto cleanup;
1792170222Sdougb		}
1793170222Sdougb	}
1794170222Sdougb
1795170222Sdougb	/*
1796193149Sdougb	 * Obtain configuration parameters that affect the decision of whether
1797193149Sdougb	 * we can reuse/share an existing cache.
1798193149Sdougb	 */
1799224092Sdougb	obj = NULL;
1800224092Sdougb	result = ns_config_get(maps, "cleaning-interval", &obj);
1801224092Sdougb	INSIST(result == ISC_R_SUCCESS);
1802224092Sdougb	cleaning_interval = cfg_obj_asuint32(obj) * 60;
1803224092Sdougb
1804224092Sdougb	obj = NULL;
1805224092Sdougb	result = ns_config_get(maps, "max-cache-size", &obj);
1806224092Sdougb	INSIST(result == ISC_R_SUCCESS);
1807224092Sdougb	if (cfg_obj_isstring(obj)) {
1808224092Sdougb		str = cfg_obj_asstring(obj);
1809224092Sdougb		INSIST(strcasecmp(str, "unlimited") == 0);
1810224092Sdougb		max_cache_size = ISC_UINT32_MAX;
1811224092Sdougb	} else {
1812224092Sdougb		isc_resourcevalue_t value;
1813224092Sdougb		value = cfg_obj_asuint64(obj);
1814224092Sdougb		if (value > ISC_UINT32_MAX) {
1815224092Sdougb			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
1816224092Sdougb				    "'max-cache-size "
1817224092Sdougb				    "%" ISC_PRINT_QUADFORMAT "d' is too large",
1818224092Sdougb				    value);
1819224092Sdougb			result = ISC_R_RANGE;
1820224092Sdougb			goto cleanup;
1821224092Sdougb		}
1822224092Sdougb		max_cache_size = (isc_uint32_t)value;
1823224092Sdougb	}
1824224092Sdougb
1825193149Sdougb	/* Check-names. */
1826193149Sdougb	obj = NULL;
1827193149Sdougb	result = ns_checknames_get(maps, "response", &obj);
1828193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1829193149Sdougb
1830193149Sdougb	str = cfg_obj_asstring(obj);
1831193149Sdougb	if (strcasecmp(str, "fail") == 0) {
1832193149Sdougb		resopts |= DNS_RESOLVER_CHECKNAMES |
1833193149Sdougb			DNS_RESOLVER_CHECKNAMESFAIL;
1834193149Sdougb		view->checknames = ISC_TRUE;
1835193149Sdougb	} else if (strcasecmp(str, "warn") == 0) {
1836193149Sdougb		resopts |= DNS_RESOLVER_CHECKNAMES;
1837193149Sdougb		view->checknames = ISC_FALSE;
1838193149Sdougb	} else if (strcasecmp(str, "ignore") == 0) {
1839193149Sdougb		view->checknames = ISC_FALSE;
1840193149Sdougb	} else
1841193149Sdougb		INSIST(0);
1842193149Sdougb
1843193149Sdougb	obj = NULL;
1844193149Sdougb	result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
1845193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1846193149Sdougb	zero_no_soattl = cfg_obj_asboolean(obj);
1847193149Sdougb
1848193149Sdougb	obj = NULL;
1849224092Sdougb	result = ns_config_get(maps, "dns64", &obj);
1850224092Sdougb	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") &&
1851224092Sdougb	    strcmp(view->name, "_meta")) {
1852224092Sdougb		const cfg_listelt_t *element;
1853224092Sdougb		isc_netaddr_t na, suffix, *sp;
1854224092Sdougb		unsigned int prefixlen;
1855224092Sdougb		const char *server, *contact;
1856224092Sdougb		const cfg_obj_t *myobj;
1857224092Sdougb
1858224092Sdougb		myobj = NULL;
1859224092Sdougb		result = ns_config_get(maps, "dns64-server", &myobj);
1860224092Sdougb		if (result == ISC_R_SUCCESS)
1861224092Sdougb			server = cfg_obj_asstring(myobj);
1862224092Sdougb		else
1863224092Sdougb			server = NULL;
1864224092Sdougb
1865224092Sdougb		myobj = NULL;
1866224092Sdougb		result = ns_config_get(maps, "dns64-contact", &myobj);
1867224092Sdougb		if (result == ISC_R_SUCCESS)
1868224092Sdougb			contact = cfg_obj_asstring(myobj);
1869224092Sdougb		else
1870224092Sdougb			contact = NULL;
1871224092Sdougb
1872224092Sdougb		for (element = cfg_list_first(obj);
1873224092Sdougb		     element != NULL;
1874224092Sdougb		     element = cfg_list_next(element))
1875224092Sdougb		{
1876224092Sdougb			const cfg_obj_t *map = cfg_listelt_value(element);
1877224092Sdougb			dns_dns64_t *dns64 = NULL;
1878224092Sdougb			unsigned int dns64options = 0;
1879224092Sdougb
1880224092Sdougb			cfg_obj_asnetprefix(cfg_map_getname(map), &na,
1881224092Sdougb					    &prefixlen);
1882224092Sdougb
1883224092Sdougb			obj = NULL;
1884224092Sdougb			(void)cfg_map_get(map, "suffix", &obj);
1885224092Sdougb			if (obj != NULL) {
1886224092Sdougb				sp = &suffix;
1887224092Sdougb				isc_netaddr_fromsockaddr(sp,
1888224092Sdougb						      cfg_obj_assockaddr(obj));
1889224092Sdougb			} else
1890224092Sdougb				sp = NULL;
1891224092Sdougb
1892224092Sdougb			clients = mapped = excluded = NULL;
1893224092Sdougb			obj = NULL;
1894224092Sdougb			(void)cfg_map_get(map, "clients", &obj);
1895224092Sdougb			if (obj != NULL) {
1896224092Sdougb				result = cfg_acl_fromconfig(obj, config,
1897224092Sdougb							    ns_g_lctx, actx,
1898224092Sdougb							    mctx, 0, &clients);
1899224092Sdougb				if (result != ISC_R_SUCCESS)
1900224092Sdougb					goto cleanup;
1901224092Sdougb			}
1902224092Sdougb			obj = NULL;
1903224092Sdougb			(void)cfg_map_get(map, "mapped", &obj);
1904224092Sdougb			if (obj != NULL) {
1905224092Sdougb				result = cfg_acl_fromconfig(obj, config,
1906224092Sdougb							    ns_g_lctx, actx,
1907224092Sdougb							    mctx, 0, &mapped);
1908224092Sdougb				if (result != ISC_R_SUCCESS)
1909224092Sdougb					goto cleanup;
1910224092Sdougb			}
1911224092Sdougb			obj = NULL;
1912224092Sdougb			(void)cfg_map_get(map, "exclude", &obj);
1913224092Sdougb			if (obj != NULL) {
1914224092Sdougb				result = cfg_acl_fromconfig(obj, config,
1915224092Sdougb							    ns_g_lctx, actx,
1916224092Sdougb							    mctx, 0, &excluded);
1917224092Sdougb				if (result != ISC_R_SUCCESS)
1918224092Sdougb					goto cleanup;
1919224092Sdougb			}
1920224092Sdougb
1921224092Sdougb			obj = NULL;
1922224092Sdougb			(void)cfg_map_get(map, "recursive-only", &obj);
1923224092Sdougb			if (obj != NULL && cfg_obj_asboolean(obj))
1924224092Sdougb				dns64options |= DNS_DNS64_RECURSIVE_ONLY;
1925224092Sdougb
1926224092Sdougb			obj = NULL;
1927224092Sdougb			(void)cfg_map_get(map, "break-dnssec", &obj);
1928224092Sdougb			if (obj != NULL && cfg_obj_asboolean(obj))
1929224092Sdougb				dns64options |= DNS_DNS64_BREAK_DNSSEC;
1930224092Sdougb
1931224092Sdougb			result = dns_dns64_create(mctx, &na, prefixlen, sp,
1932224092Sdougb						  clients, mapped, excluded,
1933224092Sdougb						  dns64options, &dns64);
1934224092Sdougb			if (result != ISC_R_SUCCESS)
1935224092Sdougb				goto cleanup;
1936224092Sdougb			dns_dns64_append(&view->dns64, dns64);
1937224092Sdougb			view->dns64cnt++;
1938224092Sdougb			result = dns64_reverse(view, mctx, &na, prefixlen,
1939224092Sdougb					       server, contact);
1940224092Sdougb			if (result != ISC_R_SUCCESS)
1941224092Sdougb				goto cleanup;
1942224092Sdougb			if (clients != NULL)
1943224092Sdougb				dns_acl_detach(&clients);
1944224092Sdougb			if (mapped != NULL)
1945224092Sdougb				dns_acl_detach(&mapped);
1946224092Sdougb			if (excluded != NULL)
1947224092Sdougb				dns_acl_detach(&excluded);
1948224092Sdougb		}
1949224092Sdougb	}
1950224092Sdougb
1951224092Sdougb	obj = NULL;
1952193149Sdougb	result = ns_config_get(maps, "dnssec-accept-expired", &obj);
1953193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1954193149Sdougb	view->acceptexpired = cfg_obj_asboolean(obj);
1955193149Sdougb
1956193149Sdougb	obj = NULL;
1957193149Sdougb	result = ns_config_get(maps, "dnssec-validation", &obj);
1958193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1959224092Sdougb	if (cfg_obj_isboolean(obj)) {
1960224092Sdougb		view->enablevalidation = cfg_obj_asboolean(obj);
1961224092Sdougb	} else {
1962224092Sdougb		/* If dnssec-validation is not boolean, it must be "auto" */
1963224092Sdougb		view->enablevalidation = ISC_TRUE;
1964224092Sdougb		auto_root = ISC_TRUE;
1965224092Sdougb	}
1966193149Sdougb
1967193149Sdougb	obj = NULL;
1968193149Sdougb	result = ns_config_get(maps, "max-cache-ttl", &obj);
1969193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1970193149Sdougb	view->maxcachettl = cfg_obj_asuint32(obj);
1971193149Sdougb
1972193149Sdougb	obj = NULL;
1973193149Sdougb	result = ns_config_get(maps, "max-ncache-ttl", &obj);
1974193149Sdougb	INSIST(result == ISC_R_SUCCESS);
1975193149Sdougb	view->maxncachettl = cfg_obj_asuint32(obj);
1976193149Sdougb	if (view->maxncachettl > 7 * 24 * 3600)
1977193149Sdougb		view->maxncachettl = 7 * 24 * 3600;
1978193149Sdougb
1979193149Sdougb	/*
1980224092Sdougb	 * Configure the view's cache.
1981135446Strhodes	 *
1982224092Sdougb	 * First, check to see if there are any attach-cache options.  If yes,
1983224092Sdougb	 * attempt to lookup an existing cache at attach it to the view.  If
1984224092Sdougb	 * there is not one, then try to reuse an existing cache if possible;
1985224092Sdougb	 * otherwise create a new cache.
1986224092Sdougb	 *
1987224092Sdougb	 * Note that the ADB is not preserved or shared in either case.
1988224092Sdougb	 *
1989224092Sdougb	 * When a matching view is found, the associated statistics are also
1990224092Sdougb	 * retrieved and reused.
1991224092Sdougb	 *
1992224092Sdougb	 * XXX Determining when it is safe to reuse or share a cache is tricky.
1993193149Sdougb	 * When the view's configuration changes, the cached data may become
1994193149Sdougb	 * invalid because it reflects our old view of the world.  We check
1995224092Sdougb	 * some of the configuration parameters that could invalidate the cache
1996224092Sdougb	 * or otherwise make it unsharable, but there are other configuration
1997224092Sdougb	 * options that should be checked.  For example, if a view uses a
1998224092Sdougb	 * forwarder, changes in the forwarder configuration may invalidate
1999224092Sdougb	 * the cache.  At the moment, it's the administrator's responsibility to
2000224092Sdougb	 * ensure these configuration options don't invalidate reusing/sharing.
2001135446Strhodes	 */
2002224092Sdougb	obj = NULL;
2003224092Sdougb	result = ns_config_get(maps, "attach-cache", &obj);
2004224092Sdougb	if (result == ISC_R_SUCCESS)
2005224092Sdougb		cachename = cfg_obj_asstring(obj);
2006224092Sdougb	else
2007224092Sdougb		cachename = view->name;
2008224092Sdougb	cache = NULL;
2009224092Sdougb	nsc = cachelist_find(cachelist, cachename);
2010224092Sdougb	if (nsc != NULL) {
2011224092Sdougb		if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
2012224092Sdougb				    cleaning_interval, max_cache_size)) {
2013193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2014224092Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
2015224092Sdougb				      "views %s and %s can't share the cache "
2016193149Sdougb				      "due to configuration parameter mismatch",
2017224092Sdougb				      nsc->primaryview->name, view->name);
2018224092Sdougb			result = ISC_R_FAILURE;
2019224092Sdougb			goto cleanup;
2020193149Sdougb		}
2021224092Sdougb		dns_cache_attach(nsc->cache, &cache);
2022224092Sdougb		shared_cache = ISC_TRUE;
2023224092Sdougb	} else {
2024224092Sdougb		if (strcmp(cachename, view->name) == 0) {
2025224092Sdougb			result = dns_viewlist_find(&ns_g_server->viewlist,
2026224092Sdougb						   cachename, view->rdclass,
2027224092Sdougb						   &pview);
2028224092Sdougb			if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
2029224092Sdougb				goto cleanup;
2030224092Sdougb			if (pview != NULL) {
2031224092Sdougb				if (!cache_reusable(pview, view,
2032224092Sdougb						    zero_no_soattl)) {
2033224092Sdougb					isc_log_write(ns_g_lctx,
2034224092Sdougb						      NS_LOGCATEGORY_GENERAL,
2035224092Sdougb						      NS_LOGMODULE_SERVER,
2036224092Sdougb						      ISC_LOG_DEBUG(1),
2037224092Sdougb						      "cache cannot be reused "
2038224092Sdougb						      "for view %s due to "
2039224092Sdougb						      "configuration parameter "
2040224092Sdougb						      "mismatch", view->name);
2041224092Sdougb				} else {
2042224092Sdougb					INSIST(pview->cache != NULL);
2043224092Sdougb					isc_log_write(ns_g_lctx,
2044224092Sdougb						      NS_LOGCATEGORY_GENERAL,
2045224092Sdougb						      NS_LOGMODULE_SERVER,
2046224092Sdougb						      ISC_LOG_DEBUG(3),
2047224092Sdougb						      "reusing existing cache");
2048224092Sdougb					reused_cache = ISC_TRUE;
2049224092Sdougb					dns_cache_attach(pview->cache, &cache);
2050224092Sdougb				}
2051224092Sdougb				dns_view_getresstats(pview, &resstats);
2052224092Sdougb				dns_view_getresquerystats(pview,
2053224092Sdougb							  &resquerystats);
2054224092Sdougb				dns_view_detach(&pview);
2055224092Sdougb			}
2056224092Sdougb		}
2057224092Sdougb		if (cache == NULL) {
2058224092Sdougb			/*
2059224092Sdougb			 * Create a cache with the desired name.  This normally
2060224092Sdougb			 * equals the view name, but may also be a forward
2061224092Sdougb			 * reference to a view that share the cache with this
2062224092Sdougb			 * view but is not yet configured.  If it is not the
2063224092Sdougb			 * view name but not a forward reference either, then it
2064224092Sdougb			 * is simply a named cache that is not shared.
2065225361Sdougb			 *
2066225361Sdougb			 * We use two separate memory contexts for the
2067225361Sdougb			 * cache, for the main cache memory and the heap
2068225361Sdougb			 * memory.
2069224092Sdougb			 */
2070224092Sdougb			CHECK(isc_mem_create(0, 0, &cmctx));
2071224092Sdougb			isc_mem_setname(cmctx, "cache", NULL);
2072225361Sdougb			CHECK(isc_mem_create(0, 0, &hmctx));
2073225361Sdougb			isc_mem_setname(hmctx, "cache_heap", NULL);
2074225361Sdougb			CHECK(dns_cache_create3(cmctx, hmctx, ns_g_taskmgr,
2075224092Sdougb						ns_g_timermgr, view->rdclass,
2076224092Sdougb						cachename, "rbt", 0, NULL,
2077224092Sdougb						&cache));
2078225361Sdougb			isc_mem_detach(&cmctx);
2079225361Sdougb			isc_mem_detach(&hmctx);
2080224092Sdougb		}
2081224092Sdougb		nsc = isc_mem_get(mctx, sizeof(*nsc));
2082224092Sdougb		if (nsc == NULL) {
2083224092Sdougb			result = ISC_R_NOMEMORY;
2084224092Sdougb			goto cleanup;
2085224092Sdougb		}
2086224092Sdougb		nsc->cache = NULL;
2087224092Sdougb		dns_cache_attach(cache, &nsc->cache);
2088224092Sdougb		nsc->primaryview = view;
2089224092Sdougb		nsc->needflush = ISC_FALSE;
2090224092Sdougb		nsc->adbsizeadjusted = ISC_FALSE;
2091224092Sdougb		ISC_LINK_INIT(nsc, link);
2092224092Sdougb		ISC_LIST_APPEND(*cachelist, nsc, link);
2093193149Sdougb	}
2094224092Sdougb	dns_view_setcache2(view, cache, shared_cache);
2095135446Strhodes
2096135446Strhodes	/*
2097135446Strhodes	 * cache-file cannot be inherited if views are present, but this
2098135446Strhodes	 * should be caught by the configuration checking stage.
2099135446Strhodes	 */
2100135446Strhodes	obj = NULL;
2101135446Strhodes	result = ns_config_get(maps, "cache-file", &obj);
2102135446Strhodes	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
2103135446Strhodes		CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
2104224092Sdougb		if (!reused_cache && !shared_cache)
2105135446Strhodes			CHECK(dns_cache_load(cache));
2106135446Strhodes	}
2107135446Strhodes
2108224092Sdougb	dns_cache_setcleaninginterval(cache, cleaning_interval);
2109135446Strhodes	dns_cache_setcachesize(cache, max_cache_size);
2110135446Strhodes
2111135446Strhodes	dns_cache_detach(&cache);
2112135446Strhodes
2113135446Strhodes	/*
2114135446Strhodes	 * Resolver.
2115135446Strhodes	 *
2116135446Strhodes	 * XXXRTH  Hardwired number of tasks.
2117135446Strhodes	 */
2118186462Sdougb	CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4,
2119186462Sdougb					    ISC_TF(ISC_LIST_PREV(view, link)
2120186462Sdougb						   == NULL)));
2121186462Sdougb	CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6,
2122186462Sdougb					    ISC_TF(ISC_LIST_PREV(view, link)
2123186462Sdougb						   == NULL)));
2124135446Strhodes	if (dispatch4 == NULL && dispatch6 == NULL) {
2125135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
2126135446Strhodes				 "unable to obtain neither an IPv4 nor"
2127135446Strhodes				 " an IPv6 dispatch");
2128135446Strhodes		result = ISC_R_UNEXPECTED;
2129135446Strhodes		goto cleanup;
2130135446Strhodes	}
2131135446Strhodes	CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
2132135446Strhodes				      ns_g_socketmgr, ns_g_timermgr,
2133193149Sdougb				      resopts, ns_g_dispatchmgr,
2134135446Strhodes				      dispatch4, dispatch6));
2135135446Strhodes
2136193149Sdougb	if (resstats == NULL) {
2137193149Sdougb		CHECK(isc_stats_create(mctx, &resstats,
2138193149Sdougb				       dns_resstatscounter_max));
2139193149Sdougb	}
2140193149Sdougb	dns_view_setresstats(view, resstats);
2141193149Sdougb	if (resquerystats == NULL)
2142193149Sdougb		CHECK(dns_rdatatypestats_create(mctx, &resquerystats));
2143193149Sdougb	dns_view_setresquerystats(view, resquerystats);
2144193149Sdougb
2145135446Strhodes	/*
2146224092Sdougb	 * Set the ADB cache size to 1/8th of the max-cache-size or
2147224092Sdougb	 * MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared.
2148135446Strhodes	 */
2149135446Strhodes	max_adb_size = 0;
2150135446Strhodes	if (max_cache_size != 0) {
2151135446Strhodes		max_adb_size = max_cache_size / 8;
2152135446Strhodes		if (max_adb_size == 0)
2153135446Strhodes			max_adb_size = 1;	/* Force minimum. */
2154224092Sdougb		if (view != nsc->primaryview &&
2155224092Sdougb		    max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE) {
2156224092Sdougb			max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE;
2157224092Sdougb			if (!nsc->adbsizeadjusted) {
2158224092Sdougb				dns_adb_setadbsize(nsc->primaryview->adb,
2159224092Sdougb						   MAX_ADB_SIZE_FOR_CACHESHARE);
2160224092Sdougb				nsc->adbsizeadjusted = ISC_TRUE;
2161224092Sdougb			}
2162224092Sdougb		}
2163135446Strhodes	}
2164135446Strhodes	dns_adb_setadbsize(view->adb, max_adb_size);
2165135446Strhodes
2166135446Strhodes	/*
2167135446Strhodes	 * Set resolver's lame-ttl.
2168135446Strhodes	 */
2169135446Strhodes	obj = NULL;
2170135446Strhodes	result = ns_config_get(maps, "lame-ttl", &obj);
2171135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2172135446Strhodes	lame_ttl = cfg_obj_asuint32(obj);
2173135446Strhodes	if (lame_ttl > 1800)
2174135446Strhodes		lame_ttl = 1800;
2175135446Strhodes	dns_resolver_setlamettl(view->resolver, lame_ttl);
2176170222Sdougb
2177135446Strhodes	/*
2178224092Sdougb	 * Set the resolver's query timeout.
2179224092Sdougb	 */
2180224092Sdougb	obj = NULL;
2181224092Sdougb	result = ns_config_get(maps, "resolver-query-timeout", &obj);
2182224092Sdougb	INSIST(result == ISC_R_SUCCESS);
2183224092Sdougb	query_timeout = cfg_obj_asuint32(obj);
2184224092Sdougb	dns_resolver_settimeout(view->resolver, query_timeout);
2185224092Sdougb
2186224092Sdougb	/* Specify whether to use 0-TTL for negative response for SOA query */
2187224092Sdougb	dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl);
2188224092Sdougb
2189224092Sdougb	/*
2190135446Strhodes	 * Set the resolver's EDNS UDP size.
2191135446Strhodes	 */
2192135446Strhodes	obj = NULL;
2193135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
2194135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2195135446Strhodes	udpsize = cfg_obj_asuint32(obj);
2196135446Strhodes	if (udpsize < 512)
2197135446Strhodes		udpsize = 512;
2198135446Strhodes	if (udpsize > 4096)
2199135446Strhodes		udpsize = 4096;
2200135446Strhodes	dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize);
2201186462Sdougb
2202135446Strhodes	/*
2203170222Sdougb	 * Set the maximum UDP response size.
2204170222Sdougb	 */
2205170222Sdougb	obj = NULL;
2206170222Sdougb	result = ns_config_get(maps, "max-udp-size", &obj);
2207170222Sdougb	INSIST(result == ISC_R_SUCCESS);
2208170222Sdougb	udpsize = cfg_obj_asuint32(obj);
2209170222Sdougb	if (udpsize < 512)
2210170222Sdougb		udpsize = 512;
2211170222Sdougb	if (udpsize > 4096)
2212170222Sdougb		udpsize = 4096;
2213170222Sdougb	view->maxudp = udpsize;
2214170222Sdougb
2215170222Sdougb	/*
2216135446Strhodes	 * Set supported DNSSEC algorithms.
2217135446Strhodes	 */
2218135446Strhodes	dns_resolver_reset_algorithms(view->resolver);
2219135446Strhodes	disabled = NULL;
2220135446Strhodes	(void)ns_config_get(maps, "disable-algorithms", &disabled);
2221135446Strhodes	if (disabled != NULL) {
2222135446Strhodes		for (element = cfg_list_first(disabled);
2223135446Strhodes		     element != NULL;
2224135446Strhodes		     element = cfg_list_next(element))
2225135446Strhodes			CHECK(disable_algorithms(cfg_listelt_value(element),
2226135446Strhodes						 view->resolver));
2227135446Strhodes	}
2228135446Strhodes
2229135446Strhodes	/*
2230135446Strhodes	 * A global or view "forwarders" option, if present,
2231135446Strhodes	 * creates an entry for "." in the forwarding table.
2232135446Strhodes	 */
2233135446Strhodes	forwardtype = NULL;
2234135446Strhodes	forwarders = NULL;
2235135446Strhodes	(void)ns_config_get(maps, "forward", &forwardtype);
2236135446Strhodes	(void)ns_config_get(maps, "forwarders", &forwarders);
2237135446Strhodes	if (forwarders != NULL)
2238186462Sdougb		CHECK(configure_forward(config, view, dns_rootname,
2239135446Strhodes					forwarders, forwardtype));
2240135446Strhodes
2241135446Strhodes	/*
2242135446Strhodes	 * Dual Stack Servers.
2243135446Strhodes	 */
2244135446Strhodes	alternates = NULL;
2245135446Strhodes	(void)ns_config_get(maps, "dual-stack-servers", &alternates);
2246135446Strhodes	if (alternates != NULL)
2247135446Strhodes		CHECK(configure_alternates(config, view, alternates));
2248135446Strhodes
2249135446Strhodes	/*
2250135446Strhodes	 * We have default hints for class IN if we need them.
2251135446Strhodes	 */
2252135446Strhodes	if (view->rdclass == dns_rdataclass_in && view->hints == NULL)
2253135446Strhodes		dns_view_sethints(view, ns_g_server->in_roothints);
2254135446Strhodes
2255135446Strhodes	/*
2256135446Strhodes	 * If we still have no hints, this is a non-IN view with no
2257135446Strhodes	 * "hints zone" configured.  Issue a warning, except if this
2258186462Sdougb	 * is a root server.  Root servers never need to consult
2259135446Strhodes	 * their hints, so it's no point requiring users to configure
2260135446Strhodes	 * them.
2261135446Strhodes	 */
2262135446Strhodes	if (view->hints == NULL) {
2263135446Strhodes		dns_zone_t *rootzone = NULL;
2264135446Strhodes		(void)dns_view_findzone(view, dns_rootname, &rootzone);
2265135446Strhodes		if (rootzone != NULL) {
2266135446Strhodes			dns_zone_detach(&rootzone);
2267135446Strhodes			need_hints = ISC_FALSE;
2268135446Strhodes		}
2269135446Strhodes		if (need_hints)
2270135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2271135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
2272135446Strhodes				      "no root hints for view '%s'",
2273135446Strhodes				      view->name);
2274135446Strhodes	}
2275135446Strhodes
2276135446Strhodes	/*
2277135446Strhodes	 * Configure the view's TSIG keys.
2278135446Strhodes	 */
2279135446Strhodes	CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
2280224092Sdougb	if (ns_g_server->sessionkey != NULL) {
2281224092Sdougb		CHECK(dns_tsigkeyring_add(ring, ns_g_server->session_keyname,
2282224092Sdougb					  ns_g_server->sessionkey));
2283224092Sdougb	}
2284135446Strhodes	dns_view_setkeyring(view, ring);
2285224092Sdougb	dns_tsigkeyring_detach(&ring);
2286135446Strhodes
2287135446Strhodes	/*
2288224092Sdougb	 * See if we can re-use a dynamic key ring.
2289224092Sdougb	 */
2290224092Sdougb	result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
2291224092Sdougb				   view->rdclass, &pview);
2292224092Sdougb	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
2293224092Sdougb		goto cleanup;
2294224092Sdougb	if (pview != NULL) {
2295224092Sdougb		dns_view_getdynamickeyring(pview, &ring);
2296224092Sdougb		if (ring != NULL)
2297224092Sdougb			dns_view_setdynamickeyring(view, ring);
2298224092Sdougb		dns_tsigkeyring_detach(&ring);
2299224092Sdougb		dns_view_detach(&pview);
2300224092Sdougb	} else
2301224092Sdougb		dns_view_restorekeyring(view);
2302224092Sdougb
2303224092Sdougb	/*
2304135446Strhodes	 * Configure the view's peer list.
2305135446Strhodes	 */
2306135446Strhodes	{
2307165071Sdougb		const cfg_obj_t *peers = NULL;
2308165071Sdougb		const cfg_listelt_t *element;
2309135446Strhodes		dns_peerlist_t *newpeers = NULL;
2310135446Strhodes
2311135446Strhodes		(void)ns_config_get(cfgmaps, "server", &peers);
2312135446Strhodes		CHECK(dns_peerlist_new(mctx, &newpeers));
2313135446Strhodes		for (element = cfg_list_first(peers);
2314135446Strhodes		     element != NULL;
2315135446Strhodes		     element = cfg_list_next(element))
2316135446Strhodes		{
2317165071Sdougb			const cfg_obj_t *cpeer = cfg_listelt_value(element);
2318135446Strhodes			dns_peer_t *peer;
2319135446Strhodes
2320135446Strhodes			CHECK(configure_peer(cpeer, mctx, &peer));
2321135446Strhodes			dns_peerlist_addpeer(newpeers, peer);
2322135446Strhodes			dns_peer_detach(&peer);
2323135446Strhodes		}
2324135446Strhodes		dns_peerlist_detach(&view->peers);
2325135446Strhodes		view->peers = newpeers; /* Transfer ownership. */
2326135446Strhodes	}
2327135446Strhodes
2328135446Strhodes	/*
2329135446Strhodes	 *	Configure the views rrset-order.
2330135446Strhodes	 */
2331135446Strhodes	{
2332165071Sdougb		const cfg_obj_t *rrsetorder = NULL;
2333165071Sdougb		const cfg_listelt_t *element;
2334135446Strhodes
2335135446Strhodes		(void)ns_config_get(maps, "rrset-order", &rrsetorder);
2336135446Strhodes		CHECK(dns_order_create(mctx, &order));
2337135446Strhodes		for (element = cfg_list_first(rrsetorder);
2338135446Strhodes		     element != NULL;
2339135446Strhodes		     element = cfg_list_next(element))
2340135446Strhodes		{
2341165071Sdougb			const cfg_obj_t *ent = cfg_listelt_value(element);
2342135446Strhodes
2343135446Strhodes			CHECK(configure_order(order, ent));
2344135446Strhodes		}
2345135446Strhodes		if (view->order != NULL)
2346135446Strhodes			dns_order_detach(&view->order);
2347135446Strhodes		dns_order_attach(order, &view->order);
2348135446Strhodes		dns_order_detach(&order);
2349135446Strhodes	}
2350135446Strhodes	/*
2351135446Strhodes	 * Copy the aclenv object.
2352135446Strhodes	 */
2353135446Strhodes	dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv);
2354135446Strhodes
2355135446Strhodes	/*
2356135446Strhodes	 * Configure the "match-clients" and "match-destinations" ACL.
2357135446Strhodes	 */
2358224092Sdougb	CHECK(configure_view_acl(vconfig, config, "match-clients", NULL, actx,
2359135446Strhodes				 ns_g_mctx, &view->matchclients));
2360224092Sdougb	CHECK(configure_view_acl(vconfig, config, "match-destinations", NULL,
2361224092Sdougb				 actx, ns_g_mctx, &view->matchdestinations));
2362135446Strhodes
2363135446Strhodes	/*
2364135446Strhodes	 * Configure the "match-recursive-only" option.
2365135446Strhodes	 */
2366135446Strhodes	obj = NULL;
2367165071Sdougb	(void)ns_config_get(maps, "match-recursive-only", &obj);
2368135446Strhodes	if (obj != NULL && cfg_obj_asboolean(obj))
2369135446Strhodes		view->matchrecursiveonly = ISC_TRUE;
2370135446Strhodes	else
2371135446Strhodes		view->matchrecursiveonly = ISC_FALSE;
2372135446Strhodes
2373135446Strhodes	/*
2374135446Strhodes	 * Configure other configurable data.
2375135446Strhodes	 */
2376135446Strhodes	obj = NULL;
2377135446Strhodes	result = ns_config_get(maps, "recursion", &obj);
2378135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2379135446Strhodes	view->recursion = cfg_obj_asboolean(obj);
2380135446Strhodes
2381135446Strhodes	obj = NULL;
2382135446Strhodes	result = ns_config_get(maps, "auth-nxdomain", &obj);
2383135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2384135446Strhodes	view->auth_nxdomain = cfg_obj_asboolean(obj);
2385135446Strhodes
2386135446Strhodes	obj = NULL;
2387135446Strhodes	result = ns_config_get(maps, "minimal-responses", &obj);
2388135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2389135446Strhodes	view->minimalresponses = cfg_obj_asboolean(obj);
2390135446Strhodes
2391135446Strhodes	obj = NULL;
2392135446Strhodes	result = ns_config_get(maps, "transfer-format", &obj);
2393135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2394135446Strhodes	str = cfg_obj_asstring(obj);
2395135446Strhodes	if (strcasecmp(str, "many-answers") == 0)
2396135446Strhodes		view->transfer_format = dns_many_answers;
2397135446Strhodes	else if (strcasecmp(str, "one-answer") == 0)
2398135446Strhodes		view->transfer_format = dns_one_answer;
2399135446Strhodes	else
2400135446Strhodes		INSIST(0);
2401186462Sdougb
2402135446Strhodes	/*
2403135446Strhodes	 * Set sources where additional data and CNAME/DNAME
2404135446Strhodes	 * targets for authoritative answers may be found.
2405135446Strhodes	 */
2406135446Strhodes	obj = NULL;
2407135446Strhodes	result = ns_config_get(maps, "additional-from-auth", &obj);
2408135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2409135446Strhodes	view->additionalfromauth = cfg_obj_asboolean(obj);
2410135446Strhodes	if (view->recursion && ! view->additionalfromauth) {
2411135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
2412135446Strhodes			    "'additional-from-auth no' is only supported "
2413135446Strhodes			    "with 'recursion no'");
2414135446Strhodes		view->additionalfromauth = ISC_TRUE;
2415135446Strhodes	}
2416135446Strhodes
2417135446Strhodes	obj = NULL;
2418135446Strhodes	result = ns_config_get(maps, "additional-from-cache", &obj);
2419135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2420135446Strhodes	view->additionalfromcache = cfg_obj_asboolean(obj);
2421135446Strhodes	if (view->recursion && ! view->additionalfromcache) {
2422135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
2423135446Strhodes			    "'additional-from-cache no' is only supported "
2424135446Strhodes			    "with 'recursion no'");
2425135446Strhodes		view->additionalfromcache = ISC_TRUE;
2426135446Strhodes	}
2427135446Strhodes
2428171577Sdougb	/*
2429193149Sdougb	 * Set "allow-query-cache", "allow-query-cache-on",
2430193149Sdougb	 * "allow-recursion", and "allow-recursion-on" acls if
2431171577Sdougb	 * configured in named.conf.
2432171577Sdougb	 */
2433224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL,
2434216175Sdougb				 actx, ns_g_mctx, &view->cacheacl));
2435224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL,
2436216175Sdougb				 actx, ns_g_mctx, &view->cacheonacl));
2437216175Sdougb	if (view->cacheonacl == NULL)
2438193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2439224092Sdougb					 "allow-query-cache-on", NULL, actx,
2440216175Sdougb					 ns_g_mctx, &view->cacheonacl));
2441193149Sdougb	if (strcmp(view->name, "_bind") != 0) {
2442135446Strhodes		CHECK(configure_view_acl(vconfig, config, "allow-recursion",
2443224092Sdougb					 NULL, actx, ns_g_mctx,
2444193149Sdougb					 &view->recursionacl));
2445193149Sdougb		CHECK(configure_view_acl(vconfig, config, "allow-recursion-on",
2446224092Sdougb					 NULL, actx, ns_g_mctx,
2447193149Sdougb					 &view->recursiononacl));
2448193149Sdougb	}
2449135446Strhodes
2450135446Strhodes	/*
2451171577Sdougb	 * "allow-query-cache" inherits from "allow-recursion" if set,
2452171577Sdougb	 * otherwise from "allow-query" if set.
2453171577Sdougb	 * "allow-recursion" inherits from "allow-query-cache" if set,
2454171577Sdougb	 * otherwise from "allow-query" if set.
2455170222Sdougb	 */
2456216175Sdougb	if (view->cacheacl == NULL && view->recursionacl != NULL)
2457216175Sdougb		dns_acl_attach(view->recursionacl, &view->cacheacl);
2458224092Sdougb	/*
2459224092Sdougb	 * XXXEACH: This call to configure_view_acl() is redundant.  We
2460224092Sdougb	 * are leaving it as it is because we are making a minimal change
2461224092Sdougb	 * for a patch release.  In the future this should be changed to
2462224092Sdougb	 * dns_acl_attach(view->queryacl, &view->cacheacl).
2463224092Sdougb	 */
2464216175Sdougb	if (view->cacheacl == NULL && view->recursion)
2465224092Sdougb		CHECK(configure_view_acl(vconfig, config, "allow-query", NULL,
2466216175Sdougb					 actx, ns_g_mctx, &view->cacheacl));
2467193149Sdougb	if (view->recursion &&
2468216175Sdougb	    view->recursionacl == NULL && view->cacheacl != NULL)
2469216175Sdougb		dns_acl_attach(view->cacheacl, &view->recursionacl);
2470171577Sdougb
2471171577Sdougb	/*
2472193149Sdougb	 * Set default "allow-recursion", "allow-recursion-on" and
2473193149Sdougb	 * "allow-query-cache" acls.
2474171577Sdougb	 */
2475170222Sdougb	if (view->recursionacl == NULL && view->recursion)
2476171577Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2477224092Sdougb					 "allow-recursion", NULL,
2478193149Sdougb					 actx, ns_g_mctx,
2479193149Sdougb					 &view->recursionacl));
2480193149Sdougb	if (view->recursiononacl == NULL && view->recursion)
2481193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2482224092Sdougb					 "allow-recursion-on", NULL,
2483193149Sdougb					 actx, ns_g_mctx,
2484193149Sdougb					 &view->recursiononacl));
2485216175Sdougb	if (view->cacheacl == NULL) {
2486193149Sdougb		if (view->recursion)
2487193149Sdougb			CHECK(configure_view_acl(NULL, ns_g_config,
2488224092Sdougb						 "allow-query-cache", NULL,
2489224092Sdougb						 actx, ns_g_mctx,
2490224092Sdougb						 &view->cacheacl));
2491216175Sdougb		else
2492224092Sdougb			CHECK(dns_acl_none(mctx, &view->cacheacl));
2493193149Sdougb	}
2494170222Sdougb
2495193149Sdougb	/*
2496224092Sdougb	 * Filter setting on addresses in the answer section.
2497224092Sdougb	 */
2498224092Sdougb	CHECK(configure_view_acl(vconfig, config, "deny-answer-addresses",
2499224092Sdougb				 "acl", actx, ns_g_mctx, &view->denyansweracl));
2500224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses",
2501224092Sdougb				       "except-from", ns_g_mctx,
2502224092Sdougb				       &view->answeracl_exclude));
2503224092Sdougb
2504224092Sdougb	/*
2505224092Sdougb	 * Filter setting on names (CNAME/DNAME targets) in the answer section.
2506224092Sdougb	 */
2507224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
2508224092Sdougb				       "name", ns_g_mctx,
2509224092Sdougb				       &view->denyanswernames));
2510224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
2511224092Sdougb				       "except-from", ns_g_mctx,
2512224092Sdougb				       &view->answernames_exclude));
2513224092Sdougb
2514224092Sdougb	/*
2515193149Sdougb	 * Configure sortlist, if set
2516193149Sdougb	 */
2517193149Sdougb	CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx,
2518193149Sdougb				      &view->sortlist));
2519135446Strhodes
2520193149Sdougb	/*
2521193149Sdougb	 * Configure default allow-transfer, allow-notify, allow-update
2522193149Sdougb	 * and allow-update-forwarding ACLs, if set, so they can be
2523193149Sdougb	 * inherited by zones.
2524193149Sdougb	 */
2525193149Sdougb	if (view->notifyacl == NULL)
2526193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2527224092Sdougb					 "allow-notify", NULL, actx,
2528193149Sdougb					 ns_g_mctx, &view->notifyacl));
2529193149Sdougb	if (view->transferacl == NULL)
2530193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2531224092Sdougb					 "allow-transfer", NULL, actx,
2532193149Sdougb					 ns_g_mctx, &view->transferacl));
2533193149Sdougb	if (view->updateacl == NULL)
2534193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2535224092Sdougb					 "allow-update", NULL, actx,
2536193149Sdougb					 ns_g_mctx, &view->updateacl));
2537193149Sdougb	if (view->upfwdacl == NULL)
2538193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
2539224092Sdougb					 "allow-update-forwarding", NULL, actx,
2540193149Sdougb					 ns_g_mctx, &view->upfwdacl));
2541193149Sdougb
2542135446Strhodes	obj = NULL;
2543135446Strhodes	result = ns_config_get(maps, "request-ixfr", &obj);
2544135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2545135446Strhodes	view->requestixfr = cfg_obj_asboolean(obj);
2546135446Strhodes
2547135446Strhodes	obj = NULL;
2548135446Strhodes	result = ns_config_get(maps, "provide-ixfr", &obj);
2549135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2550135446Strhodes	view->provideixfr = cfg_obj_asboolean(obj);
2551170222Sdougb
2552170222Sdougb	obj = NULL;
2553193149Sdougb	result = ns_config_get(maps, "request-nsid", &obj);
2554193149Sdougb	INSIST(result == ISC_R_SUCCESS);
2555193149Sdougb	view->requestnsid = cfg_obj_asboolean(obj);
2556193149Sdougb
2557193149Sdougb	obj = NULL;
2558170222Sdougb	result = ns_config_get(maps, "max-clients-per-query", &obj);
2559170222Sdougb	INSIST(result == ISC_R_SUCCESS);
2560170222Sdougb	max_clients_per_query = cfg_obj_asuint32(obj);
2561170222Sdougb
2562170222Sdougb	obj = NULL;
2563170222Sdougb	result = ns_config_get(maps, "clients-per-query", &obj);
2564170222Sdougb	INSIST(result == ISC_R_SUCCESS);
2565170222Sdougb	dns_resolver_setclientsperquery(view->resolver,
2566170222Sdougb					cfg_obj_asuint32(obj),
2567170222Sdougb					max_clients_per_query);
2568186462Sdougb
2569224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
2570135446Strhodes	obj = NULL;
2571224092Sdougb	result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
2572224092Sdougb	INSIST(result == ISC_R_SUCCESS);
2573224092Sdougb	if (cfg_obj_isboolean(obj)) {
2574224092Sdougb		if (cfg_obj_asboolean(obj))
2575224092Sdougb			view->v4_aaaa = dns_v4_aaaa_filter;
2576224092Sdougb		else
2577224092Sdougb			view->v4_aaaa = dns_v4_aaaa_ok;
2578224092Sdougb	} else {
2579224092Sdougb		const char *v4_aaaastr = cfg_obj_asstring(obj);
2580224092Sdougb		if (strcasecmp(v4_aaaastr, "break-dnssec") == 0)
2581224092Sdougb			view->v4_aaaa = dns_v4_aaaa_break_dnssec;
2582224092Sdougb		else
2583224092Sdougb			INSIST(0);
2584224092Sdougb	}
2585224092Sdougb	CHECK(configure_view_acl(vconfig, config, "filter-aaaa", NULL,
2586224092Sdougb				 actx, ns_g_mctx, &view->v4_aaaa_acl));
2587224092Sdougb#endif
2588224092Sdougb
2589224092Sdougb	obj = NULL;
2590135446Strhodes	result = ns_config_get(maps, "dnssec-enable", &obj);
2591135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2592135446Strhodes	view->enablednssec = cfg_obj_asboolean(obj);
2593135446Strhodes
2594135446Strhodes	obj = NULL;
2595224092Sdougb	result = ns_config_get(optionmaps, "dnssec-lookaside", &obj);
2596135446Strhodes	if (result == ISC_R_SUCCESS) {
2597224092Sdougb		/* If set to "auto", use the version from the defaults */
2598224092Sdougb		const cfg_obj_t *dlvobj;
2599234010Sdougb		const char *dom;
2600224092Sdougb		dlvobj = cfg_listelt_value(cfg_list_first(obj));
2601234010Sdougb		dom = cfg_obj_asstring(cfg_tuple_get(dlvobj, "domain"));
2602234010Sdougb		if (cfg_obj_isvoid(cfg_tuple_get(dlvobj, "trust-anchor"))) {
2603234010Sdougb			/* If "no", skip; if "auto", use global default */
2604234010Sdougb			if (!strcasecmp(dom, "no"))
2605234010Sdougb				result = ISC_R_NOTFOUND;
2606234010Sdougb			else if (!strcasecmp(dom, "auto")) {
2607234010Sdougb				auto_dlv = ISC_TRUE;
2608234010Sdougb				obj = NULL;
2609234010Sdougb				result = cfg_map_get(ns_g_defaults,
2610234010Sdougb						     "dnssec-lookaside", &obj);
2611234010Sdougb			}
2612224092Sdougb		}
2613224092Sdougb	}
2614224092Sdougb
2615224092Sdougb	if (result == ISC_R_SUCCESS) {
2616135446Strhodes		for (element = cfg_list_first(obj);
2617135446Strhodes		     element != NULL;
2618135446Strhodes		     element = cfg_list_next(element))
2619135446Strhodes		{
2620135446Strhodes			const char *str;
2621135446Strhodes			isc_buffer_t b;
2622135446Strhodes			dns_name_t *dlv;
2623135446Strhodes
2624135446Strhodes			obj = cfg_listelt_value(element);
2625135446Strhodes			str = cfg_obj_asstring(cfg_tuple_get(obj,
2626135446Strhodes							     "trust-anchor"));
2627135446Strhodes			isc_buffer_init(&b, str, strlen(str));
2628135446Strhodes			isc_buffer_add(&b, strlen(str));
2629135446Strhodes			dlv = dns_fixedname_name(&view->dlv_fixed);
2630135446Strhodes			CHECK(dns_name_fromtext(dlv, &b, dns_rootname,
2631224092Sdougb						DNS_NAME_DOWNCASE, NULL));
2632135446Strhodes			view->dlv = dns_fixedname_name(&view->dlv_fixed);
2633135446Strhodes		}
2634135446Strhodes	} else
2635135446Strhodes		view->dlv = NULL;
2636135446Strhodes
2637135446Strhodes	/*
2638135446Strhodes	 * For now, there is only one kind of trusted keys, the
2639135446Strhodes	 * "security roots".
2640135446Strhodes	 */
2641224092Sdougb	CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys,
2642224092Sdougb					auto_dlv, auto_root, mctx));
2643170222Sdougb	dns_resolver_resetmustbesecure(view->resolver);
2644170222Sdougb	obj = NULL;
2645170222Sdougb	result = ns_config_get(maps, "dnssec-must-be-secure", &obj);
2646170222Sdougb	if (result == ISC_R_SUCCESS)
2647170222Sdougb		CHECK(mustbesecure(obj, view->resolver));
2648135446Strhodes
2649135446Strhodes	obj = NULL;
2650135446Strhodes	result = ns_config_get(maps, "preferred-glue", &obj);
2651135446Strhodes	if (result == ISC_R_SUCCESS) {
2652135446Strhodes		str = cfg_obj_asstring(obj);
2653135446Strhodes		if (strcasecmp(str, "a") == 0)
2654135446Strhodes			view->preferred_glue = dns_rdatatype_a;
2655135446Strhodes		else if (strcasecmp(str, "aaaa") == 0)
2656135446Strhodes			view->preferred_glue = dns_rdatatype_aaaa;
2657135446Strhodes		else
2658135446Strhodes			view->preferred_glue = 0;
2659135446Strhodes	} else
2660135446Strhodes		view->preferred_glue = 0;
2661135446Strhodes
2662135446Strhodes	obj = NULL;
2663135446Strhodes	result = ns_config_get(maps, "root-delegation-only", &obj);
2664135446Strhodes	if (result == ISC_R_SUCCESS) {
2665135446Strhodes		dns_view_setrootdelonly(view, ISC_TRUE);
2666135446Strhodes		if (!cfg_obj_isvoid(obj)) {
2667135446Strhodes			dns_fixedname_t fixed;
2668135446Strhodes			dns_name_t *name;
2669135446Strhodes			isc_buffer_t b;
2670165071Sdougb			const char *str;
2671165071Sdougb			const cfg_obj_t *exclude;
2672135446Strhodes
2673135446Strhodes			dns_fixedname_init(&fixed);
2674135446Strhodes			name = dns_fixedname_name(&fixed);
2675135446Strhodes			for (element = cfg_list_first(obj);
2676135446Strhodes			     element != NULL;
2677135446Strhodes			     element = cfg_list_next(element)) {
2678135446Strhodes				exclude = cfg_listelt_value(element);
2679135446Strhodes				str = cfg_obj_asstring(exclude);
2680135446Strhodes				isc_buffer_init(&b, str, strlen(str));
2681135446Strhodes				isc_buffer_add(&b, strlen(str));
2682135446Strhodes				CHECK(dns_name_fromtext(name, &b, dns_rootname,
2683224092Sdougb							0, NULL));
2684135446Strhodes				CHECK(dns_view_excludedelegationonly(view,
2685135446Strhodes								     name));
2686135446Strhodes			}
2687135446Strhodes		}
2688135446Strhodes	} else
2689135446Strhodes		dns_view_setrootdelonly(view, ISC_FALSE);
2690135446Strhodes
2691170222Sdougb	/*
2692170222Sdougb	 * Setup automatic empty zones.  If recursion is off then
2693170222Sdougb	 * they are disabled by default.
2694170222Sdougb	 */
2695170222Sdougb	obj = NULL;
2696170222Sdougb	(void)ns_config_get(maps, "empty-zones-enable", &obj);
2697170222Sdougb	(void)ns_config_get(maps, "disable-empty-zone", &disablelist);
2698170222Sdougb	if (obj == NULL && disablelist == NULL &&
2699170222Sdougb	    view->rdclass == dns_rdataclass_in) {
2700170222Sdougb		rfc1918 = ISC_FALSE;
2701170222Sdougb		empty_zones_enable = view->recursion;
2702170222Sdougb	} else if (view->rdclass == dns_rdataclass_in) {
2703170222Sdougb		rfc1918 = ISC_TRUE;
2704170222Sdougb		if (obj != NULL)
2705170222Sdougb			empty_zones_enable = cfg_obj_asboolean(obj);
2706170222Sdougb		else
2707170222Sdougb			empty_zones_enable = view->recursion;
2708170222Sdougb	} else {
2709170222Sdougb		rfc1918 = ISC_FALSE;
2710170222Sdougb		empty_zones_enable = ISC_FALSE;
2711170222Sdougb	}
2712234010Sdougb	if (empty_zones_enable && !lwresd_g_useresolvconf) {
2713170222Sdougb		const char *empty;
2714170222Sdougb		int empty_zone = 0;
2715170222Sdougb		dns_fixedname_t fixed;
2716170222Sdougb		dns_name_t *name;
2717170222Sdougb		isc_buffer_t buffer;
2718170222Sdougb		const char *str;
2719170222Sdougb		char server[DNS_NAME_FORMATSIZE + 1];
2720170222Sdougb		char contact[DNS_NAME_FORMATSIZE + 1];
2721170222Sdougb		isc_boolean_t logit;
2722170222Sdougb		const char *empty_dbtype[4] =
2723170222Sdougb				    { "_builtin", "empty", NULL, NULL };
2724170222Sdougb		int empty_dbtypec = 4;
2725193149Sdougb		isc_boolean_t zonestats_on;
2726170222Sdougb
2727170222Sdougb		dns_fixedname_init(&fixed);
2728170222Sdougb		name = dns_fixedname_name(&fixed);
2729170222Sdougb
2730170222Sdougb		obj = NULL;
2731170222Sdougb		result = ns_config_get(maps, "empty-server", &obj);
2732170222Sdougb		if (result == ISC_R_SUCCESS) {
2733170222Sdougb			str = cfg_obj_asstring(obj);
2734170222Sdougb			isc_buffer_init(&buffer, str, strlen(str));
2735170222Sdougb			isc_buffer_add(&buffer, strlen(str));
2736224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
2737224092Sdougb						NULL));
2738170222Sdougb			isc_buffer_init(&buffer, server, sizeof(server) - 1);
2739170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
2740170222Sdougb			server[isc_buffer_usedlength(&buffer)] = 0;
2741170222Sdougb			empty_dbtype[2] = server;
2742170222Sdougb		} else
2743170222Sdougb			empty_dbtype[2] = "@";
2744170222Sdougb
2745170222Sdougb		obj = NULL;
2746170222Sdougb		result = ns_config_get(maps, "empty-contact", &obj);
2747170222Sdougb		if (result == ISC_R_SUCCESS) {
2748170222Sdougb			str = cfg_obj_asstring(obj);
2749170222Sdougb			isc_buffer_init(&buffer, str, strlen(str));
2750170222Sdougb			isc_buffer_add(&buffer, strlen(str));
2751224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
2752224092Sdougb						NULL));
2753170222Sdougb			isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
2754170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
2755170222Sdougb			contact[isc_buffer_usedlength(&buffer)] = 0;
2756170222Sdougb			empty_dbtype[3] = contact;
2757170222Sdougb		} else
2758170222Sdougb			empty_dbtype[3] = ".";
2759170222Sdougb
2760193149Sdougb		obj = NULL;
2761193149Sdougb		result = ns_config_get(maps, "zone-statistics", &obj);
2762193149Sdougb		INSIST(result == ISC_R_SUCCESS);
2763193149Sdougb		zonestats_on = cfg_obj_asboolean(obj);
2764193149Sdougb
2765170222Sdougb		logit = ISC_TRUE;
2766170222Sdougb		for (empty = empty_zones[empty_zone].zone;
2767170222Sdougb		     empty != NULL;
2768170222Sdougb		     empty = empty_zones[++empty_zone].zone)
2769170222Sdougb		{
2770170222Sdougb			dns_forwarders_t *forwarders = NULL;
2771170222Sdougb			dns_view_t *pview = NULL;
2772170222Sdougb
2773170222Sdougb			isc_buffer_init(&buffer, empty, strlen(empty));
2774170222Sdougb			isc_buffer_add(&buffer, strlen(empty));
2775170222Sdougb			/*
2776170222Sdougb			 * Look for zone on drop list.
2777170222Sdougb			 */
2778224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
2779224092Sdougb						NULL));
2780170222Sdougb			if (disablelist != NULL &&
2781170222Sdougb			    on_disable_list(disablelist, name))
2782170222Sdougb				continue;
2783170222Sdougb
2784170222Sdougb			/*
2785170222Sdougb			 * This zone already exists.
2786170222Sdougb			 */
2787170222Sdougb			(void)dns_view_findzone(view, name, &zone);
2788170222Sdougb			if (zone != NULL) {
2789193149Sdougb				CHECK(setquerystats(zone, mctx, zonestats_on));
2790170222Sdougb				dns_zone_detach(&zone);
2791170222Sdougb				continue;
2792170222Sdougb			}
2793170222Sdougb
2794170222Sdougb			/*
2795170222Sdougb			 * If we would forward this name don't add a
2796170222Sdougb			 * empty zone for it.
2797170222Sdougb			 */
2798170222Sdougb			result = dns_fwdtable_find(view->fwdtable, name,
2799170222Sdougb						   &forwarders);
2800170222Sdougb			if (result == ISC_R_SUCCESS &&
2801170222Sdougb			    forwarders->fwdpolicy == dns_fwdpolicy_only)
2802170222Sdougb				continue;
2803186462Sdougb
2804170222Sdougb			if (!rfc1918 && empty_zones[empty_zone].rfc1918) {
2805170222Sdougb				if (logit) {
2806170222Sdougb					isc_log_write(ns_g_lctx,
2807170222Sdougb						      NS_LOGCATEGORY_GENERAL,
2808170222Sdougb						      NS_LOGMODULE_SERVER,
2809170222Sdougb						      ISC_LOG_WARNING,
2810186462Sdougb						      "Warning%s%s: "
2811170222Sdougb						      "'empty-zones-enable/"
2812170222Sdougb						      "disable-empty-zone' "
2813170222Sdougb						      "not set: disabling "
2814170222Sdougb						      "RFC 1918 empty zones",
2815170222Sdougb						      sep, viewname);
2816170222Sdougb					logit = ISC_FALSE;
2817170222Sdougb				}
2818170222Sdougb				continue;
2819170222Sdougb			}
2820170222Sdougb
2821170222Sdougb			/*
2822170222Sdougb			 * See if we can re-use a existing zone.
2823170222Sdougb			 */
2824170222Sdougb			result = dns_viewlist_find(&ns_g_server->viewlist,
2825170222Sdougb						   view->name, view->rdclass,
2826170222Sdougb						   &pview);
2827170222Sdougb			if (result != ISC_R_NOTFOUND &&
2828170222Sdougb			    result != ISC_R_SUCCESS)
2829170222Sdougb				goto cleanup;
2830170222Sdougb
2831170222Sdougb			if (pview != NULL) {
2832170222Sdougb				(void)dns_view_findzone(pview, name, &zone);
2833170222Sdougb				dns_view_detach(&pview);
2834170222Sdougb				if (zone != NULL)
2835170222Sdougb					check_dbtype(&zone, empty_dbtypec,
2836170222Sdougb						     empty_dbtype, mctx);
2837170222Sdougb				if (zone != NULL) {
2838170222Sdougb					dns_zone_setview(zone, view);
2839174187Sdougb					CHECK(dns_view_addzone(view, zone));
2840193149Sdougb					CHECK(setquerystats(zone, mctx,
2841193149Sdougb							    zonestats_on));
2842170222Sdougb					dns_zone_detach(&zone);
2843170222Sdougb					continue;
2844170222Sdougb				}
2845170222Sdougb			}
2846170222Sdougb
2847170222Sdougb			CHECK(dns_zone_create(&zone, mctx));
2848170222Sdougb			CHECK(dns_zone_setorigin(zone, name));
2849170222Sdougb			dns_zone_setview(zone, view);
2850234010Sdougb			CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr,
2851234010Sdougb						     zone));
2852170222Sdougb			dns_zone_setclass(zone, view->rdclass);
2853170222Sdougb			dns_zone_settype(zone, dns_zone_master);
2854193149Sdougb			dns_zone_setstats(zone, ns_g_server->zonestats);
2855170222Sdougb			CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
2856186462Sdougb						 empty_dbtype));
2857170222Sdougb			if (view->queryacl != NULL)
2858170222Sdougb				dns_zone_setqueryacl(zone, view->queryacl);
2859193149Sdougb			if (view->queryonacl != NULL)
2860193149Sdougb				dns_zone_setqueryonacl(zone, view->queryonacl);
2861170222Sdougb			dns_zone_setdialup(zone, dns_dialuptype_no);
2862170222Sdougb			dns_zone_setnotifytype(zone, dns_notifytype_no);
2863170222Sdougb			dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS,
2864170222Sdougb					   ISC_TRUE);
2865193149Sdougb			CHECK(setquerystats(zone, mctx, zonestats_on));
2866170222Sdougb			CHECK(dns_view_addzone(view, zone));
2867170222Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2868170222Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
2869170222Sdougb				      "automatic empty zone%s%s: %s",
2870170222Sdougb				      sep, viewname,  empty);
2871170222Sdougb			dns_zone_detach(&zone);
2872170222Sdougb		}
2873170222Sdougb	}
2874186462Sdougb
2875224092Sdougb	/*
2876224092Sdougb	 * Make the list of response policy zone names for views that
2877224092Sdougb	 * are used for real lookups and so care about hints.
2878224092Sdougb	 */
2879224092Sdougb	zonelist = NULL;
2880224092Sdougb	if (view->rdclass == dns_rdataclass_in && need_hints) {
2881224092Sdougb		obj = NULL;
2882224092Sdougb		result = ns_config_get(maps, "response-policy", &obj);
2883224092Sdougb		if (result == ISC_R_SUCCESS)
2884224092Sdougb			cfg_map_get(obj, "zone", &zonelist);
2885224092Sdougb	}
2886225361Sdougb
2887224092Sdougb	if (zonelist != NULL) {
2888224092Sdougb		for (element = cfg_list_first(zonelist);
2889224092Sdougb		     element != NULL;
2890224092Sdougb		     element = cfg_list_next(element)) {
2891224092Sdougb			result = configure_rpz(view, element);
2892224092Sdougb			if (result != ISC_R_SUCCESS)
2893224092Sdougb				goto cleanup;
2894224092Sdougb			dns_rpz_set_need(ISC_TRUE);
2895224092Sdougb		}
2896224092Sdougb	}
2897224092Sdougb
2898135446Strhodes	result = ISC_R_SUCCESS;
2899135446Strhodes
2900135446Strhodes cleanup:
2901224092Sdougb	if (clients != NULL)
2902224092Sdougb		dns_acl_detach(&clients);
2903224092Sdougb	if (mapped != NULL)
2904224092Sdougb		dns_acl_detach(&mapped);
2905224092Sdougb	if (excluded != NULL)
2906224092Sdougb		dns_acl_detach(&excluded);
2907224092Sdougb	if (ring != NULL)
2908224092Sdougb		dns_tsigkeyring_detach(&ring);
2909170222Sdougb	if (zone != NULL)
2910170222Sdougb		dns_zone_detach(&zone);
2911135446Strhodes	if (dispatch4 != NULL)
2912135446Strhodes		dns_dispatch_detach(&dispatch4);
2913135446Strhodes	if (dispatch6 != NULL)
2914135446Strhodes		dns_dispatch_detach(&dispatch6);
2915193149Sdougb	if (resstats != NULL)
2916193149Sdougb		isc_stats_detach(&resstats);
2917193149Sdougb	if (resquerystats != NULL)
2918193149Sdougb		dns_stats_detach(&resquerystats);
2919135446Strhodes	if (order != NULL)
2920135446Strhodes		dns_order_detach(&order);
2921135446Strhodes	if (cmctx != NULL)
2922135446Strhodes		isc_mem_detach(&cmctx);
2923225361Sdougb	if (hmctx != NULL)
2924225361Sdougb		isc_mem_detach(&hmctx);
2925135446Strhodes
2926135446Strhodes	if (cache != NULL)
2927135446Strhodes		dns_cache_detach(&cache);
2928135446Strhodes
2929135446Strhodes	return (result);
2930135446Strhodes}
2931135446Strhodes
2932135446Strhodesstatic isc_result_t
2933135446Strhodesconfigure_hints(dns_view_t *view, const char *filename) {
2934135446Strhodes	isc_result_t result;
2935135446Strhodes	dns_db_t *db;
2936135446Strhodes
2937135446Strhodes	db = NULL;
2938135446Strhodes	result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
2939135446Strhodes	if (result == ISC_R_SUCCESS) {
2940135446Strhodes		dns_view_sethints(view, db);
2941135446Strhodes		dns_db_detach(&db);
2942135446Strhodes	}
2943135446Strhodes
2944135446Strhodes	return (result);
2945135446Strhodes}
2946135446Strhodes
2947135446Strhodesstatic isc_result_t
2948165071Sdougbconfigure_alternates(const cfg_obj_t *config, dns_view_t *view,
2949165071Sdougb		     const cfg_obj_t *alternates)
2950135446Strhodes{
2951165071Sdougb	const cfg_obj_t *portobj;
2952165071Sdougb	const cfg_obj_t *addresses;
2953165071Sdougb	const cfg_listelt_t *element;
2954135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
2955135446Strhodes	in_port_t port;
2956135446Strhodes
2957135446Strhodes	/*
2958135446Strhodes	 * Determine which port to send requests to.
2959135446Strhodes	 */
2960135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
2961135446Strhodes		port = ns_g_port;
2962135446Strhodes	else
2963135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
2964135446Strhodes
2965135446Strhodes	if (alternates != NULL) {
2966135446Strhodes		portobj = cfg_tuple_get(alternates, "port");
2967135446Strhodes		if (cfg_obj_isuint32(portobj)) {
2968135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
2969135446Strhodes			if (val > ISC_UINT16_MAX) {
2970135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
2971135446Strhodes					    "port '%u' out of range", val);
2972135446Strhodes				return (ISC_R_RANGE);
2973135446Strhodes			}
2974135446Strhodes			port = (in_port_t) val;
2975135446Strhodes		}
2976135446Strhodes	}
2977135446Strhodes
2978135446Strhodes	addresses = NULL;
2979135446Strhodes	if (alternates != NULL)
2980135446Strhodes		addresses = cfg_tuple_get(alternates, "addresses");
2981135446Strhodes
2982135446Strhodes	for (element = cfg_list_first(addresses);
2983135446Strhodes	     element != NULL;
2984135446Strhodes	     element = cfg_list_next(element))
2985135446Strhodes	{
2986165071Sdougb		const cfg_obj_t *alternate = cfg_listelt_value(element);
2987135446Strhodes		isc_sockaddr_t sa;
2988135446Strhodes
2989135446Strhodes		if (!cfg_obj_issockaddr(alternate)) {
2990135446Strhodes			dns_fixedname_t fixed;
2991135446Strhodes			dns_name_t *name;
2992165071Sdougb			const char *str = cfg_obj_asstring(cfg_tuple_get(
2993165071Sdougb							   alternate, "name"));
2994135446Strhodes			isc_buffer_t buffer;
2995135446Strhodes			in_port_t myport = port;
2996135446Strhodes
2997135446Strhodes			isc_buffer_init(&buffer, str, strlen(str));
2998135446Strhodes			isc_buffer_add(&buffer, strlen(str));
2999135446Strhodes			dns_fixedname_init(&fixed);
3000135446Strhodes			name = dns_fixedname_name(&fixed);
3001224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3002224092Sdougb						NULL));
3003135446Strhodes
3004135446Strhodes			portobj = cfg_tuple_get(alternate, "port");
3005135446Strhodes			if (cfg_obj_isuint32(portobj)) {
3006135446Strhodes				isc_uint32_t val = cfg_obj_asuint32(portobj);
3007135446Strhodes				if (val > ISC_UINT16_MAX) {
3008135446Strhodes					cfg_obj_log(portobj, ns_g_lctx,
3009135446Strhodes						    ISC_LOG_ERROR,
3010135446Strhodes						    "port '%u' out of range",
3011135446Strhodes						     val);
3012135446Strhodes					return (ISC_R_RANGE);
3013135446Strhodes				}
3014135446Strhodes				myport = (in_port_t) val;
3015135446Strhodes			}
3016135446Strhodes			CHECK(dns_resolver_addalternate(view->resolver, NULL,
3017135446Strhodes							name, myport));
3018135446Strhodes			continue;
3019135446Strhodes		}
3020135446Strhodes
3021135446Strhodes		sa = *cfg_obj_assockaddr(alternate);
3022135446Strhodes		if (isc_sockaddr_getport(&sa) == 0)
3023135446Strhodes			isc_sockaddr_setport(&sa, port);
3024135446Strhodes		CHECK(dns_resolver_addalternate(view->resolver, &sa,
3025135446Strhodes						NULL, 0));
3026135446Strhodes	}
3027135446Strhodes
3028135446Strhodes cleanup:
3029135446Strhodes	return (result);
3030135446Strhodes}
3031135446Strhodes
3032135446Strhodesstatic isc_result_t
3033165071Sdougbconfigure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
3034165071Sdougb		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype)
3035135446Strhodes{
3036165071Sdougb	const cfg_obj_t *portobj;
3037165071Sdougb	const cfg_obj_t *faddresses;
3038165071Sdougb	const cfg_listelt_t *element;
3039135446Strhodes	dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
3040135446Strhodes	isc_sockaddrlist_t addresses;
3041135446Strhodes	isc_sockaddr_t *sa;
3042135446Strhodes	isc_result_t result;
3043135446Strhodes	in_port_t port;
3044135446Strhodes
3045193149Sdougb	ISC_LIST_INIT(addresses);
3046193149Sdougb
3047135446Strhodes	/*
3048135446Strhodes	 * Determine which port to send forwarded requests to.
3049135446Strhodes	 */
3050135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
3051135446Strhodes		port = ns_g_port;
3052135446Strhodes	else
3053135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
3054135446Strhodes
3055135446Strhodes	if (forwarders != NULL) {
3056135446Strhodes		portobj = cfg_tuple_get(forwarders, "port");
3057135446Strhodes		if (cfg_obj_isuint32(portobj)) {
3058135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
3059135446Strhodes			if (val > ISC_UINT16_MAX) {
3060135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
3061135446Strhodes					    "port '%u' out of range", val);
3062135446Strhodes				return (ISC_R_RANGE);
3063135446Strhodes			}
3064135446Strhodes			port = (in_port_t) val;
3065135446Strhodes		}
3066135446Strhodes	}
3067135446Strhodes
3068135446Strhodes	faddresses = NULL;
3069135446Strhodes	if (forwarders != NULL)
3070135446Strhodes		faddresses = cfg_tuple_get(forwarders, "addresses");
3071135446Strhodes
3072135446Strhodes	for (element = cfg_list_first(faddresses);
3073135446Strhodes	     element != NULL;
3074135446Strhodes	     element = cfg_list_next(element))
3075135446Strhodes	{
3076165071Sdougb		const cfg_obj_t *forwarder = cfg_listelt_value(element);
3077135446Strhodes		sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t));
3078135446Strhodes		if (sa == NULL) {
3079135446Strhodes			result = ISC_R_NOMEMORY;
3080135446Strhodes			goto cleanup;
3081135446Strhodes		}
3082135446Strhodes		*sa = *cfg_obj_assockaddr(forwarder);
3083135446Strhodes		if (isc_sockaddr_getport(sa) == 0)
3084135446Strhodes			isc_sockaddr_setport(sa, port);
3085135446Strhodes		ISC_LINK_INIT(sa, link);
3086135446Strhodes		ISC_LIST_APPEND(addresses, sa, link);
3087135446Strhodes	}
3088135446Strhodes
3089135446Strhodes	if (ISC_LIST_EMPTY(addresses)) {
3090135446Strhodes		if (forwardtype != NULL)
3091135446Strhodes			cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
3092135446Strhodes				    "no forwarders seen; disabling "
3093135446Strhodes				    "forwarding");
3094135446Strhodes		fwdpolicy = dns_fwdpolicy_none;
3095135446Strhodes	} else {
3096135446Strhodes		if (forwardtype == NULL)
3097135446Strhodes			fwdpolicy = dns_fwdpolicy_first;
3098135446Strhodes		else {
3099165071Sdougb			const char *forwardstr = cfg_obj_asstring(forwardtype);
3100135446Strhodes			if (strcasecmp(forwardstr, "first") == 0)
3101135446Strhodes				fwdpolicy = dns_fwdpolicy_first;
3102135446Strhodes			else if (strcasecmp(forwardstr, "only") == 0)
3103135446Strhodes				fwdpolicy = dns_fwdpolicy_only;
3104135446Strhodes			else
3105135446Strhodes				INSIST(0);
3106135446Strhodes		}
3107135446Strhodes	}
3108135446Strhodes
3109135446Strhodes	result = dns_fwdtable_add(view->fwdtable, origin, &addresses,
3110135446Strhodes				  fwdpolicy);
3111135446Strhodes	if (result != ISC_R_SUCCESS) {
3112135446Strhodes		char namebuf[DNS_NAME_FORMATSIZE];
3113135446Strhodes		dns_name_format(origin, namebuf, sizeof(namebuf));
3114135446Strhodes		cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
3115135446Strhodes			    "could not set up forwarding for domain '%s': %s",
3116135446Strhodes			    namebuf, isc_result_totext(result));
3117135446Strhodes		goto cleanup;
3118135446Strhodes	}
3119135446Strhodes
3120135446Strhodes	result = ISC_R_SUCCESS;
3121135446Strhodes
3122135446Strhodes cleanup:
3123135446Strhodes
3124135446Strhodes	while (!ISC_LIST_EMPTY(addresses)) {
3125135446Strhodes		sa = ISC_LIST_HEAD(addresses);
3126135446Strhodes		ISC_LIST_UNLINK(addresses, sa, link);
3127135446Strhodes		isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t));
3128135446Strhodes	}
3129135446Strhodes
3130135446Strhodes	return (result);
3131135446Strhodes}
3132135446Strhodes
3133135446Strhodesstatic isc_result_t
3134225361Sdougbget_viewinfo(const cfg_obj_t *vconfig, const char **namep,
3135225361Sdougb	     dns_rdataclass_t *classp)
3136165071Sdougb{
3137225361Sdougb	isc_result_t result = ISC_R_SUCCESS;
3138135446Strhodes	const char *viewname;
3139135446Strhodes	dns_rdataclass_t viewclass;
3140135446Strhodes
3141225361Sdougb	REQUIRE(namep != NULL && *namep == NULL);
3142225361Sdougb	REQUIRE(classp != NULL);
3143225361Sdougb
3144135446Strhodes	if (vconfig != NULL) {
3145165071Sdougb		const cfg_obj_t *classobj = NULL;
3146135446Strhodes
3147135446Strhodes		viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
3148135446Strhodes		classobj = cfg_tuple_get(vconfig, "class");
3149135446Strhodes		result = ns_config_getclass(classobj, dns_rdataclass_in,
3150135446Strhodes					    &viewclass);
3151135446Strhodes	} else {
3152135446Strhodes		viewname = "_default";
3153135446Strhodes		viewclass = dns_rdataclass_in;
3154135446Strhodes	}
3155225361Sdougb
3156225361Sdougb	*namep = viewname;
3157225361Sdougb	*classp = viewclass;
3158225361Sdougb
3159225361Sdougb	return (result);
3160225361Sdougb}
3161225361Sdougb
3162225361Sdougb/*
3163225361Sdougb * Find a view based on its configuration info and attach to it.
3164225361Sdougb *
3165225361Sdougb * If 'vconfig' is NULL, attach to the default view.
3166225361Sdougb */
3167225361Sdougbstatic isc_result_t
3168225361Sdougbfind_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
3169225361Sdougb	  dns_view_t **viewp)
3170225361Sdougb{
3171225361Sdougb	isc_result_t result;
3172225361Sdougb	const char *viewname = NULL;
3173225361Sdougb	dns_rdataclass_t viewclass;
3174225361Sdougb	dns_view_t *view = NULL;
3175225361Sdougb
3176225361Sdougb	result = get_viewinfo(vconfig, &viewname, &viewclass);
3177225361Sdougb	if (result != ISC_R_SUCCESS)
3178225361Sdougb		return (result);
3179225361Sdougb
3180135446Strhodes	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
3181225361Sdougb	if (result != ISC_R_SUCCESS)
3182225361Sdougb		return (result);
3183225361Sdougb
3184225361Sdougb	*viewp = view;
3185225361Sdougb	return (ISC_R_SUCCESS);
3186225361Sdougb}
3187225361Sdougb
3188225361Sdougb/*
3189225361Sdougb * Create a new view and add it to the list.
3190225361Sdougb *
3191225361Sdougb * If 'vconfig' is NULL, create the default view.
3192225361Sdougb *
3193225361Sdougb * The view created is attached to '*viewp'.
3194225361Sdougb */
3195225361Sdougbstatic isc_result_t
3196225361Sdougbcreate_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
3197225361Sdougb	    dns_view_t **viewp)
3198225361Sdougb{
3199225361Sdougb	isc_result_t result;
3200225361Sdougb	const char *viewname = NULL;
3201225361Sdougb	dns_rdataclass_t viewclass;
3202225361Sdougb	dns_view_t *view = NULL;
3203225361Sdougb
3204225361Sdougb	result = get_viewinfo(vconfig, &viewname, &viewclass);
3205225361Sdougb	if (result != ISC_R_SUCCESS)
3206225361Sdougb		return (result);
3207225361Sdougb
3208225361Sdougb	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
3209135446Strhodes	if (result == ISC_R_SUCCESS)
3210135446Strhodes		return (ISC_R_EXISTS);
3211135446Strhodes	if (result != ISC_R_NOTFOUND)
3212135446Strhodes		return (result);
3213135446Strhodes	INSIST(view == NULL);
3214135446Strhodes
3215135446Strhodes	result = dns_view_create(ns_g_mctx, viewclass, viewname, &view);
3216135446Strhodes	if (result != ISC_R_SUCCESS)
3217135446Strhodes		return (result);
3218135446Strhodes
3219135446Strhodes	ISC_LIST_APPEND(*viewlist, view, link);
3220135446Strhodes	dns_view_attach(view, viewp);
3221135446Strhodes	return (ISC_R_SUCCESS);
3222135446Strhodes}
3223135446Strhodes
3224135446Strhodes/*
3225135446Strhodes * Configure or reconfigure a zone.
3226135446Strhodes */
3227135446Strhodesstatic isc_result_t
3228165071Sdougbconfigure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
3229165071Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
3230224092Sdougb	       cfg_aclconfctx_t *aclconf, isc_boolean_t added)
3231135446Strhodes{
3232135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
3233135446Strhodes	dns_zone_t *zone = NULL;	/* New or reused zone */
3234135446Strhodes	dns_zone_t *dupzone = NULL;
3235165071Sdougb	const cfg_obj_t *options = NULL;
3236165071Sdougb	const cfg_obj_t *zoptions = NULL;
3237165071Sdougb	const cfg_obj_t *typeobj = NULL;
3238165071Sdougb	const cfg_obj_t *forwarders = NULL;
3239165071Sdougb	const cfg_obj_t *forwardtype = NULL;
3240165071Sdougb	const cfg_obj_t *only = NULL;
3241135446Strhodes	isc_result_t result;
3242135446Strhodes	isc_result_t tresult;
3243135446Strhodes	isc_buffer_t buffer;
3244135446Strhodes	dns_fixedname_t fixorigin;
3245135446Strhodes	dns_name_t *origin;
3246135446Strhodes	const char *zname;
3247135446Strhodes	dns_rdataclass_t zclass;
3248135446Strhodes	const char *ztypestr;
3249135446Strhodes
3250135446Strhodes	options = NULL;
3251135446Strhodes	(void)cfg_map_get(config, "options", &options);
3252135446Strhodes
3253135446Strhodes	zoptions = cfg_tuple_get(zconfig, "options");
3254135446Strhodes
3255135446Strhodes	/*
3256135446Strhodes	 * Get the zone origin as a dns_name_t.
3257135446Strhodes	 */
3258135446Strhodes	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
3259135446Strhodes	isc_buffer_init(&buffer, zname, strlen(zname));
3260135446Strhodes	isc_buffer_add(&buffer, strlen(zname));
3261135446Strhodes	dns_fixedname_init(&fixorigin);
3262135446Strhodes	CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin),
3263224092Sdougb				&buffer, dns_rootname, 0, NULL));
3264135446Strhodes	origin = dns_fixedname_name(&fixorigin);
3265135446Strhodes
3266135446Strhodes	CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
3267135446Strhodes				 view->rdclass, &zclass));
3268135446Strhodes	if (zclass != view->rdclass) {
3269135446Strhodes		const char *vname = NULL;
3270135446Strhodes		if (vconfig != NULL)
3271135446Strhodes			vname = cfg_obj_asstring(cfg_tuple_get(vconfig,
3272135446Strhodes							       "name"));
3273135446Strhodes		else
3274135446Strhodes			vname = "<default view>";
3275186462Sdougb
3276135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3277135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3278135446Strhodes			      "zone '%s': wrong class for view '%s'",
3279135446Strhodes			      zname, vname);
3280135446Strhodes		result = ISC_R_FAILURE;
3281135446Strhodes		goto cleanup;
3282135446Strhodes	}
3283135446Strhodes
3284135446Strhodes	(void)cfg_map_get(zoptions, "type", &typeobj);
3285135446Strhodes	if (typeobj == NULL) {
3286135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3287135446Strhodes			    "zone '%s' 'type' not specified", zname);
3288135446Strhodes		return (ISC_R_FAILURE);
3289135446Strhodes	}
3290135446Strhodes	ztypestr = cfg_obj_asstring(typeobj);
3291135446Strhodes
3292135446Strhodes	/*
3293224092Sdougb	 * "hints zones" aren't zones.	If we've got one,
3294135446Strhodes	 * configure it and return.
3295135446Strhodes	 */
3296135446Strhodes	if (strcasecmp(ztypestr, "hint") == 0) {
3297165071Sdougb		const cfg_obj_t *fileobj = NULL;
3298135446Strhodes		if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) {
3299135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3300135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3301135446Strhodes				      "zone '%s': 'file' not specified",
3302135446Strhodes				      zname);
3303135446Strhodes			result = ISC_R_FAILURE;
3304135446Strhodes			goto cleanup;
3305135446Strhodes		}
3306135446Strhodes		if (dns_name_equal(origin, dns_rootname)) {
3307165071Sdougb			const char *hintsfile = cfg_obj_asstring(fileobj);
3308135446Strhodes
3309135446Strhodes			result = configure_hints(view, hintsfile);
3310135446Strhodes			if (result != ISC_R_SUCCESS) {
3311135446Strhodes				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3312135446Strhodes					      NS_LOGMODULE_SERVER,
3313135446Strhodes					      ISC_LOG_ERROR,
3314135446Strhodes					      "could not configure root hints "
3315135446Strhodes					      "from '%s': %s", hintsfile,
3316135446Strhodes					      isc_result_totext(result));
3317135446Strhodes				goto cleanup;
3318135446Strhodes			}
3319135446Strhodes			/*
3320135446Strhodes			 * Hint zones may also refer to delegation only points.
3321135446Strhodes			 */
3322135446Strhodes			only = NULL;
3323135446Strhodes			tresult = cfg_map_get(zoptions, "delegation-only",
3324135446Strhodes					      &only);
3325135446Strhodes			if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only))
3326135446Strhodes				CHECK(dns_view_adddelegationonly(view, origin));
3327135446Strhodes		} else {
3328135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3329135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
3330135446Strhodes				      "ignoring non-root hint zone '%s'",
3331135446Strhodes				      zname);
3332135446Strhodes			result = ISC_R_SUCCESS;
3333135446Strhodes		}
3334135446Strhodes		/* Skip ordinary zone processing. */
3335135446Strhodes		goto cleanup;
3336135446Strhodes	}
3337135446Strhodes
3338135446Strhodes	/*
3339135446Strhodes	 * "forward zones" aren't zones either.  Translate this syntax into
3340135446Strhodes	 * the appropriate selective forwarding configuration and return.
3341135446Strhodes	 */
3342135446Strhodes	if (strcasecmp(ztypestr, "forward") == 0) {
3343135446Strhodes		forwardtype = NULL;
3344135446Strhodes		forwarders = NULL;
3345135446Strhodes
3346135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
3347135446Strhodes		(void)cfg_map_get(zoptions, "forwarders", &forwarders);
3348135446Strhodes		result = configure_forward(config, view, origin, forwarders,
3349135446Strhodes					   forwardtype);
3350135446Strhodes		goto cleanup;
3351135446Strhodes	}
3352135446Strhodes
3353135446Strhodes	/*
3354135446Strhodes	 * "delegation-only zones" aren't zones either.
3355135446Strhodes	 */
3356135446Strhodes	if (strcasecmp(ztypestr, "delegation-only") == 0) {
3357135446Strhodes		result = dns_view_adddelegationonly(view, origin);
3358135446Strhodes		goto cleanup;
3359135446Strhodes	}
3360135446Strhodes
3361135446Strhodes	/*
3362135446Strhodes	 * Check for duplicates in the new zone table.
3363135446Strhodes	 */
3364135446Strhodes	result = dns_view_findzone(view, origin, &dupzone);
3365135446Strhodes	if (result == ISC_R_SUCCESS) {
3366135446Strhodes		/*
3367135446Strhodes		 * We already have this zone!
3368135446Strhodes		 */
3369135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3370135446Strhodes			    "zone '%s' already exists", zname);
3371135446Strhodes		dns_zone_detach(&dupzone);
3372135446Strhodes		result = ISC_R_EXISTS;
3373135446Strhodes		goto cleanup;
3374135446Strhodes	}
3375135446Strhodes	INSIST(dupzone == NULL);
3376135446Strhodes
3377135446Strhodes	/*
3378135446Strhodes	 * See if we can reuse an existing zone.  This is
3379135446Strhodes	 * only possible if all of these are true:
3380135446Strhodes	 *   - The zone's view exists
3381135446Strhodes	 *   - A zone with the right name exists in the view
3382135446Strhodes	 *   - The zone is compatible with the config
3383135446Strhodes	 *     options (e.g., an existing master zone cannot
3384135446Strhodes	 *     be reused if the options specify a slave zone)
3385135446Strhodes	 */
3386135446Strhodes	result = dns_viewlist_find(&ns_g_server->viewlist,
3387135446Strhodes				   view->name, view->rdclass,
3388135446Strhodes				   &pview);
3389135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3390135446Strhodes		goto cleanup;
3391135446Strhodes	if (pview != NULL)
3392135446Strhodes		result = dns_view_findzone(pview, origin, &zone);
3393135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3394135446Strhodes		goto cleanup;
3395170222Sdougb	if (zone != NULL && !ns_zone_reusable(zone, zconfig))
3396170222Sdougb		dns_zone_detach(&zone);
3397135446Strhodes
3398135446Strhodes	if (zone != NULL) {
3399135446Strhodes		/*
3400135446Strhodes		 * We found a reusable zone.  Make it use the
3401135446Strhodes		 * new view.
3402135446Strhodes		 */
3403135446Strhodes		dns_zone_setview(zone, view);
3404170222Sdougb		if (view->acache != NULL)
3405170222Sdougb			dns_zone_setacache(zone, view->acache);
3406135446Strhodes	} else {
3407135446Strhodes		/*
3408135446Strhodes		 * We cannot reuse an existing zone, we have
3409135446Strhodes		 * to create a new one.
3410135446Strhodes		 */
3411135446Strhodes		CHECK(dns_zone_create(&zone, mctx));
3412135446Strhodes		CHECK(dns_zone_setorigin(zone, origin));
3413135446Strhodes		dns_zone_setview(zone, view);
3414170222Sdougb		if (view->acache != NULL)
3415170222Sdougb			dns_zone_setacache(zone, view->acache);
3416135446Strhodes		CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
3417193149Sdougb		dns_zone_setstats(zone, ns_g_server->zonestats);
3418135446Strhodes	}
3419135446Strhodes
3420135446Strhodes	/*
3421135446Strhodes	 * If the zone contains a 'forwarders' statement, configure
3422135446Strhodes	 * selective forwarding.
3423135446Strhodes	 */
3424135446Strhodes	forwarders = NULL;
3425135446Strhodes	if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS)
3426135446Strhodes	{
3427135446Strhodes		forwardtype = NULL;
3428135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
3429135446Strhodes		CHECK(configure_forward(config, view, origin, forwarders,
3430135446Strhodes					forwardtype));
3431135446Strhodes	}
3432135446Strhodes
3433135446Strhodes	/*
3434135446Strhodes	 * Stub and forward zones may also refer to delegation only points.
3435135446Strhodes	 */
3436135446Strhodes	only = NULL;
3437135446Strhodes	if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS)
3438135446Strhodes	{
3439135446Strhodes		if (cfg_obj_asboolean(only))
3440135446Strhodes			CHECK(dns_view_adddelegationonly(view, origin));
3441135446Strhodes	}
3442135446Strhodes
3443135446Strhodes	/*
3444224092Sdougb	 * Mark whether the zone was originally added at runtime or not
3445224092Sdougb	 */
3446224092Sdougb	dns_zone_setadded(zone, added);
3447224092Sdougb
3448224092Sdougb	/*
3449135446Strhodes	 * Configure the zone.
3450135446Strhodes	 */
3451135446Strhodes	CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone));
3452135446Strhodes
3453135446Strhodes	/*
3454135446Strhodes	 * Add the zone to its view in the new view list.
3455135446Strhodes	 */
3456135446Strhodes	CHECK(dns_view_addzone(view, zone));
3457135446Strhodes
3458234010Sdougb	/*
3459234010Sdougb	 * Ensure that zone keys are reloaded on reconfig
3460234010Sdougb	 */
3461234010Sdougb	if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0)
3462234010Sdougb		dns_zone_rekey(zone, ISC_FALSE);
3463234010Sdougb
3464135446Strhodes cleanup:
3465135446Strhodes	if (zone != NULL)
3466135446Strhodes		dns_zone_detach(&zone);
3467135446Strhodes	if (pview != NULL)
3468135446Strhodes		dns_view_detach(&pview);
3469135446Strhodes
3470135446Strhodes	return (result);
3471135446Strhodes}
3472135446Strhodes
3473135446Strhodes/*
3474224092Sdougb * Configure built-in zone for storing managed-key data.
3475224092Sdougb */
3476224092Sdougb
3477224092Sdougb#define KEYZONE "managed-keys.bind"
3478224092Sdougb#define MKEYS ".mkeys"
3479224092Sdougb
3480224092Sdougbstatic isc_result_t
3481224092Sdougbadd_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
3482224092Sdougb	isc_result_t result;
3483224092Sdougb	dns_view_t *pview = NULL;
3484224092Sdougb	dns_zone_t *zone = NULL;
3485224092Sdougb	dns_acl_t *none = NULL;
3486224092Sdougb	char filename[PATH_MAX];
3487224092Sdougb	char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(MKEYS)];
3488224092Sdougb	int n;
3489224092Sdougb
3490224092Sdougb	REQUIRE(view != NULL);
3491224092Sdougb
3492224092Sdougb	/* See if we can re-use an existing keydata zone. */
3493224092Sdougb	result = dns_viewlist_find(&ns_g_server->viewlist,
3494224092Sdougb				   view->name, view->rdclass,
3495224092Sdougb				   &pview);
3496224092Sdougb	if (result != ISC_R_NOTFOUND &&
3497224092Sdougb	    result != ISC_R_SUCCESS)
3498224092Sdougb		return (result);
3499224092Sdougb
3500224092Sdougb	if (pview != NULL && pview->managed_keys != NULL) {
3501224092Sdougb		dns_zone_attach(pview->managed_keys, &view->managed_keys);
3502224092Sdougb		dns_zone_setview(pview->managed_keys, view);
3503224092Sdougb		dns_view_detach(&pview);
3504234010Sdougb		dns_zone_synckeyzone(view->managed_keys);
3505224092Sdougb		return (ISC_R_SUCCESS);
3506224092Sdougb	}
3507224092Sdougb
3508224092Sdougb	/* No existing keydata zone was found; create one */
3509224092Sdougb	CHECK(dns_zone_create(&zone, mctx));
3510224092Sdougb	CHECK(dns_zone_setorigin(zone, dns_rootname));
3511224092Sdougb
3512224092Sdougb	isc_sha256_data((void *)view->name, strlen(view->name), buffer);
3513224092Sdougb	strcat(buffer, MKEYS);
3514224092Sdougb	n = snprintf(filename, sizeof(filename), "%s%s%s",
3515224092Sdougb		     directory ? directory : "", directory ? "/" : "",
3516224092Sdougb		     strcmp(view->name, "_default") == 0 ? KEYZONE : buffer);
3517224092Sdougb	if (n < 0 || (size_t)n >= sizeof(filename)) {
3518224092Sdougb		result = (n < 0) ? ISC_R_FAILURE : ISC_R_NOSPACE;
3519224092Sdougb		goto cleanup;
3520224092Sdougb	}
3521224092Sdougb	CHECK(dns_zone_setfile(zone, filename));
3522224092Sdougb
3523224092Sdougb	dns_zone_setview(zone, view);
3524224092Sdougb	dns_zone_settype(zone, dns_zone_key);
3525224092Sdougb	dns_zone_setclass(zone, view->rdclass);
3526224092Sdougb
3527224092Sdougb	CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
3528224092Sdougb
3529224092Sdougb	if (view->acache != NULL)
3530224092Sdougb		dns_zone_setacache(zone, view->acache);
3531224092Sdougb
3532224092Sdougb	CHECK(dns_acl_none(mctx, &none));
3533224092Sdougb	dns_zone_setqueryacl(zone, none);
3534224092Sdougb	dns_zone_setqueryonacl(zone, none);
3535224092Sdougb	dns_acl_detach(&none);
3536224092Sdougb
3537224092Sdougb	dns_zone_setdialup(zone, dns_dialuptype_no);
3538224092Sdougb	dns_zone_setnotifytype(zone, dns_notifytype_no);
3539224092Sdougb	dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE);
3540224092Sdougb	dns_zone_setjournalsize(zone, 0);
3541224092Sdougb
3542224092Sdougb	dns_zone_setstats(zone, ns_g_server->zonestats);
3543224092Sdougb	CHECK(setquerystats(zone, mctx, ISC_FALSE));
3544224092Sdougb
3545224092Sdougb	if (view->managed_keys != NULL)
3546224092Sdougb		dns_zone_detach(&view->managed_keys);
3547224092Sdougb	dns_zone_attach(zone, &view->managed_keys);
3548224092Sdougb
3549224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3550224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
3551224092Sdougb		      "set up managed keys zone for view %s, file '%s'",
3552224092Sdougb		      view->name, filename);
3553224092Sdougb
3554224092Sdougbcleanup:
3555224092Sdougb	if (zone != NULL)
3556224092Sdougb		dns_zone_detach(&zone);
3557224092Sdougb	if (none != NULL)
3558224092Sdougb		dns_acl_detach(&none);
3559224092Sdougb
3560224092Sdougb	return (result);
3561224092Sdougb}
3562224092Sdougb
3563224092Sdougb/*
3564135446Strhodes * Configure a single server quota.
3565135446Strhodes */
3566135446Strhodesstatic void
3567165071Sdougbconfigure_server_quota(const cfg_obj_t **maps, const char *name,
3568165071Sdougb		       isc_quota_t *quota)
3569135446Strhodes{
3570165071Sdougb	const cfg_obj_t *obj = NULL;
3571135446Strhodes	isc_result_t result;
3572135446Strhodes
3573135446Strhodes	result = ns_config_get(maps, name, &obj);
3574135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3575153816Sdougb	isc_quota_max(quota, cfg_obj_asuint32(obj));
3576135446Strhodes}
3577135446Strhodes
3578135446Strhodes/*
3579135446Strhodes * This function is called as soon as the 'directory' statement has been
3580135446Strhodes * parsed.  This can be extended to support other options if necessary.
3581135446Strhodes */
3582135446Strhodesstatic isc_result_t
3583165071Sdougbdirectory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
3584135446Strhodes	isc_result_t result;
3585165071Sdougb	const char *directory;
3586135446Strhodes
3587135446Strhodes	REQUIRE(strcasecmp("directory", clausename) == 0);
3588135446Strhodes
3589135446Strhodes	UNUSED(arg);
3590135446Strhodes	UNUSED(clausename);
3591135446Strhodes
3592135446Strhodes	/*
3593135446Strhodes	 * Change directory.
3594135446Strhodes	 */
3595135446Strhodes	directory = cfg_obj_asstring(obj);
3596135446Strhodes
3597135446Strhodes	if (! isc_file_ischdiridempotent(directory))
3598135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
3599135446Strhodes			    "option 'directory' contains relative path '%s'",
3600135446Strhodes			    directory);
3601135446Strhodes
3602135446Strhodes	result = isc_dir_chdir(directory);
3603135446Strhodes	if (result != ISC_R_SUCCESS) {
3604135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
3605135446Strhodes			    "change directory to '%s' failed: %s",
3606135446Strhodes			    directory, isc_result_totext(result));
3607135446Strhodes		return (result);
3608135446Strhodes	}
3609135446Strhodes
3610135446Strhodes	return (ISC_R_SUCCESS);
3611135446Strhodes}
3612135446Strhodes
3613135446Strhodesstatic void
3614135446Strhodesscan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
3615135446Strhodes	isc_boolean_t match_mapped = server->aclenv.match_mapped;
3616135446Strhodes
3617135446Strhodes	ns_interfacemgr_scan(server->interfacemgr, verbose);
3618135446Strhodes	/*
3619135446Strhodes	 * Update the "localhost" and "localnets" ACLs to match the
3620135446Strhodes	 * current set of network interfaces.
3621135446Strhodes	 */
3622135446Strhodes	dns_aclenv_copy(&server->aclenv,
3623135446Strhodes			ns_interfacemgr_getaclenv(server->interfacemgr));
3624135446Strhodes
3625135446Strhodes	server->aclenv.match_mapped = match_mapped;
3626135446Strhodes}
3627135446Strhodes
3628135446Strhodesstatic isc_result_t
3629180477Sdougbadd_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
3630180477Sdougb	      isc_boolean_t wcardport_ok)
3631180477Sdougb{
3632135446Strhodes	ns_listenelt_t *lelt = NULL;
3633135446Strhodes	dns_acl_t *src_acl = NULL;
3634135446Strhodes	isc_result_t result;
3635135446Strhodes	isc_sockaddr_t any_sa6;
3636193149Sdougb	isc_netaddr_t netaddr;
3637135446Strhodes
3638135446Strhodes	REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
3639135446Strhodes
3640135446Strhodes	isc_sockaddr_any6(&any_sa6);
3641180477Sdougb	if (!isc_sockaddr_equal(&any_sa6, addr) &&
3642180477Sdougb	    (wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
3643193149Sdougb		isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr);
3644135446Strhodes
3645193149Sdougb		result = dns_acl_create(mctx, 0, &src_acl);
3646135446Strhodes		if (result != ISC_R_SUCCESS)
3647135446Strhodes			return (result);
3648193149Sdougb
3649193149Sdougb		result = dns_iptable_addprefix(src_acl->iptable,
3650193149Sdougb					       &netaddr, 128, ISC_TRUE);
3651135446Strhodes		if (result != ISC_R_SUCCESS)
3652135446Strhodes			goto clean;
3653135446Strhodes
3654135446Strhodes		result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr),
3655135446Strhodes					     src_acl, &lelt);
3656135446Strhodes		if (result != ISC_R_SUCCESS)
3657135446Strhodes			goto clean;
3658135446Strhodes		ISC_LIST_APPEND(list->elts, lelt, link);
3659135446Strhodes	}
3660135446Strhodes
3661135446Strhodes	return (ISC_R_SUCCESS);
3662135446Strhodes
3663135446Strhodes clean:
3664135446Strhodes	INSIST(lelt == NULL);
3665165071Sdougb	dns_acl_detach(&src_acl);
3666135446Strhodes
3667135446Strhodes	return (result);
3668135446Strhodes}
3669135446Strhodes
3670135446Strhodes/*
3671135446Strhodes * Make a list of xxx-source addresses and call ns_interfacemgr_adjust()
3672135446Strhodes * to update the listening interfaces accordingly.
3673135446Strhodes * We currently only consider IPv6, because this only affects IPv6 wildcard
3674135446Strhodes * sockets.
3675135446Strhodes */
3676135446Strhodesstatic void
3677135446Strhodesadjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
3678135446Strhodes	isc_result_t result;
3679135446Strhodes	ns_listenlist_t *list = NULL;
3680135446Strhodes	dns_view_t *view;
3681135446Strhodes	dns_zone_t *zone, *next;
3682135446Strhodes	isc_sockaddr_t addr, *addrp;
3683135446Strhodes
3684135446Strhodes	result = ns_listenlist_create(mctx, &list);
3685135446Strhodes	if (result != ISC_R_SUCCESS)
3686135446Strhodes		return;
3687135446Strhodes
3688135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
3689135446Strhodes	     view != NULL;
3690135446Strhodes	     view = ISC_LIST_NEXT(view, link)) {
3691135446Strhodes		dns_dispatch_t *dispatch6;
3692135446Strhodes
3693135446Strhodes		dispatch6 = dns_resolver_dispatchv6(view->resolver);
3694143731Sdougb		if (dispatch6 == NULL)
3695143731Sdougb			continue;
3696135446Strhodes		result = dns_dispatch_getlocaladdress(dispatch6, &addr);
3697135446Strhodes		if (result != ISC_R_SUCCESS)
3698135446Strhodes			goto fail;
3699180477Sdougb
3700180477Sdougb		/*
3701180477Sdougb		 * We always add non-wildcard address regardless of whether
3702180477Sdougb		 * the port is 'any' (the fourth arg is TRUE): if the port is
3703180477Sdougb		 * specific, we need to add it since it may conflict with a
3704180477Sdougb		 * listening interface; if it's zero, we'll dynamically open
3705180477Sdougb		 * query ports, and some of them may override an existing
3706180477Sdougb		 * wildcard IPv6 port.
3707180477Sdougb		 */
3708180477Sdougb		result = add_listenelt(mctx, list, &addr, ISC_TRUE);
3709135446Strhodes		if (result != ISC_R_SUCCESS)
3710135446Strhodes			goto fail;
3711135446Strhodes	}
3712135446Strhodes
3713135446Strhodes	zone = NULL;
3714135446Strhodes	for (result = dns_zone_first(server->zonemgr, &zone);
3715135446Strhodes	     result == ISC_R_SUCCESS;
3716135446Strhodes	     next = NULL, result = dns_zone_next(zone, &next), zone = next) {
3717135446Strhodes		dns_view_t *zoneview;
3718135446Strhodes
3719135446Strhodes		/*
3720135446Strhodes		 * At this point the zone list may contain a stale zone
3721135446Strhodes		 * just removed from the configuration.  To see the validity,
3722135446Strhodes		 * check if the corresponding view is in our current view list.
3723153816Sdougb		 * There may also be old zones that are still in the process
3724153816Sdougb		 * of shutting down and have detached from their old view
3725153816Sdougb		 * (zoneview == NULL).
3726135446Strhodes		 */
3727135446Strhodes		zoneview = dns_zone_getview(zone);
3728153816Sdougb		if (zoneview == NULL)
3729153816Sdougb			continue;
3730135446Strhodes		for (view = ISC_LIST_HEAD(server->viewlist);
3731135446Strhodes		     view != NULL && view != zoneview;
3732135446Strhodes		     view = ISC_LIST_NEXT(view, link))
3733135446Strhodes			;
3734135446Strhodes		if (view == NULL)
3735135446Strhodes			continue;
3736135446Strhodes
3737135446Strhodes		addrp = dns_zone_getnotifysrc6(zone);
3738180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
3739135446Strhodes		if (result != ISC_R_SUCCESS)
3740135446Strhodes			goto fail;
3741135446Strhodes
3742135446Strhodes		addrp = dns_zone_getxfrsource6(zone);
3743180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
3744135446Strhodes		if (result != ISC_R_SUCCESS)
3745135446Strhodes			goto fail;
3746135446Strhodes	}
3747135446Strhodes
3748135446Strhodes	ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE);
3749186462Sdougb
3750135446Strhodes clean:
3751135446Strhodes	ns_listenlist_detach(&list);
3752135446Strhodes	return;
3753135446Strhodes
3754135446Strhodes fail:
3755135446Strhodes	/*
3756135446Strhodes	 * Even when we failed the procedure, most of other interfaces
3757135446Strhodes	 * should work correctly.  We therefore just warn it.
3758135446Strhodes	 */
3759135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3760135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
3761135446Strhodes		      "could not adjust the listen-on list; "
3762135446Strhodes		      "some interfaces may not work");
3763135446Strhodes	goto clean;
3764135446Strhodes}
3765135446Strhodes
3766135446Strhodes/*
3767135446Strhodes * This event callback is invoked to do periodic network
3768135446Strhodes * interface scanning.
3769135446Strhodes */
3770135446Strhodesstatic void
3771135446Strhodesinterface_timer_tick(isc_task_t *task, isc_event_t *event) {
3772135446Strhodes	isc_result_t result;
3773135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
3774135446Strhodes	INSIST(task == server->task);
3775135446Strhodes	UNUSED(task);
3776135446Strhodes	isc_event_free(&event);
3777135446Strhodes	/*
3778135446Strhodes	 * XXX should scan interfaces unlocked and get exclusive access
3779135446Strhodes	 * only to replace ACLs.
3780135446Strhodes	 */
3781135446Strhodes	result = isc_task_beginexclusive(server->task);
3782135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3783135446Strhodes	scan_interfaces(server, ISC_FALSE);
3784135446Strhodes	isc_task_endexclusive(server->task);
3785135446Strhodes}
3786135446Strhodes
3787135446Strhodesstatic void
3788135446Strhodesheartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
3789135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
3790135446Strhodes	dns_view_t *view;
3791135446Strhodes
3792135446Strhodes	UNUSED(task);
3793135446Strhodes	isc_event_free(&event);
3794135446Strhodes	view = ISC_LIST_HEAD(server->viewlist);
3795135446Strhodes	while (view != NULL) {
3796135446Strhodes		dns_view_dialup(view);
3797135446Strhodes		view = ISC_LIST_NEXT(view, link);
3798135446Strhodes	}
3799135446Strhodes}
3800135446Strhodes
3801170222Sdougbstatic void
3802170222Sdougbpps_timer_tick(isc_task_t *task, isc_event_t *event) {
3803170222Sdougb	static unsigned int oldrequests = 0;
3804170222Sdougb	unsigned int requests = ns_client_requests;
3805170222Sdougb
3806170222Sdougb	UNUSED(task);
3807170222Sdougb	isc_event_free(&event);
3808170222Sdougb
3809170222Sdougb	/*
3810170222Sdougb	 * Don't worry about wrapping as the overflow result will be right.
3811170222Sdougb	 */
3812170222Sdougb	dns_pps = (requests - oldrequests) / 1200;
3813170222Sdougb	oldrequests = requests;
3814170222Sdougb}
3815170222Sdougb
3816135446Strhodes/*
3817135446Strhodes * Replace the current value of '*field', a dynamically allocated
3818135446Strhodes * string or NULL, with a dynamically allocated copy of the
3819135446Strhodes * null-terminated string pointed to by 'value', or NULL.
3820135446Strhodes */
3821135446Strhodesstatic isc_result_t
3822135446Strhodessetstring(ns_server_t *server, char **field, const char *value) {
3823135446Strhodes	char *copy;
3824135446Strhodes
3825135446Strhodes	if (value != NULL) {
3826135446Strhodes		copy = isc_mem_strdup(server->mctx, value);
3827135446Strhodes		if (copy == NULL)
3828135446Strhodes			return (ISC_R_NOMEMORY);
3829135446Strhodes	} else {
3830135446Strhodes		copy = NULL;
3831135446Strhodes	}
3832135446Strhodes
3833135446Strhodes	if (*field != NULL)
3834135446Strhodes		isc_mem_free(server->mctx, *field);
3835135446Strhodes
3836135446Strhodes	*field = copy;
3837135446Strhodes	return (ISC_R_SUCCESS);
3838186462Sdougb}
3839135446Strhodes
3840135446Strhodes/*
3841135446Strhodes * Replace the current value of '*field', a dynamically allocated
3842135446Strhodes * string or NULL, with another dynamically allocated string
3843135446Strhodes * or NULL if whether 'obj' is a string or void value, respectively.
3844135446Strhodes */
3845135446Strhodesstatic isc_result_t
3846165071Sdougbsetoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) {
3847135446Strhodes	if (cfg_obj_isvoid(obj))
3848135446Strhodes		return (setstring(server, field, NULL));
3849135446Strhodes	else
3850135446Strhodes		return (setstring(server, field, cfg_obj_asstring(obj)));
3851135446Strhodes}
3852135446Strhodes
3853135446Strhodesstatic void
3854165071Sdougbset_limit(const cfg_obj_t **maps, const char *configname,
3855165071Sdougb	  const char *description, isc_resource_t resourceid,
3856165071Sdougb	  isc_resourcevalue_t defaultvalue)
3857135446Strhodes{
3858165071Sdougb	const cfg_obj_t *obj = NULL;
3859165071Sdougb	const char *resource;
3860135446Strhodes	isc_resourcevalue_t value;
3861135446Strhodes	isc_result_t result;
3862135446Strhodes
3863135446Strhodes	if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS)
3864135446Strhodes		return;
3865135446Strhodes
3866135446Strhodes	if (cfg_obj_isstring(obj)) {
3867135446Strhodes		resource = cfg_obj_asstring(obj);
3868135446Strhodes		if (strcasecmp(resource, "unlimited") == 0)
3869135446Strhodes			value = ISC_RESOURCE_UNLIMITED;
3870135446Strhodes		else {
3871135446Strhodes			INSIST(strcasecmp(resource, "default") == 0);
3872135446Strhodes			value = defaultvalue;
3873135446Strhodes		}
3874135446Strhodes	} else
3875135446Strhodes		value = cfg_obj_asuint64(obj);
3876135446Strhodes
3877135446Strhodes	result = isc_resource_setlimit(resourceid, value);
3878135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
3879135446Strhodes		      result == ISC_R_SUCCESS ?
3880186462Sdougb			ISC_LOG_DEBUG(3) : ISC_LOG_WARNING,
3881204619Sdougb		      "set maximum %s to %" ISC_PRINT_QUADFORMAT "u: %s",
3882135446Strhodes		      description, value, isc_result_totext(result));
3883135446Strhodes}
3884135446Strhodes
3885135446Strhodes#define SETLIMIT(cfgvar, resource, description) \
3886135446Strhodes	set_limit(maps, cfgvar, description, isc_resource_ ## resource, \
3887135446Strhodes		  ns_g_init ## resource)
3888135446Strhodes
3889135446Strhodesstatic void
3890165071Sdougbset_limits(const cfg_obj_t **maps) {
3891135446Strhodes	SETLIMIT("stacksize", stacksize, "stack size");
3892135446Strhodes	SETLIMIT("datasize", datasize, "data size");
3893135446Strhodes	SETLIMIT("coresize", coresize, "core size");
3894135446Strhodes	SETLIMIT("files", openfiles, "open files");
3895135446Strhodes}
3896135446Strhodes
3897186462Sdougbstatic void
3898186462Sdougbportset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports,
3899186462Sdougb		 isc_boolean_t positive)
3900135446Strhodes{
3901165071Sdougb	const cfg_listelt_t *element;
3902135446Strhodes
3903135446Strhodes	for (element = cfg_list_first(ports);
3904135446Strhodes	     element != NULL;
3905135446Strhodes	     element = cfg_list_next(element)) {
3906165071Sdougb		const cfg_obj_t *obj = cfg_listelt_value(element);
3907186462Sdougb
3908186462Sdougb		if (cfg_obj_isuint32(obj)) {
3909186462Sdougb			in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
3910186462Sdougb
3911186462Sdougb			if (positive)
3912186462Sdougb				isc_portset_add(portset, port);
3913186462Sdougb			else
3914186462Sdougb				isc_portset_remove(portset, port);
3915186462Sdougb		} else {
3916186462Sdougb			const cfg_obj_t *obj_loport, *obj_hiport;
3917186462Sdougb			in_port_t loport, hiport;
3918186462Sdougb
3919186462Sdougb			obj_loport = cfg_tuple_get(obj, "loport");
3920186462Sdougb			loport = (in_port_t)cfg_obj_asuint32(obj_loport);
3921186462Sdougb			obj_hiport = cfg_tuple_get(obj, "hiport");
3922186462Sdougb			hiport = (in_port_t)cfg_obj_asuint32(obj_hiport);
3923186462Sdougb
3924186462Sdougb			if (positive)
3925186462Sdougb				isc_portset_addrange(portset, loport, hiport);
3926186462Sdougb			else {
3927186462Sdougb				isc_portset_removerange(portset, loport,
3928186462Sdougb							hiport);
3929186462Sdougb			}
3930186462Sdougb		}
3931135446Strhodes	}
3932135446Strhodes}
3933135446Strhodes
3934135446Strhodesstatic isc_result_t
3935170222Sdougbremoved(dns_zone_t *zone, void *uap) {
3936170222Sdougb	const char *type;
3937170222Sdougb
3938186462Sdougb	if (dns_zone_getview(zone) != uap)
3939170222Sdougb		return (ISC_R_SUCCESS);
3940170222Sdougb
3941170222Sdougb	switch (dns_zone_gettype(zone)) {
3942170222Sdougb	case dns_zone_master:
3943170222Sdougb		type = "master";
3944170222Sdougb		break;
3945170222Sdougb	case dns_zone_slave:
3946170222Sdougb		type = "slave";
3947170222Sdougb		break;
3948170222Sdougb	case dns_zone_stub:
3949170222Sdougb		type = "stub";
3950170222Sdougb		break;
3951170222Sdougb	default:
3952170222Sdougb		type = "other";
3953170222Sdougb		break;
3954170222Sdougb	}
3955170222Sdougb	dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type);
3956170222Sdougb	return (ISC_R_SUCCESS);
3957170222Sdougb}
3958170222Sdougb
3959224092Sdougbstatic void
3960224092Sdougbcleanup_session_key(ns_server_t *server, isc_mem_t *mctx) {
3961224092Sdougb	if (server->session_keyfile != NULL) {
3962224092Sdougb		isc_file_remove(server->session_keyfile);
3963224092Sdougb		isc_mem_free(mctx, server->session_keyfile);
3964224092Sdougb		server->session_keyfile = NULL;
3965224092Sdougb	}
3966224092Sdougb
3967224092Sdougb	if (server->session_keyname != NULL) {
3968224092Sdougb		if (dns_name_dynamic(server->session_keyname))
3969224092Sdougb			dns_name_free(server->session_keyname, mctx);
3970224092Sdougb		isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t));
3971224092Sdougb		server->session_keyname = NULL;
3972224092Sdougb	}
3973224092Sdougb
3974224092Sdougb	if (server->sessionkey != NULL)
3975224092Sdougb		dns_tsigkey_detach(&server->sessionkey);
3976224092Sdougb
3977224092Sdougb	server->session_keyalg = DST_ALG_UNKNOWN;
3978224092Sdougb	server->session_keybits = 0;
3979224092Sdougb}
3980224092Sdougb
3981170222Sdougbstatic isc_result_t
3982224092Sdougbgenerate_session_key(const char *filename, const char *keynamestr,
3983224092Sdougb		     dns_name_t *keyname, const char *algstr,
3984224092Sdougb		     dns_name_t *algname, unsigned int algtype,
3985224092Sdougb		     isc_uint16_t bits, isc_mem_t *mctx,
3986224092Sdougb		     dns_tsigkey_t **tsigkeyp)
3987224092Sdougb{
3988224092Sdougb	isc_result_t result = ISC_R_SUCCESS;
3989224092Sdougb	dst_key_t *key = NULL;
3990224092Sdougb	isc_buffer_t key_txtbuffer;
3991224092Sdougb	isc_buffer_t key_rawbuffer;
3992224092Sdougb	char key_txtsecret[256];
3993224092Sdougb	char key_rawsecret[64];
3994224092Sdougb	isc_region_t key_rawregion;
3995224092Sdougb	isc_stdtime_t now;
3996224092Sdougb	dns_tsigkey_t *tsigkey = NULL;
3997224092Sdougb	FILE *fp = NULL;
3998224092Sdougb
3999224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4000224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4001224092Sdougb		      "generating session key for dynamic DNS");
4002224092Sdougb
4003224092Sdougb	/* generate key */
4004224092Sdougb	result = dst_key_generate(keyname, algtype, bits, 1, 0,
4005224092Sdougb				  DNS_KEYPROTO_ANY, dns_rdataclass_in,
4006224092Sdougb				  mctx, &key);
4007224092Sdougb	if (result != ISC_R_SUCCESS)
4008224092Sdougb		return (result);
4009224092Sdougb
4010224092Sdougb	/*
4011224092Sdougb	 * Dump the key to the buffer for later use.  Should be done before
4012224092Sdougb	 * we transfer the ownership of key to tsigkey.
4013224092Sdougb	 */
4014224092Sdougb	isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));
4015224092Sdougb	CHECK(dst_key_tobuffer(key, &key_rawbuffer));
4016224092Sdougb
4017224092Sdougb	isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);
4018224092Sdougb	isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret));
4019224092Sdougb	CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer));
4020224092Sdougb
4021224092Sdougb	/* Store the key in tsigkey. */
4022224092Sdougb	isc_stdtime_get(&now);
4023224092Sdougb	CHECK(dns_tsigkey_createfromkey(dst_key_name(key), algname, key,
4024224092Sdougb					ISC_FALSE, NULL, now, now, mctx, NULL,
4025224092Sdougb					&tsigkey));
4026224092Sdougb
4027224092Sdougb	/* Dump the key to the key file. */
4028224092Sdougb	fp = ns_os_openfile(filename, S_IRUSR|S_IWUSR, ISC_TRUE);
4029224092Sdougb	if (fp == NULL) {
4030224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4031224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4032224092Sdougb			      "could not create %s", filename);
4033224092Sdougb		result = ISC_R_NOPERM;
4034224092Sdougb		goto cleanup;
4035224092Sdougb	}
4036224092Sdougb
4037224092Sdougb	fprintf(fp, "key \"%s\" {\n"
4038224092Sdougb		"\talgorithm %s;\n"
4039224092Sdougb		"\tsecret \"%.*s\";\n};\n", keynamestr, algstr,
4040224092Sdougb		(int) isc_buffer_usedlength(&key_txtbuffer),
4041224092Sdougb		(char*) isc_buffer_base(&key_txtbuffer));
4042224092Sdougb
4043224092Sdougb	RUNTIME_CHECK(isc_stdio_flush(fp) == ISC_R_SUCCESS);
4044224092Sdougb	RUNTIME_CHECK(isc_stdio_close(fp) == ISC_R_SUCCESS);
4045224092Sdougb
4046224092Sdougb	dst_key_free(&key);
4047224092Sdougb
4048224092Sdougb	*tsigkeyp = tsigkey;
4049224092Sdougb
4050224092Sdougb	return (ISC_R_SUCCESS);
4051224092Sdougb
4052224092Sdougb  cleanup:
4053224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4054224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4055224092Sdougb		      "failed to generate session key "
4056224092Sdougb		      "for dynamic DNS: %s", isc_result_totext(result));
4057224092Sdougb	if (tsigkey != NULL)
4058224092Sdougb		dns_tsigkey_detach(&tsigkey);
4059224092Sdougb	if (key != NULL)
4060224092Sdougb		dst_key_free(&key);
4061224092Sdougb
4062224092Sdougb	return (result);
4063224092Sdougb}
4064224092Sdougb
4065224092Sdougbstatic isc_result_t
4066224092Sdougbconfigure_session_key(const cfg_obj_t **maps, ns_server_t *server,
4067224092Sdougb		      isc_mem_t *mctx)
4068224092Sdougb{
4069224092Sdougb	const char *keyfile, *keynamestr, *algstr;
4070224092Sdougb	unsigned int algtype;
4071224092Sdougb	dns_fixedname_t fname;
4072224092Sdougb	dns_name_t *keyname, *algname;
4073224092Sdougb	isc_buffer_t buffer;
4074224092Sdougb	isc_uint16_t bits;
4075224092Sdougb	const cfg_obj_t *obj;
4076224092Sdougb	isc_boolean_t need_deleteold = ISC_FALSE;
4077224092Sdougb	isc_boolean_t need_createnew = ISC_FALSE;
4078224092Sdougb	isc_result_t result;
4079224092Sdougb
4080224092Sdougb	obj = NULL;
4081224092Sdougb	result = ns_config_get(maps, "session-keyfile", &obj);
4082224092Sdougb	if (result == ISC_R_SUCCESS) {
4083224092Sdougb		if (cfg_obj_isvoid(obj))
4084224092Sdougb			keyfile = NULL; /* disable it */
4085224092Sdougb		else
4086224092Sdougb			keyfile = cfg_obj_asstring(obj);
4087224092Sdougb	} else
4088224092Sdougb		keyfile = ns_g_defaultsessionkeyfile;
4089224092Sdougb
4090224092Sdougb	obj = NULL;
4091224092Sdougb	result = ns_config_get(maps, "session-keyname", &obj);
4092224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4093224092Sdougb	keynamestr = cfg_obj_asstring(obj);
4094224092Sdougb	dns_fixedname_init(&fname);
4095224092Sdougb	isc_buffer_init(&buffer, keynamestr, strlen(keynamestr));
4096224092Sdougb	isc_buffer_add(&buffer, strlen(keynamestr));
4097224092Sdougb	keyname = dns_fixedname_name(&fname);
4098224092Sdougb	result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL);
4099224092Sdougb	if (result != ISC_R_SUCCESS)
4100224092Sdougb		return (result);
4101224092Sdougb
4102224092Sdougb	obj = NULL;
4103224092Sdougb	result = ns_config_get(maps, "session-keyalg", &obj);
4104224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4105224092Sdougb	algstr = cfg_obj_asstring(obj);
4106224092Sdougb	algname = NULL;
4107224092Sdougb	result = ns_config_getkeyalgorithm2(algstr, &algname, &algtype, &bits);
4108224092Sdougb	if (result != ISC_R_SUCCESS) {
4109224092Sdougb		const char *s = " (keeping current key)";
4110224092Sdougb
4111224092Sdougb		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "session-keyalg: "
4112224092Sdougb			    "unsupported or unknown algorithm '%s'%s",
4113224092Sdougb			    algstr,
4114224092Sdougb			    server->session_keyfile != NULL ? s : "");
4115224092Sdougb		return (result);
4116224092Sdougb	}
4117224092Sdougb
4118224092Sdougb	/* See if we need to (re)generate a new key. */
4119224092Sdougb	if (keyfile == NULL) {
4120224092Sdougb		if (server->session_keyfile != NULL)
4121224092Sdougb			need_deleteold = ISC_TRUE;
4122224092Sdougb	} else if (server->session_keyfile == NULL)
4123224092Sdougb		need_createnew = ISC_TRUE;
4124224092Sdougb	else if (strcmp(keyfile, server->session_keyfile) != 0 ||
4125224092Sdougb		 !dns_name_equal(server->session_keyname, keyname) ||
4126224092Sdougb		 server->session_keyalg != algtype ||
4127224092Sdougb		 server->session_keybits != bits) {
4128224092Sdougb		need_deleteold = ISC_TRUE;
4129224092Sdougb		need_createnew = ISC_TRUE;
4130224092Sdougb	}
4131224092Sdougb
4132224092Sdougb	if (need_deleteold) {
4133224092Sdougb		INSIST(server->session_keyfile != NULL);
4134224092Sdougb		INSIST(server->session_keyname != NULL);
4135224092Sdougb		INSIST(server->sessionkey != NULL);
4136224092Sdougb
4137224092Sdougb		cleanup_session_key(server, mctx);
4138224092Sdougb	}
4139224092Sdougb
4140224092Sdougb	if (need_createnew) {
4141224092Sdougb		INSIST(server->sessionkey == NULL);
4142224092Sdougb		INSIST(server->session_keyfile == NULL);
4143224092Sdougb		INSIST(server->session_keyname == NULL);
4144224092Sdougb		INSIST(server->session_keyalg == DST_ALG_UNKNOWN);
4145224092Sdougb		INSIST(server->session_keybits == 0);
4146224092Sdougb
4147224092Sdougb		server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t));
4148224092Sdougb		if (server->session_keyname == NULL)
4149224092Sdougb			goto cleanup;
4150224092Sdougb		dns_name_init(server->session_keyname, NULL);
4151224092Sdougb		CHECK(dns_name_dup(keyname, mctx, server->session_keyname));
4152224092Sdougb
4153224092Sdougb		server->session_keyfile = isc_mem_strdup(mctx, keyfile);
4154224092Sdougb		if (server->session_keyfile == NULL)
4155224092Sdougb			goto cleanup;
4156224092Sdougb
4157224092Sdougb		server->session_keyalg = algtype;
4158224092Sdougb		server->session_keybits = bits;
4159224092Sdougb
4160224092Sdougb		CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr,
4161224092Sdougb					   algname, algtype, bits, mctx,
4162224092Sdougb					   &server->sessionkey));
4163224092Sdougb	}
4164224092Sdougb
4165224092Sdougb	return (result);
4166224092Sdougb
4167224092Sdougb  cleanup:
4168224092Sdougb	cleanup_session_key(server, mctx);
4169224092Sdougb	return (result);
4170224092Sdougb}
4171224092Sdougb
4172224092Sdougbstatic isc_result_t
4173225361Sdougbsetup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
4174225361Sdougb	       cfg_parser_t *parser, cfg_aclconfctx_t *actx)
4175225361Sdougb{
4176225361Sdougb	isc_result_t result = ISC_R_SUCCESS;
4177225361Sdougb	isc_boolean_t allow = ISC_FALSE;
4178225361Sdougb	struct cfg_context *nzcfg = NULL;
4179225361Sdougb	cfg_parser_t *nzparser = NULL;
4180225361Sdougb	cfg_obj_t *nzconfig = NULL;
4181225361Sdougb	const cfg_obj_t *maps[4];
4182225361Sdougb	const cfg_obj_t *options = NULL, *voptions = NULL;
4183225361Sdougb	const cfg_obj_t *nz = NULL;
4184225361Sdougb	int i = 0;
4185225361Sdougb
4186225361Sdougb	REQUIRE (config != NULL);
4187225361Sdougb
4188225361Sdougb	if (vconfig != NULL)
4189225361Sdougb		voptions = cfg_tuple_get(vconfig, "options");
4190225361Sdougb	if (voptions != NULL)
4191225361Sdougb		maps[i++] = voptions;
4192225361Sdougb	result = cfg_map_get(config, "options", &options);
4193225361Sdougb	if (result == ISC_R_SUCCESS)
4194225361Sdougb		maps[i++] = options;
4195225361Sdougb	maps[i++] = ns_g_defaults;
4196225361Sdougb	maps[i] = NULL;
4197225361Sdougb
4198225361Sdougb	result = ns_config_get(maps, "allow-new-zones", &nz);
4199225361Sdougb	if (result == ISC_R_SUCCESS)
4200225361Sdougb		allow = cfg_obj_asboolean(nz);
4201225361Sdougb
4202225361Sdougb	if (!allow) {
4203225361Sdougb		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
4204225361Sdougb		return (ISC_R_SUCCESS);
4205225361Sdougb	}
4206225361Sdougb
4207225361Sdougb	nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg));
4208225361Sdougb	if (nzcfg == NULL) {
4209225361Sdougb		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
4210225361Sdougb		return (ISC_R_NOMEMORY);
4211225361Sdougb	}
4212225361Sdougb
4213225361Sdougb	dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy);
4214225361Sdougb
4215225361Sdougb	memset(nzcfg, 0, sizeof(*nzcfg));
4216225361Sdougb	isc_mem_attach(view->mctx, &nzcfg->mctx);
4217225361Sdougb	cfg_obj_attach(config, &nzcfg->config);
4218225361Sdougb	cfg_parser_attach(parser, &nzcfg->parser);
4219225361Sdougb	cfg_aclconfctx_attach(actx, &nzcfg->actx);
4220225361Sdougb
4221225361Sdougb	/*
4222225361Sdougb	 * Attempt to create a parser and parse the newzones
4223225361Sdougb	 * file.  If successful, preserve both; otherwise leave
4224225361Sdougb	 * them NULL.
4225225361Sdougb	 */
4226225361Sdougb	result = cfg_parser_create(view->mctx, ns_g_lctx, &nzparser);
4227225361Sdougb	if (result == ISC_R_SUCCESS)
4228225361Sdougb		result = cfg_parse_file(nzparser, view->new_zone_file,
4229225361Sdougb					&cfg_type_newzones, &nzconfig);
4230225361Sdougb	if (result == ISC_R_SUCCESS) {
4231225361Sdougb		cfg_parser_attach(nzparser, &nzcfg->nzparser);
4232225361Sdougb		cfg_obj_attach(nzconfig, &nzcfg->nzconfig);
4233225361Sdougb	}
4234225361Sdougb
4235225361Sdougb	if (nzparser != NULL) {
4236225361Sdougb		if (nzconfig != NULL)
4237225361Sdougb			cfg_obj_destroy(nzparser, &nzconfig);
4238225361Sdougb		cfg_parser_destroy(&nzparser);
4239225361Sdougb	}
4240225361Sdougb
4241225361Sdougb	return (ISC_R_SUCCESS);
4242225361Sdougb}
4243225361Sdougb
4244225361Sdougbstatic int
4245225361Sdougbcount_zones(const cfg_obj_t *conf) {
4246225361Sdougb	const cfg_obj_t *zonelist = NULL;
4247225361Sdougb	const cfg_listelt_t *element;
4248225361Sdougb	int n = 0;
4249225361Sdougb
4250225361Sdougb	REQUIRE(conf != NULL);
4251225361Sdougb
4252225361Sdougb	cfg_map_get(conf, "zone", &zonelist);
4253225361Sdougb	for (element = cfg_list_first(zonelist);
4254225361Sdougb	     element != NULL;
4255225361Sdougb	     element = cfg_list_next(element))
4256225361Sdougb		n++;
4257225361Sdougb
4258225361Sdougb	return (n);
4259225361Sdougb}
4260225361Sdougb
4261225361Sdougbstatic isc_result_t
4262135446Strhodesload_configuration(const char *filename, ns_server_t *server,
4263135446Strhodes		   isc_boolean_t first_time)
4264135446Strhodes{
4265224092Sdougb	cfg_obj_t *config = NULL, *bindkeys = NULL;
4266224092Sdougb	cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL;
4267182645Sdougb	const cfg_listelt_t *element;
4268182645Sdougb	const cfg_obj_t *builtin_views;
4269182645Sdougb	const cfg_obj_t *maps[3];
4270182645Sdougb	const cfg_obj_t *obj;
4271165071Sdougb	const cfg_obj_t *options;
4272186462Sdougb	const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports;
4273165071Sdougb	const cfg_obj_t *views;
4274135446Strhodes	dns_view_t *view = NULL;
4275135446Strhodes	dns_view_t *view_next;
4276182645Sdougb	dns_viewlist_t tmpviewlist;
4277224092Sdougb	dns_viewlist_t viewlist, builtin_viewlist;
4278186462Sdougb	in_port_t listen_port, udpport_low, udpport_high;
4279182645Sdougb	int i;
4280182645Sdougb	isc_interval_t interval;
4281186462Sdougb	isc_portset_t *v4portset = NULL;
4282186462Sdougb	isc_portset_t *v6portset = NULL;
4283186462Sdougb	isc_resourcevalue_t nfiles;
4284182645Sdougb	isc_result_t result;
4285182645Sdougb	isc_uint32_t heartbeat_interval;
4286135446Strhodes	isc_uint32_t interface_interval;
4287182645Sdougb	isc_uint32_t reserved;
4288135446Strhodes	isc_uint32_t udpsize;
4289224092Sdougb	ns_cachelist_t cachelist, tmpcachelist;
4290186462Sdougb	unsigned int maxsocks;
4291224092Sdougb	ns_cache_t *nsc;
4292225361Sdougb	struct cfg_context *nzctx;
4293225361Sdougb	int num_zones = 0;
4294234010Sdougb	isc_boolean_t exclusive = ISC_FALSE;
4295135446Strhodes
4296135446Strhodes	ISC_LIST_INIT(viewlist);
4297224092Sdougb	ISC_LIST_INIT(builtin_viewlist);
4298224092Sdougb	ISC_LIST_INIT(cachelist);
4299135446Strhodes
4300225361Sdougb	/* Create the ACL configuration context */
4301225361Sdougb	if (ns_g_aclconfctx != NULL)
4302225361Sdougb		cfg_aclconfctx_detach(&ns_g_aclconfctx);
4303225361Sdougb	CHECK(cfg_aclconfctx_create(ns_g_mctx, &ns_g_aclconfctx));
4304225361Sdougb
4305135446Strhodes	/*
4306135446Strhodes	 * Parse the global default pseudo-config file.
4307135446Strhodes	 */
4308135446Strhodes	if (first_time) {
4309135446Strhodes		CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config));
4310135446Strhodes		RUNTIME_CHECK(cfg_map_get(ns_g_config, "options",
4311224092Sdougb					  &ns_g_defaults) == ISC_R_SUCCESS);
4312135446Strhodes	}
4313135446Strhodes
4314135446Strhodes	/*
4315135446Strhodes	 * Parse the configuration file using the new config code.
4316135446Strhodes	 */
4317135446Strhodes	result = ISC_R_FAILURE;
4318135446Strhodes	config = NULL;
4319135446Strhodes
4320135446Strhodes	/*
4321135446Strhodes	 * Unless this is lwresd with the -C option, parse the config file.
4322135446Strhodes	 */
4323135446Strhodes	if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) {
4324135446Strhodes		isc_log_write(ns_g_lctx,
4325135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4326135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
4327135446Strhodes			      filename);
4328224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
4329224092Sdougb		cfg_parser_setcallback(conf_parser, directory_callback, NULL);
4330224092Sdougb		result = cfg_parse_file(conf_parser, filename,
4331224092Sdougb					&cfg_type_namedconf, &config);
4332135446Strhodes	}
4333135446Strhodes
4334135446Strhodes	/*
4335135446Strhodes	 * If this is lwresd with the -C option, or lwresd with no -C or -c
4336135446Strhodes	 * option where the above parsing failed, parse resolv.conf.
4337135446Strhodes	 */
4338135446Strhodes	if (ns_g_lwresdonly &&
4339135446Strhodes	    (lwresd_g_useresolvconf ||
4340135446Strhodes	     (!ns_g_conffileset && result == ISC_R_FILENOTFOUND)))
4341135446Strhodes	{
4342135446Strhodes		isc_log_write(ns_g_lctx,
4343135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4344135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
4345135446Strhodes			      lwresd_g_resolvconffile);
4346224092Sdougb		if (conf_parser != NULL)
4347224092Sdougb			cfg_parser_destroy(&conf_parser);
4348224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
4349224092Sdougb		result = ns_lwresd_parseeresolvconf(ns_g_mctx, conf_parser,
4350135446Strhodes						    &config);
4351135446Strhodes	}
4352135446Strhodes	CHECK(result);
4353135446Strhodes
4354135446Strhodes	/*
4355135446Strhodes	 * Check the validity of the configuration.
4356135446Strhodes	 */
4357135446Strhodes	CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx));
4358135446Strhodes
4359135446Strhodes	/*
4360135446Strhodes	 * Fill in the maps array, used for resolving defaults.
4361135446Strhodes	 */
4362135446Strhodes	i = 0;
4363135446Strhodes	options = NULL;
4364135446Strhodes	result = cfg_map_get(config, "options", &options);
4365135446Strhodes	if (result == ISC_R_SUCCESS)
4366135446Strhodes		maps[i++] = options;
4367135446Strhodes	maps[i++] = ns_g_defaults;
4368225361Sdougb	maps[i] = NULL;
4369135446Strhodes
4370135446Strhodes	/*
4371224092Sdougb	 * If bind.keys exists, load it.  If "dnssec-lookaside auto"
4372224092Sdougb	 * is turned on, the keys found there will be used as default
4373224092Sdougb	 * trust anchors.
4374224092Sdougb	 */
4375224092Sdougb	obj = NULL;
4376224092Sdougb	result = ns_config_get(maps, "bindkeys-file", &obj);
4377224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4378224092Sdougb	CHECKM(setstring(server, &server->bindkeysfile,
4379224092Sdougb	       cfg_obj_asstring(obj)), "strdup");
4380224092Sdougb
4381224092Sdougb	if (access(server->bindkeysfile, R_OK) == 0) {
4382224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4383224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4384224092Sdougb			      "reading built-in trusted "
4385224092Sdougb			      "keys from file '%s'", server->bindkeysfile);
4386224092Sdougb
4387224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx,
4388224092Sdougb					&bindkeys_parser));
4389224092Sdougb
4390224092Sdougb		result = cfg_parse_file(bindkeys_parser, server->bindkeysfile,
4391224092Sdougb					&cfg_type_bindkeys, &bindkeys);
4392224092Sdougb		CHECK(result);
4393224092Sdougb	}
4394224092Sdougb
4395234010Sdougb	/* Ensure exclusive access to configuration data. */
4396234010Sdougb	if (!exclusive) {
4397234010Sdougb		result = isc_task_beginexclusive(server->task);
4398234010Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
4399234010Sdougb		exclusive = ISC_TRUE;
4400234010Sdougb	}
4401234010Sdougb
4402224092Sdougb	/*
4403135446Strhodes	 * Set process limits, which (usually) needs to be done as root.
4404135446Strhodes	 */
4405135446Strhodes	set_limits(maps);
4406135446Strhodes
4407135446Strhodes	/*
4408186462Sdougb	 * Check if max number of open sockets that the system allows is
4409224092Sdougb	 * sufficiently large.	Failing this condition is not necessarily fatal,
4410186462Sdougb	 * but may cause subsequent runtime failures for a busy recursive
4411186462Sdougb	 * server.
4412182645Sdougb	 */
4413186462Sdougb	result = isc_socketmgr_getmaxsockets(ns_g_socketmgr, &maxsocks);
4414186462Sdougb	if (result != ISC_R_SUCCESS)
4415186462Sdougb		maxsocks = 0;
4416186462Sdougb	result = isc_resource_getcurlimit(isc_resource_openfiles, &nfiles);
4417186462Sdougb	if (result == ISC_R_SUCCESS && (isc_resourcevalue_t)maxsocks > nfiles) {
4418182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4419182645Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
4420186462Sdougb			      "max open files (%" ISC_PRINT_QUADFORMAT "u)"
4421186462Sdougb			      " is smaller than max sockets (%u)",
4422186462Sdougb			      nfiles, maxsocks);
4423186462Sdougb	}
4424182645Sdougb
4425182645Sdougb	/*
4426182645Sdougb	 * Set the number of socket reserved for TCP, stdio etc.
4427182645Sdougb	 */
4428182645Sdougb	obj = NULL;
4429182645Sdougb	result = ns_config_get(maps, "reserved-sockets", &obj);
4430182645Sdougb	INSIST(result == ISC_R_SUCCESS);
4431182645Sdougb	reserved = cfg_obj_asuint32(obj);
4432186462Sdougb	if (maxsocks != 0) {
4433186462Sdougb		if (maxsocks < 128U)			/* Prevent underflow. */
4434186462Sdougb			reserved = 0;
4435186462Sdougb		else if (reserved > maxsocks - 128U)	/* Minimum UDP space. */
4436186462Sdougb			reserved = maxsocks - 128;
4437186462Sdougb	}
4438186462Sdougb	/* Minimum TCP/stdio space. */
4439186462Sdougb	if (reserved < 128U)
4440182645Sdougb		reserved = 128;
4441186462Sdougb	if (reserved + 128U > maxsocks && maxsocks != 0) {
4442182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4443186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
4444182645Sdougb			      "less than 128 UDP sockets available after "
4445186462Sdougb			      "applying 'reserved-sockets' and 'maxsockets'");
4446182645Sdougb	}
4447182645Sdougb	isc__socketmgr_setreserved(ns_g_socketmgr, reserved);
4448186462Sdougb
4449182645Sdougb	/*
4450135446Strhodes	 * Configure various server options.
4451135446Strhodes	 */
4452135446Strhodes	configure_server_quota(maps, "transfers-out", &server->xfroutquota);
4453135446Strhodes	configure_server_quota(maps, "tcp-clients", &server->tcpquota);
4454135446Strhodes	configure_server_quota(maps, "recursive-clients",
4455135446Strhodes			       &server->recursionquota);
4456153816Sdougb	if (server->recursionquota.max > 1000)
4457153816Sdougb		isc_quota_soft(&server->recursionquota,
4458153816Sdougb			       server->recursionquota.max - 100);
4459153816Sdougb	else
4460153816Sdougb		isc_quota_soft(&server->recursionquota, 0);
4461135446Strhodes
4462225361Sdougb	CHECK(configure_view_acl(NULL, config, "blackhole", NULL,
4463225361Sdougb				 ns_g_aclconfctx, ns_g_mctx,
4464225361Sdougb				 &server->blackholeacl));
4465135446Strhodes	if (server->blackholeacl != NULL)
4466135446Strhodes		dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
4467135446Strhodes					     server->blackholeacl);
4468135446Strhodes
4469135446Strhodes	obj = NULL;
4470135446Strhodes	result = ns_config_get(maps, "match-mapped-addresses", &obj);
4471135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4472135446Strhodes	server->aclenv.match_mapped = cfg_obj_asboolean(obj);
4473135446Strhodes
4474225361Sdougb	CHECKM(ns_statschannels_configure(ns_g_server, config, ns_g_aclconfctx),
4475193149Sdougb	       "configuring statistics server(s)");
4476193149Sdougb
4477186462Sdougb	/*
4478186462Sdougb	 * Configure sets of UDP query source ports.
4479186462Sdougb	 */
4480186462Sdougb	CHECKM(isc_portset_create(ns_g_mctx, &v4portset),
4481186462Sdougb	       "creating UDP port set");
4482186462Sdougb	CHECKM(isc_portset_create(ns_g_mctx, &v6portset),
4483186462Sdougb	       "creating UDP port set");
4484135446Strhodes
4485186462Sdougb	usev4ports = NULL;
4486186462Sdougb	usev6ports = NULL;
4487186462Sdougb	avoidv4ports = NULL;
4488186462Sdougb	avoidv6ports = NULL;
4489186462Sdougb
4490186462Sdougb	(void)ns_config_get(maps, "use-v4-udp-ports", &usev4ports);
4491186462Sdougb	if (usev4ports != NULL)
4492186462Sdougb		portset_fromconf(v4portset, usev4ports, ISC_TRUE);
4493186462Sdougb	else {
4494186462Sdougb		CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low,
4495186462Sdougb					       &udpport_high),
4496186462Sdougb		       "get the default UDP/IPv4 port range");
4497186462Sdougb		if (udpport_low == udpport_high)
4498186462Sdougb			isc_portset_add(v4portset, udpport_low);
4499186462Sdougb		else {
4500186462Sdougb			isc_portset_addrange(v4portset, udpport_low,
4501186462Sdougb					     udpport_high);
4502186462Sdougb		}
4503186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4504186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4505186462Sdougb			      "using default UDP/IPv4 port range: [%d, %d]",
4506186462Sdougb			      udpport_low, udpport_high);
4507186462Sdougb	}
4508186462Sdougb	(void)ns_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports);
4509186462Sdougb	if (avoidv4ports != NULL)
4510186462Sdougb		portset_fromconf(v4portset, avoidv4ports, ISC_FALSE);
4511186462Sdougb
4512186462Sdougb	(void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports);
4513186462Sdougb	if (usev6ports != NULL)
4514186462Sdougb		portset_fromconf(v6portset, usev6ports, ISC_TRUE);
4515186462Sdougb	else {
4516186462Sdougb		CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low,
4517186462Sdougb					       &udpport_high),
4518186462Sdougb		       "get the default UDP/IPv6 port range");
4519186462Sdougb		if (udpport_low == udpport_high)
4520186462Sdougb			isc_portset_add(v6portset, udpport_low);
4521186462Sdougb		else {
4522186462Sdougb			isc_portset_addrange(v6portset, udpport_low,
4523186462Sdougb					     udpport_high);
4524186462Sdougb		}
4525186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4526186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4527186462Sdougb			      "using default UDP/IPv6 port range: [%d, %d]",
4528186462Sdougb			      udpport_low, udpport_high);
4529186462Sdougb	}
4530186462Sdougb	(void)ns_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports);
4531186462Sdougb	if (avoidv6ports != NULL)
4532186462Sdougb		portset_fromconf(v6portset, avoidv6ports, ISC_FALSE);
4533186462Sdougb
4534186462Sdougb	dns_dispatchmgr_setavailports(ns_g_dispatchmgr, v4portset, v6portset);
4535186462Sdougb
4536135446Strhodes	/*
4537135446Strhodes	 * Set the EDNS UDP size when we don't match a view.
4538135446Strhodes	 */
4539135446Strhodes	obj = NULL;
4540135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
4541135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4542135446Strhodes	udpsize = cfg_obj_asuint32(obj);
4543135446Strhodes	if (udpsize < 512)
4544135446Strhodes		udpsize = 512;
4545135446Strhodes	if (udpsize > 4096)
4546135446Strhodes		udpsize = 4096;
4547135446Strhodes	ns_g_udpsize = (isc_uint16_t)udpsize;
4548135446Strhodes
4549135446Strhodes	/*
4550135446Strhodes	 * Configure the zone manager.
4551135446Strhodes	 */
4552135446Strhodes	obj = NULL;
4553135446Strhodes	result = ns_config_get(maps, "transfers-in", &obj);
4554135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4555135446Strhodes	dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj));
4556135446Strhodes
4557135446Strhodes	obj = NULL;
4558135446Strhodes	result = ns_config_get(maps, "transfers-per-ns", &obj);
4559135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4560135446Strhodes	dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj));
4561135446Strhodes
4562135446Strhodes	obj = NULL;
4563135446Strhodes	result = ns_config_get(maps, "serial-query-rate", &obj);
4564135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4565135446Strhodes	dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj));
4566135446Strhodes
4567135446Strhodes	/*
4568135446Strhodes	 * Determine which port to use for listening for incoming connections.
4569135446Strhodes	 */
4570135446Strhodes	if (ns_g_port != 0)
4571135446Strhodes		listen_port = ns_g_port;
4572135446Strhodes	else
4573135446Strhodes		CHECKM(ns_config_getport(config, &listen_port), "port");
4574135446Strhodes
4575135446Strhodes	/*
4576135446Strhodes	 * Find the listen queue depth.
4577135446Strhodes	 */
4578135446Strhodes	obj = NULL;
4579135446Strhodes	result = ns_config_get(maps, "tcp-listen-queue", &obj);
4580135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4581135446Strhodes	ns_g_listen = cfg_obj_asuint32(obj);
4582135446Strhodes	if (ns_g_listen < 3)
4583135446Strhodes		ns_g_listen = 3;
4584135446Strhodes
4585135446Strhodes	/*
4586135446Strhodes	 * Configure the interface manager according to the "listen-on"
4587135446Strhodes	 * statement.
4588135446Strhodes	 */
4589135446Strhodes	{
4590165071Sdougb		const cfg_obj_t *clistenon = NULL;
4591135446Strhodes		ns_listenlist_t *listenon = NULL;
4592135446Strhodes
4593135446Strhodes		clistenon = NULL;
4594135446Strhodes		/*
4595135446Strhodes		 * Even though listen-on is present in the default
4596135446Strhodes		 * configuration, we can't use it here, since it isn't
4597135446Strhodes		 * used if we're in lwresd mode.  This way is easier.
4598135446Strhodes		 */
4599135446Strhodes		if (options != NULL)
4600135446Strhodes			(void)cfg_map_get(options, "listen-on", &clistenon);
4601135446Strhodes		if (clistenon != NULL) {
4602225361Sdougb			/* check return code? */
4603225361Sdougb			(void)ns_listenlist_fromconfig(clistenon, config,
4604225361Sdougb						       ns_g_aclconfctx,
4605225361Sdougb						       ns_g_mctx, &listenon);
4606135446Strhodes		} else if (!ns_g_lwresdonly) {
4607135446Strhodes			/*
4608135446Strhodes			 * Not specified, use default.
4609135446Strhodes			 */
4610135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
4611135446Strhodes						    ISC_TRUE, &listenon));
4612135446Strhodes		}
4613135446Strhodes		if (listenon != NULL) {
4614135446Strhodes			ns_interfacemgr_setlistenon4(server->interfacemgr,
4615135446Strhodes						     listenon);
4616135446Strhodes			ns_listenlist_detach(&listenon);
4617135446Strhodes		}
4618135446Strhodes	}
4619135446Strhodes	/*
4620135446Strhodes	 * Ditto for IPv6.
4621135446Strhodes	 */
4622135446Strhodes	{
4623165071Sdougb		const cfg_obj_t *clistenon = NULL;
4624135446Strhodes		ns_listenlist_t *listenon = NULL;
4625135446Strhodes
4626135446Strhodes		if (options != NULL)
4627135446Strhodes			(void)cfg_map_get(options, "listen-on-v6", &clistenon);
4628135446Strhodes		if (clistenon != NULL) {
4629225361Sdougb			/* check return code? */
4630225361Sdougb			(void)ns_listenlist_fromconfig(clistenon, config,
4631225361Sdougb						       ns_g_aclconfctx,
4632225361Sdougb						       ns_g_mctx, &listenon);
4633135446Strhodes		} else if (!ns_g_lwresdonly) {
4634193149Sdougb			isc_boolean_t enable;
4635135446Strhodes			/*
4636135446Strhodes			 * Not specified, use default.
4637135446Strhodes			 */
4638193149Sdougb			enable = ISC_TF(isc_net_probeipv4() != ISC_R_SUCCESS);
4639135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
4640193149Sdougb						    enable, &listenon));
4641135446Strhodes		}
4642135446Strhodes		if (listenon != NULL) {
4643135446Strhodes			ns_interfacemgr_setlistenon6(server->interfacemgr,
4644135446Strhodes						     listenon);
4645135446Strhodes			ns_listenlist_detach(&listenon);
4646135446Strhodes		}
4647135446Strhodes	}
4648135446Strhodes
4649135446Strhodes	/*
4650135446Strhodes	 * Rescan the interface list to pick up changes in the
4651135446Strhodes	 * listen-on option.  It's important that we do this before we try
4652135446Strhodes	 * to configure the query source, since the dispatcher we use might
4653135446Strhodes	 * be shared with an interface.
4654135446Strhodes	 */
4655135446Strhodes	scan_interfaces(server, ISC_TRUE);
4656135446Strhodes
4657135446Strhodes	/*
4658135446Strhodes	 * Arrange for further interface scanning to occur periodically
4659135446Strhodes	 * as specified by the "interface-interval" option.
4660135446Strhodes	 */
4661135446Strhodes	obj = NULL;
4662135446Strhodes	result = ns_config_get(maps, "interface-interval", &obj);
4663135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4664135446Strhodes	interface_interval = cfg_obj_asuint32(obj) * 60;
4665135446Strhodes	if (interface_interval == 0) {
4666135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
4667135446Strhodes				      isc_timertype_inactive,
4668135446Strhodes				      NULL, NULL, ISC_TRUE));
4669135446Strhodes	} else if (server->interface_interval != interface_interval) {
4670135446Strhodes		isc_interval_set(&interval, interface_interval, 0);
4671135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
4672135446Strhodes				      isc_timertype_ticker,
4673135446Strhodes				      NULL, &interval, ISC_FALSE));
4674135446Strhodes	}
4675135446Strhodes	server->interface_interval = interface_interval;
4676135446Strhodes
4677135446Strhodes	/*
4678135446Strhodes	 * Configure the dialup heartbeat timer.
4679135446Strhodes	 */
4680135446Strhodes	obj = NULL;
4681135446Strhodes	result = ns_config_get(maps, "heartbeat-interval", &obj);
4682135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4683135446Strhodes	heartbeat_interval = cfg_obj_asuint32(obj) * 60;
4684135446Strhodes	if (heartbeat_interval == 0) {
4685135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
4686135446Strhodes				      isc_timertype_inactive,
4687135446Strhodes				      NULL, NULL, ISC_TRUE));
4688135446Strhodes	} else if (server->heartbeat_interval != heartbeat_interval) {
4689135446Strhodes		isc_interval_set(&interval, heartbeat_interval, 0);
4690135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
4691135446Strhodes				      isc_timertype_ticker,
4692135446Strhodes				      NULL, &interval, ISC_FALSE));
4693135446Strhodes	}
4694135446Strhodes	server->heartbeat_interval = heartbeat_interval;
4695186462Sdougb
4696170222Sdougb	isc_interval_set(&interval, 1200, 0);
4697170222Sdougb	CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
4698170222Sdougb			      &interval, ISC_FALSE));
4699135446Strhodes
4700135446Strhodes	/*
4701224092Sdougb	 * Write the PID file.
4702224092Sdougb	 */
4703224092Sdougb	obj = NULL;
4704224092Sdougb	if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS)
4705224092Sdougb		if (cfg_obj_isvoid(obj))
4706224092Sdougb			ns_os_writepidfile(NULL, first_time);
4707224092Sdougb		else
4708224092Sdougb			ns_os_writepidfile(cfg_obj_asstring(obj), first_time);
4709224092Sdougb	else if (ns_g_lwresdonly)
4710224092Sdougb		ns_os_writepidfile(lwresd_g_defaultpidfile, first_time);
4711224092Sdougb	else
4712224092Sdougb		ns_os_writepidfile(ns_g_defaultpidfile, first_time);
4713224092Sdougb
4714224092Sdougb	/*
4715224092Sdougb	 * Configure the server-wide session key.  This must be done before
4716224092Sdougb	 * configure views because zone configuration may need to know
4717224092Sdougb	 * session-keyname.
4718224092Sdougb	 *
4719224092Sdougb	 * Failure of session key generation isn't fatal at this time; if it
4720224092Sdougb	 * turns out that a session key is really needed but doesn't exist,
4721224092Sdougb	 * we'll treat it as a fatal error then.
4722224092Sdougb	 */
4723224092Sdougb	(void)configure_session_key(maps, server, ns_g_mctx);
4724224092Sdougb
4725225361Sdougb	views = NULL;
4726225361Sdougb	(void)cfg_map_get(config, "view", &views);
4727225361Sdougb
4728224092Sdougb	/*
4729225361Sdougb	 * Create the views and count all the configured zones in
4730225361Sdougb	 * order to correctly size the zone manager's task table.
4731225361Sdougb	 * (We only count zones for configured views; the built-in
4732225361Sdougb	 * "bind" view can be ignored as it only adds a negligible
4733225361Sdougb	 * number of zones.)
4734225361Sdougb	 *
4735225361Sdougb	 * If we're allowing new zones, we need to be able to find the
4736225361Sdougb	 * new zone file and count those as well.  So we setup the new
4737225361Sdougb	 * zone configuration context, but otherwise view configuration
4738225361Sdougb	 * waits until after the zone manager's task list has been sized.
4739225361Sdougb	 */
4740225361Sdougb	for (element = cfg_list_first(views);
4741225361Sdougb	     element != NULL;
4742225361Sdougb	     element = cfg_list_next(element))
4743225361Sdougb	{
4744225361Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
4745225361Sdougb		const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options");
4746225361Sdougb		view = NULL;
4747225361Sdougb
4748225361Sdougb		CHECK(create_view(vconfig, &viewlist, &view));
4749225361Sdougb		INSIST(view != NULL);
4750225361Sdougb
4751225361Sdougb		num_zones += count_zones(voptions);
4752225361Sdougb		CHECK(setup_newzones(view, config, vconfig, conf_parser,
4753225361Sdougb				     ns_g_aclconfctx));
4754225361Sdougb
4755225361Sdougb		nzctx = view->new_zone_config;
4756225361Sdougb		if (nzctx != NULL && nzctx->nzconfig != NULL)
4757225361Sdougb			num_zones += count_zones(nzctx->nzconfig);
4758225361Sdougb
4759225361Sdougb		dns_view_detach(&view);
4760225361Sdougb	}
4761225361Sdougb
4762225361Sdougb	/*
4763225361Sdougb	 * If there were no explicit views then we do the default
4764225361Sdougb	 * view here.
4765225361Sdougb	 */
4766225361Sdougb	if (views == NULL) {
4767225361Sdougb		CHECK(create_view(NULL, &viewlist, &view));
4768225361Sdougb		INSIST(view != NULL);
4769225361Sdougb
4770225361Sdougb		num_zones = count_zones(config);
4771225361Sdougb
4772225361Sdougb		CHECK(setup_newzones(view, config, NULL,  conf_parser,
4773225361Sdougb				     ns_g_aclconfctx));
4774225361Sdougb
4775225361Sdougb		nzctx = view->new_zone_config;
4776225361Sdougb		if (nzctx != NULL && nzctx->nzconfig != NULL)
4777225361Sdougb			num_zones += count_zones(nzctx->nzconfig);
4778225361Sdougb
4779225361Sdougb		dns_view_detach(&view);
4780225361Sdougb	}
4781225361Sdougb
4782225361Sdougb	/*
4783225361Sdougb	 * Zones have been counted; set the zone manager task pool size.
4784225361Sdougb	 */
4785225361Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4786225361Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4787225361Sdougb		      "sizing zone task pool based on %d zones", num_zones);
4788225361Sdougb	CHECK(dns_zonemgr_setsize(ns_g_server->zonemgr, num_zones));
4789225361Sdougb
4790225361Sdougb	/*
4791135446Strhodes	 * Configure and freeze all explicit views.  Explicit
4792135446Strhodes	 * views that have zones were already created at parsing
4793135446Strhodes	 * time, but views with no zones must be created here.
4794135446Strhodes	 */
4795135446Strhodes	for (element = cfg_list_first(views);
4796135446Strhodes	     element != NULL;
4797135446Strhodes	     element = cfg_list_next(element))
4798135446Strhodes	{
4799224092Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
4800225361Sdougb
4801135446Strhodes		view = NULL;
4802225361Sdougb		CHECK(find_view(vconfig, &viewlist, &view));
4803225361Sdougb		CHECK(configure_view(view, config, vconfig,
4804225361Sdougb				     &cachelist, bindkeys, ns_g_mctx,
4805225361Sdougb				     ns_g_aclconfctx, ISC_TRUE));
4806135446Strhodes		dns_view_freeze(view);
4807135446Strhodes		dns_view_detach(&view);
4808135446Strhodes	}
4809135446Strhodes
4810135446Strhodes	/*
4811135446Strhodes	 * Make sure we have a default view if and only if there
4812135446Strhodes	 * were no explicit views.
4813135446Strhodes	 */
4814135446Strhodes	if (views == NULL) {
4815225361Sdougb		view = NULL;
4816225361Sdougb		CHECK(find_view(NULL, &viewlist, &view));
4817225361Sdougb		CHECK(configure_view(view, config, NULL,
4818224092Sdougb				     &cachelist, bindkeys,
4819225361Sdougb				     ns_g_mctx, ns_g_aclconfctx, ISC_TRUE));
4820135446Strhodes		dns_view_freeze(view);
4821135446Strhodes		dns_view_detach(&view);
4822135446Strhodes	}
4823135446Strhodes
4824135446Strhodes	/*
4825224092Sdougb	 * Create (or recreate) the built-in views.
4826135446Strhodes	 */
4827135446Strhodes	builtin_views = NULL;
4828135446Strhodes	RUNTIME_CHECK(cfg_map_get(ns_g_config, "view",
4829135446Strhodes				  &builtin_views) == ISC_R_SUCCESS);
4830135446Strhodes	for (element = cfg_list_first(builtin_views);
4831135446Strhodes	     element != NULL;
4832135446Strhodes	     element = cfg_list_next(element))
4833135446Strhodes	{
4834224092Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
4835224092Sdougb
4836224092Sdougb		CHECK(create_view(vconfig, &builtin_viewlist, &view));
4837225361Sdougb		CHECK(configure_view(view, config, vconfig,
4838224092Sdougb				     &cachelist, bindkeys,
4839225361Sdougb				     ns_g_mctx, ns_g_aclconfctx, ISC_FALSE));
4840135446Strhodes		dns_view_freeze(view);
4841135446Strhodes		dns_view_detach(&view);
4842135446Strhodes		view = NULL;
4843135446Strhodes	}
4844135446Strhodes
4845224092Sdougb	/* Now combine the two viewlists into one */
4846224092Sdougb	ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link);
4847224092Sdougb
4848224092Sdougb	/* Swap our new view list with the production one. */
4849135446Strhodes	tmpviewlist = server->viewlist;
4850135446Strhodes	server->viewlist = viewlist;
4851135446Strhodes	viewlist = tmpviewlist;
4852135446Strhodes
4853224092Sdougb	/* Make the view list available to each of the views */
4854224092Sdougb	view = ISC_LIST_HEAD(server->viewlist);
4855224092Sdougb	while (view != NULL) {
4856224092Sdougb		view->viewlist = &server->viewlist;
4857224092Sdougb		view = ISC_LIST_NEXT(view, link);
4858224092Sdougb	}
4859224092Sdougb
4860224092Sdougb	/* Swap our new cache list with the production one. */
4861224092Sdougb	tmpcachelist = server->cachelist;
4862224092Sdougb	server->cachelist = cachelist;
4863224092Sdougb	cachelist = tmpcachelist;
4864224092Sdougb
4865224092Sdougb	/* Load the TKEY information from the configuration. */
4866135446Strhodes	if (options != NULL) {
4867135446Strhodes		dns_tkeyctx_t *t = NULL;
4868135446Strhodes		CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy,
4869135446Strhodes					     &t),
4870135446Strhodes		       "configuring TKEY");
4871135446Strhodes		if (server->tkeyctx != NULL)
4872135446Strhodes			dns_tkeyctx_destroy(&server->tkeyctx);
4873135446Strhodes		server->tkeyctx = t;
4874135446Strhodes	}
4875135446Strhodes
4876135446Strhodes	/*
4877135446Strhodes	 * Bind the control port(s).
4878135446Strhodes	 */
4879135446Strhodes	CHECKM(ns_controls_configure(ns_g_server->controls, config,
4880225361Sdougb				     ns_g_aclconfctx),
4881135446Strhodes	       "binding control channel(s)");
4882135446Strhodes
4883135446Strhodes	/*
4884135446Strhodes	 * Bind the lwresd port(s).
4885135446Strhodes	 */
4886135446Strhodes	CHECKM(ns_lwresd_configure(ns_g_mctx, config),
4887135446Strhodes	       "binding lightweight resolver ports");
4888135446Strhodes
4889135446Strhodes	/*
4890135446Strhodes	 * Open the source of entropy.
4891135446Strhodes	 */
4892135446Strhodes	if (first_time) {
4893135446Strhodes		obj = NULL;
4894135446Strhodes		result = ns_config_get(maps, "random-device", &obj);
4895135446Strhodes		if (result != ISC_R_SUCCESS) {
4896135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4897135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4898135446Strhodes				      "no source of entropy found");
4899135446Strhodes		} else {
4900135446Strhodes			const char *randomdev = cfg_obj_asstring(obj);
4901135446Strhodes			result = isc_entropy_createfilesource(ns_g_entropy,
4902135446Strhodes							      randomdev);
4903135446Strhodes			if (result != ISC_R_SUCCESS)
4904135446Strhodes				isc_log_write(ns_g_lctx,
4905135446Strhodes					      NS_LOGCATEGORY_GENERAL,
4906135446Strhodes					      NS_LOGMODULE_SERVER,
4907135446Strhodes					      ISC_LOG_INFO,
4908135446Strhodes					      "could not open entropy source "
4909135446Strhodes					      "%s: %s",
4910135446Strhodes					      randomdev,
4911135446Strhodes					      isc_result_totext(result));
4912135446Strhodes#ifdef PATH_RANDOMDEV
4913135446Strhodes			if (ns_g_fallbackentropy != NULL) {
4914135446Strhodes				if (result != ISC_R_SUCCESS) {
4915135446Strhodes					isc_log_write(ns_g_lctx,
4916135446Strhodes						      NS_LOGCATEGORY_GENERAL,
4917135446Strhodes						      NS_LOGMODULE_SERVER,
4918135446Strhodes						      ISC_LOG_INFO,
4919135446Strhodes						      "using pre-chroot entropy source "
4920135446Strhodes						      "%s",
4921135446Strhodes						      PATH_RANDOMDEV);
4922135446Strhodes					isc_entropy_detach(&ns_g_entropy);
4923135446Strhodes					isc_entropy_attach(ns_g_fallbackentropy,
4924135446Strhodes							   &ns_g_entropy);
4925135446Strhodes				}
4926135446Strhodes				isc_entropy_detach(&ns_g_fallbackentropy);
4927135446Strhodes			}
4928135446Strhodes#endif
4929135446Strhodes		}
4930135446Strhodes	}
4931135446Strhodes
4932135446Strhodes	/*
4933135446Strhodes	 * Relinquish root privileges.
4934135446Strhodes	 */
4935135446Strhodes	if (first_time)
4936135446Strhodes		ns_os_changeuser();
4937135446Strhodes
4938135446Strhodes	/*
4939186462Sdougb	 * Check that the working directory is writable.
4940186462Sdougb	 */
4941186462Sdougb	if (access(".", W_OK) != 0) {
4942186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4943186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4944186462Sdougb			      "the working directory is not writable");
4945186462Sdougb	}
4946186462Sdougb
4947186462Sdougb	/*
4948135446Strhodes	 * Configure the logging system.
4949135446Strhodes	 *
4950135446Strhodes	 * Do this after changing UID to make sure that any log
4951135446Strhodes	 * files specified in named.conf get created by the
4952135446Strhodes	 * unprivileged user, not root.
4953135446Strhodes	 */
4954135446Strhodes	if (ns_g_logstderr) {
4955135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4956135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4957135446Strhodes			      "ignoring config file logging "
4958135446Strhodes			      "statement due to -g option");
4959135446Strhodes	} else {
4960165071Sdougb		const cfg_obj_t *logobj = NULL;
4961135446Strhodes		isc_logconfig_t *logc = NULL;
4962135446Strhodes
4963135446Strhodes		CHECKM(isc_logconfig_create(ns_g_lctx, &logc),
4964135446Strhodes		       "creating new logging configuration");
4965135446Strhodes
4966135446Strhodes		logobj = NULL;
4967135446Strhodes		(void)cfg_map_get(config, "logging", &logobj);
4968135446Strhodes		if (logobj != NULL) {
4969135446Strhodes			CHECKM(ns_log_configure(logc, logobj),
4970135446Strhodes			       "configuring logging");
4971135446Strhodes		} else {
4972135446Strhodes			CHECKM(ns_log_setdefaultchannels(logc),
4973135446Strhodes			       "setting up default logging channels");
4974135446Strhodes			CHECKM(ns_log_setunmatchedcategory(logc),
4975135446Strhodes			       "setting up default 'category unmatched'");
4976135446Strhodes			CHECKM(ns_log_setdefaultcategory(logc),
4977135446Strhodes			       "setting up default 'category default'");
4978135446Strhodes		}
4979135446Strhodes
4980135446Strhodes		result = isc_logconfig_use(ns_g_lctx, logc);
4981135446Strhodes		if (result != ISC_R_SUCCESS) {
4982135446Strhodes			isc_logconfig_destroy(&logc);
4983135446Strhodes			CHECKM(result, "installing logging configuration");
4984135446Strhodes		}
4985135446Strhodes
4986135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4987135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
4988135446Strhodes			      "now using logging configuration from "
4989135446Strhodes			      "config file");
4990135446Strhodes	}
4991135446Strhodes
4992135446Strhodes	/*
4993135446Strhodes	 * Set the default value of the query logging flag depending
4994135446Strhodes	 * whether a "queries" category has been defined.  This is
4995135446Strhodes	 * a disgusting hack, but we need to do this for BIND 8
4996135446Strhodes	 * compatibility.
4997135446Strhodes	 */
4998135446Strhodes	if (first_time) {
4999165071Sdougb		const cfg_obj_t *logobj = NULL;
5000165071Sdougb		const cfg_obj_t *categories = NULL;
5001135446Strhodes
5002135446Strhodes		obj = NULL;
5003135446Strhodes		if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) {
5004135446Strhodes			server->log_queries = cfg_obj_asboolean(obj);
5005135446Strhodes		} else {
5006135446Strhodes
5007135446Strhodes			(void)cfg_map_get(config, "logging", &logobj);
5008135446Strhodes			if (logobj != NULL)
5009135446Strhodes				(void)cfg_map_get(logobj, "category",
5010135446Strhodes						  &categories);
5011135446Strhodes			if (categories != NULL) {
5012165071Sdougb				const cfg_listelt_t *element;
5013135446Strhodes				for (element = cfg_list_first(categories);
5014135446Strhodes				     element != NULL;
5015135446Strhodes				     element = cfg_list_next(element))
5016135446Strhodes				{
5017165071Sdougb					const cfg_obj_t *catobj;
5018165071Sdougb					const char *str;
5019135446Strhodes
5020135446Strhodes					obj = cfg_listelt_value(element);
5021135446Strhodes					catobj = cfg_tuple_get(obj, "name");
5022135446Strhodes					str = cfg_obj_asstring(catobj);
5023135446Strhodes					if (strcasecmp(str, "queries") == 0)
5024135446Strhodes						server->log_queries = ISC_TRUE;
5025135446Strhodes				}
5026135446Strhodes			}
5027135446Strhodes		}
5028135446Strhodes	}
5029135446Strhodes
5030186462Sdougb
5031135446Strhodes	obj = NULL;
5032135446Strhodes	if (options != NULL &&
5033193149Sdougb	    cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS)
5034193149Sdougb		ns_g_memstatistics = cfg_obj_asboolean(obj);
5035193149Sdougb	else
5036193149Sdougb		ns_g_memstatistics =
5037193149Sdougb			ISC_TF((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0);
5038193149Sdougb
5039193149Sdougb	obj = NULL;
5040193149Sdougb	if (ns_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS)
5041135446Strhodes		ns_main_setmemstats(cfg_obj_asstring(obj));
5042193149Sdougb	else if (ns_g_memstatistics)
5043193149Sdougb		ns_main_setmemstats("named.memstats");
5044135446Strhodes	else
5045135446Strhodes		ns_main_setmemstats(NULL);
5046135446Strhodes
5047135446Strhodes	obj = NULL;
5048135446Strhodes	result = ns_config_get(maps, "statistics-file", &obj);
5049135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5050135446Strhodes	CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)),
5051135446Strhodes	       "strdup");
5052135446Strhodes
5053135446Strhodes	obj = NULL;
5054135446Strhodes	result = ns_config_get(maps, "dump-file", &obj);
5055135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5056135446Strhodes	CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)),
5057135446Strhodes	       "strdup");
5058135446Strhodes
5059135446Strhodes	obj = NULL;
5060224092Sdougb	result = ns_config_get(maps, "secroots-file", &obj);
5061224092Sdougb	INSIST(result == ISC_R_SUCCESS);
5062224092Sdougb	CHECKM(setstring(server, &server->secrootsfile, cfg_obj_asstring(obj)),
5063224092Sdougb	       "strdup");
5064224092Sdougb
5065224092Sdougb	obj = NULL;
5066135446Strhodes	result = ns_config_get(maps, "recursing-file", &obj);
5067135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5068135446Strhodes	CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)),
5069135446Strhodes	       "strdup");
5070135446Strhodes
5071135446Strhodes	obj = NULL;
5072135446Strhodes	result = ns_config_get(maps, "version", &obj);
5073135446Strhodes	if (result == ISC_R_SUCCESS) {
5074135446Strhodes		CHECKM(setoptstring(server, &server->version, obj), "strdup");
5075135446Strhodes		server->version_set = ISC_TRUE;
5076135446Strhodes	} else {
5077135446Strhodes		server->version_set = ISC_FALSE;
5078135446Strhodes	}
5079135446Strhodes
5080135446Strhodes	obj = NULL;
5081135446Strhodes	result = ns_config_get(maps, "hostname", &obj);
5082135446Strhodes	if (result == ISC_R_SUCCESS) {
5083135446Strhodes		CHECKM(setoptstring(server, &server->hostname, obj), "strdup");
5084135446Strhodes		server->hostname_set = ISC_TRUE;
5085135446Strhodes	} else {
5086135446Strhodes		server->hostname_set = ISC_FALSE;
5087135446Strhodes	}
5088135446Strhodes
5089135446Strhodes	obj = NULL;
5090135446Strhodes	result = ns_config_get(maps, "server-id", &obj);
5091135446Strhodes	server->server_usehostname = ISC_FALSE;
5092135446Strhodes	if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
5093193149Sdougb		/* The parser translates "hostname" to ISC_TRUE */
5094193149Sdougb		server->server_usehostname = cfg_obj_asboolean(obj);
5095193149Sdougb		result = setstring(server, &server->server_id, NULL);
5096193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5097135446Strhodes	} else if (result == ISC_R_SUCCESS) {
5098193149Sdougb		/* Found a quoted string */
5099135446Strhodes		CHECKM(setoptstring(server, &server->server_id, obj), "strdup");
5100135446Strhodes	} else {
5101170222Sdougb		result = setstring(server, &server->server_id, NULL);
5102135446Strhodes		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5103135446Strhodes	}
5104135446Strhodes
5105135446Strhodes	obj = NULL;
5106135446Strhodes	result = ns_config_get(maps, "flush-zones-on-shutdown", &obj);
5107135446Strhodes	if (result == ISC_R_SUCCESS) {
5108135446Strhodes		server->flushonshutdown = cfg_obj_asboolean(obj);
5109135446Strhodes	} else {
5110135446Strhodes		server->flushonshutdown = ISC_FALSE;
5111135446Strhodes	}
5112135446Strhodes
5113135446Strhodes	result = ISC_R_SUCCESS;
5114135446Strhodes
5115135446Strhodes cleanup:
5116186462Sdougb	if (v4portset != NULL)
5117186462Sdougb		isc_portset_destroy(ns_g_mctx, &v4portset);
5118186462Sdougb
5119186462Sdougb	if (v6portset != NULL)
5120186462Sdougb		isc_portset_destroy(ns_g_mctx, &v6portset);
5121186462Sdougb
5122224092Sdougb	if (conf_parser != NULL) {
5123135446Strhodes		if (config != NULL)
5124224092Sdougb			cfg_obj_destroy(conf_parser, &config);
5125224092Sdougb		cfg_parser_destroy(&conf_parser);
5126135446Strhodes	}
5127135446Strhodes
5128224092Sdougb	if (bindkeys_parser != NULL) {
5129224092Sdougb		if (bindkeys  != NULL)
5130224092Sdougb			cfg_obj_destroy(bindkeys_parser, &bindkeys);
5131224092Sdougb		cfg_parser_destroy(&bindkeys_parser);
5132224092Sdougb	}
5133224092Sdougb
5134135446Strhodes	if (view != NULL)
5135135446Strhodes		dns_view_detach(&view);
5136135446Strhodes
5137135446Strhodes	/*
5138135446Strhodes	 * This cleans up either the old production view list
5139135446Strhodes	 * or our temporary list depending on whether they
5140135446Strhodes	 * were swapped above or not.
5141135446Strhodes	 */
5142135446Strhodes	for (view = ISC_LIST_HEAD(viewlist);
5143135446Strhodes	     view != NULL;
5144135446Strhodes	     view = view_next) {
5145135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
5146135446Strhodes		ISC_LIST_UNLINK(viewlist, view, link);
5147170222Sdougb		if (result == ISC_R_SUCCESS &&
5148170222Sdougb		    strcmp(view->name, "_bind") != 0)
5149170222Sdougb			(void)dns_zt_apply(view->zonetable, ISC_FALSE,
5150170222Sdougb					   removed, view);
5151135446Strhodes		dns_view_detach(&view);
5152135446Strhodes	}
5153135446Strhodes
5154224092Sdougb	/* Same cleanup for cache list. */
5155224092Sdougb	while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) {
5156224092Sdougb		ISC_LIST_UNLINK(cachelist, nsc, link);
5157224092Sdougb		dns_cache_detach(&nsc->cache);
5158224092Sdougb		isc_mem_put(server->mctx, nsc, sizeof(*nsc));
5159224092Sdougb	}
5160224092Sdougb
5161135446Strhodes	/*
5162135446Strhodes	 * Adjust the listening interfaces in accordance with the source
5163135446Strhodes	 * addresses specified in views and zones.
5164135446Strhodes	 */
5165135446Strhodes	if (isc_net_probeipv6() == ISC_R_SUCCESS)
5166135446Strhodes		adjust_interfaces(server, ns_g_mctx);
5167135446Strhodes
5168135446Strhodes	/* Relinquish exclusive access to configuration data. */
5169234010Sdougb	if (exclusive)
5170234010Sdougb		isc_task_endexclusive(server->task);
5171135446Strhodes
5172135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5173135446Strhodes		      ISC_LOG_DEBUG(1), "load_configuration: %s",
5174135446Strhodes		      isc_result_totext(result));
5175135446Strhodes
5176135446Strhodes	return (result);
5177135446Strhodes}
5178135446Strhodes
5179135446Strhodesstatic isc_result_t
5180135446Strhodesload_zones(ns_server_t *server, isc_boolean_t stop) {
5181135446Strhodes	isc_result_t result;
5182135446Strhodes	dns_view_t *view;
5183135446Strhodes
5184135446Strhodes	result = isc_task_beginexclusive(server->task);
5185135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5186135446Strhodes
5187135446Strhodes	/*
5188135446Strhodes	 * Load zone data from disk.
5189135446Strhodes	 */
5190135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5191135446Strhodes	     view != NULL;
5192135446Strhodes	     view = ISC_LIST_NEXT(view, link))
5193135446Strhodes	{
5194135446Strhodes		CHECK(dns_view_load(view, stop));
5195224092Sdougb		if (view->managed_keys != NULL)
5196224092Sdougb			CHECK(dns_zone_load(view->managed_keys));
5197135446Strhodes	}
5198135446Strhodes
5199135446Strhodes	/*
5200135446Strhodes	 * Force zone maintenance.  Do this after loading
5201135446Strhodes	 * so that we know when we need to force AXFR of
5202135446Strhodes	 * slave zones whose master files are missing.
5203135446Strhodes	 */
5204135446Strhodes	CHECK(dns_zonemgr_forcemaint(server->zonemgr));
5205135446Strhodes cleanup:
5206186462Sdougb	isc_task_endexclusive(server->task);
5207135446Strhodes	return (result);
5208135446Strhodes}
5209135446Strhodes
5210135446Strhodesstatic isc_result_t
5211135446Strhodesload_new_zones(ns_server_t *server, isc_boolean_t stop) {
5212135446Strhodes	isc_result_t result;
5213135446Strhodes	dns_view_t *view;
5214135446Strhodes
5215135446Strhodes	result = isc_task_beginexclusive(server->task);
5216135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5217135446Strhodes
5218135446Strhodes	/*
5219135446Strhodes	 * Load zone data from disk.
5220135446Strhodes	 */
5221135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5222135446Strhodes	     view != NULL;
5223135446Strhodes	     view = ISC_LIST_NEXT(view, link))
5224135446Strhodes	{
5225135446Strhodes		CHECK(dns_view_loadnew(view, stop));
5226224092Sdougb
5227224092Sdougb		/* Load managed-keys data */
5228224092Sdougb		if (view->managed_keys != NULL)
5229224092Sdougb			CHECK(dns_zone_loadnew(view->managed_keys));
5230135446Strhodes	}
5231224092Sdougb
5232135446Strhodes	/*
5233224092Sdougb	 * Resume zone XFRs.
5234135446Strhodes	 */
5235135446Strhodes	dns_zonemgr_resumexfrs(server->zonemgr);
5236135446Strhodes cleanup:
5237186462Sdougb	isc_task_endexclusive(server->task);
5238135446Strhodes	return (result);
5239135446Strhodes}
5240135446Strhodes
5241135446Strhodesstatic void
5242135446Strhodesrun_server(isc_task_t *task, isc_event_t *event) {
5243135446Strhodes	isc_result_t result;
5244135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
5245135446Strhodes
5246143731Sdougb	INSIST(task == server->task);
5247135446Strhodes
5248135446Strhodes	isc_event_free(&event);
5249135446Strhodes
5250135446Strhodes	CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy,
5251135446Strhodes					  &ns_g_dispatchmgr),
5252135446Strhodes		   "creating dispatch manager");
5253135446Strhodes
5254193149Sdougb	dns_dispatchmgr_setstats(ns_g_dispatchmgr, server->resolverstats);
5255193149Sdougb
5256135446Strhodes	CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr,
5257135446Strhodes					  ns_g_socketmgr, ns_g_dispatchmgr,
5258135446Strhodes					  &server->interfacemgr),
5259135446Strhodes		   "creating interface manager");
5260135446Strhodes
5261135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5262135446Strhodes				    NULL, NULL, server->task,
5263135446Strhodes				    interface_timer_tick,
5264135446Strhodes				    server, &server->interface_timer),
5265135446Strhodes		   "creating interface timer");
5266135446Strhodes
5267135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5268135446Strhodes				    NULL, NULL, server->task,
5269135446Strhodes				    heartbeat_timer_tick,
5270135446Strhodes				    server, &server->heartbeat_timer),
5271135446Strhodes		   "creating heartbeat timer");
5272135446Strhodes
5273170222Sdougb	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5274170222Sdougb				    NULL, NULL, server->task, pps_timer_tick,
5275170222Sdougb				    server, &server->pps_timer),
5276170222Sdougb		   "creating pps timer");
5277170222Sdougb
5278135446Strhodes	CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser),
5279135446Strhodes		   "creating default configuration parser");
5280135446Strhodes
5281135446Strhodes	if (ns_g_lwresdonly)
5282135446Strhodes		CHECKFATAL(load_configuration(lwresd_g_conffile, server,
5283135446Strhodes					      ISC_TRUE),
5284135446Strhodes			   "loading configuration");
5285135446Strhodes	else
5286135446Strhodes		CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE),
5287135446Strhodes			   "loading configuration");
5288135446Strhodes
5289135446Strhodes	isc_hash_init();
5290135446Strhodes
5291143731Sdougb	CHECKFATAL(load_zones(server, ISC_FALSE), "loading zones");
5292135446Strhodes
5293143731Sdougb	ns_os_started();
5294135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5295143731Sdougb		      ISC_LOG_NOTICE, "running");
5296135446Strhodes}
5297135446Strhodes
5298186462Sdougbvoid
5299135446Strhodesns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) {
5300135446Strhodes
5301135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5302135446Strhodes
5303135446Strhodes	server->flushonshutdown = flush;
5304135446Strhodes}
5305135446Strhodes
5306135446Strhodesstatic void
5307135446Strhodesshutdown_server(isc_task_t *task, isc_event_t *event) {
5308135446Strhodes	isc_result_t result;
5309135446Strhodes	dns_view_t *view, *view_next;
5310135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
5311135446Strhodes	isc_boolean_t flush = server->flushonshutdown;
5312224092Sdougb	ns_cache_t *nsc;
5313135446Strhodes
5314135446Strhodes	UNUSED(task);
5315135446Strhodes	INSIST(task == server->task);
5316135446Strhodes
5317135446Strhodes	result = isc_task_beginexclusive(server->task);
5318135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5319135446Strhodes
5320135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5321135446Strhodes		      ISC_LOG_INFO, "shutting down%s",
5322135446Strhodes		      flush ? ": flushing changes" : "");
5323135446Strhodes
5324193149Sdougb	ns_statschannels_shutdown(server);
5325135446Strhodes	ns_controls_shutdown(server->controls);
5326135446Strhodes	end_reserved_dispatches(server, ISC_TRUE);
5327224092Sdougb	cleanup_session_key(server, server->mctx);
5328135446Strhodes
5329225361Sdougb	if (ns_g_aclconfctx != NULL)
5330225361Sdougb		cfg_aclconfctx_detach(&ns_g_aclconfctx);
5331225361Sdougb
5332135446Strhodes	cfg_obj_destroy(ns_g_parser, &ns_g_config);
5333135446Strhodes	cfg_parser_destroy(&ns_g_parser);
5334135446Strhodes
5335135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5336135446Strhodes	     view != NULL;
5337135446Strhodes	     view = view_next) {
5338135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
5339135446Strhodes		ISC_LIST_UNLINK(server->viewlist, view, link);
5340135446Strhodes		if (flush)
5341135446Strhodes			dns_view_flushanddetach(&view);
5342135446Strhodes		else
5343135446Strhodes			dns_view_detach(&view);
5344135446Strhodes	}
5345135446Strhodes
5346224092Sdougb	while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) {
5347224092Sdougb		ISC_LIST_UNLINK(server->cachelist, nsc, link);
5348224092Sdougb		dns_cache_detach(&nsc->cache);
5349224092Sdougb		isc_mem_put(server->mctx, nsc, sizeof(*nsc));
5350224092Sdougb	}
5351224092Sdougb
5352135446Strhodes	isc_timer_detach(&server->interface_timer);
5353135446Strhodes	isc_timer_detach(&server->heartbeat_timer);
5354170222Sdougb	isc_timer_detach(&server->pps_timer);
5355135446Strhodes
5356135446Strhodes	ns_interfacemgr_shutdown(server->interfacemgr);
5357135446Strhodes	ns_interfacemgr_detach(&server->interfacemgr);
5358135446Strhodes
5359135446Strhodes	dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
5360135446Strhodes
5361135446Strhodes	dns_zonemgr_shutdown(server->zonemgr);
5362135446Strhodes
5363224092Sdougb	if (ns_g_sessionkey != NULL) {
5364224092Sdougb		dns_tsigkey_detach(&ns_g_sessionkey);
5365224092Sdougb		dns_name_free(&ns_g_sessionkeyname, server->mctx);
5366224092Sdougb	}
5367224092Sdougb
5368135446Strhodes	if (server->blackholeacl != NULL)
5369135446Strhodes		dns_acl_detach(&server->blackholeacl);
5370135446Strhodes
5371135446Strhodes	dns_db_detach(&server->in_roothints);
5372135446Strhodes
5373135446Strhodes	isc_task_endexclusive(server->task);
5374135446Strhodes
5375135446Strhodes	isc_task_detach(&server->task);
5376135446Strhodes
5377135446Strhodes	isc_event_free(&event);
5378135446Strhodes}
5379135446Strhodes
5380135446Strhodesvoid
5381135446Strhodesns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
5382135446Strhodes	isc_result_t result;
5383225361Sdougb	ns_server_t *server = isc_mem_get(mctx, sizeof(*server));
5384135446Strhodes
5385135446Strhodes	if (server == NULL)
5386135446Strhodes		fatal("allocating server object", ISC_R_NOMEMORY);
5387135446Strhodes
5388135446Strhodes	server->mctx = mctx;
5389135446Strhodes	server->task = NULL;
5390135446Strhodes
5391135446Strhodes	/* Initialize configuration data with default values. */
5392135446Strhodes
5393135446Strhodes	result = isc_quota_init(&server->xfroutquota, 10);
5394135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5395135446Strhodes	result = isc_quota_init(&server->tcpquota, 10);
5396135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5397135446Strhodes	result = isc_quota_init(&server->recursionquota, 100);
5398135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5399135446Strhodes
5400135446Strhodes	result = dns_aclenv_init(mctx, &server->aclenv);
5401135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5402135446Strhodes
5403135446Strhodes	/* Initialize server data structures. */
5404135446Strhodes	server->zonemgr = NULL;
5405135446Strhodes	server->interfacemgr = NULL;
5406135446Strhodes	ISC_LIST_INIT(server->viewlist);
5407135446Strhodes	server->in_roothints = NULL;
5408135446Strhodes	server->blackholeacl = NULL;
5409135446Strhodes
5410135446Strhodes	CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
5411135446Strhodes				     &server->in_roothints),
5412135446Strhodes		   "setting up root hints");
5413135446Strhodes
5414135446Strhodes	CHECKFATAL(isc_mutex_init(&server->reload_event_lock),
5415135446Strhodes		   "initializing reload event lock");
5416135446Strhodes	server->reload_event =
5417135446Strhodes		isc_event_allocate(ns_g_mctx, server,
5418135446Strhodes				   NS_EVENT_RELOAD,
5419135446Strhodes				   ns_server_reload,
5420135446Strhodes				   server,
5421135446Strhodes				   sizeof(isc_event_t));
5422135446Strhodes	CHECKFATAL(server->reload_event == NULL ?
5423135446Strhodes		   ISC_R_NOMEMORY : ISC_R_SUCCESS,
5424135446Strhodes		   "allocating reload event");
5425135446Strhodes
5426224092Sdougb	CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy,
5427224092Sdougb				 ns_g_engine, ISC_ENTROPY_GOODONLY),
5428135446Strhodes		   "initializing DST");
5429135446Strhodes
5430135446Strhodes	server->tkeyctx = NULL;
5431135446Strhodes	CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
5432135446Strhodes				      &server->tkeyctx),
5433135446Strhodes		   "creating TKEY context");
5434135446Strhodes
5435135446Strhodes	/*
5436135446Strhodes	 * Setup the server task, which is responsible for coordinating
5437135446Strhodes	 * startup and shutdown of the server.
5438135446Strhodes	 */
5439135446Strhodes	CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
5440135446Strhodes		   "creating server task");
5441135446Strhodes	isc_task_setname(server->task, "server", server);
5442135446Strhodes	CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
5443135446Strhodes		   "isc_task_onshutdown");
5444135446Strhodes	CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),
5445135446Strhodes		   "isc_app_onrun");
5446135446Strhodes
5447135446Strhodes	server->interface_timer = NULL;
5448135446Strhodes	server->heartbeat_timer = NULL;
5449170222Sdougb	server->pps_timer = NULL;
5450186462Sdougb
5451135446Strhodes	server->interface_interval = 0;
5452135446Strhodes	server->heartbeat_interval = 0;
5453135446Strhodes
5454135446Strhodes	CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr,
5455135446Strhodes				      ns_g_socketmgr, &server->zonemgr),
5456135446Strhodes		   "dns_zonemgr_create");
5457225361Sdougb	CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000),
5458225361Sdougb		   "dns_zonemgr_setsize");
5459135446Strhodes
5460135446Strhodes	server->statsfile = isc_mem_strdup(server->mctx, "named.stats");
5461135446Strhodes	CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
5462135446Strhodes		   "isc_mem_strdup");
5463193149Sdougb	server->nsstats = NULL;
5464193149Sdougb	server->rcvquerystats = NULL;
5465193149Sdougb	server->opcodestats = NULL;
5466193149Sdougb	server->zonestats = NULL;
5467193149Sdougb	server->resolverstats = NULL;
5468193149Sdougb	server->sockstats = NULL;
5469193149Sdougb	CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats,
5470193149Sdougb				    isc_sockstatscounter_max),
5471193149Sdougb		   "isc_stats_create");
5472193149Sdougb	isc_socketmgr_setstats(ns_g_socketmgr, server->sockstats);
5473135446Strhodes
5474224092Sdougb	server->bindkeysfile = isc_mem_strdup(server->mctx, "bind.keys");
5475224092Sdougb	CHECKFATAL(server->bindkeysfile == NULL ? ISC_R_NOMEMORY :
5476224092Sdougb						  ISC_R_SUCCESS,
5477224092Sdougb		   "isc_mem_strdup");
5478224092Sdougb
5479135446Strhodes	server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db");
5480135446Strhodes	CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
5481135446Strhodes		   "isc_mem_strdup");
5482135446Strhodes
5483224092Sdougb	server->secrootsfile = isc_mem_strdup(server->mctx, "named.secroots");
5484224092Sdougb	CHECKFATAL(server->secrootsfile == NULL ? ISC_R_NOMEMORY :
5485224092Sdougb						  ISC_R_SUCCESS,
5486224092Sdougb		   "isc_mem_strdup");
5487224092Sdougb
5488135446Strhodes	server->recfile = isc_mem_strdup(server->mctx, "named.recursing");
5489135446Strhodes	CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
5490135446Strhodes		   "isc_mem_strdup");
5491135446Strhodes
5492135446Strhodes	server->hostname_set = ISC_FALSE;
5493135446Strhodes	server->hostname = NULL;
5494186462Sdougb	server->version_set = ISC_FALSE;
5495135446Strhodes	server->version = NULL;
5496135446Strhodes	server->server_usehostname = ISC_FALSE;
5497135446Strhodes	server->server_id = NULL;
5498135446Strhodes
5499193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->nsstats,
5500193149Sdougb				    dns_nsstatscounter_max),
5501193149Sdougb		   "dns_stats_create (server)");
5502135446Strhodes
5503193149Sdougb	CHECKFATAL(dns_rdatatypestats_create(ns_g_mctx,
5504193149Sdougb					     &server->rcvquerystats),
5505193149Sdougb		   "dns_stats_create (rcvquery)");
5506193149Sdougb
5507193149Sdougb	CHECKFATAL(dns_opcodestats_create(ns_g_mctx, &server->opcodestats),
5508193149Sdougb		   "dns_stats_create (opcode)");
5509193149Sdougb
5510193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->zonestats,
5511193149Sdougb				    dns_zonestatscounter_max),
5512193149Sdougb		   "dns_stats_create (zone)");
5513193149Sdougb
5514193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->resolverstats,
5515193149Sdougb				    dns_resstatscounter_max),
5516193149Sdougb		   "dns_stats_create (resolver)");
5517193149Sdougb
5518135446Strhodes	server->flushonshutdown = ISC_FALSE;
5519135446Strhodes	server->log_queries = ISC_FALSE;
5520135446Strhodes
5521135446Strhodes	server->controls = NULL;
5522135446Strhodes	CHECKFATAL(ns_controls_create(server, &server->controls),
5523135446Strhodes		   "ns_controls_create");
5524135446Strhodes	server->dispatchgen = 0;
5525135446Strhodes	ISC_LIST_INIT(server->dispatches);
5526135446Strhodes
5527193149Sdougb	ISC_LIST_INIT(server->statschannels);
5528193149Sdougb
5529224092Sdougb	ISC_LIST_INIT(server->cachelist);
5530224092Sdougb
5531224092Sdougb	server->sessionkey = NULL;
5532224092Sdougb	server->session_keyfile = NULL;
5533224092Sdougb	server->session_keyname = NULL;
5534224092Sdougb	server->session_keyalg = DST_ALG_UNKNOWN;
5535224092Sdougb	server->session_keybits = 0;
5536224092Sdougb
5537135446Strhodes	server->magic = NS_SERVER_MAGIC;
5538135446Strhodes	*serverp = server;
5539135446Strhodes}
5540135446Strhodes
5541135446Strhodesvoid
5542135446Strhodesns_server_destroy(ns_server_t **serverp) {
5543135446Strhodes	ns_server_t *server = *serverp;
5544135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5545135446Strhodes
5546135446Strhodes	ns_controls_destroy(&server->controls);
5547135446Strhodes
5548193149Sdougb	isc_stats_detach(&server->nsstats);
5549193149Sdougb	dns_stats_detach(&server->rcvquerystats);
5550193149Sdougb	dns_stats_detach(&server->opcodestats);
5551193149Sdougb	isc_stats_detach(&server->zonestats);
5552193149Sdougb	isc_stats_detach(&server->resolverstats);
5553193149Sdougb	isc_stats_detach(&server->sockstats);
5554135446Strhodes
5555135446Strhodes	isc_mem_free(server->mctx, server->statsfile);
5556224092Sdougb	isc_mem_free(server->mctx, server->bindkeysfile);
5557135446Strhodes	isc_mem_free(server->mctx, server->dumpfile);
5558224092Sdougb	isc_mem_free(server->mctx, server->secrootsfile);
5559135446Strhodes	isc_mem_free(server->mctx, server->recfile);
5560135446Strhodes
5561135446Strhodes	if (server->version != NULL)
5562135446Strhodes		isc_mem_free(server->mctx, server->version);
5563135446Strhodes	if (server->hostname != NULL)
5564135446Strhodes		isc_mem_free(server->mctx, server->hostname);
5565135446Strhodes	if (server->server_id != NULL)
5566135446Strhodes		isc_mem_free(server->mctx, server->server_id);
5567135446Strhodes
5568225361Sdougb	if (server->zonemgr != NULL)
5569225361Sdougb		dns_zonemgr_detach(&server->zonemgr);
5570135446Strhodes
5571135446Strhodes	if (server->tkeyctx != NULL)
5572135446Strhodes		dns_tkeyctx_destroy(&server->tkeyctx);
5573135446Strhodes
5574135446Strhodes	dst_lib_destroy();
5575135446Strhodes
5576135446Strhodes	isc_event_free(&server->reload_event);
5577135446Strhodes
5578135446Strhodes	INSIST(ISC_LIST_EMPTY(server->viewlist));
5579224092Sdougb	INSIST(ISC_LIST_EMPTY(server->cachelist));
5580135446Strhodes
5581135446Strhodes	dns_aclenv_destroy(&server->aclenv);
5582135446Strhodes
5583135446Strhodes	isc_quota_destroy(&server->recursionquota);
5584135446Strhodes	isc_quota_destroy(&server->tcpquota);
5585135446Strhodes	isc_quota_destroy(&server->xfroutquota);
5586135446Strhodes
5587135446Strhodes	server->magic = 0;
5588135446Strhodes	isc_mem_put(server->mctx, server, sizeof(*server));
5589135446Strhodes	*serverp = NULL;
5590135446Strhodes}
5591135446Strhodes
5592135446Strhodesstatic void
5593135446Strhodesfatal(const char *msg, isc_result_t result) {
5594135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5595135446Strhodes		      ISC_LOG_CRITICAL, "%s: %s", msg,
5596135446Strhodes		      isc_result_totext(result));
5597135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5598135446Strhodes		      ISC_LOG_CRITICAL, "exiting (due to fatal error)");
5599135446Strhodes	exit(1);
5600135446Strhodes}
5601135446Strhodes
5602135446Strhodesstatic void
5603135446Strhodesstart_reserved_dispatches(ns_server_t *server) {
5604135446Strhodes
5605135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5606135446Strhodes
5607135446Strhodes	server->dispatchgen++;
5608135446Strhodes}
5609135446Strhodes
5610135446Strhodesstatic void
5611135446Strhodesend_reserved_dispatches(ns_server_t *server, isc_boolean_t all) {
5612135446Strhodes	ns_dispatch_t *dispatch, *nextdispatch;
5613135446Strhodes
5614135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5615135446Strhodes
5616135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
5617135446Strhodes	     dispatch != NULL;
5618135446Strhodes	     dispatch = nextdispatch) {
5619135446Strhodes		nextdispatch = ISC_LIST_NEXT(dispatch, link);
5620135446Strhodes		if (!all && server->dispatchgen == dispatch-> dispatchgen)
5621135446Strhodes			continue;
5622135446Strhodes		ISC_LIST_UNLINK(server->dispatches, dispatch, link);
5623135446Strhodes		dns_dispatch_detach(&dispatch->dispatch);
5624135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
5625135446Strhodes	}
5626135446Strhodes}
5627135446Strhodes
5628135446Strhodesvoid
5629165071Sdougbns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) {
5630135446Strhodes	ns_dispatch_t *dispatch;
5631135446Strhodes	in_port_t port;
5632135446Strhodes	char addrbuf[ISC_SOCKADDR_FORMATSIZE];
5633135446Strhodes	isc_result_t result;
5634135446Strhodes	unsigned int attrs, attrmask;
5635135446Strhodes
5636135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
5637135446Strhodes
5638135446Strhodes	port = isc_sockaddr_getport(addr);
5639135446Strhodes	if (port == 0 || port >= 1024)
5640135446Strhodes		return;
5641135446Strhodes
5642135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
5643135446Strhodes	     dispatch != NULL;
5644135446Strhodes	     dispatch = ISC_LIST_NEXT(dispatch, link)) {
5645135446Strhodes		if (isc_sockaddr_equal(&dispatch->addr, addr))
5646135446Strhodes			break;
5647135446Strhodes	}
5648135446Strhodes	if (dispatch != NULL) {
5649135446Strhodes		dispatch->dispatchgen = server->dispatchgen;
5650135446Strhodes		return;
5651135446Strhodes	}
5652135446Strhodes
5653135446Strhodes	dispatch = isc_mem_get(server->mctx, sizeof(*dispatch));
5654135446Strhodes	if (dispatch == NULL) {
5655135446Strhodes		result = ISC_R_NOMEMORY;
5656135446Strhodes		goto cleanup;
5657135446Strhodes	}
5658135446Strhodes
5659135446Strhodes	dispatch->addr = *addr;
5660135446Strhodes	dispatch->dispatchgen = server->dispatchgen;
5661135446Strhodes	dispatch->dispatch = NULL;
5662135446Strhodes
5663135446Strhodes	attrs = 0;
5664135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
5665135446Strhodes	switch (isc_sockaddr_pf(addr)) {
5666135446Strhodes	case AF_INET:
5667135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
5668135446Strhodes		break;
5669135446Strhodes	case AF_INET6:
5670135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
5671135446Strhodes		break;
5672135446Strhodes	default:
5673135446Strhodes		result = ISC_R_NOTIMPLEMENTED;
5674135446Strhodes		goto cleanup;
5675135446Strhodes	}
5676135446Strhodes	attrmask = 0;
5677135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP;
5678135446Strhodes	attrmask |= DNS_DISPATCHATTR_TCP;
5679135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4;
5680135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV6;
5681135446Strhodes
5682135446Strhodes	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
5683135446Strhodes				     ns_g_taskmgr, &dispatch->addr, 4096,
5684135446Strhodes				     1000, 32768, 16411, 16433,
5685186462Sdougb				     attrs, attrmask, &dispatch->dispatch);
5686135446Strhodes	if (result != ISC_R_SUCCESS)
5687135446Strhodes		goto cleanup;
5688135446Strhodes
5689135446Strhodes	ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link);
5690135446Strhodes
5691135446Strhodes	return;
5692135446Strhodes
5693135446Strhodes cleanup:
5694135446Strhodes	if (dispatch != NULL)
5695135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
5696135446Strhodes	isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
5697135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5698135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
5699135446Strhodes		      "unable to create dispatch for reserved port %s: %s",
5700135446Strhodes		      addrbuf, isc_result_totext(result));
5701135446Strhodes}
5702135446Strhodes
5703135446Strhodes
5704135446Strhodesstatic isc_result_t
5705135446Strhodesloadconfig(ns_server_t *server) {
5706135446Strhodes	isc_result_t result;
5707135446Strhodes	start_reserved_dispatches(server);
5708135446Strhodes	result = load_configuration(ns_g_lwresdonly ?
5709135446Strhodes				    lwresd_g_conffile : ns_g_conffile,
5710143731Sdougb				    server, ISC_FALSE);
5711193149Sdougb	if (result == ISC_R_SUCCESS) {
5712135446Strhodes		end_reserved_dispatches(server, ISC_FALSE);
5713135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5714193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5715193149Sdougb			      "reloading configuration succeeded");
5716193149Sdougb	} else {
5717193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5718135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
5719135446Strhodes			      "reloading configuration failed: %s",
5720135446Strhodes			      isc_result_totext(result));
5721193149Sdougb	}
5722135446Strhodes	return (result);
5723135446Strhodes}
5724135446Strhodes
5725135446Strhodesstatic isc_result_t
5726135446Strhodesreload(ns_server_t *server) {
5727135446Strhodes	isc_result_t result;
5728135446Strhodes	CHECK(loadconfig(server));
5729135446Strhodes
5730135446Strhodes	result = load_zones(server, ISC_FALSE);
5731193149Sdougb	if (result == ISC_R_SUCCESS)
5732135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5733193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5734193149Sdougb			      "reloading zones succeeded");
5735193149Sdougb	else
5736193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5737135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
5738135446Strhodes			      "reloading zones failed: %s",
5739135446Strhodes			      isc_result_totext(result));
5740193149Sdougb
5741135446Strhodes cleanup:
5742135446Strhodes	return (result);
5743135446Strhodes}
5744135446Strhodes
5745135446Strhodesstatic void
5746135446Strhodesreconfig(ns_server_t *server) {
5747135446Strhodes	isc_result_t result;
5748135446Strhodes	CHECK(loadconfig(server));
5749135446Strhodes
5750135446Strhodes	result = load_new_zones(server, ISC_FALSE);
5751193149Sdougb	if (result == ISC_R_SUCCESS)
5752135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5753193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5754193149Sdougb			      "any newly configured zones are now loaded");
5755193149Sdougb	else
5756193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5757135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
5758135446Strhodes			      "loading new zones failed: %s",
5759135446Strhodes			      isc_result_totext(result));
5760193149Sdougb
5761135446Strhodes cleanup: ;
5762135446Strhodes}
5763135446Strhodes
5764135446Strhodes/*
5765135446Strhodes * Handle a reload event (from SIGHUP).
5766135446Strhodes */
5767135446Strhodesstatic void
5768135446Strhodesns_server_reload(isc_task_t *task, isc_event_t *event) {
5769135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
5770135446Strhodes
5771135446Strhodes	INSIST(task = server->task);
5772135446Strhodes	UNUSED(task);
5773135446Strhodes
5774193149Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5775193149Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5776193149Sdougb		      "received SIGHUP signal to reload zones");
5777135446Strhodes	(void)reload(server);
5778135446Strhodes
5779135446Strhodes	LOCK(&server->reload_event_lock);
5780135446Strhodes	INSIST(server->reload_event == NULL);
5781135446Strhodes	server->reload_event = event;
5782135446Strhodes	UNLOCK(&server->reload_event_lock);
5783135446Strhodes}
5784135446Strhodes
5785135446Strhodesvoid
5786135446Strhodesns_server_reloadwanted(ns_server_t *server) {
5787135446Strhodes	LOCK(&server->reload_event_lock);
5788135446Strhodes	if (server->reload_event != NULL)
5789135446Strhodes		isc_task_send(server->task, &server->reload_event);
5790135446Strhodes	UNLOCK(&server->reload_event_lock);
5791135446Strhodes}
5792135446Strhodes
5793135446Strhodesstatic char *
5794135446Strhodesnext_token(char **stringp, const char *delim) {
5795135446Strhodes	char *res;
5796135446Strhodes
5797135446Strhodes	do {
5798135446Strhodes		res = strsep(stringp, delim);
5799135446Strhodes		if (res == NULL)
5800135446Strhodes			break;
5801135446Strhodes	} while (*res == '\0');
5802135446Strhodes	return (res);
5803186462Sdougb}
5804135446Strhodes
5805135446Strhodes/*
5806135446Strhodes * Find the zone specified in the control channel command 'args',
5807135446Strhodes * if any.  If a zone is specified, point '*zonep' at it, otherwise
5808135446Strhodes * set '*zonep' to NULL.
5809135446Strhodes */
5810135446Strhodesstatic isc_result_t
5811224092Sdougbzone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep,
5812224092Sdougb	       const char **zonename)
5813224092Sdougb{
5814135446Strhodes	char *input, *ptr;
5815135446Strhodes	const char *zonetxt;
5816135446Strhodes	char *classtxt;
5817135446Strhodes	const char *viewtxt = NULL;
5818135446Strhodes	dns_fixedname_t name;
5819135446Strhodes	isc_result_t result;
5820135446Strhodes	isc_buffer_t buf;
5821135446Strhodes	dns_view_t *view = NULL;
5822135446Strhodes	dns_rdataclass_t rdclass;
5823135446Strhodes
5824135446Strhodes	REQUIRE(zonep != NULL && *zonep == NULL);
5825135446Strhodes
5826135446Strhodes	input = args;
5827135446Strhodes
5828135446Strhodes	/* Skip the command name. */
5829135446Strhodes	ptr = next_token(&input, " \t");
5830135446Strhodes	if (ptr == NULL)
5831135446Strhodes		return (ISC_R_UNEXPECTEDEND);
5832135446Strhodes
5833135446Strhodes	/* Look for the zone name. */
5834135446Strhodes	zonetxt = next_token(&input, " \t");
5835135446Strhodes	if (zonetxt == NULL)
5836135446Strhodes		return (ISC_R_SUCCESS);
5837224092Sdougb	if (zonename)
5838224092Sdougb		*zonename = zonetxt;
5839135446Strhodes
5840135446Strhodes	/* Look for the optional class name. */
5841135446Strhodes	classtxt = next_token(&input, " \t");
5842135446Strhodes	if (classtxt != NULL) {
5843135446Strhodes		/* Look for the optional view name. */
5844135446Strhodes		viewtxt = next_token(&input, " \t");
5845135446Strhodes	}
5846135446Strhodes
5847135446Strhodes	isc_buffer_init(&buf, zonetxt, strlen(zonetxt));
5848135446Strhodes	isc_buffer_add(&buf, strlen(zonetxt));
5849135446Strhodes	dns_fixedname_init(&name);
5850135446Strhodes	result = dns_name_fromtext(dns_fixedname_name(&name),
5851224092Sdougb				   &buf, dns_rootname, 0, NULL);
5852135446Strhodes	if (result != ISC_R_SUCCESS)
5853135446Strhodes		goto fail1;
5854135446Strhodes
5855135446Strhodes	if (classtxt != NULL) {
5856135446Strhodes		isc_textregion_t r;
5857135446Strhodes		r.base = classtxt;
5858135446Strhodes		r.length = strlen(classtxt);
5859135446Strhodes		result = dns_rdataclass_fromtext(&rdclass, &r);
5860135446Strhodes		if (result != ISC_R_SUCCESS)
5861135446Strhodes			goto fail1;
5862193149Sdougb	} else
5863193149Sdougb		rdclass = dns_rdataclass_in;
5864193149Sdougb
5865193149Sdougb	if (viewtxt == NULL) {
5866193149Sdougb		result = dns_viewlist_findzone(&server->viewlist,
5867193149Sdougb					       dns_fixedname_name(&name),
5868193149Sdougb					       ISC_TF(classtxt == NULL),
5869193149Sdougb					       rdclass, zonep);
5870135446Strhodes	} else {
5871193149Sdougb		result = dns_viewlist_find(&server->viewlist, viewtxt,
5872193149Sdougb					   rdclass, &view);
5873193149Sdougb		if (result != ISC_R_SUCCESS)
5874193149Sdougb			goto fail1;
5875193149Sdougb
5876193149Sdougb		result = dns_zt_find(view->zonetable, dns_fixedname_name(&name),
5877193149Sdougb				     0, NULL, zonep);
5878193149Sdougb		dns_view_detach(&view);
5879135446Strhodes	}
5880186462Sdougb
5881135446Strhodes	/* Partial match? */
5882135446Strhodes	if (result != ISC_R_SUCCESS && *zonep != NULL)
5883135446Strhodes		dns_zone_detach(zonep);
5884204619Sdougb	if (result == DNS_R_PARTIALMATCH)
5885204619Sdougb		result = ISC_R_NOTFOUND;
5886135446Strhodes fail1:
5887135446Strhodes	return (result);
5888135446Strhodes}
5889135446Strhodes
5890135446Strhodes/*
5891135446Strhodes * Act on a "retransfer" command from the command channel.
5892135446Strhodes */
5893135446Strhodesisc_result_t
5894135446Strhodesns_server_retransfercommand(ns_server_t *server, char *args) {
5895135446Strhodes	isc_result_t result;
5896135446Strhodes	dns_zone_t *zone = NULL;
5897135446Strhodes	dns_zonetype_t type;
5898186462Sdougb
5899224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
5900135446Strhodes	if (result != ISC_R_SUCCESS)
5901135446Strhodes		return (result);
5902135446Strhodes	if (zone == NULL)
5903135446Strhodes		return (ISC_R_UNEXPECTEDEND);
5904135446Strhodes	type = dns_zone_gettype(zone);
5905135446Strhodes	if (type == dns_zone_slave || type == dns_zone_stub)
5906135446Strhodes		dns_zone_forcereload(zone);
5907135446Strhodes	else
5908135446Strhodes		result = ISC_R_NOTFOUND;
5909135446Strhodes	dns_zone_detach(&zone);
5910135446Strhodes	return (result);
5911186462Sdougb}
5912135446Strhodes
5913135446Strhodes/*
5914135446Strhodes * Act on a "reload" command from the command channel.
5915135446Strhodes */
5916135446Strhodesisc_result_t
5917135446Strhodesns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
5918135446Strhodes	isc_result_t result;
5919135446Strhodes	dns_zone_t *zone = NULL;
5920135446Strhodes	dns_zonetype_t type;
5921135446Strhodes	const char *msg = NULL;
5922186462Sdougb
5923224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
5924135446Strhodes	if (result != ISC_R_SUCCESS)
5925135446Strhodes		return (result);
5926135446Strhodes	if (zone == NULL) {
5927135446Strhodes		result = reload(server);
5928135446Strhodes		if (result == ISC_R_SUCCESS)
5929135446Strhodes			msg = "server reload successful";
5930135446Strhodes	} else {
5931135446Strhodes		type = dns_zone_gettype(zone);
5932135446Strhodes		if (type == dns_zone_slave || type == dns_zone_stub) {
5933135446Strhodes			dns_zone_refresh(zone);
5934174187Sdougb			dns_zone_detach(&zone);
5935135446Strhodes			msg = "zone refresh queued";
5936135446Strhodes		} else {
5937135446Strhodes			result = dns_zone_load(zone);
5938135446Strhodes			dns_zone_detach(&zone);
5939186462Sdougb			switch (result) {
5940135446Strhodes			case ISC_R_SUCCESS:
5941135446Strhodes				 msg = "zone reload successful";
5942135446Strhodes				 break;
5943135446Strhodes			case DNS_R_CONTINUE:
5944135446Strhodes				msg = "zone reload queued";
5945135446Strhodes				result = ISC_R_SUCCESS;
5946135446Strhodes				break;
5947135446Strhodes			case DNS_R_UPTODATE:
5948135446Strhodes				msg = "zone reload up-to-date";
5949135446Strhodes				result = ISC_R_SUCCESS;
5950135446Strhodes				break;
5951135446Strhodes			default:
5952135446Strhodes				/* failure message will be generated by rndc */
5953135446Strhodes				break;
5954135446Strhodes			}
5955135446Strhodes		}
5956135446Strhodes	}
5957135446Strhodes	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
5958135446Strhodes		isc_buffer_putmem(text, (const unsigned char *)msg,
5959135446Strhodes				  strlen(msg) + 1);
5960135446Strhodes	return (result);
5961186462Sdougb}
5962135446Strhodes
5963135446Strhodes/*
5964135446Strhodes * Act on a "reconfig" command from the command channel.
5965135446Strhodes */
5966135446Strhodesisc_result_t
5967135446Strhodesns_server_reconfigcommand(ns_server_t *server, char *args) {
5968135446Strhodes	UNUSED(args);
5969135446Strhodes
5970135446Strhodes	reconfig(server);
5971135446Strhodes	return (ISC_R_SUCCESS);
5972135446Strhodes}
5973135446Strhodes
5974135446Strhodes/*
5975170222Sdougb * Act on a "notify" command from the command channel.
5976170222Sdougb */
5977170222Sdougbisc_result_t
5978170222Sdougbns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) {
5979170222Sdougb	isc_result_t result;
5980170222Sdougb	dns_zone_t *zone = NULL;
5981170222Sdougb	const unsigned char msg[] = "zone notify queued";
5982170222Sdougb
5983224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
5984170222Sdougb	if (result != ISC_R_SUCCESS)
5985170222Sdougb		return (result);
5986170222Sdougb	if (zone == NULL)
5987170222Sdougb		return (ISC_R_UNEXPECTEDEND);
5988186462Sdougb
5989170222Sdougb	dns_zone_notify(zone);
5990170222Sdougb	dns_zone_detach(&zone);
5991170222Sdougb	if (sizeof(msg) <= isc_buffer_availablelength(text))
5992170222Sdougb		isc_buffer_putmem(text, msg, sizeof(msg));
5993170222Sdougb
5994170222Sdougb	return (ISC_R_SUCCESS);
5995186462Sdougb}
5996170222Sdougb
5997170222Sdougb/*
5998135446Strhodes * Act on a "refresh" command from the command channel.
5999135446Strhodes */
6000135446Strhodesisc_result_t
6001135446Strhodesns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6002135446Strhodes	isc_result_t result;
6003135446Strhodes	dns_zone_t *zone = NULL;
6004165071Sdougb	const unsigned char msg1[] = "zone refresh queued";
6005165071Sdougb	const unsigned char msg2[] = "not a slave or stub zone";
6006165071Sdougb	dns_zonetype_t type;
6007135446Strhodes
6008224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
6009135446Strhodes	if (result != ISC_R_SUCCESS)
6010135446Strhodes		return (result);
6011135446Strhodes	if (zone == NULL)
6012135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6013165071Sdougb
6014165071Sdougb	type = dns_zone_gettype(zone);
6015165071Sdougb	if (type == dns_zone_slave || type == dns_zone_stub) {
6016165071Sdougb		dns_zone_refresh(zone);
6017165071Sdougb		dns_zone_detach(&zone);
6018165071Sdougb		if (sizeof(msg1) <= isc_buffer_availablelength(text))
6019165071Sdougb			isc_buffer_putmem(text, msg1, sizeof(msg1));
6020165071Sdougb		return (ISC_R_SUCCESS);
6021165071Sdougb	}
6022186462Sdougb
6023135446Strhodes	dns_zone_detach(&zone);
6024165071Sdougb	if (sizeof(msg2) <= isc_buffer_availablelength(text))
6025165071Sdougb		isc_buffer_putmem(text, msg2, sizeof(msg2));
6026165071Sdougb	return (ISC_R_FAILURE);
6027186462Sdougb}
6028135446Strhodes
6029135446Strhodesisc_result_t
6030135446Strhodesns_server_togglequerylog(ns_server_t *server) {
6031135446Strhodes	server->log_queries = server->log_queries ? ISC_FALSE : ISC_TRUE;
6032186462Sdougb
6033135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6034135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6035135446Strhodes		      "query logging is now %s",
6036135446Strhodes		      server->log_queries ? "on" : "off");
6037135446Strhodes	return (ISC_R_SUCCESS);
6038135446Strhodes}
6039135446Strhodes
6040135446Strhodesstatic isc_result_t
6041165071Sdougbns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
6042170222Sdougb			 cfg_aclconfctx_t *actx,
6043135446Strhodes			 isc_mem_t *mctx, ns_listenlist_t **target)
6044135446Strhodes{
6045135446Strhodes	isc_result_t result;
6046165071Sdougb	const cfg_listelt_t *element;
6047135446Strhodes	ns_listenlist_t *dlist = NULL;
6048135446Strhodes
6049135446Strhodes	REQUIRE(target != NULL && *target == NULL);
6050135446Strhodes
6051135446Strhodes	result = ns_listenlist_create(mctx, &dlist);
6052135446Strhodes	if (result != ISC_R_SUCCESS)
6053135446Strhodes		return (result);
6054135446Strhodes
6055135446Strhodes	for (element = cfg_list_first(listenlist);
6056135446Strhodes	     element != NULL;
6057135446Strhodes	     element = cfg_list_next(element))
6058135446Strhodes	{
6059135446Strhodes		ns_listenelt_t *delt = NULL;
6060165071Sdougb		const cfg_obj_t *listener = cfg_listelt_value(element);
6061135446Strhodes		result = ns_listenelt_fromconfig(listener, config, actx,
6062135446Strhodes						 mctx, &delt);
6063135446Strhodes		if (result != ISC_R_SUCCESS)
6064135446Strhodes			goto cleanup;
6065135446Strhodes		ISC_LIST_APPEND(dlist->elts, delt, link);
6066135446Strhodes	}
6067135446Strhodes	*target = dlist;
6068135446Strhodes	return (ISC_R_SUCCESS);
6069135446Strhodes
6070135446Strhodes cleanup:
6071135446Strhodes	ns_listenlist_detach(&dlist);
6072135446Strhodes	return (result);
6073135446Strhodes}
6074135446Strhodes
6075135446Strhodes/*
6076135446Strhodes * Create a listen list from the corresponding configuration
6077135446Strhodes * data structure.
6078135446Strhodes */
6079135446Strhodesstatic isc_result_t
6080165071Sdougbns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
6081170222Sdougb			cfg_aclconfctx_t *actx,
6082135446Strhodes			isc_mem_t *mctx, ns_listenelt_t **target)
6083135446Strhodes{
6084135446Strhodes	isc_result_t result;
6085165071Sdougb	const cfg_obj_t *portobj;
6086135446Strhodes	in_port_t port;
6087135446Strhodes	ns_listenelt_t *delt = NULL;
6088135446Strhodes	REQUIRE(target != NULL && *target == NULL);
6089135446Strhodes
6090135446Strhodes	portobj = cfg_tuple_get(listener, "port");
6091135446Strhodes	if (!cfg_obj_isuint32(portobj)) {
6092135446Strhodes		if (ns_g_port != 0) {
6093135446Strhodes			port = ns_g_port;
6094135446Strhodes		} else {
6095135446Strhodes			result = ns_config_getport(config, &port);
6096135446Strhodes			if (result != ISC_R_SUCCESS)
6097135446Strhodes				return (result);
6098135446Strhodes		}
6099135446Strhodes	} else {
6100135446Strhodes		if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
6101135446Strhodes			cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
6102135446Strhodes				    "port value '%u' is out of range",
6103135446Strhodes				    cfg_obj_asuint32(portobj));
6104135446Strhodes			return (ISC_R_RANGE);
6105135446Strhodes		}
6106135446Strhodes		port = (in_port_t)cfg_obj_asuint32(portobj);
6107135446Strhodes	}
6108135446Strhodes
6109135446Strhodes	result = ns_listenelt_create(mctx, port, NULL, &delt);
6110135446Strhodes	if (result != ISC_R_SUCCESS)
6111135446Strhodes		return (result);
6112135446Strhodes
6113170222Sdougb	result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
6114193149Sdougb				   config, ns_g_lctx, actx, mctx, 0,
6115193149Sdougb				   &delt->acl);
6116135446Strhodes	if (result != ISC_R_SUCCESS) {
6117135446Strhodes		ns_listenelt_destroy(delt);
6118135446Strhodes		return (result);
6119135446Strhodes	}
6120135446Strhodes	*target = delt;
6121135446Strhodes	return (ISC_R_SUCCESS);
6122135446Strhodes}
6123135446Strhodes
6124135446Strhodesisc_result_t
6125135446Strhodesns_server_dumpstats(ns_server_t *server) {
6126135446Strhodes	isc_result_t result;
6127135446Strhodes	FILE *fp = NULL;
6128135446Strhodes
6129135446Strhodes	CHECKMF(isc_stdio_open(server->statsfile, "a", &fp),
6130135446Strhodes		"could not open statistics dump file", server->statsfile);
6131186462Sdougb
6132193149Sdougb	result = ns_stats_dump(server, fp);
6133186462Sdougb
6134135446Strhodes cleanup:
6135135446Strhodes	if (fp != NULL)
6136135446Strhodes		(void)isc_stdio_close(fp);
6137193149Sdougb	if (result == ISC_R_SUCCESS)
6138193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6139193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6140193149Sdougb			      "dumpstats complete");
6141193149Sdougb	else
6142193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6143193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6144193149Sdougb			      "dumpstats failed: %s",
6145193149Sdougb			      dns_result_totext(result));
6146135446Strhodes	return (result);
6147135446Strhodes}
6148135446Strhodes
6149135446Strhodesstatic isc_result_t
6150135446Strhodesadd_zone_tolist(dns_zone_t *zone, void *uap) {
6151135446Strhodes	struct dumpcontext *dctx = uap;
6152135446Strhodes	struct zonelistentry *zle;
6153135446Strhodes
6154135446Strhodes	zle = isc_mem_get(dctx->mctx, sizeof *zle);
6155135446Strhodes	if (zle ==  NULL)
6156135446Strhodes		return (ISC_R_NOMEMORY);
6157135446Strhodes	zle->zone = NULL;
6158135446Strhodes	dns_zone_attach(zone, &zle->zone);
6159135446Strhodes	ISC_LINK_INIT(zle, link);
6160135446Strhodes	ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link);
6161135446Strhodes	return (ISC_R_SUCCESS);
6162135446Strhodes}
6163135446Strhodes
6164135446Strhodesstatic isc_result_t
6165135446Strhodesadd_view_tolist(struct dumpcontext *dctx, dns_view_t *view) {
6166135446Strhodes	struct viewlistentry *vle;
6167135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
6168186462Sdougb
6169153816Sdougb	/*
6170153816Sdougb	 * Prevent duplicate views.
6171153816Sdougb	 */
6172153816Sdougb	for (vle = ISC_LIST_HEAD(dctx->viewlist);
6173153816Sdougb	     vle != NULL;
6174153816Sdougb	     vle = ISC_LIST_NEXT(vle, link))
6175153816Sdougb		if (vle->view == view)
6176153816Sdougb			return (ISC_R_SUCCESS);
6177153816Sdougb
6178135446Strhodes	vle = isc_mem_get(dctx->mctx, sizeof *vle);
6179135446Strhodes	if (vle == NULL)
6180135446Strhodes		return (ISC_R_NOMEMORY);
6181135446Strhodes	vle->view = NULL;
6182135446Strhodes	dns_view_attach(view, &vle->view);
6183135446Strhodes	ISC_LINK_INIT(vle, link);
6184135446Strhodes	ISC_LIST_INIT(vle->zonelist);
6185135446Strhodes	ISC_LIST_APPEND(dctx->viewlist, vle, link);
6186135446Strhodes	if (dctx->dumpzones)
6187135446Strhodes		result = dns_zt_apply(view->zonetable, ISC_TRUE,
6188135446Strhodes				      add_zone_tolist, dctx);
6189135446Strhodes	return (result);
6190135446Strhodes}
6191135446Strhodes
6192135446Strhodesstatic void
6193135446Strhodesdumpcontext_destroy(struct dumpcontext *dctx) {
6194135446Strhodes	struct viewlistentry *vle;
6195135446Strhodes	struct zonelistentry *zle;
6196135446Strhodes
6197135446Strhodes	vle = ISC_LIST_HEAD(dctx->viewlist);
6198135446Strhodes	while (vle != NULL) {
6199135446Strhodes		ISC_LIST_UNLINK(dctx->viewlist, vle, link);
6200135446Strhodes		zle = ISC_LIST_HEAD(vle->zonelist);
6201135446Strhodes		while (zle != NULL) {
6202135446Strhodes			ISC_LIST_UNLINK(vle->zonelist, zle, link);
6203135446Strhodes			dns_zone_detach(&zle->zone);
6204135446Strhodes			isc_mem_put(dctx->mctx, zle, sizeof *zle);
6205135446Strhodes			zle = ISC_LIST_HEAD(vle->zonelist);
6206135446Strhodes		}
6207135446Strhodes		dns_view_detach(&vle->view);
6208135446Strhodes		isc_mem_put(dctx->mctx, vle, sizeof *vle);
6209135446Strhodes		vle = ISC_LIST_HEAD(dctx->viewlist);
6210135446Strhodes	}
6211135446Strhodes	if (dctx->version != NULL)
6212135446Strhodes		dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
6213135446Strhodes	if (dctx->db != NULL)
6214135446Strhodes		dns_db_detach(&dctx->db);
6215135446Strhodes	if (dctx->cache != NULL)
6216135446Strhodes		dns_db_detach(&dctx->cache);
6217135446Strhodes	if (dctx->task != NULL)
6218135446Strhodes		isc_task_detach(&dctx->task);
6219135446Strhodes	if (dctx->fp != NULL)
6220135446Strhodes		(void)isc_stdio_close(dctx->fp);
6221135446Strhodes	if (dctx->mdctx != NULL)
6222135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
6223135446Strhodes	isc_mem_put(dctx->mctx, dctx, sizeof *dctx);
6224135446Strhodes}
6225135446Strhodes
6226135446Strhodesstatic void
6227135446Strhodesdumpdone(void *arg, isc_result_t result) {
6228135446Strhodes	struct dumpcontext *dctx = arg;
6229135446Strhodes	char buf[1024+32];
6230135446Strhodes	const dns_master_style_t *style;
6231186462Sdougb
6232135446Strhodes	if (result != ISC_R_SUCCESS)
6233135446Strhodes		goto cleanup;
6234135446Strhodes	if (dctx->mdctx != NULL)
6235135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
6236135446Strhodes	if (dctx->view == NULL) {
6237135446Strhodes		dctx->view = ISC_LIST_HEAD(dctx->viewlist);
6238135446Strhodes		if (dctx->view == NULL)
6239135446Strhodes			goto done;
6240135446Strhodes		INSIST(dctx->zone == NULL);
6241153816Sdougb	} else
6242153816Sdougb		goto resume;
6243135446Strhodes nextview:
6244135446Strhodes	fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
6245153816Sdougb resume:
6246224092Sdougb	if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) {
6247224092Sdougb		fprintf(dctx->fp,
6248224092Sdougb			";\n; Cache of view '%s' is shared as '%s'\n",
6249224092Sdougb			dctx->view->view->name,
6250224092Sdougb			dns_cache_getname(dctx->view->view->cache));
6251224092Sdougb	} else if (dctx->zone == NULL && dctx->cache == NULL &&
6252224092Sdougb		   dctx->dumpcache)
6253224092Sdougb	{
6254135446Strhodes		style = &dns_master_style_cache;
6255135446Strhodes		/* start cache dump */
6256135446Strhodes		if (dctx->view->view->cachedb != NULL)
6257135446Strhodes			dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
6258135446Strhodes		if (dctx->cache != NULL) {
6259224092Sdougb			fprintf(dctx->fp,
6260224092Sdougb				";\n; Cache dump of view '%s' (cache %s)\n;\n",
6261224092Sdougb				dctx->view->view->name,
6262224092Sdougb				dns_cache_getname(dctx->view->view->cache));
6263135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
6264135446Strhodes							    dctx->cache, NULL,
6265135446Strhodes							    style, dctx->fp,
6266135446Strhodes							    dctx->task,
6267135446Strhodes							    dumpdone, dctx,
6268135446Strhodes							    &dctx->mdctx);
6269135446Strhodes			if (result == DNS_R_CONTINUE)
6270135446Strhodes				return;
6271135446Strhodes			if (result == ISC_R_NOTIMPLEMENTED)
6272135446Strhodes				fprintf(dctx->fp, "; %s\n",
6273135446Strhodes					dns_result_totext(result));
6274135446Strhodes			else if (result != ISC_R_SUCCESS)
6275135446Strhodes				goto cleanup;
6276135446Strhodes		}
6277135446Strhodes	}
6278135446Strhodes	if (dctx->cache != NULL) {
6279135446Strhodes		dns_adb_dump(dctx->view->view->adb, dctx->fp);
6280205292Sdougb		dns_resolver_printbadcache(dctx->view->view->resolver,
6281205292Sdougb					   dctx->fp);
6282135446Strhodes		dns_db_detach(&dctx->cache);
6283135446Strhodes	}
6284135446Strhodes	if (dctx->dumpzones) {
6285135446Strhodes		style = &dns_master_style_full;
6286135446Strhodes nextzone:
6287135446Strhodes		if (dctx->version != NULL)
6288135446Strhodes			dns_db_closeversion(dctx->db, &dctx->version,
6289135446Strhodes					    ISC_FALSE);
6290135446Strhodes		if (dctx->db != NULL)
6291135446Strhodes			dns_db_detach(&dctx->db);
6292135446Strhodes		if (dctx->zone == NULL)
6293135446Strhodes			dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist);
6294135446Strhodes		else
6295135446Strhodes			dctx->zone = ISC_LIST_NEXT(dctx->zone, link);
6296135446Strhodes		if (dctx->zone != NULL) {
6297135446Strhodes			/* start zone dump */
6298135446Strhodes			dns_zone_name(dctx->zone->zone, buf, sizeof(buf));
6299135446Strhodes			fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf);
6300135446Strhodes			result = dns_zone_getdb(dctx->zone->zone, &dctx->db);
6301135446Strhodes			if (result != ISC_R_SUCCESS) {
6302135446Strhodes				fprintf(dctx->fp, "; %s\n",
6303135446Strhodes					dns_result_totext(result));
6304135446Strhodes				goto nextzone;
6305135446Strhodes			}
6306135446Strhodes			dns_db_currentversion(dctx->db, &dctx->version);
6307135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
6308135446Strhodes							    dctx->db,
6309135446Strhodes							    dctx->version,
6310135446Strhodes							    style, dctx->fp,
6311135446Strhodes							    dctx->task,
6312135446Strhodes							    dumpdone, dctx,
6313135446Strhodes							    &dctx->mdctx);
6314135446Strhodes			if (result == DNS_R_CONTINUE)
6315135446Strhodes				return;
6316153816Sdougb			if (result == ISC_R_NOTIMPLEMENTED) {
6317135446Strhodes				fprintf(dctx->fp, "; %s\n",
6318135446Strhodes					dns_result_totext(result));
6319153816Sdougb				result = ISC_R_SUCCESS;
6320225361Sdougb				POST(result);
6321153816Sdougb				goto nextzone;
6322153816Sdougb			}
6323135446Strhodes			if (result != ISC_R_SUCCESS)
6324135446Strhodes				goto cleanup;
6325135446Strhodes		}
6326135446Strhodes	}
6327135446Strhodes	if (dctx->view != NULL)
6328135446Strhodes		dctx->view = ISC_LIST_NEXT(dctx->view, link);
6329135446Strhodes	if (dctx->view != NULL)
6330135446Strhodes		goto nextview;
6331135446Strhodes done:
6332135446Strhodes	fprintf(dctx->fp, "; Dump complete\n");
6333135446Strhodes	result = isc_stdio_flush(dctx->fp);
6334135446Strhodes	if (result == ISC_R_SUCCESS)
6335135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6336135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6337135446Strhodes			      "dumpdb complete");
6338135446Strhodes cleanup:
6339135446Strhodes	if (result != ISC_R_SUCCESS)
6340135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6341193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6342135446Strhodes			      "dumpdb failed: %s", dns_result_totext(result));
6343135446Strhodes	dumpcontext_destroy(dctx);
6344135446Strhodes}
6345135446Strhodes
6346135446Strhodesisc_result_t
6347135446Strhodesns_server_dumpdb(ns_server_t *server, char *args) {
6348135446Strhodes	struct dumpcontext *dctx = NULL;
6349135446Strhodes	dns_view_t *view;
6350135446Strhodes	isc_result_t result;
6351135446Strhodes	char *ptr;
6352135446Strhodes	const char *sep;
6353135446Strhodes
6354165071Sdougb	/* Skip the command name. */
6355165071Sdougb	ptr = next_token(&args, " \t");
6356165071Sdougb	if (ptr == NULL)
6357165071Sdougb		return (ISC_R_UNEXPECTEDEND);
6358165071Sdougb
6359135446Strhodes	dctx = isc_mem_get(server->mctx, sizeof(*dctx));
6360135446Strhodes	if (dctx == NULL)
6361135446Strhodes		return (ISC_R_NOMEMORY);
6362135446Strhodes
6363135446Strhodes	dctx->mctx = server->mctx;
6364135446Strhodes	dctx->dumpcache = ISC_TRUE;
6365135446Strhodes	dctx->dumpzones = ISC_FALSE;
6366135446Strhodes	dctx->fp = NULL;
6367135446Strhodes	ISC_LIST_INIT(dctx->viewlist);
6368135446Strhodes	dctx->view = NULL;
6369135446Strhodes	dctx->zone = NULL;
6370135446Strhodes	dctx->cache = NULL;
6371135446Strhodes	dctx->mdctx = NULL;
6372135446Strhodes	dctx->db = NULL;
6373135446Strhodes	dctx->cache = NULL;
6374135446Strhodes	dctx->task = NULL;
6375135446Strhodes	dctx->version = NULL;
6376135446Strhodes	isc_task_attach(server->task, &dctx->task);
6377135446Strhodes
6378135446Strhodes	CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp),
6379135446Strhodes		"could not open dump file", server->dumpfile);
6380135446Strhodes
6381135446Strhodes	sep = (args == NULL) ? "" : ": ";
6382135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6383135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6384135446Strhodes		      "dumpdb started%s%s", sep, (args != NULL) ? args : "");
6385135446Strhodes
6386135446Strhodes	ptr = next_token(&args, " \t");
6387135446Strhodes	if (ptr != NULL && strcmp(ptr, "-all") == 0) {
6388135446Strhodes		dctx->dumpzones = ISC_TRUE;
6389135446Strhodes		dctx->dumpcache = ISC_TRUE;
6390135446Strhodes		ptr = next_token(&args, " \t");
6391135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
6392135446Strhodes		dctx->dumpzones = ISC_FALSE;
6393135446Strhodes		dctx->dumpcache = ISC_TRUE;
6394135446Strhodes		ptr = next_token(&args, " \t");
6395135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
6396135446Strhodes		dctx->dumpzones = ISC_TRUE;
6397135446Strhodes		dctx->dumpcache = ISC_FALSE;
6398135446Strhodes		ptr = next_token(&args, " \t");
6399186462Sdougb	}
6400135446Strhodes
6401153816Sdougb nextview:
6402135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
6403135446Strhodes	     view != NULL;
6404135446Strhodes	     view = ISC_LIST_NEXT(view, link))
6405135446Strhodes	{
6406135446Strhodes		if (ptr != NULL && strcmp(view->name, ptr) != 0)
6407135446Strhodes			continue;
6408135446Strhodes		CHECK(add_view_tolist(dctx, view));
6409135446Strhodes	}
6410153816Sdougb	if (ptr != NULL) {
6411153816Sdougb		ptr = next_token(&args, " \t");
6412153816Sdougb		if (ptr != NULL)
6413153816Sdougb			goto nextview;
6414153816Sdougb	}
6415135446Strhodes	dumpdone(dctx, ISC_R_SUCCESS);
6416135446Strhodes	return (ISC_R_SUCCESS);
6417135446Strhodes
6418135446Strhodes cleanup:
6419135446Strhodes	if (dctx != NULL)
6420135446Strhodes		dumpcontext_destroy(dctx);
6421135446Strhodes	return (result);
6422135446Strhodes}
6423135446Strhodes
6424135446Strhodesisc_result_t
6425224092Sdougbns_server_dumpsecroots(ns_server_t *server, char *args) {
6426224092Sdougb	dns_view_t *view;
6427224092Sdougb	dns_keytable_t *secroots = NULL;
6428224092Sdougb	isc_result_t result;
6429224092Sdougb	char *ptr;
6430224092Sdougb	FILE *fp = NULL;
6431224092Sdougb	isc_time_t now;
6432224092Sdougb	char tbuf[64];
6433224092Sdougb
6434224092Sdougb	/* Skip the command name. */
6435224092Sdougb	ptr = next_token(&args, " \t");
6436224092Sdougb	if (ptr == NULL)
6437224092Sdougb		return (ISC_R_UNEXPECTEDEND);
6438224092Sdougb	ptr = next_token(&args, " \t");
6439224092Sdougb
6440224092Sdougb	CHECKMF(isc_stdio_open(server->secrootsfile, "w", &fp),
6441224092Sdougb		"could not open secroots dump file", server->secrootsfile);
6442224092Sdougb	TIME_NOW(&now);
6443224092Sdougb	isc_time_formattimestamp(&now, tbuf, sizeof(tbuf));
6444224092Sdougb	fprintf(fp, "%s\n", tbuf);
6445224092Sdougb
6446225361Sdougb	do {
6447225361Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
6448225361Sdougb		     view != NULL;
6449225361Sdougb		     view = ISC_LIST_NEXT(view, link))
6450225361Sdougb		{
6451225361Sdougb			if (ptr != NULL && strcmp(view->name, ptr) != 0)
6452225361Sdougb				continue;
6453225361Sdougb			if (secroots != NULL)
6454225361Sdougb				dns_keytable_detach(&secroots);
6455225361Sdougb			result = dns_view_getsecroots(view, &secroots);
6456225361Sdougb			if (result == ISC_R_NOTFOUND) {
6457225361Sdougb				result = ISC_R_SUCCESS;
6458225361Sdougb				continue;
6459225361Sdougb			}
6460225361Sdougb			fprintf(fp, "\n Start view %s\n\n", view->name);
6461225361Sdougb			result = dns_keytable_dump(secroots, fp);
6462225361Sdougb			if (result != ISC_R_SUCCESS)
6463225361Sdougb				fprintf(fp, " dumpsecroots failed: %s\n",
6464225361Sdougb					isc_result_totext(result));
6465224092Sdougb		}
6466224092Sdougb		if (ptr != NULL)
6467225361Sdougb			ptr = next_token(&args, " \t");
6468225361Sdougb	} while (ptr != NULL);
6469224092Sdougb
6470224092Sdougb cleanup:
6471224092Sdougb	if (secroots != NULL)
6472224092Sdougb		dns_keytable_detach(&secroots);
6473224092Sdougb	if (fp != NULL)
6474224092Sdougb		(void)isc_stdio_close(fp);
6475224092Sdougb	if (result == ISC_R_SUCCESS)
6476224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6477224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6478224092Sdougb			      "dumpsecroots complete");
6479224092Sdougb	else
6480224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6481224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6482224092Sdougb			      "dumpsecroots failed: %s",
6483224092Sdougb			      dns_result_totext(result));
6484224092Sdougb	return (result);
6485224092Sdougb}
6486224092Sdougb
6487224092Sdougbisc_result_t
6488135446Strhodesns_server_dumprecursing(ns_server_t *server) {
6489135446Strhodes	FILE *fp = NULL;
6490135446Strhodes	isc_result_t result;
6491135446Strhodes
6492135446Strhodes	CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
6493135446Strhodes		"could not open dump file", server->recfile);
6494135446Strhodes	fprintf(fp,";\n; Recursing Queries\n;\n");
6495135446Strhodes	ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
6496135446Strhodes	fprintf(fp, "; Dump complete\n");
6497135446Strhodes
6498135446Strhodes cleanup:
6499135446Strhodes	if (fp != NULL)
6500135446Strhodes		result = isc_stdio_close(fp);
6501193149Sdougb	if (result == ISC_R_SUCCESS)
6502193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6503193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6504193149Sdougb			      "dumprecursing complete");
6505193149Sdougb	else
6506193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6507193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6508193149Sdougb			      "dumprecursing failed: %s",
6509193149Sdougb			      dns_result_totext(result));
6510135446Strhodes	return (result);
6511135446Strhodes}
6512135446Strhodes
6513135446Strhodesisc_result_t
6514135446Strhodesns_server_setdebuglevel(ns_server_t *server, char *args) {
6515135446Strhodes	char *ptr;
6516135446Strhodes	char *levelstr;
6517135446Strhodes	char *endp;
6518135446Strhodes	long newlevel;
6519135446Strhodes
6520135446Strhodes	UNUSED(server);
6521135446Strhodes
6522135446Strhodes	/* Skip the command name. */
6523135446Strhodes	ptr = next_token(&args, " \t");
6524135446Strhodes	if (ptr == NULL)
6525135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6526135446Strhodes
6527135446Strhodes	/* Look for the new level name. */
6528135446Strhodes	levelstr = next_token(&args, " \t");
6529135446Strhodes	if (levelstr == NULL) {
6530135446Strhodes		if (ns_g_debuglevel < 99)
6531135446Strhodes			ns_g_debuglevel++;
6532135446Strhodes	} else {
6533135446Strhodes		newlevel = strtol(levelstr, &endp, 10);
6534135446Strhodes		if (*endp != '\0' || newlevel < 0 || newlevel > 99)
6535135446Strhodes			return (ISC_R_RANGE);
6536135446Strhodes		ns_g_debuglevel = (unsigned int)newlevel;
6537135446Strhodes	}
6538135446Strhodes	isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
6539193149Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6540193149Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6541193149Sdougb		      "debug level is now %d", ns_g_debuglevel);
6542135446Strhodes	return (ISC_R_SUCCESS);
6543135446Strhodes}
6544135446Strhodes
6545135446Strhodesisc_result_t
6546170222Sdougbns_server_validation(ns_server_t *server, char *args) {
6547170222Sdougb	char *ptr, *viewname;
6548170222Sdougb	dns_view_t *view;
6549170222Sdougb	isc_boolean_t changed = ISC_FALSE;
6550170222Sdougb	isc_result_t result;
6551170222Sdougb	isc_boolean_t enable;
6552170222Sdougb
6553170222Sdougb	/* Skip the command name. */
6554170222Sdougb	ptr = next_token(&args, " \t");
6555170222Sdougb	if (ptr == NULL)
6556170222Sdougb		return (ISC_R_UNEXPECTEDEND);
6557170222Sdougb
6558170222Sdougb	/* Find out what we are to do. */
6559170222Sdougb	ptr = next_token(&args, " \t");
6560170222Sdougb	if (ptr == NULL)
6561170222Sdougb		return (ISC_R_UNEXPECTEDEND);
6562170222Sdougb
6563170222Sdougb	if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
6564170222Sdougb	    !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
6565170222Sdougb		enable = ISC_TRUE;
6566170222Sdougb	else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
6567170222Sdougb		 !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
6568170222Sdougb		enable = ISC_FALSE;
6569170222Sdougb	else
6570170222Sdougb		return (DNS_R_SYNTAX);
6571170222Sdougb
6572170222Sdougb	/* Look for the view name. */
6573170222Sdougb	viewname = next_token(&args, " \t");
6574170222Sdougb
6575170222Sdougb	result = isc_task_beginexclusive(server->task);
6576170222Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6577170222Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
6578170222Sdougb	     view != NULL;
6579170222Sdougb	     view = ISC_LIST_NEXT(view, link))
6580170222Sdougb	{
6581170222Sdougb		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
6582170222Sdougb			continue;
6583170222Sdougb		result = dns_view_flushcache(view);
6584170222Sdougb		if (result != ISC_R_SUCCESS)
6585170222Sdougb			goto out;
6586170222Sdougb		view->enablevalidation = enable;
6587170222Sdougb		changed = ISC_TRUE;
6588170222Sdougb	}
6589170222Sdougb	if (changed)
6590170222Sdougb		result = ISC_R_SUCCESS;
6591170222Sdougb	else
6592170222Sdougb		result = ISC_R_FAILURE;
6593170222Sdougb out:
6594186462Sdougb	isc_task_endexclusive(server->task);
6595170222Sdougb	return (result);
6596170222Sdougb}
6597170222Sdougb
6598170222Sdougbisc_result_t
6599135446Strhodesns_server_flushcache(ns_server_t *server, char *args) {
6600135446Strhodes	char *ptr, *viewname;
6601135446Strhodes	dns_view_t *view;
6602174187Sdougb	isc_boolean_t flushed;
6603174187Sdougb	isc_boolean_t found;
6604135446Strhodes	isc_result_t result;
6605224092Sdougb	ns_cache_t *nsc;
6606135446Strhodes
6607135446Strhodes	/* Skip the command name. */
6608135446Strhodes	ptr = next_token(&args, " \t");
6609135446Strhodes	if (ptr == NULL)
6610135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6611135446Strhodes
6612135446Strhodes	/* Look for the view name. */
6613135446Strhodes	viewname = next_token(&args, " \t");
6614135446Strhodes
6615135446Strhodes	result = isc_task_beginexclusive(server->task);
6616135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6617174187Sdougb	flushed = ISC_TRUE;
6618174187Sdougb	found = ISC_FALSE;
6619224092Sdougb
6620224092Sdougb	/*
6621224092Sdougb	 * Flushing a cache is tricky when caches are shared by multiple views.
6622224092Sdougb	 * We first identify which caches should be flushed in the local cache
6623224092Sdougb	 * list, flush these caches, and then update other views that refer to
6624224092Sdougb	 * the flushed cache DB.
6625224092Sdougb	 */
6626224092Sdougb	if (viewname != NULL) {
6627224092Sdougb		/*
6628224092Sdougb		 * Mark caches that need to be flushed.  This is an O(#view^2)
6629224092Sdougb		 * operation in the very worst case, but should be normally
6630224092Sdougb		 * much more lightweight because only a few (most typically just
6631224092Sdougb		 * one) views will match.
6632224092Sdougb		 */
6633224092Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
6634224092Sdougb		     view != NULL;
6635224092Sdougb		     view = ISC_LIST_NEXT(view, link))
6636224092Sdougb		{
6637224092Sdougb			if (strcasecmp(viewname, view->name) != 0)
6638224092Sdougb				continue;
6639224092Sdougb			found = ISC_TRUE;
6640224092Sdougb			for (nsc = ISC_LIST_HEAD(server->cachelist);
6641224092Sdougb			     nsc != NULL;
6642224092Sdougb			     nsc = ISC_LIST_NEXT(nsc, link)) {
6643224092Sdougb				if (nsc->cache == view->cache)
6644224092Sdougb					break;
6645224092Sdougb			}
6646224092Sdougb			INSIST(nsc != NULL);
6647224092Sdougb			nsc->needflush = ISC_TRUE;
6648224092Sdougb		}
6649224092Sdougb	} else
6650224092Sdougb		found = ISC_TRUE;
6651224092Sdougb
6652224092Sdougb	/* Perform flush */
6653224092Sdougb	for (nsc = ISC_LIST_HEAD(server->cachelist);
6654224092Sdougb	     nsc != NULL;
6655224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
6656224092Sdougb		if (viewname != NULL && !nsc->needflush)
6657135446Strhodes			continue;
6658224092Sdougb		nsc->needflush = ISC_TRUE;
6659224092Sdougb		result = dns_view_flushcache2(nsc->primaryview, ISC_FALSE);
6660193149Sdougb		if (result != ISC_R_SUCCESS) {
6661174187Sdougb			flushed = ISC_FALSE;
6662193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6663193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6664193149Sdougb				      "flushing cache in view '%s' failed: %s",
6665224092Sdougb				      nsc->primaryview->name,
6666224092Sdougb				      isc_result_totext(result));
6667193149Sdougb		}
6668135446Strhodes	}
6669224092Sdougb
6670224092Sdougb	/*
6671224092Sdougb	 * Fix up views that share a flushed cache: let the views update the
6672224092Sdougb	 * cache DB they're referring to.  This could also be an expensive
6673224092Sdougb	 * operation, but should typically be marginal: the inner loop is only
6674224092Sdougb	 * necessary for views that share a cache, and if there are many such
6675224092Sdougb	 * views the number of shared cache should normally be small.
6676224092Sdougb	 * A worst case is that we have n views and n/2 caches, each shared by
6677224092Sdougb	 * two views.  Then this will be a O(n^2/4) operation.
6678224092Sdougb	 */
6679224092Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
6680224092Sdougb	     view != NULL;
6681224092Sdougb	     view = ISC_LIST_NEXT(view, link))
6682224092Sdougb	{
6683224092Sdougb		if (!dns_view_iscacheshared(view))
6684224092Sdougb			continue;
6685224092Sdougb		for (nsc = ISC_LIST_HEAD(server->cachelist);
6686224092Sdougb		     nsc != NULL;
6687224092Sdougb		     nsc = ISC_LIST_NEXT(nsc, link)) {
6688224092Sdougb			if (!nsc->needflush || nsc->cache != view->cache)
6689224092Sdougb				continue;
6690224092Sdougb			result = dns_view_flushcache2(view, ISC_TRUE);
6691224092Sdougb			if (result != ISC_R_SUCCESS) {
6692224092Sdougb				flushed = ISC_FALSE;
6693224092Sdougb				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6694224092Sdougb					      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6695224092Sdougb					      "fixing cache in view '%s' "
6696224092Sdougb					      "failed: %s", view->name,
6697224092Sdougb					      isc_result_totext(result));
6698224092Sdougb			}
6699224092Sdougb		}
6700224092Sdougb	}
6701224092Sdougb
6702224092Sdougb	/* Cleanup the cache list. */
6703224092Sdougb	for (nsc = ISC_LIST_HEAD(server->cachelist);
6704224092Sdougb	     nsc != NULL;
6705224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
6706224092Sdougb		nsc->needflush = ISC_FALSE;
6707224092Sdougb	}
6708224092Sdougb
6709174187Sdougb	if (flushed && found) {
6710193149Sdougb		if (viewname != NULL)
6711193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6712193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6713193149Sdougb				      "flushing cache in view '%s' succeeded",
6714193149Sdougb				      viewname);
6715193149Sdougb		else
6716193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6717193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6718193149Sdougb				      "flushing caches in all views succeeded");
6719135446Strhodes		result = ISC_R_SUCCESS;
6720174187Sdougb	} else {
6721193149Sdougb		if (!found) {
6722193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6723193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6724193149Sdougb				      "flushing cache in view '%s' failed: "
6725193149Sdougb				      "view not found", viewname);
6726174187Sdougb			result = ISC_R_NOTFOUND;
6727193149Sdougb		} else
6728174187Sdougb			result = ISC_R_FAILURE;
6729174187Sdougb	}
6730186462Sdougb	isc_task_endexclusive(server->task);
6731135446Strhodes	return (result);
6732135446Strhodes}
6733135446Strhodes
6734135446Strhodesisc_result_t
6735135446Strhodesns_server_flushname(ns_server_t *server, char *args) {
6736135446Strhodes	char *ptr, *target, *viewname;
6737135446Strhodes	dns_view_t *view;
6738174187Sdougb	isc_boolean_t flushed;
6739174187Sdougb	isc_boolean_t found;
6740135446Strhodes	isc_result_t result;
6741135446Strhodes	isc_buffer_t b;
6742135446Strhodes	dns_fixedname_t fixed;
6743135446Strhodes	dns_name_t *name;
6744135446Strhodes
6745135446Strhodes	/* Skip the command name. */
6746135446Strhodes	ptr = next_token(&args, " \t");
6747135446Strhodes	if (ptr == NULL)
6748135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6749135446Strhodes
6750135446Strhodes	/* Find the domain name to flush. */
6751135446Strhodes	target = next_token(&args, " \t");
6752135446Strhodes	if (target == NULL)
6753135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6754135446Strhodes
6755135446Strhodes	isc_buffer_init(&b, target, strlen(target));
6756135446Strhodes	isc_buffer_add(&b, strlen(target));
6757135446Strhodes	dns_fixedname_init(&fixed);
6758135446Strhodes	name = dns_fixedname_name(&fixed);
6759224092Sdougb	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
6760135446Strhodes	if (result != ISC_R_SUCCESS)
6761135446Strhodes		return (result);
6762135446Strhodes
6763135446Strhodes	/* Look for the view name. */
6764135446Strhodes	viewname = next_token(&args, " \t");
6765135446Strhodes
6766135446Strhodes	result = isc_task_beginexclusive(server->task);
6767135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6768135446Strhodes	flushed = ISC_TRUE;
6769174187Sdougb	found = ISC_FALSE;
6770135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
6771135446Strhodes	     view != NULL;
6772135446Strhodes	     view = ISC_LIST_NEXT(view, link))
6773135446Strhodes	{
6774135446Strhodes		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
6775135446Strhodes			continue;
6776174187Sdougb		found = ISC_TRUE;
6777224092Sdougb		/*
6778224092Sdougb		 * It's a little inefficient to try flushing name for all views
6779224092Sdougb		 * if some of the views share a single cache.  But since the
6780224092Sdougb		 * operation is lightweight we prefer simplicity here.
6781224092Sdougb		 */
6782135446Strhodes		result = dns_view_flushname(view, name);
6783193149Sdougb		if (result != ISC_R_SUCCESS) {
6784135446Strhodes			flushed = ISC_FALSE;
6785193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6786193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6787193149Sdougb				      "flushing name '%s' in cache view '%s' "
6788193149Sdougb				      "failed: %s", target, view->name,
6789193149Sdougb				      isc_result_totext(result));
6790193149Sdougb		}
6791135446Strhodes	}
6792193149Sdougb	if (flushed && found) {
6793193149Sdougb		if (viewname != NULL)
6794193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6795193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6796193149Sdougb				      "flushing name '%s' in cache view '%s' "
6797193149Sdougb				      "succeeded", target, viewname);
6798193149Sdougb		else
6799193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6800193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6801193149Sdougb				      "flushing name '%s' in all cache views "
6802193149Sdougb				      "succeeded", target);
6803135446Strhodes		result = ISC_R_SUCCESS;
6804193149Sdougb	} else {
6805193149Sdougb		if (!found)
6806193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6807193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6808193149Sdougb				      "flushing name '%s' in cache view '%s' "
6809193149Sdougb				      "failed: view not found", target,
6810193149Sdougb				      viewname);
6811135446Strhodes		result = ISC_R_FAILURE;
6812193149Sdougb	}
6813186462Sdougb	isc_task_endexclusive(server->task);
6814135446Strhodes	return (result);
6815135446Strhodes}
6816135446Strhodes
6817135446Strhodesisc_result_t
6818135446Strhodesns_server_status(ns_server_t *server, isc_buffer_t *text) {
6819135446Strhodes	int zonecount, xferrunning, xferdeferred, soaqueries;
6820135446Strhodes	unsigned int n;
6821193149Sdougb	const char *ob = "", *cb = "", *alt = "";
6822135446Strhodes
6823193149Sdougb	if (ns_g_server->version_set) {
6824193149Sdougb		ob = " (";
6825193149Sdougb		cb = ")";
6826193149Sdougb		if (ns_g_server->version == NULL)
6827193149Sdougb			alt = "version.bind/txt/ch disabled";
6828193149Sdougb		else
6829193149Sdougb			alt = ns_g_server->version;
6830193149Sdougb	}
6831135446Strhodes	zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY);
6832135446Strhodes	xferrunning = dns_zonemgr_getcount(server->zonemgr,
6833135446Strhodes					   DNS_ZONESTATE_XFERRUNNING);
6834135446Strhodes	xferdeferred = dns_zonemgr_getcount(server->zonemgr,
6835135446Strhodes					    DNS_ZONESTATE_XFERDEFERRED);
6836135446Strhodes	soaqueries = dns_zonemgr_getcount(server->zonemgr,
6837135446Strhodes					  DNS_ZONESTATE_SOAQUERY);
6838193149Sdougb
6839135446Strhodes	n = snprintf((char *)isc_buffer_used(text),
6840135446Strhodes		     isc_buffer_availablelength(text),
6841193149Sdougb		     "version: %s%s%s%s\n"
6842193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
6843193149Sdougb		     "CPUs found: %u\n"
6844193149Sdougb		     "worker threads: %u\n"
6845193149Sdougb#endif
6846135446Strhodes		     "number of zones: %u\n"
6847135446Strhodes		     "debug level: %d\n"
6848135446Strhodes		     "xfers running: %u\n"
6849135446Strhodes		     "xfers deferred: %u\n"
6850135446Strhodes		     "soa queries in progress: %u\n"
6851135446Strhodes		     "query logging is %s\n"
6852170222Sdougb		     "recursive clients: %d/%d/%d\n"
6853135446Strhodes		     "tcp clients: %d/%d\n"
6854135446Strhodes		     "server is up and running",
6855193149Sdougb		     ns_g_version, ob, alt, cb,
6856193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
6857193149Sdougb		     ns_g_cpus_detected, ns_g_cpus,
6858193149Sdougb#endif
6859135446Strhodes		     zonecount, ns_g_debuglevel, xferrunning, xferdeferred,
6860135446Strhodes		     soaqueries, server->log_queries ? "ON" : "OFF",
6861170222Sdougb		     server->recursionquota.used, server->recursionquota.soft,
6862170222Sdougb		     server->recursionquota.max,
6863135446Strhodes		     server->tcpquota.used, server->tcpquota.max);
6864135446Strhodes	if (n >= isc_buffer_availablelength(text))
6865135446Strhodes		return (ISC_R_NOSPACE);
6866135446Strhodes	isc_buffer_add(text, n);
6867135446Strhodes	return (ISC_R_SUCCESS);
6868135446Strhodes}
6869135446Strhodes
6870193149Sdougbstatic isc_result_t
6871193149Sdougbdelete_keynames(dns_tsig_keyring_t *ring, char *target,
6872193149Sdougb		unsigned int *foundkeys)
6873193149Sdougb{
6874193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
6875193149Sdougb	isc_result_t result;
6876193149Sdougb	dns_rbtnodechain_t chain;
6877193149Sdougb	dns_name_t foundname;
6878193149Sdougb	dns_fixedname_t fixedorigin;
6879193149Sdougb	dns_name_t *origin;
6880193149Sdougb	dns_rbtnode_t *node;
6881193149Sdougb	dns_tsigkey_t *tkey;
6882193149Sdougb
6883193149Sdougb	dns_name_init(&foundname, NULL);
6884193149Sdougb	dns_fixedname_init(&fixedorigin);
6885193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
6886193149Sdougb
6887193149Sdougb again:
6888193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
6889193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
6890193149Sdougb					origin);
6891193149Sdougb	if (result == ISC_R_NOTFOUND) {
6892193149Sdougb		dns_rbtnodechain_invalidate(&chain);
6893193149Sdougb		return (ISC_R_SUCCESS);
6894193149Sdougb	}
6895193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
6896193149Sdougb		dns_rbtnodechain_invalidate(&chain);
6897193149Sdougb		return (result);
6898193149Sdougb	}
6899193149Sdougb
6900193149Sdougb	for (;;) {
6901193149Sdougb		node = NULL;
6902193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
6903193149Sdougb		tkey = node->data;
6904193149Sdougb
6905193149Sdougb		if (tkey != NULL) {
6906193149Sdougb			if (!tkey->generated)
6907193149Sdougb				goto nextkey;
6908193149Sdougb
6909193149Sdougb			dns_name_format(&tkey->name, namestr, sizeof(namestr));
6910193149Sdougb			if (strcmp(namestr, target) == 0) {
6911193149Sdougb				(*foundkeys)++;
6912193149Sdougb				dns_rbtnodechain_invalidate(&chain);
6913193149Sdougb				(void)dns_rbt_deletename(ring->keys,
6914193149Sdougb							 &tkey->name,
6915193149Sdougb							 ISC_FALSE);
6916193149Sdougb				goto again;
6917193149Sdougb			}
6918193149Sdougb		}
6919193149Sdougb
6920193149Sdougb	nextkey:
6921193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname, origin);
6922193149Sdougb		if (result == ISC_R_NOMORE)
6923193149Sdougb			break;
6924193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
6925193149Sdougb			dns_rbtnodechain_invalidate(&chain);
6926193149Sdougb			return (result);
6927193149Sdougb		}
6928193149Sdougb	}
6929193149Sdougb
6930193149Sdougb	return (ISC_R_SUCCESS);
6931193149Sdougb}
6932193149Sdougb
6933193149Sdougbisc_result_t
6934193149Sdougbns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) {
6935193149Sdougb	isc_result_t result;
6936193149Sdougb	unsigned int n;
6937193149Sdougb	dns_view_t *view;
6938193149Sdougb	unsigned int foundkeys = 0;
6939193149Sdougb	char *target;
6940193149Sdougb	char *viewname;
6941193149Sdougb
6942193149Sdougb	(void)next_token(&command, " \t");  /* skip command name */
6943193149Sdougb	target = next_token(&command, " \t");
6944193149Sdougb	if (target == NULL)
6945193149Sdougb		return (ISC_R_UNEXPECTEDEND);
6946193149Sdougb	viewname = next_token(&command, " \t");
6947193149Sdougb
6948193149Sdougb	result = isc_task_beginexclusive(server->task);
6949193149Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6950193149Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
6951193149Sdougb	     view != NULL;
6952193149Sdougb	     view = ISC_LIST_NEXT(view, link)) {
6953193149Sdougb		if (viewname == NULL || strcmp(view->name, viewname) == 0) {
6954193149Sdougb			RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write);
6955193149Sdougb			result = delete_keynames(view->dynamickeys, target,
6956193149Sdougb						 &foundkeys);
6957193149Sdougb			RWUNLOCK(&view->dynamickeys->lock,
6958193149Sdougb				 isc_rwlocktype_write);
6959193149Sdougb			if (result != ISC_R_SUCCESS) {
6960193149Sdougb				isc_task_endexclusive(server->task);
6961193149Sdougb				return (result);
6962193149Sdougb			}
6963193149Sdougb		}
6964193149Sdougb	}
6965193149Sdougb	isc_task_endexclusive(server->task);
6966193149Sdougb
6967193149Sdougb	n = snprintf((char *)isc_buffer_used(text),
6968193149Sdougb		     isc_buffer_availablelength(text),
6969193149Sdougb		     "%d tsig keys deleted.\n", foundkeys);
6970218384Sdougb	if (n >= isc_buffer_availablelength(text))
6971193149Sdougb		return (ISC_R_NOSPACE);
6972193149Sdougb	isc_buffer_add(text, n);
6973193149Sdougb
6974193149Sdougb	return (ISC_R_SUCCESS);
6975193149Sdougb}
6976193149Sdougb
6977193149Sdougbstatic isc_result_t
6978193149Sdougblist_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text,
6979193149Sdougb	     unsigned int *foundkeys)
6980193149Sdougb{
6981193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
6982193149Sdougb	char creatorstr[DNS_NAME_FORMATSIZE];
6983193149Sdougb	isc_result_t result;
6984193149Sdougb	dns_rbtnodechain_t chain;
6985193149Sdougb	dns_name_t foundname;
6986193149Sdougb	dns_fixedname_t fixedorigin;
6987193149Sdougb	dns_name_t *origin;
6988193149Sdougb	dns_rbtnode_t *node;
6989193149Sdougb	dns_tsigkey_t *tkey;
6990193149Sdougb	unsigned int n;
6991193149Sdougb	const char *viewname;
6992193149Sdougb
6993193149Sdougb	if (view != NULL)
6994193149Sdougb		viewname = view->name;
6995193149Sdougb	else
6996193149Sdougb		viewname = "(global)";
6997193149Sdougb
6998193149Sdougb	dns_name_init(&foundname, NULL);
6999193149Sdougb	dns_fixedname_init(&fixedorigin);
7000193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
7001193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
7002193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
7003193149Sdougb					origin);
7004193149Sdougb	if (result == ISC_R_NOTFOUND) {
7005193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7006193149Sdougb		return (ISC_R_SUCCESS);
7007193149Sdougb	}
7008193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7009193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7010193149Sdougb		return (result);
7011193149Sdougb	}
7012193149Sdougb
7013193149Sdougb	for (;;) {
7014193149Sdougb		node = NULL;
7015193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
7016193149Sdougb		tkey = node->data;
7017193149Sdougb
7018193149Sdougb		if (tkey != NULL) {
7019193149Sdougb			(*foundkeys)++;
7020193149Sdougb			dns_name_format(&tkey->name, namestr, sizeof(namestr));
7021193149Sdougb			if (tkey->generated) {
7022193149Sdougb				dns_name_format(tkey->creator, creatorstr,
7023193149Sdougb						sizeof(creatorstr));
7024193149Sdougb				n = snprintf((char *)isc_buffer_used(text),
7025193149Sdougb					     isc_buffer_availablelength(text),
7026193149Sdougb					     "view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n",
7027193149Sdougb					     viewname, namestr, creatorstr);
7028193149Sdougb			} else {
7029193149Sdougb				n = snprintf((char *)isc_buffer_used(text),
7030193149Sdougb					     isc_buffer_availablelength(text),
7031193149Sdougb					     "view \"%s\"; type \"static\"; key \"%s\";\n",
7032193149Sdougb					     viewname, namestr);
7033193149Sdougb			}
7034193149Sdougb			if (n >= isc_buffer_availablelength(text)) {
7035193149Sdougb				dns_rbtnodechain_invalidate(&chain);
7036193149Sdougb				return (ISC_R_NOSPACE);
7037193149Sdougb			}
7038193149Sdougb			isc_buffer_add(text, n);
7039193149Sdougb		}
7040193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname, origin);
7041193149Sdougb		if (result == ISC_R_NOMORE)
7042193149Sdougb			break;
7043193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7044193149Sdougb			dns_rbtnodechain_invalidate(&chain);
7045193149Sdougb			return (result);
7046193149Sdougb		}
7047193149Sdougb	}
7048193149Sdougb
7049193149Sdougb	return (ISC_R_SUCCESS);
7050193149Sdougb}
7051193149Sdougb
7052193149Sdougbisc_result_t
7053193149Sdougbns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) {
7054193149Sdougb	isc_result_t result;
7055193149Sdougb	unsigned int n;
7056193149Sdougb	dns_view_t *view;
7057193149Sdougb	unsigned int foundkeys = 0;
7058193149Sdougb
7059193149Sdougb	result = isc_task_beginexclusive(server->task);
7060193149Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7061193149Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7062193149Sdougb	     view != NULL;
7063193149Sdougb	     view = ISC_LIST_NEXT(view, link)) {
7064193149Sdougb		RWLOCK(&view->statickeys->lock, isc_rwlocktype_read);
7065193149Sdougb		result = list_keynames(view, view->statickeys, text,
7066193149Sdougb				       &foundkeys);
7067193149Sdougb		RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read);
7068193149Sdougb		if (result != ISC_R_SUCCESS) {
7069193149Sdougb			isc_task_endexclusive(server->task);
7070193149Sdougb			return (result);
7071193149Sdougb		}
7072193149Sdougb		RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
7073193149Sdougb		result = list_keynames(view, view->dynamickeys, text,
7074193149Sdougb				       &foundkeys);
7075193149Sdougb		RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
7076193149Sdougb		if (result != ISC_R_SUCCESS) {
7077193149Sdougb			isc_task_endexclusive(server->task);
7078193149Sdougb			return (result);
7079193149Sdougb		}
7080193149Sdougb	}
7081193149Sdougb	isc_task_endexclusive(server->task);
7082193149Sdougb
7083193149Sdougb	if (foundkeys == 0) {
7084193149Sdougb		n = snprintf((char *)isc_buffer_used(text),
7085193149Sdougb			     isc_buffer_availablelength(text),
7086193149Sdougb			     "no tsig keys found.\n");
7087218384Sdougb		if (n >= isc_buffer_availablelength(text))
7088193149Sdougb			return (ISC_R_NOSPACE);
7089193149Sdougb		isc_buffer_add(text, n);
7090193149Sdougb	}
7091193149Sdougb
7092193149Sdougb	return (ISC_R_SUCCESS);
7093193149Sdougb}
7094193149Sdougb
7095135446Strhodes/*
7096224092Sdougb * Act on a "sign" or "loadkeys" command from the command channel.
7097224092Sdougb */
7098224092Sdougbisc_result_t
7099224092Sdougbns_server_rekey(ns_server_t *server, char *args) {
7100224092Sdougb	isc_result_t result;
7101224092Sdougb	dns_zone_t *zone = NULL;
7102224092Sdougb	dns_zonetype_t type;
7103224092Sdougb	isc_uint16_t keyopts;
7104224092Sdougb	isc_boolean_t fullsign = ISC_FALSE;
7105224092Sdougb
7106224092Sdougb	if (strncasecmp(args, NS_COMMAND_SIGN, strlen(NS_COMMAND_SIGN)) == 0)
7107224092Sdougb	    fullsign = ISC_TRUE;
7108224092Sdougb
7109224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
7110224092Sdougb	if (result != ISC_R_SUCCESS)
7111224092Sdougb		return (result);
7112224092Sdougb	if (zone == NULL)
7113224092Sdougb		return (ISC_R_UNEXPECTEDEND);   /* XXX: or do all zones? */
7114224092Sdougb
7115224092Sdougb	type = dns_zone_gettype(zone);
7116224092Sdougb	if (type != dns_zone_master) {
7117224092Sdougb		dns_zone_detach(&zone);
7118224092Sdougb		return (DNS_R_NOTMASTER);
7119224092Sdougb	}
7120224092Sdougb
7121224092Sdougb	keyopts = dns_zone_getkeyopts(zone);
7122224092Sdougb
7123224092Sdougb	/* "rndc loadkeys" requires "auto-dnssec maintain". */
7124224092Sdougb	if ((keyopts & DNS_ZONEKEY_ALLOW) == 0)
7125224092Sdougb		result = ISC_R_NOPERM;
7126224092Sdougb	else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign)
7127224092Sdougb		result = ISC_R_NOPERM;
7128224092Sdougb	else
7129224092Sdougb		dns_zone_rekey(zone, fullsign);
7130224092Sdougb
7131224092Sdougb	dns_zone_detach(&zone);
7132224092Sdougb	return (result);
7133224092Sdougb}
7134224092Sdougb
7135224092Sdougb/*
7136170222Sdougb * Act on a "freeze" or "thaw" command from the command channel.
7137135446Strhodes */
7138135446Strhodesisc_result_t
7139204619Sdougbns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args,
7140204619Sdougb		 isc_buffer_t *text)
7141204619Sdougb{
7142170222Sdougb	isc_result_t result, tresult;
7143135446Strhodes	dns_zone_t *zone = NULL;
7144135446Strhodes	dns_zonetype_t type;
7145135446Strhodes	char classstr[DNS_RDATACLASS_FORMATSIZE];
7146135446Strhodes	char zonename[DNS_NAME_FORMATSIZE];
7147135446Strhodes	dns_view_t *view;
7148135446Strhodes	char *journal;
7149135446Strhodes	const char *vname, *sep;
7150135446Strhodes	isc_boolean_t frozen;
7151204619Sdougb	const char *msg = NULL;
7152186462Sdougb
7153224092Sdougb	result = zone_from_args(server, args, &zone, NULL);
7154135446Strhodes	if (result != ISC_R_SUCCESS)
7155135446Strhodes		return (result);
7156170222Sdougb	if (zone == NULL) {
7157170222Sdougb		result = isc_task_beginexclusive(server->task);
7158170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
7159170222Sdougb		tresult = ISC_R_SUCCESS;
7160186462Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
7161170222Sdougb		     view != NULL;
7162170222Sdougb		     view = ISC_LIST_NEXT(view, link)) {
7163170222Sdougb			result = dns_view_freezezones(view, freeze);
7164170222Sdougb			if (result != ISC_R_SUCCESS &&
7165170222Sdougb			    tresult == ISC_R_SUCCESS)
7166170222Sdougb				tresult = result;
7167170222Sdougb		}
7168170222Sdougb		isc_task_endexclusive(server->task);
7169170222Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7170170222Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7171170222Sdougb			      "%s all zones: %s",
7172170222Sdougb			      freeze ? "freezing" : "thawing",
7173170222Sdougb			      isc_result_totext(tresult));
7174170222Sdougb		return (tresult);
7175170222Sdougb	}
7176135446Strhodes	type = dns_zone_gettype(zone);
7177135446Strhodes	if (type != dns_zone_master) {
7178135446Strhodes		dns_zone_detach(&zone);
7179224092Sdougb		return (DNS_R_NOTMASTER);
7180135446Strhodes	}
7181135446Strhodes
7182204619Sdougb	result = isc_task_beginexclusive(server->task);
7183204619Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7184135446Strhodes	frozen = dns_zone_getupdatedisabled(zone);
7185135446Strhodes	if (freeze) {
7186204619Sdougb		if (frozen) {
7187204619Sdougb			msg = "WARNING: The zone was already frozen.\n"
7188204619Sdougb			      "Someone else may be editing it or "
7189204619Sdougb			      "it may still be re-loading.";
7190135446Strhodes			result = DNS_R_FROZEN;
7191204619Sdougb		}
7192204619Sdougb		if (result == ISC_R_SUCCESS) {
7193135446Strhodes			result = dns_zone_flush(zone);
7194204619Sdougb			if (result != ISC_R_SUCCESS)
7195204619Sdougb				msg = "Flushing the zone updates to "
7196204619Sdougb				      "disk failed.";
7197204619Sdougb		}
7198135446Strhodes		if (result == ISC_R_SUCCESS) {
7199135446Strhodes			journal = dns_zone_getjournal(zone);
7200135446Strhodes			if (journal != NULL)
7201135446Strhodes				(void)isc_file_remove(journal);
7202135446Strhodes		}
7203204619Sdougb		if (result == ISC_R_SUCCESS)
7204204619Sdougb			dns_zone_setupdatedisabled(zone, freeze);
7205135446Strhodes	} else {
7206135446Strhodes		if (frozen) {
7207204619Sdougb			result = dns_zone_loadandthaw(zone);
7208204619Sdougb			switch (result) {
7209204619Sdougb			case ISC_R_SUCCESS:
7210204619Sdougb			case DNS_R_UPTODATE:
7211204619Sdougb				msg = "The zone reload and thaw was "
7212204619Sdougb				      "successful.";
7213135446Strhodes				result = ISC_R_SUCCESS;
7214204619Sdougb				break;
7215204619Sdougb			case DNS_R_CONTINUE:
7216204619Sdougb				msg = "A zone reload and thaw was started.\n"
7217204619Sdougb				      "Check the logs to see the result.";
7218204619Sdougb				result = ISC_R_SUCCESS;
7219204619Sdougb				break;
7220204619Sdougb			}
7221135446Strhodes		}
7222135446Strhodes	}
7223204619Sdougb	isc_task_endexclusive(server->task);
7224135446Strhodes
7225204619Sdougb	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
7226204619Sdougb		isc_buffer_putmem(text, (const unsigned char *)msg,
7227204619Sdougb				  strlen(msg) + 1);
7228204619Sdougb
7229135446Strhodes	view = dns_zone_getview(zone);
7230224092Sdougb	if (strcmp(view->name, "_default") == 0 ||
7231224092Sdougb	    strcmp(view->name, "_bind") == 0)
7232135446Strhodes	{
7233135446Strhodes		vname = "";
7234135446Strhodes		sep = "";
7235135446Strhodes	} else {
7236135446Strhodes		vname = view->name;
7237135446Strhodes		sep = " ";
7238135446Strhodes	}
7239135446Strhodes	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
7240135446Strhodes			      sizeof(classstr));
7241135446Strhodes	dns_name_format(dns_zone_getorigin(zone),
7242135446Strhodes			zonename, sizeof(zonename));
7243135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7244135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7245135446Strhodes		      "%s zone '%s/%s'%s%s: %s",
7246170222Sdougb		      freeze ? "freezing" : "thawing",
7247135446Strhodes		      zonename, classstr, sep, vname,
7248135446Strhodes		      isc_result_totext(result));
7249135446Strhodes	dns_zone_detach(&zone);
7250135446Strhodes	return (result);
7251135446Strhodes}
7252153816Sdougb
7253153816Sdougb#ifdef HAVE_LIBSCF
7254153816Sdougb/*
7255153816Sdougb * This function adds a message for rndc to echo if named
7256153816Sdougb * is managed by smf and is also running chroot.
7257153816Sdougb */
7258153816Sdougbisc_result_t
7259153816Sdougbns_smf_add_message(isc_buffer_t *text) {
7260153816Sdougb	unsigned int n;
7261153816Sdougb
7262153816Sdougb	n = snprintf((char *)isc_buffer_used(text),
7263153816Sdougb		isc_buffer_availablelength(text),
7264153816Sdougb		"use svcadm(1M) to manage named");
7265153816Sdougb	if (n >= isc_buffer_availablelength(text))
7266153816Sdougb		return (ISC_R_NOSPACE);
7267153816Sdougb	isc_buffer_add(text, n);
7268153816Sdougb	return (ISC_R_SUCCESS);
7269153816Sdougb}
7270153816Sdougb#endif /* HAVE_LIBSCF */
7271224092Sdougb
7272224092Sdougb/*
7273224092Sdougb * Act on an "addzone" command from the command channel.
7274224092Sdougb */
7275224092Sdougbisc_result_t
7276224092Sdougbns_server_add_zone(ns_server_t *server, char *args) {
7277224092Sdougb	isc_result_t	     result;
7278224092Sdougb	isc_buffer_t	     argbuf;
7279224092Sdougb	size_t		     arglen;
7280224092Sdougb	cfg_parser_t	    *parser = NULL;
7281224092Sdougb	cfg_obj_t	    *config = NULL;
7282224092Sdougb	const cfg_obj_t	    *vconfig = NULL;
7283224092Sdougb	const cfg_obj_t	    *views = NULL;
7284224092Sdougb	const cfg_obj_t     *parms = NULL;
7285224092Sdougb	const cfg_obj_t     *obj = NULL;
7286224092Sdougb	const cfg_listelt_t *element;
7287224092Sdougb	const char	    *zonename;
7288224092Sdougb	const char	    *classname = NULL;
7289224092Sdougb	const char	    *argp;
7290224092Sdougb	const char	    *viewname = NULL;
7291224092Sdougb	dns_rdataclass_t     rdclass;
7292224092Sdougb	dns_view_t	    *view = 0;
7293224092Sdougb	isc_buffer_t	     buf, *nbuf = NULL;
7294224092Sdougb	dns_name_t	     dnsname;
7295224092Sdougb	dns_zone_t	    *zone = NULL;
7296224092Sdougb	FILE		    *fp = NULL;
7297224092Sdougb	struct cfg_context  *cfg = NULL;
7298224092Sdougb
7299224092Sdougb	/* Try to parse the argument string */
7300224092Sdougb	arglen = strlen(args);
7301224092Sdougb	isc_buffer_init(&argbuf, args, arglen);
7302224092Sdougb	isc_buffer_add(&argbuf, strlen(args));
7303224092Sdougb	CHECK(cfg_parser_create(server->mctx, ns_g_lctx, &parser));
7304224092Sdougb	CHECK(cfg_parse_buffer(parser, &argbuf, &cfg_type_addzoneconf,
7305224092Sdougb			       &config));
7306224092Sdougb	CHECK(cfg_map_get(config, "addzone", &parms));
7307224092Sdougb
7308224092Sdougb	zonename = cfg_obj_asstring(cfg_tuple_get(parms, "name"));
7309224092Sdougb	isc_buffer_init(&buf, zonename, strlen(zonename));
7310224092Sdougb	isc_buffer_add(&buf, strlen(zonename));
7311224092Sdougb	dns_name_init(&dnsname, NULL);
7312224092Sdougb	isc_buffer_allocate(server->mctx, &nbuf, 256);
7313224092Sdougb	dns_name_setbuffer(&dnsname, nbuf);
7314224092Sdougb	CHECK(dns_name_fromtext(&dnsname, &buf, dns_rootname, ISC_FALSE, NULL));
7315224092Sdougb
7316224092Sdougb	/* Make sense of optional class argument */
7317224092Sdougb	obj = cfg_tuple_get(parms, "class");
7318224092Sdougb	CHECK(ns_config_getclass(obj, dns_rdataclass_in, &rdclass));
7319224092Sdougb	if (rdclass != dns_rdataclass_in && obj)
7320224092Sdougb		classname = cfg_obj_asstring(obj);
7321224092Sdougb
7322224092Sdougb	/* Make sense of optional view argument */
7323224092Sdougb	obj = cfg_tuple_get(parms, "view");
7324224092Sdougb	if (obj && cfg_obj_isstring(obj))
7325224092Sdougb		viewname = cfg_obj_asstring(obj);
7326224092Sdougb	if (viewname == NULL || *viewname == '\0')
7327224092Sdougb		viewname = "_default";
7328224092Sdougb	CHECK(dns_viewlist_find(&server->viewlist, viewname, rdclass, &view));
7329224092Sdougb
7330224092Sdougb	/* Are we accepting new zones? */
7331224092Sdougb	if (view->new_zone_file == NULL) {
7332224092Sdougb		result = ISC_R_NOPERM;
7333224092Sdougb		goto cleanup;
7334224092Sdougb	}
7335224092Sdougb
7336224092Sdougb	cfg = (struct cfg_context *) view->new_zone_config;
7337224092Sdougb	if (cfg == NULL) {
7338224092Sdougb		result = ISC_R_FAILURE;
7339224092Sdougb		goto cleanup;
7340224092Sdougb	}
7341224092Sdougb
7342224092Sdougb	/* Zone shouldn't already exist */
7343224092Sdougb	result = dns_zt_find(view->zonetable, &dnsname, 0, NULL, &zone);
7344224092Sdougb	if (result == ISC_R_SUCCESS) {
7345224092Sdougb		result = ISC_R_EXISTS;
7346224092Sdougb		goto cleanup;
7347224092Sdougb	} else if (result == DNS_R_PARTIALMATCH) {
7348224092Sdougb		/* Create our sub-zone anyway */
7349224092Sdougb		dns_zone_detach(&zone);
7350224092Sdougb		zone = NULL;
7351224092Sdougb	}
7352224092Sdougb	else if (result != ISC_R_NOTFOUND)
7353224092Sdougb		goto cleanup;
7354224092Sdougb
7355224092Sdougb	/* Find the view statement */
7356224092Sdougb	cfg_map_get(cfg->config, "view", &views);
7357224092Sdougb	for (element = cfg_list_first(views);
7358224092Sdougb	     element != NULL;
7359224092Sdougb	     element = cfg_list_next(element))
7360224092Sdougb	{
7361224092Sdougb		const char *vname;
7362224092Sdougb		vconfig = cfg_listelt_value(element);
7363224092Sdougb		vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
7364224092Sdougb		if (vname && !strcasecmp(vname, viewname))
7365224092Sdougb			break;
7366224092Sdougb		vconfig = NULL;
7367224092Sdougb	}
7368224092Sdougb
7369224092Sdougb	/* Open save file for write configuration */
7370224092Sdougb	CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
7371224092Sdougb
7372224092Sdougb	/* Mark view unfrozen so that zone can be added */
7373234010Sdougb	isc_task_beginexclusive(server->task);
7374224092Sdougb	dns_view_thaw(view);
7375224092Sdougb	result = configure_zone(cfg->config, parms, vconfig,
7376225361Sdougb				server->mctx, view, cfg->actx, ISC_FALSE);
7377224092Sdougb	dns_view_freeze(view);
7378234010Sdougb	isc_task_endexclusive(server->task);
7379234010Sdougb	if (result != ISC_R_SUCCESS)
7380224092Sdougb		goto cleanup;
7381224092Sdougb
7382224092Sdougb	/* Is it there yet? */
7383224092Sdougb	CHECK(dns_zt_find(view->zonetable, &dnsname, 0, NULL, &zone));
7384224092Sdougb
7385224092Sdougb	/*
7386224092Sdougb	 * Load the zone from the master file.  If this fails, we'll
7387224092Sdougb	 * need to undo the configuration we've done already.
7388224092Sdougb	 */
7389224092Sdougb	result = dns_zone_loadnew(zone);
7390224092Sdougb	if (result != ISC_R_SUCCESS) {
7391224092Sdougb		dns_db_t *dbp = NULL;
7392224092Sdougb
7393224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7394224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7395224092Sdougb			      "addzone failed; reverting.");
7396224092Sdougb
7397224092Sdougb		/* If the zone loaded partially, unload it */
7398224092Sdougb		if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
7399224092Sdougb			dns_db_detach(&dbp);
7400224092Sdougb			dns_zone_unload(zone);
7401224092Sdougb		}
7402224092Sdougb
7403224092Sdougb		/* Remove the zone from the zone table */
7404224092Sdougb		dns_zt_unmount(view->zonetable, zone);
7405224092Sdougb		goto cleanup;
7406224092Sdougb	}
7407224092Sdougb
7408224092Sdougb	/* Flag the zone as having been added at runtime */
7409224092Sdougb	dns_zone_setadded(zone, ISC_TRUE);
7410224092Sdougb
7411224092Sdougb	/* Emit just the zone name from args */
7412224092Sdougb	CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
7413224092Sdougb	CHECK(isc_stdio_write(zonename, strlen(zonename), 1, fp, NULL));
7414224092Sdougb	CHECK(isc_stdio_write(" ", 1, 1, fp, NULL));
7415224092Sdougb
7416224092Sdougb	/* Classname, if not default */
7417224092Sdougb	if (classname != NULL && *classname != '\0') {
7418224092Sdougb		CHECK(isc_stdio_write(classname, strlen(classname), 1, fp,
7419224092Sdougb				      NULL));
7420224092Sdougb		CHECK(isc_stdio_write(" ", 1, 1, fp, NULL));
7421224092Sdougb	}
7422224092Sdougb
7423224092Sdougb	/* Find beginning of option block from args */
7424224092Sdougb	for (argp = args; *argp; argp++, arglen--) {
7425224092Sdougb		if (*argp == '{') {	/* Assume matching '}' */
7426224092Sdougb			/* Add that to our file */
7427224092Sdougb			CHECK(isc_stdio_write(argp, arglen, 1, fp, NULL));
7428224092Sdougb
7429224092Sdougb			/* Make sure we end with a LF */
7430224092Sdougb			if (argp[arglen-1] != '\n') {
7431224092Sdougb				CHECK(isc_stdio_write("\n", 1, 1, fp, NULL));
7432224092Sdougb			}
7433224092Sdougb			break;
7434224092Sdougb		}
7435224092Sdougb	}
7436224092Sdougb
7437224092Sdougb	CHECK(isc_stdio_close(fp));
7438224092Sdougb	fp = NULL;
7439224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7440224092Sdougb				  NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7441224092Sdougb				  "zone %s added to view %s via addzone",
7442224092Sdougb				  zonename, viewname);
7443224092Sdougb
7444224092Sdougb	result = ISC_R_SUCCESS;
7445224092Sdougb
7446224092Sdougb cleanup:
7447224092Sdougb	if (fp != NULL)
7448224092Sdougb		isc_stdio_close(fp);
7449224092Sdougb	if (parser != NULL) {
7450224092Sdougb		if (config != NULL)
7451224092Sdougb			cfg_obj_destroy(parser, &config);
7452224092Sdougb		cfg_parser_destroy(&parser);
7453224092Sdougb	}
7454224092Sdougb	if (zone != NULL)
7455224092Sdougb		dns_zone_detach(&zone);
7456224092Sdougb	if (view != NULL)
7457224092Sdougb		dns_view_detach(&view);
7458224092Sdougb	if (nbuf != NULL)
7459224092Sdougb		isc_buffer_free(&nbuf);
7460224092Sdougb
7461224092Sdougb	return (result);
7462224092Sdougb}
7463224092Sdougb
7464224092Sdougb/*
7465224092Sdougb * Act on a "delzone" command from the command channel.
7466224092Sdougb */
7467224092Sdougbisc_result_t
7468224092Sdougbns_server_del_zone(ns_server_t *server, char *args) {
7469224092Sdougb	isc_result_t	       result;
7470224092Sdougb	dns_zone_t	      *zone = NULL;
7471224092Sdougb	dns_view_t	      *view = NULL;
7472224092Sdougb	dns_db_t	      *dbp = NULL;
7473224092Sdougb	const char	      *filename = NULL;
7474224092Sdougb	char		      *tmpname = NULL;
7475224092Sdougb	char		       buf[1024];
7476224092Sdougb	const char	      *zonename = NULL;
7477224092Sdougb	size_t		       znamelen = 0;
7478224092Sdougb	FILE		      *ifp = NULL, *ofp = NULL;
7479224092Sdougb
7480224092Sdougb	/* Parse parameters */
7481224092Sdougb	CHECK(zone_from_args(server, args, &zone, &zonename));
7482224092Sdougb	if (result != ISC_R_SUCCESS)
7483224092Sdougb		return (result);
7484224092Sdougb	if (zone == NULL) {
7485224092Sdougb		result = ISC_R_UNEXPECTEDEND;
7486224092Sdougb		goto cleanup;
7487224092Sdougb	}
7488224092Sdougb
7489224092Sdougb	/*
7490224092Sdougb	 * Was this zone originally added at runtime?
7491224092Sdougb	 * If not, we can't delete it now.
7492224092Sdougb	 */
7493224092Sdougb	if (!dns_zone_getadded(zone)) {
7494224092Sdougb		result = ISC_R_NOPERM;
7495224092Sdougb		goto cleanup;
7496224092Sdougb	}
7497224092Sdougb
7498224092Sdougb	if (zonename != NULL)
7499224092Sdougb		znamelen = strlen(zonename);
7500224092Sdougb
7501224092Sdougb	/* Dig out configuration for this zone */
7502224092Sdougb	view = dns_zone_getview(zone);
7503224092Sdougb	filename = view->new_zone_file;
7504224092Sdougb	if (filename == NULL) {
7505224092Sdougb		/* No adding zones in this view */
7506224092Sdougb		result = ISC_R_FAILURE;
7507224092Sdougb		goto cleanup;
7508224092Sdougb	}
7509224092Sdougb
7510224092Sdougb	/* Rewrite zone list */
7511224092Sdougb	result = isc_stdio_open(filename, "r", &ifp);
7512224092Sdougb	if (ifp != NULL && result == ISC_R_SUCCESS) {
7513224092Sdougb		char *found = NULL, *p = NULL;
7514224092Sdougb		size_t n;
7515224092Sdougb
7516224092Sdougb		/* Create a temporary file */
7517224092Sdougb		CHECK(isc_string_printf(buf, 1023, "%s.%ld", filename,
7518224092Sdougb					(long)getpid()));
7519224092Sdougb		if (!(tmpname = isc_mem_strdup(server->mctx, buf))) {
7520224092Sdougb			result = ISC_R_NOMEMORY;
7521224092Sdougb			goto cleanup;
7522224092Sdougb		}
7523224092Sdougb		CHECK(isc_stdio_open(tmpname, "w", &ofp));
7524224092Sdougb
7525224092Sdougb		/* Look for the entry for that zone */
7526224092Sdougb		while (fgets(buf, 1024, ifp)) {
7527224092Sdougb			/* A 'zone' line */
7528224092Sdougb			if (strncasecmp(buf, "zone", 4)) {
7529224092Sdougb				fputs(buf, ofp);
7530224092Sdougb				continue;
7531224092Sdougb			}
7532224092Sdougb			p = buf+4;
7533224092Sdougb
7534224092Sdougb			/* Locate a name */
7535224092Sdougb			while (*p &&
7536224092Sdougb			       ((*p == '"') || isspace((unsigned char)*p)))
7537224092Sdougb				p++;
7538224092Sdougb
7539224092Sdougb			/* Is that the zone we're looking for */
7540224092Sdougb			if (strncasecmp(p, zonename, znamelen)) {
7541224092Sdougb				fputs(buf, ofp);
7542224092Sdougb				continue;
7543224092Sdougb			}
7544224092Sdougb
7545224092Sdougb			/* And nothing else? */
7546224092Sdougb			p += znamelen;
7547224092Sdougb			if (isspace((unsigned char)*p) ||
7548224092Sdougb			    *p == '"' || *p == '{') {
7549224092Sdougb				/* This must be the entry */
7550224092Sdougb				found = p;
7551224092Sdougb				break;
7552224092Sdougb			}
7553224092Sdougb
7554224092Sdougb			/* Spit it out, keep looking */
7555224092Sdougb			fputs(buf, ofp);
7556224092Sdougb		}
7557224092Sdougb
7558224092Sdougb		/* Skip over an option block (matching # of braces) */
7559224092Sdougb		if (found) {
7560224092Sdougb			int obrace = 0, cbrace = 0;
7561224092Sdougb			for (;;) {
7562224092Sdougb				while (*p) {
7563224092Sdougb					if (*p == '{') obrace++;
7564224092Sdougb					if (*p == '}') cbrace++;
7565224092Sdougb					p++;
7566224092Sdougb				}
7567224092Sdougb				if (obrace && (obrace == cbrace))
7568224092Sdougb					break;
7569224092Sdougb				if (!fgets(buf, 1024, ifp))
7570224092Sdougb					break;
7571224092Sdougb				p = buf;
7572224092Sdougb			}
7573224092Sdougb
7574224092Sdougb			/* Just spool the remainder of the file out */
7575224092Sdougb			result = isc_stdio_read(buf, 1, 1024, ifp, &n);
7576224092Sdougb			while (n > 0U) {
7577224092Sdougb				if (result == ISC_R_EOF)
7578224092Sdougb					result = ISC_R_SUCCESS;
7579224092Sdougb				CHECK(result);
7580224092Sdougb				isc_stdio_write(buf, 1, n, ofp, NULL);
7581224092Sdougb				result = isc_stdio_read(buf, 1, 1024, ifp, &n);
7582224092Sdougb			}
7583224092Sdougb
7584224092Sdougb			/* Move temporary into place */
7585224092Sdougb			CHECK(isc_file_rename(tmpname, view->new_zone_file));
7586224092Sdougb		} else {
7587224092Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7588224092Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
7589224092Sdougb				      "deleted zone %s was missing from "
7590224092Sdougb				      "new zone file", zonename);
7591224092Sdougb			goto cleanup;
7592224092Sdougb		}
7593224092Sdougb	}
7594224092Sdougb
7595224092Sdougb	/* Stop answering for this zone */
7596224092Sdougb	if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
7597224092Sdougb		dns_db_detach(&dbp);
7598224092Sdougb		dns_zone_unload(zone);
7599224092Sdougb	}
7600224092Sdougb
7601224092Sdougb	CHECK(dns_zt_unmount(view->zonetable, zone));
7602224092Sdougb
7603224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7604224092Sdougb				  NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7605224092Sdougb				  "zone %s removed via delzone", zonename);
7606224092Sdougb
7607224092Sdougb	result = ISC_R_SUCCESS;
7608224092Sdougb
7609224092Sdougb cleanup:
7610224092Sdougb	if (ifp != NULL)
7611224092Sdougb		isc_stdio_close(ifp);
7612224092Sdougb	if (ofp != NULL) {
7613224092Sdougb		isc_stdio_close(ofp);
7614224092Sdougb		isc_file_remove(tmpname);
7615224092Sdougb	}
7616224092Sdougb	if (tmpname != NULL)
7617224092Sdougb		isc_mem_free(server->mctx, tmpname);
7618224092Sdougb	if (zone != NULL)
7619224092Sdougb		dns_zone_detach(&zone);
7620224092Sdougb
7621224092Sdougb	return (result);
7622224092Sdougb}
7623224092Sdougb
7624224092Sdougbstatic void
7625225361Sdougbnewzone_cfgctx_destroy(void **cfgp) {
7626224092Sdougb	struct cfg_context *cfg;
7627224092Sdougb
7628224092Sdougb	REQUIRE(cfgp != NULL && *cfgp != NULL);
7629225361Sdougb
7630224092Sdougb	cfg = *cfgp;
7631224092Sdougb
7632225361Sdougb	if (cfg->actx != NULL)
7633225361Sdougb		cfg_aclconfctx_detach(&cfg->actx);
7634225361Sdougb
7635224092Sdougb	if (cfg->parser != NULL) {
7636224092Sdougb		if (cfg->config != NULL)
7637224092Sdougb			cfg_obj_destroy(cfg->parser, &cfg->config);
7638224092Sdougb		cfg_parser_destroy(&cfg->parser);
7639224092Sdougb	}
7640225361Sdougb	if (cfg->nzparser != NULL) {
7641225361Sdougb		if (cfg->nzconfig != NULL)
7642225361Sdougb			cfg_obj_destroy(cfg->nzparser, &cfg->nzconfig);
7643225361Sdougb		cfg_parser_destroy(&cfg->nzparser);
7644225361Sdougb	}
7645224092Sdougb
7646225361Sdougb	isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg));
7647224092Sdougb	*cfgp = NULL;
7648224092Sdougb}
7649