1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2014  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
18254897Serwin/* $Id$ */
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>
37254897Serwin#include <isc/hex.h>
38193149Sdougb#include <isc/httpd.h>
39135446Strhodes#include <isc/lex.h>
40135446Strhodes#include <isc/parseint.h>
41186462Sdougb#include <isc/portset.h>
42135446Strhodes#include <isc/print.h>
43254897Serwin#include <isc/refcount.h>
44135446Strhodes#include <isc/resource.h>
45224092Sdougb#include <isc/sha2.h>
46186462Sdougb#include <isc/socket.h>
47224092Sdougb#include <isc/stat.h>
48193149Sdougb#include <isc/stats.h>
49135446Strhodes#include <isc/stdio.h>
50135446Strhodes#include <isc/string.h>
51135446Strhodes#include <isc/task.h>
52135446Strhodes#include <isc/timer.h>
53135446Strhodes#include <isc/util.h>
54193149Sdougb#include <isc/xml.h>
55135446Strhodes
56135446Strhodes#include <isccfg/namedconf.h>
57135446Strhodes
58135446Strhodes#include <bind9/check.h>
59135446Strhodes
60170222Sdougb#include <dns/acache.h>
61135446Strhodes#include <dns/adb.h>
62135446Strhodes#include <dns/cache.h>
63135446Strhodes#include <dns/db.h>
64135446Strhodes#include <dns/dispatch.h>
65170222Sdougb#include <dns/dlz.h>
66224092Sdougb#include <dns/dns64.h>
67135446Strhodes#include <dns/forward.h>
68135446Strhodes#include <dns/journal.h>
69135446Strhodes#include <dns/keytable.h>
70224092Sdougb#include <dns/keyvalues.h>
71170222Sdougb#include <dns/lib.h>
72135446Strhodes#include <dns/master.h>
73135446Strhodes#include <dns/masterdump.h>
74135446Strhodes#include <dns/order.h>
75135446Strhodes#include <dns/peer.h>
76135446Strhodes#include <dns/portlist.h>
77254897Serwin#include <dns/private.h>
78193149Sdougb#include <dns/rbt.h>
79135446Strhodes#include <dns/rdataclass.h>
80262706Serwin#include <dns/rdatalist.h>
81135446Strhodes#include <dns/rdataset.h>
82135446Strhodes#include <dns/rdatastruct.h>
83135446Strhodes#include <dns/resolver.h>
84135446Strhodes#include <dns/rootns.h>
85135446Strhodes#include <dns/secalg.h>
86262706Serwin#include <dns/soa.h>
87135446Strhodes#include <dns/stats.h>
88135446Strhodes#include <dns/tkey.h>
89193149Sdougb#include <dns/tsig.h>
90135446Strhodes#include <dns/view.h>
91135446Strhodes#include <dns/zone.h>
92135446Strhodes#include <dns/zt.h>
93135446Strhodes
94135446Strhodes#include <dst/dst.h>
95135446Strhodes#include <dst/result.h>
96135446Strhodes
97135446Strhodes#include <named/client.h>
98135446Strhodes#include <named/config.h>
99135446Strhodes#include <named/control.h>
100135446Strhodes#include <named/interfacemgr.h>
101135446Strhodes#include <named/log.h>
102135446Strhodes#include <named/logconf.h>
103135446Strhodes#include <named/lwresd.h>
104135446Strhodes#include <named/main.h>
105135446Strhodes#include <named/os.h>
106135446Strhodes#include <named/server.h>
107193149Sdougb#include <named/statschannel.h>
108135446Strhodes#include <named/tkeyconf.h>
109135446Strhodes#include <named/tsigconf.h>
110135446Strhodes#include <named/zoneconf.h>
111153816Sdougb#ifdef HAVE_LIBSCF
112153816Sdougb#include <named/ns_smf_globals.h>
113153816Sdougb#include <stdlib.h>
114153816Sdougb#endif
115135446Strhodes
116224092Sdougb#ifndef PATH_MAX
117224092Sdougb#define PATH_MAX 1024
118224092Sdougb#endif
119224092Sdougb
120254897Serwin#ifndef SIZE_MAX
121254897Serwin#define SIZE_MAX ((size_t)-1)
122254897Serwin#endif
123254897Serwin
124170222Sdougb/*%
125135446Strhodes * Check an operation for failure.  Assumes that the function
126135446Strhodes * using it has a 'result' variable and a 'cleanup' label.
127135446Strhodes */
128135446Strhodes#define CHECK(op) \
129193149Sdougb	do { result = (op);					 \
130193149Sdougb	       if (result != ISC_R_SUCCESS) goto cleanup;	 \
131135446Strhodes	} while (0)
132135446Strhodes
133135446Strhodes#define CHECKM(op, msg) \
134193149Sdougb	do { result = (op);					  \
135135446Strhodes	       if (result != ISC_R_SUCCESS) {			  \
136135446Strhodes			isc_log_write(ns_g_lctx,		  \
137135446Strhodes				      NS_LOGCATEGORY_GENERAL,	  \
138135446Strhodes				      NS_LOGMODULE_SERVER,	  \
139135446Strhodes				      ISC_LOG_ERROR,		  \
140135446Strhodes				      "%s: %s", msg,		  \
141135446Strhodes				      isc_result_totext(result)); \
142135446Strhodes			goto cleanup;				  \
143135446Strhodes		}						  \
144135446Strhodes	} while (0)						  \
145135446Strhodes
146135446Strhodes#define CHECKMF(op, msg, file) \
147193149Sdougb	do { result = (op);					  \
148135446Strhodes	       if (result != ISC_R_SUCCESS) {			  \
149135446Strhodes			isc_log_write(ns_g_lctx,		  \
150135446Strhodes				      NS_LOGCATEGORY_GENERAL,	  \
151135446Strhodes				      NS_LOGMODULE_SERVER,	  \
152135446Strhodes				      ISC_LOG_ERROR,		  \
153135446Strhodes				      "%s '%s': %s", msg, file,	  \
154135446Strhodes				      isc_result_totext(result)); \
155135446Strhodes			goto cleanup;				  \
156135446Strhodes		}						  \
157135446Strhodes	} while (0)						  \
158135446Strhodes
159135446Strhodes#define CHECKFATAL(op, msg) \
160193149Sdougb	do { result = (op);					  \
161135446Strhodes	       if (result != ISC_R_SUCCESS)			  \
162135446Strhodes			fatal(msg, result);			  \
163135446Strhodes	} while (0)						  \
164135446Strhodes
165224092Sdougb/*%
166224092Sdougb * Maximum ADB size for views that share a cache.  Use this limit to suppress
167224092Sdougb * the total of memory footprint, which should be the main reason for sharing
168224092Sdougb * a cache.  Only effective when a finite max-cache-size is specified.
169224092Sdougb * This is currently defined to be 8MB.
170224092Sdougb */
171254402Serwin#define MAX_ADB_SIZE_FOR_CACHESHARE	8388608U
172224092Sdougb
173135446Strhodesstruct ns_dispatch {
174135446Strhodes	isc_sockaddr_t			addr;
175135446Strhodes	unsigned int			dispatchgen;
176135446Strhodes	dns_dispatch_t			*dispatch;
177135446Strhodes	ISC_LINK(struct ns_dispatch)	link;
178135446Strhodes};
179135446Strhodes
180224092Sdougbstruct ns_cache {
181224092Sdougb	dns_cache_t			*cache;
182224092Sdougb	dns_view_t			*primaryview;
183224092Sdougb	isc_boolean_t			needflush;
184224092Sdougb	isc_boolean_t			adbsizeadjusted;
185224092Sdougb	ISC_LINK(ns_cache_t)		link;
186224092Sdougb};
187224092Sdougb
188135446Strhodesstruct dumpcontext {
189135446Strhodes	isc_mem_t			*mctx;
190135446Strhodes	isc_boolean_t			dumpcache;
191135446Strhodes	isc_boolean_t			dumpzones;
192135446Strhodes	FILE				*fp;
193135446Strhodes	ISC_LIST(struct viewlistentry)	viewlist;
194135446Strhodes	struct viewlistentry		*view;
195135446Strhodes	struct zonelistentry		*zone;
196135446Strhodes	dns_dumpctx_t			*mdctx;
197135446Strhodes	dns_db_t			*db;
198135446Strhodes	dns_db_t			*cache;
199135446Strhodes	isc_task_t			*task;
200135446Strhodes	dns_dbversion_t			*version;
201135446Strhodes};
202135446Strhodes
203135446Strhodesstruct viewlistentry {
204135446Strhodes	dns_view_t			*view;
205135446Strhodes	ISC_LINK(struct viewlistentry)	link;
206135446Strhodes	ISC_LIST(struct zonelistentry)	zonelist;
207135446Strhodes};
208135446Strhodes
209135446Strhodesstruct zonelistentry {
210135446Strhodes	dns_zone_t			*zone;
211135446Strhodes	ISC_LINK(struct zonelistentry)	link;
212135446Strhodes};
213135446Strhodes
214224092Sdougb/*%
215224092Sdougb * Configuration context to retain for each view that allows
216225361Sdougb * new zones to be added at runtime.
217224092Sdougb */
218224092Sdougbstruct cfg_context {
219224092Sdougb	isc_mem_t *			mctx;
220225361Sdougb	cfg_parser_t *			parser;
221224092Sdougb	cfg_obj_t *			config;
222225361Sdougb	cfg_parser_t *			nzparser;
223225361Sdougb	cfg_obj_t *			nzconfig;
224225361Sdougb	cfg_aclconfctx_t *		actx;
225224092Sdougb};
226224092Sdougb
227254897Serwin/*%
228254897Serwin * Holds state information for the initial zone loading process.
229254897Serwin * Uses the isc_refcount structure to count the number of views
230254897Serwin * with pending zone loads, dereferencing as each view finishes.
231254897Serwin */
232254897Serwintypedef struct {
233254897Serwin		ns_server_t *server;
234254897Serwin		isc_refcount_t refs;
235254897Serwin} ns_zoneload_t;
236254897Serwin
237170222Sdougb/*
238170222Sdougb * These zones should not leak onto the Internet.
239170222Sdougb */
240254897Serwinconst char *empty_zones[] = {
241170222Sdougb	/* RFC 1918 */
242254897Serwin	"10.IN-ADDR.ARPA",
243254897Serwin	"16.172.IN-ADDR.ARPA",
244254897Serwin	"17.172.IN-ADDR.ARPA",
245254897Serwin	"18.172.IN-ADDR.ARPA",
246254897Serwin	"19.172.IN-ADDR.ARPA",
247254897Serwin	"20.172.IN-ADDR.ARPA",
248254897Serwin	"21.172.IN-ADDR.ARPA",
249254897Serwin	"22.172.IN-ADDR.ARPA",
250254897Serwin	"23.172.IN-ADDR.ARPA",
251254897Serwin	"24.172.IN-ADDR.ARPA",
252254897Serwin	"25.172.IN-ADDR.ARPA",
253254897Serwin	"26.172.IN-ADDR.ARPA",
254254897Serwin	"27.172.IN-ADDR.ARPA",
255254897Serwin	"28.172.IN-ADDR.ARPA",
256254897Serwin	"29.172.IN-ADDR.ARPA",
257254897Serwin	"30.172.IN-ADDR.ARPA",
258254897Serwin	"31.172.IN-ADDR.ARPA",
259254897Serwin	"168.192.IN-ADDR.ARPA",
260170222Sdougb
261254402Serwin	/* RFC 6598 */
262254897Serwin	"64.100.IN-ADDR.ARPA",
263254897Serwin	"65.100.IN-ADDR.ARPA",
264254897Serwin	"66.100.IN-ADDR.ARPA",
265254897Serwin	"67.100.IN-ADDR.ARPA",
266254897Serwin	"68.100.IN-ADDR.ARPA",
267254897Serwin	"69.100.IN-ADDR.ARPA",
268254897Serwin	"70.100.IN-ADDR.ARPA",
269254897Serwin	"71.100.IN-ADDR.ARPA",
270254897Serwin	"72.100.IN-ADDR.ARPA",
271254897Serwin	"73.100.IN-ADDR.ARPA",
272254897Serwin	"74.100.IN-ADDR.ARPA",
273254897Serwin	"75.100.IN-ADDR.ARPA",
274254897Serwin	"76.100.IN-ADDR.ARPA",
275254897Serwin	"77.100.IN-ADDR.ARPA",
276254897Serwin	"78.100.IN-ADDR.ARPA",
277254897Serwin	"79.100.IN-ADDR.ARPA",
278254897Serwin	"80.100.IN-ADDR.ARPA",
279254897Serwin	"81.100.IN-ADDR.ARPA",
280254897Serwin	"82.100.IN-ADDR.ARPA",
281254897Serwin	"83.100.IN-ADDR.ARPA",
282254897Serwin	"84.100.IN-ADDR.ARPA",
283254897Serwin	"85.100.IN-ADDR.ARPA",
284254897Serwin	"86.100.IN-ADDR.ARPA",
285254897Serwin	"87.100.IN-ADDR.ARPA",
286254897Serwin	"88.100.IN-ADDR.ARPA",
287254897Serwin	"89.100.IN-ADDR.ARPA",
288254897Serwin	"90.100.IN-ADDR.ARPA",
289254897Serwin	"91.100.IN-ADDR.ARPA",
290254897Serwin	"92.100.IN-ADDR.ARPA",
291254897Serwin	"93.100.IN-ADDR.ARPA",
292254897Serwin	"94.100.IN-ADDR.ARPA",
293254897Serwin	"95.100.IN-ADDR.ARPA",
294254897Serwin	"96.100.IN-ADDR.ARPA",
295254897Serwin	"97.100.IN-ADDR.ARPA",
296254897Serwin	"98.100.IN-ADDR.ARPA",
297254897Serwin	"99.100.IN-ADDR.ARPA",
298254897Serwin	"100.100.IN-ADDR.ARPA",
299254897Serwin	"101.100.IN-ADDR.ARPA",
300254897Serwin	"102.100.IN-ADDR.ARPA",
301254897Serwin	"103.100.IN-ADDR.ARPA",
302254897Serwin	"104.100.IN-ADDR.ARPA",
303254897Serwin	"105.100.IN-ADDR.ARPA",
304254897Serwin	"106.100.IN-ADDR.ARPA",
305254897Serwin	"107.100.IN-ADDR.ARPA",
306254897Serwin	"108.100.IN-ADDR.ARPA",
307254897Serwin	"109.100.IN-ADDR.ARPA",
308254897Serwin	"110.100.IN-ADDR.ARPA",
309254897Serwin	"111.100.IN-ADDR.ARPA",
310254897Serwin	"112.100.IN-ADDR.ARPA",
311254897Serwin	"113.100.IN-ADDR.ARPA",
312254897Serwin	"114.100.IN-ADDR.ARPA",
313254897Serwin	"115.100.IN-ADDR.ARPA",
314254897Serwin	"116.100.IN-ADDR.ARPA",
315254897Serwin	"117.100.IN-ADDR.ARPA",
316254897Serwin	"118.100.IN-ADDR.ARPA",
317254897Serwin	"119.100.IN-ADDR.ARPA",
318254897Serwin	"120.100.IN-ADDR.ARPA",
319254897Serwin	"121.100.IN-ADDR.ARPA",
320254897Serwin	"122.100.IN-ADDR.ARPA",
321254897Serwin	"123.100.IN-ADDR.ARPA",
322254897Serwin	"124.100.IN-ADDR.ARPA",
323254897Serwin	"125.100.IN-ADDR.ARPA",
324254897Serwin	"126.100.IN-ADDR.ARPA",
325254897Serwin	"127.100.IN-ADDR.ARPA",
326254402Serwin
327218384Sdougb	/* RFC 5735 and RFC 5737 */
328254897Serwin	"0.IN-ADDR.ARPA",	/* THIS NETWORK */
329254897Serwin	"127.IN-ADDR.ARPA",	/* LOOPBACK */
330254897Serwin	"254.169.IN-ADDR.ARPA",	/* LINK LOCAL */
331254897Serwin	"2.0.192.IN-ADDR.ARPA",	/* TEST NET */
332254897Serwin	"100.51.198.IN-ADDR.ARPA",	/* TEST NET 2 */
333254897Serwin	"113.0.203.IN-ADDR.ARPA",	/* TEST NET 3 */
334254897Serwin	"255.255.255.255.IN-ADDR.ARPA",	/* BROADCAST */
335170222Sdougb
336170222Sdougb	/* Local IPv6 Unicast Addresses */
337254897Serwin	"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",
338254897Serwin	"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",
339193149Sdougb	/* LOCALLY ASSIGNED LOCAL ADDRESS SCOPE */
340254897Serwin	"D.F.IP6.ARPA",
341254897Serwin	"8.E.F.IP6.ARPA",	/* LINK LOCAL */
342254897Serwin	"9.E.F.IP6.ARPA",	/* LINK LOCAL */
343254897Serwin	"A.E.F.IP6.ARPA",	/* LINK LOCAL */
344254897Serwin	"B.E.F.IP6.ARPA",	/* LINK LOCAL */
345170222Sdougb
346218384Sdougb	/* Example Prefix, RFC 3849. */
347254897Serwin	"8.B.D.0.1.0.0.2.IP6.ARPA",
348218384Sdougb
349254897Serwin	NULL
350170222Sdougb};
351170222Sdougb
352224092SdougbISC_PLATFORM_NORETURN_PRE static void
353224092Sdougbfatal(const char *msg, isc_result_t result) ISC_PLATFORM_NORETURN_POST;
354135446Strhodes
355135446Strhodesstatic void
356135446Strhodesns_server_reload(isc_task_t *task, isc_event_t *event);
357135446Strhodes
358135446Strhodesstatic isc_result_t
359165071Sdougbns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
360170222Sdougb			cfg_aclconfctx_t *actx,
361135446Strhodes			isc_mem_t *mctx, ns_listenelt_t **target);
362135446Strhodesstatic isc_result_t
363165071Sdougbns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
364170222Sdougb			 cfg_aclconfctx_t *actx,
365135446Strhodes			 isc_mem_t *mctx, ns_listenlist_t **target);
366135446Strhodes
367135446Strhodesstatic isc_result_t
368165071Sdougbconfigure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
369165071Sdougb		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype);
370135446Strhodes
371135446Strhodesstatic isc_result_t
372165071Sdougbconfigure_alternates(const cfg_obj_t *config, dns_view_t *view,
373165071Sdougb		     const cfg_obj_t *alternates);
374135446Strhodes
375135446Strhodesstatic isc_result_t
376165071Sdougbconfigure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
377165071Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
378224092Sdougb	       cfg_aclconfctx_t *aclconf, isc_boolean_t added);
379135446Strhodes
380224092Sdougbstatic isc_result_t
381224092Sdougbadd_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
382224092Sdougb
383135446Strhodesstatic void
384135446Strhodesend_reserved_dispatches(ns_server_t *server, isc_boolean_t all);
385135446Strhodes
386224092Sdougbstatic void
387225361Sdougbnewzone_cfgctx_destroy(void **cfgp);
388224092Sdougb
389262706Serwinstatic isc_result_t
390262706Serwinputstr(isc_buffer_t *b, const char *str);
391262706Serwin
392262706Serwinisc_result_t
393262706Serwinadd_comment(FILE *fp, const char *viewname);
394262706Serwin
395170222Sdougb/*%
396193149Sdougb * Configure a single view ACL at '*aclp'.  Get its configuration from
397193149Sdougb * 'vconfig' (for per-view configuration) and maybe from 'config'
398135446Strhodes */
399135446Strhodesstatic isc_result_t
400165071Sdougbconfigure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
401224092Sdougb		   const char *aclname, const char *acltuplename,
402224092Sdougb		   cfg_aclconfctx_t *actx, isc_mem_t *mctx, dns_acl_t **aclp)
403135446Strhodes{
404135446Strhodes	isc_result_t result;
405165071Sdougb	const cfg_obj_t *maps[3];
406165071Sdougb	const cfg_obj_t *aclobj = NULL;
407135446Strhodes	int i = 0;
408135446Strhodes
409135446Strhodes	if (*aclp != NULL)
410135446Strhodes		dns_acl_detach(aclp);
411135446Strhodes	if (vconfig != NULL)
412135446Strhodes		maps[i++] = cfg_tuple_get(vconfig, "options");
413135446Strhodes	if (config != NULL) {
414165071Sdougb		const cfg_obj_t *options = NULL;
415135446Strhodes		(void)cfg_map_get(config, "options", &options);
416135446Strhodes		if (options != NULL)
417135446Strhodes			maps[i++] = options;
418135446Strhodes	}
419135446Strhodes	maps[i] = NULL;
420135446Strhodes
421165071Sdougb	(void)ns_config_get(maps, aclname, &aclobj);
422135446Strhodes	if (aclobj == NULL)
423135446Strhodes		/*
424193149Sdougb		 * No value available.	*aclp == NULL.
425135446Strhodes		 */
426135446Strhodes		return (ISC_R_SUCCESS);
427135446Strhodes
428224092Sdougb	if (acltuplename != NULL) {
429224092Sdougb		/*
430224092Sdougb		 * If the ACL is given in an optional tuple, retrieve it.
431224092Sdougb		 * The parser should have ensured that a valid object be
432224092Sdougb		 * returned.
433224092Sdougb		 */
434224092Sdougb		aclobj = cfg_tuple_get(aclobj, acltuplename);
435224092Sdougb	}
436224092Sdougb
437170222Sdougb	result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
438193149Sdougb				    actx, mctx, 0, aclp);
439135446Strhodes
440135446Strhodes	return (result);
441135446Strhodes}
442135446Strhodes
443193149Sdougb/*%
444193149Sdougb * Configure a sortlist at '*aclp'.  Essentially the same as
445193149Sdougb * configure_view_acl() except it calls cfg_acl_fromconfig with a
446193149Sdougb * nest_level value of 2.
447193149Sdougb */
448135446Strhodesstatic isc_result_t
449193149Sdougbconfigure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config,
450193149Sdougb			cfg_aclconfctx_t *actx, isc_mem_t *mctx,
451193149Sdougb			dns_acl_t **aclp)
452193149Sdougb{
453193149Sdougb	isc_result_t result;
454193149Sdougb	const cfg_obj_t *maps[3];
455193149Sdougb	const cfg_obj_t *aclobj = NULL;
456193149Sdougb	int i = 0;
457193149Sdougb
458193149Sdougb	if (*aclp != NULL)
459193149Sdougb		dns_acl_detach(aclp);
460193149Sdougb	if (vconfig != NULL)
461193149Sdougb		maps[i++] = cfg_tuple_get(vconfig, "options");
462193149Sdougb	if (config != NULL) {
463193149Sdougb		const cfg_obj_t *options = NULL;
464193149Sdougb		(void)cfg_map_get(config, "options", &options);
465193149Sdougb		if (options != NULL)
466193149Sdougb			maps[i++] = options;
467193149Sdougb	}
468193149Sdougb	maps[i] = NULL;
469193149Sdougb
470193149Sdougb	(void)ns_config_get(maps, "sortlist", &aclobj);
471193149Sdougb	if (aclobj == NULL)
472193149Sdougb		return (ISC_R_SUCCESS);
473193149Sdougb
474193149Sdougb	/*
475193149Sdougb	 * Use a nest level of 3 for the "top level" of the sortlist;
476193149Sdougb	 * this means each entry in the top three levels will be stored
477193149Sdougb	 * as lists of separate, nested ACLs, rather than merged together
478193149Sdougb	 * into IP tables as is usually done with ACLs.
479193149Sdougb	 */
480193149Sdougb	result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
481193149Sdougb				    actx, mctx, 3, aclp);
482193149Sdougb
483193149Sdougb	return (result);
484193149Sdougb}
485193149Sdougb
486193149Sdougbstatic isc_result_t
487224092Sdougbconfigure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
488224092Sdougb			 const char *confname, const char *conftuplename,
489224092Sdougb			 isc_mem_t *mctx, dns_rbt_t **rbtp)
490135446Strhodes{
491224092Sdougb	isc_result_t result;
492224092Sdougb	const cfg_obj_t *maps[3];
493224092Sdougb	const cfg_obj_t *obj = NULL;
494224092Sdougb	const cfg_listelt_t *element;
495224092Sdougb	int i = 0;
496224092Sdougb	dns_fixedname_t fixed;
497224092Sdougb	dns_name_t *name;
498224092Sdougb	isc_buffer_t b;
499224092Sdougb	const char *str;
500224092Sdougb	const cfg_obj_t *nameobj;
501224092Sdougb
502224092Sdougb	if (*rbtp != NULL)
503224092Sdougb		dns_rbt_destroy(rbtp);
504224092Sdougb	if (vconfig != NULL)
505224092Sdougb		maps[i++] = cfg_tuple_get(vconfig, "options");
506224092Sdougb	if (config != NULL) {
507224092Sdougb		const cfg_obj_t *options = NULL;
508224092Sdougb		(void)cfg_map_get(config, "options", &options);
509224092Sdougb		if (options != NULL)
510224092Sdougb			maps[i++] = options;
511224092Sdougb	}
512224092Sdougb	maps[i] = NULL;
513224092Sdougb
514224092Sdougb	(void)ns_config_get(maps, confname, &obj);
515224092Sdougb	if (obj == NULL)
516224092Sdougb		/*
517224092Sdougb		 * No value available.	*rbtp == NULL.
518224092Sdougb		 */
519224092Sdougb		return (ISC_R_SUCCESS);
520224092Sdougb
521224092Sdougb	if (conftuplename != NULL) {
522224092Sdougb		obj = cfg_tuple_get(obj, conftuplename);
523224092Sdougb		if (cfg_obj_isvoid(obj))
524224092Sdougb			return (ISC_R_SUCCESS);
525224092Sdougb	}
526224092Sdougb
527224092Sdougb	result = dns_rbt_create(mctx, NULL, NULL, rbtp);
528224092Sdougb	if (result != ISC_R_SUCCESS)
529224092Sdougb		return (result);
530224092Sdougb
531224092Sdougb	dns_fixedname_init(&fixed);
532224092Sdougb	name = dns_fixedname_name(&fixed);
533224092Sdougb	for (element = cfg_list_first(obj);
534224092Sdougb	     element != NULL;
535224092Sdougb	     element = cfg_list_next(element)) {
536224092Sdougb		nameobj = cfg_listelt_value(element);
537224092Sdougb		str = cfg_obj_asstring(nameobj);
538254402Serwin		isc_buffer_constinit(&b, str, strlen(str));
539224092Sdougb		isc_buffer_add(&b, strlen(str));
540224092Sdougb		CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
541224092Sdougb		/*
542224092Sdougb		 * We don't need the node data, but need to set dummy data to
543224092Sdougb		 * avoid a partial match with an empty node.  For example, if
544224092Sdougb		 * we have foo.example.com and bar.example.com, we'd get a match
545224092Sdougb		 * for baz.example.com, which is not the expected result.
546224092Sdougb		 * We simply use (void *)1 as the dummy data.
547224092Sdougb		 */
548224092Sdougb		result = dns_rbt_addname(*rbtp, name, (void *)1);
549224092Sdougb		if (result != ISC_R_SUCCESS) {
550224092Sdougb			cfg_obj_log(nameobj, ns_g_lctx, ISC_LOG_ERROR,
551224092Sdougb				    "failed to add %s for %s: %s",
552224092Sdougb				    str, confname, isc_result_totext(result));
553224092Sdougb			goto cleanup;
554224092Sdougb		}
555224092Sdougb
556224092Sdougb	}
557224092Sdougb
558224092Sdougb	return (result);
559224092Sdougb
560224092Sdougb  cleanup:
561224092Sdougb	dns_rbt_destroy(rbtp);
562224092Sdougb	return (result);
563224092Sdougb
564224092Sdougb}
565224092Sdougb
566224092Sdougbstatic isc_result_t
567224092Sdougbdstkey_fromconfig(const cfg_obj_t *vconfig, const cfg_obj_t *key,
568224092Sdougb		  isc_boolean_t managed, dst_key_t **target, isc_mem_t *mctx)
569224092Sdougb{
570135446Strhodes	dns_rdataclass_t viewclass;
571135446Strhodes	dns_rdata_dnskey_t keystruct;
572135446Strhodes	isc_uint32_t flags, proto, alg;
573165071Sdougb	const char *keystr, *keynamestr;
574135446Strhodes	unsigned char keydata[4096];
575135446Strhodes	isc_buffer_t keydatabuf;
576135446Strhodes	unsigned char rrdata[4096];
577135446Strhodes	isc_buffer_t rrdatabuf;
578135446Strhodes	isc_region_t r;
579135446Strhodes	dns_fixedname_t fkeyname;
580135446Strhodes	dns_name_t *keyname;
581135446Strhodes	isc_buffer_t namebuf;
582135446Strhodes	isc_result_t result;
583135446Strhodes	dst_key_t *dstkey = NULL;
584135446Strhodes
585224092Sdougb	INSIST(target != NULL && *target == NULL);
586224092Sdougb
587135446Strhodes	flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags"));
588135446Strhodes	proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol"));
589135446Strhodes	alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm"));
590135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
591135446Strhodes	keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
592135446Strhodes
593224092Sdougb	if (managed) {
594224092Sdougb		const char *initmethod;
595224092Sdougb		initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init"));
596224092Sdougb
597224092Sdougb		if (strcasecmp(initmethod, "initial-key") != 0) {
598224092Sdougb			cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
599224092Sdougb				    "managed key '%s': "
600224092Sdougb				    "invalid initialization method '%s'",
601224092Sdougb				    keynamestr, initmethod);
602224092Sdougb			result = ISC_R_FAILURE;
603224092Sdougb			goto cleanup;
604224092Sdougb		}
605224092Sdougb	}
606224092Sdougb
607135446Strhodes	if (vconfig == NULL)
608135446Strhodes		viewclass = dns_rdataclass_in;
609135446Strhodes	else {
610165071Sdougb		const cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class");
611135446Strhodes		CHECK(ns_config_getclass(classobj, dns_rdataclass_in,
612135446Strhodes					 &viewclass));
613135446Strhodes	}
614135446Strhodes	keystruct.common.rdclass = viewclass;
615135446Strhodes	keystruct.common.rdtype = dns_rdatatype_dnskey;
616135446Strhodes	/*
617135446Strhodes	 * The key data in keystruct is not dynamically allocated.
618135446Strhodes	 */
619135446Strhodes	keystruct.mctx = NULL;
620135446Strhodes
621135446Strhodes	ISC_LINK_INIT(&keystruct.common, link);
622135446Strhodes
623135446Strhodes	if (flags > 0xffff)
624135446Strhodes		CHECKM(ISC_R_RANGE, "key flags");
625135446Strhodes	if (proto > 0xff)
626135446Strhodes		CHECKM(ISC_R_RANGE, "key protocol");
627135446Strhodes	if (alg > 0xff)
628135446Strhodes		CHECKM(ISC_R_RANGE, "key algorithm");
629135446Strhodes	keystruct.flags = (isc_uint16_t)flags;
630135446Strhodes	keystruct.protocol = (isc_uint8_t)proto;
631135446Strhodes	keystruct.algorithm = (isc_uint8_t)alg;
632135446Strhodes
633135446Strhodes	isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
634135446Strhodes	isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
635135446Strhodes
636135446Strhodes	keystr = cfg_obj_asstring(cfg_tuple_get(key, "key"));
637135446Strhodes	CHECK(isc_base64_decodestring(keystr, &keydatabuf));
638135446Strhodes	isc_buffer_usedregion(&keydatabuf, &r);
639135446Strhodes	keystruct.datalen = r.length;
640135446Strhodes	keystruct.data = r.base;
641135446Strhodes
642170222Sdougb	if ((keystruct.algorithm == DST_ALG_RSASHA1 ||
643170222Sdougb	     keystruct.algorithm == DST_ALG_RSAMD5) &&
644170222Sdougb	    r.length > 1 && r.base[0] == 1 && r.base[1] == 3)
645170222Sdougb		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
646224092Sdougb			    "%s key '%s' has a weak exponent",
647224092Sdougb			    managed ? "managed" : "trusted",
648170222Sdougb			    keynamestr);
649170222Sdougb
650135446Strhodes	CHECK(dns_rdata_fromstruct(NULL,
651135446Strhodes				   keystruct.common.rdclass,
652135446Strhodes				   keystruct.common.rdtype,
653135446Strhodes				   &keystruct, &rrdatabuf));
654135446Strhodes	dns_fixedname_init(&fkeyname);
655254402Serwin	isc_buffer_constinit(&namebuf, keynamestr, strlen(keynamestr));
656135446Strhodes	isc_buffer_add(&namebuf, strlen(keynamestr));
657224092Sdougb	CHECK(dns_name_fromtext(keyname, &namebuf, dns_rootname, 0, NULL));
658135446Strhodes	CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf,
659135446Strhodes			      mctx, &dstkey));
660135446Strhodes
661224092Sdougb	*target = dstkey;
662135446Strhodes	return (ISC_R_SUCCESS);
663135446Strhodes
664135446Strhodes cleanup:
665135446Strhodes	if (result == DST_R_NOCRYPTO) {
666135446Strhodes		cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
667224092Sdougb			    "ignoring %s key for '%s': no crypto support",
668224092Sdougb			    managed ? "managed" : "trusted",
669135446Strhodes			    keynamestr);
670224092Sdougb	} else if (result == DST_R_UNSUPPORTEDALG) {
671224092Sdougb		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
672224092Sdougb			    "skipping %s key for '%s': %s",
673224092Sdougb			    managed ? "managed" : "trusted",
674224092Sdougb			    keynamestr, isc_result_totext(result));
675135446Strhodes	} else {
676135446Strhodes		cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
677224092Sdougb			    "configuring %s key for '%s': %s",
678224092Sdougb			    managed ? "managed" : "trusted",
679135446Strhodes			    keynamestr, isc_result_totext(result));
680135446Strhodes		result = ISC_R_FAILURE;
681135446Strhodes	}
682135446Strhodes
683135446Strhodes	if (dstkey != NULL)
684135446Strhodes		dst_key_free(&dstkey);
685135446Strhodes
686135446Strhodes	return (result);
687135446Strhodes}
688135446Strhodes
689224092Sdougbstatic isc_result_t
690224092Sdougbload_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig,
691224092Sdougb	       dns_view_t *view, isc_boolean_t managed,
692224092Sdougb	       dns_name_t *keyname, isc_mem_t *mctx)
693224092Sdougb{
694224092Sdougb	const cfg_listelt_t *elt, *elt2;
695224092Sdougb	const cfg_obj_t *key, *keylist;
696224092Sdougb	dst_key_t *dstkey = NULL;
697224092Sdougb	isc_result_t result;
698224092Sdougb	dns_keytable_t *secroots = NULL;
699224092Sdougb
700224092Sdougb	CHECK(dns_view_getsecroots(view, &secroots));
701224092Sdougb
702224092Sdougb	for (elt = cfg_list_first(keys);
703224092Sdougb	     elt != NULL;
704224092Sdougb	     elt = cfg_list_next(elt)) {
705224092Sdougb		keylist = cfg_listelt_value(elt);
706224092Sdougb
707224092Sdougb		for (elt2 = cfg_list_first(keylist);
708224092Sdougb		     elt2 != NULL;
709224092Sdougb		     elt2 = cfg_list_next(elt2)) {
710224092Sdougb			key = cfg_listelt_value(elt2);
711224092Sdougb			result = dstkey_fromconfig(vconfig, key, managed,
712224092Sdougb						   &dstkey, mctx);
713224092Sdougb			if (result ==  DST_R_UNSUPPORTEDALG) {
714224092Sdougb				result = ISC_R_SUCCESS;
715224092Sdougb				continue;
716224092Sdougb			}
717224092Sdougb			if (result != ISC_R_SUCCESS)
718224092Sdougb				goto cleanup;
719224092Sdougb
720224092Sdougb			/*
721224092Sdougb			 * If keyname was specified, we only add that key.
722224092Sdougb			 */
723224092Sdougb			if (keyname != NULL &&
724224092Sdougb			    !dns_name_equal(keyname, dst_key_name(dstkey)))
725224092Sdougb			{
726224092Sdougb				dst_key_free(&dstkey);
727224092Sdougb				continue;
728224092Sdougb			}
729224092Sdougb
730224092Sdougb			CHECK(dns_keytable_add(secroots, managed, &dstkey));
731224092Sdougb		}
732224092Sdougb	}
733224092Sdougb
734224092Sdougb cleanup:
735224092Sdougb	if (dstkey != NULL)
736224092Sdougb		dst_key_free(&dstkey);
737224092Sdougb	if (secroots != NULL)
738224092Sdougb		dns_keytable_detach(&secroots);
739224092Sdougb	if (result == DST_R_NOCRYPTO)
740224092Sdougb		result = ISC_R_SUCCESS;
741224092Sdougb	return (result);
742224092Sdougb}
743224092Sdougb
744170222Sdougb/*%
745224092Sdougb * Configure DNSSEC keys for a view.
746135446Strhodes *
747135446Strhodes * The per-view configuration values and the server-global defaults are read
748224092Sdougb * from 'vconfig' and 'config'.
749135446Strhodes */
750135446Strhodesstatic isc_result_t
751224092Sdougbconfigure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig,
752224092Sdougb			  const cfg_obj_t *config, const cfg_obj_t *bindkeys,
753224092Sdougb			  isc_boolean_t auto_dlv, isc_boolean_t auto_root,
754224092Sdougb			  isc_mem_t *mctx)
755135446Strhodes{
756224092Sdougb	isc_result_t result = ISC_R_SUCCESS;
757224092Sdougb	const cfg_obj_t *view_keys = NULL;
758224092Sdougb	const cfg_obj_t *global_keys = NULL;
759224092Sdougb	const cfg_obj_t *view_managed_keys = NULL;
760224092Sdougb	const cfg_obj_t *global_managed_keys = NULL;
761224092Sdougb	const cfg_obj_t *maps[4];
762165071Sdougb	const cfg_obj_t *voptions = NULL;
763224092Sdougb	const cfg_obj_t *options = NULL;
764224092Sdougb	const cfg_obj_t *obj = NULL;
765224092Sdougb	const char *directory;
766224092Sdougb	int i = 0;
767135446Strhodes
768224092Sdougb	/* We don't need trust anchors for the _bind view */
769224092Sdougb	if (strcmp(view->name, "_bind") == 0 &&
770224092Sdougb	    view->rdclass == dns_rdataclass_chaos) {
771224092Sdougb		return (ISC_R_SUCCESS);
772224092Sdougb	}
773135446Strhodes
774224092Sdougb	if (vconfig != NULL) {
775135446Strhodes		voptions = cfg_tuple_get(vconfig, "options");
776224092Sdougb		if (voptions != NULL) {
777224092Sdougb			(void) cfg_map_get(voptions, "trusted-keys",
778224092Sdougb					   &view_keys);
779224092Sdougb			(void) cfg_map_get(voptions, "managed-keys",
780224092Sdougb					   &view_managed_keys);
781224092Sdougb			maps[i++] = voptions;
782224092Sdougb		}
783224092Sdougb	}
784135446Strhodes
785224092Sdougb	if (config != NULL) {
786224092Sdougb		(void)cfg_map_get(config, "trusted-keys", &global_keys);
787224092Sdougb		(void)cfg_map_get(config, "managed-keys", &global_managed_keys);
788224092Sdougb		(void)cfg_map_get(config, "options", &options);
789224092Sdougb		if (options != NULL) {
790224092Sdougb			maps[i++] = options;
791224092Sdougb		}
792224092Sdougb	}
793135446Strhodes
794224092Sdougb	maps[i++] = ns_g_defaults;
795224092Sdougb	maps[i] = NULL;
796224092Sdougb
797224092Sdougb	result = dns_view_initsecroots(view, mctx);
798224092Sdougb	if (result != ISC_R_SUCCESS) {
799224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
800224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
801224092Sdougb			      "couldn't create keytable");
802224092Sdougb		return (ISC_R_UNEXPECTED);
803224092Sdougb	}
804224092Sdougb
805224092Sdougb	if (auto_dlv && view->rdclass == dns_rdataclass_in) {
806224092Sdougb		const cfg_obj_t *builtin_keys = NULL;
807224092Sdougb		const cfg_obj_t *builtin_managed_keys = NULL;
808224092Sdougb
809224092Sdougb		isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
810224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
811224092Sdougb			      "using built-in DLV key for view %s",
812224092Sdougb			      view->name);
813224092Sdougb
814224092Sdougb		/*
815224092Sdougb		 * If bind.keys exists, it overrides the managed-keys
816224092Sdougb		 * clause hard-coded in ns_g_config.
817224092Sdougb		 */
818224092Sdougb		if (bindkeys != NULL) {
819224092Sdougb			(void)cfg_map_get(bindkeys, "trusted-keys",
820224092Sdougb					  &builtin_keys);
821224092Sdougb			(void)cfg_map_get(bindkeys, "managed-keys",
822224092Sdougb					  &builtin_managed_keys);
823224092Sdougb		} else {
824224092Sdougb			(void)cfg_map_get(ns_g_config, "trusted-keys",
825224092Sdougb					  &builtin_keys);
826224092Sdougb			(void)cfg_map_get(ns_g_config, "managed-keys",
827224092Sdougb					  &builtin_managed_keys);
828135446Strhodes		}
829224092Sdougb
830224092Sdougb		if (builtin_keys != NULL)
831224092Sdougb			CHECK(load_view_keys(builtin_keys, vconfig, view,
832224092Sdougb					     ISC_FALSE, view->dlv, mctx));
833224092Sdougb		if (builtin_managed_keys != NULL)
834224092Sdougb			CHECK(load_view_keys(builtin_managed_keys, vconfig,
835224092Sdougb					     view, ISC_TRUE, view->dlv, mctx));
836135446Strhodes	}
837135446Strhodes
838224092Sdougb	if (auto_root && view->rdclass == dns_rdataclass_in) {
839224092Sdougb		const cfg_obj_t *builtin_keys = NULL;
840224092Sdougb		const cfg_obj_t *builtin_managed_keys = NULL;
841186462Sdougb
842224092Sdougb		isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
843224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
844224092Sdougb			      "using built-in root key for view %s",
845224092Sdougb			      view->name);
846224092Sdougb
847224092Sdougb		/*
848224092Sdougb		 * If bind.keys exists, it overrides the managed-keys
849224092Sdougb		 * clause hard-coded in ns_g_config.
850224092Sdougb		 */
851224092Sdougb		if (bindkeys != NULL) {
852224092Sdougb			(void)cfg_map_get(bindkeys, "trusted-keys",
853224092Sdougb					  &builtin_keys);
854224092Sdougb			(void)cfg_map_get(bindkeys, "managed-keys",
855224092Sdougb					  &builtin_managed_keys);
856224092Sdougb		} else {
857224092Sdougb			(void)cfg_map_get(ns_g_config, "trusted-keys",
858224092Sdougb					  &builtin_keys);
859224092Sdougb			(void)cfg_map_get(ns_g_config, "managed-keys",
860224092Sdougb					  &builtin_managed_keys);
861224092Sdougb		}
862224092Sdougb
863224092Sdougb		if (builtin_keys != NULL)
864224092Sdougb			CHECK(load_view_keys(builtin_keys, vconfig, view,
865224092Sdougb					     ISC_FALSE, dns_rootname, mctx));
866224092Sdougb		if (builtin_managed_keys != NULL)
867224092Sdougb			CHECK(load_view_keys(builtin_managed_keys, vconfig,
868224092Sdougb					     view, ISC_TRUE, dns_rootname,
869224092Sdougb					     mctx));
870224092Sdougb	}
871224092Sdougb
872224092Sdougb	CHECK(load_view_keys(view_keys, vconfig, view, ISC_FALSE,
873224092Sdougb			     NULL, mctx));
874224092Sdougb	CHECK(load_view_keys(view_managed_keys, vconfig, view, ISC_TRUE,
875224092Sdougb			     NULL, mctx));
876224092Sdougb
877224092Sdougb	if (view->rdclass == dns_rdataclass_in) {
878224092Sdougb		CHECK(load_view_keys(global_keys, vconfig, view, ISC_FALSE,
879224092Sdougb				     NULL, mctx));
880224092Sdougb		CHECK(load_view_keys(global_managed_keys, vconfig, view,
881224092Sdougb				     ISC_TRUE, NULL, mctx));
882224092Sdougb	}
883224092Sdougb
884224092Sdougb	/*
885224092Sdougb	 * Add key zone for managed-keys.
886224092Sdougb	 */
887224092Sdougb	obj = NULL;
888224092Sdougb	(void)ns_config_get(maps, "managed-keys-directory", &obj);
889254402Serwin	directory = (obj != NULL ? cfg_obj_asstring(obj) : NULL);
890254402Serwin	if (directory != NULL)
891254402Serwin		result = isc_file_isdirectory(directory);
892254402Serwin	if (result != ISC_R_SUCCESS) {
893254402Serwin		isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
894254402Serwin			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
895254402Serwin			      "invalid managed-keys-directory %s: %s",
896254402Serwin			      directory, isc_result_totext(result));
897254402Serwin		goto cleanup;
898254402Serwin
899254402Serwin	}
900224092Sdougb	CHECK(add_keydata_zone(view, directory, ns_g_mctx));
901224092Sdougb
902224092Sdougb  cleanup:
903135446Strhodes	return (result);
904135446Strhodes}
905135446Strhodes
906135446Strhodesstatic isc_result_t
907224092Sdougbmustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver) {
908165071Sdougb	const cfg_listelt_t *element;
909165071Sdougb	const cfg_obj_t *obj;
910135446Strhodes	const char *str;
911135446Strhodes	dns_fixedname_t fixed;
912135446Strhodes	dns_name_t *name;
913135446Strhodes	isc_boolean_t value;
914135446Strhodes	isc_result_t result;
915135446Strhodes	isc_buffer_t b;
916186462Sdougb
917135446Strhodes	dns_fixedname_init(&fixed);
918135446Strhodes	name = dns_fixedname_name(&fixed);
919135446Strhodes	for (element = cfg_list_first(mbs);
920135446Strhodes	     element != NULL;
921135446Strhodes	     element = cfg_list_next(element))
922135446Strhodes	{
923135446Strhodes		obj = cfg_listelt_value(element);
924135446Strhodes		str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
925254402Serwin		isc_buffer_constinit(&b, str, strlen(str));
926135446Strhodes		isc_buffer_add(&b, strlen(str));
927224092Sdougb		CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
928135446Strhodes		value = cfg_obj_asboolean(cfg_tuple_get(obj, "value"));
929135446Strhodes		CHECK(dns_resolver_setmustbesecure(resolver, name, value));
930135446Strhodes	}
931135446Strhodes
932135446Strhodes	result = ISC_R_SUCCESS;
933186462Sdougb
934135446Strhodes cleanup:
935135446Strhodes	return (result);
936135446Strhodes}
937135446Strhodes
938170222Sdougb/*%
939135446Strhodes * Get a dispatch appropriate for the resolver of a given view.
940135446Strhodes */
941135446Strhodesstatic isc_result_t
942165071Sdougbget_view_querysource_dispatch(const cfg_obj_t **maps,
943186462Sdougb			      int af, dns_dispatch_t **dispatchp,
944186462Sdougb			      isc_boolean_t is_firstview)
945135446Strhodes{
946225361Sdougb	isc_result_t result = ISC_R_FAILURE;
947135446Strhodes	dns_dispatch_t *disp;
948135446Strhodes	isc_sockaddr_t sa;
949135446Strhodes	unsigned int attrs, attrmask;
950165071Sdougb	const cfg_obj_t *obj = NULL;
951186462Sdougb	unsigned int maxdispatchbuffers;
952135446Strhodes
953135446Strhodes	switch (af) {
954135446Strhodes	case AF_INET:
955135446Strhodes		result = ns_config_get(maps, "query-source", &obj);
956135446Strhodes		INSIST(result == ISC_R_SUCCESS);
957135446Strhodes		break;
958135446Strhodes	case AF_INET6:
959135446Strhodes		result = ns_config_get(maps, "query-source-v6", &obj);
960135446Strhodes		INSIST(result == ISC_R_SUCCESS);
961135446Strhodes		break;
962135446Strhodes	default:
963135446Strhodes		INSIST(0);
964135446Strhodes	}
965135446Strhodes
966135446Strhodes	sa = *(cfg_obj_assockaddr(obj));
967135446Strhodes	INSIST(isc_sockaddr_pf(&sa) == af);
968135446Strhodes
969135446Strhodes	/*
970135446Strhodes	 * If we don't support this address family, we're done!
971135446Strhodes	 */
972135446Strhodes	switch (af) {
973135446Strhodes	case AF_INET:
974135446Strhodes		result = isc_net_probeipv4();
975135446Strhodes		break;
976135446Strhodes	case AF_INET6:
977135446Strhodes		result = isc_net_probeipv6();
978135446Strhodes		break;
979135446Strhodes	default:
980135446Strhodes		INSIST(0);
981135446Strhodes	}
982135446Strhodes	if (result != ISC_R_SUCCESS)
983135446Strhodes		return (ISC_R_SUCCESS);
984135446Strhodes
985135446Strhodes	/*
986135446Strhodes	 * Try to find a dispatcher that we can share.
987135446Strhodes	 */
988135446Strhodes	attrs = 0;
989135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
990135446Strhodes	switch (af) {
991135446Strhodes	case AF_INET:
992135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
993135446Strhodes		break;
994135446Strhodes	case AF_INET6:
995135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
996135446Strhodes		break;
997135446Strhodes	}
998186462Sdougb	if (isc_sockaddr_getport(&sa) == 0) {
999186462Sdougb		attrs |= DNS_DISPATCHATTR_EXCLUSIVE;
1000186462Sdougb		maxdispatchbuffers = 4096;
1001186462Sdougb	} else {
1002180477Sdougb		INSIST(obj != NULL);
1003186462Sdougb		if (is_firstview) {
1004186462Sdougb			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_INFO,
1005186462Sdougb				    "using specific query-source port "
1006186462Sdougb				    "suppresses port randomization and can be "
1007186462Sdougb				    "insecure.");
1008186462Sdougb		}
1009186462Sdougb		maxdispatchbuffers = 1000;
1010180477Sdougb	}
1011180477Sdougb
1012135446Strhodes	attrmask = 0;
1013135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP;
1014135446Strhodes	attrmask |= DNS_DISPATCHATTR_TCP;
1015135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4;
1016135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV6;
1017135446Strhodes
1018135446Strhodes	disp = NULL;
1019135446Strhodes	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
1020135446Strhodes				     ns_g_taskmgr, &sa, 4096,
1021186462Sdougb				     maxdispatchbuffers, 32768, 16411, 16433,
1022135446Strhodes				     attrs, attrmask, &disp);
1023135446Strhodes	if (result != ISC_R_SUCCESS) {
1024135446Strhodes		isc_sockaddr_t any;
1025135446Strhodes		char buf[ISC_SOCKADDR_FORMATSIZE];
1026135446Strhodes
1027135446Strhodes		switch (af) {
1028135446Strhodes		case AF_INET:
1029135446Strhodes			isc_sockaddr_any(&any);
1030135446Strhodes			break;
1031135446Strhodes		case AF_INET6:
1032135446Strhodes			isc_sockaddr_any6(&any);
1033135446Strhodes			break;
1034135446Strhodes		}
1035135446Strhodes		if (isc_sockaddr_equal(&sa, &any))
1036135446Strhodes			return (ISC_R_SUCCESS);
1037135446Strhodes		isc_sockaddr_format(&sa, buf, sizeof(buf));
1038135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1039135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
1040135446Strhodes			      "could not get query source dispatcher (%s)",
1041135446Strhodes			      buf);
1042135446Strhodes		return (result);
1043135446Strhodes	}
1044135446Strhodes
1045135446Strhodes	*dispatchp = disp;
1046135446Strhodes
1047135446Strhodes	return (ISC_R_SUCCESS);
1048135446Strhodes}
1049135446Strhodes
1050135446Strhodesstatic isc_result_t
1051165071Sdougbconfigure_order(dns_order_t *order, const cfg_obj_t *ent) {
1052135446Strhodes	dns_rdataclass_t rdclass;
1053135446Strhodes	dns_rdatatype_t rdtype;
1054165071Sdougb	const cfg_obj_t *obj;
1055135446Strhodes	dns_fixedname_t fixed;
1056135446Strhodes	unsigned int mode = 0;
1057135446Strhodes	const char *str;
1058135446Strhodes	isc_buffer_t b;
1059135446Strhodes	isc_result_t result;
1060143731Sdougb	isc_boolean_t addroot;
1061135446Strhodes
1062135446Strhodes	result = ns_config_getclass(cfg_tuple_get(ent, "class"),
1063135446Strhodes				    dns_rdataclass_any, &rdclass);
1064135446Strhodes	if (result != ISC_R_SUCCESS)
1065135446Strhodes		return (result);
1066135446Strhodes
1067135446Strhodes	result = ns_config_gettype(cfg_tuple_get(ent, "type"),
1068135446Strhodes				   dns_rdatatype_any, &rdtype);
1069135446Strhodes	if (result != ISC_R_SUCCESS)
1070135446Strhodes		return (result);
1071135446Strhodes
1072135446Strhodes	obj = cfg_tuple_get(ent, "name");
1073186462Sdougb	if (cfg_obj_isstring(obj))
1074135446Strhodes		str = cfg_obj_asstring(obj);
1075135446Strhodes	else
1076135446Strhodes		str = "*";
1077143731Sdougb	addroot = ISC_TF(strcmp(str, "*") == 0);
1078254402Serwin	isc_buffer_constinit(&b, str, strlen(str));
1079135446Strhodes	isc_buffer_add(&b, strlen(str));
1080135446Strhodes	dns_fixedname_init(&fixed);
1081135446Strhodes	result = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
1082224092Sdougb				   dns_rootname, 0, NULL);
1083135446Strhodes	if (result != ISC_R_SUCCESS)
1084135446Strhodes		return (result);
1085135446Strhodes
1086135446Strhodes	obj = cfg_tuple_get(ent, "ordering");
1087135446Strhodes	INSIST(cfg_obj_isstring(obj));
1088135446Strhodes	str = cfg_obj_asstring(obj);
1089135446Strhodes	if (!strcasecmp(str, "fixed"))
1090135446Strhodes		mode = DNS_RDATASETATTR_FIXEDORDER;
1091135446Strhodes	else if (!strcasecmp(str, "random"))
1092135446Strhodes		mode = DNS_RDATASETATTR_RANDOMIZE;
1093135446Strhodes	else if (!strcasecmp(str, "cyclic"))
1094135446Strhodes		mode = 0;
1095135446Strhodes	else
1096135446Strhodes		INSIST(0);
1097135446Strhodes
1098143731Sdougb	/*
1099143731Sdougb	 * "*" should match everything including the root (BIND 8 compat).
1100143731Sdougb	 * As dns_name_matcheswildcard(".", "*.") returns FALSE add a
1101165071Sdougb	 * explicit entry for "." when the name is "*".
1102143731Sdougb	 */
1103143731Sdougb	if (addroot) {
1104143731Sdougb		result = dns_order_add(order, dns_rootname,
1105143731Sdougb				       rdtype, rdclass, mode);
1106143731Sdougb		if (result != ISC_R_SUCCESS)
1107143731Sdougb			return (result);
1108143731Sdougb	}
1109143731Sdougb
1110135446Strhodes	return (dns_order_add(order, dns_fixedname_name(&fixed),
1111135446Strhodes			      rdtype, rdclass, mode));
1112135446Strhodes}
1113135446Strhodes
1114135446Strhodesstatic isc_result_t
1115165071Sdougbconfigure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
1116135446Strhodes	isc_netaddr_t na;
1117135446Strhodes	dns_peer_t *peer;
1118165071Sdougb	const cfg_obj_t *obj;
1119165071Sdougb	const char *str;
1120135446Strhodes	isc_result_t result;
1121170222Sdougb	unsigned int prefixlen;
1122135446Strhodes
1123170222Sdougb	cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen);
1124135446Strhodes
1125135446Strhodes	peer = NULL;
1126186462Sdougb	result = dns_peer_newprefix(mctx, &na, prefixlen, &peer);
1127135446Strhodes	if (result != ISC_R_SUCCESS)
1128135446Strhodes		return (result);
1129135446Strhodes
1130135446Strhodes	obj = NULL;
1131135446Strhodes	(void)cfg_map_get(cpeer, "bogus", &obj);
1132135446Strhodes	if (obj != NULL)
1133135446Strhodes		CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj)));
1134135446Strhodes
1135135446Strhodes	obj = NULL;
1136135446Strhodes	(void)cfg_map_get(cpeer, "provide-ixfr", &obj);
1137135446Strhodes	if (obj != NULL)
1138135446Strhodes		CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
1139135446Strhodes
1140135446Strhodes	obj = NULL;
1141135446Strhodes	(void)cfg_map_get(cpeer, "request-ixfr", &obj);
1142135446Strhodes	if (obj != NULL)
1143135446Strhodes		CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj)));
1144135446Strhodes
1145135446Strhodes	obj = NULL;
1146193149Sdougb	(void)cfg_map_get(cpeer, "request-nsid", &obj);
1147193149Sdougb	if (obj != NULL)
1148193149Sdougb		CHECK(dns_peer_setrequestnsid(peer, cfg_obj_asboolean(obj)));
1149193149Sdougb
1150193149Sdougb	obj = NULL;
1151135446Strhodes	(void)cfg_map_get(cpeer, "edns", &obj);
1152135446Strhodes	if (obj != NULL)
1153135446Strhodes		CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj)));
1154135446Strhodes
1155135446Strhodes	obj = NULL;
1156170222Sdougb	(void)cfg_map_get(cpeer, "edns-udp-size", &obj);
1157170222Sdougb	if (obj != NULL) {
1158170222Sdougb		isc_uint32_t udpsize = cfg_obj_asuint32(obj);
1159170222Sdougb		if (udpsize < 512)
1160170222Sdougb			udpsize = 512;
1161170222Sdougb		if (udpsize > 4096)
1162170222Sdougb			udpsize = 4096;
1163170222Sdougb		CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize));
1164170222Sdougb	}
1165170222Sdougb
1166170222Sdougb	obj = NULL;
1167170222Sdougb	(void)cfg_map_get(cpeer, "max-udp-size", &obj);
1168170222Sdougb	if (obj != NULL) {
1169170222Sdougb		isc_uint32_t udpsize = cfg_obj_asuint32(obj);
1170170222Sdougb		if (udpsize < 512)
1171170222Sdougb			udpsize = 512;
1172170222Sdougb		if (udpsize > 4096)
1173170222Sdougb			udpsize = 4096;
1174170222Sdougb		CHECK(dns_peer_setmaxudp(peer, (isc_uint16_t)udpsize));
1175170222Sdougb	}
1176170222Sdougb
1177170222Sdougb	obj = NULL;
1178135446Strhodes	(void)cfg_map_get(cpeer, "transfers", &obj);
1179135446Strhodes	if (obj != NULL)
1180135446Strhodes		CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj)));
1181135446Strhodes
1182135446Strhodes	obj = NULL;
1183135446Strhodes	(void)cfg_map_get(cpeer, "transfer-format", &obj);
1184135446Strhodes	if (obj != NULL) {
1185135446Strhodes		str = cfg_obj_asstring(obj);
1186135446Strhodes		if (strcasecmp(str, "many-answers") == 0)
1187135446Strhodes			CHECK(dns_peer_settransferformat(peer,
1188135446Strhodes							 dns_many_answers));
1189135446Strhodes		else if (strcasecmp(str, "one-answer") == 0)
1190135446Strhodes			CHECK(dns_peer_settransferformat(peer,
1191135446Strhodes							 dns_one_answer));
1192135446Strhodes		else
1193135446Strhodes			INSIST(0);
1194135446Strhodes	}
1195135446Strhodes
1196135446Strhodes	obj = NULL;
1197135446Strhodes	(void)cfg_map_get(cpeer, "keys", &obj);
1198135446Strhodes	if (obj != NULL) {
1199135446Strhodes		result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj));
1200135446Strhodes		if (result != ISC_R_SUCCESS)
1201135446Strhodes			goto cleanup;
1202135446Strhodes	}
1203135446Strhodes
1204135446Strhodes	obj = NULL;
1205170222Sdougb	if (na.family == AF_INET)
1206135446Strhodes		(void)cfg_map_get(cpeer, "transfer-source", &obj);
1207135446Strhodes	else
1208135446Strhodes		(void)cfg_map_get(cpeer, "transfer-source-v6", &obj);
1209135446Strhodes	if (obj != NULL) {
1210135446Strhodes		result = dns_peer_settransfersource(peer,
1211135446Strhodes						    cfg_obj_assockaddr(obj));
1212135446Strhodes		if (result != ISC_R_SUCCESS)
1213135446Strhodes			goto cleanup;
1214170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
1215135446Strhodes	}
1216170222Sdougb
1217170222Sdougb	obj = NULL;
1218170222Sdougb	if (na.family == AF_INET)
1219170222Sdougb		(void)cfg_map_get(cpeer, "notify-source", &obj);
1220170222Sdougb	else
1221170222Sdougb		(void)cfg_map_get(cpeer, "notify-source-v6", &obj);
1222170222Sdougb	if (obj != NULL) {
1223170222Sdougb		result = dns_peer_setnotifysource(peer,
1224170222Sdougb						  cfg_obj_assockaddr(obj));
1225170222Sdougb		if (result != ISC_R_SUCCESS)
1226170222Sdougb			goto cleanup;
1227170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
1228170222Sdougb	}
1229170222Sdougb
1230170222Sdougb	obj = NULL;
1231170222Sdougb	if (na.family == AF_INET)
1232170222Sdougb		(void)cfg_map_get(cpeer, "query-source", &obj);
1233170222Sdougb	else
1234170222Sdougb		(void)cfg_map_get(cpeer, "query-source-v6", &obj);
1235170222Sdougb	if (obj != NULL) {
1236170222Sdougb		result = dns_peer_setquerysource(peer,
1237170222Sdougb						 cfg_obj_assockaddr(obj));
1238170222Sdougb		if (result != ISC_R_SUCCESS)
1239170222Sdougb			goto cleanup;
1240170222Sdougb		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
1241170222Sdougb	}
1242170222Sdougb
1243135446Strhodes	*peerp = peer;
1244135446Strhodes	return (ISC_R_SUCCESS);
1245135446Strhodes
1246135446Strhodes cleanup:
1247135446Strhodes	dns_peer_detach(&peer);
1248135446Strhodes	return (result);
1249135446Strhodes}
1250135446Strhodes
1251135446Strhodesstatic isc_result_t
1252165071Sdougbdisable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
1253135446Strhodes	isc_result_t result;
1254165071Sdougb	const cfg_obj_t *algorithms;
1255165071Sdougb	const cfg_listelt_t *element;
1256135446Strhodes	const char *str;
1257135446Strhodes	dns_fixedname_t fixed;
1258135446Strhodes	dns_name_t *name;
1259135446Strhodes	isc_buffer_t b;
1260135446Strhodes
1261135446Strhodes	dns_fixedname_init(&fixed);
1262135446Strhodes	name = dns_fixedname_name(&fixed);
1263135446Strhodes	str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
1264254402Serwin	isc_buffer_constinit(&b, str, strlen(str));
1265135446Strhodes	isc_buffer_add(&b, strlen(str));
1266224092Sdougb	CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
1267135446Strhodes
1268135446Strhodes	algorithms = cfg_tuple_get(disabled, "algorithms");
1269135446Strhodes	for (element = cfg_list_first(algorithms);
1270135446Strhodes	     element != NULL;
1271135446Strhodes	     element = cfg_list_next(element))
1272135446Strhodes	{
1273135446Strhodes		isc_textregion_t r;
1274135446Strhodes		dns_secalg_t alg;
1275135446Strhodes
1276165071Sdougb		DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
1277135446Strhodes		r.length = strlen(r.base);
1278135446Strhodes
1279135446Strhodes		result = dns_secalg_fromtext(&alg, &r);
1280135446Strhodes		if (result != ISC_R_SUCCESS) {
1281135446Strhodes			isc_uint8_t ui;
1282135446Strhodes			result = isc_parse_uint8(&ui, r.base, 10);
1283135446Strhodes			alg = ui;
1284135446Strhodes		}
1285135446Strhodes		if (result != ISC_R_SUCCESS) {
1286135446Strhodes			cfg_obj_log(cfg_listelt_value(element),
1287135446Strhodes				    ns_g_lctx, ISC_LOG_ERROR,
1288135446Strhodes				    "invalid algorithm");
1289135446Strhodes			CHECK(result);
1290135446Strhodes		}
1291135446Strhodes		CHECK(dns_resolver_disable_algorithm(resolver, name, alg));
1292135446Strhodes	}
1293135446Strhodes cleanup:
1294135446Strhodes	return (result);
1295135446Strhodes}
1296135446Strhodes
1297170222Sdougbstatic isc_boolean_t
1298170222Sdougbon_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) {
1299170222Sdougb	const cfg_listelt_t *element;
1300170222Sdougb	dns_fixedname_t fixed;
1301170222Sdougb	dns_name_t *name;
1302170222Sdougb	isc_result_t result;
1303170222Sdougb	const cfg_obj_t *value;
1304170222Sdougb	const char *str;
1305170222Sdougb	isc_buffer_t b;
1306170222Sdougb
1307170222Sdougb	dns_fixedname_init(&fixed);
1308170222Sdougb	name = dns_fixedname_name(&fixed);
1309186462Sdougb
1310170222Sdougb	for (element = cfg_list_first(disablelist);
1311170222Sdougb	     element != NULL;
1312170222Sdougb	     element = cfg_list_next(element))
1313170222Sdougb	{
1314170222Sdougb		value = cfg_listelt_value(element);
1315170222Sdougb		str = cfg_obj_asstring(value);
1316254402Serwin		isc_buffer_constinit(&b, str, strlen(str));
1317170222Sdougb		isc_buffer_add(&b, strlen(str));
1318170222Sdougb		result = dns_name_fromtext(name, &b, dns_rootname,
1319224092Sdougb					   0, NULL);
1320170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
1321170222Sdougb		if (dns_name_equal(name, zonename))
1322170222Sdougb			return (ISC_TRUE);
1323170222Sdougb	}
1324170222Sdougb	return (ISC_FALSE);
1325170222Sdougb}
1326170222Sdougb
1327262706Serwinstatic isc_result_t
1328262706Serwincheck_dbtype(dns_zone_t *zone, unsigned int dbtypec, const char **dbargv,
1329170222Sdougb	     isc_mem_t *mctx)
1330170222Sdougb{
1331170222Sdougb	char **argv = NULL;
1332170222Sdougb	unsigned int i;
1333262706Serwin	isc_result_t result = ISC_R_SUCCESS;
1334170222Sdougb
1335262706Serwin	CHECK(dns_zone_getdbtype(zone, &argv, mctx));
1336170222Sdougb
1337170222Sdougb	/*
1338170222Sdougb	 * Check that all the arguments match.
1339170222Sdougb	 */
1340170222Sdougb	for (i = 0; i < dbtypec; i++)
1341170222Sdougb		if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) {
1342262706Serwin			CHECK(ISC_R_FAILURE);
1343170222Sdougb			break;
1344170222Sdougb		}
1345170222Sdougb
1346170222Sdougb	/*
1347170222Sdougb	 * Check that there are not extra arguments.
1348170222Sdougb	 */
1349170222Sdougb	if (i == dbtypec && argv[i] != NULL)
1350262706Serwin		result = ISC_R_FAILURE;
1351262706Serwin
1352262706Serwin cleanup:
1353170222Sdougb	isc_mem_free(mctx, argv);
1354262706Serwin	return (result);
1355170222Sdougb}
1356170222Sdougb
1357193149Sdougbstatic isc_result_t
1358254897Serwinsetquerystats(dns_zone_t *zone, isc_mem_t *mctx, dns_zonestat_level_t level) {
1359193149Sdougb	isc_result_t result;
1360193149Sdougb	isc_stats_t *zoneqrystats;
1361170222Sdougb
1362254897Serwin	dns_zone_setstatlevel(zone, level);
1363254897Serwin
1364193149Sdougb	zoneqrystats = NULL;
1365254897Serwin	if (level == dns_zonestat_full) {
1366193149Sdougb		result = isc_stats_create(mctx, &zoneqrystats,
1367193149Sdougb					  dns_nsstatscounter_max);
1368193149Sdougb		if (result != ISC_R_SUCCESS)
1369193149Sdougb			return (result);
1370193149Sdougb	}
1371193149Sdougb	dns_zone_setrequeststats(zone, zoneqrystats);
1372193149Sdougb	if (zoneqrystats != NULL)
1373193149Sdougb		isc_stats_detach(&zoneqrystats);
1374193149Sdougb
1375193149Sdougb	return (ISC_R_SUCCESS);
1376193149Sdougb}
1377193149Sdougb
1378224092Sdougbstatic ns_cache_t *
1379224092Sdougbcachelist_find(ns_cachelist_t *cachelist, const char *cachename) {
1380224092Sdougb	ns_cache_t *nsc;
1381224092Sdougb
1382224092Sdougb	for (nsc = ISC_LIST_HEAD(*cachelist);
1383224092Sdougb	     nsc != NULL;
1384224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
1385224092Sdougb		if (strcmp(dns_cache_getname(nsc->cache), cachename) == 0)
1386224092Sdougb			return (nsc);
1387224092Sdougb	}
1388224092Sdougb
1389224092Sdougb	return (NULL);
1390224092Sdougb}
1391224092Sdougb
1392193149Sdougbstatic isc_boolean_t
1393193149Sdougbcache_reusable(dns_view_t *originview, dns_view_t *view,
1394193149Sdougb	       isc_boolean_t new_zero_no_soattl)
1395193149Sdougb{
1396193149Sdougb	if (originview->checknames != view->checknames ||
1397193149Sdougb	    dns_resolver_getzeronosoattl(originview->resolver) !=
1398193149Sdougb	    new_zero_no_soattl ||
1399193149Sdougb	    originview->acceptexpired != view->acceptexpired ||
1400193149Sdougb	    originview->enablevalidation != view->enablevalidation ||
1401193149Sdougb	    originview->maxcachettl != view->maxcachettl ||
1402193149Sdougb	    originview->maxncachettl != view->maxncachettl) {
1403193149Sdougb		return (ISC_FALSE);
1404193149Sdougb	}
1405193149Sdougb
1406193149Sdougb	return (ISC_TRUE);
1407193149Sdougb}
1408193149Sdougb
1409224092Sdougbstatic isc_boolean_t
1410224092Sdougbcache_sharable(dns_view_t *originview, dns_view_t *view,
1411224092Sdougb	       isc_boolean_t new_zero_no_soattl,
1412224092Sdougb	       unsigned int new_cleaning_interval,
1413254897Serwin	       isc_uint64_t new_max_cache_size)
1414224092Sdougb{
1415224092Sdougb	/*
1416224092Sdougb	 * If the cache cannot even reused for the same view, it cannot be
1417224092Sdougb	 * shared with other views.
1418224092Sdougb	 */
1419224092Sdougb	if (!cache_reusable(originview, view, new_zero_no_soattl))
1420224092Sdougb		return (ISC_FALSE);
1421224092Sdougb
1422224092Sdougb	/*
1423224092Sdougb	 * Check other cache related parameters that must be consistent among
1424224092Sdougb	 * the sharing views.
1425224092Sdougb	 */
1426224092Sdougb	if (dns_cache_getcleaninginterval(originview->cache) !=
1427224092Sdougb	    new_cleaning_interval ||
1428224092Sdougb	    dns_cache_getcachesize(originview->cache) != new_max_cache_size) {
1429224092Sdougb		return (ISC_FALSE);
1430224092Sdougb	}
1431224092Sdougb
1432224092Sdougb	return (ISC_TRUE);
1433224092Sdougb}
1434224092Sdougb
1435135446Strhodes/*
1436224092Sdougb * Callback from DLZ configure when the driver sets up a writeable zone
1437224092Sdougb */
1438224092Sdougbstatic isc_result_t
1439224092Sdougbdlzconfigure_callback(dns_view_t *view, dns_zone_t *zone) {
1440224092Sdougb	dns_name_t *origin = dns_zone_getorigin(zone);
1441224092Sdougb	dns_rdataclass_t zclass = view->rdclass;
1442224092Sdougb	isc_result_t result;
1443224092Sdougb
1444224092Sdougb	result = dns_zonemgr_managezone(ns_g_server->zonemgr, zone);
1445224092Sdougb	if (result != ISC_R_SUCCESS)
1446254897Serwin		return (result);
1447224092Sdougb	dns_zone_setstats(zone, ns_g_server->zonestats);
1448224092Sdougb
1449254897Serwin	return (ns_zone_configure_writeable_dlz(view->dlzdatabase,
1450254897Serwin						zone, zclass, origin));
1451224092Sdougb}
1452224092Sdougb
1453224092Sdougbstatic isc_result_t
1454224092Sdougbdns64_reverse(dns_view_t *view, isc_mem_t *mctx, isc_netaddr_t *na,
1455224092Sdougb	      unsigned int prefixlen, const char *server,
1456224092Sdougb	      const char *contact)
1457224092Sdougb{
1458224092Sdougb	char *cp;
1459224092Sdougb	char reverse[48+sizeof("ip6.arpa.")];
1460236374Sdougb	const char *dns64_dbtype[4] = { "_dns64", "dns64", ".", "." };
1461224092Sdougb	const char *sep = ": view ";
1462224092Sdougb	const char *viewname = view->name;
1463224092Sdougb	const unsigned char *s6;
1464224092Sdougb	dns_fixedname_t fixed;
1465224092Sdougb	dns_name_t *name;
1466224092Sdougb	dns_zone_t *zone = NULL;
1467224092Sdougb	int dns64_dbtypec = 4;
1468224092Sdougb	isc_buffer_t b;
1469224092Sdougb	isc_result_t result;
1470224092Sdougb
1471224092Sdougb	REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 ||
1472224092Sdougb		prefixlen == 56 || prefixlen == 64 || prefixlen == 96);
1473224092Sdougb
1474224092Sdougb	if (!strcmp(viewname, "_default")) {
1475224092Sdougb		sep = "";
1476224092Sdougb		viewname = "";
1477224092Sdougb	}
1478224092Sdougb
1479224092Sdougb	/*
1480224092Sdougb	 * Construct the reverse name of the zone.
1481224092Sdougb	 */
1482224092Sdougb	cp = reverse;
1483224092Sdougb	s6 = na->type.in6.s6_addr;
1484224092Sdougb	while (prefixlen > 0) {
1485224092Sdougb		prefixlen -= 8;
1486224092Sdougb		sprintf(cp, "%x.%x.", s6[prefixlen/8] & 0xf,
1487224092Sdougb			(s6[prefixlen/8] >> 4) & 0xf);
1488224092Sdougb		cp += 4;
1489224092Sdougb	}
1490224092Sdougb	strcat(cp, "ip6.arpa.");
1491224092Sdougb
1492224092Sdougb	/*
1493224092Sdougb	 * Create the actual zone.
1494224092Sdougb	 */
1495224092Sdougb	if (server != NULL)
1496224092Sdougb		dns64_dbtype[2] = server;
1497224092Sdougb	if (contact != NULL)
1498224092Sdougb		dns64_dbtype[3] = contact;
1499224092Sdougb	dns_fixedname_init(&fixed);
1500224092Sdougb	name = dns_fixedname_name(&fixed);
1501254402Serwin	isc_buffer_constinit(&b, reverse, strlen(reverse));
1502224092Sdougb	isc_buffer_add(&b, strlen(reverse));
1503224092Sdougb	CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
1504224092Sdougb	CHECK(dns_zone_create(&zone, mctx));
1505224092Sdougb	CHECK(dns_zone_setorigin(zone, name));
1506224092Sdougb	dns_zone_setview(zone, view);
1507224092Sdougb	CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
1508224092Sdougb	dns_zone_setclass(zone, view->rdclass);
1509224092Sdougb	dns_zone_settype(zone, dns_zone_master);
1510224092Sdougb	dns_zone_setstats(zone, ns_g_server->zonestats);
1511224092Sdougb	CHECK(dns_zone_setdbtype(zone, dns64_dbtypec, dns64_dbtype));
1512224092Sdougb	if (view->queryacl != NULL)
1513224092Sdougb		dns_zone_setqueryacl(zone, view->queryacl);
1514224092Sdougb	if (view->queryonacl != NULL)
1515224092Sdougb		dns_zone_setqueryonacl(zone, view->queryonacl);
1516224092Sdougb	dns_zone_setdialup(zone, dns_dialuptype_no);
1517224092Sdougb	dns_zone_setnotifytype(zone, dns_notifytype_no);
1518224092Sdougb	dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE);
1519254897Serwin	CHECK(setquerystats(zone, mctx, dns_zonestat_none));	/* XXXMPA */
1520224092Sdougb	CHECK(dns_view_addzone(view, zone));
1521224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
1522224092Sdougb		      ISC_LOG_INFO, "dns64 reverse zone%s%s: %s", sep,
1523224092Sdougb		      viewname, reverse);
1524224092Sdougb
1525224092Sdougbcleanup:
1526224092Sdougb	if (zone != NULL)
1527224092Sdougb		dns_zone_detach(&zone);
1528224092Sdougb	return (result);
1529224092Sdougb}
1530224092Sdougb
1531224092Sdougbstatic isc_result_t
1532254402Serwinconfigure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
1533254402Serwin		   const char *str, const char *msg)
1534254402Serwin{
1535254402Serwin	isc_result_t result;
1536254402Serwin
1537254402Serwin	result = dns_name_fromstring(name, str, DNS_NAME_DOWNCASE, view->mctx);
1538254402Serwin	if (result != ISC_R_SUCCESS)
1539254402Serwin		cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1540254402Serwin			    "invalid %s '%s'", msg, str);
1541254402Serwin	return (result);
1542254402Serwin}
1543254402Serwin
1544254402Serwinstatic isc_result_t
1545254402Serwinconfigure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
1546254402Serwin		    const char *str, const dns_name_t *origin)
1547254402Serwin{
1548254402Serwin	isc_result_t result;
1549254402Serwin
1550254402Serwin	result = dns_name_fromstring2(name, str, origin, DNS_NAME_DOWNCASE,
1551254402Serwin				      view->mctx);
1552254402Serwin	if (result != ISC_R_SUCCESS)
1553254402Serwin		cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1554254402Serwin			    "invalid zone '%s'", str);
1555254402Serwin	return (result);
1556254402Serwin}
1557254402Serwin
1558254402Serwinstatic isc_result_t
1559245163Serwinconfigure_rpz(dns_view_t *view, const cfg_listelt_t *element,
1560245163Serwin	      isc_boolean_t recursive_only_def, dns_ttl_t ttl_def)
1561245163Serwin{
1562254402Serwin	const cfg_obj_t *rpz_obj, *obj;
1563224092Sdougb	const char *str;
1564224092Sdougb	dns_rpz_zone_t *old, *new;
1565224092Sdougb	isc_result_t result;
1566224092Sdougb
1567254402Serwin	rpz_obj = cfg_listelt_value(element);
1568254402Serwin
1569224092Sdougb	new = isc_mem_get(view->mctx, sizeof(*new));
1570224092Sdougb	if (new == NULL) {
1571254402Serwin		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1572254402Serwin			    "no memory for response policy zones");
1573254402Serwin		return (ISC_R_NOMEMORY);
1574224092Sdougb	}
1575224092Sdougb
1576224092Sdougb	memset(new, 0, sizeof(*new));
1577245163Serwin	dns_name_init(&new->origin, NULL);
1578224092Sdougb	dns_name_init(&new->nsdname, NULL);
1579254402Serwin	dns_name_init(&new->passthru, NULL);
1580224092Sdougb	dns_name_init(&new->cname, NULL);
1581224092Sdougb	ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link);
1582224092Sdougb
1583245163Serwin	obj = cfg_tuple_get(rpz_obj, "recursive-only");
1584245163Serwin	if (cfg_obj_isvoid(obj)) {
1585245163Serwin		new->recursive_only = recursive_only_def;
1586245163Serwin	} else {
1587245163Serwin		new->recursive_only = cfg_obj_asboolean(obj);
1588245163Serwin	}
1589245163Serwin	if (!new->recursive_only)
1590245163Serwin		view->rpz_recursive_only = ISC_FALSE;
1591245163Serwin
1592245163Serwin	obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
1593245163Serwin	if (cfg_obj_isuint32(obj)) {
1594245163Serwin		new->max_policy_ttl = cfg_obj_asuint32(obj);
1595245163Serwin	} else {
1596245163Serwin		new->max_policy_ttl = ttl_def;
1597245163Serwin	}
1598245163Serwin
1599245163Serwin	str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name"));
1600254402Serwin	result = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone");
1601254402Serwin	if (result != ISC_R_SUCCESS)
1602254402Serwin		return (result);
1603254402Serwin	if (dns_name_equal(&new->origin, dns_rootname)) {
1604224092Sdougb		cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1605254402Serwin			    "invalid zone name '%s'", str);
1606254402Serwin		return (DNS_R_EMPTYLABEL);
1607224092Sdougb	}
1608224092Sdougb	for (old = ISC_LIST_HEAD(view->rpz_zones);
1609224092Sdougb	     old != new;
1610224092Sdougb	     old = ISC_LIST_NEXT(old, link)) {
1611224092Sdougb		++new->num;
1612224092Sdougb		if (dns_name_equal(&old->origin, &new->origin)) {
1613224092Sdougb			cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
1614224092Sdougb				    "duplicate '%s'", str);
1615224092Sdougb			result = DNS_R_DUPLICATE;
1616254402Serwin			return (result);
1617224092Sdougb		}
1618224092Sdougb	}
1619224092Sdougb
1620254402Serwin	result = configure_rpz_name2(view, rpz_obj, &new->nsdname,
1621254402Serwin				     DNS_RPZ_NSDNAME_ZONE, &new->origin);
1622254402Serwin	if (result != ISC_R_SUCCESS)
1623254402Serwin		return (result);
1624254402Serwin
1625254402Serwin	result = configure_rpz_name(view, rpz_obj, &new->passthru,
1626254402Serwin				    DNS_RPZ_PASSTHRU_ZONE, "zone");
1627254402Serwin	if (result != ISC_R_SUCCESS)
1628254402Serwin		return (result);
1629254402Serwin
1630254402Serwin	obj = cfg_tuple_get(rpz_obj, "policy");
1631254402Serwin	if (cfg_obj_isvoid(obj)) {
1632254402Serwin		new->policy = DNS_RPZ_POLICY_GIVEN;
1633254402Serwin	} else {
1634254402Serwin		str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name"));
1635254402Serwin		new->policy = dns_rpz_str2policy(str);
1636254402Serwin		INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
1637254402Serwin		if (new->policy == DNS_RPZ_POLICY_CNAME) {
1638254402Serwin			str = cfg_obj_asstring(cfg_tuple_get(obj, "cname"));
1639254402Serwin			result = configure_rpz_name(view, rpz_obj, &new->cname,
1640254402Serwin						    str, "cname");
1641254402Serwin			if (result != ISC_R_SUCCESS)
1642254402Serwin				return (result);
1643224092Sdougb		}
1644224092Sdougb	}
1645224092Sdougb
1646224092Sdougb	return (ISC_R_SUCCESS);
1647224092Sdougb}
1648224092Sdougb
1649262706Serwin#ifdef USE_RRL
1650262706Serwin#define CHECK_RRL(cond, pat, val1, val2)				\
1651262706Serwin	do {								\
1652262706Serwin		if (!(cond)) {						\
1653262706Serwin			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,	\
1654262706Serwin				    pat, val1, val2);			\
1655262706Serwin			result = ISC_R_RANGE;				\
1656262706Serwin			goto cleanup;					\
1657262706Serwin		    }							\
1658262706Serwin	} while (0)
1659262706Serwin
1660262706Serwin#define CHECK_RRL_RATE(rate, def, max_rate, name)			\
1661262706Serwin	do {								\
1662262706Serwin		obj = NULL;						\
1663262706Serwin		rrl->rate.str = name;					\
1664262706Serwin		result = cfg_map_get(map, name, &obj);			\
1665262706Serwin		if (result == ISC_R_SUCCESS) {				\
1666262706Serwin			rrl->rate.r = cfg_obj_asuint32(obj);		\
1667262706Serwin			CHECK_RRL(rrl->rate.r <= max_rate,		\
1668262706Serwin				  name" %d > %d",			\
1669262706Serwin				  rrl->rate.r, max_rate);		\
1670262706Serwin		} else {						\
1671262706Serwin			rrl->rate.r = def;				\
1672262706Serwin		}							\
1673262706Serwin		rrl->rate.scaled = rrl->rate.r;				\
1674262706Serwin	} while (0)
1675262706Serwin
1676262706Serwinstatic isc_result_t
1677262706Serwinconfigure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
1678262706Serwin	const cfg_obj_t *obj;
1679262706Serwin	dns_rrl_t *rrl;
1680262706Serwin	isc_result_t result;
1681262706Serwin	int min_entries, i, j;
1682262706Serwin
1683262706Serwin	/*
1684262706Serwin	 * Most DNS servers have few clients, but intentinally open
1685262706Serwin	 * recursive and authoritative servers often have many.
1686262706Serwin	 * So start with a small number of entries unless told otherwise
1687262706Serwin	 * to reduce cold-start costs.
1688262706Serwin	 */
1689262706Serwin	min_entries = 500;
1690262706Serwin	obj = NULL;
1691262706Serwin	result = cfg_map_get(map, "min-table-size", &obj);
1692262706Serwin	if (result == ISC_R_SUCCESS) {
1693262706Serwin		min_entries = cfg_obj_asuint32(obj);
1694262706Serwin		if (min_entries < 1)
1695262706Serwin			min_entries = 1;
1696262706Serwin	}
1697262706Serwin	result = dns_rrl_init(&rrl, view, min_entries);
1698262706Serwin	if (result != ISC_R_SUCCESS)
1699262706Serwin		return (result);
1700262706Serwin
1701262706Serwin	i = ISC_MAX(20000, min_entries);
1702262706Serwin	obj = NULL;
1703262706Serwin	result = cfg_map_get(map, "max-table-size", &obj);
1704262706Serwin	if (result == ISC_R_SUCCESS) {
1705262706Serwin		i = cfg_obj_asuint32(obj);
1706262706Serwin		CHECK_RRL(i >= min_entries,
1707262706Serwin			  "max-table-size %d < min-table-size %d",
1708262706Serwin			  i, min_entries);
1709262706Serwin	}
1710262706Serwin	rrl->max_entries = i;
1711262706Serwin
1712262706Serwin	CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
1713262706Serwin		       "responses-per-second");
1714262706Serwin	CHECK_RRL_RATE(referrals_per_second,
1715262706Serwin		       rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
1716262706Serwin		       "referrals-per-second");
1717262706Serwin	CHECK_RRL_RATE(nodata_per_second,
1718262706Serwin		       rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
1719262706Serwin		       "nodata-per-second");
1720262706Serwin	CHECK_RRL_RATE(nxdomains_per_second,
1721262706Serwin		       rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
1722262706Serwin		       "nxdomains-per-second");
1723262706Serwin	CHECK_RRL_RATE(errors_per_second,
1724262706Serwin		       rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
1725262706Serwin		       "errors-per-second");
1726262706Serwin
1727262706Serwin	CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE,
1728262706Serwin		       "all-per-second");
1729262706Serwin
1730262706Serwin	CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP,
1731262706Serwin		       "slip");
1732262706Serwin
1733262706Serwin	i = 15;
1734262706Serwin	obj = NULL;
1735262706Serwin	result = cfg_map_get(map, "window", &obj);
1736262706Serwin	if (result == ISC_R_SUCCESS) {
1737262706Serwin		i = cfg_obj_asuint32(obj);
1738262706Serwin		CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW,
1739262706Serwin			  "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
1740262706Serwin	}
1741262706Serwin	rrl->window = i;
1742262706Serwin
1743262706Serwin	i = 0;
1744262706Serwin	obj = NULL;
1745262706Serwin	result = cfg_map_get(map, "qps-scale", &obj);
1746262706Serwin	if (result == ISC_R_SUCCESS) {
1747262706Serwin		i = cfg_obj_asuint32(obj);
1748262706Serwin		CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, "");
1749262706Serwin	}
1750262706Serwin	rrl->qps_scale = i;
1751262706Serwin	rrl->qps = 1.0;
1752262706Serwin
1753262706Serwin	i = 24;
1754262706Serwin	obj = NULL;
1755262706Serwin	result = cfg_map_get(map, "ipv4-prefix-length", &obj);
1756262706Serwin	if (result == ISC_R_SUCCESS) {
1757262706Serwin		i = cfg_obj_asuint32(obj);
1758262706Serwin		CHECK_RRL(i >= 8 && i <= 32,
1759262706Serwin			  "invalid 'ipv4-prefix-length %d'%s", i, "");
1760262706Serwin	}
1761262706Serwin	rrl->ipv4_prefixlen = i;
1762262706Serwin	if (i == 32)
1763262706Serwin		rrl->ipv4_mask = 0xffffffff;
1764262706Serwin	else
1765262706Serwin		rrl->ipv4_mask = htonl(0xffffffff << (32-i));
1766262706Serwin
1767262706Serwin	i = 56;
1768262706Serwin	obj = NULL;
1769262706Serwin	result = cfg_map_get(map, "ipv6-prefix-length", &obj);
1770262706Serwin	if (result == ISC_R_SUCCESS) {
1771262706Serwin		i = cfg_obj_asuint32(obj);
1772262706Serwin		CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
1773262706Serwin			  "ipv6-prefix-length %d < 16 or > %d",
1774262706Serwin			  i, DNS_RRL_MAX_PREFIX);
1775262706Serwin	}
1776262706Serwin	rrl->ipv6_prefixlen = i;
1777262706Serwin	for (j = 0; j < 4; ++j) {
1778262706Serwin		if (i <= 0) {
1779262706Serwin			rrl->ipv6_mask[j] = 0;
1780262706Serwin		} else if (i < 32) {
1781262706Serwin			rrl->ipv6_mask[j] = htonl(0xffffffff << (32-i));
1782262706Serwin		} else {
1783262706Serwin			rrl->ipv6_mask[j] = 0xffffffff;
1784262706Serwin		}
1785262706Serwin		i -= 32;
1786262706Serwin	}
1787262706Serwin
1788262706Serwin	obj = NULL;
1789262706Serwin	result = cfg_map_get(map, "exempt-clients", &obj);
1790262706Serwin	if (result == ISC_R_SUCCESS) {
1791262706Serwin		result = cfg_acl_fromconfig(obj, config, ns_g_lctx,
1792262706Serwin					    ns_g_aclconfctx, ns_g_mctx,
1793262706Serwin					    0, &rrl->exempt);
1794262706Serwin		CHECK_RRL(result == ISC_R_SUCCESS,
1795262706Serwin			  "invalid %s%s", "address match list", "");
1796262706Serwin	}
1797262706Serwin
1798262706Serwin	obj = NULL;
1799262706Serwin	result = cfg_map_get(map, "log-only", &obj);
1800262706Serwin	if (result == ISC_R_SUCCESS && cfg_obj_asboolean(obj))
1801262706Serwin		rrl->log_only = ISC_TRUE;
1802262706Serwin	else
1803262706Serwin		rrl->log_only = ISC_FALSE;
1804262706Serwin
1805262706Serwin	return (ISC_R_SUCCESS);
1806262706Serwin
1807262706Serwin cleanup:
1808262706Serwin	dns_rrl_view_destroy(view);
1809262706Serwin	return (result);
1810262706Serwin}
1811262706Serwin#endif /* USE_RRL */
1812262706Serwin
1813262706Serwinstatic isc_result_t
1814262706Serwinadd_soa(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
1815262706Serwin	dns_name_t *origin, dns_name_t *contact)
1816262706Serwin{
1817262706Serwin	dns_dbnode_t *node = NULL;
1818262706Serwin	dns_rdata_t rdata = DNS_RDATA_INIT;
1819262706Serwin	dns_rdatalist_t rdatalist;
1820262706Serwin	dns_rdataset_t rdataset;
1821262706Serwin	isc_result_t result;
1822262706Serwin	unsigned char buf[DNS_SOA_BUFFERSIZE];
1823262706Serwin
1824262706Serwin	dns_rdataset_init(&rdataset);
1825262706Serwin	dns_rdatalist_init(&rdatalist);
1826262706Serwin	CHECK(dns_soa_buildrdata(origin, contact, dns_db_class(db),
1827262706Serwin				 0, 28800, 7200, 604800, 86400, buf, &rdata));
1828262706Serwin	rdatalist.type = rdata.type;
1829262706Serwin	rdatalist.covers = 0;
1830262706Serwin	rdatalist.rdclass = rdata.rdclass;
1831262706Serwin	rdatalist.ttl = 86400;
1832262706Serwin	ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
1833262706Serwin	CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
1834262706Serwin	CHECK(dns_db_findnode(db, name, ISC_TRUE, &node));
1835262706Serwin	CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
1836262706Serwin cleanup:
1837262706Serwin	if (node != NULL)
1838262706Serwin		dns_db_detachnode(db, &node);
1839262706Serwin	return (result);
1840262706Serwin}
1841262706Serwin
1842262706Serwinstatic isc_result_t
1843262706Serwinadd_ns(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
1844262706Serwin       dns_name_t *nsname)
1845262706Serwin{
1846262706Serwin	dns_dbnode_t *node = NULL;
1847262706Serwin	dns_rdata_ns_t ns;
1848262706Serwin	dns_rdata_t rdata = DNS_RDATA_INIT;
1849262706Serwin	dns_rdatalist_t rdatalist;
1850262706Serwin	dns_rdataset_t rdataset;
1851262706Serwin	isc_result_t result;
1852262706Serwin	isc_buffer_t b;
1853262706Serwin	unsigned char buf[DNS_NAME_MAXWIRE];
1854262706Serwin
1855262706Serwin	isc_buffer_init(&b, buf, sizeof(buf));
1856262706Serwin
1857262706Serwin	dns_rdataset_init(&rdataset);
1858262706Serwin	dns_rdatalist_init(&rdatalist);
1859262706Serwin	ns.common.rdtype = dns_rdatatype_ns;
1860262706Serwin	ns.common.rdclass = dns_db_class(db);
1861262706Serwin	ns.mctx = NULL;
1862262706Serwin	dns_name_init(&ns.name, NULL);
1863262706Serwin	dns_name_clone(nsname, &ns.name);
1864262706Serwin	CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns,
1865262706Serwin				   &ns, &b));
1866262706Serwin	rdatalist.type = rdata.type;
1867262706Serwin	rdatalist.covers = 0;
1868262706Serwin	rdatalist.rdclass = rdata.rdclass;
1869262706Serwin	rdatalist.ttl = 86400;
1870262706Serwin	ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
1871262706Serwin	CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
1872262706Serwin	CHECK(dns_db_findnode(db, name, ISC_TRUE, &node));
1873262706Serwin	CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
1874262706Serwin cleanup:
1875262706Serwin	if (node != NULL)
1876262706Serwin		dns_db_detachnode(db, &node);
1877262706Serwin	return (result);
1878262706Serwin}
1879262706Serwin
1880262706Serwinstatic isc_result_t
1881262706Serwincreate_empty_zone(dns_zone_t *zone, dns_name_t *name, dns_view_t *view,
1882262706Serwin		  const cfg_obj_t *zonelist, const char **empty_dbtype,
1883262706Serwin		  int empty_dbtypec, dns_zonestat_level_t statlevel)
1884262706Serwin{
1885262706Serwin	char namebuf[DNS_NAME_FORMATSIZE];
1886262706Serwin	const cfg_listelt_t *element;
1887262706Serwin	const cfg_obj_t *obj;
1888262706Serwin	const cfg_obj_t *zconfig;
1889262706Serwin	const cfg_obj_t *zoptions;
1890262706Serwin	const char *rbt_dbtype[4] = { "rbt" };
1891262706Serwin	const char *sep = ": view ";
1892262706Serwin	const char *str;
1893262706Serwin	const char *viewname = view->name;
1894262706Serwin	dns_db_t *db = NULL;
1895262706Serwin	dns_dbversion_t *version = NULL;
1896262706Serwin	dns_fixedname_t cfixed;
1897262706Serwin	dns_fixedname_t fixed;
1898262706Serwin	dns_fixedname_t nsfixed;
1899262706Serwin	dns_name_t *contact;
1900262706Serwin	dns_name_t *ns;
1901262706Serwin	dns_name_t *zname;
1902262706Serwin	dns_zone_t *myzone = NULL;
1903262706Serwin	int rbt_dbtypec = 1;
1904262706Serwin	isc_result_t result;
1905262706Serwin	dns_namereln_t namereln;
1906262706Serwin	int order;
1907262706Serwin	unsigned int nlabels;
1908262706Serwin
1909262706Serwin	dns_fixedname_init(&fixed);
1910262706Serwin	zname = dns_fixedname_name(&fixed);
1911262706Serwin	dns_fixedname_init(&nsfixed);
1912262706Serwin	ns = dns_fixedname_name(&nsfixed);
1913262706Serwin	dns_fixedname_init(&cfixed);
1914262706Serwin	contact = dns_fixedname_name(&cfixed);
1915262706Serwin
1916262706Serwin	/*
1917262706Serwin	 * Look for forward "zones" beneath this empty zone and if so
1918262706Serwin	 * create a custom db for the empty zone.
1919262706Serwin	 */
1920262706Serwin	for (element = cfg_list_first(zonelist);
1921262706Serwin	     element != NULL;
1922262706Serwin	     element = cfg_list_next(element)) {
1923262706Serwin
1924262706Serwin		zconfig = cfg_listelt_value(element);
1925262706Serwin		str = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
1926262706Serwin		CHECK(dns_name_fromstring(zname, str, 0, NULL));
1927262706Serwin		namereln = dns_name_fullcompare(zname, name, &order, &nlabels);
1928262706Serwin		if (namereln != dns_namereln_subdomain)
1929262706Serwin			continue;
1930262706Serwin
1931262706Serwin		zoptions = cfg_tuple_get(zconfig, "options");
1932262706Serwin
1933262706Serwin		obj = NULL;
1934262706Serwin		(void)cfg_map_get(zoptions, "type", &obj);
1935262706Serwin		INSIST(obj != NULL);
1936262706Serwin		if (strcasecmp(cfg_obj_asstring(obj), "forward") == 0) {
1937262706Serwin			obj = NULL;
1938262706Serwin			(void)cfg_map_get(zoptions, "forward", &obj);
1939262706Serwin			if (obj == NULL)
1940262706Serwin				continue;
1941262706Serwin			if (strcasecmp(cfg_obj_asstring(obj), "only") != 0)
1942262706Serwin				continue;
1943262706Serwin		}
1944262706Serwin		if (db == NULL) {
1945262706Serwin			CHECK(dns_db_create(view->mctx, "rbt", name,
1946262706Serwin					    dns_dbtype_zone, view->rdclass,
1947262706Serwin					    0, NULL, &db));
1948262706Serwin			CHECK(dns_db_newversion(db, &version));
1949262706Serwin			if (strcmp(empty_dbtype[2], "@") == 0)
1950262706Serwin				dns_name_clone(name, ns);
1951262706Serwin			else
1952262706Serwin				CHECK(dns_name_fromstring(ns, empty_dbtype[2],
1953262706Serwin							  0, NULL));
1954262706Serwin			CHECK(dns_name_fromstring(contact, empty_dbtype[3],
1955262706Serwin						  0, NULL));
1956262706Serwin			CHECK(add_soa(db, version, name, ns, contact));
1957262706Serwin			CHECK(add_ns(db, version, name, ns));
1958262706Serwin		}
1959262706Serwin		CHECK(add_ns(db, version, zname, dns_rootname));
1960262706Serwin	}
1961262706Serwin
1962262706Serwin	/*
1963262706Serwin	 * Is the existing zone the ok to use?
1964262706Serwin	 */
1965262706Serwin	if (zone != NULL) {
1966262706Serwin		unsigned int typec;
1967262706Serwin		const char **dbargv;
1968262706Serwin
1969262706Serwin		if (db != NULL) {
1970262706Serwin			typec = rbt_dbtypec;
1971262706Serwin			dbargv = rbt_dbtype;
1972262706Serwin		} else {
1973262706Serwin			typec = empty_dbtypec;
1974262706Serwin			dbargv = empty_dbtype;
1975262706Serwin		}
1976262706Serwin
1977262706Serwin		result = check_dbtype(zone, typec, dbargv, view->mctx);
1978262706Serwin		if (result != ISC_R_SUCCESS)
1979262706Serwin			zone = NULL;
1980262706Serwin
1981262706Serwin		if (zone != NULL && dns_zone_gettype(zone) != dns_zone_master)
1982262706Serwin			zone = NULL;
1983262706Serwin		if (zone != NULL && dns_zone_getfile(zone) != NULL)
1984262706Serwin			zone = NULL;
1985262706Serwin		if (zone != NULL) {
1986262706Serwin			dns_zone_getraw(zone, &myzone);
1987262706Serwin			if (myzone != NULL) {
1988262706Serwin				dns_zone_detach(&myzone);
1989262706Serwin				zone = NULL;
1990262706Serwin			}
1991262706Serwin		}
1992262706Serwin	}
1993262706Serwin
1994262706Serwin	if (zone == NULL) {
1995262706Serwin		CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &myzone));
1996262706Serwin		zone = myzone;
1997262706Serwin		CHECK(dns_zone_setorigin(zone, name));
1998262706Serwin		CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
1999262706Serwin		if (db == NULL)
2000262706Serwin			CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
2001262706Serwin						 empty_dbtype));
2002262706Serwin		dns_zone_setclass(zone, view->rdclass);
2003262706Serwin		dns_zone_settype(zone, dns_zone_master);
2004262706Serwin		dns_zone_setstats(zone, ns_g_server->zonestats);
2005262706Serwin	}
2006262706Serwin
2007262706Serwin	dns_zone_setoption(zone, ~DNS_ZONEOPT_NOCHECKNS, ISC_FALSE);
2008262706Serwin	dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE);
2009262706Serwin	dns_zone_setnotifytype(zone, dns_notifytype_no);
2010262706Serwin	dns_zone_setdialup(zone, dns_dialuptype_no);
2011262706Serwin	if (view->queryacl)
2012262706Serwin		dns_zone_setqueryacl(zone, view->queryacl);
2013262706Serwin	else
2014262706Serwin		dns_zone_clearqueryacl(zone);
2015262706Serwin	if (view->queryonacl)
2016262706Serwin		dns_zone_setqueryonacl(zone, view->queryonacl);
2017262706Serwin	else
2018262706Serwin		dns_zone_clearqueryonacl(zone);
2019262706Serwin	dns_zone_clearupdateacl(zone);
2020262706Serwin	dns_zone_clearxfracl(zone);
2021262706Serwin
2022262706Serwin	CHECK(setquerystats(zone, view->mctx, statlevel));
2023262706Serwin	if (db != NULL) {
2024262706Serwin		dns_db_closeversion(db, &version, ISC_TRUE);
2025262706Serwin		CHECK(dns_zone_replacedb(zone, db, ISC_FALSE));
2026262706Serwin	}
2027262706Serwin	dns_zone_setview(zone, view);
2028262706Serwin	CHECK(dns_view_addzone(view, zone));
2029262706Serwin
2030262706Serwin	if (!strcmp(viewname, "_default")) {
2031262706Serwin		sep = "";
2032262706Serwin		viewname = "";
2033262706Serwin	}
2034262706Serwin	dns_name_format(name, namebuf, sizeof(namebuf));
2035262706Serwin	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
2036262706Serwin		      ISC_LOG_INFO, "automatic empty zone%s%s: %s",
2037262706Serwin		      sep, viewname, namebuf);
2038262706Serwin
2039262706Serwin cleanup:
2040262706Serwin	if (myzone != NULL)
2041262706Serwin		dns_zone_detach(&myzone);
2042262706Serwin	if (version != NULL)
2043262706Serwin		dns_db_closeversion(db, &version, ISC_FALSE);
2044262706Serwin	if (db != NULL)
2045262706Serwin		dns_db_detach(&db);
2046262706Serwin	return (result);
2047262706Serwin}
2048262706Serwin
2049224092Sdougb/*
2050135446Strhodes * Configure 'view' according to 'vconfig', taking defaults from 'config'
2051135446Strhodes * where values are missing in 'vconfig'.
2052135446Strhodes *
2053135446Strhodes * When configuring the default view, 'vconfig' will be NULL and the
2054135446Strhodes * global defaults in 'config' used exclusively.
2055135446Strhodes */
2056135446Strhodesstatic isc_result_t
2057225361Sdougbconfigure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
2058224092Sdougb	       ns_cachelist_t *cachelist, const cfg_obj_t *bindkeys,
2059224092Sdougb	       isc_mem_t *mctx, cfg_aclconfctx_t *actx,
2060224092Sdougb	       isc_boolean_t need_hints)
2061135446Strhodes{
2062165071Sdougb	const cfg_obj_t *maps[4];
2063165071Sdougb	const cfg_obj_t *cfgmaps[3];
2064224092Sdougb	const cfg_obj_t *optionmaps[3];
2065165071Sdougb	const cfg_obj_t *options = NULL;
2066165071Sdougb	const cfg_obj_t *voptions = NULL;
2067165071Sdougb	const cfg_obj_t *forwardtype;
2068165071Sdougb	const cfg_obj_t *forwarders;
2069165071Sdougb	const cfg_obj_t *alternates;
2070165071Sdougb	const cfg_obj_t *zonelist;
2071186462Sdougb	const cfg_obj_t *dlz;
2072186462Sdougb	unsigned int dlzargc;
2073186462Sdougb	char **dlzargv;
2074165071Sdougb	const cfg_obj_t *disabled;
2075165071Sdougb	const cfg_obj_t *obj;
2076165071Sdougb	const cfg_listelt_t *element;
2077135446Strhodes	in_port_t port;
2078135446Strhodes	dns_cache_t *cache = NULL;
2079135446Strhodes	isc_result_t result;
2080224092Sdougb	unsigned int cleaning_interval;
2081254897Serwin	size_t max_cache_size;
2082254897Serwin	size_t max_acache_size;
2083254897Serwin	size_t max_adb_size;
2084135446Strhodes	isc_uint32_t lame_ttl;
2085224092Sdougb	dns_tsig_keyring_t *ring = NULL;
2086135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
2087225361Sdougb	isc_mem_t *cmctx = NULL, *hmctx = NULL;
2088135446Strhodes	dns_dispatch_t *dispatch4 = NULL;
2089135446Strhodes	dns_dispatch_t *dispatch6 = NULL;
2090135446Strhodes	isc_boolean_t reused_cache = ISC_FALSE;
2091224092Sdougb	isc_boolean_t shared_cache = ISC_FALSE;
2092224092Sdougb	int i = 0, j = 0, k = 0;
2093135446Strhodes	const char *str;
2094224092Sdougb	const char *cachename = NULL;
2095135446Strhodes	dns_order_t *order = NULL;
2096135446Strhodes	isc_uint32_t udpsize;
2097254897Serwin	isc_uint32_t maxbits;
2098193149Sdougb	unsigned int resopts = 0;
2099170222Sdougb	dns_zone_t *zone = NULL;
2100170222Sdougb	isc_uint32_t max_clients_per_query;
2101170222Sdougb	isc_boolean_t empty_zones_enable;
2102170222Sdougb	const cfg_obj_t *disablelist = NULL;
2103193149Sdougb	isc_stats_t *resstats = NULL;
2104193149Sdougb	dns_stats_t *resquerystats = NULL;
2105224092Sdougb	isc_boolean_t auto_dlv = ISC_FALSE;
2106224092Sdougb	isc_boolean_t auto_root = ISC_FALSE;
2107224092Sdougb	ns_cache_t *nsc;
2108193149Sdougb	isc_boolean_t zero_no_soattl;
2109224092Sdougb	dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
2110254897Serwin	unsigned int query_timeout, ndisp;
2111225361Sdougb	struct cfg_context *nzctx;
2112254402Serwin	dns_rpz_zone_t *rpz;
2113135446Strhodes
2114135446Strhodes	REQUIRE(DNS_VIEW_VALID(view));
2115135446Strhodes
2116135446Strhodes	if (config != NULL)
2117135446Strhodes		(void)cfg_map_get(config, "options", &options);
2118135446Strhodes
2119224092Sdougb	/*
2120224092Sdougb	 * maps: view options, options, defaults
2121224092Sdougb	 * cfgmaps: view options, config
2122224092Sdougb	 * optionmaps: view options, options
2123224092Sdougb	 */
2124135446Strhodes	if (vconfig != NULL) {
2125135446Strhodes		voptions = cfg_tuple_get(vconfig, "options");
2126135446Strhodes		maps[i++] = voptions;
2127224092Sdougb		optionmaps[j++] = voptions;
2128224092Sdougb		cfgmaps[k++] = voptions;
2129135446Strhodes	}
2130224092Sdougb	if (options != NULL) {
2131135446Strhodes		maps[i++] = options;
2132224092Sdougb		optionmaps[j++] = options;
2133224092Sdougb	}
2134224092Sdougb
2135135446Strhodes	maps[i++] = ns_g_defaults;
2136135446Strhodes	maps[i] = NULL;
2137224092Sdougb	optionmaps[j] = NULL;
2138135446Strhodes	if (config != NULL)
2139224092Sdougb		cfgmaps[k++] = config;
2140224092Sdougb	cfgmaps[k] = NULL;
2141135446Strhodes
2142135446Strhodes	/*
2143135446Strhodes	 * Set the view's port number for outgoing queries.
2144135446Strhodes	 */
2145135446Strhodes	CHECKM(ns_config_getport(config, &port), "port");
2146135446Strhodes	dns_view_setdstport(view, port);
2147135446Strhodes
2148135446Strhodes	/*
2149170222Sdougb	 * Create additional cache for this view and zones under the view
2150170222Sdougb	 * if explicitly enabled.
2151170222Sdougb	 * XXX950 default to on.
2152170222Sdougb	 */
2153170222Sdougb	obj = NULL;
2154170222Sdougb	(void)ns_config_get(maps, "acache-enable", &obj);
2155170222Sdougb	if (obj != NULL && cfg_obj_asboolean(obj)) {
2156170222Sdougb		cmctx = NULL;
2157170222Sdougb		CHECK(isc_mem_create(0, 0, &cmctx));
2158170222Sdougb		CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr,
2159170222Sdougb					ns_g_timermgr));
2160193149Sdougb		isc_mem_setname(cmctx, "acache", NULL);
2161170222Sdougb		isc_mem_detach(&cmctx);
2162170222Sdougb	}
2163170222Sdougb	if (view->acache != NULL) {
2164170222Sdougb		obj = NULL;
2165170222Sdougb		result = ns_config_get(maps, "acache-cleaning-interval", &obj);
2166170222Sdougb		INSIST(result == ISC_R_SUCCESS);
2167170222Sdougb		dns_acache_setcleaninginterval(view->acache,
2168170222Sdougb					       cfg_obj_asuint32(obj) * 60);
2169170222Sdougb
2170170222Sdougb		obj = NULL;
2171170222Sdougb		result = ns_config_get(maps, "max-acache-size", &obj);
2172170222Sdougb		INSIST(result == ISC_R_SUCCESS);
2173170222Sdougb		if (cfg_obj_isstring(obj)) {
2174170222Sdougb			str = cfg_obj_asstring(obj);
2175170222Sdougb			INSIST(strcasecmp(str, "unlimited") == 0);
2176170222Sdougb			max_acache_size = ISC_UINT32_MAX;
2177170222Sdougb		} else {
2178170222Sdougb			isc_resourcevalue_t value;
2179170222Sdougb			value = cfg_obj_asuint64(obj);
2180254897Serwin			if (value > SIZE_MAX) {
2181254897Serwin				cfg_obj_log(obj, ns_g_lctx,
2182254897Serwin					    ISC_LOG_WARNING,
2183170222Sdougb					    "'max-acache-size "
2184254897Serwin					    "%" ISC_PRINT_QUADFORMAT "u' "
2185254897Serwin					    "is too large for this "
2186254897Serwin					    "system; reducing to %lu",
2187254897Serwin					    value, (unsigned long)SIZE_MAX);
2188254897Serwin				value = SIZE_MAX;
2189170222Sdougb			}
2190254897Serwin			max_acache_size = (size_t) value;
2191170222Sdougb		}
2192170222Sdougb		dns_acache_setcachesize(view->acache, max_acache_size);
2193170222Sdougb	}
2194170222Sdougb
2195224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query", NULL, actx,
2196216175Sdougb				 ns_g_mctx, &view->queryacl));
2197216175Sdougb	if (view->queryacl == NULL) {
2198224092Sdougb		CHECK(configure_view_acl(NULL, ns_g_config, "allow-query",
2199224092Sdougb					 NULL, actx, ns_g_mctx,
2200224092Sdougb					 &view->queryacl));
2201216175Sdougb	}
2202216175Sdougb
2203170222Sdougb	/*
2204254402Serwin	 * Make the list of response policy zone names for a view that
2205254402Serwin	 * is used for real lookups and so cares about hints.
2206254402Serwin	 */
2207254402Serwin	obj = NULL;
2208254402Serwin	if (view->rdclass == dns_rdataclass_in && need_hints &&
2209254402Serwin	    ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
2210254402Serwin		const cfg_obj_t *rpz_obj;
2211254402Serwin		isc_boolean_t recursive_only_def;
2212254402Serwin		dns_ttl_t ttl_def;
2213254402Serwin
2214254402Serwin		rpz_obj = cfg_tuple_get(obj, "recursive-only");
2215254402Serwin		if (!cfg_obj_isvoid(rpz_obj) &&
2216254402Serwin		    !cfg_obj_asboolean(rpz_obj))
2217254402Serwin			recursive_only_def = ISC_FALSE;
2218254402Serwin		else
2219254402Serwin			recursive_only_def = ISC_TRUE;
2220254402Serwin
2221254402Serwin		rpz_obj = cfg_tuple_get(obj, "break-dnssec");
2222254402Serwin		if (!cfg_obj_isvoid(rpz_obj) &&
2223254402Serwin		    cfg_obj_asboolean(rpz_obj))
2224254402Serwin			view->rpz_break_dnssec = ISC_TRUE;
2225254402Serwin		else
2226254402Serwin			view->rpz_break_dnssec = ISC_FALSE;
2227254402Serwin
2228254402Serwin		rpz_obj = cfg_tuple_get(obj, "max-policy-ttl");
2229254402Serwin		if (cfg_obj_isuint32(rpz_obj))
2230254402Serwin			ttl_def = cfg_obj_asuint32(rpz_obj);
2231254402Serwin		else
2232254402Serwin			ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
2233254402Serwin
2234254402Serwin		rpz_obj = cfg_tuple_get(obj, "min-ns-dots");
2235254402Serwin		if (cfg_obj_isuint32(rpz_obj))
2236254402Serwin			view->rpz_min_ns_labels = cfg_obj_asuint32(rpz_obj) + 1;
2237254402Serwin		else
2238254402Serwin			view->rpz_min_ns_labels = 2;
2239254402Serwin
2240254402Serwin		element = cfg_list_first(cfg_tuple_get(obj, "zone list"));
2241254402Serwin		while (element != NULL) {
2242254402Serwin			result = configure_rpz(view, element,
2243254402Serwin					       recursive_only_def, ttl_def);
2244254402Serwin			if (result != ISC_R_SUCCESS)
2245254402Serwin				goto cleanup;
2246254402Serwin			element = cfg_list_next(element);
2247254402Serwin		}
2248254402Serwin	}
2249254402Serwin
2250254402Serwin	/*
2251135446Strhodes	 * Configure the zones.
2252135446Strhodes	 */
2253135446Strhodes	zonelist = NULL;
2254135446Strhodes	if (voptions != NULL)
2255135446Strhodes		(void)cfg_map_get(voptions, "zone", &zonelist);
2256135446Strhodes	else
2257135446Strhodes		(void)cfg_map_get(config, "zone", &zonelist);
2258225361Sdougb
2259225361Sdougb	/*
2260225361Sdougb	 * Load zone configuration
2261225361Sdougb	 */
2262135446Strhodes	for (element = cfg_list_first(zonelist);
2263135446Strhodes	     element != NULL;
2264135446Strhodes	     element = cfg_list_next(element))
2265135446Strhodes	{
2266165071Sdougb		const cfg_obj_t *zconfig = cfg_listelt_value(element);
2267135446Strhodes		CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
2268224092Sdougb				     actx, ISC_FALSE));
2269135446Strhodes	}
2270135446Strhodes
2271254402Serwin	for (rpz = ISC_LIST_HEAD(view->rpz_zones);
2272254402Serwin	     rpz != NULL;
2273254402Serwin	     rpz = ISC_LIST_NEXT(rpz, link))
2274254402Serwin	{
2275254402Serwin		if (!rpz->defined) {
2276254402Serwin			char namebuf[DNS_NAME_FORMATSIZE];
2277254402Serwin
2278254402Serwin			dns_name_format(&rpz->origin, namebuf, sizeof(namebuf));
2279254402Serwin			cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
2280254402Serwin				    "'%s' is not a master or slave zone",
2281254402Serwin				    namebuf);
2282254402Serwin			result = ISC_R_NOTFOUND;
2283254402Serwin			goto cleanup;
2284254402Serwin		}
2285254402Serwin	}
2286254402Serwin
2287224092Sdougb	/*
2288224092Sdougb	 * If we're allowing added zones, then load zone configuration
2289224092Sdougb	 * from the newzone file for zones that were added during previous
2290224092Sdougb	 * runs.
2291224092Sdougb	 */
2292225361Sdougb	nzctx = view->new_zone_config;
2293225361Sdougb	if (nzctx != NULL && nzctx->nzconfig != NULL) {
2294224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2295224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
2296224092Sdougb			      "loading additional zones for view '%s'",
2297224092Sdougb			      view->name);
2298224092Sdougb
2299225361Sdougb		zonelist = NULL;
2300225361Sdougb		cfg_map_get(nzctx->nzconfig, "zone", &zonelist);
2301225361Sdougb
2302225361Sdougb		for (element = cfg_list_first(zonelist);
2303225361Sdougb		     element != NULL;
2304225361Sdougb		     element = cfg_list_next(element))
2305225361Sdougb		{
2306225361Sdougb			const cfg_obj_t *zconfig = cfg_listelt_value(element);
2307225361Sdougb			CHECK(configure_zone(config, zconfig, vconfig,
2308225361Sdougb					     mctx, view, actx,
2309225361Sdougb					     ISC_TRUE));
2310224092Sdougb		}
2311224092Sdougb	}
2312224092Sdougb
2313135446Strhodes	/*
2314170222Sdougb	 * Create Dynamically Loadable Zone driver.
2315170222Sdougb	 */
2316170222Sdougb	dlz = NULL;
2317170222Sdougb	if (voptions != NULL)
2318170222Sdougb		(void)cfg_map_get(voptions, "dlz", &dlz);
2319170222Sdougb	else
2320170222Sdougb		(void)cfg_map_get(config, "dlz", &dlz);
2321170222Sdougb
2322170222Sdougb	obj = NULL;
2323170222Sdougb	if (dlz != NULL) {
2324170222Sdougb		(void)cfg_map_get(cfg_tuple_get(dlz, "options"),
2325170222Sdougb				  "database", &obj);
2326170222Sdougb		if (obj != NULL) {
2327170222Sdougb			char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
2328170222Sdougb			if (s == NULL) {
2329170222Sdougb				result = ISC_R_NOMEMORY;
2330170222Sdougb				goto cleanup;
2331170222Sdougb			}
2332186462Sdougb
2333170222Sdougb			result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv);
2334170222Sdougb			if (result != ISC_R_SUCCESS) {
2335170222Sdougb				isc_mem_free(mctx, s);
2336170222Sdougb				goto cleanup;
2337170222Sdougb			}
2338170222Sdougb
2339170222Sdougb			obj = cfg_tuple_get(dlz, "name");
2340170222Sdougb			result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
2341170222Sdougb					       dlzargv[0], dlzargc, dlzargv,
2342170222Sdougb					       &view->dlzdatabase);
2343170222Sdougb			isc_mem_free(mctx, s);
2344170222Sdougb			isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
2345170222Sdougb			if (result != ISC_R_SUCCESS)
2346170222Sdougb				goto cleanup;
2347224092Sdougb
2348224092Sdougb			/*
2349224092Sdougb			 * If the dlz backend supports configuration,
2350224092Sdougb			 * then call its configure method now.
2351224092Sdougb			 */
2352224092Sdougb			result = dns_dlzconfigure(view, dlzconfigure_callback);
2353224092Sdougb			if (result != ISC_R_SUCCESS)
2354224092Sdougb				goto cleanup;
2355170222Sdougb		}
2356170222Sdougb	}
2357170222Sdougb
2358170222Sdougb	/*
2359193149Sdougb	 * Obtain configuration parameters that affect the decision of whether
2360193149Sdougb	 * we can reuse/share an existing cache.
2361193149Sdougb	 */
2362224092Sdougb	obj = NULL;
2363224092Sdougb	result = ns_config_get(maps, "cleaning-interval", &obj);
2364224092Sdougb	INSIST(result == ISC_R_SUCCESS);
2365224092Sdougb	cleaning_interval = cfg_obj_asuint32(obj) * 60;
2366224092Sdougb
2367224092Sdougb	obj = NULL;
2368224092Sdougb	result = ns_config_get(maps, "max-cache-size", &obj);
2369224092Sdougb	INSIST(result == ISC_R_SUCCESS);
2370224092Sdougb	if (cfg_obj_isstring(obj)) {
2371224092Sdougb		str = cfg_obj_asstring(obj);
2372224092Sdougb		INSIST(strcasecmp(str, "unlimited") == 0);
2373224092Sdougb		max_cache_size = ISC_UINT32_MAX;
2374224092Sdougb	} else {
2375224092Sdougb		isc_resourcevalue_t value;
2376224092Sdougb		value = cfg_obj_asuint64(obj);
2377254897Serwin		if (value > SIZE_MAX) {
2378254897Serwin			cfg_obj_log(obj, ns_g_lctx,
2379254897Serwin				    ISC_LOG_WARNING,
2380224092Sdougb				    "'max-cache-size "
2381254897Serwin				    "%" ISC_PRINT_QUADFORMAT "u' "
2382254897Serwin				    "is too large for this "
2383254897Serwin				    "system; reducing to %lu",
2384254897Serwin				    value, (unsigned long)SIZE_MAX);
2385254897Serwin			value = SIZE_MAX;
2386224092Sdougb		}
2387254897Serwin		max_cache_size = (size_t) value;
2388224092Sdougb	}
2389224092Sdougb
2390193149Sdougb	/* Check-names. */
2391193149Sdougb	obj = NULL;
2392193149Sdougb	result = ns_checknames_get(maps, "response", &obj);
2393193149Sdougb	INSIST(result == ISC_R_SUCCESS);
2394193149Sdougb
2395193149Sdougb	str = cfg_obj_asstring(obj);
2396193149Sdougb	if (strcasecmp(str, "fail") == 0) {
2397193149Sdougb		resopts |= DNS_RESOLVER_CHECKNAMES |
2398193149Sdougb			DNS_RESOLVER_CHECKNAMESFAIL;
2399193149Sdougb		view->checknames = ISC_TRUE;
2400193149Sdougb	} else if (strcasecmp(str, "warn") == 0) {
2401193149Sdougb		resopts |= DNS_RESOLVER_CHECKNAMES;
2402193149Sdougb		view->checknames = ISC_FALSE;
2403193149Sdougb	} else if (strcasecmp(str, "ignore") == 0) {
2404193149Sdougb		view->checknames = ISC_FALSE;
2405193149Sdougb	} else
2406193149Sdougb		INSIST(0);
2407193149Sdougb
2408193149Sdougb	obj = NULL;
2409193149Sdougb	result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
2410193149Sdougb	INSIST(result == ISC_R_SUCCESS);
2411193149Sdougb	zero_no_soattl = cfg_obj_asboolean(obj);
2412193149Sdougb
2413193149Sdougb	obj = NULL;
2414224092Sdougb	result = ns_config_get(maps, "dns64", &obj);
2415224092Sdougb	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") &&
2416224092Sdougb	    strcmp(view->name, "_meta")) {
2417224092Sdougb		const cfg_listelt_t *element;
2418224092Sdougb		isc_netaddr_t na, suffix, *sp;
2419224092Sdougb		unsigned int prefixlen;
2420224092Sdougb		const char *server, *contact;
2421224092Sdougb		const cfg_obj_t *myobj;
2422224092Sdougb
2423224092Sdougb		myobj = NULL;
2424224092Sdougb		result = ns_config_get(maps, "dns64-server", &myobj);
2425224092Sdougb		if (result == ISC_R_SUCCESS)
2426224092Sdougb			server = cfg_obj_asstring(myobj);
2427224092Sdougb		else
2428224092Sdougb			server = NULL;
2429224092Sdougb
2430224092Sdougb		myobj = NULL;
2431224092Sdougb		result = ns_config_get(maps, "dns64-contact", &myobj);
2432224092Sdougb		if (result == ISC_R_SUCCESS)
2433224092Sdougb			contact = cfg_obj_asstring(myobj);
2434224092Sdougb		else
2435224092Sdougb			contact = NULL;
2436224092Sdougb
2437224092Sdougb		for (element = cfg_list_first(obj);
2438224092Sdougb		     element != NULL;
2439224092Sdougb		     element = cfg_list_next(element))
2440224092Sdougb		{
2441224092Sdougb			const cfg_obj_t *map = cfg_listelt_value(element);
2442224092Sdougb			dns_dns64_t *dns64 = NULL;
2443224092Sdougb			unsigned int dns64options = 0;
2444224092Sdougb
2445224092Sdougb			cfg_obj_asnetprefix(cfg_map_getname(map), &na,
2446224092Sdougb					    &prefixlen);
2447224092Sdougb
2448224092Sdougb			obj = NULL;
2449224092Sdougb			(void)cfg_map_get(map, "suffix", &obj);
2450224092Sdougb			if (obj != NULL) {
2451224092Sdougb				sp = &suffix;
2452224092Sdougb				isc_netaddr_fromsockaddr(sp,
2453224092Sdougb						      cfg_obj_assockaddr(obj));
2454224092Sdougb			} else
2455224092Sdougb				sp = NULL;
2456224092Sdougb
2457224092Sdougb			clients = mapped = excluded = NULL;
2458224092Sdougb			obj = NULL;
2459224092Sdougb			(void)cfg_map_get(map, "clients", &obj);
2460224092Sdougb			if (obj != NULL) {
2461224092Sdougb				result = cfg_acl_fromconfig(obj, config,
2462224092Sdougb							    ns_g_lctx, actx,
2463224092Sdougb							    mctx, 0, &clients);
2464224092Sdougb				if (result != ISC_R_SUCCESS)
2465224092Sdougb					goto cleanup;
2466224092Sdougb			}
2467224092Sdougb			obj = NULL;
2468224092Sdougb			(void)cfg_map_get(map, "mapped", &obj);
2469224092Sdougb			if (obj != NULL) {
2470224092Sdougb				result = cfg_acl_fromconfig(obj, config,
2471224092Sdougb							    ns_g_lctx, actx,
2472224092Sdougb							    mctx, 0, &mapped);
2473224092Sdougb				if (result != ISC_R_SUCCESS)
2474224092Sdougb					goto cleanup;
2475224092Sdougb			}
2476224092Sdougb			obj = NULL;
2477224092Sdougb			(void)cfg_map_get(map, "exclude", &obj);
2478224092Sdougb			if (obj != NULL) {
2479224092Sdougb				result = cfg_acl_fromconfig(obj, config,
2480224092Sdougb							    ns_g_lctx, actx,
2481224092Sdougb							    mctx, 0, &excluded);
2482224092Sdougb				if (result != ISC_R_SUCCESS)
2483224092Sdougb					goto cleanup;
2484224092Sdougb			}
2485224092Sdougb
2486224092Sdougb			obj = NULL;
2487224092Sdougb			(void)cfg_map_get(map, "recursive-only", &obj);
2488224092Sdougb			if (obj != NULL && cfg_obj_asboolean(obj))
2489224092Sdougb				dns64options |= DNS_DNS64_RECURSIVE_ONLY;
2490224092Sdougb
2491224092Sdougb			obj = NULL;
2492224092Sdougb			(void)cfg_map_get(map, "break-dnssec", &obj);
2493224092Sdougb			if (obj != NULL && cfg_obj_asboolean(obj))
2494224092Sdougb				dns64options |= DNS_DNS64_BREAK_DNSSEC;
2495224092Sdougb
2496224092Sdougb			result = dns_dns64_create(mctx, &na, prefixlen, sp,
2497224092Sdougb						  clients, mapped, excluded,
2498224092Sdougb						  dns64options, &dns64);
2499224092Sdougb			if (result != ISC_R_SUCCESS)
2500224092Sdougb				goto cleanup;
2501224092Sdougb			dns_dns64_append(&view->dns64, dns64);
2502224092Sdougb			view->dns64cnt++;
2503224092Sdougb			result = dns64_reverse(view, mctx, &na, prefixlen,
2504224092Sdougb					       server, contact);
2505224092Sdougb			if (result != ISC_R_SUCCESS)
2506224092Sdougb				goto cleanup;
2507224092Sdougb			if (clients != NULL)
2508224092Sdougb				dns_acl_detach(&clients);
2509224092Sdougb			if (mapped != NULL)
2510224092Sdougb				dns_acl_detach(&mapped);
2511224092Sdougb			if (excluded != NULL)
2512224092Sdougb				dns_acl_detach(&excluded);
2513224092Sdougb		}
2514224092Sdougb	}
2515224092Sdougb
2516224092Sdougb	obj = NULL;
2517193149Sdougb	result = ns_config_get(maps, "dnssec-accept-expired", &obj);
2518193149Sdougb	INSIST(result == ISC_R_SUCCESS);
2519193149Sdougb	view->acceptexpired = cfg_obj_asboolean(obj);
2520193149Sdougb
2521193149Sdougb	obj = NULL;
2522193149Sdougb	result = ns_config_get(maps, "dnssec-validation", &obj);
2523193149Sdougb	INSIST(result == ISC_R_SUCCESS);
2524224092Sdougb	if (cfg_obj_isboolean(obj)) {
2525224092Sdougb		view->enablevalidation = cfg_obj_asboolean(obj);
2526224092Sdougb	} else {
2527224092Sdougb		/* If dnssec-validation is not boolean, it must be "auto" */
2528224092Sdougb		view->enablevalidation = ISC_TRUE;
2529224092Sdougb		auto_root = ISC_TRUE;
2530224092Sdougb	}
2531193149Sdougb
2532193149Sdougb	obj = NULL;
2533193149Sdougb	result = ns_config_get(maps, "max-cache-ttl", &obj);
2534193149Sdougb	INSIST(result == ISC_R_SUCCESS);
2535193149Sdougb	view->maxcachettl = cfg_obj_asuint32(obj);
2536193149Sdougb
2537193149Sdougb	obj = NULL;
2538193149Sdougb	result = ns_config_get(maps, "max-ncache-ttl", &obj);
2539193149Sdougb	INSIST(result == ISC_R_SUCCESS);
2540193149Sdougb	view->maxncachettl = cfg_obj_asuint32(obj);
2541193149Sdougb	if (view->maxncachettl > 7 * 24 * 3600)
2542193149Sdougb		view->maxncachettl = 7 * 24 * 3600;
2543193149Sdougb
2544193149Sdougb	/*
2545224092Sdougb	 * Configure the view's cache.
2546135446Strhodes	 *
2547224092Sdougb	 * First, check to see if there are any attach-cache options.  If yes,
2548224092Sdougb	 * attempt to lookup an existing cache at attach it to the view.  If
2549224092Sdougb	 * there is not one, then try to reuse an existing cache if possible;
2550224092Sdougb	 * otherwise create a new cache.
2551224092Sdougb	 *
2552224092Sdougb	 * Note that the ADB is not preserved or shared in either case.
2553224092Sdougb	 *
2554224092Sdougb	 * When a matching view is found, the associated statistics are also
2555224092Sdougb	 * retrieved and reused.
2556224092Sdougb	 *
2557224092Sdougb	 * XXX Determining when it is safe to reuse or share a cache is tricky.
2558193149Sdougb	 * When the view's configuration changes, the cached data may become
2559193149Sdougb	 * invalid because it reflects our old view of the world.  We check
2560224092Sdougb	 * some of the configuration parameters that could invalidate the cache
2561224092Sdougb	 * or otherwise make it unsharable, but there are other configuration
2562224092Sdougb	 * options that should be checked.  For example, if a view uses a
2563224092Sdougb	 * forwarder, changes in the forwarder configuration may invalidate
2564224092Sdougb	 * the cache.  At the moment, it's the administrator's responsibility to
2565224092Sdougb	 * ensure these configuration options don't invalidate reusing/sharing.
2566135446Strhodes	 */
2567224092Sdougb	obj = NULL;
2568224092Sdougb	result = ns_config_get(maps, "attach-cache", &obj);
2569224092Sdougb	if (result == ISC_R_SUCCESS)
2570224092Sdougb		cachename = cfg_obj_asstring(obj);
2571224092Sdougb	else
2572224092Sdougb		cachename = view->name;
2573224092Sdougb	cache = NULL;
2574224092Sdougb	nsc = cachelist_find(cachelist, cachename);
2575224092Sdougb	if (nsc != NULL) {
2576224092Sdougb		if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
2577224092Sdougb				    cleaning_interval, max_cache_size)) {
2578193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2579224092Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
2580224092Sdougb				      "views %s and %s can't share the cache "
2581193149Sdougb				      "due to configuration parameter mismatch",
2582224092Sdougb				      nsc->primaryview->name, view->name);
2583224092Sdougb			result = ISC_R_FAILURE;
2584224092Sdougb			goto cleanup;
2585193149Sdougb		}
2586224092Sdougb		dns_cache_attach(nsc->cache, &cache);
2587224092Sdougb		shared_cache = ISC_TRUE;
2588224092Sdougb	} else {
2589224092Sdougb		if (strcmp(cachename, view->name) == 0) {
2590224092Sdougb			result = dns_viewlist_find(&ns_g_server->viewlist,
2591224092Sdougb						   cachename, view->rdclass,
2592224092Sdougb						   &pview);
2593224092Sdougb			if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
2594224092Sdougb				goto cleanup;
2595224092Sdougb			if (pview != NULL) {
2596224092Sdougb				if (!cache_reusable(pview, view,
2597224092Sdougb						    zero_no_soattl)) {
2598224092Sdougb					isc_log_write(ns_g_lctx,
2599224092Sdougb						      NS_LOGCATEGORY_GENERAL,
2600224092Sdougb						      NS_LOGMODULE_SERVER,
2601224092Sdougb						      ISC_LOG_DEBUG(1),
2602224092Sdougb						      "cache cannot be reused "
2603224092Sdougb						      "for view %s due to "
2604224092Sdougb						      "configuration parameter "
2605224092Sdougb						      "mismatch", view->name);
2606224092Sdougb				} else {
2607224092Sdougb					INSIST(pview->cache != NULL);
2608224092Sdougb					isc_log_write(ns_g_lctx,
2609224092Sdougb						      NS_LOGCATEGORY_GENERAL,
2610224092Sdougb						      NS_LOGMODULE_SERVER,
2611224092Sdougb						      ISC_LOG_DEBUG(3),
2612224092Sdougb						      "reusing existing cache");
2613224092Sdougb					reused_cache = ISC_TRUE;
2614224092Sdougb					dns_cache_attach(pview->cache, &cache);
2615224092Sdougb				}
2616224092Sdougb				dns_view_getresstats(pview, &resstats);
2617224092Sdougb				dns_view_getresquerystats(pview,
2618224092Sdougb							  &resquerystats);
2619224092Sdougb				dns_view_detach(&pview);
2620224092Sdougb			}
2621224092Sdougb		}
2622224092Sdougb		if (cache == NULL) {
2623224092Sdougb			/*
2624224092Sdougb			 * Create a cache with the desired name.  This normally
2625224092Sdougb			 * equals the view name, but may also be a forward
2626224092Sdougb			 * reference to a view that share the cache with this
2627224092Sdougb			 * view but is not yet configured.  If it is not the
2628224092Sdougb			 * view name but not a forward reference either, then it
2629224092Sdougb			 * is simply a named cache that is not shared.
2630225361Sdougb			 *
2631225361Sdougb			 * We use two separate memory contexts for the
2632225361Sdougb			 * cache, for the main cache memory and the heap
2633225361Sdougb			 * memory.
2634224092Sdougb			 */
2635224092Sdougb			CHECK(isc_mem_create(0, 0, &cmctx));
2636224092Sdougb			isc_mem_setname(cmctx, "cache", NULL);
2637225361Sdougb			CHECK(isc_mem_create(0, 0, &hmctx));
2638225361Sdougb			isc_mem_setname(hmctx, "cache_heap", NULL);
2639225361Sdougb			CHECK(dns_cache_create3(cmctx, hmctx, ns_g_taskmgr,
2640224092Sdougb						ns_g_timermgr, view->rdclass,
2641224092Sdougb						cachename, "rbt", 0, NULL,
2642224092Sdougb						&cache));
2643225361Sdougb			isc_mem_detach(&cmctx);
2644225361Sdougb			isc_mem_detach(&hmctx);
2645224092Sdougb		}
2646224092Sdougb		nsc = isc_mem_get(mctx, sizeof(*nsc));
2647224092Sdougb		if (nsc == NULL) {
2648224092Sdougb			result = ISC_R_NOMEMORY;
2649224092Sdougb			goto cleanup;
2650224092Sdougb		}
2651224092Sdougb		nsc->cache = NULL;
2652224092Sdougb		dns_cache_attach(cache, &nsc->cache);
2653224092Sdougb		nsc->primaryview = view;
2654224092Sdougb		nsc->needflush = ISC_FALSE;
2655224092Sdougb		nsc->adbsizeadjusted = ISC_FALSE;
2656224092Sdougb		ISC_LINK_INIT(nsc, link);
2657224092Sdougb		ISC_LIST_APPEND(*cachelist, nsc, link);
2658193149Sdougb	}
2659224092Sdougb	dns_view_setcache2(view, cache, shared_cache);
2660135446Strhodes
2661135446Strhodes	/*
2662135446Strhodes	 * cache-file cannot be inherited if views are present, but this
2663135446Strhodes	 * should be caught by the configuration checking stage.
2664135446Strhodes	 */
2665135446Strhodes	obj = NULL;
2666135446Strhodes	result = ns_config_get(maps, "cache-file", &obj);
2667135446Strhodes	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
2668135446Strhodes		CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
2669224092Sdougb		if (!reused_cache && !shared_cache)
2670135446Strhodes			CHECK(dns_cache_load(cache));
2671135446Strhodes	}
2672135446Strhodes
2673224092Sdougb	dns_cache_setcleaninginterval(cache, cleaning_interval);
2674135446Strhodes	dns_cache_setcachesize(cache, max_cache_size);
2675135446Strhodes
2676135446Strhodes	dns_cache_detach(&cache);
2677135446Strhodes
2678135446Strhodes	/*
2679135446Strhodes	 * Resolver.
2680135446Strhodes	 *
2681135446Strhodes	 * XXXRTH  Hardwired number of tasks.
2682135446Strhodes	 */
2683186462Sdougb	CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4,
2684186462Sdougb					    ISC_TF(ISC_LIST_PREV(view, link)
2685186462Sdougb						   == NULL)));
2686186462Sdougb	CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6,
2687186462Sdougb					    ISC_TF(ISC_LIST_PREV(view, link)
2688186462Sdougb						   == NULL)));
2689135446Strhodes	if (dispatch4 == NULL && dispatch6 == NULL) {
2690135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
2691135446Strhodes				 "unable to obtain neither an IPv4 nor"
2692135446Strhodes				 " an IPv6 dispatch");
2693135446Strhodes		result = ISC_R_UNEXPECTED;
2694135446Strhodes		goto cleanup;
2695135446Strhodes	}
2696254897Serwin
2697254897Serwin	ndisp = 4 * ISC_MIN(ns_g_udpdisp, MAX_UDP_DISPATCH);
2698254897Serwin	CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31, ndisp,
2699135446Strhodes				      ns_g_socketmgr, ns_g_timermgr,
2700193149Sdougb				      resopts, ns_g_dispatchmgr,
2701135446Strhodes				      dispatch4, dispatch6));
2702135446Strhodes
2703193149Sdougb	if (resstats == NULL) {
2704193149Sdougb		CHECK(isc_stats_create(mctx, &resstats,
2705193149Sdougb				       dns_resstatscounter_max));
2706193149Sdougb	}
2707193149Sdougb	dns_view_setresstats(view, resstats);
2708193149Sdougb	if (resquerystats == NULL)
2709193149Sdougb		CHECK(dns_rdatatypestats_create(mctx, &resquerystats));
2710193149Sdougb	dns_view_setresquerystats(view, resquerystats);
2711193149Sdougb
2712135446Strhodes	/*
2713224092Sdougb	 * Set the ADB cache size to 1/8th of the max-cache-size or
2714224092Sdougb	 * MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared.
2715135446Strhodes	 */
2716135446Strhodes	max_adb_size = 0;
2717254402Serwin	if (max_cache_size != 0U) {
2718135446Strhodes		max_adb_size = max_cache_size / 8;
2719254402Serwin		if (max_adb_size == 0U)
2720135446Strhodes			max_adb_size = 1;	/* Force minimum. */
2721224092Sdougb		if (view != nsc->primaryview &&
2722224092Sdougb		    max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE) {
2723224092Sdougb			max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE;
2724224092Sdougb			if (!nsc->adbsizeadjusted) {
2725224092Sdougb				dns_adb_setadbsize(nsc->primaryview->adb,
2726224092Sdougb						   MAX_ADB_SIZE_FOR_CACHESHARE);
2727224092Sdougb				nsc->adbsizeadjusted = ISC_TRUE;
2728224092Sdougb			}
2729224092Sdougb		}
2730135446Strhodes	}
2731135446Strhodes	dns_adb_setadbsize(view->adb, max_adb_size);
2732135446Strhodes
2733135446Strhodes	/*
2734135446Strhodes	 * Set resolver's lame-ttl.
2735135446Strhodes	 */
2736135446Strhodes	obj = NULL;
2737135446Strhodes	result = ns_config_get(maps, "lame-ttl", &obj);
2738135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2739135446Strhodes	lame_ttl = cfg_obj_asuint32(obj);
2740135446Strhodes	if (lame_ttl > 1800)
2741135446Strhodes		lame_ttl = 1800;
2742135446Strhodes	dns_resolver_setlamettl(view->resolver, lame_ttl);
2743170222Sdougb
2744135446Strhodes	/*
2745224092Sdougb	 * Set the resolver's query timeout.
2746224092Sdougb	 */
2747224092Sdougb	obj = NULL;
2748224092Sdougb	result = ns_config_get(maps, "resolver-query-timeout", &obj);
2749224092Sdougb	INSIST(result == ISC_R_SUCCESS);
2750224092Sdougb	query_timeout = cfg_obj_asuint32(obj);
2751224092Sdougb	dns_resolver_settimeout(view->resolver, query_timeout);
2752224092Sdougb
2753224092Sdougb	/* Specify whether to use 0-TTL for negative response for SOA query */
2754224092Sdougb	dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl);
2755224092Sdougb
2756224092Sdougb	/*
2757135446Strhodes	 * Set the resolver's EDNS UDP size.
2758135446Strhodes	 */
2759135446Strhodes	obj = NULL;
2760135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
2761135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2762135446Strhodes	udpsize = cfg_obj_asuint32(obj);
2763135446Strhodes	if (udpsize < 512)
2764135446Strhodes		udpsize = 512;
2765135446Strhodes	if (udpsize > 4096)
2766135446Strhodes		udpsize = 4096;
2767135446Strhodes	dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize);
2768186462Sdougb
2769135446Strhodes	/*
2770170222Sdougb	 * Set the maximum UDP response size.
2771170222Sdougb	 */
2772170222Sdougb	obj = NULL;
2773170222Sdougb	result = ns_config_get(maps, "max-udp-size", &obj);
2774170222Sdougb	INSIST(result == ISC_R_SUCCESS);
2775170222Sdougb	udpsize = cfg_obj_asuint32(obj);
2776170222Sdougb	if (udpsize < 512)
2777170222Sdougb		udpsize = 512;
2778170222Sdougb	if (udpsize > 4096)
2779170222Sdougb		udpsize = 4096;
2780170222Sdougb	view->maxudp = udpsize;
2781170222Sdougb
2782170222Sdougb	/*
2783254897Serwin	 * Set the maximum rsa exponent bits.
2784254897Serwin	 */
2785254897Serwin	obj = NULL;
2786254897Serwin	result = ns_config_get(maps, "max-rsa-exponent-size", &obj);
2787254897Serwin	INSIST(result == ISC_R_SUCCESS);
2788254897Serwin	maxbits = cfg_obj_asuint32(obj);
2789254897Serwin	if (maxbits != 0 && maxbits < 35)
2790254897Serwin		maxbits = 35;
2791254897Serwin	if (maxbits > 4096)
2792254897Serwin		maxbits = 4096;
2793254897Serwin	view->maxbits = maxbits;
2794254897Serwin
2795254897Serwin	/*
2796135446Strhodes	 * Set supported DNSSEC algorithms.
2797135446Strhodes	 */
2798135446Strhodes	dns_resolver_reset_algorithms(view->resolver);
2799135446Strhodes	disabled = NULL;
2800135446Strhodes	(void)ns_config_get(maps, "disable-algorithms", &disabled);
2801135446Strhodes	if (disabled != NULL) {
2802135446Strhodes		for (element = cfg_list_first(disabled);
2803135446Strhodes		     element != NULL;
2804135446Strhodes		     element = cfg_list_next(element))
2805135446Strhodes			CHECK(disable_algorithms(cfg_listelt_value(element),
2806135446Strhodes						 view->resolver));
2807135446Strhodes	}
2808135446Strhodes
2809135446Strhodes	/*
2810135446Strhodes	 * A global or view "forwarders" option, if present,
2811135446Strhodes	 * creates an entry for "." in the forwarding table.
2812135446Strhodes	 */
2813135446Strhodes	forwardtype = NULL;
2814135446Strhodes	forwarders = NULL;
2815135446Strhodes	(void)ns_config_get(maps, "forward", &forwardtype);
2816135446Strhodes	(void)ns_config_get(maps, "forwarders", &forwarders);
2817135446Strhodes	if (forwarders != NULL)
2818186462Sdougb		CHECK(configure_forward(config, view, dns_rootname,
2819135446Strhodes					forwarders, forwardtype));
2820135446Strhodes
2821135446Strhodes	/*
2822135446Strhodes	 * Dual Stack Servers.
2823135446Strhodes	 */
2824135446Strhodes	alternates = NULL;
2825135446Strhodes	(void)ns_config_get(maps, "dual-stack-servers", &alternates);
2826135446Strhodes	if (alternates != NULL)
2827135446Strhodes		CHECK(configure_alternates(config, view, alternates));
2828135446Strhodes
2829135446Strhodes	/*
2830135446Strhodes	 * We have default hints for class IN if we need them.
2831135446Strhodes	 */
2832135446Strhodes	if (view->rdclass == dns_rdataclass_in && view->hints == NULL)
2833135446Strhodes		dns_view_sethints(view, ns_g_server->in_roothints);
2834135446Strhodes
2835135446Strhodes	/*
2836135446Strhodes	 * If we still have no hints, this is a non-IN view with no
2837135446Strhodes	 * "hints zone" configured.  Issue a warning, except if this
2838186462Sdougb	 * is a root server.  Root servers never need to consult
2839135446Strhodes	 * their hints, so it's no point requiring users to configure
2840135446Strhodes	 * them.
2841135446Strhodes	 */
2842135446Strhodes	if (view->hints == NULL) {
2843135446Strhodes		dns_zone_t *rootzone = NULL;
2844135446Strhodes		(void)dns_view_findzone(view, dns_rootname, &rootzone);
2845135446Strhodes		if (rootzone != NULL) {
2846135446Strhodes			dns_zone_detach(&rootzone);
2847135446Strhodes			need_hints = ISC_FALSE;
2848135446Strhodes		}
2849135446Strhodes		if (need_hints)
2850135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
2851135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
2852135446Strhodes				      "no root hints for view '%s'",
2853135446Strhodes				      view->name);
2854135446Strhodes	}
2855135446Strhodes
2856135446Strhodes	/*
2857135446Strhodes	 * Configure the view's TSIG keys.
2858135446Strhodes	 */
2859135446Strhodes	CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
2860224092Sdougb	if (ns_g_server->sessionkey != NULL) {
2861224092Sdougb		CHECK(dns_tsigkeyring_add(ring, ns_g_server->session_keyname,
2862224092Sdougb					  ns_g_server->sessionkey));
2863224092Sdougb	}
2864135446Strhodes	dns_view_setkeyring(view, ring);
2865224092Sdougb	dns_tsigkeyring_detach(&ring);
2866135446Strhodes
2867135446Strhodes	/*
2868224092Sdougb	 * See if we can re-use a dynamic key ring.
2869224092Sdougb	 */
2870224092Sdougb	result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
2871224092Sdougb				   view->rdclass, &pview);
2872224092Sdougb	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
2873224092Sdougb		goto cleanup;
2874224092Sdougb	if (pview != NULL) {
2875224092Sdougb		dns_view_getdynamickeyring(pview, &ring);
2876224092Sdougb		if (ring != NULL)
2877224092Sdougb			dns_view_setdynamickeyring(view, ring);
2878224092Sdougb		dns_tsigkeyring_detach(&ring);
2879224092Sdougb		dns_view_detach(&pview);
2880224092Sdougb	} else
2881224092Sdougb		dns_view_restorekeyring(view);
2882224092Sdougb
2883224092Sdougb	/*
2884135446Strhodes	 * Configure the view's peer list.
2885135446Strhodes	 */
2886135446Strhodes	{
2887165071Sdougb		const cfg_obj_t *peers = NULL;
2888165071Sdougb		const cfg_listelt_t *element;
2889135446Strhodes		dns_peerlist_t *newpeers = NULL;
2890135446Strhodes
2891135446Strhodes		(void)ns_config_get(cfgmaps, "server", &peers);
2892135446Strhodes		CHECK(dns_peerlist_new(mctx, &newpeers));
2893135446Strhodes		for (element = cfg_list_first(peers);
2894135446Strhodes		     element != NULL;
2895135446Strhodes		     element = cfg_list_next(element))
2896135446Strhodes		{
2897165071Sdougb			const cfg_obj_t *cpeer = cfg_listelt_value(element);
2898135446Strhodes			dns_peer_t *peer;
2899135446Strhodes
2900135446Strhodes			CHECK(configure_peer(cpeer, mctx, &peer));
2901135446Strhodes			dns_peerlist_addpeer(newpeers, peer);
2902135446Strhodes			dns_peer_detach(&peer);
2903135446Strhodes		}
2904135446Strhodes		dns_peerlist_detach(&view->peers);
2905135446Strhodes		view->peers = newpeers; /* Transfer ownership. */
2906135446Strhodes	}
2907135446Strhodes
2908135446Strhodes	/*
2909135446Strhodes	 *	Configure the views rrset-order.
2910135446Strhodes	 */
2911135446Strhodes	{
2912165071Sdougb		const cfg_obj_t *rrsetorder = NULL;
2913165071Sdougb		const cfg_listelt_t *element;
2914135446Strhodes
2915135446Strhodes		(void)ns_config_get(maps, "rrset-order", &rrsetorder);
2916135446Strhodes		CHECK(dns_order_create(mctx, &order));
2917135446Strhodes		for (element = cfg_list_first(rrsetorder);
2918135446Strhodes		     element != NULL;
2919135446Strhodes		     element = cfg_list_next(element))
2920135446Strhodes		{
2921165071Sdougb			const cfg_obj_t *ent = cfg_listelt_value(element);
2922135446Strhodes
2923135446Strhodes			CHECK(configure_order(order, ent));
2924135446Strhodes		}
2925135446Strhodes		if (view->order != NULL)
2926135446Strhodes			dns_order_detach(&view->order);
2927135446Strhodes		dns_order_attach(order, &view->order);
2928135446Strhodes		dns_order_detach(&order);
2929135446Strhodes	}
2930135446Strhodes	/*
2931135446Strhodes	 * Copy the aclenv object.
2932135446Strhodes	 */
2933135446Strhodes	dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv);
2934135446Strhodes
2935135446Strhodes	/*
2936135446Strhodes	 * Configure the "match-clients" and "match-destinations" ACL.
2937135446Strhodes	 */
2938224092Sdougb	CHECK(configure_view_acl(vconfig, config, "match-clients", NULL, actx,
2939135446Strhodes				 ns_g_mctx, &view->matchclients));
2940224092Sdougb	CHECK(configure_view_acl(vconfig, config, "match-destinations", NULL,
2941224092Sdougb				 actx, ns_g_mctx, &view->matchdestinations));
2942135446Strhodes
2943135446Strhodes	/*
2944135446Strhodes	 * Configure the "match-recursive-only" option.
2945135446Strhodes	 */
2946135446Strhodes	obj = NULL;
2947165071Sdougb	(void)ns_config_get(maps, "match-recursive-only", &obj);
2948135446Strhodes	if (obj != NULL && cfg_obj_asboolean(obj))
2949135446Strhodes		view->matchrecursiveonly = ISC_TRUE;
2950135446Strhodes	else
2951135446Strhodes		view->matchrecursiveonly = ISC_FALSE;
2952135446Strhodes
2953135446Strhodes	/*
2954135446Strhodes	 * Configure other configurable data.
2955135446Strhodes	 */
2956135446Strhodes	obj = NULL;
2957135446Strhodes	result = ns_config_get(maps, "recursion", &obj);
2958135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2959135446Strhodes	view->recursion = cfg_obj_asboolean(obj);
2960135446Strhodes
2961135446Strhodes	obj = NULL;
2962135446Strhodes	result = ns_config_get(maps, "auth-nxdomain", &obj);
2963135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2964135446Strhodes	view->auth_nxdomain = cfg_obj_asboolean(obj);
2965135446Strhodes
2966135446Strhodes	obj = NULL;
2967135446Strhodes	result = ns_config_get(maps, "minimal-responses", &obj);
2968135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2969135446Strhodes	view->minimalresponses = cfg_obj_asboolean(obj);
2970135446Strhodes
2971135446Strhodes	obj = NULL;
2972135446Strhodes	result = ns_config_get(maps, "transfer-format", &obj);
2973135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2974135446Strhodes	str = cfg_obj_asstring(obj);
2975135446Strhodes	if (strcasecmp(str, "many-answers") == 0)
2976135446Strhodes		view->transfer_format = dns_many_answers;
2977135446Strhodes	else if (strcasecmp(str, "one-answer") == 0)
2978135446Strhodes		view->transfer_format = dns_one_answer;
2979135446Strhodes	else
2980135446Strhodes		INSIST(0);
2981186462Sdougb
2982135446Strhodes	/*
2983135446Strhodes	 * Set sources where additional data and CNAME/DNAME
2984135446Strhodes	 * targets for authoritative answers may be found.
2985135446Strhodes	 */
2986135446Strhodes	obj = NULL;
2987135446Strhodes	result = ns_config_get(maps, "additional-from-auth", &obj);
2988135446Strhodes	INSIST(result == ISC_R_SUCCESS);
2989135446Strhodes	view->additionalfromauth = cfg_obj_asboolean(obj);
2990135446Strhodes	if (view->recursion && ! view->additionalfromauth) {
2991135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
2992135446Strhodes			    "'additional-from-auth no' is only supported "
2993135446Strhodes			    "with 'recursion no'");
2994135446Strhodes		view->additionalfromauth = ISC_TRUE;
2995135446Strhodes	}
2996135446Strhodes
2997135446Strhodes	obj = NULL;
2998135446Strhodes	result = ns_config_get(maps, "additional-from-cache", &obj);
2999135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3000135446Strhodes	view->additionalfromcache = cfg_obj_asboolean(obj);
3001135446Strhodes	if (view->recursion && ! view->additionalfromcache) {
3002135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
3003135446Strhodes			    "'additional-from-cache no' is only supported "
3004135446Strhodes			    "with 'recursion no'");
3005135446Strhodes		view->additionalfromcache = ISC_TRUE;
3006135446Strhodes	}
3007135446Strhodes
3008171577Sdougb	/*
3009193149Sdougb	 * Set "allow-query-cache", "allow-query-cache-on",
3010193149Sdougb	 * "allow-recursion", and "allow-recursion-on" acls if
3011171577Sdougb	 * configured in named.conf.
3012171577Sdougb	 */
3013224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL,
3014216175Sdougb				 actx, ns_g_mctx, &view->cacheacl));
3015224092Sdougb	CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL,
3016216175Sdougb				 actx, ns_g_mctx, &view->cacheonacl));
3017216175Sdougb	if (view->cacheonacl == NULL)
3018193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
3019224092Sdougb					 "allow-query-cache-on", NULL, actx,
3020216175Sdougb					 ns_g_mctx, &view->cacheonacl));
3021193149Sdougb	if (strcmp(view->name, "_bind") != 0) {
3022135446Strhodes		CHECK(configure_view_acl(vconfig, config, "allow-recursion",
3023224092Sdougb					 NULL, actx, ns_g_mctx,
3024193149Sdougb					 &view->recursionacl));
3025193149Sdougb		CHECK(configure_view_acl(vconfig, config, "allow-recursion-on",
3026224092Sdougb					 NULL, actx, ns_g_mctx,
3027193149Sdougb					 &view->recursiononacl));
3028193149Sdougb	}
3029135446Strhodes
3030135446Strhodes	/*
3031171577Sdougb	 * "allow-query-cache" inherits from "allow-recursion" if set,
3032171577Sdougb	 * otherwise from "allow-query" if set.
3033171577Sdougb	 * "allow-recursion" inherits from "allow-query-cache" if set,
3034171577Sdougb	 * otherwise from "allow-query" if set.
3035170222Sdougb	 */
3036216175Sdougb	if (view->cacheacl == NULL && view->recursionacl != NULL)
3037216175Sdougb		dns_acl_attach(view->recursionacl, &view->cacheacl);
3038224092Sdougb	/*
3039224092Sdougb	 * XXXEACH: This call to configure_view_acl() is redundant.  We
3040224092Sdougb	 * are leaving it as it is because we are making a minimal change
3041224092Sdougb	 * for a patch release.  In the future this should be changed to
3042224092Sdougb	 * dns_acl_attach(view->queryacl, &view->cacheacl).
3043224092Sdougb	 */
3044216175Sdougb	if (view->cacheacl == NULL && view->recursion)
3045224092Sdougb		CHECK(configure_view_acl(vconfig, config, "allow-query", NULL,
3046216175Sdougb					 actx, ns_g_mctx, &view->cacheacl));
3047193149Sdougb	if (view->recursion &&
3048216175Sdougb	    view->recursionacl == NULL && view->cacheacl != NULL)
3049216175Sdougb		dns_acl_attach(view->cacheacl, &view->recursionacl);
3050171577Sdougb
3051171577Sdougb	/*
3052193149Sdougb	 * Set default "allow-recursion", "allow-recursion-on" and
3053193149Sdougb	 * "allow-query-cache" acls.
3054171577Sdougb	 */
3055170222Sdougb	if (view->recursionacl == NULL && view->recursion)
3056171577Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
3057224092Sdougb					 "allow-recursion", NULL,
3058193149Sdougb					 actx, ns_g_mctx,
3059193149Sdougb					 &view->recursionacl));
3060193149Sdougb	if (view->recursiononacl == NULL && view->recursion)
3061193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
3062224092Sdougb					 "allow-recursion-on", NULL,
3063193149Sdougb					 actx, ns_g_mctx,
3064193149Sdougb					 &view->recursiononacl));
3065216175Sdougb	if (view->cacheacl == NULL) {
3066193149Sdougb		if (view->recursion)
3067193149Sdougb			CHECK(configure_view_acl(NULL, ns_g_config,
3068224092Sdougb						 "allow-query-cache", NULL,
3069224092Sdougb						 actx, ns_g_mctx,
3070224092Sdougb						 &view->cacheacl));
3071216175Sdougb		else
3072224092Sdougb			CHECK(dns_acl_none(mctx, &view->cacheacl));
3073193149Sdougb	}
3074170222Sdougb
3075193149Sdougb	/*
3076224092Sdougb	 * Filter setting on addresses in the answer section.
3077224092Sdougb	 */
3078224092Sdougb	CHECK(configure_view_acl(vconfig, config, "deny-answer-addresses",
3079224092Sdougb				 "acl", actx, ns_g_mctx, &view->denyansweracl));
3080224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses",
3081224092Sdougb				       "except-from", ns_g_mctx,
3082224092Sdougb				       &view->answeracl_exclude));
3083224092Sdougb
3084224092Sdougb	/*
3085224092Sdougb	 * Filter setting on names (CNAME/DNAME targets) in the answer section.
3086224092Sdougb	 */
3087224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
3088224092Sdougb				       "name", ns_g_mctx,
3089224092Sdougb				       &view->denyanswernames));
3090224092Sdougb	CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
3091224092Sdougb				       "except-from", ns_g_mctx,
3092224092Sdougb				       &view->answernames_exclude));
3093224092Sdougb
3094224092Sdougb	/*
3095193149Sdougb	 * Configure sortlist, if set
3096193149Sdougb	 */
3097193149Sdougb	CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx,
3098193149Sdougb				      &view->sortlist));
3099135446Strhodes
3100193149Sdougb	/*
3101193149Sdougb	 * Configure default allow-transfer, allow-notify, allow-update
3102193149Sdougb	 * and allow-update-forwarding ACLs, if set, so they can be
3103193149Sdougb	 * inherited by zones.
3104193149Sdougb	 */
3105193149Sdougb	if (view->notifyacl == NULL)
3106193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
3107224092Sdougb					 "allow-notify", NULL, actx,
3108193149Sdougb					 ns_g_mctx, &view->notifyacl));
3109193149Sdougb	if (view->transferacl == NULL)
3110193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
3111224092Sdougb					 "allow-transfer", NULL, actx,
3112193149Sdougb					 ns_g_mctx, &view->transferacl));
3113193149Sdougb	if (view->updateacl == NULL)
3114193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
3115224092Sdougb					 "allow-update", NULL, actx,
3116193149Sdougb					 ns_g_mctx, &view->updateacl));
3117193149Sdougb	if (view->upfwdacl == NULL)
3118193149Sdougb		CHECK(configure_view_acl(NULL, ns_g_config,
3119224092Sdougb					 "allow-update-forwarding", NULL, actx,
3120193149Sdougb					 ns_g_mctx, &view->upfwdacl));
3121193149Sdougb
3122135446Strhodes	obj = NULL;
3123135446Strhodes	result = ns_config_get(maps, "provide-ixfr", &obj);
3124135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3125135446Strhodes	view->provideixfr = cfg_obj_asboolean(obj);
3126170222Sdougb
3127170222Sdougb	obj = NULL;
3128193149Sdougb	result = ns_config_get(maps, "request-nsid", &obj);
3129193149Sdougb	INSIST(result == ISC_R_SUCCESS);
3130193149Sdougb	view->requestnsid = cfg_obj_asboolean(obj);
3131193149Sdougb
3132193149Sdougb	obj = NULL;
3133170222Sdougb	result = ns_config_get(maps, "max-clients-per-query", &obj);
3134170222Sdougb	INSIST(result == ISC_R_SUCCESS);
3135170222Sdougb	max_clients_per_query = cfg_obj_asuint32(obj);
3136170222Sdougb
3137170222Sdougb	obj = NULL;
3138170222Sdougb	result = ns_config_get(maps, "clients-per-query", &obj);
3139170222Sdougb	INSIST(result == ISC_R_SUCCESS);
3140170222Sdougb	dns_resolver_setclientsperquery(view->resolver,
3141170222Sdougb					cfg_obj_asuint32(obj),
3142170222Sdougb					max_clients_per_query);
3143186462Sdougb
3144275672Sdelphij	obj = NULL;
3145275672Sdelphij	result = ns_config_get(maps, "max-recursion-depth", &obj);
3146275672Sdelphij	INSIST(result == ISC_R_SUCCESS);
3147275672Sdelphij	dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj));
3148275672Sdelphij
3149224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
3150135446Strhodes	obj = NULL;
3151224092Sdougb	result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
3152224092Sdougb	INSIST(result == ISC_R_SUCCESS);
3153224092Sdougb	if (cfg_obj_isboolean(obj)) {
3154224092Sdougb		if (cfg_obj_asboolean(obj))
3155224092Sdougb			view->v4_aaaa = dns_v4_aaaa_filter;
3156224092Sdougb		else
3157224092Sdougb			view->v4_aaaa = dns_v4_aaaa_ok;
3158224092Sdougb	} else {
3159224092Sdougb		const char *v4_aaaastr = cfg_obj_asstring(obj);
3160224092Sdougb		if (strcasecmp(v4_aaaastr, "break-dnssec") == 0)
3161224092Sdougb			view->v4_aaaa = dns_v4_aaaa_break_dnssec;
3162224092Sdougb		else
3163224092Sdougb			INSIST(0);
3164224092Sdougb	}
3165224092Sdougb	CHECK(configure_view_acl(vconfig, config, "filter-aaaa", NULL,
3166224092Sdougb				 actx, ns_g_mctx, &view->v4_aaaa_acl));
3167224092Sdougb#endif
3168224092Sdougb
3169224092Sdougb	obj = NULL;
3170135446Strhodes	result = ns_config_get(maps, "dnssec-enable", &obj);
3171135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3172135446Strhodes	view->enablednssec = cfg_obj_asboolean(obj);
3173135446Strhodes
3174135446Strhodes	obj = NULL;
3175224092Sdougb	result = ns_config_get(optionmaps, "dnssec-lookaside", &obj);
3176135446Strhodes	if (result == ISC_R_SUCCESS) {
3177224092Sdougb		/* If set to "auto", use the version from the defaults */
3178224092Sdougb		const cfg_obj_t *dlvobj;
3179234010Sdougb		const char *dom;
3180224092Sdougb		dlvobj = cfg_listelt_value(cfg_list_first(obj));
3181234010Sdougb		dom = cfg_obj_asstring(cfg_tuple_get(dlvobj, "domain"));
3182234010Sdougb		if (cfg_obj_isvoid(cfg_tuple_get(dlvobj, "trust-anchor"))) {
3183234010Sdougb			/* If "no", skip; if "auto", use global default */
3184234010Sdougb			if (!strcasecmp(dom, "no"))
3185234010Sdougb				result = ISC_R_NOTFOUND;
3186234010Sdougb			else if (!strcasecmp(dom, "auto")) {
3187234010Sdougb				auto_dlv = ISC_TRUE;
3188234010Sdougb				obj = NULL;
3189234010Sdougb				result = cfg_map_get(ns_g_defaults,
3190234010Sdougb						     "dnssec-lookaside", &obj);
3191234010Sdougb			}
3192224092Sdougb		}
3193224092Sdougb	}
3194224092Sdougb
3195224092Sdougb	if (result == ISC_R_SUCCESS) {
3196135446Strhodes		for (element = cfg_list_first(obj);
3197135446Strhodes		     element != NULL;
3198135446Strhodes		     element = cfg_list_next(element))
3199135446Strhodes		{
3200135446Strhodes			const char *str;
3201135446Strhodes			isc_buffer_t b;
3202135446Strhodes			dns_name_t *dlv;
3203135446Strhodes
3204135446Strhodes			obj = cfg_listelt_value(element);
3205135446Strhodes			str = cfg_obj_asstring(cfg_tuple_get(obj,
3206135446Strhodes							     "trust-anchor"));
3207254402Serwin			isc_buffer_constinit(&b, str, strlen(str));
3208135446Strhodes			isc_buffer_add(&b, strlen(str));
3209135446Strhodes			dlv = dns_fixedname_name(&view->dlv_fixed);
3210135446Strhodes			CHECK(dns_name_fromtext(dlv, &b, dns_rootname,
3211224092Sdougb						DNS_NAME_DOWNCASE, NULL));
3212135446Strhodes			view->dlv = dns_fixedname_name(&view->dlv_fixed);
3213135446Strhodes		}
3214135446Strhodes	} else
3215135446Strhodes		view->dlv = NULL;
3216135446Strhodes
3217135446Strhodes	/*
3218135446Strhodes	 * For now, there is only one kind of trusted keys, the
3219135446Strhodes	 * "security roots".
3220135446Strhodes	 */
3221224092Sdougb	CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys,
3222224092Sdougb					auto_dlv, auto_root, mctx));
3223170222Sdougb	dns_resolver_resetmustbesecure(view->resolver);
3224170222Sdougb	obj = NULL;
3225170222Sdougb	result = ns_config_get(maps, "dnssec-must-be-secure", &obj);
3226170222Sdougb	if (result == ISC_R_SUCCESS)
3227170222Sdougb		CHECK(mustbesecure(obj, view->resolver));
3228135446Strhodes
3229135446Strhodes	obj = NULL;
3230135446Strhodes	result = ns_config_get(maps, "preferred-glue", &obj);
3231135446Strhodes	if (result == ISC_R_SUCCESS) {
3232135446Strhodes		str = cfg_obj_asstring(obj);
3233135446Strhodes		if (strcasecmp(str, "a") == 0)
3234135446Strhodes			view->preferred_glue = dns_rdatatype_a;
3235135446Strhodes		else if (strcasecmp(str, "aaaa") == 0)
3236135446Strhodes			view->preferred_glue = dns_rdatatype_aaaa;
3237135446Strhodes		else
3238135446Strhodes			view->preferred_glue = 0;
3239135446Strhodes	} else
3240135446Strhodes		view->preferred_glue = 0;
3241135446Strhodes
3242135446Strhodes	obj = NULL;
3243135446Strhodes	result = ns_config_get(maps, "root-delegation-only", &obj);
3244135446Strhodes	if (result == ISC_R_SUCCESS) {
3245135446Strhodes		dns_view_setrootdelonly(view, ISC_TRUE);
3246135446Strhodes		if (!cfg_obj_isvoid(obj)) {
3247135446Strhodes			dns_fixedname_t fixed;
3248135446Strhodes			dns_name_t *name;
3249135446Strhodes			isc_buffer_t b;
3250165071Sdougb			const char *str;
3251165071Sdougb			const cfg_obj_t *exclude;
3252135446Strhodes
3253135446Strhodes			dns_fixedname_init(&fixed);
3254135446Strhodes			name = dns_fixedname_name(&fixed);
3255135446Strhodes			for (element = cfg_list_first(obj);
3256135446Strhodes			     element != NULL;
3257135446Strhodes			     element = cfg_list_next(element)) {
3258135446Strhodes				exclude = cfg_listelt_value(element);
3259135446Strhodes				str = cfg_obj_asstring(exclude);
3260254402Serwin				isc_buffer_constinit(&b, str, strlen(str));
3261135446Strhodes				isc_buffer_add(&b, strlen(str));
3262135446Strhodes				CHECK(dns_name_fromtext(name, &b, dns_rootname,
3263224092Sdougb							0, NULL));
3264135446Strhodes				CHECK(dns_view_excludedelegationonly(view,
3265135446Strhodes								     name));
3266135446Strhodes			}
3267135446Strhodes		}
3268135446Strhodes	} else
3269135446Strhodes		dns_view_setrootdelonly(view, ISC_FALSE);
3270135446Strhodes
3271170222Sdougb	/*
3272170222Sdougb	 * Setup automatic empty zones.  If recursion is off then
3273170222Sdougb	 * they are disabled by default.
3274170222Sdougb	 */
3275170222Sdougb	obj = NULL;
3276170222Sdougb	(void)ns_config_get(maps, "empty-zones-enable", &obj);
3277170222Sdougb	(void)ns_config_get(maps, "disable-empty-zone", &disablelist);
3278170222Sdougb	if (obj == NULL && disablelist == NULL &&
3279170222Sdougb	    view->rdclass == dns_rdataclass_in) {
3280170222Sdougb		empty_zones_enable = view->recursion;
3281170222Sdougb	} else if (view->rdclass == dns_rdataclass_in) {
3282170222Sdougb		if (obj != NULL)
3283170222Sdougb			empty_zones_enable = cfg_obj_asboolean(obj);
3284170222Sdougb		else
3285170222Sdougb			empty_zones_enable = view->recursion;
3286170222Sdougb	} else {
3287170222Sdougb		empty_zones_enable = ISC_FALSE;
3288170222Sdougb	}
3289234010Sdougb	if (empty_zones_enable && !lwresd_g_useresolvconf) {
3290170222Sdougb		const char *empty;
3291170222Sdougb		int empty_zone = 0;
3292170222Sdougb		dns_fixedname_t fixed;
3293170222Sdougb		dns_name_t *name;
3294170222Sdougb		isc_buffer_t buffer;
3295170222Sdougb		const char *str;
3296170222Sdougb		char server[DNS_NAME_FORMATSIZE + 1];
3297170222Sdougb		char contact[DNS_NAME_FORMATSIZE + 1];
3298170222Sdougb		const char *empty_dbtype[4] =
3299170222Sdougb				    { "_builtin", "empty", NULL, NULL };
3300170222Sdougb		int empty_dbtypec = 4;
3301254897Serwin		dns_zonestat_level_t statlevel;
3302170222Sdougb
3303170222Sdougb		dns_fixedname_init(&fixed);
3304170222Sdougb		name = dns_fixedname_name(&fixed);
3305170222Sdougb
3306170222Sdougb		obj = NULL;
3307170222Sdougb		result = ns_config_get(maps, "empty-server", &obj);
3308170222Sdougb		if (result == ISC_R_SUCCESS) {
3309170222Sdougb			str = cfg_obj_asstring(obj);
3310254402Serwin			isc_buffer_constinit(&buffer, str, strlen(str));
3311170222Sdougb			isc_buffer_add(&buffer, strlen(str));
3312224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3313224092Sdougb						NULL));
3314170222Sdougb			isc_buffer_init(&buffer, server, sizeof(server) - 1);
3315170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
3316170222Sdougb			server[isc_buffer_usedlength(&buffer)] = 0;
3317170222Sdougb			empty_dbtype[2] = server;
3318170222Sdougb		} else
3319170222Sdougb			empty_dbtype[2] = "@";
3320170222Sdougb
3321170222Sdougb		obj = NULL;
3322170222Sdougb		result = ns_config_get(maps, "empty-contact", &obj);
3323170222Sdougb		if (result == ISC_R_SUCCESS) {
3324170222Sdougb			str = cfg_obj_asstring(obj);
3325254402Serwin			isc_buffer_constinit(&buffer, str, strlen(str));
3326170222Sdougb			isc_buffer_add(&buffer, strlen(str));
3327224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3328224092Sdougb						NULL));
3329170222Sdougb			isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
3330170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
3331170222Sdougb			contact[isc_buffer_usedlength(&buffer)] = 0;
3332170222Sdougb			empty_dbtype[3] = contact;
3333170222Sdougb		} else
3334170222Sdougb			empty_dbtype[3] = ".";
3335170222Sdougb
3336193149Sdougb		obj = NULL;
3337193149Sdougb		result = ns_config_get(maps, "zone-statistics", &obj);
3338193149Sdougb		INSIST(result == ISC_R_SUCCESS);
3339254897Serwin		if (cfg_obj_isboolean(obj)) {
3340254897Serwin			if (cfg_obj_asboolean(obj))
3341254897Serwin				statlevel = dns_zonestat_full;
3342254897Serwin			else
3343254897Serwin				statlevel = dns_zonestat_terse; /* XXX */
3344254897Serwin		} else {
3345254897Serwin			const char *levelstr = cfg_obj_asstring(obj);
3346254897Serwin			if (strcasecmp(levelstr, "full") == 0)
3347254897Serwin				statlevel = dns_zonestat_full;
3348254897Serwin			else if (strcasecmp(levelstr, "terse") == 0)
3349254897Serwin				statlevel = dns_zonestat_terse;
3350254897Serwin			else if (strcasecmp(levelstr, "none") == 0)
3351254897Serwin				statlevel = dns_zonestat_none;
3352254897Serwin			else
3353254897Serwin				INSIST(0);
3354254897Serwin		}
3355193149Sdougb
3356254897Serwin		for (empty = empty_zones[empty_zone];
3357170222Sdougb		     empty != NULL;
3358254897Serwin		     empty = empty_zones[++empty_zone])
3359170222Sdougb		{
3360170222Sdougb			dns_forwarders_t *forwarders = NULL;
3361170222Sdougb			dns_view_t *pview = NULL;
3362170222Sdougb
3363254402Serwin			isc_buffer_constinit(&buffer, empty, strlen(empty));
3364170222Sdougb			isc_buffer_add(&buffer, strlen(empty));
3365170222Sdougb			/*
3366170222Sdougb			 * Look for zone on drop list.
3367170222Sdougb			 */
3368224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3369224092Sdougb						NULL));
3370170222Sdougb			if (disablelist != NULL &&
3371170222Sdougb			    on_disable_list(disablelist, name))
3372170222Sdougb				continue;
3373170222Sdougb
3374170222Sdougb			/*
3375170222Sdougb			 * This zone already exists.
3376170222Sdougb			 */
3377170222Sdougb			(void)dns_view_findzone(view, name, &zone);
3378170222Sdougb			if (zone != NULL) {
3379170222Sdougb				dns_zone_detach(&zone);
3380170222Sdougb				continue;
3381170222Sdougb			}
3382170222Sdougb
3383170222Sdougb			/*
3384170222Sdougb			 * If we would forward this name don't add a
3385170222Sdougb			 * empty zone for it.
3386170222Sdougb			 */
3387170222Sdougb			result = dns_fwdtable_find(view->fwdtable, name,
3388170222Sdougb						   &forwarders);
3389170222Sdougb			if (result == ISC_R_SUCCESS &&
3390170222Sdougb			    forwarders->fwdpolicy == dns_fwdpolicy_only)
3391170222Sdougb				continue;
3392186462Sdougb
3393170222Sdougb			/*
3394170222Sdougb			 * See if we can re-use a existing zone.
3395170222Sdougb			 */
3396170222Sdougb			result = dns_viewlist_find(&ns_g_server->viewlist,
3397170222Sdougb						   view->name, view->rdclass,
3398170222Sdougb						   &pview);
3399170222Sdougb			if (result != ISC_R_NOTFOUND &&
3400170222Sdougb			    result != ISC_R_SUCCESS)
3401170222Sdougb				goto cleanup;
3402170222Sdougb
3403170222Sdougb			if (pview != NULL) {
3404170222Sdougb				(void)dns_view_findzone(pview, name, &zone);
3405170222Sdougb				dns_view_detach(&pview);
3406170222Sdougb			}
3407170222Sdougb
3408262706Serwin			CHECK(create_empty_zone(zone, name, view, zonelist,
3409262706Serwin						empty_dbtype, empty_dbtypec,
3410262706Serwin						statlevel));
3411262706Serwin			if (zone != NULL)
3412262706Serwin				dns_zone_detach(&zone);
3413170222Sdougb		}
3414170222Sdougb	}
3415186462Sdougb
3416262706Serwin#ifdef USE_RRL
3417262706Serwin	obj = NULL;
3418262706Serwin	result = ns_config_get(maps, "rate-limit", &obj);
3419262706Serwin	if (result == ISC_R_SUCCESS) {
3420262706Serwin		result = configure_rrl(view, config, obj);
3421262706Serwin		if (result != ISC_R_SUCCESS)
3422262706Serwin			goto cleanup;
3423262706Serwin	}
3424262706Serwin#endif /* USE_RRL */
3425262706Serwin
3426135446Strhodes	result = ISC_R_SUCCESS;
3427135446Strhodes
3428135446Strhodes cleanup:
3429224092Sdougb	if (clients != NULL)
3430224092Sdougb		dns_acl_detach(&clients);
3431224092Sdougb	if (mapped != NULL)
3432224092Sdougb		dns_acl_detach(&mapped);
3433224092Sdougb	if (excluded != NULL)
3434224092Sdougb		dns_acl_detach(&excluded);
3435224092Sdougb	if (ring != NULL)
3436224092Sdougb		dns_tsigkeyring_detach(&ring);
3437170222Sdougb	if (zone != NULL)
3438170222Sdougb		dns_zone_detach(&zone);
3439135446Strhodes	if (dispatch4 != NULL)
3440135446Strhodes		dns_dispatch_detach(&dispatch4);
3441135446Strhodes	if (dispatch6 != NULL)
3442135446Strhodes		dns_dispatch_detach(&dispatch6);
3443193149Sdougb	if (resstats != NULL)
3444193149Sdougb		isc_stats_detach(&resstats);
3445193149Sdougb	if (resquerystats != NULL)
3446193149Sdougb		dns_stats_detach(&resquerystats);
3447135446Strhodes	if (order != NULL)
3448135446Strhodes		dns_order_detach(&order);
3449135446Strhodes	if (cmctx != NULL)
3450135446Strhodes		isc_mem_detach(&cmctx);
3451225361Sdougb	if (hmctx != NULL)
3452225361Sdougb		isc_mem_detach(&hmctx);
3453135446Strhodes
3454135446Strhodes	if (cache != NULL)
3455135446Strhodes		dns_cache_detach(&cache);
3456135446Strhodes
3457135446Strhodes	return (result);
3458135446Strhodes}
3459135446Strhodes
3460135446Strhodesstatic isc_result_t
3461135446Strhodesconfigure_hints(dns_view_t *view, const char *filename) {
3462135446Strhodes	isc_result_t result;
3463135446Strhodes	dns_db_t *db;
3464135446Strhodes
3465135446Strhodes	db = NULL;
3466135446Strhodes	result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
3467135446Strhodes	if (result == ISC_R_SUCCESS) {
3468135446Strhodes		dns_view_sethints(view, db);
3469135446Strhodes		dns_db_detach(&db);
3470135446Strhodes	}
3471135446Strhodes
3472135446Strhodes	return (result);
3473135446Strhodes}
3474135446Strhodes
3475135446Strhodesstatic isc_result_t
3476165071Sdougbconfigure_alternates(const cfg_obj_t *config, dns_view_t *view,
3477165071Sdougb		     const cfg_obj_t *alternates)
3478135446Strhodes{
3479165071Sdougb	const cfg_obj_t *portobj;
3480165071Sdougb	const cfg_obj_t *addresses;
3481165071Sdougb	const cfg_listelt_t *element;
3482135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
3483135446Strhodes	in_port_t port;
3484135446Strhodes
3485135446Strhodes	/*
3486135446Strhodes	 * Determine which port to send requests to.
3487135446Strhodes	 */
3488135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
3489135446Strhodes		port = ns_g_port;
3490135446Strhodes	else
3491135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
3492135446Strhodes
3493135446Strhodes	if (alternates != NULL) {
3494135446Strhodes		portobj = cfg_tuple_get(alternates, "port");
3495135446Strhodes		if (cfg_obj_isuint32(portobj)) {
3496135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
3497135446Strhodes			if (val > ISC_UINT16_MAX) {
3498135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
3499135446Strhodes					    "port '%u' out of range", val);
3500135446Strhodes				return (ISC_R_RANGE);
3501135446Strhodes			}
3502135446Strhodes			port = (in_port_t) val;
3503135446Strhodes		}
3504135446Strhodes	}
3505135446Strhodes
3506135446Strhodes	addresses = NULL;
3507135446Strhodes	if (alternates != NULL)
3508135446Strhodes		addresses = cfg_tuple_get(alternates, "addresses");
3509135446Strhodes
3510135446Strhodes	for (element = cfg_list_first(addresses);
3511135446Strhodes	     element != NULL;
3512135446Strhodes	     element = cfg_list_next(element))
3513135446Strhodes	{
3514165071Sdougb		const cfg_obj_t *alternate = cfg_listelt_value(element);
3515135446Strhodes		isc_sockaddr_t sa;
3516135446Strhodes
3517135446Strhodes		if (!cfg_obj_issockaddr(alternate)) {
3518135446Strhodes			dns_fixedname_t fixed;
3519135446Strhodes			dns_name_t *name;
3520165071Sdougb			const char *str = cfg_obj_asstring(cfg_tuple_get(
3521165071Sdougb							   alternate, "name"));
3522135446Strhodes			isc_buffer_t buffer;
3523135446Strhodes			in_port_t myport = port;
3524135446Strhodes
3525254402Serwin			isc_buffer_constinit(&buffer, str, strlen(str));
3526135446Strhodes			isc_buffer_add(&buffer, strlen(str));
3527135446Strhodes			dns_fixedname_init(&fixed);
3528135446Strhodes			name = dns_fixedname_name(&fixed);
3529224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3530224092Sdougb						NULL));
3531135446Strhodes
3532135446Strhodes			portobj = cfg_tuple_get(alternate, "port");
3533135446Strhodes			if (cfg_obj_isuint32(portobj)) {
3534135446Strhodes				isc_uint32_t val = cfg_obj_asuint32(portobj);
3535135446Strhodes				if (val > ISC_UINT16_MAX) {
3536135446Strhodes					cfg_obj_log(portobj, ns_g_lctx,
3537135446Strhodes						    ISC_LOG_ERROR,
3538135446Strhodes						    "port '%u' out of range",
3539135446Strhodes						     val);
3540135446Strhodes					return (ISC_R_RANGE);
3541135446Strhodes				}
3542135446Strhodes				myport = (in_port_t) val;
3543135446Strhodes			}
3544135446Strhodes			CHECK(dns_resolver_addalternate(view->resolver, NULL,
3545135446Strhodes							name, myport));
3546135446Strhodes			continue;
3547135446Strhodes		}
3548135446Strhodes
3549135446Strhodes		sa = *cfg_obj_assockaddr(alternate);
3550135446Strhodes		if (isc_sockaddr_getport(&sa) == 0)
3551135446Strhodes			isc_sockaddr_setport(&sa, port);
3552135446Strhodes		CHECK(dns_resolver_addalternate(view->resolver, &sa,
3553135446Strhodes						NULL, 0));
3554135446Strhodes	}
3555135446Strhodes
3556135446Strhodes cleanup:
3557135446Strhodes	return (result);
3558135446Strhodes}
3559135446Strhodes
3560135446Strhodesstatic isc_result_t
3561165071Sdougbconfigure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
3562165071Sdougb		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype)
3563135446Strhodes{
3564165071Sdougb	const cfg_obj_t *portobj;
3565165071Sdougb	const cfg_obj_t *faddresses;
3566165071Sdougb	const cfg_listelt_t *element;
3567135446Strhodes	dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
3568135446Strhodes	isc_sockaddrlist_t addresses;
3569135446Strhodes	isc_sockaddr_t *sa;
3570135446Strhodes	isc_result_t result;
3571135446Strhodes	in_port_t port;
3572135446Strhodes
3573193149Sdougb	ISC_LIST_INIT(addresses);
3574193149Sdougb
3575135446Strhodes	/*
3576135446Strhodes	 * Determine which port to send forwarded requests to.
3577135446Strhodes	 */
3578135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
3579135446Strhodes		port = ns_g_port;
3580135446Strhodes	else
3581135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
3582135446Strhodes
3583135446Strhodes	if (forwarders != NULL) {
3584135446Strhodes		portobj = cfg_tuple_get(forwarders, "port");
3585135446Strhodes		if (cfg_obj_isuint32(portobj)) {
3586135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
3587135446Strhodes			if (val > ISC_UINT16_MAX) {
3588135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
3589135446Strhodes					    "port '%u' out of range", val);
3590135446Strhodes				return (ISC_R_RANGE);
3591135446Strhodes			}
3592135446Strhodes			port = (in_port_t) val;
3593135446Strhodes		}
3594135446Strhodes	}
3595135446Strhodes
3596135446Strhodes	faddresses = NULL;
3597135446Strhodes	if (forwarders != NULL)
3598135446Strhodes		faddresses = cfg_tuple_get(forwarders, "addresses");
3599135446Strhodes
3600135446Strhodes	for (element = cfg_list_first(faddresses);
3601135446Strhodes	     element != NULL;
3602135446Strhodes	     element = cfg_list_next(element))
3603135446Strhodes	{
3604165071Sdougb		const cfg_obj_t *forwarder = cfg_listelt_value(element);
3605135446Strhodes		sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t));
3606135446Strhodes		if (sa == NULL) {
3607135446Strhodes			result = ISC_R_NOMEMORY;
3608135446Strhodes			goto cleanup;
3609135446Strhodes		}
3610135446Strhodes		*sa = *cfg_obj_assockaddr(forwarder);
3611135446Strhodes		if (isc_sockaddr_getport(sa) == 0)
3612135446Strhodes			isc_sockaddr_setport(sa, port);
3613135446Strhodes		ISC_LINK_INIT(sa, link);
3614135446Strhodes		ISC_LIST_APPEND(addresses, sa, link);
3615135446Strhodes	}
3616135446Strhodes
3617135446Strhodes	if (ISC_LIST_EMPTY(addresses)) {
3618135446Strhodes		if (forwardtype != NULL)
3619135446Strhodes			cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
3620135446Strhodes				    "no forwarders seen; disabling "
3621135446Strhodes				    "forwarding");
3622135446Strhodes		fwdpolicy = dns_fwdpolicy_none;
3623135446Strhodes	} else {
3624135446Strhodes		if (forwardtype == NULL)
3625135446Strhodes			fwdpolicy = dns_fwdpolicy_first;
3626135446Strhodes		else {
3627165071Sdougb			const char *forwardstr = cfg_obj_asstring(forwardtype);
3628135446Strhodes			if (strcasecmp(forwardstr, "first") == 0)
3629135446Strhodes				fwdpolicy = dns_fwdpolicy_first;
3630135446Strhodes			else if (strcasecmp(forwardstr, "only") == 0)
3631135446Strhodes				fwdpolicy = dns_fwdpolicy_only;
3632135446Strhodes			else
3633135446Strhodes				INSIST(0);
3634135446Strhodes		}
3635135446Strhodes	}
3636135446Strhodes
3637135446Strhodes	result = dns_fwdtable_add(view->fwdtable, origin, &addresses,
3638135446Strhodes				  fwdpolicy);
3639135446Strhodes	if (result != ISC_R_SUCCESS) {
3640135446Strhodes		char namebuf[DNS_NAME_FORMATSIZE];
3641135446Strhodes		dns_name_format(origin, namebuf, sizeof(namebuf));
3642135446Strhodes		cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
3643135446Strhodes			    "could not set up forwarding for domain '%s': %s",
3644135446Strhodes			    namebuf, isc_result_totext(result));
3645135446Strhodes		goto cleanup;
3646135446Strhodes	}
3647135446Strhodes
3648135446Strhodes	result = ISC_R_SUCCESS;
3649135446Strhodes
3650135446Strhodes cleanup:
3651135446Strhodes
3652135446Strhodes	while (!ISC_LIST_EMPTY(addresses)) {
3653135446Strhodes		sa = ISC_LIST_HEAD(addresses);
3654135446Strhodes		ISC_LIST_UNLINK(addresses, sa, link);
3655135446Strhodes		isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t));
3656135446Strhodes	}
3657135446Strhodes
3658135446Strhodes	return (result);
3659135446Strhodes}
3660135446Strhodes
3661135446Strhodesstatic isc_result_t
3662225361Sdougbget_viewinfo(const cfg_obj_t *vconfig, const char **namep,
3663225361Sdougb	     dns_rdataclass_t *classp)
3664165071Sdougb{
3665225361Sdougb	isc_result_t result = ISC_R_SUCCESS;
3666135446Strhodes	const char *viewname;
3667135446Strhodes	dns_rdataclass_t viewclass;
3668135446Strhodes
3669225361Sdougb	REQUIRE(namep != NULL && *namep == NULL);
3670225361Sdougb	REQUIRE(classp != NULL);
3671225361Sdougb
3672135446Strhodes	if (vconfig != NULL) {
3673165071Sdougb		const cfg_obj_t *classobj = NULL;
3674135446Strhodes
3675135446Strhodes		viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
3676135446Strhodes		classobj = cfg_tuple_get(vconfig, "class");
3677135446Strhodes		result = ns_config_getclass(classobj, dns_rdataclass_in,
3678135446Strhodes					    &viewclass);
3679135446Strhodes	} else {
3680135446Strhodes		viewname = "_default";
3681135446Strhodes		viewclass = dns_rdataclass_in;
3682135446Strhodes	}
3683225361Sdougb
3684225361Sdougb	*namep = viewname;
3685225361Sdougb	*classp = viewclass;
3686225361Sdougb
3687225361Sdougb	return (result);
3688225361Sdougb}
3689225361Sdougb
3690225361Sdougb/*
3691225361Sdougb * Find a view based on its configuration info and attach to it.
3692225361Sdougb *
3693225361Sdougb * If 'vconfig' is NULL, attach to the default view.
3694225361Sdougb */
3695225361Sdougbstatic isc_result_t
3696225361Sdougbfind_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
3697225361Sdougb	  dns_view_t **viewp)
3698225361Sdougb{
3699225361Sdougb	isc_result_t result;
3700225361Sdougb	const char *viewname = NULL;
3701225361Sdougb	dns_rdataclass_t viewclass;
3702225361Sdougb	dns_view_t *view = NULL;
3703225361Sdougb
3704225361Sdougb	result = get_viewinfo(vconfig, &viewname, &viewclass);
3705225361Sdougb	if (result != ISC_R_SUCCESS)
3706225361Sdougb		return (result);
3707225361Sdougb
3708135446Strhodes	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
3709225361Sdougb	if (result != ISC_R_SUCCESS)
3710225361Sdougb		return (result);
3711225361Sdougb
3712225361Sdougb	*viewp = view;
3713225361Sdougb	return (ISC_R_SUCCESS);
3714225361Sdougb}
3715225361Sdougb
3716225361Sdougb/*
3717225361Sdougb * Create a new view and add it to the list.
3718225361Sdougb *
3719225361Sdougb * If 'vconfig' is NULL, create the default view.
3720225361Sdougb *
3721225361Sdougb * The view created is attached to '*viewp'.
3722225361Sdougb */
3723225361Sdougbstatic isc_result_t
3724225361Sdougbcreate_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
3725225361Sdougb	    dns_view_t **viewp)
3726225361Sdougb{
3727225361Sdougb	isc_result_t result;
3728225361Sdougb	const char *viewname = NULL;
3729225361Sdougb	dns_rdataclass_t viewclass;
3730225361Sdougb	dns_view_t *view = NULL;
3731225361Sdougb
3732225361Sdougb	result = get_viewinfo(vconfig, &viewname, &viewclass);
3733225361Sdougb	if (result != ISC_R_SUCCESS)
3734225361Sdougb		return (result);
3735225361Sdougb
3736225361Sdougb	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
3737135446Strhodes	if (result == ISC_R_SUCCESS)
3738135446Strhodes		return (ISC_R_EXISTS);
3739135446Strhodes	if (result != ISC_R_NOTFOUND)
3740135446Strhodes		return (result);
3741135446Strhodes	INSIST(view == NULL);
3742135446Strhodes
3743135446Strhodes	result = dns_view_create(ns_g_mctx, viewclass, viewname, &view);
3744135446Strhodes	if (result != ISC_R_SUCCESS)
3745135446Strhodes		return (result);
3746135446Strhodes
3747135446Strhodes	ISC_LIST_APPEND(*viewlist, view, link);
3748135446Strhodes	dns_view_attach(view, viewp);
3749135446Strhodes	return (ISC_R_SUCCESS);
3750135446Strhodes}
3751135446Strhodes
3752135446Strhodes/*
3753135446Strhodes * Configure or reconfigure a zone.
3754135446Strhodes */
3755135446Strhodesstatic isc_result_t
3756165071Sdougbconfigure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
3757165071Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
3758224092Sdougb	       cfg_aclconfctx_t *aclconf, isc_boolean_t added)
3759135446Strhodes{
3760135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
3761135446Strhodes	dns_zone_t *zone = NULL;	/* New or reused zone */
3762254897Serwin	dns_zone_t *raw = NULL;		/* New or reused raw zone */
3763135446Strhodes	dns_zone_t *dupzone = NULL;
3764165071Sdougb	const cfg_obj_t *options = NULL;
3765165071Sdougb	const cfg_obj_t *zoptions = NULL;
3766165071Sdougb	const cfg_obj_t *typeobj = NULL;
3767165071Sdougb	const cfg_obj_t *forwarders = NULL;
3768165071Sdougb	const cfg_obj_t *forwardtype = NULL;
3769165071Sdougb	const cfg_obj_t *only = NULL;
3770254897Serwin	const cfg_obj_t *signing = NULL;
3771135446Strhodes	isc_result_t result;
3772135446Strhodes	isc_result_t tresult;
3773135446Strhodes	isc_buffer_t buffer;
3774135446Strhodes	dns_fixedname_t fixorigin;
3775135446Strhodes	dns_name_t *origin;
3776135446Strhodes	const char *zname;
3777135446Strhodes	dns_rdataclass_t zclass;
3778135446Strhodes	const char *ztypestr;
3779254402Serwin	isc_boolean_t is_rpz;
3780254402Serwin	dns_rpz_zone_t *rpz;
3781135446Strhodes
3782135446Strhodes	options = NULL;
3783135446Strhodes	(void)cfg_map_get(config, "options", &options);
3784135446Strhodes
3785135446Strhodes	zoptions = cfg_tuple_get(zconfig, "options");
3786135446Strhodes
3787135446Strhodes	/*
3788135446Strhodes	 * Get the zone origin as a dns_name_t.
3789135446Strhodes	 */
3790135446Strhodes	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
3791254402Serwin	isc_buffer_constinit(&buffer, zname, strlen(zname));
3792135446Strhodes	isc_buffer_add(&buffer, strlen(zname));
3793135446Strhodes	dns_fixedname_init(&fixorigin);
3794135446Strhodes	CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin),
3795224092Sdougb				&buffer, dns_rootname, 0, NULL));
3796135446Strhodes	origin = dns_fixedname_name(&fixorigin);
3797135446Strhodes
3798135446Strhodes	CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
3799135446Strhodes				 view->rdclass, &zclass));
3800135446Strhodes	if (zclass != view->rdclass) {
3801135446Strhodes		const char *vname = NULL;
3802135446Strhodes		if (vconfig != NULL)
3803135446Strhodes			vname = cfg_obj_asstring(cfg_tuple_get(vconfig,
3804135446Strhodes							       "name"));
3805135446Strhodes		else
3806135446Strhodes			vname = "<default view>";
3807186462Sdougb
3808135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3809135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3810135446Strhodes			      "zone '%s': wrong class for view '%s'",
3811135446Strhodes			      zname, vname);
3812135446Strhodes		result = ISC_R_FAILURE;
3813135446Strhodes		goto cleanup;
3814135446Strhodes	}
3815135446Strhodes
3816135446Strhodes	(void)cfg_map_get(zoptions, "type", &typeobj);
3817135446Strhodes	if (typeobj == NULL) {
3818135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3819135446Strhodes			    "zone '%s' 'type' not specified", zname);
3820135446Strhodes		return (ISC_R_FAILURE);
3821135446Strhodes	}
3822135446Strhodes	ztypestr = cfg_obj_asstring(typeobj);
3823135446Strhodes
3824135446Strhodes	/*
3825224092Sdougb	 * "hints zones" aren't zones.	If we've got one,
3826135446Strhodes	 * configure it and return.
3827135446Strhodes	 */
3828135446Strhodes	if (strcasecmp(ztypestr, "hint") == 0) {
3829165071Sdougb		const cfg_obj_t *fileobj = NULL;
3830135446Strhodes		if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) {
3831135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3832135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3833135446Strhodes				      "zone '%s': 'file' not specified",
3834135446Strhodes				      zname);
3835135446Strhodes			result = ISC_R_FAILURE;
3836135446Strhodes			goto cleanup;
3837135446Strhodes		}
3838135446Strhodes		if (dns_name_equal(origin, dns_rootname)) {
3839165071Sdougb			const char *hintsfile = cfg_obj_asstring(fileobj);
3840135446Strhodes
3841135446Strhodes			result = configure_hints(view, hintsfile);
3842135446Strhodes			if (result != ISC_R_SUCCESS) {
3843135446Strhodes				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3844135446Strhodes					      NS_LOGMODULE_SERVER,
3845135446Strhodes					      ISC_LOG_ERROR,
3846135446Strhodes					      "could not configure root hints "
3847135446Strhodes					      "from '%s': %s", hintsfile,
3848135446Strhodes					      isc_result_totext(result));
3849135446Strhodes				goto cleanup;
3850135446Strhodes			}
3851135446Strhodes			/*
3852135446Strhodes			 * Hint zones may also refer to delegation only points.
3853135446Strhodes			 */
3854135446Strhodes			only = NULL;
3855135446Strhodes			tresult = cfg_map_get(zoptions, "delegation-only",
3856135446Strhodes					      &only);
3857135446Strhodes			if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only))
3858135446Strhodes				CHECK(dns_view_adddelegationonly(view, origin));
3859135446Strhodes		} else {
3860135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3861135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
3862135446Strhodes				      "ignoring non-root hint zone '%s'",
3863135446Strhodes				      zname);
3864135446Strhodes			result = ISC_R_SUCCESS;
3865135446Strhodes		}
3866135446Strhodes		/* Skip ordinary zone processing. */
3867135446Strhodes		goto cleanup;
3868135446Strhodes	}
3869135446Strhodes
3870135446Strhodes	/*
3871135446Strhodes	 * "forward zones" aren't zones either.  Translate this syntax into
3872135446Strhodes	 * the appropriate selective forwarding configuration and return.
3873135446Strhodes	 */
3874135446Strhodes	if (strcasecmp(ztypestr, "forward") == 0) {
3875135446Strhodes		forwardtype = NULL;
3876135446Strhodes		forwarders = NULL;
3877135446Strhodes
3878135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
3879135446Strhodes		(void)cfg_map_get(zoptions, "forwarders", &forwarders);
3880135446Strhodes		result = configure_forward(config, view, origin, forwarders,
3881135446Strhodes					   forwardtype);
3882135446Strhodes		goto cleanup;
3883135446Strhodes	}
3884135446Strhodes
3885135446Strhodes	/*
3886135446Strhodes	 * "delegation-only zones" aren't zones either.
3887135446Strhodes	 */
3888135446Strhodes	if (strcasecmp(ztypestr, "delegation-only") == 0) {
3889135446Strhodes		result = dns_view_adddelegationonly(view, origin);
3890135446Strhodes		goto cleanup;
3891135446Strhodes	}
3892135446Strhodes
3893135446Strhodes	/*
3894254897Serwin	 * Redirect zones only require minimal configuration.
3895254897Serwin	 */
3896254897Serwin	if (strcasecmp(ztypestr, "redirect") == 0) {
3897254897Serwin		if (view->redirect != NULL) {
3898254897Serwin			cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3899254897Serwin				    "redirect zone already exists");
3900254897Serwin			result = ISC_R_EXISTS;
3901254897Serwin			goto cleanup;
3902254897Serwin		}
3903254897Serwin		result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
3904254897Serwin					   view->rdclass, &pview);
3905254897Serwin		if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3906254897Serwin			goto cleanup;
3907254897Serwin		if (pview != NULL && pview->redirect != NULL) {
3908254897Serwin			dns_zone_attach(pview->redirect, &zone);
3909254897Serwin			dns_zone_setview(zone, view);
3910254897Serwin		} else {
3911254897Serwin			CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr,
3912254897Serwin						     &zone));
3913254897Serwin			CHECK(dns_zone_setorigin(zone, origin));
3914254897Serwin			dns_zone_setview(zone, view);
3915254897Serwin			CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr,
3916254897Serwin						     zone));
3917254897Serwin			dns_zone_setstats(zone, ns_g_server->zonestats);
3918254897Serwin		}
3919254897Serwin		CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf,
3920254897Serwin					zone, NULL));
3921254897Serwin		dns_zone_attach(zone, &view->redirect);
3922254897Serwin		goto cleanup;
3923254897Serwin	}
3924254897Serwin
3925254897Serwin	/*
3926135446Strhodes	 * Check for duplicates in the new zone table.
3927135446Strhodes	 */
3928135446Strhodes	result = dns_view_findzone(view, origin, &dupzone);
3929135446Strhodes	if (result == ISC_R_SUCCESS) {
3930135446Strhodes		/*
3931135446Strhodes		 * We already have this zone!
3932135446Strhodes		 */
3933135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3934135446Strhodes			    "zone '%s' already exists", zname);
3935135446Strhodes		dns_zone_detach(&dupzone);
3936135446Strhodes		result = ISC_R_EXISTS;
3937135446Strhodes		goto cleanup;
3938135446Strhodes	}
3939135446Strhodes	INSIST(dupzone == NULL);
3940135446Strhodes
3941135446Strhodes	/*
3942254402Serwin	 * Note whether this is a response policy zone.
3943254402Serwin	 */
3944254402Serwin	is_rpz = ISC_FALSE;
3945254402Serwin	for (rpz = ISC_LIST_HEAD(view->rpz_zones);
3946254402Serwin	     rpz != NULL;
3947254402Serwin	     rpz = ISC_LIST_NEXT(rpz, link))
3948254402Serwin	{
3949254402Serwin		if (dns_name_equal(&rpz->origin, origin)) {
3950254402Serwin			is_rpz = ISC_TRUE;
3951254402Serwin			rpz->defined = ISC_TRUE;
3952254402Serwin			break;
3953254402Serwin		}
3954254402Serwin	}
3955254402Serwin
3956254402Serwin	/*
3957135446Strhodes	 * See if we can reuse an existing zone.  This is
3958135446Strhodes	 * only possible if all of these are true:
3959135446Strhodes	 *   - The zone's view exists
3960135446Strhodes	 *   - A zone with the right name exists in the view
3961135446Strhodes	 *   - The zone is compatible with the config
3962135446Strhodes	 *     options (e.g., an existing master zone cannot
3963135446Strhodes	 *     be reused if the options specify a slave zone)
3964254402Serwin	 *   - The zone was and is or was not and is not a policy zone
3965135446Strhodes	 */
3966254897Serwin	result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
3967254897Serwin				   view->rdclass, &pview);
3968135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3969135446Strhodes		goto cleanup;
3970135446Strhodes	if (pview != NULL)
3971135446Strhodes		result = dns_view_findzone(pview, origin, &zone);
3972135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3973135446Strhodes		goto cleanup;
3974254897Serwin
3975170222Sdougb	if (zone != NULL && !ns_zone_reusable(zone, zconfig))
3976170222Sdougb		dns_zone_detach(&zone);
3977135446Strhodes
3978254402Serwin	if (zone != NULL && is_rpz != dns_zone_get_rpz(zone))
3979254402Serwin		dns_zone_detach(&zone);
3980254402Serwin
3981135446Strhodes	if (zone != NULL) {
3982135446Strhodes		/*
3983135446Strhodes		 * We found a reusable zone.  Make it use the
3984135446Strhodes		 * new view.
3985135446Strhodes		 */
3986135446Strhodes		dns_zone_setview(zone, view);
3987170222Sdougb		if (view->acache != NULL)
3988170222Sdougb			dns_zone_setacache(zone, view->acache);
3989135446Strhodes	} else {
3990135446Strhodes		/*
3991135446Strhodes		 * We cannot reuse an existing zone, we have
3992135446Strhodes		 * to create a new one.
3993135446Strhodes		 */
3994254897Serwin		CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone));
3995135446Strhodes		CHECK(dns_zone_setorigin(zone, origin));
3996135446Strhodes		dns_zone_setview(zone, view);
3997170222Sdougb		if (view->acache != NULL)
3998170222Sdougb			dns_zone_setacache(zone, view->acache);
3999135446Strhodes		CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
4000193149Sdougb		dns_zone_setstats(zone, ns_g_server->zonestats);
4001135446Strhodes	}
4002135446Strhodes
4003254402Serwin	if (is_rpz) {
4004254402Serwin		result = dns_zone_rpz_enable(zone);
4005254402Serwin		if (result != ISC_R_SUCCESS) {
4006254402Serwin			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4007254402Serwin				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4008254402Serwin				      "zone '%s': incompatible"
4009254402Serwin				      " masterfile-format or database"
4010254402Serwin				      " for a response policy zone",
4011254402Serwin				      zname);
4012254402Serwin			goto cleanup;
4013254402Serwin		}
4014254402Serwin	}
4015254402Serwin
4016135446Strhodes	/*
4017135446Strhodes	 * If the zone contains a 'forwarders' statement, configure
4018135446Strhodes	 * selective forwarding.
4019135446Strhodes	 */
4020135446Strhodes	forwarders = NULL;
4021135446Strhodes	if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS)
4022135446Strhodes	{
4023135446Strhodes		forwardtype = NULL;
4024135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
4025135446Strhodes		CHECK(configure_forward(config, view, origin, forwarders,
4026135446Strhodes					forwardtype));
4027135446Strhodes	}
4028135446Strhodes
4029135446Strhodes	/*
4030135446Strhodes	 * Stub and forward zones may also refer to delegation only points.
4031135446Strhodes	 */
4032135446Strhodes	only = NULL;
4033135446Strhodes	if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS)
4034135446Strhodes	{
4035135446Strhodes		if (cfg_obj_asboolean(only))
4036135446Strhodes			CHECK(dns_view_adddelegationonly(view, origin));
4037135446Strhodes	}
4038135446Strhodes
4039135446Strhodes	/*
4040224092Sdougb	 * Mark whether the zone was originally added at runtime or not
4041224092Sdougb	 */
4042224092Sdougb	dns_zone_setadded(zone, added);
4043224092Sdougb
4044254897Serwin	signing = NULL;
4045254897Serwin	if ((strcasecmp(ztypestr, "master") == 0 ||
4046254897Serwin	     strcasecmp(ztypestr, "slave") == 0) &&
4047254897Serwin	    cfg_map_get(zoptions, "inline-signing", &signing) == ISC_R_SUCCESS &&
4048254897Serwin	    cfg_obj_asboolean(signing))
4049254897Serwin	{
4050254897Serwin		dns_zone_getraw(zone, &raw);
4051254897Serwin		if (raw == NULL) {
4052254897Serwin			CHECK(dns_zone_create(&raw, mctx));
4053254897Serwin			CHECK(dns_zone_setorigin(raw, origin));
4054254897Serwin			dns_zone_setview(raw, view);
4055254897Serwin			if (view->acache != NULL)
4056254897Serwin				dns_zone_setacache(raw, view->acache);
4057254897Serwin			dns_zone_setstats(raw, ns_g_server->zonestats);
4058254897Serwin			CHECK(dns_zone_link(zone, raw));
4059254897Serwin		}
4060254897Serwin	}
4061254897Serwin
4062224092Sdougb	/*
4063135446Strhodes	 * Configure the zone.
4064135446Strhodes	 */
4065254897Serwin	CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone, raw));
4066135446Strhodes
4067135446Strhodes	/*
4068135446Strhodes	 * Add the zone to its view in the new view list.
4069135446Strhodes	 */
4070135446Strhodes	CHECK(dns_view_addzone(view, zone));
4071135446Strhodes
4072234010Sdougb	/*
4073234010Sdougb	 * Ensure that zone keys are reloaded on reconfig
4074234010Sdougb	 */
4075234010Sdougb	if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0)
4076234010Sdougb		dns_zone_rekey(zone, ISC_FALSE);
4077234010Sdougb
4078135446Strhodes cleanup:
4079135446Strhodes	if (zone != NULL)
4080135446Strhodes		dns_zone_detach(&zone);
4081254897Serwin	if (raw != NULL)
4082254897Serwin		dns_zone_detach(&raw);
4083135446Strhodes	if (pview != NULL)
4084135446Strhodes		dns_view_detach(&pview);
4085135446Strhodes
4086135446Strhodes	return (result);
4087135446Strhodes}
4088135446Strhodes
4089135446Strhodes/*
4090224092Sdougb * Configure built-in zone for storing managed-key data.
4091224092Sdougb */
4092224092Sdougb
4093224092Sdougb#define KEYZONE "managed-keys.bind"
4094224092Sdougb#define MKEYS ".mkeys"
4095224092Sdougb
4096224092Sdougbstatic isc_result_t
4097224092Sdougbadd_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
4098224092Sdougb	isc_result_t result;
4099224092Sdougb	dns_view_t *pview = NULL;
4100224092Sdougb	dns_zone_t *zone = NULL;
4101224092Sdougb	dns_acl_t *none = NULL;
4102224092Sdougb	char filename[PATH_MAX];
4103224092Sdougb	char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(MKEYS)];
4104224092Sdougb	int n;
4105224092Sdougb
4106224092Sdougb	REQUIRE(view != NULL);
4107224092Sdougb
4108224092Sdougb	/* See if we can re-use an existing keydata zone. */
4109224092Sdougb	result = dns_viewlist_find(&ns_g_server->viewlist,
4110224092Sdougb				   view->name, view->rdclass,
4111224092Sdougb				   &pview);
4112224092Sdougb	if (result != ISC_R_NOTFOUND &&
4113224092Sdougb	    result != ISC_R_SUCCESS)
4114224092Sdougb		return (result);
4115224092Sdougb
4116224092Sdougb	if (pview != NULL && pview->managed_keys != NULL) {
4117224092Sdougb		dns_zone_attach(pview->managed_keys, &view->managed_keys);
4118224092Sdougb		dns_zone_setview(pview->managed_keys, view);
4119224092Sdougb		dns_view_detach(&pview);
4120234010Sdougb		dns_zone_synckeyzone(view->managed_keys);
4121224092Sdougb		return (ISC_R_SUCCESS);
4122224092Sdougb	}
4123224092Sdougb
4124224092Sdougb	/* No existing keydata zone was found; create one */
4125254897Serwin	CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone));
4126224092Sdougb	CHECK(dns_zone_setorigin(zone, dns_rootname));
4127224092Sdougb
4128224092Sdougb	isc_sha256_data((void *)view->name, strlen(view->name), buffer);
4129224092Sdougb	strcat(buffer, MKEYS);
4130224092Sdougb	n = snprintf(filename, sizeof(filename), "%s%s%s",
4131224092Sdougb		     directory ? directory : "", directory ? "/" : "",
4132224092Sdougb		     strcmp(view->name, "_default") == 0 ? KEYZONE : buffer);
4133224092Sdougb	if (n < 0 || (size_t)n >= sizeof(filename)) {
4134224092Sdougb		result = (n < 0) ? ISC_R_FAILURE : ISC_R_NOSPACE;
4135224092Sdougb		goto cleanup;
4136224092Sdougb	}
4137224092Sdougb	CHECK(dns_zone_setfile(zone, filename));
4138224092Sdougb
4139224092Sdougb	dns_zone_setview(zone, view);
4140224092Sdougb	dns_zone_settype(zone, dns_zone_key);
4141224092Sdougb	dns_zone_setclass(zone, view->rdclass);
4142224092Sdougb
4143224092Sdougb	CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
4144224092Sdougb
4145224092Sdougb	if (view->acache != NULL)
4146224092Sdougb		dns_zone_setacache(zone, view->acache);
4147224092Sdougb
4148224092Sdougb	CHECK(dns_acl_none(mctx, &none));
4149224092Sdougb	dns_zone_setqueryacl(zone, none);
4150224092Sdougb	dns_zone_setqueryonacl(zone, none);
4151224092Sdougb	dns_acl_detach(&none);
4152224092Sdougb
4153224092Sdougb	dns_zone_setdialup(zone, dns_dialuptype_no);
4154224092Sdougb	dns_zone_setnotifytype(zone, dns_notifytype_no);
4155224092Sdougb	dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE);
4156224092Sdougb	dns_zone_setjournalsize(zone, 0);
4157224092Sdougb
4158224092Sdougb	dns_zone_setstats(zone, ns_g_server->zonestats);
4159254897Serwin	CHECK(setquerystats(zone, mctx, dns_zonestat_none));
4160224092Sdougb
4161224092Sdougb	if (view->managed_keys != NULL)
4162224092Sdougb		dns_zone_detach(&view->managed_keys);
4163224092Sdougb	dns_zone_attach(zone, &view->managed_keys);
4164224092Sdougb
4165224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4166224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4167224092Sdougb		      "set up managed keys zone for view %s, file '%s'",
4168224092Sdougb		      view->name, filename);
4169224092Sdougb
4170224092Sdougbcleanup:
4171224092Sdougb	if (zone != NULL)
4172224092Sdougb		dns_zone_detach(&zone);
4173224092Sdougb	if (none != NULL)
4174224092Sdougb		dns_acl_detach(&none);
4175224092Sdougb
4176224092Sdougb	return (result);
4177224092Sdougb}
4178224092Sdougb
4179224092Sdougb/*
4180135446Strhodes * Configure a single server quota.
4181135446Strhodes */
4182135446Strhodesstatic void
4183165071Sdougbconfigure_server_quota(const cfg_obj_t **maps, const char *name,
4184165071Sdougb		       isc_quota_t *quota)
4185135446Strhodes{
4186165071Sdougb	const cfg_obj_t *obj = NULL;
4187135446Strhodes	isc_result_t result;
4188135446Strhodes
4189135446Strhodes	result = ns_config_get(maps, name, &obj);
4190135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4191153816Sdougb	isc_quota_max(quota, cfg_obj_asuint32(obj));
4192135446Strhodes}
4193135446Strhodes
4194135446Strhodes/*
4195135446Strhodes * This function is called as soon as the 'directory' statement has been
4196135446Strhodes * parsed.  This can be extended to support other options if necessary.
4197135446Strhodes */
4198135446Strhodesstatic isc_result_t
4199165071Sdougbdirectory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
4200135446Strhodes	isc_result_t result;
4201165071Sdougb	const char *directory;
4202135446Strhodes
4203135446Strhodes	REQUIRE(strcasecmp("directory", clausename) == 0);
4204135446Strhodes
4205135446Strhodes	UNUSED(arg);
4206135446Strhodes	UNUSED(clausename);
4207135446Strhodes
4208135446Strhodes	/*
4209135446Strhodes	 * Change directory.
4210135446Strhodes	 */
4211135446Strhodes	directory = cfg_obj_asstring(obj);
4212135446Strhodes
4213135446Strhodes	if (! isc_file_ischdiridempotent(directory))
4214135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
4215135446Strhodes			    "option 'directory' contains relative path '%s'",
4216135446Strhodes			    directory);
4217135446Strhodes
4218135446Strhodes	result = isc_dir_chdir(directory);
4219135446Strhodes	if (result != ISC_R_SUCCESS) {
4220135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
4221135446Strhodes			    "change directory to '%s' failed: %s",
4222135446Strhodes			    directory, isc_result_totext(result));
4223135446Strhodes		return (result);
4224135446Strhodes	}
4225135446Strhodes
4226135446Strhodes	return (ISC_R_SUCCESS);
4227135446Strhodes}
4228135446Strhodes
4229135446Strhodesstatic void
4230135446Strhodesscan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
4231135446Strhodes	isc_boolean_t match_mapped = server->aclenv.match_mapped;
4232135446Strhodes
4233135446Strhodes	ns_interfacemgr_scan(server->interfacemgr, verbose);
4234135446Strhodes	/*
4235135446Strhodes	 * Update the "localhost" and "localnets" ACLs to match the
4236135446Strhodes	 * current set of network interfaces.
4237135446Strhodes	 */
4238135446Strhodes	dns_aclenv_copy(&server->aclenv,
4239135446Strhodes			ns_interfacemgr_getaclenv(server->interfacemgr));
4240135446Strhodes
4241135446Strhodes	server->aclenv.match_mapped = match_mapped;
4242135446Strhodes}
4243135446Strhodes
4244135446Strhodesstatic isc_result_t
4245180477Sdougbadd_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
4246180477Sdougb	      isc_boolean_t wcardport_ok)
4247180477Sdougb{
4248135446Strhodes	ns_listenelt_t *lelt = NULL;
4249135446Strhodes	dns_acl_t *src_acl = NULL;
4250135446Strhodes	isc_result_t result;
4251135446Strhodes	isc_sockaddr_t any_sa6;
4252193149Sdougb	isc_netaddr_t netaddr;
4253135446Strhodes
4254135446Strhodes	REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
4255135446Strhodes
4256135446Strhodes	isc_sockaddr_any6(&any_sa6);
4257180477Sdougb	if (!isc_sockaddr_equal(&any_sa6, addr) &&
4258180477Sdougb	    (wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
4259193149Sdougb		isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr);
4260135446Strhodes
4261193149Sdougb		result = dns_acl_create(mctx, 0, &src_acl);
4262135446Strhodes		if (result != ISC_R_SUCCESS)
4263135446Strhodes			return (result);
4264193149Sdougb
4265193149Sdougb		result = dns_iptable_addprefix(src_acl->iptable,
4266193149Sdougb					       &netaddr, 128, ISC_TRUE);
4267135446Strhodes		if (result != ISC_R_SUCCESS)
4268135446Strhodes			goto clean;
4269135446Strhodes
4270135446Strhodes		result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr),
4271135446Strhodes					     src_acl, &lelt);
4272135446Strhodes		if (result != ISC_R_SUCCESS)
4273135446Strhodes			goto clean;
4274135446Strhodes		ISC_LIST_APPEND(list->elts, lelt, link);
4275135446Strhodes	}
4276135446Strhodes
4277135446Strhodes	return (ISC_R_SUCCESS);
4278135446Strhodes
4279135446Strhodes clean:
4280135446Strhodes	INSIST(lelt == NULL);
4281165071Sdougb	dns_acl_detach(&src_acl);
4282135446Strhodes
4283135446Strhodes	return (result);
4284135446Strhodes}
4285135446Strhodes
4286135446Strhodes/*
4287135446Strhodes * Make a list of xxx-source addresses and call ns_interfacemgr_adjust()
4288135446Strhodes * to update the listening interfaces accordingly.
4289135446Strhodes * We currently only consider IPv6, because this only affects IPv6 wildcard
4290135446Strhodes * sockets.
4291135446Strhodes */
4292135446Strhodesstatic void
4293135446Strhodesadjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
4294135446Strhodes	isc_result_t result;
4295135446Strhodes	ns_listenlist_t *list = NULL;
4296135446Strhodes	dns_view_t *view;
4297135446Strhodes	dns_zone_t *zone, *next;
4298135446Strhodes	isc_sockaddr_t addr, *addrp;
4299135446Strhodes
4300135446Strhodes	result = ns_listenlist_create(mctx, &list);
4301135446Strhodes	if (result != ISC_R_SUCCESS)
4302135446Strhodes		return;
4303135446Strhodes
4304135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
4305135446Strhodes	     view != NULL;
4306135446Strhodes	     view = ISC_LIST_NEXT(view, link)) {
4307135446Strhodes		dns_dispatch_t *dispatch6;
4308135446Strhodes
4309135446Strhodes		dispatch6 = dns_resolver_dispatchv6(view->resolver);
4310143731Sdougb		if (dispatch6 == NULL)
4311143731Sdougb			continue;
4312135446Strhodes		result = dns_dispatch_getlocaladdress(dispatch6, &addr);
4313135446Strhodes		if (result != ISC_R_SUCCESS)
4314135446Strhodes			goto fail;
4315180477Sdougb
4316180477Sdougb		/*
4317180477Sdougb		 * We always add non-wildcard address regardless of whether
4318180477Sdougb		 * the port is 'any' (the fourth arg is TRUE): if the port is
4319180477Sdougb		 * specific, we need to add it since it may conflict with a
4320180477Sdougb		 * listening interface; if it's zero, we'll dynamically open
4321180477Sdougb		 * query ports, and some of them may override an existing
4322180477Sdougb		 * wildcard IPv6 port.
4323180477Sdougb		 */
4324180477Sdougb		result = add_listenelt(mctx, list, &addr, ISC_TRUE);
4325135446Strhodes		if (result != ISC_R_SUCCESS)
4326135446Strhodes			goto fail;
4327135446Strhodes	}
4328135446Strhodes
4329135446Strhodes	zone = NULL;
4330135446Strhodes	for (result = dns_zone_first(server->zonemgr, &zone);
4331135446Strhodes	     result == ISC_R_SUCCESS;
4332135446Strhodes	     next = NULL, result = dns_zone_next(zone, &next), zone = next) {
4333135446Strhodes		dns_view_t *zoneview;
4334135446Strhodes
4335135446Strhodes		/*
4336135446Strhodes		 * At this point the zone list may contain a stale zone
4337135446Strhodes		 * just removed from the configuration.  To see the validity,
4338135446Strhodes		 * check if the corresponding view is in our current view list.
4339153816Sdougb		 * There may also be old zones that are still in the process
4340153816Sdougb		 * of shutting down and have detached from their old view
4341153816Sdougb		 * (zoneview == NULL).
4342135446Strhodes		 */
4343135446Strhodes		zoneview = dns_zone_getview(zone);
4344153816Sdougb		if (zoneview == NULL)
4345153816Sdougb			continue;
4346135446Strhodes		for (view = ISC_LIST_HEAD(server->viewlist);
4347135446Strhodes		     view != NULL && view != zoneview;
4348135446Strhodes		     view = ISC_LIST_NEXT(view, link))
4349135446Strhodes			;
4350135446Strhodes		if (view == NULL)
4351135446Strhodes			continue;
4352135446Strhodes
4353135446Strhodes		addrp = dns_zone_getnotifysrc6(zone);
4354180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
4355135446Strhodes		if (result != ISC_R_SUCCESS)
4356135446Strhodes			goto fail;
4357135446Strhodes
4358135446Strhodes		addrp = dns_zone_getxfrsource6(zone);
4359180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
4360135446Strhodes		if (result != ISC_R_SUCCESS)
4361135446Strhodes			goto fail;
4362135446Strhodes	}
4363135446Strhodes
4364135446Strhodes	ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE);
4365186462Sdougb
4366135446Strhodes clean:
4367135446Strhodes	ns_listenlist_detach(&list);
4368135446Strhodes	return;
4369135446Strhodes
4370135446Strhodes fail:
4371135446Strhodes	/*
4372135446Strhodes	 * Even when we failed the procedure, most of other interfaces
4373135446Strhodes	 * should work correctly.  We therefore just warn it.
4374135446Strhodes	 */
4375135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4376135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
4377135446Strhodes		      "could not adjust the listen-on list; "
4378135446Strhodes		      "some interfaces may not work");
4379135446Strhodes	goto clean;
4380135446Strhodes}
4381135446Strhodes
4382135446Strhodes/*
4383135446Strhodes * This event callback is invoked to do periodic network
4384135446Strhodes * interface scanning.
4385135446Strhodes */
4386135446Strhodesstatic void
4387135446Strhodesinterface_timer_tick(isc_task_t *task, isc_event_t *event) {
4388135446Strhodes	isc_result_t result;
4389135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
4390135446Strhodes	INSIST(task == server->task);
4391135446Strhodes	UNUSED(task);
4392135446Strhodes	isc_event_free(&event);
4393135446Strhodes	/*
4394135446Strhodes	 * XXX should scan interfaces unlocked and get exclusive access
4395135446Strhodes	 * only to replace ACLs.
4396135446Strhodes	 */
4397135446Strhodes	result = isc_task_beginexclusive(server->task);
4398135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
4399135446Strhodes	scan_interfaces(server, ISC_FALSE);
4400135446Strhodes	isc_task_endexclusive(server->task);
4401135446Strhodes}
4402135446Strhodes
4403135446Strhodesstatic void
4404135446Strhodesheartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
4405135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
4406135446Strhodes	dns_view_t *view;
4407135446Strhodes
4408135446Strhodes	UNUSED(task);
4409135446Strhodes	isc_event_free(&event);
4410135446Strhodes	view = ISC_LIST_HEAD(server->viewlist);
4411135446Strhodes	while (view != NULL) {
4412135446Strhodes		dns_view_dialup(view);
4413135446Strhodes		view = ISC_LIST_NEXT(view, link);
4414135446Strhodes	}
4415135446Strhodes}
4416135446Strhodes
4417170222Sdougbstatic void
4418170222Sdougbpps_timer_tick(isc_task_t *task, isc_event_t *event) {
4419170222Sdougb	static unsigned int oldrequests = 0;
4420170222Sdougb	unsigned int requests = ns_client_requests;
4421170222Sdougb
4422170222Sdougb	UNUSED(task);
4423170222Sdougb	isc_event_free(&event);
4424170222Sdougb
4425170222Sdougb	/*
4426170222Sdougb	 * Don't worry about wrapping as the overflow result will be right.
4427170222Sdougb	 */
4428170222Sdougb	dns_pps = (requests - oldrequests) / 1200;
4429170222Sdougb	oldrequests = requests;
4430170222Sdougb}
4431170222Sdougb
4432135446Strhodes/*
4433135446Strhodes * Replace the current value of '*field', a dynamically allocated
4434135446Strhodes * string or NULL, with a dynamically allocated copy of the
4435135446Strhodes * null-terminated string pointed to by 'value', or NULL.
4436135446Strhodes */
4437135446Strhodesstatic isc_result_t
4438135446Strhodessetstring(ns_server_t *server, char **field, const char *value) {
4439135446Strhodes	char *copy;
4440135446Strhodes
4441135446Strhodes	if (value != NULL) {
4442135446Strhodes		copy = isc_mem_strdup(server->mctx, value);
4443135446Strhodes		if (copy == NULL)
4444135446Strhodes			return (ISC_R_NOMEMORY);
4445135446Strhodes	} else {
4446135446Strhodes		copy = NULL;
4447135446Strhodes	}
4448135446Strhodes
4449135446Strhodes	if (*field != NULL)
4450135446Strhodes		isc_mem_free(server->mctx, *field);
4451135446Strhodes
4452135446Strhodes	*field = copy;
4453135446Strhodes	return (ISC_R_SUCCESS);
4454186462Sdougb}
4455135446Strhodes
4456135446Strhodes/*
4457135446Strhodes * Replace the current value of '*field', a dynamically allocated
4458135446Strhodes * string or NULL, with another dynamically allocated string
4459135446Strhodes * or NULL if whether 'obj' is a string or void value, respectively.
4460135446Strhodes */
4461135446Strhodesstatic isc_result_t
4462165071Sdougbsetoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) {
4463135446Strhodes	if (cfg_obj_isvoid(obj))
4464135446Strhodes		return (setstring(server, field, NULL));
4465135446Strhodes	else
4466135446Strhodes		return (setstring(server, field, cfg_obj_asstring(obj)));
4467135446Strhodes}
4468135446Strhodes
4469135446Strhodesstatic void
4470165071Sdougbset_limit(const cfg_obj_t **maps, const char *configname,
4471165071Sdougb	  const char *description, isc_resource_t resourceid,
4472165071Sdougb	  isc_resourcevalue_t defaultvalue)
4473135446Strhodes{
4474165071Sdougb	const cfg_obj_t *obj = NULL;
4475165071Sdougb	const char *resource;
4476135446Strhodes	isc_resourcevalue_t value;
4477135446Strhodes	isc_result_t result;
4478135446Strhodes
4479135446Strhodes	if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS)
4480135446Strhodes		return;
4481135446Strhodes
4482135446Strhodes	if (cfg_obj_isstring(obj)) {
4483135446Strhodes		resource = cfg_obj_asstring(obj);
4484135446Strhodes		if (strcasecmp(resource, "unlimited") == 0)
4485135446Strhodes			value = ISC_RESOURCE_UNLIMITED;
4486135446Strhodes		else {
4487135446Strhodes			INSIST(strcasecmp(resource, "default") == 0);
4488135446Strhodes			value = defaultvalue;
4489135446Strhodes		}
4490135446Strhodes	} else
4491135446Strhodes		value = cfg_obj_asuint64(obj);
4492135446Strhodes
4493135446Strhodes	result = isc_resource_setlimit(resourceid, value);
4494135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4495135446Strhodes		      result == ISC_R_SUCCESS ?
4496186462Sdougb			ISC_LOG_DEBUG(3) : ISC_LOG_WARNING,
4497204619Sdougb		      "set maximum %s to %" ISC_PRINT_QUADFORMAT "u: %s",
4498135446Strhodes		      description, value, isc_result_totext(result));
4499135446Strhodes}
4500135446Strhodes
4501135446Strhodes#define SETLIMIT(cfgvar, resource, description) \
4502135446Strhodes	set_limit(maps, cfgvar, description, isc_resource_ ## resource, \
4503135446Strhodes		  ns_g_init ## resource)
4504135446Strhodes
4505135446Strhodesstatic void
4506165071Sdougbset_limits(const cfg_obj_t **maps) {
4507135446Strhodes	SETLIMIT("stacksize", stacksize, "stack size");
4508135446Strhodes	SETLIMIT("datasize", datasize, "data size");
4509135446Strhodes	SETLIMIT("coresize", coresize, "core size");
4510135446Strhodes	SETLIMIT("files", openfiles, "open files");
4511135446Strhodes}
4512135446Strhodes
4513186462Sdougbstatic void
4514186462Sdougbportset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports,
4515186462Sdougb		 isc_boolean_t positive)
4516135446Strhodes{
4517165071Sdougb	const cfg_listelt_t *element;
4518135446Strhodes
4519135446Strhodes	for (element = cfg_list_first(ports);
4520135446Strhodes	     element != NULL;
4521135446Strhodes	     element = cfg_list_next(element)) {
4522165071Sdougb		const cfg_obj_t *obj = cfg_listelt_value(element);
4523186462Sdougb
4524186462Sdougb		if (cfg_obj_isuint32(obj)) {
4525186462Sdougb			in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
4526186462Sdougb
4527186462Sdougb			if (positive)
4528186462Sdougb				isc_portset_add(portset, port);
4529186462Sdougb			else
4530186462Sdougb				isc_portset_remove(portset, port);
4531186462Sdougb		} else {
4532186462Sdougb			const cfg_obj_t *obj_loport, *obj_hiport;
4533186462Sdougb			in_port_t loport, hiport;
4534186462Sdougb
4535186462Sdougb			obj_loport = cfg_tuple_get(obj, "loport");
4536186462Sdougb			loport = (in_port_t)cfg_obj_asuint32(obj_loport);
4537186462Sdougb			obj_hiport = cfg_tuple_get(obj, "hiport");
4538186462Sdougb			hiport = (in_port_t)cfg_obj_asuint32(obj_hiport);
4539186462Sdougb
4540186462Sdougb			if (positive)
4541186462Sdougb				isc_portset_addrange(portset, loport, hiport);
4542186462Sdougb			else {
4543186462Sdougb				isc_portset_removerange(portset, loport,
4544186462Sdougb							hiport);
4545186462Sdougb			}
4546186462Sdougb		}
4547135446Strhodes	}
4548135446Strhodes}
4549135446Strhodes
4550135446Strhodesstatic isc_result_t
4551170222Sdougbremoved(dns_zone_t *zone, void *uap) {
4552170222Sdougb	const char *type;
4553170222Sdougb
4554186462Sdougb	if (dns_zone_getview(zone) != uap)
4555170222Sdougb		return (ISC_R_SUCCESS);
4556170222Sdougb
4557170222Sdougb	switch (dns_zone_gettype(zone)) {
4558170222Sdougb	case dns_zone_master:
4559170222Sdougb		type = "master";
4560170222Sdougb		break;
4561170222Sdougb	case dns_zone_slave:
4562170222Sdougb		type = "slave";
4563170222Sdougb		break;
4564170222Sdougb	case dns_zone_stub:
4565170222Sdougb		type = "stub";
4566170222Sdougb		break;
4567254897Serwin	case dns_zone_redirect:
4568254897Serwin		type = "redirect";
4569254897Serwin		break;
4570170222Sdougb	default:
4571170222Sdougb		type = "other";
4572170222Sdougb		break;
4573170222Sdougb	}
4574170222Sdougb	dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type);
4575170222Sdougb	return (ISC_R_SUCCESS);
4576170222Sdougb}
4577170222Sdougb
4578224092Sdougbstatic void
4579224092Sdougbcleanup_session_key(ns_server_t *server, isc_mem_t *mctx) {
4580224092Sdougb	if (server->session_keyfile != NULL) {
4581224092Sdougb		isc_file_remove(server->session_keyfile);
4582224092Sdougb		isc_mem_free(mctx, server->session_keyfile);
4583224092Sdougb		server->session_keyfile = NULL;
4584224092Sdougb	}
4585224092Sdougb
4586224092Sdougb	if (server->session_keyname != NULL) {
4587224092Sdougb		if (dns_name_dynamic(server->session_keyname))
4588224092Sdougb			dns_name_free(server->session_keyname, mctx);
4589224092Sdougb		isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t));
4590224092Sdougb		server->session_keyname = NULL;
4591224092Sdougb	}
4592224092Sdougb
4593224092Sdougb	if (server->sessionkey != NULL)
4594224092Sdougb		dns_tsigkey_detach(&server->sessionkey);
4595224092Sdougb
4596224092Sdougb	server->session_keyalg = DST_ALG_UNKNOWN;
4597224092Sdougb	server->session_keybits = 0;
4598224092Sdougb}
4599224092Sdougb
4600170222Sdougbstatic isc_result_t
4601224092Sdougbgenerate_session_key(const char *filename, const char *keynamestr,
4602224092Sdougb		     dns_name_t *keyname, const char *algstr,
4603224092Sdougb		     dns_name_t *algname, unsigned int algtype,
4604224092Sdougb		     isc_uint16_t bits, isc_mem_t *mctx,
4605224092Sdougb		     dns_tsigkey_t **tsigkeyp)
4606224092Sdougb{
4607224092Sdougb	isc_result_t result = ISC_R_SUCCESS;
4608224092Sdougb	dst_key_t *key = NULL;
4609224092Sdougb	isc_buffer_t key_txtbuffer;
4610224092Sdougb	isc_buffer_t key_rawbuffer;
4611224092Sdougb	char key_txtsecret[256];
4612224092Sdougb	char key_rawsecret[64];
4613224092Sdougb	isc_region_t key_rawregion;
4614224092Sdougb	isc_stdtime_t now;
4615224092Sdougb	dns_tsigkey_t *tsigkey = NULL;
4616224092Sdougb	FILE *fp = NULL;
4617224092Sdougb
4618224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4619224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4620224092Sdougb		      "generating session key for dynamic DNS");
4621224092Sdougb
4622224092Sdougb	/* generate key */
4623224092Sdougb	result = dst_key_generate(keyname, algtype, bits, 1, 0,
4624224092Sdougb				  DNS_KEYPROTO_ANY, dns_rdataclass_in,
4625224092Sdougb				  mctx, &key);
4626224092Sdougb	if (result != ISC_R_SUCCESS)
4627224092Sdougb		return (result);
4628224092Sdougb
4629224092Sdougb	/*
4630224092Sdougb	 * Dump the key to the buffer for later use.  Should be done before
4631224092Sdougb	 * we transfer the ownership of key to tsigkey.
4632224092Sdougb	 */
4633224092Sdougb	isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));
4634224092Sdougb	CHECK(dst_key_tobuffer(key, &key_rawbuffer));
4635224092Sdougb
4636224092Sdougb	isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);
4637224092Sdougb	isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret));
4638224092Sdougb	CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer));
4639224092Sdougb
4640224092Sdougb	/* Store the key in tsigkey. */
4641224092Sdougb	isc_stdtime_get(&now);
4642224092Sdougb	CHECK(dns_tsigkey_createfromkey(dst_key_name(key), algname, key,
4643224092Sdougb					ISC_FALSE, NULL, now, now, mctx, NULL,
4644224092Sdougb					&tsigkey));
4645224092Sdougb
4646224092Sdougb	/* Dump the key to the key file. */
4647224092Sdougb	fp = ns_os_openfile(filename, S_IRUSR|S_IWUSR, ISC_TRUE);
4648224092Sdougb	if (fp == NULL) {
4649224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4650224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4651224092Sdougb			      "could not create %s", filename);
4652224092Sdougb		result = ISC_R_NOPERM;
4653224092Sdougb		goto cleanup;
4654224092Sdougb	}
4655224092Sdougb
4656224092Sdougb	fprintf(fp, "key \"%s\" {\n"
4657224092Sdougb		"\talgorithm %s;\n"
4658224092Sdougb		"\tsecret \"%.*s\";\n};\n", keynamestr, algstr,
4659224092Sdougb		(int) isc_buffer_usedlength(&key_txtbuffer),
4660224092Sdougb		(char*) isc_buffer_base(&key_txtbuffer));
4661224092Sdougb
4662224092Sdougb	RUNTIME_CHECK(isc_stdio_flush(fp) == ISC_R_SUCCESS);
4663224092Sdougb	RUNTIME_CHECK(isc_stdio_close(fp) == ISC_R_SUCCESS);
4664224092Sdougb
4665224092Sdougb	dst_key_free(&key);
4666224092Sdougb
4667224092Sdougb	*tsigkeyp = tsigkey;
4668224092Sdougb
4669224092Sdougb	return (ISC_R_SUCCESS);
4670224092Sdougb
4671224092Sdougb  cleanup:
4672224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4673224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4674224092Sdougb		      "failed to generate session key "
4675224092Sdougb		      "for dynamic DNS: %s", isc_result_totext(result));
4676224092Sdougb	if (tsigkey != NULL)
4677224092Sdougb		dns_tsigkey_detach(&tsigkey);
4678224092Sdougb	if (key != NULL)
4679224092Sdougb		dst_key_free(&key);
4680224092Sdougb
4681224092Sdougb	return (result);
4682224092Sdougb}
4683224092Sdougb
4684224092Sdougbstatic isc_result_t
4685224092Sdougbconfigure_session_key(const cfg_obj_t **maps, ns_server_t *server,
4686224092Sdougb		      isc_mem_t *mctx)
4687224092Sdougb{
4688224092Sdougb	const char *keyfile, *keynamestr, *algstr;
4689224092Sdougb	unsigned int algtype;
4690224092Sdougb	dns_fixedname_t fname;
4691224092Sdougb	dns_name_t *keyname, *algname;
4692224092Sdougb	isc_buffer_t buffer;
4693224092Sdougb	isc_uint16_t bits;
4694224092Sdougb	const cfg_obj_t *obj;
4695224092Sdougb	isc_boolean_t need_deleteold = ISC_FALSE;
4696224092Sdougb	isc_boolean_t need_createnew = ISC_FALSE;
4697224092Sdougb	isc_result_t result;
4698224092Sdougb
4699224092Sdougb	obj = NULL;
4700224092Sdougb	result = ns_config_get(maps, "session-keyfile", &obj);
4701224092Sdougb	if (result == ISC_R_SUCCESS) {
4702224092Sdougb		if (cfg_obj_isvoid(obj))
4703224092Sdougb			keyfile = NULL; /* disable it */
4704224092Sdougb		else
4705224092Sdougb			keyfile = cfg_obj_asstring(obj);
4706224092Sdougb	} else
4707224092Sdougb		keyfile = ns_g_defaultsessionkeyfile;
4708224092Sdougb
4709224092Sdougb	obj = NULL;
4710224092Sdougb	result = ns_config_get(maps, "session-keyname", &obj);
4711224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4712224092Sdougb	keynamestr = cfg_obj_asstring(obj);
4713224092Sdougb	dns_fixedname_init(&fname);
4714254402Serwin	isc_buffer_constinit(&buffer, keynamestr, strlen(keynamestr));
4715224092Sdougb	isc_buffer_add(&buffer, strlen(keynamestr));
4716224092Sdougb	keyname = dns_fixedname_name(&fname);
4717224092Sdougb	result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL);
4718224092Sdougb	if (result != ISC_R_SUCCESS)
4719224092Sdougb		return (result);
4720224092Sdougb
4721224092Sdougb	obj = NULL;
4722224092Sdougb	result = ns_config_get(maps, "session-keyalg", &obj);
4723224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4724224092Sdougb	algstr = cfg_obj_asstring(obj);
4725224092Sdougb	algname = NULL;
4726224092Sdougb	result = ns_config_getkeyalgorithm2(algstr, &algname, &algtype, &bits);
4727224092Sdougb	if (result != ISC_R_SUCCESS) {
4728224092Sdougb		const char *s = " (keeping current key)";
4729224092Sdougb
4730224092Sdougb		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "session-keyalg: "
4731224092Sdougb			    "unsupported or unknown algorithm '%s'%s",
4732224092Sdougb			    algstr,
4733224092Sdougb			    server->session_keyfile != NULL ? s : "");
4734224092Sdougb		return (result);
4735224092Sdougb	}
4736224092Sdougb
4737224092Sdougb	/* See if we need to (re)generate a new key. */
4738224092Sdougb	if (keyfile == NULL) {
4739224092Sdougb		if (server->session_keyfile != NULL)
4740224092Sdougb			need_deleteold = ISC_TRUE;
4741224092Sdougb	} else if (server->session_keyfile == NULL)
4742224092Sdougb		need_createnew = ISC_TRUE;
4743224092Sdougb	else if (strcmp(keyfile, server->session_keyfile) != 0 ||
4744224092Sdougb		 !dns_name_equal(server->session_keyname, keyname) ||
4745224092Sdougb		 server->session_keyalg != algtype ||
4746224092Sdougb		 server->session_keybits != bits) {
4747224092Sdougb		need_deleteold = ISC_TRUE;
4748224092Sdougb		need_createnew = ISC_TRUE;
4749224092Sdougb	}
4750224092Sdougb
4751224092Sdougb	if (need_deleteold) {
4752224092Sdougb		INSIST(server->session_keyfile != NULL);
4753224092Sdougb		INSIST(server->session_keyname != NULL);
4754224092Sdougb		INSIST(server->sessionkey != NULL);
4755224092Sdougb
4756224092Sdougb		cleanup_session_key(server, mctx);
4757224092Sdougb	}
4758224092Sdougb
4759224092Sdougb	if (need_createnew) {
4760224092Sdougb		INSIST(server->sessionkey == NULL);
4761224092Sdougb		INSIST(server->session_keyfile == NULL);
4762224092Sdougb		INSIST(server->session_keyname == NULL);
4763224092Sdougb		INSIST(server->session_keyalg == DST_ALG_UNKNOWN);
4764224092Sdougb		INSIST(server->session_keybits == 0);
4765224092Sdougb
4766224092Sdougb		server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t));
4767224092Sdougb		if (server->session_keyname == NULL)
4768224092Sdougb			goto cleanup;
4769224092Sdougb		dns_name_init(server->session_keyname, NULL);
4770224092Sdougb		CHECK(dns_name_dup(keyname, mctx, server->session_keyname));
4771224092Sdougb
4772224092Sdougb		server->session_keyfile = isc_mem_strdup(mctx, keyfile);
4773224092Sdougb		if (server->session_keyfile == NULL)
4774224092Sdougb			goto cleanup;
4775224092Sdougb
4776224092Sdougb		server->session_keyalg = algtype;
4777224092Sdougb		server->session_keybits = bits;
4778224092Sdougb
4779224092Sdougb		CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr,
4780224092Sdougb					   algname, algtype, bits, mctx,
4781224092Sdougb					   &server->sessionkey));
4782224092Sdougb	}
4783224092Sdougb
4784224092Sdougb	return (result);
4785224092Sdougb
4786224092Sdougb  cleanup:
4787224092Sdougb	cleanup_session_key(server, mctx);
4788224092Sdougb	return (result);
4789224092Sdougb}
4790224092Sdougb
4791224092Sdougbstatic isc_result_t
4792225361Sdougbsetup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
4793225361Sdougb	       cfg_parser_t *parser, cfg_aclconfctx_t *actx)
4794225361Sdougb{
4795225361Sdougb	isc_result_t result = ISC_R_SUCCESS;
4796225361Sdougb	isc_boolean_t allow = ISC_FALSE;
4797225361Sdougb	struct cfg_context *nzcfg = NULL;
4798225361Sdougb	cfg_parser_t *nzparser = NULL;
4799225361Sdougb	cfg_obj_t *nzconfig = NULL;
4800225361Sdougb	const cfg_obj_t *maps[4];
4801225361Sdougb	const cfg_obj_t *options = NULL, *voptions = NULL;
4802225361Sdougb	const cfg_obj_t *nz = NULL;
4803225361Sdougb	int i = 0;
4804225361Sdougb
4805225361Sdougb	REQUIRE (config != NULL);
4806225361Sdougb
4807225361Sdougb	if (vconfig != NULL)
4808225361Sdougb		voptions = cfg_tuple_get(vconfig, "options");
4809225361Sdougb	if (voptions != NULL)
4810225361Sdougb		maps[i++] = voptions;
4811225361Sdougb	result = cfg_map_get(config, "options", &options);
4812225361Sdougb	if (result == ISC_R_SUCCESS)
4813225361Sdougb		maps[i++] = options;
4814225361Sdougb	maps[i++] = ns_g_defaults;
4815225361Sdougb	maps[i] = NULL;
4816225361Sdougb
4817225361Sdougb	result = ns_config_get(maps, "allow-new-zones", &nz);
4818225361Sdougb	if (result == ISC_R_SUCCESS)
4819225361Sdougb		allow = cfg_obj_asboolean(nz);
4820225361Sdougb
4821225361Sdougb	if (!allow) {
4822225361Sdougb		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
4823225361Sdougb		return (ISC_R_SUCCESS);
4824225361Sdougb	}
4825225361Sdougb
4826225361Sdougb	nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg));
4827225361Sdougb	if (nzcfg == NULL) {
4828225361Sdougb		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
4829225361Sdougb		return (ISC_R_NOMEMORY);
4830225361Sdougb	}
4831225361Sdougb
4832225361Sdougb	dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy);
4833225361Sdougb
4834225361Sdougb	memset(nzcfg, 0, sizeof(*nzcfg));
4835225361Sdougb	isc_mem_attach(view->mctx, &nzcfg->mctx);
4836225361Sdougb	cfg_obj_attach(config, &nzcfg->config);
4837225361Sdougb	cfg_parser_attach(parser, &nzcfg->parser);
4838225361Sdougb	cfg_aclconfctx_attach(actx, &nzcfg->actx);
4839225361Sdougb
4840225361Sdougb	/*
4841225361Sdougb	 * Attempt to create a parser and parse the newzones
4842225361Sdougb	 * file.  If successful, preserve both; otherwise leave
4843225361Sdougb	 * them NULL.
4844225361Sdougb	 */
4845225361Sdougb	result = cfg_parser_create(view->mctx, ns_g_lctx, &nzparser);
4846225361Sdougb	if (result == ISC_R_SUCCESS)
4847225361Sdougb		result = cfg_parse_file(nzparser, view->new_zone_file,
4848225361Sdougb					&cfg_type_newzones, &nzconfig);
4849225361Sdougb	if (result == ISC_R_SUCCESS) {
4850225361Sdougb		cfg_parser_attach(nzparser, &nzcfg->nzparser);
4851225361Sdougb		cfg_obj_attach(nzconfig, &nzcfg->nzconfig);
4852225361Sdougb	}
4853225361Sdougb
4854225361Sdougb	if (nzparser != NULL) {
4855225361Sdougb		if (nzconfig != NULL)
4856225361Sdougb			cfg_obj_destroy(nzparser, &nzconfig);
4857225361Sdougb		cfg_parser_destroy(&nzparser);
4858225361Sdougb	}
4859225361Sdougb
4860225361Sdougb	return (ISC_R_SUCCESS);
4861225361Sdougb}
4862225361Sdougb
4863225361Sdougbstatic int
4864225361Sdougbcount_zones(const cfg_obj_t *conf) {
4865225361Sdougb	const cfg_obj_t *zonelist = NULL;
4866225361Sdougb	const cfg_listelt_t *element;
4867225361Sdougb	int n = 0;
4868225361Sdougb
4869225361Sdougb	REQUIRE(conf != NULL);
4870225361Sdougb
4871225361Sdougb	cfg_map_get(conf, "zone", &zonelist);
4872225361Sdougb	for (element = cfg_list_first(zonelist);
4873225361Sdougb	     element != NULL;
4874225361Sdougb	     element = cfg_list_next(element))
4875225361Sdougb		n++;
4876225361Sdougb
4877225361Sdougb	return (n);
4878225361Sdougb}
4879225361Sdougb
4880225361Sdougbstatic isc_result_t
4881135446Strhodesload_configuration(const char *filename, ns_server_t *server,
4882135446Strhodes		   isc_boolean_t first_time)
4883135446Strhodes{
4884224092Sdougb	cfg_obj_t *config = NULL, *bindkeys = NULL;
4885224092Sdougb	cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL;
4886182645Sdougb	const cfg_listelt_t *element;
4887182645Sdougb	const cfg_obj_t *builtin_views;
4888182645Sdougb	const cfg_obj_t *maps[3];
4889182645Sdougb	const cfg_obj_t *obj;
4890165071Sdougb	const cfg_obj_t *options;
4891186462Sdougb	const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports;
4892165071Sdougb	const cfg_obj_t *views;
4893135446Strhodes	dns_view_t *view = NULL;
4894135446Strhodes	dns_view_t *view_next;
4895182645Sdougb	dns_viewlist_t tmpviewlist;
4896224092Sdougb	dns_viewlist_t viewlist, builtin_viewlist;
4897186462Sdougb	in_port_t listen_port, udpport_low, udpport_high;
4898182645Sdougb	int i;
4899262706Serwin	int num_zones = 0;
4900262706Serwin	isc_boolean_t exclusive = ISC_FALSE;
4901182645Sdougb	isc_interval_t interval;
4902262706Serwin	isc_logconfig_t *logc = NULL;
4903186462Sdougb	isc_portset_t *v4portset = NULL;
4904186462Sdougb	isc_portset_t *v6portset = NULL;
4905186462Sdougb	isc_resourcevalue_t nfiles;
4906182645Sdougb	isc_result_t result;
4907182645Sdougb	isc_uint32_t heartbeat_interval;
4908135446Strhodes	isc_uint32_t interface_interval;
4909182645Sdougb	isc_uint32_t reserved;
4910135446Strhodes	isc_uint32_t udpsize;
4911262706Serwin	ns_cache_t *nsc;
4912224092Sdougb	ns_cachelist_t cachelist, tmpcachelist;
4913262706Serwin	struct cfg_context *nzctx;
4914186462Sdougb	unsigned int maxsocks;
4915135446Strhodes
4916135446Strhodes	ISC_LIST_INIT(viewlist);
4917224092Sdougb	ISC_LIST_INIT(builtin_viewlist);
4918224092Sdougb	ISC_LIST_INIT(cachelist);
4919135446Strhodes
4920225361Sdougb	/* Create the ACL configuration context */
4921225361Sdougb	if (ns_g_aclconfctx != NULL)
4922225361Sdougb		cfg_aclconfctx_detach(&ns_g_aclconfctx);
4923225361Sdougb	CHECK(cfg_aclconfctx_create(ns_g_mctx, &ns_g_aclconfctx));
4924225361Sdougb
4925135446Strhodes	/*
4926135446Strhodes	 * Parse the global default pseudo-config file.
4927135446Strhodes	 */
4928135446Strhodes	if (first_time) {
4929135446Strhodes		CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config));
4930135446Strhodes		RUNTIME_CHECK(cfg_map_get(ns_g_config, "options",
4931224092Sdougb					  &ns_g_defaults) == ISC_R_SUCCESS);
4932135446Strhodes	}
4933135446Strhodes
4934135446Strhodes	/*
4935135446Strhodes	 * Parse the configuration file using the new config code.
4936135446Strhodes	 */
4937135446Strhodes	result = ISC_R_FAILURE;
4938135446Strhodes	config = NULL;
4939135446Strhodes
4940135446Strhodes	/*
4941135446Strhodes	 * Unless this is lwresd with the -C option, parse the config file.
4942135446Strhodes	 */
4943135446Strhodes	if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) {
4944135446Strhodes		isc_log_write(ns_g_lctx,
4945135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4946135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
4947135446Strhodes			      filename);
4948224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
4949224092Sdougb		cfg_parser_setcallback(conf_parser, directory_callback, NULL);
4950224092Sdougb		result = cfg_parse_file(conf_parser, filename,
4951224092Sdougb					&cfg_type_namedconf, &config);
4952135446Strhodes	}
4953135446Strhodes
4954135446Strhodes	/*
4955135446Strhodes	 * If this is lwresd with the -C option, or lwresd with no -C or -c
4956135446Strhodes	 * option where the above parsing failed, parse resolv.conf.
4957135446Strhodes	 */
4958135446Strhodes	if (ns_g_lwresdonly &&
4959135446Strhodes	    (lwresd_g_useresolvconf ||
4960135446Strhodes	     (!ns_g_conffileset && result == ISC_R_FILENOTFOUND)))
4961135446Strhodes	{
4962135446Strhodes		isc_log_write(ns_g_lctx,
4963135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4964135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
4965135446Strhodes			      lwresd_g_resolvconffile);
4966224092Sdougb		if (conf_parser != NULL)
4967224092Sdougb			cfg_parser_destroy(&conf_parser);
4968224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
4969224092Sdougb		result = ns_lwresd_parseeresolvconf(ns_g_mctx, conf_parser,
4970135446Strhodes						    &config);
4971135446Strhodes	}
4972135446Strhodes	CHECK(result);
4973135446Strhodes
4974135446Strhodes	/*
4975135446Strhodes	 * Check the validity of the configuration.
4976135446Strhodes	 */
4977135446Strhodes	CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx));
4978135446Strhodes
4979135446Strhodes	/*
4980135446Strhodes	 * Fill in the maps array, used for resolving defaults.
4981135446Strhodes	 */
4982135446Strhodes	i = 0;
4983135446Strhodes	options = NULL;
4984135446Strhodes	result = cfg_map_get(config, "options", &options);
4985135446Strhodes	if (result == ISC_R_SUCCESS)
4986135446Strhodes		maps[i++] = options;
4987135446Strhodes	maps[i++] = ns_g_defaults;
4988225361Sdougb	maps[i] = NULL;
4989135446Strhodes
4990135446Strhodes	/*
4991224092Sdougb	 * If bind.keys exists, load it.  If "dnssec-lookaside auto"
4992224092Sdougb	 * is turned on, the keys found there will be used as default
4993224092Sdougb	 * trust anchors.
4994224092Sdougb	 */
4995224092Sdougb	obj = NULL;
4996224092Sdougb	result = ns_config_get(maps, "bindkeys-file", &obj);
4997224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4998224092Sdougb	CHECKM(setstring(server, &server->bindkeysfile,
4999224092Sdougb	       cfg_obj_asstring(obj)), "strdup");
5000224092Sdougb
5001224092Sdougb	if (access(server->bindkeysfile, R_OK) == 0) {
5002224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5003224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5004224092Sdougb			      "reading built-in trusted "
5005224092Sdougb			      "keys from file '%s'", server->bindkeysfile);
5006224092Sdougb
5007224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx,
5008224092Sdougb					&bindkeys_parser));
5009224092Sdougb
5010224092Sdougb		result = cfg_parse_file(bindkeys_parser, server->bindkeysfile,
5011224092Sdougb					&cfg_type_bindkeys, &bindkeys);
5012224092Sdougb		CHECK(result);
5013224092Sdougb	}
5014224092Sdougb
5015234010Sdougb	/* Ensure exclusive access to configuration data. */
5016234010Sdougb	if (!exclusive) {
5017234010Sdougb		result = isc_task_beginexclusive(server->task);
5018234010Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5019234010Sdougb		exclusive = ISC_TRUE;
5020234010Sdougb	}
5021234010Sdougb
5022224092Sdougb	/*
5023135446Strhodes	 * Set process limits, which (usually) needs to be done as root.
5024135446Strhodes	 */
5025135446Strhodes	set_limits(maps);
5026135446Strhodes
5027135446Strhodes	/*
5028186462Sdougb	 * Check if max number of open sockets that the system allows is
5029224092Sdougb	 * sufficiently large.	Failing this condition is not necessarily fatal,
5030186462Sdougb	 * but may cause subsequent runtime failures for a busy recursive
5031186462Sdougb	 * server.
5032182645Sdougb	 */
5033186462Sdougb	result = isc_socketmgr_getmaxsockets(ns_g_socketmgr, &maxsocks);
5034186462Sdougb	if (result != ISC_R_SUCCESS)
5035186462Sdougb		maxsocks = 0;
5036186462Sdougb	result = isc_resource_getcurlimit(isc_resource_openfiles, &nfiles);
5037186462Sdougb	if (result == ISC_R_SUCCESS && (isc_resourcevalue_t)maxsocks > nfiles) {
5038182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5039182645Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
5040186462Sdougb			      "max open files (%" ISC_PRINT_QUADFORMAT "u)"
5041186462Sdougb			      " is smaller than max sockets (%u)",
5042186462Sdougb			      nfiles, maxsocks);
5043186462Sdougb	}
5044182645Sdougb
5045182645Sdougb	/*
5046182645Sdougb	 * Set the number of socket reserved for TCP, stdio etc.
5047182645Sdougb	 */
5048182645Sdougb	obj = NULL;
5049182645Sdougb	result = ns_config_get(maps, "reserved-sockets", &obj);
5050182645Sdougb	INSIST(result == ISC_R_SUCCESS);
5051182645Sdougb	reserved = cfg_obj_asuint32(obj);
5052186462Sdougb	if (maxsocks != 0) {
5053186462Sdougb		if (maxsocks < 128U)			/* Prevent underflow. */
5054186462Sdougb			reserved = 0;
5055186462Sdougb		else if (reserved > maxsocks - 128U)	/* Minimum UDP space. */
5056186462Sdougb			reserved = maxsocks - 128;
5057186462Sdougb	}
5058186462Sdougb	/* Minimum TCP/stdio space. */
5059186462Sdougb	if (reserved < 128U)
5060182645Sdougb		reserved = 128;
5061186462Sdougb	if (reserved + 128U > maxsocks && maxsocks != 0) {
5062182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5063186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
5064182645Sdougb			      "less than 128 UDP sockets available after "
5065186462Sdougb			      "applying 'reserved-sockets' and 'maxsockets'");
5066182645Sdougb	}
5067182645Sdougb	isc__socketmgr_setreserved(ns_g_socketmgr, reserved);
5068186462Sdougb
5069182645Sdougb	/*
5070135446Strhodes	 * Configure various server options.
5071135446Strhodes	 */
5072135446Strhodes	configure_server_quota(maps, "transfers-out", &server->xfroutquota);
5073135446Strhodes	configure_server_quota(maps, "tcp-clients", &server->tcpquota);
5074135446Strhodes	configure_server_quota(maps, "recursive-clients",
5075135446Strhodes			       &server->recursionquota);
5076153816Sdougb	if (server->recursionquota.max > 1000)
5077153816Sdougb		isc_quota_soft(&server->recursionquota,
5078153816Sdougb			       server->recursionquota.max - 100);
5079153816Sdougb	else
5080153816Sdougb		isc_quota_soft(&server->recursionquota, 0);
5081135446Strhodes
5082225361Sdougb	CHECK(configure_view_acl(NULL, config, "blackhole", NULL,
5083225361Sdougb				 ns_g_aclconfctx, ns_g_mctx,
5084225361Sdougb				 &server->blackholeacl));
5085135446Strhodes	if (server->blackholeacl != NULL)
5086135446Strhodes		dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
5087135446Strhodes					     server->blackholeacl);
5088135446Strhodes
5089135446Strhodes	obj = NULL;
5090135446Strhodes	result = ns_config_get(maps, "match-mapped-addresses", &obj);
5091135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5092135446Strhodes	server->aclenv.match_mapped = cfg_obj_asboolean(obj);
5093135446Strhodes
5094225361Sdougb	CHECKM(ns_statschannels_configure(ns_g_server, config, ns_g_aclconfctx),
5095193149Sdougb	       "configuring statistics server(s)");
5096193149Sdougb
5097186462Sdougb	/*
5098186462Sdougb	 * Configure sets of UDP query source ports.
5099186462Sdougb	 */
5100186462Sdougb	CHECKM(isc_portset_create(ns_g_mctx, &v4portset),
5101186462Sdougb	       "creating UDP port set");
5102186462Sdougb	CHECKM(isc_portset_create(ns_g_mctx, &v6portset),
5103186462Sdougb	       "creating UDP port set");
5104135446Strhodes
5105186462Sdougb	usev4ports = NULL;
5106186462Sdougb	usev6ports = NULL;
5107186462Sdougb	avoidv4ports = NULL;
5108186462Sdougb	avoidv6ports = NULL;
5109186462Sdougb
5110186462Sdougb	(void)ns_config_get(maps, "use-v4-udp-ports", &usev4ports);
5111186462Sdougb	if (usev4ports != NULL)
5112186462Sdougb		portset_fromconf(v4portset, usev4ports, ISC_TRUE);
5113186462Sdougb	else {
5114186462Sdougb		CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low,
5115186462Sdougb					       &udpport_high),
5116186462Sdougb		       "get the default UDP/IPv4 port range");
5117186462Sdougb		if (udpport_low == udpport_high)
5118186462Sdougb			isc_portset_add(v4portset, udpport_low);
5119186462Sdougb		else {
5120186462Sdougb			isc_portset_addrange(v4portset, udpport_low,
5121186462Sdougb					     udpport_high);
5122186462Sdougb		}
5123186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5124186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5125186462Sdougb			      "using default UDP/IPv4 port range: [%d, %d]",
5126186462Sdougb			      udpport_low, udpport_high);
5127186462Sdougb	}
5128186462Sdougb	(void)ns_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports);
5129186462Sdougb	if (avoidv4ports != NULL)
5130186462Sdougb		portset_fromconf(v4portset, avoidv4ports, ISC_FALSE);
5131186462Sdougb
5132186462Sdougb	(void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports);
5133186462Sdougb	if (usev6ports != NULL)
5134186462Sdougb		portset_fromconf(v6portset, usev6ports, ISC_TRUE);
5135186462Sdougb	else {
5136186462Sdougb		CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low,
5137186462Sdougb					       &udpport_high),
5138186462Sdougb		       "get the default UDP/IPv6 port range");
5139186462Sdougb		if (udpport_low == udpport_high)
5140186462Sdougb			isc_portset_add(v6portset, udpport_low);
5141186462Sdougb		else {
5142186462Sdougb			isc_portset_addrange(v6portset, udpport_low,
5143186462Sdougb					     udpport_high);
5144186462Sdougb		}
5145186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5146186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5147186462Sdougb			      "using default UDP/IPv6 port range: [%d, %d]",
5148186462Sdougb			      udpport_low, udpport_high);
5149186462Sdougb	}
5150186462Sdougb	(void)ns_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports);
5151186462Sdougb	if (avoidv6ports != NULL)
5152186462Sdougb		portset_fromconf(v6portset, avoidv6ports, ISC_FALSE);
5153186462Sdougb
5154186462Sdougb	dns_dispatchmgr_setavailports(ns_g_dispatchmgr, v4portset, v6portset);
5155186462Sdougb
5156135446Strhodes	/*
5157135446Strhodes	 * Set the EDNS UDP size when we don't match a view.
5158135446Strhodes	 */
5159135446Strhodes	obj = NULL;
5160135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
5161135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5162135446Strhodes	udpsize = cfg_obj_asuint32(obj);
5163135446Strhodes	if (udpsize < 512)
5164135446Strhodes		udpsize = 512;
5165135446Strhodes	if (udpsize > 4096)
5166135446Strhodes		udpsize = 4096;
5167135446Strhodes	ns_g_udpsize = (isc_uint16_t)udpsize;
5168135446Strhodes
5169135446Strhodes	/*
5170135446Strhodes	 * Configure the zone manager.
5171135446Strhodes	 */
5172135446Strhodes	obj = NULL;
5173135446Strhodes	result = ns_config_get(maps, "transfers-in", &obj);
5174135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5175135446Strhodes	dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj));
5176135446Strhodes
5177135446Strhodes	obj = NULL;
5178135446Strhodes	result = ns_config_get(maps, "transfers-per-ns", &obj);
5179135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5180135446Strhodes	dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj));
5181135446Strhodes
5182135446Strhodes	obj = NULL;
5183135446Strhodes	result = ns_config_get(maps, "serial-query-rate", &obj);
5184135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5185135446Strhodes	dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj));
5186135446Strhodes
5187135446Strhodes	/*
5188135446Strhodes	 * Determine which port to use for listening for incoming connections.
5189135446Strhodes	 */
5190135446Strhodes	if (ns_g_port != 0)
5191135446Strhodes		listen_port = ns_g_port;
5192135446Strhodes	else
5193135446Strhodes		CHECKM(ns_config_getport(config, &listen_port), "port");
5194135446Strhodes
5195135446Strhodes	/*
5196135446Strhodes	 * Find the listen queue depth.
5197135446Strhodes	 */
5198135446Strhodes	obj = NULL;
5199135446Strhodes	result = ns_config_get(maps, "tcp-listen-queue", &obj);
5200135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5201135446Strhodes	ns_g_listen = cfg_obj_asuint32(obj);
5202262706Serwin	if ((ns_g_listen > 0) && (ns_g_listen < 10))
5203262706Serwin		ns_g_listen = 10;
5204135446Strhodes
5205135446Strhodes	/*
5206135446Strhodes	 * Configure the interface manager according to the "listen-on"
5207135446Strhodes	 * statement.
5208135446Strhodes	 */
5209135446Strhodes	{
5210165071Sdougb		const cfg_obj_t *clistenon = NULL;
5211135446Strhodes		ns_listenlist_t *listenon = NULL;
5212135446Strhodes
5213135446Strhodes		clistenon = NULL;
5214135446Strhodes		/*
5215135446Strhodes		 * Even though listen-on is present in the default
5216135446Strhodes		 * configuration, we can't use it here, since it isn't
5217135446Strhodes		 * used if we're in lwresd mode.  This way is easier.
5218135446Strhodes		 */
5219135446Strhodes		if (options != NULL)
5220135446Strhodes			(void)cfg_map_get(options, "listen-on", &clistenon);
5221135446Strhodes		if (clistenon != NULL) {
5222225361Sdougb			/* check return code? */
5223225361Sdougb			(void)ns_listenlist_fromconfig(clistenon, config,
5224225361Sdougb						       ns_g_aclconfctx,
5225225361Sdougb						       ns_g_mctx, &listenon);
5226135446Strhodes		} else if (!ns_g_lwresdonly) {
5227135446Strhodes			/*
5228135446Strhodes			 * Not specified, use default.
5229135446Strhodes			 */
5230135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
5231135446Strhodes						    ISC_TRUE, &listenon));
5232135446Strhodes		}
5233135446Strhodes		if (listenon != NULL) {
5234135446Strhodes			ns_interfacemgr_setlistenon4(server->interfacemgr,
5235135446Strhodes						     listenon);
5236135446Strhodes			ns_listenlist_detach(&listenon);
5237135446Strhodes		}
5238135446Strhodes	}
5239135446Strhodes	/*
5240135446Strhodes	 * Ditto for IPv6.
5241135446Strhodes	 */
5242135446Strhodes	{
5243165071Sdougb		const cfg_obj_t *clistenon = NULL;
5244135446Strhodes		ns_listenlist_t *listenon = NULL;
5245135446Strhodes
5246135446Strhodes		if (options != NULL)
5247135446Strhodes			(void)cfg_map_get(options, "listen-on-v6", &clistenon);
5248135446Strhodes		if (clistenon != NULL) {
5249225361Sdougb			/* check return code? */
5250225361Sdougb			(void)ns_listenlist_fromconfig(clistenon, config,
5251225361Sdougb						       ns_g_aclconfctx,
5252225361Sdougb						       ns_g_mctx, &listenon);
5253135446Strhodes		} else if (!ns_g_lwresdonly) {
5254193149Sdougb			isc_boolean_t enable;
5255135446Strhodes			/*
5256135446Strhodes			 * Not specified, use default.
5257135446Strhodes			 */
5258193149Sdougb			enable = ISC_TF(isc_net_probeipv4() != ISC_R_SUCCESS);
5259135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
5260193149Sdougb						    enable, &listenon));
5261135446Strhodes		}
5262135446Strhodes		if (listenon != NULL) {
5263135446Strhodes			ns_interfacemgr_setlistenon6(server->interfacemgr,
5264135446Strhodes						     listenon);
5265135446Strhodes			ns_listenlist_detach(&listenon);
5266135446Strhodes		}
5267135446Strhodes	}
5268135446Strhodes
5269135446Strhodes	/*
5270135446Strhodes	 * Rescan the interface list to pick up changes in the
5271135446Strhodes	 * listen-on option.  It's important that we do this before we try
5272135446Strhodes	 * to configure the query source, since the dispatcher we use might
5273135446Strhodes	 * be shared with an interface.
5274135446Strhodes	 */
5275135446Strhodes	scan_interfaces(server, ISC_TRUE);
5276135446Strhodes
5277135446Strhodes	/*
5278135446Strhodes	 * Arrange for further interface scanning to occur periodically
5279135446Strhodes	 * as specified by the "interface-interval" option.
5280135446Strhodes	 */
5281135446Strhodes	obj = NULL;
5282135446Strhodes	result = ns_config_get(maps, "interface-interval", &obj);
5283135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5284135446Strhodes	interface_interval = cfg_obj_asuint32(obj) * 60;
5285135446Strhodes	if (interface_interval == 0) {
5286135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
5287135446Strhodes				      isc_timertype_inactive,
5288135446Strhodes				      NULL, NULL, ISC_TRUE));
5289135446Strhodes	} else if (server->interface_interval != interface_interval) {
5290135446Strhodes		isc_interval_set(&interval, interface_interval, 0);
5291135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
5292135446Strhodes				      isc_timertype_ticker,
5293135446Strhodes				      NULL, &interval, ISC_FALSE));
5294135446Strhodes	}
5295135446Strhodes	server->interface_interval = interface_interval;
5296135446Strhodes
5297135446Strhodes	/*
5298135446Strhodes	 * Configure the dialup heartbeat timer.
5299135446Strhodes	 */
5300135446Strhodes	obj = NULL;
5301135446Strhodes	result = ns_config_get(maps, "heartbeat-interval", &obj);
5302135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5303135446Strhodes	heartbeat_interval = cfg_obj_asuint32(obj) * 60;
5304135446Strhodes	if (heartbeat_interval == 0) {
5305135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
5306135446Strhodes				      isc_timertype_inactive,
5307135446Strhodes				      NULL, NULL, ISC_TRUE));
5308135446Strhodes	} else if (server->heartbeat_interval != heartbeat_interval) {
5309135446Strhodes		isc_interval_set(&interval, heartbeat_interval, 0);
5310135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
5311135446Strhodes				      isc_timertype_ticker,
5312135446Strhodes				      NULL, &interval, ISC_FALSE));
5313135446Strhodes	}
5314135446Strhodes	server->heartbeat_interval = heartbeat_interval;
5315186462Sdougb
5316170222Sdougb	isc_interval_set(&interval, 1200, 0);
5317170222Sdougb	CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
5318170222Sdougb			      &interval, ISC_FALSE));
5319135446Strhodes
5320135446Strhodes	/*
5321224092Sdougb	 * Write the PID file.
5322224092Sdougb	 */
5323224092Sdougb	obj = NULL;
5324224092Sdougb	if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS)
5325224092Sdougb		if (cfg_obj_isvoid(obj))
5326224092Sdougb			ns_os_writepidfile(NULL, first_time);
5327224092Sdougb		else
5328224092Sdougb			ns_os_writepidfile(cfg_obj_asstring(obj), first_time);
5329224092Sdougb	else if (ns_g_lwresdonly)
5330224092Sdougb		ns_os_writepidfile(lwresd_g_defaultpidfile, first_time);
5331224092Sdougb	else
5332224092Sdougb		ns_os_writepidfile(ns_g_defaultpidfile, first_time);
5333224092Sdougb
5334224092Sdougb	/*
5335224092Sdougb	 * Configure the server-wide session key.  This must be done before
5336224092Sdougb	 * configure views because zone configuration may need to know
5337224092Sdougb	 * session-keyname.
5338224092Sdougb	 *
5339224092Sdougb	 * Failure of session key generation isn't fatal at this time; if it
5340224092Sdougb	 * turns out that a session key is really needed but doesn't exist,
5341224092Sdougb	 * we'll treat it as a fatal error then.
5342224092Sdougb	 */
5343224092Sdougb	(void)configure_session_key(maps, server, ns_g_mctx);
5344224092Sdougb
5345225361Sdougb	views = NULL;
5346225361Sdougb	(void)cfg_map_get(config, "view", &views);
5347225361Sdougb
5348224092Sdougb	/*
5349225361Sdougb	 * Create the views and count all the configured zones in
5350225361Sdougb	 * order to correctly size the zone manager's task table.
5351225361Sdougb	 * (We only count zones for configured views; the built-in
5352225361Sdougb	 * "bind" view can be ignored as it only adds a negligible
5353225361Sdougb	 * number of zones.)
5354225361Sdougb	 *
5355225361Sdougb	 * If we're allowing new zones, we need to be able to find the
5356225361Sdougb	 * new zone file and count those as well.  So we setup the new
5357225361Sdougb	 * zone configuration context, but otherwise view configuration
5358225361Sdougb	 * waits until after the zone manager's task list has been sized.
5359225361Sdougb	 */
5360225361Sdougb	for (element = cfg_list_first(views);
5361225361Sdougb	     element != NULL;
5362225361Sdougb	     element = cfg_list_next(element))
5363225361Sdougb	{
5364225361Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
5365225361Sdougb		const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options");
5366225361Sdougb		view = NULL;
5367225361Sdougb
5368225361Sdougb		CHECK(create_view(vconfig, &viewlist, &view));
5369225361Sdougb		INSIST(view != NULL);
5370225361Sdougb
5371225361Sdougb		num_zones += count_zones(voptions);
5372225361Sdougb		CHECK(setup_newzones(view, config, vconfig, conf_parser,
5373225361Sdougb				     ns_g_aclconfctx));
5374225361Sdougb
5375225361Sdougb		nzctx = view->new_zone_config;
5376225361Sdougb		if (nzctx != NULL && nzctx->nzconfig != NULL)
5377225361Sdougb			num_zones += count_zones(nzctx->nzconfig);
5378225361Sdougb
5379225361Sdougb		dns_view_detach(&view);
5380225361Sdougb	}
5381225361Sdougb
5382225361Sdougb	/*
5383225361Sdougb	 * If there were no explicit views then we do the default
5384225361Sdougb	 * view here.
5385225361Sdougb	 */
5386225361Sdougb	if (views == NULL) {
5387225361Sdougb		CHECK(create_view(NULL, &viewlist, &view));
5388225361Sdougb		INSIST(view != NULL);
5389225361Sdougb
5390225361Sdougb		num_zones = count_zones(config);
5391225361Sdougb
5392225361Sdougb		CHECK(setup_newzones(view, config, NULL,  conf_parser,
5393225361Sdougb				     ns_g_aclconfctx));
5394225361Sdougb
5395225361Sdougb		nzctx = view->new_zone_config;
5396225361Sdougb		if (nzctx != NULL && nzctx->nzconfig != NULL)
5397225361Sdougb			num_zones += count_zones(nzctx->nzconfig);
5398225361Sdougb
5399225361Sdougb		dns_view_detach(&view);
5400225361Sdougb	}
5401225361Sdougb
5402225361Sdougb	/*
5403225361Sdougb	 * Zones have been counted; set the zone manager task pool size.
5404225361Sdougb	 */
5405225361Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5406225361Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5407225361Sdougb		      "sizing zone task pool based on %d zones", num_zones);
5408225361Sdougb	CHECK(dns_zonemgr_setsize(ns_g_server->zonemgr, num_zones));
5409225361Sdougb
5410225361Sdougb	/*
5411135446Strhodes	 * Configure and freeze all explicit views.  Explicit
5412135446Strhodes	 * views that have zones were already created at parsing
5413135446Strhodes	 * time, but views with no zones must be created here.
5414135446Strhodes	 */
5415135446Strhodes	for (element = cfg_list_first(views);
5416135446Strhodes	     element != NULL;
5417135446Strhodes	     element = cfg_list_next(element))
5418135446Strhodes	{
5419224092Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
5420225361Sdougb
5421135446Strhodes		view = NULL;
5422225361Sdougb		CHECK(find_view(vconfig, &viewlist, &view));
5423225361Sdougb		CHECK(configure_view(view, config, vconfig,
5424225361Sdougb				     &cachelist, bindkeys, ns_g_mctx,
5425225361Sdougb				     ns_g_aclconfctx, ISC_TRUE));
5426135446Strhodes		dns_view_freeze(view);
5427135446Strhodes		dns_view_detach(&view);
5428135446Strhodes	}
5429135446Strhodes
5430135446Strhodes	/*
5431135446Strhodes	 * Make sure we have a default view if and only if there
5432135446Strhodes	 * were no explicit views.
5433135446Strhodes	 */
5434135446Strhodes	if (views == NULL) {
5435225361Sdougb		view = NULL;
5436225361Sdougb		CHECK(find_view(NULL, &viewlist, &view));
5437225361Sdougb		CHECK(configure_view(view, config, NULL,
5438224092Sdougb				     &cachelist, bindkeys,
5439225361Sdougb				     ns_g_mctx, ns_g_aclconfctx, ISC_TRUE));
5440135446Strhodes		dns_view_freeze(view);
5441135446Strhodes		dns_view_detach(&view);
5442135446Strhodes	}
5443135446Strhodes
5444135446Strhodes	/*
5445224092Sdougb	 * Create (or recreate) the built-in views.
5446135446Strhodes	 */
5447135446Strhodes	builtin_views = NULL;
5448135446Strhodes	RUNTIME_CHECK(cfg_map_get(ns_g_config, "view",
5449135446Strhodes				  &builtin_views) == ISC_R_SUCCESS);
5450135446Strhodes	for (element = cfg_list_first(builtin_views);
5451135446Strhodes	     element != NULL;
5452135446Strhodes	     element = cfg_list_next(element))
5453135446Strhodes	{
5454224092Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
5455224092Sdougb
5456224092Sdougb		CHECK(create_view(vconfig, &builtin_viewlist, &view));
5457225361Sdougb		CHECK(configure_view(view, config, vconfig,
5458224092Sdougb				     &cachelist, bindkeys,
5459225361Sdougb				     ns_g_mctx, ns_g_aclconfctx, ISC_FALSE));
5460135446Strhodes		dns_view_freeze(view);
5461135446Strhodes		dns_view_detach(&view);
5462135446Strhodes		view = NULL;
5463135446Strhodes	}
5464135446Strhodes
5465224092Sdougb	/* Now combine the two viewlists into one */
5466224092Sdougb	ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link);
5467224092Sdougb
5468224092Sdougb	/* Swap our new view list with the production one. */
5469135446Strhodes	tmpviewlist = server->viewlist;
5470135446Strhodes	server->viewlist = viewlist;
5471135446Strhodes	viewlist = tmpviewlist;
5472135446Strhodes
5473224092Sdougb	/* Make the view list available to each of the views */
5474224092Sdougb	view = ISC_LIST_HEAD(server->viewlist);
5475224092Sdougb	while (view != NULL) {
5476224092Sdougb		view->viewlist = &server->viewlist;
5477224092Sdougb		view = ISC_LIST_NEXT(view, link);
5478224092Sdougb	}
5479224092Sdougb
5480224092Sdougb	/* Swap our new cache list with the production one. */
5481224092Sdougb	tmpcachelist = server->cachelist;
5482224092Sdougb	server->cachelist = cachelist;
5483224092Sdougb	cachelist = tmpcachelist;
5484224092Sdougb
5485224092Sdougb	/* Load the TKEY information from the configuration. */
5486135446Strhodes	if (options != NULL) {
5487135446Strhodes		dns_tkeyctx_t *t = NULL;
5488135446Strhodes		CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy,
5489135446Strhodes					     &t),
5490135446Strhodes		       "configuring TKEY");
5491135446Strhodes		if (server->tkeyctx != NULL)
5492135446Strhodes			dns_tkeyctx_destroy(&server->tkeyctx);
5493135446Strhodes		server->tkeyctx = t;
5494135446Strhodes	}
5495135446Strhodes
5496135446Strhodes	/*
5497135446Strhodes	 * Bind the control port(s).
5498135446Strhodes	 */
5499135446Strhodes	CHECKM(ns_controls_configure(ns_g_server->controls, config,
5500225361Sdougb				     ns_g_aclconfctx),
5501135446Strhodes	       "binding control channel(s)");
5502135446Strhodes
5503135446Strhodes	/*
5504135446Strhodes	 * Bind the lwresd port(s).
5505135446Strhodes	 */
5506135446Strhodes	CHECKM(ns_lwresd_configure(ns_g_mctx, config),
5507135446Strhodes	       "binding lightweight resolver ports");
5508135446Strhodes
5509135446Strhodes	/*
5510135446Strhodes	 * Open the source of entropy.
5511135446Strhodes	 */
5512135446Strhodes	if (first_time) {
5513135446Strhodes		obj = NULL;
5514135446Strhodes		result = ns_config_get(maps, "random-device", &obj);
5515135446Strhodes		if (result != ISC_R_SUCCESS) {
5516135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5517135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5518135446Strhodes				      "no source of entropy found");
5519135446Strhodes		} else {
5520135446Strhodes			const char *randomdev = cfg_obj_asstring(obj);
5521135446Strhodes			result = isc_entropy_createfilesource(ns_g_entropy,
5522135446Strhodes							      randomdev);
5523135446Strhodes			if (result != ISC_R_SUCCESS)
5524135446Strhodes				isc_log_write(ns_g_lctx,
5525135446Strhodes					      NS_LOGCATEGORY_GENERAL,
5526135446Strhodes					      NS_LOGMODULE_SERVER,
5527135446Strhodes					      ISC_LOG_INFO,
5528135446Strhodes					      "could not open entropy source "
5529135446Strhodes					      "%s: %s",
5530135446Strhodes					      randomdev,
5531135446Strhodes					      isc_result_totext(result));
5532135446Strhodes#ifdef PATH_RANDOMDEV
5533135446Strhodes			if (ns_g_fallbackentropy != NULL) {
5534135446Strhodes				if (result != ISC_R_SUCCESS) {
5535135446Strhodes					isc_log_write(ns_g_lctx,
5536135446Strhodes						      NS_LOGCATEGORY_GENERAL,
5537135446Strhodes						      NS_LOGMODULE_SERVER,
5538135446Strhodes						      ISC_LOG_INFO,
5539135446Strhodes						      "using pre-chroot entropy source "
5540135446Strhodes						      "%s",
5541135446Strhodes						      PATH_RANDOMDEV);
5542135446Strhodes					isc_entropy_detach(&ns_g_entropy);
5543135446Strhodes					isc_entropy_attach(ns_g_fallbackentropy,
5544135446Strhodes							   &ns_g_entropy);
5545135446Strhodes				}
5546135446Strhodes				isc_entropy_detach(&ns_g_fallbackentropy);
5547135446Strhodes			}
5548135446Strhodes#endif
5549135446Strhodes		}
5550135446Strhodes	}
5551135446Strhodes
5552135446Strhodes	/*
5553135446Strhodes	 * Relinquish root privileges.
5554135446Strhodes	 */
5555135446Strhodes	if (first_time)
5556135446Strhodes		ns_os_changeuser();
5557135446Strhodes
5558135446Strhodes	/*
5559186462Sdougb	 * Check that the working directory is writable.
5560186462Sdougb	 */
5561186462Sdougb	if (access(".", W_OK) != 0) {
5562186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5563186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
5564186462Sdougb			      "the working directory is not writable");
5565186462Sdougb	}
5566186462Sdougb
5567186462Sdougb	/*
5568135446Strhodes	 * Configure the logging system.
5569135446Strhodes	 *
5570135446Strhodes	 * Do this after changing UID to make sure that any log
5571135446Strhodes	 * files specified in named.conf get created by the
5572135446Strhodes	 * unprivileged user, not root.
5573135446Strhodes	 */
5574135446Strhodes	if (ns_g_logstderr) {
5575262706Serwin		const cfg_obj_t *logobj = NULL;
5576262706Serwin
5577135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5578135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5579262706Serwin			      "not using config file logging "
5580262706Serwin			      "statement for logging due to "
5581262706Serwin			      "-g option");
5582262706Serwin
5583262706Serwin		(void)cfg_map_get(config, "logging", &logobj);
5584262706Serwin		if (logobj != NULL) {
5585262706Serwin			result = ns_log_configure(NULL, logobj);
5586262706Serwin			if (result != ISC_R_SUCCESS) {
5587262706Serwin				isc_log_write(ns_g_lctx,
5588262706Serwin					      NS_LOGCATEGORY_GENERAL,
5589262706Serwin					      NS_LOGMODULE_SERVER,
5590262706Serwin					      ISC_LOG_ERROR,
5591262706Serwin					      "checking logging configuration "
5592262706Serwin					      "failed: %s",
5593262706Serwin					      isc_result_totext(result));
5594262706Serwin				goto cleanup;
5595262706Serwin			}
5596262706Serwin		}
5597135446Strhodes	} else {
5598165071Sdougb		const cfg_obj_t *logobj = NULL;
5599135446Strhodes
5600135446Strhodes		CHECKM(isc_logconfig_create(ns_g_lctx, &logc),
5601135446Strhodes		       "creating new logging configuration");
5602135446Strhodes
5603135446Strhodes		logobj = NULL;
5604135446Strhodes		(void)cfg_map_get(config, "logging", &logobj);
5605135446Strhodes		if (logobj != NULL) {
5606135446Strhodes			CHECKM(ns_log_configure(logc, logobj),
5607135446Strhodes			       "configuring logging");
5608135446Strhodes		} else {
5609135446Strhodes			CHECKM(ns_log_setdefaultchannels(logc),
5610135446Strhodes			       "setting up default logging channels");
5611135446Strhodes			CHECKM(ns_log_setunmatchedcategory(logc),
5612135446Strhodes			       "setting up default 'category unmatched'");
5613135446Strhodes			CHECKM(ns_log_setdefaultcategory(logc),
5614135446Strhodes			       "setting up default 'category default'");
5615135446Strhodes		}
5616135446Strhodes
5617262706Serwin		CHECKM(isc_logconfig_use(ns_g_lctx, logc),
5618262706Serwin		       "installing logging configuration");
5619262706Serwin		logc = NULL;
5620135446Strhodes
5621135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5622135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
5623135446Strhodes			      "now using logging configuration from "
5624135446Strhodes			      "config file");
5625135446Strhodes	}
5626135446Strhodes
5627135446Strhodes	/*
5628135446Strhodes	 * Set the default value of the query logging flag depending
5629135446Strhodes	 * whether a "queries" category has been defined.  This is
5630135446Strhodes	 * a disgusting hack, but we need to do this for BIND 8
5631135446Strhodes	 * compatibility.
5632135446Strhodes	 */
5633135446Strhodes	if (first_time) {
5634165071Sdougb		const cfg_obj_t *logobj = NULL;
5635165071Sdougb		const cfg_obj_t *categories = NULL;
5636135446Strhodes
5637135446Strhodes		obj = NULL;
5638135446Strhodes		if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) {
5639135446Strhodes			server->log_queries = cfg_obj_asboolean(obj);
5640135446Strhodes		} else {
5641135446Strhodes
5642135446Strhodes			(void)cfg_map_get(config, "logging", &logobj);
5643135446Strhodes			if (logobj != NULL)
5644135446Strhodes				(void)cfg_map_get(logobj, "category",
5645135446Strhodes						  &categories);
5646135446Strhodes			if (categories != NULL) {
5647165071Sdougb				const cfg_listelt_t *element;
5648135446Strhodes				for (element = cfg_list_first(categories);
5649135446Strhodes				     element != NULL;
5650135446Strhodes				     element = cfg_list_next(element))
5651135446Strhodes				{
5652165071Sdougb					const cfg_obj_t *catobj;
5653165071Sdougb					const char *str;
5654135446Strhodes
5655135446Strhodes					obj = cfg_listelt_value(element);
5656135446Strhodes					catobj = cfg_tuple_get(obj, "name");
5657135446Strhodes					str = cfg_obj_asstring(catobj);
5658135446Strhodes					if (strcasecmp(str, "queries") == 0)
5659135446Strhodes						server->log_queries = ISC_TRUE;
5660135446Strhodes				}
5661135446Strhodes			}
5662135446Strhodes		}
5663135446Strhodes	}
5664135446Strhodes
5665186462Sdougb
5666135446Strhodes	obj = NULL;
5667135446Strhodes	if (options != NULL &&
5668193149Sdougb	    cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS)
5669193149Sdougb		ns_g_memstatistics = cfg_obj_asboolean(obj);
5670193149Sdougb	else
5671193149Sdougb		ns_g_memstatistics =
5672193149Sdougb			ISC_TF((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0);
5673193149Sdougb
5674193149Sdougb	obj = NULL;
5675193149Sdougb	if (ns_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS)
5676135446Strhodes		ns_main_setmemstats(cfg_obj_asstring(obj));
5677193149Sdougb	else if (ns_g_memstatistics)
5678193149Sdougb		ns_main_setmemstats("named.memstats");
5679135446Strhodes	else
5680135446Strhodes		ns_main_setmemstats(NULL);
5681135446Strhodes
5682135446Strhodes	obj = NULL;
5683135446Strhodes	result = ns_config_get(maps, "statistics-file", &obj);
5684135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5685135446Strhodes	CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)),
5686135446Strhodes	       "strdup");
5687135446Strhodes
5688135446Strhodes	obj = NULL;
5689135446Strhodes	result = ns_config_get(maps, "dump-file", &obj);
5690135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5691135446Strhodes	CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)),
5692135446Strhodes	       "strdup");
5693135446Strhodes
5694135446Strhodes	obj = NULL;
5695224092Sdougb	result = ns_config_get(maps, "secroots-file", &obj);
5696224092Sdougb	INSIST(result == ISC_R_SUCCESS);
5697224092Sdougb	CHECKM(setstring(server, &server->secrootsfile, cfg_obj_asstring(obj)),
5698224092Sdougb	       "strdup");
5699224092Sdougb
5700224092Sdougb	obj = NULL;
5701135446Strhodes	result = ns_config_get(maps, "recursing-file", &obj);
5702135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5703135446Strhodes	CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)),
5704135446Strhodes	       "strdup");
5705135446Strhodes
5706135446Strhodes	obj = NULL;
5707135446Strhodes	result = ns_config_get(maps, "version", &obj);
5708135446Strhodes	if (result == ISC_R_SUCCESS) {
5709135446Strhodes		CHECKM(setoptstring(server, &server->version, obj), "strdup");
5710135446Strhodes		server->version_set = ISC_TRUE;
5711135446Strhodes	} else {
5712135446Strhodes		server->version_set = ISC_FALSE;
5713135446Strhodes	}
5714135446Strhodes
5715135446Strhodes	obj = NULL;
5716135446Strhodes	result = ns_config_get(maps, "hostname", &obj);
5717135446Strhodes	if (result == ISC_R_SUCCESS) {
5718135446Strhodes		CHECKM(setoptstring(server, &server->hostname, obj), "strdup");
5719135446Strhodes		server->hostname_set = ISC_TRUE;
5720135446Strhodes	} else {
5721135446Strhodes		server->hostname_set = ISC_FALSE;
5722135446Strhodes	}
5723135446Strhodes
5724135446Strhodes	obj = NULL;
5725135446Strhodes	result = ns_config_get(maps, "server-id", &obj);
5726135446Strhodes	server->server_usehostname = ISC_FALSE;
5727135446Strhodes	if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
5728193149Sdougb		/* The parser translates "hostname" to ISC_TRUE */
5729193149Sdougb		server->server_usehostname = cfg_obj_asboolean(obj);
5730193149Sdougb		result = setstring(server, &server->server_id, NULL);
5731193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5732135446Strhodes	} else if (result == ISC_R_SUCCESS) {
5733193149Sdougb		/* Found a quoted string */
5734135446Strhodes		CHECKM(setoptstring(server, &server->server_id, obj), "strdup");
5735135446Strhodes	} else {
5736170222Sdougb		result = setstring(server, &server->server_id, NULL);
5737135446Strhodes		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5738135446Strhodes	}
5739135446Strhodes
5740135446Strhodes	obj = NULL;
5741135446Strhodes	result = ns_config_get(maps, "flush-zones-on-shutdown", &obj);
5742135446Strhodes	if (result == ISC_R_SUCCESS) {
5743135446Strhodes		server->flushonshutdown = cfg_obj_asboolean(obj);
5744135446Strhodes	} else {
5745135446Strhodes		server->flushonshutdown = ISC_FALSE;
5746135446Strhodes	}
5747135446Strhodes
5748135446Strhodes	result = ISC_R_SUCCESS;
5749135446Strhodes
5750135446Strhodes cleanup:
5751262706Serwin	if (logc != NULL)
5752262706Serwin		isc_logconfig_destroy(&logc);
5753262706Serwin
5754186462Sdougb	if (v4portset != NULL)
5755186462Sdougb		isc_portset_destroy(ns_g_mctx, &v4portset);
5756186462Sdougb
5757186462Sdougb	if (v6portset != NULL)
5758186462Sdougb		isc_portset_destroy(ns_g_mctx, &v6portset);
5759186462Sdougb
5760224092Sdougb	if (conf_parser != NULL) {
5761135446Strhodes		if (config != NULL)
5762224092Sdougb			cfg_obj_destroy(conf_parser, &config);
5763224092Sdougb		cfg_parser_destroy(&conf_parser);
5764135446Strhodes	}
5765135446Strhodes
5766224092Sdougb	if (bindkeys_parser != NULL) {
5767224092Sdougb		if (bindkeys  != NULL)
5768224092Sdougb			cfg_obj_destroy(bindkeys_parser, &bindkeys);
5769224092Sdougb		cfg_parser_destroy(&bindkeys_parser);
5770224092Sdougb	}
5771224092Sdougb
5772135446Strhodes	if (view != NULL)
5773135446Strhodes		dns_view_detach(&view);
5774135446Strhodes
5775135446Strhodes	/*
5776135446Strhodes	 * This cleans up either the old production view list
5777135446Strhodes	 * or our temporary list depending on whether they
5778135446Strhodes	 * were swapped above or not.
5779135446Strhodes	 */
5780135446Strhodes	for (view = ISC_LIST_HEAD(viewlist);
5781135446Strhodes	     view != NULL;
5782135446Strhodes	     view = view_next) {
5783135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
5784135446Strhodes		ISC_LIST_UNLINK(viewlist, view, link);
5785170222Sdougb		if (result == ISC_R_SUCCESS &&
5786170222Sdougb		    strcmp(view->name, "_bind") != 0)
5787170222Sdougb			(void)dns_zt_apply(view->zonetable, ISC_FALSE,
5788170222Sdougb					   removed, view);
5789135446Strhodes		dns_view_detach(&view);
5790135446Strhodes	}
5791135446Strhodes
5792224092Sdougb	/* Same cleanup for cache list. */
5793224092Sdougb	while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) {
5794224092Sdougb		ISC_LIST_UNLINK(cachelist, nsc, link);
5795224092Sdougb		dns_cache_detach(&nsc->cache);
5796224092Sdougb		isc_mem_put(server->mctx, nsc, sizeof(*nsc));
5797224092Sdougb	}
5798224092Sdougb
5799135446Strhodes	/*
5800135446Strhodes	 * Adjust the listening interfaces in accordance with the source
5801135446Strhodes	 * addresses specified in views and zones.
5802135446Strhodes	 */
5803135446Strhodes	if (isc_net_probeipv6() == ISC_R_SUCCESS)
5804135446Strhodes		adjust_interfaces(server, ns_g_mctx);
5805135446Strhodes
5806135446Strhodes	/* Relinquish exclusive access to configuration data. */
5807234010Sdougb	if (exclusive)
5808234010Sdougb		isc_task_endexclusive(server->task);
5809135446Strhodes
5810135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5811135446Strhodes		      ISC_LOG_DEBUG(1), "load_configuration: %s",
5812135446Strhodes		      isc_result_totext(result));
5813135446Strhodes
5814135446Strhodes	return (result);
5815135446Strhodes}
5816135446Strhodes
5817135446Strhodesstatic isc_result_t
5818254897Serwinview_loaded(void *arg) {
5819135446Strhodes	isc_result_t result;
5820254897Serwin	ns_zoneload_t *zl = (ns_zoneload_t *) arg;
5821254897Serwin	ns_server_t *server = zl->server;
5822254897Serwin	unsigned int refs;
5823254897Serwin
5824254897Serwin
5825254897Serwin	/*
5826254897Serwin	 * Force zone maintenance.  Do this after loading
5827254897Serwin	 * so that we know when we need to force AXFR of
5828254897Serwin	 * slave zones whose master files are missing.
5829254897Serwin	 *
5830254897Serwin	 * We use the zoneload reference counter to let us
5831254897Serwin	 * know when all views are finished.
5832254897Serwin	 */
5833254897Serwin	isc_refcount_decrement(&zl->refs, &refs);
5834254897Serwin	if (refs != 0)
5835254897Serwin		return (ISC_R_SUCCESS);
5836254897Serwin
5837254897Serwin	isc_refcount_destroy(&zl->refs);
5838254897Serwin	isc_mem_put(server->mctx, zl, sizeof (*zl));
5839254897Serwin
5840254897Serwin	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5841254897Serwin		      ISC_LOG_NOTICE, "all zones loaded");
5842254897Serwin	CHECKFATAL(dns_zonemgr_forcemaint(server->zonemgr),
5843254897Serwin		   "forcing zone maintenance");
5844254897Serwin
5845254897Serwin	ns_os_started();
5846254897Serwin	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5847254897Serwin		      ISC_LOG_NOTICE, "running");
5848254897Serwin
5849254897Serwin	return (ISC_R_SUCCESS);
5850254897Serwin}
5851254897Serwin
5852254897Serwinstatic isc_result_t
5853262706Serwinload_zones(ns_server_t *server, isc_boolean_t init) {
5854254897Serwin	isc_result_t result;
5855135446Strhodes	dns_view_t *view;
5856254897Serwin	ns_zoneload_t *zl;
5857254897Serwin	unsigned int refs = 0;
5858135446Strhodes
5859254897Serwin	zl = isc_mem_get(server->mctx, sizeof (*zl));
5860254897Serwin	if (zl == NULL)
5861254897Serwin		return (ISC_R_NOMEMORY);
5862254897Serwin	zl->server = server;
5863254897Serwin
5864135446Strhodes	result = isc_task_beginexclusive(server->task);
5865135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5866135446Strhodes
5867254897Serwin	isc_refcount_init(&zl->refs, 1);
5868254897Serwin
5869135446Strhodes	/*
5870254897Serwin	 * Schedule zones to be loaded from disk.
5871135446Strhodes	 */
5872135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5873135446Strhodes	     view != NULL;
5874135446Strhodes	     view = ISC_LIST_NEXT(view, link))
5875135446Strhodes	{
5876254897Serwin		if (view->managed_keys != NULL) {
5877254897Serwin			result = dns_zone_load(view->managed_keys);
5878254897Serwin			if (result != ISC_R_SUCCESS &&
5879254897Serwin			    result != DNS_R_UPTODATE &&
5880254897Serwin			    result != DNS_R_CONTINUE)
5881254897Serwin				goto cleanup;
5882254897Serwin		}
5883254897Serwin		if (view->redirect != NULL) {
5884254897Serwin			result = dns_zone_load(view->redirect);
5885254897Serwin			if (result != ISC_R_SUCCESS &&
5886254897Serwin			    result != DNS_R_UPTODATE &&
5887254897Serwin			    result != DNS_R_CONTINUE)
5888254897Serwin				goto cleanup;
5889254897Serwin		}
5890254897Serwin
5891254897Serwin		/*
5892254897Serwin		 * 'dns_view_asyncload' calls view_loaded if there are no
5893254897Serwin		 * zones.
5894254897Serwin		 */
5895254897Serwin		isc_refcount_increment(&zl->refs, NULL);
5896254897Serwin		CHECK(dns_view_asyncload(view, view_loaded, zl));
5897135446Strhodes	}
5898135446Strhodes
5899135446Strhodes cleanup:
5900254897Serwin	isc_refcount_decrement(&zl->refs, &refs);
5901254897Serwin	if (refs == 0) {
5902254897Serwin		isc_refcount_destroy(&zl->refs);
5903254897Serwin		isc_mem_put(server->mctx, zl, sizeof (*zl));
5904262706Serwin	} else if (init) {
5905254897Serwin		/*
5906254897Serwin		 * Place the task manager into privileged mode.  This
5907254897Serwin		 * ensures that after we leave task-exclusive mode, no
5908254897Serwin		 * other tasks will be able to run except for the ones
5909262706Serwin		 * that are loading zones. (This should only be done during
5910262706Serwin		 * the initial server setup; it isn't necessary during
5911262706Serwin		 * a reload.)
5912254897Serwin		 */
5913254897Serwin		isc_taskmgr_setmode(ns_g_taskmgr, isc_taskmgrmode_privileged);
5914254897Serwin	}
5915254897Serwin
5916186462Sdougb	isc_task_endexclusive(server->task);
5917135446Strhodes	return (result);
5918135446Strhodes}
5919135446Strhodes
5920135446Strhodesstatic isc_result_t
5921135446Strhodesload_new_zones(ns_server_t *server, isc_boolean_t stop) {
5922135446Strhodes	isc_result_t result;
5923135446Strhodes	dns_view_t *view;
5924135446Strhodes
5925135446Strhodes	result = isc_task_beginexclusive(server->task);
5926135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5927135446Strhodes
5928135446Strhodes	/*
5929135446Strhodes	 * Load zone data from disk.
5930135446Strhodes	 */
5931135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5932135446Strhodes	     view != NULL;
5933135446Strhodes	     view = ISC_LIST_NEXT(view, link))
5934135446Strhodes	{
5935135446Strhodes		CHECK(dns_view_loadnew(view, stop));
5936224092Sdougb
5937224092Sdougb		/* Load managed-keys data */
5938224092Sdougb		if (view->managed_keys != NULL)
5939224092Sdougb			CHECK(dns_zone_loadnew(view->managed_keys));
5940254897Serwin		if (view->redirect != NULL)
5941254897Serwin			CHECK(dns_zone_loadnew(view->redirect));
5942135446Strhodes	}
5943224092Sdougb
5944135446Strhodes	/*
5945224092Sdougb	 * Resume zone XFRs.
5946135446Strhodes	 */
5947135446Strhodes	dns_zonemgr_resumexfrs(server->zonemgr);
5948135446Strhodes cleanup:
5949186462Sdougb	isc_task_endexclusive(server->task);
5950135446Strhodes	return (result);
5951135446Strhodes}
5952135446Strhodes
5953135446Strhodesstatic void
5954135446Strhodesrun_server(isc_task_t *task, isc_event_t *event) {
5955135446Strhodes	isc_result_t result;
5956135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
5957135446Strhodes
5958143731Sdougb	INSIST(task == server->task);
5959135446Strhodes
5960135446Strhodes	isc_event_free(&event);
5961135446Strhodes
5962135446Strhodes	CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy,
5963135446Strhodes					  &ns_g_dispatchmgr),
5964135446Strhodes		   "creating dispatch manager");
5965135446Strhodes
5966193149Sdougb	dns_dispatchmgr_setstats(ns_g_dispatchmgr, server->resolverstats);
5967193149Sdougb
5968135446Strhodes	CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr,
5969135446Strhodes					  ns_g_socketmgr, ns_g_dispatchmgr,
5970135446Strhodes					  &server->interfacemgr),
5971135446Strhodes		   "creating interface manager");
5972135446Strhodes
5973135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5974135446Strhodes				    NULL, NULL, server->task,
5975135446Strhodes				    interface_timer_tick,
5976135446Strhodes				    server, &server->interface_timer),
5977135446Strhodes		   "creating interface timer");
5978135446Strhodes
5979135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5980135446Strhodes				    NULL, NULL, server->task,
5981135446Strhodes				    heartbeat_timer_tick,
5982135446Strhodes				    server, &server->heartbeat_timer),
5983135446Strhodes		   "creating heartbeat timer");
5984135446Strhodes
5985170222Sdougb	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5986170222Sdougb				    NULL, NULL, server->task, pps_timer_tick,
5987170222Sdougb				    server, &server->pps_timer),
5988170222Sdougb		   "creating pps timer");
5989170222Sdougb
5990135446Strhodes	CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser),
5991135446Strhodes		   "creating default configuration parser");
5992135446Strhodes
5993135446Strhodes	if (ns_g_lwresdonly)
5994135446Strhodes		CHECKFATAL(load_configuration(lwresd_g_conffile, server,
5995135446Strhodes					      ISC_TRUE),
5996135446Strhodes			   "loading configuration");
5997135446Strhodes	else
5998135446Strhodes		CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE),
5999135446Strhodes			   "loading configuration");
6000135446Strhodes
6001135446Strhodes	isc_hash_init();
6002135446Strhodes
6003262706Serwin	CHECKFATAL(load_zones(server, ISC_TRUE), "loading zones");
6004135446Strhodes}
6005135446Strhodes
6006186462Sdougbvoid
6007135446Strhodesns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) {
6008135446Strhodes
6009135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6010135446Strhodes
6011135446Strhodes	server->flushonshutdown = flush;
6012135446Strhodes}
6013135446Strhodes
6014135446Strhodesstatic void
6015135446Strhodesshutdown_server(isc_task_t *task, isc_event_t *event) {
6016135446Strhodes	isc_result_t result;
6017135446Strhodes	dns_view_t *view, *view_next;
6018135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
6019135446Strhodes	isc_boolean_t flush = server->flushonshutdown;
6020224092Sdougb	ns_cache_t *nsc;
6021135446Strhodes
6022135446Strhodes	UNUSED(task);
6023135446Strhodes	INSIST(task == server->task);
6024135446Strhodes
6025135446Strhodes	result = isc_task_beginexclusive(server->task);
6026135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6027135446Strhodes
6028135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
6029135446Strhodes		      ISC_LOG_INFO, "shutting down%s",
6030135446Strhodes		      flush ? ": flushing changes" : "");
6031135446Strhodes
6032193149Sdougb	ns_statschannels_shutdown(server);
6033135446Strhodes	ns_controls_shutdown(server->controls);
6034135446Strhodes	end_reserved_dispatches(server, ISC_TRUE);
6035224092Sdougb	cleanup_session_key(server, server->mctx);
6036135446Strhodes
6037225361Sdougb	if (ns_g_aclconfctx != NULL)
6038225361Sdougb		cfg_aclconfctx_detach(&ns_g_aclconfctx);
6039225361Sdougb
6040135446Strhodes	cfg_obj_destroy(ns_g_parser, &ns_g_config);
6041135446Strhodes	cfg_parser_destroy(&ns_g_parser);
6042135446Strhodes
6043135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
6044135446Strhodes	     view != NULL;
6045135446Strhodes	     view = view_next) {
6046135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
6047135446Strhodes		ISC_LIST_UNLINK(server->viewlist, view, link);
6048135446Strhodes		if (flush)
6049135446Strhodes			dns_view_flushanddetach(&view);
6050135446Strhodes		else
6051135446Strhodes			dns_view_detach(&view);
6052135446Strhodes	}
6053135446Strhodes
6054224092Sdougb	while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) {
6055224092Sdougb		ISC_LIST_UNLINK(server->cachelist, nsc, link);
6056224092Sdougb		dns_cache_detach(&nsc->cache);
6057224092Sdougb		isc_mem_put(server->mctx, nsc, sizeof(*nsc));
6058224092Sdougb	}
6059224092Sdougb
6060135446Strhodes	isc_timer_detach(&server->interface_timer);
6061135446Strhodes	isc_timer_detach(&server->heartbeat_timer);
6062170222Sdougb	isc_timer_detach(&server->pps_timer);
6063135446Strhodes
6064135446Strhodes	ns_interfacemgr_shutdown(server->interfacemgr);
6065135446Strhodes	ns_interfacemgr_detach(&server->interfacemgr);
6066135446Strhodes
6067135446Strhodes	dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
6068135446Strhodes
6069135446Strhodes	dns_zonemgr_shutdown(server->zonemgr);
6070135446Strhodes
6071224092Sdougb	if (ns_g_sessionkey != NULL) {
6072224092Sdougb		dns_tsigkey_detach(&ns_g_sessionkey);
6073224092Sdougb		dns_name_free(&ns_g_sessionkeyname, server->mctx);
6074224092Sdougb	}
6075224092Sdougb
6076135446Strhodes	if (server->blackholeacl != NULL)
6077135446Strhodes		dns_acl_detach(&server->blackholeacl);
6078135446Strhodes
6079135446Strhodes	dns_db_detach(&server->in_roothints);
6080135446Strhodes
6081135446Strhodes	isc_task_endexclusive(server->task);
6082135446Strhodes
6083135446Strhodes	isc_task_detach(&server->task);
6084135446Strhodes
6085135446Strhodes	isc_event_free(&event);
6086135446Strhodes}
6087135446Strhodes
6088135446Strhodesvoid
6089135446Strhodesns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
6090135446Strhodes	isc_result_t result;
6091225361Sdougb	ns_server_t *server = isc_mem_get(mctx, sizeof(*server));
6092135446Strhodes
6093135446Strhodes	if (server == NULL)
6094135446Strhodes		fatal("allocating server object", ISC_R_NOMEMORY);
6095135446Strhodes
6096135446Strhodes	server->mctx = mctx;
6097135446Strhodes	server->task = NULL;
6098135446Strhodes
6099135446Strhodes	/* Initialize configuration data with default values. */
6100135446Strhodes
6101135446Strhodes	result = isc_quota_init(&server->xfroutquota, 10);
6102135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6103135446Strhodes	result = isc_quota_init(&server->tcpquota, 10);
6104135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6105135446Strhodes	result = isc_quota_init(&server->recursionquota, 100);
6106135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6107135446Strhodes
6108135446Strhodes	result = dns_aclenv_init(mctx, &server->aclenv);
6109135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6110135446Strhodes
6111135446Strhodes	/* Initialize server data structures. */
6112135446Strhodes	server->zonemgr = NULL;
6113135446Strhodes	server->interfacemgr = NULL;
6114135446Strhodes	ISC_LIST_INIT(server->viewlist);
6115135446Strhodes	server->in_roothints = NULL;
6116135446Strhodes	server->blackholeacl = NULL;
6117135446Strhodes
6118135446Strhodes	CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
6119135446Strhodes				     &server->in_roothints),
6120135446Strhodes		   "setting up root hints");
6121135446Strhodes
6122135446Strhodes	CHECKFATAL(isc_mutex_init(&server->reload_event_lock),
6123135446Strhodes		   "initializing reload event lock");
6124135446Strhodes	server->reload_event =
6125135446Strhodes		isc_event_allocate(ns_g_mctx, server,
6126135446Strhodes				   NS_EVENT_RELOAD,
6127135446Strhodes				   ns_server_reload,
6128135446Strhodes				   server,
6129135446Strhodes				   sizeof(isc_event_t));
6130135446Strhodes	CHECKFATAL(server->reload_event == NULL ?
6131135446Strhodes		   ISC_R_NOMEMORY : ISC_R_SUCCESS,
6132135446Strhodes		   "allocating reload event");
6133135446Strhodes
6134224092Sdougb	CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy,
6135224092Sdougb				 ns_g_engine, ISC_ENTROPY_GOODONLY),
6136135446Strhodes		   "initializing DST");
6137135446Strhodes
6138135446Strhodes	server->tkeyctx = NULL;
6139135446Strhodes	CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
6140135446Strhodes				      &server->tkeyctx),
6141135446Strhodes		   "creating TKEY context");
6142135446Strhodes
6143135446Strhodes	/*
6144135446Strhodes	 * Setup the server task, which is responsible for coordinating
6145245163Serwin	 * startup and shutdown of the server, as well as all exclusive
6146245163Serwin	 * tasks.
6147135446Strhodes	 */
6148135446Strhodes	CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
6149135446Strhodes		   "creating server task");
6150135446Strhodes	isc_task_setname(server->task, "server", server);
6151245163Serwin	isc_taskmgr_setexcltask(ns_g_taskmgr, server->task);
6152135446Strhodes	CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
6153135446Strhodes		   "isc_task_onshutdown");
6154135446Strhodes	CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),
6155135446Strhodes		   "isc_app_onrun");
6156135446Strhodes
6157135446Strhodes	server->interface_timer = NULL;
6158135446Strhodes	server->heartbeat_timer = NULL;
6159170222Sdougb	server->pps_timer = NULL;
6160186462Sdougb
6161135446Strhodes	server->interface_interval = 0;
6162135446Strhodes	server->heartbeat_interval = 0;
6163135446Strhodes
6164135446Strhodes	CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr,
6165135446Strhodes				      ns_g_socketmgr, &server->zonemgr),
6166135446Strhodes		   "dns_zonemgr_create");
6167225361Sdougb	CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000),
6168225361Sdougb		   "dns_zonemgr_setsize");
6169135446Strhodes
6170135446Strhodes	server->statsfile = isc_mem_strdup(server->mctx, "named.stats");
6171135446Strhodes	CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
6172135446Strhodes		   "isc_mem_strdup");
6173193149Sdougb	server->nsstats = NULL;
6174193149Sdougb	server->rcvquerystats = NULL;
6175193149Sdougb	server->opcodestats = NULL;
6176193149Sdougb	server->zonestats = NULL;
6177193149Sdougb	server->resolverstats = NULL;
6178193149Sdougb	server->sockstats = NULL;
6179193149Sdougb	CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats,
6180193149Sdougb				    isc_sockstatscounter_max),
6181193149Sdougb		   "isc_stats_create");
6182193149Sdougb	isc_socketmgr_setstats(ns_g_socketmgr, server->sockstats);
6183135446Strhodes
6184224092Sdougb	server->bindkeysfile = isc_mem_strdup(server->mctx, "bind.keys");
6185224092Sdougb	CHECKFATAL(server->bindkeysfile == NULL ? ISC_R_NOMEMORY :
6186224092Sdougb						  ISC_R_SUCCESS,
6187224092Sdougb		   "isc_mem_strdup");
6188224092Sdougb
6189135446Strhodes	server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db");
6190135446Strhodes	CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
6191135446Strhodes		   "isc_mem_strdup");
6192135446Strhodes
6193224092Sdougb	server->secrootsfile = isc_mem_strdup(server->mctx, "named.secroots");
6194224092Sdougb	CHECKFATAL(server->secrootsfile == NULL ? ISC_R_NOMEMORY :
6195224092Sdougb						  ISC_R_SUCCESS,
6196224092Sdougb		   "isc_mem_strdup");
6197224092Sdougb
6198135446Strhodes	server->recfile = isc_mem_strdup(server->mctx, "named.recursing");
6199135446Strhodes	CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
6200135446Strhodes		   "isc_mem_strdup");
6201135446Strhodes
6202135446Strhodes	server->hostname_set = ISC_FALSE;
6203135446Strhodes	server->hostname = NULL;
6204186462Sdougb	server->version_set = ISC_FALSE;
6205135446Strhodes	server->version = NULL;
6206135446Strhodes	server->server_usehostname = ISC_FALSE;
6207135446Strhodes	server->server_id = NULL;
6208135446Strhodes
6209193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->nsstats,
6210193149Sdougb				    dns_nsstatscounter_max),
6211193149Sdougb		   "dns_stats_create (server)");
6212135446Strhodes
6213193149Sdougb	CHECKFATAL(dns_rdatatypestats_create(ns_g_mctx,
6214193149Sdougb					     &server->rcvquerystats),
6215193149Sdougb		   "dns_stats_create (rcvquery)");
6216193149Sdougb
6217193149Sdougb	CHECKFATAL(dns_opcodestats_create(ns_g_mctx, &server->opcodestats),
6218193149Sdougb		   "dns_stats_create (opcode)");
6219193149Sdougb
6220193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->zonestats,
6221193149Sdougb				    dns_zonestatscounter_max),
6222193149Sdougb		   "dns_stats_create (zone)");
6223193149Sdougb
6224193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->resolverstats,
6225193149Sdougb				    dns_resstatscounter_max),
6226193149Sdougb		   "dns_stats_create (resolver)");
6227193149Sdougb
6228135446Strhodes	server->flushonshutdown = ISC_FALSE;
6229135446Strhodes	server->log_queries = ISC_FALSE;
6230135446Strhodes
6231135446Strhodes	server->controls = NULL;
6232135446Strhodes	CHECKFATAL(ns_controls_create(server, &server->controls),
6233135446Strhodes		   "ns_controls_create");
6234135446Strhodes	server->dispatchgen = 0;
6235135446Strhodes	ISC_LIST_INIT(server->dispatches);
6236135446Strhodes
6237193149Sdougb	ISC_LIST_INIT(server->statschannels);
6238193149Sdougb
6239224092Sdougb	ISC_LIST_INIT(server->cachelist);
6240224092Sdougb
6241224092Sdougb	server->sessionkey = NULL;
6242224092Sdougb	server->session_keyfile = NULL;
6243224092Sdougb	server->session_keyname = NULL;
6244224092Sdougb	server->session_keyalg = DST_ALG_UNKNOWN;
6245224092Sdougb	server->session_keybits = 0;
6246224092Sdougb
6247135446Strhodes	server->magic = NS_SERVER_MAGIC;
6248135446Strhodes	*serverp = server;
6249135446Strhodes}
6250135446Strhodes
6251135446Strhodesvoid
6252135446Strhodesns_server_destroy(ns_server_t **serverp) {
6253135446Strhodes	ns_server_t *server = *serverp;
6254135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6255135446Strhodes
6256135446Strhodes	ns_controls_destroy(&server->controls);
6257135446Strhodes
6258193149Sdougb	isc_stats_detach(&server->nsstats);
6259193149Sdougb	dns_stats_detach(&server->rcvquerystats);
6260193149Sdougb	dns_stats_detach(&server->opcodestats);
6261193149Sdougb	isc_stats_detach(&server->zonestats);
6262193149Sdougb	isc_stats_detach(&server->resolverstats);
6263193149Sdougb	isc_stats_detach(&server->sockstats);
6264135446Strhodes
6265135446Strhodes	isc_mem_free(server->mctx, server->statsfile);
6266224092Sdougb	isc_mem_free(server->mctx, server->bindkeysfile);
6267135446Strhodes	isc_mem_free(server->mctx, server->dumpfile);
6268224092Sdougb	isc_mem_free(server->mctx, server->secrootsfile);
6269135446Strhodes	isc_mem_free(server->mctx, server->recfile);
6270135446Strhodes
6271135446Strhodes	if (server->version != NULL)
6272135446Strhodes		isc_mem_free(server->mctx, server->version);
6273135446Strhodes	if (server->hostname != NULL)
6274135446Strhodes		isc_mem_free(server->mctx, server->hostname);
6275135446Strhodes	if (server->server_id != NULL)
6276135446Strhodes		isc_mem_free(server->mctx, server->server_id);
6277135446Strhodes
6278225361Sdougb	if (server->zonemgr != NULL)
6279225361Sdougb		dns_zonemgr_detach(&server->zonemgr);
6280135446Strhodes
6281135446Strhodes	if (server->tkeyctx != NULL)
6282135446Strhodes		dns_tkeyctx_destroy(&server->tkeyctx);
6283135446Strhodes
6284135446Strhodes	dst_lib_destroy();
6285135446Strhodes
6286135446Strhodes	isc_event_free(&server->reload_event);
6287135446Strhodes
6288135446Strhodes	INSIST(ISC_LIST_EMPTY(server->viewlist));
6289224092Sdougb	INSIST(ISC_LIST_EMPTY(server->cachelist));
6290135446Strhodes
6291135446Strhodes	dns_aclenv_destroy(&server->aclenv);
6292135446Strhodes
6293135446Strhodes	isc_quota_destroy(&server->recursionquota);
6294135446Strhodes	isc_quota_destroy(&server->tcpquota);
6295135446Strhodes	isc_quota_destroy(&server->xfroutquota);
6296135446Strhodes
6297135446Strhodes	server->magic = 0;
6298135446Strhodes	isc_mem_put(server->mctx, server, sizeof(*server));
6299135446Strhodes	*serverp = NULL;
6300135446Strhodes}
6301135446Strhodes
6302135446Strhodesstatic void
6303135446Strhodesfatal(const char *msg, isc_result_t result) {
6304135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
6305135446Strhodes		      ISC_LOG_CRITICAL, "%s: %s", msg,
6306135446Strhodes		      isc_result_totext(result));
6307135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
6308135446Strhodes		      ISC_LOG_CRITICAL, "exiting (due to fatal error)");
6309135446Strhodes	exit(1);
6310135446Strhodes}
6311135446Strhodes
6312135446Strhodesstatic void
6313135446Strhodesstart_reserved_dispatches(ns_server_t *server) {
6314135446Strhodes
6315135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6316135446Strhodes
6317135446Strhodes	server->dispatchgen++;
6318135446Strhodes}
6319135446Strhodes
6320135446Strhodesstatic void
6321135446Strhodesend_reserved_dispatches(ns_server_t *server, isc_boolean_t all) {
6322135446Strhodes	ns_dispatch_t *dispatch, *nextdispatch;
6323135446Strhodes
6324135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6325135446Strhodes
6326135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
6327135446Strhodes	     dispatch != NULL;
6328135446Strhodes	     dispatch = nextdispatch) {
6329135446Strhodes		nextdispatch = ISC_LIST_NEXT(dispatch, link);
6330135446Strhodes		if (!all && server->dispatchgen == dispatch-> dispatchgen)
6331135446Strhodes			continue;
6332135446Strhodes		ISC_LIST_UNLINK(server->dispatches, dispatch, link);
6333135446Strhodes		dns_dispatch_detach(&dispatch->dispatch);
6334135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
6335135446Strhodes	}
6336135446Strhodes}
6337135446Strhodes
6338135446Strhodesvoid
6339165071Sdougbns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) {
6340135446Strhodes	ns_dispatch_t *dispatch;
6341135446Strhodes	in_port_t port;
6342135446Strhodes	char addrbuf[ISC_SOCKADDR_FORMATSIZE];
6343135446Strhodes	isc_result_t result;
6344135446Strhodes	unsigned int attrs, attrmask;
6345135446Strhodes
6346135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6347135446Strhodes
6348135446Strhodes	port = isc_sockaddr_getport(addr);
6349135446Strhodes	if (port == 0 || port >= 1024)
6350135446Strhodes		return;
6351135446Strhodes
6352135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
6353135446Strhodes	     dispatch != NULL;
6354135446Strhodes	     dispatch = ISC_LIST_NEXT(dispatch, link)) {
6355135446Strhodes		if (isc_sockaddr_equal(&dispatch->addr, addr))
6356135446Strhodes			break;
6357135446Strhodes	}
6358135446Strhodes	if (dispatch != NULL) {
6359135446Strhodes		dispatch->dispatchgen = server->dispatchgen;
6360135446Strhodes		return;
6361135446Strhodes	}
6362135446Strhodes
6363135446Strhodes	dispatch = isc_mem_get(server->mctx, sizeof(*dispatch));
6364135446Strhodes	if (dispatch == NULL) {
6365135446Strhodes		result = ISC_R_NOMEMORY;
6366135446Strhodes		goto cleanup;
6367135446Strhodes	}
6368135446Strhodes
6369135446Strhodes	dispatch->addr = *addr;
6370135446Strhodes	dispatch->dispatchgen = server->dispatchgen;
6371135446Strhodes	dispatch->dispatch = NULL;
6372135446Strhodes
6373135446Strhodes	attrs = 0;
6374135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
6375135446Strhodes	switch (isc_sockaddr_pf(addr)) {
6376135446Strhodes	case AF_INET:
6377135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
6378135446Strhodes		break;
6379135446Strhodes	case AF_INET6:
6380135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
6381135446Strhodes		break;
6382135446Strhodes	default:
6383135446Strhodes		result = ISC_R_NOTIMPLEMENTED;
6384135446Strhodes		goto cleanup;
6385135446Strhodes	}
6386135446Strhodes	attrmask = 0;
6387135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP;
6388135446Strhodes	attrmask |= DNS_DISPATCHATTR_TCP;
6389135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4;
6390135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV6;
6391135446Strhodes
6392135446Strhodes	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
6393135446Strhodes				     ns_g_taskmgr, &dispatch->addr, 4096,
6394135446Strhodes				     1000, 32768, 16411, 16433,
6395186462Sdougb				     attrs, attrmask, &dispatch->dispatch);
6396135446Strhodes	if (result != ISC_R_SUCCESS)
6397135446Strhodes		goto cleanup;
6398135446Strhodes
6399135446Strhodes	ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link);
6400135446Strhodes
6401135446Strhodes	return;
6402135446Strhodes
6403135446Strhodes cleanup:
6404135446Strhodes	if (dispatch != NULL)
6405135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
6406135446Strhodes	isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
6407135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6408135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
6409135446Strhodes		      "unable to create dispatch for reserved port %s: %s",
6410135446Strhodes		      addrbuf, isc_result_totext(result));
6411135446Strhodes}
6412135446Strhodes
6413135446Strhodes
6414135446Strhodesstatic isc_result_t
6415135446Strhodesloadconfig(ns_server_t *server) {
6416135446Strhodes	isc_result_t result;
6417135446Strhodes	start_reserved_dispatches(server);
6418135446Strhodes	result = load_configuration(ns_g_lwresdonly ?
6419135446Strhodes				    lwresd_g_conffile : ns_g_conffile,
6420143731Sdougb				    server, ISC_FALSE);
6421193149Sdougb	if (result == ISC_R_SUCCESS) {
6422135446Strhodes		end_reserved_dispatches(server, ISC_FALSE);
6423135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6424193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6425193149Sdougb			      "reloading configuration succeeded");
6426193149Sdougb	} else {
6427193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6428135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6429135446Strhodes			      "reloading configuration failed: %s",
6430135446Strhodes			      isc_result_totext(result));
6431193149Sdougb	}
6432135446Strhodes	return (result);
6433135446Strhodes}
6434135446Strhodes
6435135446Strhodesstatic isc_result_t
6436135446Strhodesreload(ns_server_t *server) {
6437135446Strhodes	isc_result_t result;
6438135446Strhodes	CHECK(loadconfig(server));
6439135446Strhodes
6440262706Serwin	result = load_zones(server, ISC_FALSE);
6441193149Sdougb	if (result == ISC_R_SUCCESS)
6442135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6443193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6444193149Sdougb			      "reloading zones succeeded");
6445193149Sdougb	else
6446193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6447135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6448135446Strhodes			      "reloading zones failed: %s",
6449135446Strhodes			      isc_result_totext(result));
6450193149Sdougb
6451135446Strhodes cleanup:
6452135446Strhodes	return (result);
6453135446Strhodes}
6454135446Strhodes
6455135446Strhodesstatic void
6456135446Strhodesreconfig(ns_server_t *server) {
6457135446Strhodes	isc_result_t result;
6458135446Strhodes	CHECK(loadconfig(server));
6459135446Strhodes
6460135446Strhodes	result = load_new_zones(server, ISC_FALSE);
6461193149Sdougb	if (result == ISC_R_SUCCESS)
6462135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6463193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6464193149Sdougb			      "any newly configured zones are now loaded");
6465193149Sdougb	else
6466193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6467135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6468135446Strhodes			      "loading new zones failed: %s",
6469135446Strhodes			      isc_result_totext(result));
6470193149Sdougb
6471135446Strhodes cleanup: ;
6472135446Strhodes}
6473135446Strhodes
6474135446Strhodes/*
6475135446Strhodes * Handle a reload event (from SIGHUP).
6476135446Strhodes */
6477135446Strhodesstatic void
6478135446Strhodesns_server_reload(isc_task_t *task, isc_event_t *event) {
6479135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
6480135446Strhodes
6481135446Strhodes	INSIST(task = server->task);
6482135446Strhodes	UNUSED(task);
6483135446Strhodes
6484193149Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6485193149Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6486193149Sdougb		      "received SIGHUP signal to reload zones");
6487135446Strhodes	(void)reload(server);
6488135446Strhodes
6489135446Strhodes	LOCK(&server->reload_event_lock);
6490135446Strhodes	INSIST(server->reload_event == NULL);
6491135446Strhodes	server->reload_event = event;
6492135446Strhodes	UNLOCK(&server->reload_event_lock);
6493135446Strhodes}
6494135446Strhodes
6495135446Strhodesvoid
6496135446Strhodesns_server_reloadwanted(ns_server_t *server) {
6497135446Strhodes	LOCK(&server->reload_event_lock);
6498135446Strhodes	if (server->reload_event != NULL)
6499135446Strhodes		isc_task_send(server->task, &server->reload_event);
6500135446Strhodes	UNLOCK(&server->reload_event_lock);
6501135446Strhodes}
6502135446Strhodes
6503135446Strhodesstatic char *
6504135446Strhodesnext_token(char **stringp, const char *delim) {
6505135446Strhodes	char *res;
6506135446Strhodes
6507135446Strhodes	do {
6508135446Strhodes		res = strsep(stringp, delim);
6509135446Strhodes		if (res == NULL)
6510135446Strhodes			break;
6511135446Strhodes	} while (*res == '\0');
6512135446Strhodes	return (res);
6513186462Sdougb}
6514135446Strhodes
6515135446Strhodes/*
6516135446Strhodes * Find the zone specified in the control channel command 'args',
6517135446Strhodes * if any.  If a zone is specified, point '*zonep' at it, otherwise
6518135446Strhodes * set '*zonep' to NULL.
6519135446Strhodes */
6520135446Strhodesstatic isc_result_t
6521254897Serwinzone_from_args(ns_server_t *server, char *args, const char *zonetxt,
6522262706Serwin	       dns_zone_t **zonep, const char **zonename,
6523262706Serwin	       isc_buffer_t *text, isc_boolean_t skip)
6524224092Sdougb{
6525135446Strhodes	char *input, *ptr;
6526135446Strhodes	char *classtxt;
6527135446Strhodes	const char *viewtxt = NULL;
6528262706Serwin	dns_fixedname_t fname;
6529262706Serwin	dns_name_t *name;
6530135446Strhodes	isc_result_t result;
6531135446Strhodes	dns_view_t *view = NULL;
6532135446Strhodes	dns_rdataclass_t rdclass;
6533262706Serwin	char problem[DNS_NAME_FORMATSIZE + 500] = "";
6534135446Strhodes
6535135446Strhodes	REQUIRE(zonep != NULL && *zonep == NULL);
6536254402Serwin	REQUIRE(zonename == NULL || *zonename == NULL);
6537135446Strhodes
6538135446Strhodes	input = args;
6539135446Strhodes
6540254897Serwin	if (skip) {
6541254897Serwin		/* Skip the command name. */
6542254897Serwin		ptr = next_token(&input, " \t");
6543254897Serwin		if (ptr == NULL)
6544254897Serwin			return (ISC_R_UNEXPECTEDEND);
6545254897Serwin	}
6546135446Strhodes
6547135446Strhodes	/* Look for the zone name. */
6548135446Strhodes	if (zonetxt == NULL)
6549254897Serwin		zonetxt = next_token(&input, " \t");
6550254897Serwin	if (zonetxt == NULL)
6551135446Strhodes		return (ISC_R_SUCCESS);
6552254402Serwin	if (zonename != NULL)
6553224092Sdougb		*zonename = zonetxt;
6554135446Strhodes
6555135446Strhodes	/* Look for the optional class name. */
6556135446Strhodes	classtxt = next_token(&input, " \t");
6557135446Strhodes	if (classtxt != NULL) {
6558135446Strhodes		/* Look for the optional view name. */
6559135446Strhodes		viewtxt = next_token(&input, " \t");
6560135446Strhodes	}
6561135446Strhodes
6562262706Serwin	dns_fixedname_init(&fname);
6563262706Serwin	name = dns_fixedname_name(&fname);
6564262706Serwin	CHECK(dns_name_fromstring(name, zonetxt, 0, NULL));
6565135446Strhodes
6566135446Strhodes	if (classtxt != NULL) {
6567135446Strhodes		isc_textregion_t r;
6568135446Strhodes		r.base = classtxt;
6569135446Strhodes		r.length = strlen(classtxt);
6570262706Serwin		CHECK(dns_rdataclass_fromtext(&rdclass, &r));
6571193149Sdougb	} else
6572193149Sdougb		rdclass = dns_rdataclass_in;
6573193149Sdougb
6574193149Sdougb	if (viewtxt == NULL) {
6575262706Serwin		result = dns_viewlist_findzone(&server->viewlist, name,
6576193149Sdougb					       ISC_TF(classtxt == NULL),
6577193149Sdougb					       rdclass, zonep);
6578262706Serwin		if (result == ISC_R_NOTFOUND)
6579262706Serwin			snprintf(problem, sizeof(problem),
6580262706Serwin				 "no matching zone '%s' in any view",
6581262706Serwin				 zonetxt);
6582135446Strhodes	} else {
6583193149Sdougb		result = dns_viewlist_find(&server->viewlist, viewtxt,
6584193149Sdougb					   rdclass, &view);
6585262706Serwin		if (result != ISC_R_SUCCESS) {
6586262706Serwin			snprintf(problem, sizeof(problem),
6587262706Serwin				 "no matching view '%s'", viewtxt);
6588262706Serwin			goto report;
6589262706Serwin		}
6590262706Serwin
6591262706Serwin		result = dns_zt_find(view->zonetable, name, 0, NULL, zonep);
6592193149Sdougb		if (result != ISC_R_SUCCESS)
6593262706Serwin			snprintf(problem, sizeof(problem),
6594262706Serwin				 "no matching zone '%s' in view '%s'",
6595262706Serwin				 zonetxt, viewtxt);
6596135446Strhodes	}
6597186462Sdougb
6598135446Strhodes	/* Partial match? */
6599135446Strhodes	if (result != ISC_R_SUCCESS && *zonep != NULL)
6600135446Strhodes		dns_zone_detach(zonep);
6601204619Sdougb	if (result == DNS_R_PARTIALMATCH)
6602204619Sdougb		result = ISC_R_NOTFOUND;
6603262706Serwin report:
6604262706Serwin	if (result != ISC_R_SUCCESS) {
6605262706Serwin		isc_result_t tresult;
6606262706Serwin
6607262706Serwin		tresult = putstr(text, problem);
6608262706Serwin		if (tresult == ISC_R_SUCCESS &&
6609262706Serwin		    isc_buffer_availablelength(text) > 0U)
6610262706Serwin			isc_buffer_putuint8(text, 0);
6611262706Serwin	}
6612262706Serwin
6613262706Serwin cleanup:
6614262706Serwin	if (view != NULL)
6615262706Serwin		dns_view_detach(&view);
6616262706Serwin
6617135446Strhodes	return (result);
6618135446Strhodes}
6619135446Strhodes
6620135446Strhodes/*
6621135446Strhodes * Act on a "retransfer" command from the command channel.
6622135446Strhodes */
6623135446Strhodesisc_result_t
6624262706Serwinns_server_retransfercommand(ns_server_t *server, char *args,
6625262706Serwin			    isc_buffer_t *text)
6626262706Serwin{
6627135446Strhodes	isc_result_t result;
6628135446Strhodes	dns_zone_t *zone = NULL;
6629254897Serwin	dns_zone_t *raw = NULL;
6630135446Strhodes	dns_zonetype_t type;
6631186462Sdougb
6632262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
6633262706Serwin				text, ISC_TRUE);
6634135446Strhodes	if (result != ISC_R_SUCCESS)
6635135446Strhodes		return (result);
6636135446Strhodes	if (zone == NULL)
6637135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6638254897Serwin	dns_zone_getraw(zone, &raw);
6639254897Serwin	if (raw != NULL) {
6640254897Serwin		dns_zone_detach(&zone);
6641254897Serwin		dns_zone_attach(raw, &zone);
6642254897Serwin		dns_zone_detach(&raw);
6643254897Serwin	}
6644135446Strhodes	type = dns_zone_gettype(zone);
6645135446Strhodes	if (type == dns_zone_slave || type == dns_zone_stub)
6646135446Strhodes		dns_zone_forcereload(zone);
6647135446Strhodes	else
6648135446Strhodes		result = ISC_R_NOTFOUND;
6649135446Strhodes	dns_zone_detach(&zone);
6650135446Strhodes	return (result);
6651186462Sdougb}
6652135446Strhodes
6653135446Strhodes/*
6654135446Strhodes * Act on a "reload" command from the command channel.
6655135446Strhodes */
6656135446Strhodesisc_result_t
6657135446Strhodesns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6658135446Strhodes	isc_result_t result;
6659135446Strhodes	dns_zone_t *zone = NULL;
6660135446Strhodes	dns_zonetype_t type;
6661135446Strhodes	const char *msg = NULL;
6662186462Sdougb
6663262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
6664262706Serwin				text, ISC_TRUE);
6665135446Strhodes	if (result != ISC_R_SUCCESS)
6666135446Strhodes		return (result);
6667135446Strhodes	if (zone == NULL) {
6668135446Strhodes		result = reload(server);
6669135446Strhodes		if (result == ISC_R_SUCCESS)
6670135446Strhodes			msg = "server reload successful";
6671135446Strhodes	} else {
6672135446Strhodes		type = dns_zone_gettype(zone);
6673135446Strhodes		if (type == dns_zone_slave || type == dns_zone_stub) {
6674135446Strhodes			dns_zone_refresh(zone);
6675174187Sdougb			dns_zone_detach(&zone);
6676135446Strhodes			msg = "zone refresh queued";
6677135446Strhodes		} else {
6678135446Strhodes			result = dns_zone_load(zone);
6679135446Strhodes			dns_zone_detach(&zone);
6680186462Sdougb			switch (result) {
6681135446Strhodes			case ISC_R_SUCCESS:
6682135446Strhodes				 msg = "zone reload successful";
6683135446Strhodes				 break;
6684135446Strhodes			case DNS_R_CONTINUE:
6685135446Strhodes				msg = "zone reload queued";
6686135446Strhodes				result = ISC_R_SUCCESS;
6687135446Strhodes				break;
6688135446Strhodes			case DNS_R_UPTODATE:
6689135446Strhodes				msg = "zone reload up-to-date";
6690135446Strhodes				result = ISC_R_SUCCESS;
6691135446Strhodes				break;
6692135446Strhodes			default:
6693135446Strhodes				/* failure message will be generated by rndc */
6694135446Strhodes				break;
6695135446Strhodes			}
6696135446Strhodes		}
6697135446Strhodes	}
6698135446Strhodes	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
6699135446Strhodes		isc_buffer_putmem(text, (const unsigned char *)msg,
6700135446Strhodes				  strlen(msg) + 1);
6701135446Strhodes	return (result);
6702186462Sdougb}
6703135446Strhodes
6704135446Strhodes/*
6705135446Strhodes * Act on a "reconfig" command from the command channel.
6706135446Strhodes */
6707135446Strhodesisc_result_t
6708135446Strhodesns_server_reconfigcommand(ns_server_t *server, char *args) {
6709135446Strhodes	UNUSED(args);
6710135446Strhodes
6711135446Strhodes	reconfig(server);
6712135446Strhodes	return (ISC_R_SUCCESS);
6713135446Strhodes}
6714135446Strhodes
6715135446Strhodes/*
6716170222Sdougb * Act on a "notify" command from the command channel.
6717170222Sdougb */
6718170222Sdougbisc_result_t
6719170222Sdougbns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6720170222Sdougb	isc_result_t result;
6721170222Sdougb	dns_zone_t *zone = NULL;
6722170222Sdougb	const unsigned char msg[] = "zone notify queued";
6723170222Sdougb
6724262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
6725262706Serwin				text, ISC_TRUE);
6726170222Sdougb	if (result != ISC_R_SUCCESS)
6727170222Sdougb		return (result);
6728170222Sdougb	if (zone == NULL)
6729170222Sdougb		return (ISC_R_UNEXPECTEDEND);
6730186462Sdougb
6731170222Sdougb	dns_zone_notify(zone);
6732170222Sdougb	dns_zone_detach(&zone);
6733170222Sdougb	if (sizeof(msg) <= isc_buffer_availablelength(text))
6734170222Sdougb		isc_buffer_putmem(text, msg, sizeof(msg));
6735170222Sdougb
6736170222Sdougb	return (ISC_R_SUCCESS);
6737186462Sdougb}
6738170222Sdougb
6739170222Sdougb/*
6740135446Strhodes * Act on a "refresh" command from the command channel.
6741135446Strhodes */
6742135446Strhodesisc_result_t
6743135446Strhodesns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6744135446Strhodes	isc_result_t result;
6745262706Serwin	dns_zone_t *zone = NULL, *raw = NULL;
6746165071Sdougb	const unsigned char msg1[] = "zone refresh queued";
6747165071Sdougb	const unsigned char msg2[] = "not a slave or stub zone";
6748165071Sdougb	dns_zonetype_t type;
6749135446Strhodes
6750262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
6751262706Serwin				text, ISC_TRUE);
6752135446Strhodes	if (result != ISC_R_SUCCESS)
6753135446Strhodes		return (result);
6754135446Strhodes	if (zone == NULL)
6755135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6756165071Sdougb
6757262706Serwin	dns_zone_getraw(zone, &raw);
6758262706Serwin	if (raw != NULL) {
6759262706Serwin		dns_zone_detach(&zone);
6760262706Serwin		dns_zone_attach(raw, &zone);
6761262706Serwin		dns_zone_detach(&raw);
6762262706Serwin	}
6763262706Serwin
6764165071Sdougb	type = dns_zone_gettype(zone);
6765165071Sdougb	if (type == dns_zone_slave || type == dns_zone_stub) {
6766165071Sdougb		dns_zone_refresh(zone);
6767165071Sdougb		dns_zone_detach(&zone);
6768165071Sdougb		if (sizeof(msg1) <= isc_buffer_availablelength(text))
6769165071Sdougb			isc_buffer_putmem(text, msg1, sizeof(msg1));
6770165071Sdougb		return (ISC_R_SUCCESS);
6771165071Sdougb	}
6772186462Sdougb
6773135446Strhodes	dns_zone_detach(&zone);
6774165071Sdougb	if (sizeof(msg2) <= isc_buffer_availablelength(text))
6775165071Sdougb		isc_buffer_putmem(text, msg2, sizeof(msg2));
6776165071Sdougb	return (ISC_R_FAILURE);
6777186462Sdougb}
6778135446Strhodes
6779135446Strhodesisc_result_t
6780254897Serwinns_server_togglequerylog(ns_server_t *server, char *args) {
6781254897Serwin	isc_boolean_t value;
6782254897Serwin	char *ptr;
6783186462Sdougb
6784254897Serwin	/* Skip the command name. */
6785254897Serwin	ptr = next_token(&args, " \t");
6786254897Serwin	if (ptr == NULL)
6787254897Serwin		return (ISC_R_UNEXPECTEDEND);
6788254897Serwin
6789254897Serwin	ptr = next_token(&args, " \t");
6790254897Serwin	if (ptr == NULL)
6791254897Serwin		value = server->log_queries ? ISC_FALSE : ISC_TRUE;
6792254897Serwin	else if (strcasecmp(ptr, "yes") == 0 || strcasecmp(ptr, "on") == 0)
6793254897Serwin		value = ISC_TRUE;
6794254897Serwin	else if (strcasecmp(ptr, "no") == 0 || strcasecmp(ptr, "off") == 0)
6795254897Serwin		value = ISC_FALSE;
6796254897Serwin	else
6797254897Serwin		return (ISC_R_NOTFOUND);
6798254897Serwin
6799254897Serwin	if (server->log_queries == value)
6800254897Serwin		return (ISC_R_SUCCESS);
6801254897Serwin
6802254897Serwin	server->log_queries = value;
6803254897Serwin
6804135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6805135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6806135446Strhodes		      "query logging is now %s",
6807135446Strhodes		      server->log_queries ? "on" : "off");
6808135446Strhodes	return (ISC_R_SUCCESS);
6809135446Strhodes}
6810135446Strhodes
6811135446Strhodesstatic isc_result_t
6812165071Sdougbns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
6813170222Sdougb			 cfg_aclconfctx_t *actx,
6814135446Strhodes			 isc_mem_t *mctx, ns_listenlist_t **target)
6815135446Strhodes{
6816135446Strhodes	isc_result_t result;
6817165071Sdougb	const cfg_listelt_t *element;
6818135446Strhodes	ns_listenlist_t *dlist = NULL;
6819135446Strhodes
6820135446Strhodes	REQUIRE(target != NULL && *target == NULL);
6821135446Strhodes
6822135446Strhodes	result = ns_listenlist_create(mctx, &dlist);
6823135446Strhodes	if (result != ISC_R_SUCCESS)
6824135446Strhodes		return (result);
6825135446Strhodes
6826135446Strhodes	for (element = cfg_list_first(listenlist);
6827135446Strhodes	     element != NULL;
6828135446Strhodes	     element = cfg_list_next(element))
6829135446Strhodes	{
6830135446Strhodes		ns_listenelt_t *delt = NULL;
6831165071Sdougb		const cfg_obj_t *listener = cfg_listelt_value(element);
6832135446Strhodes		result = ns_listenelt_fromconfig(listener, config, actx,
6833135446Strhodes						 mctx, &delt);
6834135446Strhodes		if (result != ISC_R_SUCCESS)
6835135446Strhodes			goto cleanup;
6836135446Strhodes		ISC_LIST_APPEND(dlist->elts, delt, link);
6837135446Strhodes	}
6838135446Strhodes	*target = dlist;
6839135446Strhodes	return (ISC_R_SUCCESS);
6840135446Strhodes
6841135446Strhodes cleanup:
6842135446Strhodes	ns_listenlist_detach(&dlist);
6843135446Strhodes	return (result);
6844135446Strhodes}
6845135446Strhodes
6846135446Strhodes/*
6847135446Strhodes * Create a listen list from the corresponding configuration
6848135446Strhodes * data structure.
6849135446Strhodes */
6850135446Strhodesstatic isc_result_t
6851165071Sdougbns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
6852170222Sdougb			cfg_aclconfctx_t *actx,
6853135446Strhodes			isc_mem_t *mctx, ns_listenelt_t **target)
6854135446Strhodes{
6855135446Strhodes	isc_result_t result;
6856165071Sdougb	const cfg_obj_t *portobj;
6857135446Strhodes	in_port_t port;
6858135446Strhodes	ns_listenelt_t *delt = NULL;
6859135446Strhodes	REQUIRE(target != NULL && *target == NULL);
6860135446Strhodes
6861135446Strhodes	portobj = cfg_tuple_get(listener, "port");
6862135446Strhodes	if (!cfg_obj_isuint32(portobj)) {
6863135446Strhodes		if (ns_g_port != 0) {
6864135446Strhodes			port = ns_g_port;
6865135446Strhodes		} else {
6866135446Strhodes			result = ns_config_getport(config, &port);
6867135446Strhodes			if (result != ISC_R_SUCCESS)
6868135446Strhodes				return (result);
6869135446Strhodes		}
6870135446Strhodes	} else {
6871135446Strhodes		if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
6872135446Strhodes			cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
6873135446Strhodes				    "port value '%u' is out of range",
6874135446Strhodes				    cfg_obj_asuint32(portobj));
6875135446Strhodes			return (ISC_R_RANGE);
6876135446Strhodes		}
6877135446Strhodes		port = (in_port_t)cfg_obj_asuint32(portobj);
6878135446Strhodes	}
6879135446Strhodes
6880135446Strhodes	result = ns_listenelt_create(mctx, port, NULL, &delt);
6881135446Strhodes	if (result != ISC_R_SUCCESS)
6882135446Strhodes		return (result);
6883135446Strhodes
6884170222Sdougb	result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
6885193149Sdougb				   config, ns_g_lctx, actx, mctx, 0,
6886193149Sdougb				   &delt->acl);
6887135446Strhodes	if (result != ISC_R_SUCCESS) {
6888135446Strhodes		ns_listenelt_destroy(delt);
6889135446Strhodes		return (result);
6890135446Strhodes	}
6891135446Strhodes	*target = delt;
6892135446Strhodes	return (ISC_R_SUCCESS);
6893135446Strhodes}
6894135446Strhodes
6895135446Strhodesisc_result_t
6896135446Strhodesns_server_dumpstats(ns_server_t *server) {
6897135446Strhodes	isc_result_t result;
6898135446Strhodes	FILE *fp = NULL;
6899135446Strhodes
6900135446Strhodes	CHECKMF(isc_stdio_open(server->statsfile, "a", &fp),
6901135446Strhodes		"could not open statistics dump file", server->statsfile);
6902186462Sdougb
6903193149Sdougb	result = ns_stats_dump(server, fp);
6904186462Sdougb
6905135446Strhodes cleanup:
6906135446Strhodes	if (fp != NULL)
6907135446Strhodes		(void)isc_stdio_close(fp);
6908193149Sdougb	if (result == ISC_R_SUCCESS)
6909193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6910193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6911193149Sdougb			      "dumpstats complete");
6912193149Sdougb	else
6913193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6914193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6915193149Sdougb			      "dumpstats failed: %s",
6916193149Sdougb			      dns_result_totext(result));
6917135446Strhodes	return (result);
6918135446Strhodes}
6919135446Strhodes
6920135446Strhodesstatic isc_result_t
6921135446Strhodesadd_zone_tolist(dns_zone_t *zone, void *uap) {
6922135446Strhodes	struct dumpcontext *dctx = uap;
6923135446Strhodes	struct zonelistentry *zle;
6924135446Strhodes
6925135446Strhodes	zle = isc_mem_get(dctx->mctx, sizeof *zle);
6926135446Strhodes	if (zle ==  NULL)
6927135446Strhodes		return (ISC_R_NOMEMORY);
6928135446Strhodes	zle->zone = NULL;
6929135446Strhodes	dns_zone_attach(zone, &zle->zone);
6930135446Strhodes	ISC_LINK_INIT(zle, link);
6931135446Strhodes	ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link);
6932135446Strhodes	return (ISC_R_SUCCESS);
6933135446Strhodes}
6934135446Strhodes
6935135446Strhodesstatic isc_result_t
6936135446Strhodesadd_view_tolist(struct dumpcontext *dctx, dns_view_t *view) {
6937135446Strhodes	struct viewlistentry *vle;
6938135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
6939186462Sdougb
6940153816Sdougb	/*
6941153816Sdougb	 * Prevent duplicate views.
6942153816Sdougb	 */
6943153816Sdougb	for (vle = ISC_LIST_HEAD(dctx->viewlist);
6944153816Sdougb	     vle != NULL;
6945153816Sdougb	     vle = ISC_LIST_NEXT(vle, link))
6946153816Sdougb		if (vle->view == view)
6947153816Sdougb			return (ISC_R_SUCCESS);
6948153816Sdougb
6949135446Strhodes	vle = isc_mem_get(dctx->mctx, sizeof *vle);
6950135446Strhodes	if (vle == NULL)
6951135446Strhodes		return (ISC_R_NOMEMORY);
6952135446Strhodes	vle->view = NULL;
6953135446Strhodes	dns_view_attach(view, &vle->view);
6954135446Strhodes	ISC_LINK_INIT(vle, link);
6955135446Strhodes	ISC_LIST_INIT(vle->zonelist);
6956135446Strhodes	ISC_LIST_APPEND(dctx->viewlist, vle, link);
6957135446Strhodes	if (dctx->dumpzones)
6958135446Strhodes		result = dns_zt_apply(view->zonetable, ISC_TRUE,
6959135446Strhodes				      add_zone_tolist, dctx);
6960135446Strhodes	return (result);
6961135446Strhodes}
6962135446Strhodes
6963135446Strhodesstatic void
6964135446Strhodesdumpcontext_destroy(struct dumpcontext *dctx) {
6965135446Strhodes	struct viewlistentry *vle;
6966135446Strhodes	struct zonelistentry *zle;
6967135446Strhodes
6968135446Strhodes	vle = ISC_LIST_HEAD(dctx->viewlist);
6969135446Strhodes	while (vle != NULL) {
6970135446Strhodes		ISC_LIST_UNLINK(dctx->viewlist, vle, link);
6971135446Strhodes		zle = ISC_LIST_HEAD(vle->zonelist);
6972135446Strhodes		while (zle != NULL) {
6973135446Strhodes			ISC_LIST_UNLINK(vle->zonelist, zle, link);
6974135446Strhodes			dns_zone_detach(&zle->zone);
6975135446Strhodes			isc_mem_put(dctx->mctx, zle, sizeof *zle);
6976135446Strhodes			zle = ISC_LIST_HEAD(vle->zonelist);
6977135446Strhodes		}
6978135446Strhodes		dns_view_detach(&vle->view);
6979135446Strhodes		isc_mem_put(dctx->mctx, vle, sizeof *vle);
6980135446Strhodes		vle = ISC_LIST_HEAD(dctx->viewlist);
6981135446Strhodes	}
6982135446Strhodes	if (dctx->version != NULL)
6983135446Strhodes		dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
6984135446Strhodes	if (dctx->db != NULL)
6985135446Strhodes		dns_db_detach(&dctx->db);
6986135446Strhodes	if (dctx->cache != NULL)
6987135446Strhodes		dns_db_detach(&dctx->cache);
6988135446Strhodes	if (dctx->task != NULL)
6989135446Strhodes		isc_task_detach(&dctx->task);
6990135446Strhodes	if (dctx->fp != NULL)
6991135446Strhodes		(void)isc_stdio_close(dctx->fp);
6992135446Strhodes	if (dctx->mdctx != NULL)
6993135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
6994135446Strhodes	isc_mem_put(dctx->mctx, dctx, sizeof *dctx);
6995135446Strhodes}
6996135446Strhodes
6997135446Strhodesstatic void
6998135446Strhodesdumpdone(void *arg, isc_result_t result) {
6999135446Strhodes	struct dumpcontext *dctx = arg;
7000135446Strhodes	char buf[1024+32];
7001135446Strhodes	const dns_master_style_t *style;
7002186462Sdougb
7003135446Strhodes	if (result != ISC_R_SUCCESS)
7004135446Strhodes		goto cleanup;
7005135446Strhodes	if (dctx->mdctx != NULL)
7006135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
7007135446Strhodes	if (dctx->view == NULL) {
7008135446Strhodes		dctx->view = ISC_LIST_HEAD(dctx->viewlist);
7009135446Strhodes		if (dctx->view == NULL)
7010135446Strhodes			goto done;
7011135446Strhodes		INSIST(dctx->zone == NULL);
7012153816Sdougb	} else
7013153816Sdougb		goto resume;
7014135446Strhodes nextview:
7015135446Strhodes	fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
7016153816Sdougb resume:
7017224092Sdougb	if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) {
7018224092Sdougb		fprintf(dctx->fp,
7019224092Sdougb			";\n; Cache of view '%s' is shared as '%s'\n",
7020224092Sdougb			dctx->view->view->name,
7021224092Sdougb			dns_cache_getname(dctx->view->view->cache));
7022224092Sdougb	} else if (dctx->zone == NULL && dctx->cache == NULL &&
7023224092Sdougb		   dctx->dumpcache)
7024224092Sdougb	{
7025135446Strhodes		style = &dns_master_style_cache;
7026135446Strhodes		/* start cache dump */
7027135446Strhodes		if (dctx->view->view->cachedb != NULL)
7028135446Strhodes			dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
7029135446Strhodes		if (dctx->cache != NULL) {
7030224092Sdougb			fprintf(dctx->fp,
7031224092Sdougb				";\n; Cache dump of view '%s' (cache %s)\n;\n",
7032224092Sdougb				dctx->view->view->name,
7033224092Sdougb				dns_cache_getname(dctx->view->view->cache));
7034135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
7035135446Strhodes							    dctx->cache, NULL,
7036135446Strhodes							    style, dctx->fp,
7037135446Strhodes							    dctx->task,
7038135446Strhodes							    dumpdone, dctx,
7039135446Strhodes							    &dctx->mdctx);
7040135446Strhodes			if (result == DNS_R_CONTINUE)
7041135446Strhodes				return;
7042135446Strhodes			if (result == ISC_R_NOTIMPLEMENTED)
7043135446Strhodes				fprintf(dctx->fp, "; %s\n",
7044135446Strhodes					dns_result_totext(result));
7045135446Strhodes			else if (result != ISC_R_SUCCESS)
7046135446Strhodes				goto cleanup;
7047135446Strhodes		}
7048135446Strhodes	}
7049135446Strhodes	if (dctx->cache != NULL) {
7050135446Strhodes		dns_adb_dump(dctx->view->view->adb, dctx->fp);
7051205292Sdougb		dns_resolver_printbadcache(dctx->view->view->resolver,
7052205292Sdougb					   dctx->fp);
7053135446Strhodes		dns_db_detach(&dctx->cache);
7054135446Strhodes	}
7055135446Strhodes	if (dctx->dumpzones) {
7056135446Strhodes		style = &dns_master_style_full;
7057135446Strhodes nextzone:
7058135446Strhodes		if (dctx->version != NULL)
7059135446Strhodes			dns_db_closeversion(dctx->db, &dctx->version,
7060135446Strhodes					    ISC_FALSE);
7061135446Strhodes		if (dctx->db != NULL)
7062135446Strhodes			dns_db_detach(&dctx->db);
7063135446Strhodes		if (dctx->zone == NULL)
7064135446Strhodes			dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist);
7065135446Strhodes		else
7066135446Strhodes			dctx->zone = ISC_LIST_NEXT(dctx->zone, link);
7067135446Strhodes		if (dctx->zone != NULL) {
7068135446Strhodes			/* start zone dump */
7069135446Strhodes			dns_zone_name(dctx->zone->zone, buf, sizeof(buf));
7070135446Strhodes			fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf);
7071135446Strhodes			result = dns_zone_getdb(dctx->zone->zone, &dctx->db);
7072135446Strhodes			if (result != ISC_R_SUCCESS) {
7073135446Strhodes				fprintf(dctx->fp, "; %s\n",
7074135446Strhodes					dns_result_totext(result));
7075135446Strhodes				goto nextzone;
7076135446Strhodes			}
7077135446Strhodes			dns_db_currentversion(dctx->db, &dctx->version);
7078135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
7079135446Strhodes							    dctx->db,
7080135446Strhodes							    dctx->version,
7081135446Strhodes							    style, dctx->fp,
7082135446Strhodes							    dctx->task,
7083135446Strhodes							    dumpdone, dctx,
7084135446Strhodes							    &dctx->mdctx);
7085135446Strhodes			if (result == DNS_R_CONTINUE)
7086135446Strhodes				return;
7087153816Sdougb			if (result == ISC_R_NOTIMPLEMENTED) {
7088135446Strhodes				fprintf(dctx->fp, "; %s\n",
7089135446Strhodes					dns_result_totext(result));
7090153816Sdougb				result = ISC_R_SUCCESS;
7091225361Sdougb				POST(result);
7092153816Sdougb				goto nextzone;
7093153816Sdougb			}
7094135446Strhodes			if (result != ISC_R_SUCCESS)
7095135446Strhodes				goto cleanup;
7096135446Strhodes		}
7097135446Strhodes	}
7098135446Strhodes	if (dctx->view != NULL)
7099135446Strhodes		dctx->view = ISC_LIST_NEXT(dctx->view, link);
7100135446Strhodes	if (dctx->view != NULL)
7101135446Strhodes		goto nextview;
7102135446Strhodes done:
7103135446Strhodes	fprintf(dctx->fp, "; Dump complete\n");
7104135446Strhodes	result = isc_stdio_flush(dctx->fp);
7105135446Strhodes	if (result == ISC_R_SUCCESS)
7106135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7107135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7108135446Strhodes			      "dumpdb complete");
7109135446Strhodes cleanup:
7110135446Strhodes	if (result != ISC_R_SUCCESS)
7111135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7112193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7113135446Strhodes			      "dumpdb failed: %s", dns_result_totext(result));
7114135446Strhodes	dumpcontext_destroy(dctx);
7115135446Strhodes}
7116135446Strhodes
7117135446Strhodesisc_result_t
7118135446Strhodesns_server_dumpdb(ns_server_t *server, char *args) {
7119135446Strhodes	struct dumpcontext *dctx = NULL;
7120135446Strhodes	dns_view_t *view;
7121135446Strhodes	isc_result_t result;
7122135446Strhodes	char *ptr;
7123135446Strhodes	const char *sep;
7124135446Strhodes
7125165071Sdougb	/* Skip the command name. */
7126165071Sdougb	ptr = next_token(&args, " \t");
7127165071Sdougb	if (ptr == NULL)
7128165071Sdougb		return (ISC_R_UNEXPECTEDEND);
7129165071Sdougb
7130135446Strhodes	dctx = isc_mem_get(server->mctx, sizeof(*dctx));
7131135446Strhodes	if (dctx == NULL)
7132135446Strhodes		return (ISC_R_NOMEMORY);
7133135446Strhodes
7134135446Strhodes	dctx->mctx = server->mctx;
7135135446Strhodes	dctx->dumpcache = ISC_TRUE;
7136135446Strhodes	dctx->dumpzones = ISC_FALSE;
7137135446Strhodes	dctx->fp = NULL;
7138135446Strhodes	ISC_LIST_INIT(dctx->viewlist);
7139135446Strhodes	dctx->view = NULL;
7140135446Strhodes	dctx->zone = NULL;
7141135446Strhodes	dctx->cache = NULL;
7142135446Strhodes	dctx->mdctx = NULL;
7143135446Strhodes	dctx->db = NULL;
7144135446Strhodes	dctx->cache = NULL;
7145135446Strhodes	dctx->task = NULL;
7146135446Strhodes	dctx->version = NULL;
7147135446Strhodes	isc_task_attach(server->task, &dctx->task);
7148135446Strhodes
7149135446Strhodes	CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp),
7150135446Strhodes		"could not open dump file", server->dumpfile);
7151135446Strhodes
7152135446Strhodes	sep = (args == NULL) ? "" : ": ";
7153135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7154135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7155135446Strhodes		      "dumpdb started%s%s", sep, (args != NULL) ? args : "");
7156135446Strhodes
7157135446Strhodes	ptr = next_token(&args, " \t");
7158135446Strhodes	if (ptr != NULL && strcmp(ptr, "-all") == 0) {
7159135446Strhodes		dctx->dumpzones = ISC_TRUE;
7160135446Strhodes		dctx->dumpcache = ISC_TRUE;
7161135446Strhodes		ptr = next_token(&args, " \t");
7162135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
7163135446Strhodes		dctx->dumpzones = ISC_FALSE;
7164135446Strhodes		dctx->dumpcache = ISC_TRUE;
7165135446Strhodes		ptr = next_token(&args, " \t");
7166135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
7167135446Strhodes		dctx->dumpzones = ISC_TRUE;
7168135446Strhodes		dctx->dumpcache = ISC_FALSE;
7169135446Strhodes		ptr = next_token(&args, " \t");
7170186462Sdougb	}
7171135446Strhodes
7172153816Sdougb nextview:
7173135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
7174135446Strhodes	     view != NULL;
7175135446Strhodes	     view = ISC_LIST_NEXT(view, link))
7176135446Strhodes	{
7177135446Strhodes		if (ptr != NULL && strcmp(view->name, ptr) != 0)
7178135446Strhodes			continue;
7179135446Strhodes		CHECK(add_view_tolist(dctx, view));
7180135446Strhodes	}
7181153816Sdougb	if (ptr != NULL) {
7182153816Sdougb		ptr = next_token(&args, " \t");
7183153816Sdougb		if (ptr != NULL)
7184153816Sdougb			goto nextview;
7185153816Sdougb	}
7186135446Strhodes	dumpdone(dctx, ISC_R_SUCCESS);
7187135446Strhodes	return (ISC_R_SUCCESS);
7188135446Strhodes
7189135446Strhodes cleanup:
7190135446Strhodes	if (dctx != NULL)
7191135446Strhodes		dumpcontext_destroy(dctx);
7192135446Strhodes	return (result);
7193135446Strhodes}
7194135446Strhodes
7195135446Strhodesisc_result_t
7196224092Sdougbns_server_dumpsecroots(ns_server_t *server, char *args) {
7197224092Sdougb	dns_view_t *view;
7198224092Sdougb	dns_keytable_t *secroots = NULL;
7199224092Sdougb	isc_result_t result;
7200224092Sdougb	char *ptr;
7201224092Sdougb	FILE *fp = NULL;
7202224092Sdougb	isc_time_t now;
7203224092Sdougb	char tbuf[64];
7204224092Sdougb
7205224092Sdougb	/* Skip the command name. */
7206224092Sdougb	ptr = next_token(&args, " \t");
7207224092Sdougb	if (ptr == NULL)
7208224092Sdougb		return (ISC_R_UNEXPECTEDEND);
7209254897Serwin
7210224092Sdougb	ptr = next_token(&args, " \t");
7211224092Sdougb
7212224092Sdougb	CHECKMF(isc_stdio_open(server->secrootsfile, "w", &fp),
7213224092Sdougb		"could not open secroots dump file", server->secrootsfile);
7214224092Sdougb	TIME_NOW(&now);
7215224092Sdougb	isc_time_formattimestamp(&now, tbuf, sizeof(tbuf));
7216224092Sdougb	fprintf(fp, "%s\n", tbuf);
7217224092Sdougb
7218225361Sdougb	do {
7219225361Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
7220225361Sdougb		     view != NULL;
7221225361Sdougb		     view = ISC_LIST_NEXT(view, link))
7222225361Sdougb		{
7223225361Sdougb			if (ptr != NULL && strcmp(view->name, ptr) != 0)
7224225361Sdougb				continue;
7225225361Sdougb			if (secroots != NULL)
7226225361Sdougb				dns_keytable_detach(&secroots);
7227225361Sdougb			result = dns_view_getsecroots(view, &secroots);
7228225361Sdougb			if (result == ISC_R_NOTFOUND) {
7229225361Sdougb				result = ISC_R_SUCCESS;
7230225361Sdougb				continue;
7231225361Sdougb			}
7232225361Sdougb			fprintf(fp, "\n Start view %s\n\n", view->name);
7233225361Sdougb			result = dns_keytable_dump(secroots, fp);
7234225361Sdougb			if (result != ISC_R_SUCCESS)
7235225361Sdougb				fprintf(fp, " dumpsecroots failed: %s\n",
7236225361Sdougb					isc_result_totext(result));
7237224092Sdougb		}
7238224092Sdougb		if (ptr != NULL)
7239225361Sdougb			ptr = next_token(&args, " \t");
7240225361Sdougb	} while (ptr != NULL);
7241224092Sdougb
7242224092Sdougb cleanup:
7243224092Sdougb	if (secroots != NULL)
7244224092Sdougb		dns_keytable_detach(&secroots);
7245224092Sdougb	if (fp != NULL)
7246224092Sdougb		(void)isc_stdio_close(fp);
7247224092Sdougb	if (result == ISC_R_SUCCESS)
7248224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7249224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7250224092Sdougb			      "dumpsecroots complete");
7251224092Sdougb	else
7252224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7253224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7254224092Sdougb			      "dumpsecroots failed: %s",
7255224092Sdougb			      dns_result_totext(result));
7256224092Sdougb	return (result);
7257224092Sdougb}
7258224092Sdougb
7259224092Sdougbisc_result_t
7260135446Strhodesns_server_dumprecursing(ns_server_t *server) {
7261135446Strhodes	FILE *fp = NULL;
7262135446Strhodes	isc_result_t result;
7263135446Strhodes
7264135446Strhodes	CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
7265135446Strhodes		"could not open dump file", server->recfile);
7266135446Strhodes	fprintf(fp,";\n; Recursing Queries\n;\n");
7267135446Strhodes	ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
7268135446Strhodes	fprintf(fp, "; Dump complete\n");
7269135446Strhodes
7270135446Strhodes cleanup:
7271135446Strhodes	if (fp != NULL)
7272135446Strhodes		result = isc_stdio_close(fp);
7273193149Sdougb	if (result == ISC_R_SUCCESS)
7274193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7275193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7276193149Sdougb			      "dumprecursing complete");
7277193149Sdougb	else
7278193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7279193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7280193149Sdougb			      "dumprecursing failed: %s",
7281193149Sdougb			      dns_result_totext(result));
7282135446Strhodes	return (result);
7283135446Strhodes}
7284135446Strhodes
7285135446Strhodesisc_result_t
7286135446Strhodesns_server_setdebuglevel(ns_server_t *server, char *args) {
7287135446Strhodes	char *ptr;
7288135446Strhodes	char *levelstr;
7289135446Strhodes	char *endp;
7290135446Strhodes	long newlevel;
7291135446Strhodes
7292135446Strhodes	UNUSED(server);
7293135446Strhodes
7294135446Strhodes	/* Skip the command name. */
7295135446Strhodes	ptr = next_token(&args, " \t");
7296135446Strhodes	if (ptr == NULL)
7297135446Strhodes		return (ISC_R_UNEXPECTEDEND);
7298135446Strhodes
7299135446Strhodes	/* Look for the new level name. */
7300135446Strhodes	levelstr = next_token(&args, " \t");
7301135446Strhodes	if (levelstr == NULL) {
7302135446Strhodes		if (ns_g_debuglevel < 99)
7303135446Strhodes			ns_g_debuglevel++;
7304135446Strhodes	} else {
7305135446Strhodes		newlevel = strtol(levelstr, &endp, 10);
7306135446Strhodes		if (*endp != '\0' || newlevel < 0 || newlevel > 99)
7307135446Strhodes			return (ISC_R_RANGE);
7308135446Strhodes		ns_g_debuglevel = (unsigned int)newlevel;
7309135446Strhodes	}
7310135446Strhodes	isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
7311193149Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7312193149Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7313193149Sdougb		      "debug level is now %d", ns_g_debuglevel);
7314135446Strhodes	return (ISC_R_SUCCESS);
7315135446Strhodes}
7316135446Strhodes
7317135446Strhodesisc_result_t
7318170222Sdougbns_server_validation(ns_server_t *server, char *args) {
7319170222Sdougb	char *ptr, *viewname;
7320170222Sdougb	dns_view_t *view;
7321170222Sdougb	isc_boolean_t changed = ISC_FALSE;
7322170222Sdougb	isc_result_t result;
7323170222Sdougb	isc_boolean_t enable;
7324170222Sdougb
7325170222Sdougb	/* Skip the command name. */
7326170222Sdougb	ptr = next_token(&args, " \t");
7327170222Sdougb	if (ptr == NULL)
7328170222Sdougb		return (ISC_R_UNEXPECTEDEND);
7329170222Sdougb
7330170222Sdougb	/* Find out what we are to do. */
7331170222Sdougb	ptr = next_token(&args, " \t");
7332170222Sdougb	if (ptr == NULL)
7333170222Sdougb		return (ISC_R_UNEXPECTEDEND);
7334170222Sdougb
7335170222Sdougb	if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
7336170222Sdougb	    !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
7337170222Sdougb		enable = ISC_TRUE;
7338170222Sdougb	else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
7339170222Sdougb		 !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
7340170222Sdougb		enable = ISC_FALSE;
7341170222Sdougb	else
7342170222Sdougb		return (DNS_R_SYNTAX);
7343170222Sdougb
7344170222Sdougb	/* Look for the view name. */
7345170222Sdougb	viewname = next_token(&args, " \t");
7346170222Sdougb
7347170222Sdougb	result = isc_task_beginexclusive(server->task);
7348170222Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7349170222Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7350170222Sdougb	     view != NULL;
7351170222Sdougb	     view = ISC_LIST_NEXT(view, link))
7352170222Sdougb	{
7353170222Sdougb		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
7354170222Sdougb			continue;
7355170222Sdougb		result = dns_view_flushcache(view);
7356170222Sdougb		if (result != ISC_R_SUCCESS)
7357170222Sdougb			goto out;
7358170222Sdougb		view->enablevalidation = enable;
7359170222Sdougb		changed = ISC_TRUE;
7360170222Sdougb	}
7361170222Sdougb	if (changed)
7362170222Sdougb		result = ISC_R_SUCCESS;
7363170222Sdougb	else
7364170222Sdougb		result = ISC_R_FAILURE;
7365170222Sdougb out:
7366186462Sdougb	isc_task_endexclusive(server->task);
7367170222Sdougb	return (result);
7368170222Sdougb}
7369170222Sdougb
7370170222Sdougbisc_result_t
7371135446Strhodesns_server_flushcache(ns_server_t *server, char *args) {
7372135446Strhodes	char *ptr, *viewname;
7373135446Strhodes	dns_view_t *view;
7374174187Sdougb	isc_boolean_t flushed;
7375174187Sdougb	isc_boolean_t found;
7376135446Strhodes	isc_result_t result;
7377224092Sdougb	ns_cache_t *nsc;
7378135446Strhodes
7379135446Strhodes	/* Skip the command name. */
7380135446Strhodes	ptr = next_token(&args, " \t");
7381135446Strhodes	if (ptr == NULL)
7382135446Strhodes		return (ISC_R_UNEXPECTEDEND);
7383135446Strhodes
7384135446Strhodes	/* Look for the view name. */
7385135446Strhodes	viewname = next_token(&args, " \t");
7386135446Strhodes
7387135446Strhodes	result = isc_task_beginexclusive(server->task);
7388135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7389174187Sdougb	flushed = ISC_TRUE;
7390174187Sdougb	found = ISC_FALSE;
7391224092Sdougb
7392224092Sdougb	/*
7393224092Sdougb	 * Flushing a cache is tricky when caches are shared by multiple views.
7394224092Sdougb	 * We first identify which caches should be flushed in the local cache
7395224092Sdougb	 * list, flush these caches, and then update other views that refer to
7396224092Sdougb	 * the flushed cache DB.
7397224092Sdougb	 */
7398224092Sdougb	if (viewname != NULL) {
7399224092Sdougb		/*
7400224092Sdougb		 * Mark caches that need to be flushed.  This is an O(#view^2)
7401224092Sdougb		 * operation in the very worst case, but should be normally
7402224092Sdougb		 * much more lightweight because only a few (most typically just
7403224092Sdougb		 * one) views will match.
7404224092Sdougb		 */
7405224092Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
7406224092Sdougb		     view != NULL;
7407224092Sdougb		     view = ISC_LIST_NEXT(view, link))
7408224092Sdougb		{
7409224092Sdougb			if (strcasecmp(viewname, view->name) != 0)
7410224092Sdougb				continue;
7411224092Sdougb			found = ISC_TRUE;
7412224092Sdougb			for (nsc = ISC_LIST_HEAD(server->cachelist);
7413224092Sdougb			     nsc != NULL;
7414224092Sdougb			     nsc = ISC_LIST_NEXT(nsc, link)) {
7415224092Sdougb				if (nsc->cache == view->cache)
7416224092Sdougb					break;
7417224092Sdougb			}
7418224092Sdougb			INSIST(nsc != NULL);
7419224092Sdougb			nsc->needflush = ISC_TRUE;
7420224092Sdougb		}
7421224092Sdougb	} else
7422224092Sdougb		found = ISC_TRUE;
7423224092Sdougb
7424224092Sdougb	/* Perform flush */
7425224092Sdougb	for (nsc = ISC_LIST_HEAD(server->cachelist);
7426224092Sdougb	     nsc != NULL;
7427224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
7428224092Sdougb		if (viewname != NULL && !nsc->needflush)
7429135446Strhodes			continue;
7430224092Sdougb		nsc->needflush = ISC_TRUE;
7431224092Sdougb		result = dns_view_flushcache2(nsc->primaryview, ISC_FALSE);
7432193149Sdougb		if (result != ISC_R_SUCCESS) {
7433174187Sdougb			flushed = ISC_FALSE;
7434193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7435193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7436193149Sdougb				      "flushing cache in view '%s' failed: %s",
7437224092Sdougb				      nsc->primaryview->name,
7438224092Sdougb				      isc_result_totext(result));
7439193149Sdougb		}
7440135446Strhodes	}
7441224092Sdougb
7442224092Sdougb	/*
7443224092Sdougb	 * Fix up views that share a flushed cache: let the views update the
7444224092Sdougb	 * cache DB they're referring to.  This could also be an expensive
7445224092Sdougb	 * operation, but should typically be marginal: the inner loop is only
7446224092Sdougb	 * necessary for views that share a cache, and if there are many such
7447224092Sdougb	 * views the number of shared cache should normally be small.
7448224092Sdougb	 * A worst case is that we have n views and n/2 caches, each shared by
7449224092Sdougb	 * two views.  Then this will be a O(n^2/4) operation.
7450224092Sdougb	 */
7451224092Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7452224092Sdougb	     view != NULL;
7453224092Sdougb	     view = ISC_LIST_NEXT(view, link))
7454224092Sdougb	{
7455224092Sdougb		if (!dns_view_iscacheshared(view))
7456224092Sdougb			continue;
7457224092Sdougb		for (nsc = ISC_LIST_HEAD(server->cachelist);
7458224092Sdougb		     nsc != NULL;
7459224092Sdougb		     nsc = ISC_LIST_NEXT(nsc, link)) {
7460224092Sdougb			if (!nsc->needflush || nsc->cache != view->cache)
7461224092Sdougb				continue;
7462224092Sdougb			result = dns_view_flushcache2(view, ISC_TRUE);
7463224092Sdougb			if (result != ISC_R_SUCCESS) {
7464224092Sdougb				flushed = ISC_FALSE;
7465224092Sdougb				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7466224092Sdougb					      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7467224092Sdougb					      "fixing cache in view '%s' "
7468224092Sdougb					      "failed: %s", view->name,
7469224092Sdougb					      isc_result_totext(result));
7470224092Sdougb			}
7471224092Sdougb		}
7472224092Sdougb	}
7473224092Sdougb
7474224092Sdougb	/* Cleanup the cache list. */
7475224092Sdougb	for (nsc = ISC_LIST_HEAD(server->cachelist);
7476224092Sdougb	     nsc != NULL;
7477224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
7478224092Sdougb		nsc->needflush = ISC_FALSE;
7479224092Sdougb	}
7480224092Sdougb
7481174187Sdougb	if (flushed && found) {
7482193149Sdougb		if (viewname != NULL)
7483193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7484193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7485193149Sdougb				      "flushing cache in view '%s' succeeded",
7486193149Sdougb				      viewname);
7487193149Sdougb		else
7488193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7489193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7490193149Sdougb				      "flushing caches in all views succeeded");
7491135446Strhodes		result = ISC_R_SUCCESS;
7492174187Sdougb	} else {
7493193149Sdougb		if (!found) {
7494193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7495193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7496193149Sdougb				      "flushing cache in view '%s' failed: "
7497193149Sdougb				      "view not found", viewname);
7498174187Sdougb			result = ISC_R_NOTFOUND;
7499193149Sdougb		} else
7500174187Sdougb			result = ISC_R_FAILURE;
7501174187Sdougb	}
7502186462Sdougb	isc_task_endexclusive(server->task);
7503135446Strhodes	return (result);
7504135446Strhodes}
7505135446Strhodes
7506135446Strhodesisc_result_t
7507254897Serwinns_server_flushnode(ns_server_t *server, char *args, isc_boolean_t tree) {
7508135446Strhodes	char *ptr, *target, *viewname;
7509135446Strhodes	dns_view_t *view;
7510174187Sdougb	isc_boolean_t flushed;
7511174187Sdougb	isc_boolean_t found;
7512135446Strhodes	isc_result_t result;
7513135446Strhodes	isc_buffer_t b;
7514135446Strhodes	dns_fixedname_t fixed;
7515135446Strhodes	dns_name_t *name;
7516135446Strhodes
7517135446Strhodes	/* Skip the command name. */
7518135446Strhodes	ptr = next_token(&args, " \t");
7519135446Strhodes	if (ptr == NULL)
7520135446Strhodes		return (ISC_R_UNEXPECTEDEND);
7521135446Strhodes
7522135446Strhodes	/* Find the domain name to flush. */
7523135446Strhodes	target = next_token(&args, " \t");
7524135446Strhodes	if (target == NULL)
7525135446Strhodes		return (ISC_R_UNEXPECTEDEND);
7526135446Strhodes
7527254402Serwin	isc_buffer_constinit(&b, target, strlen(target));
7528135446Strhodes	isc_buffer_add(&b, strlen(target));
7529135446Strhodes	dns_fixedname_init(&fixed);
7530135446Strhodes	name = dns_fixedname_name(&fixed);
7531224092Sdougb	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
7532135446Strhodes	if (result != ISC_R_SUCCESS)
7533135446Strhodes		return (result);
7534135446Strhodes
7535135446Strhodes	/* Look for the view name. */
7536135446Strhodes	viewname = next_token(&args, " \t");
7537135446Strhodes
7538135446Strhodes	result = isc_task_beginexclusive(server->task);
7539135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7540135446Strhodes	flushed = ISC_TRUE;
7541174187Sdougb	found = ISC_FALSE;
7542135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
7543135446Strhodes	     view != NULL;
7544135446Strhodes	     view = ISC_LIST_NEXT(view, link))
7545135446Strhodes	{
7546135446Strhodes		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
7547135446Strhodes			continue;
7548174187Sdougb		found = ISC_TRUE;
7549224092Sdougb		/*
7550224092Sdougb		 * It's a little inefficient to try flushing name for all views
7551224092Sdougb		 * if some of the views share a single cache.  But since the
7552224092Sdougb		 * operation is lightweight we prefer simplicity here.
7553224092Sdougb		 */
7554254897Serwin		result = dns_view_flushnode(view, name, tree);
7555193149Sdougb		if (result != ISC_R_SUCCESS) {
7556135446Strhodes			flushed = ISC_FALSE;
7557193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7558193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7559254897Serwin				      "flushing %s '%s' in cache view '%s' "
7560254897Serwin				      "failed: %s",
7561254897Serwin				      tree ? "tree" : "name",
7562254897Serwin				      target, view->name,
7563193149Sdougb				      isc_result_totext(result));
7564193149Sdougb		}
7565135446Strhodes	}
7566193149Sdougb	if (flushed && found) {
7567193149Sdougb		if (viewname != NULL)
7568193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7569193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7570254897Serwin				      "flushing %s '%s' in cache view '%s' "
7571254897Serwin				      "succeeded",
7572254897Serwin				      tree ? "tree" : "name",
7573254897Serwin				      target, viewname);
7574193149Sdougb		else
7575193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7576193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7577254897Serwin				      "flushing %s '%s' in all cache views "
7578254897Serwin				      "succeeded",
7579254897Serwin				      tree ? "tree" : "name",
7580254897Serwin				      target);
7581135446Strhodes		result = ISC_R_SUCCESS;
7582193149Sdougb	} else {
7583193149Sdougb		if (!found)
7584193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7585193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7586254897Serwin				      "flushing %s '%s' in cache view '%s' "
7587254897Serwin				      "failed: view not found",
7588254897Serwin				      tree ? "tree" : "name",
7589254897Serwin				      target, viewname);
7590135446Strhodes		result = ISC_R_FAILURE;
7591193149Sdougb	}
7592186462Sdougb	isc_task_endexclusive(server->task);
7593135446Strhodes	return (result);
7594135446Strhodes}
7595135446Strhodes
7596135446Strhodesisc_result_t
7597135446Strhodesns_server_status(ns_server_t *server, isc_buffer_t *text) {
7598135446Strhodes	int zonecount, xferrunning, xferdeferred, soaqueries;
7599135446Strhodes	unsigned int n;
7600193149Sdougb	const char *ob = "", *cb = "", *alt = "";
7601135446Strhodes
7602193149Sdougb	if (ns_g_server->version_set) {
7603193149Sdougb		ob = " (";
7604193149Sdougb		cb = ")";
7605193149Sdougb		if (ns_g_server->version == NULL)
7606193149Sdougb			alt = "version.bind/txt/ch disabled";
7607193149Sdougb		else
7608193149Sdougb			alt = ns_g_server->version;
7609193149Sdougb	}
7610135446Strhodes	zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY);
7611135446Strhodes	xferrunning = dns_zonemgr_getcount(server->zonemgr,
7612135446Strhodes					   DNS_ZONESTATE_XFERRUNNING);
7613135446Strhodes	xferdeferred = dns_zonemgr_getcount(server->zonemgr,
7614135446Strhodes					    DNS_ZONESTATE_XFERDEFERRED);
7615135446Strhodes	soaqueries = dns_zonemgr_getcount(server->zonemgr,
7616135446Strhodes					  DNS_ZONESTATE_SOAQUERY);
7617193149Sdougb
7618135446Strhodes	n = snprintf((char *)isc_buffer_used(text),
7619135446Strhodes		     isc_buffer_availablelength(text),
7620262706Serwin		     "version: %s%s%s%s <id:%s>\n"
7621193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
7622193149Sdougb		     "CPUs found: %u\n"
7623193149Sdougb		     "worker threads: %u\n"
7624254897Serwin		     "UDP listeners per interface: %u\n"
7625193149Sdougb#endif
7626135446Strhodes		     "number of zones: %u\n"
7627135446Strhodes		     "debug level: %d\n"
7628135446Strhodes		     "xfers running: %u\n"
7629135446Strhodes		     "xfers deferred: %u\n"
7630135446Strhodes		     "soa queries in progress: %u\n"
7631135446Strhodes		     "query logging is %s\n"
7632170222Sdougb		     "recursive clients: %d/%d/%d\n"
7633135446Strhodes		     "tcp clients: %d/%d\n"
7634135446Strhodes		     "server is up and running",
7635262706Serwin		     ns_g_version, ob, alt, cb, ns_g_srcid,
7636193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
7637254897Serwin		     ns_g_cpus_detected, ns_g_cpus, ns_g_udpdisp,
7638193149Sdougb#endif
7639135446Strhodes		     zonecount, ns_g_debuglevel, xferrunning, xferdeferred,
7640135446Strhodes		     soaqueries, server->log_queries ? "ON" : "OFF",
7641170222Sdougb		     server->recursionquota.used, server->recursionquota.soft,
7642170222Sdougb		     server->recursionquota.max,
7643135446Strhodes		     server->tcpquota.used, server->tcpquota.max);
7644135446Strhodes	if (n >= isc_buffer_availablelength(text))
7645135446Strhodes		return (ISC_R_NOSPACE);
7646135446Strhodes	isc_buffer_add(text, n);
7647135446Strhodes	return (ISC_R_SUCCESS);
7648135446Strhodes}
7649135446Strhodes
7650193149Sdougbstatic isc_result_t
7651193149Sdougbdelete_keynames(dns_tsig_keyring_t *ring, char *target,
7652193149Sdougb		unsigned int *foundkeys)
7653193149Sdougb{
7654193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
7655193149Sdougb	isc_result_t result;
7656193149Sdougb	dns_rbtnodechain_t chain;
7657193149Sdougb	dns_name_t foundname;
7658193149Sdougb	dns_fixedname_t fixedorigin;
7659193149Sdougb	dns_name_t *origin;
7660193149Sdougb	dns_rbtnode_t *node;
7661193149Sdougb	dns_tsigkey_t *tkey;
7662193149Sdougb
7663193149Sdougb	dns_name_init(&foundname, NULL);
7664193149Sdougb	dns_fixedname_init(&fixedorigin);
7665193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
7666193149Sdougb
7667193149Sdougb again:
7668193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
7669193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
7670193149Sdougb					origin);
7671193149Sdougb	if (result == ISC_R_NOTFOUND) {
7672193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7673193149Sdougb		return (ISC_R_SUCCESS);
7674193149Sdougb	}
7675193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7676193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7677193149Sdougb		return (result);
7678193149Sdougb	}
7679193149Sdougb
7680193149Sdougb	for (;;) {
7681193149Sdougb		node = NULL;
7682193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
7683193149Sdougb		tkey = node->data;
7684193149Sdougb
7685193149Sdougb		if (tkey != NULL) {
7686193149Sdougb			if (!tkey->generated)
7687193149Sdougb				goto nextkey;
7688193149Sdougb
7689193149Sdougb			dns_name_format(&tkey->name, namestr, sizeof(namestr));
7690193149Sdougb			if (strcmp(namestr, target) == 0) {
7691193149Sdougb				(*foundkeys)++;
7692193149Sdougb				dns_rbtnodechain_invalidate(&chain);
7693193149Sdougb				(void)dns_rbt_deletename(ring->keys,
7694193149Sdougb							 &tkey->name,
7695193149Sdougb							 ISC_FALSE);
7696193149Sdougb				goto again;
7697193149Sdougb			}
7698193149Sdougb		}
7699193149Sdougb
7700193149Sdougb	nextkey:
7701193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname, origin);
7702193149Sdougb		if (result == ISC_R_NOMORE)
7703193149Sdougb			break;
7704193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7705193149Sdougb			dns_rbtnodechain_invalidate(&chain);
7706193149Sdougb			return (result);
7707193149Sdougb		}
7708193149Sdougb	}
7709193149Sdougb
7710193149Sdougb	return (ISC_R_SUCCESS);
7711193149Sdougb}
7712193149Sdougb
7713193149Sdougbisc_result_t
7714193149Sdougbns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) {
7715193149Sdougb	isc_result_t result;
7716193149Sdougb	unsigned int n;
7717193149Sdougb	dns_view_t *view;
7718193149Sdougb	unsigned int foundkeys = 0;
7719193149Sdougb	char *target;
7720193149Sdougb	char *viewname;
7721193149Sdougb
7722193149Sdougb	(void)next_token(&command, " \t");  /* skip command name */
7723193149Sdougb	target = next_token(&command, " \t");
7724193149Sdougb	if (target == NULL)
7725193149Sdougb		return (ISC_R_UNEXPECTEDEND);
7726193149Sdougb	viewname = next_token(&command, " \t");
7727193149Sdougb
7728193149Sdougb	result = isc_task_beginexclusive(server->task);
7729193149Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7730193149Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7731193149Sdougb	     view != NULL;
7732193149Sdougb	     view = ISC_LIST_NEXT(view, link)) {
7733193149Sdougb		if (viewname == NULL || strcmp(view->name, viewname) == 0) {
7734193149Sdougb			RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write);
7735193149Sdougb			result = delete_keynames(view->dynamickeys, target,
7736193149Sdougb						 &foundkeys);
7737193149Sdougb			RWUNLOCK(&view->dynamickeys->lock,
7738193149Sdougb				 isc_rwlocktype_write);
7739193149Sdougb			if (result != ISC_R_SUCCESS) {
7740193149Sdougb				isc_task_endexclusive(server->task);
7741193149Sdougb				return (result);
7742193149Sdougb			}
7743193149Sdougb		}
7744193149Sdougb	}
7745193149Sdougb	isc_task_endexclusive(server->task);
7746193149Sdougb
7747193149Sdougb	n = snprintf((char *)isc_buffer_used(text),
7748193149Sdougb		     isc_buffer_availablelength(text),
7749193149Sdougb		     "%d tsig keys deleted.\n", foundkeys);
7750218384Sdougb	if (n >= isc_buffer_availablelength(text))
7751193149Sdougb		return (ISC_R_NOSPACE);
7752193149Sdougb	isc_buffer_add(text, n);
7753193149Sdougb
7754193149Sdougb	return (ISC_R_SUCCESS);
7755193149Sdougb}
7756193149Sdougb
7757193149Sdougbstatic isc_result_t
7758193149Sdougblist_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text,
7759193149Sdougb	     unsigned int *foundkeys)
7760193149Sdougb{
7761193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
7762193149Sdougb	char creatorstr[DNS_NAME_FORMATSIZE];
7763193149Sdougb	isc_result_t result;
7764193149Sdougb	dns_rbtnodechain_t chain;
7765193149Sdougb	dns_name_t foundname;
7766193149Sdougb	dns_fixedname_t fixedorigin;
7767193149Sdougb	dns_name_t *origin;
7768193149Sdougb	dns_rbtnode_t *node;
7769193149Sdougb	dns_tsigkey_t *tkey;
7770193149Sdougb	unsigned int n;
7771193149Sdougb	const char *viewname;
7772193149Sdougb
7773193149Sdougb	if (view != NULL)
7774193149Sdougb		viewname = view->name;
7775193149Sdougb	else
7776193149Sdougb		viewname = "(global)";
7777193149Sdougb
7778193149Sdougb	dns_name_init(&foundname, NULL);
7779193149Sdougb	dns_fixedname_init(&fixedorigin);
7780193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
7781193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
7782193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
7783193149Sdougb					origin);
7784193149Sdougb	if (result == ISC_R_NOTFOUND) {
7785193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7786193149Sdougb		return (ISC_R_SUCCESS);
7787193149Sdougb	}
7788193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7789193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7790193149Sdougb		return (result);
7791193149Sdougb	}
7792193149Sdougb
7793193149Sdougb	for (;;) {
7794193149Sdougb		node = NULL;
7795193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
7796193149Sdougb		tkey = node->data;
7797193149Sdougb
7798193149Sdougb		if (tkey != NULL) {
7799193149Sdougb			(*foundkeys)++;
7800193149Sdougb			dns_name_format(&tkey->name, namestr, sizeof(namestr));
7801193149Sdougb			if (tkey->generated) {
7802193149Sdougb				dns_name_format(tkey->creator, creatorstr,
7803193149Sdougb						sizeof(creatorstr));
7804193149Sdougb				n = snprintf((char *)isc_buffer_used(text),
7805193149Sdougb					     isc_buffer_availablelength(text),
7806193149Sdougb					     "view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n",
7807193149Sdougb					     viewname, namestr, creatorstr);
7808193149Sdougb			} else {
7809193149Sdougb				n = snprintf((char *)isc_buffer_used(text),
7810193149Sdougb					     isc_buffer_availablelength(text),
7811193149Sdougb					     "view \"%s\"; type \"static\"; key \"%s\";\n",
7812193149Sdougb					     viewname, namestr);
7813193149Sdougb			}
7814193149Sdougb			if (n >= isc_buffer_availablelength(text)) {
7815193149Sdougb				dns_rbtnodechain_invalidate(&chain);
7816193149Sdougb				return (ISC_R_NOSPACE);
7817193149Sdougb			}
7818193149Sdougb			isc_buffer_add(text, n);
7819193149Sdougb		}
7820193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname, origin);
7821193149Sdougb		if (result == ISC_R_NOMORE)
7822193149Sdougb			break;
7823193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7824193149Sdougb			dns_rbtnodechain_invalidate(&chain);
7825193149Sdougb			return (result);
7826193149Sdougb		}
7827193149Sdougb	}
7828193149Sdougb
7829193149Sdougb	return (ISC_R_SUCCESS);
7830193149Sdougb}
7831193149Sdougb
7832193149Sdougbisc_result_t
7833193149Sdougbns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) {
7834193149Sdougb	isc_result_t result;
7835193149Sdougb	unsigned int n;
7836193149Sdougb	dns_view_t *view;
7837193149Sdougb	unsigned int foundkeys = 0;
7838193149Sdougb
7839193149Sdougb	result = isc_task_beginexclusive(server->task);
7840193149Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7841193149Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7842193149Sdougb	     view != NULL;
7843193149Sdougb	     view = ISC_LIST_NEXT(view, link)) {
7844193149Sdougb		RWLOCK(&view->statickeys->lock, isc_rwlocktype_read);
7845193149Sdougb		result = list_keynames(view, view->statickeys, text,
7846193149Sdougb				       &foundkeys);
7847193149Sdougb		RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read);
7848193149Sdougb		if (result != ISC_R_SUCCESS) {
7849193149Sdougb			isc_task_endexclusive(server->task);
7850193149Sdougb			return (result);
7851193149Sdougb		}
7852193149Sdougb		RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
7853193149Sdougb		result = list_keynames(view, view->dynamickeys, text,
7854193149Sdougb				       &foundkeys);
7855193149Sdougb		RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
7856193149Sdougb		if (result != ISC_R_SUCCESS) {
7857193149Sdougb			isc_task_endexclusive(server->task);
7858193149Sdougb			return (result);
7859193149Sdougb		}
7860193149Sdougb	}
7861193149Sdougb	isc_task_endexclusive(server->task);
7862193149Sdougb
7863193149Sdougb	if (foundkeys == 0) {
7864193149Sdougb		n = snprintf((char *)isc_buffer_used(text),
7865193149Sdougb			     isc_buffer_availablelength(text),
7866193149Sdougb			     "no tsig keys found.\n");
7867218384Sdougb		if (n >= isc_buffer_availablelength(text))
7868193149Sdougb			return (ISC_R_NOSPACE);
7869193149Sdougb		isc_buffer_add(text, n);
7870193149Sdougb	}
7871193149Sdougb
7872193149Sdougb	return (ISC_R_SUCCESS);
7873193149Sdougb}
7874193149Sdougb
7875135446Strhodes/*
7876224092Sdougb * Act on a "sign" or "loadkeys" command from the command channel.
7877224092Sdougb */
7878224092Sdougbisc_result_t
7879262706Serwinns_server_rekey(ns_server_t *server, char *args, isc_buffer_t *text) {
7880224092Sdougb	isc_result_t result;
7881224092Sdougb	dns_zone_t *zone = NULL;
7882224092Sdougb	dns_zonetype_t type;
7883224092Sdougb	isc_uint16_t keyopts;
7884224092Sdougb	isc_boolean_t fullsign = ISC_FALSE;
7885224092Sdougb
7886224092Sdougb	if (strncasecmp(args, NS_COMMAND_SIGN, strlen(NS_COMMAND_SIGN)) == 0)
7887224092Sdougb	    fullsign = ISC_TRUE;
7888224092Sdougb
7889262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
7890262706Serwin				text, ISC_TRUE);
7891224092Sdougb	if (result != ISC_R_SUCCESS)
7892224092Sdougb		return (result);
7893224092Sdougb	if (zone == NULL)
7894224092Sdougb		return (ISC_R_UNEXPECTEDEND);   /* XXX: or do all zones? */
7895224092Sdougb
7896224092Sdougb	type = dns_zone_gettype(zone);
7897224092Sdougb	if (type != dns_zone_master) {
7898224092Sdougb		dns_zone_detach(&zone);
7899224092Sdougb		return (DNS_R_NOTMASTER);
7900224092Sdougb	}
7901224092Sdougb
7902224092Sdougb	keyopts = dns_zone_getkeyopts(zone);
7903224092Sdougb
7904224092Sdougb	/* "rndc loadkeys" requires "auto-dnssec maintain". */
7905224092Sdougb	if ((keyopts & DNS_ZONEKEY_ALLOW) == 0)
7906224092Sdougb		result = ISC_R_NOPERM;
7907224092Sdougb	else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign)
7908224092Sdougb		result = ISC_R_NOPERM;
7909224092Sdougb	else
7910224092Sdougb		dns_zone_rekey(zone, fullsign);
7911224092Sdougb
7912224092Sdougb	dns_zone_detach(&zone);
7913224092Sdougb	return (result);
7914224092Sdougb}
7915224092Sdougb
7916224092Sdougb/*
7917254897Serwin * Act on a "sync" command from the command channel.
7918254897Serwin*/
7919254897Serwinstatic isc_result_t
7920254897Serwinsynczone(dns_zone_t *zone, void *uap) {
7921254897Serwin	isc_boolean_t cleanup = *(isc_boolean_t *)uap;
7922254897Serwin	isc_result_t result;
7923254897Serwin	dns_zone_t *raw = NULL;
7924254897Serwin	char *journal;
7925254897Serwin
7926254897Serwin	dns_zone_getraw(zone, &raw);
7927254897Serwin	if (raw != NULL) {
7928254897Serwin		synczone(raw, uap);
7929254897Serwin		dns_zone_detach(&raw);
7930254897Serwin	}
7931254897Serwin
7932254897Serwin	result = dns_zone_flush(zone);
7933254897Serwin	if (result != ISC_R_SUCCESS)
7934254897Serwin		cleanup = ISC_FALSE;
7935254897Serwin	if (cleanup) {
7936254897Serwin		journal = dns_zone_getjournal(zone);
7937254897Serwin		if (journal != NULL)
7938254897Serwin			(void)isc_file_remove(journal);
7939254897Serwin	}
7940254897Serwin
7941254897Serwin	return (result);
7942254897Serwin}
7943254897Serwin
7944254897Serwinisc_result_t
7945254897Serwinns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text) {
7946254897Serwin	isc_result_t result, tresult;
7947254897Serwin	dns_view_t *view;
7948254897Serwin	dns_zone_t *zone = NULL;
7949254897Serwin	char classstr[DNS_RDATACLASS_FORMATSIZE];
7950254897Serwin	char zonename[DNS_NAME_FORMATSIZE];
7951254897Serwin	const char *vname, *sep, *msg = NULL, *arg;
7952254897Serwin	isc_boolean_t cleanup = ISC_FALSE;
7953254897Serwin
7954254897Serwin	(void) next_token(&args, " \t");
7955254897Serwin
7956254897Serwin	arg = next_token(&args, " \t");
7957254897Serwin	if (arg != NULL &&
7958254897Serwin	    (strcmp(arg, "-clean") == 0 || strcmp(arg, "-clear") == 0)) {
7959254897Serwin		cleanup = ISC_TRUE;
7960254897Serwin		arg = next_token(&args, " \t");
7961254897Serwin	}
7962254897Serwin
7963262706Serwin	result = zone_from_args(server, args, arg, &zone, NULL,
7964262706Serwin				text, ISC_FALSE);
7965254897Serwin	if (result != ISC_R_SUCCESS)
7966254897Serwin		return (result);
7967254897Serwin
7968254897Serwin	if (zone == NULL) {
7969254897Serwin		result = isc_task_beginexclusive(server->task);
7970254897Serwin		RUNTIME_CHECK(result == ISC_R_SUCCESS);
7971254897Serwin		tresult = ISC_R_SUCCESS;
7972254897Serwin		for (view = ISC_LIST_HEAD(server->viewlist);
7973254897Serwin		     view != NULL;
7974254897Serwin		     view = ISC_LIST_NEXT(view, link)) {
7975254897Serwin			result = dns_zt_apply(view->zonetable, ISC_FALSE,
7976254897Serwin					      synczone, &cleanup);
7977254897Serwin			if (result != ISC_R_SUCCESS &&
7978254897Serwin			    tresult == ISC_R_SUCCESS)
7979254897Serwin				tresult = result;
7980254897Serwin		}
7981254897Serwin		isc_task_endexclusive(server->task);
7982254897Serwin		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7983254897Serwin			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7984254897Serwin			      "dumping all zones%s: %s",
7985254897Serwin			      cleanup ? ", removing journal files" : "",
7986254897Serwin			      isc_result_totext(result));
7987254897Serwin		return (tresult);
7988254897Serwin	}
7989254897Serwin
7990254897Serwin	result = isc_task_beginexclusive(server->task);
7991254897Serwin	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7992254897Serwin	result = synczone(zone, &cleanup);
7993254897Serwin	isc_task_endexclusive(server->task);
7994254897Serwin
7995254897Serwin	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
7996254897Serwin		isc_buffer_putmem(text, (const unsigned char *)msg,
7997254897Serwin				  strlen(msg) + 1);
7998254897Serwin
7999254897Serwin	view = dns_zone_getview(zone);
8000254897Serwin	if (strcmp(view->name, "_default") == 0 ||
8001254897Serwin	    strcmp(view->name, "_bind") == 0)
8002254897Serwin	{
8003254897Serwin		vname = "";
8004254897Serwin		sep = "";
8005254897Serwin	} else {
8006254897Serwin		vname = view->name;
8007254897Serwin		sep = " ";
8008254897Serwin	}
8009254897Serwin	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
8010254897Serwin			      sizeof(classstr));
8011254897Serwin	dns_name_format(dns_zone_getorigin(zone),
8012254897Serwin			zonename, sizeof(zonename));
8013254897Serwin	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8014254897Serwin		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8015254897Serwin		      "sync: dumping zone '%s/%s'%s%s%s: %s",
8016254897Serwin		      zonename, classstr, sep, vname,
8017254897Serwin		      cleanup ? ", removing journal file" : "",
8018254897Serwin		      isc_result_totext(result));
8019254897Serwin	dns_zone_detach(&zone);
8020254897Serwin	return (result);
8021254897Serwin}
8022254897Serwin
8023254897Serwin/*
8024170222Sdougb * Act on a "freeze" or "thaw" command from the command channel.
8025135446Strhodes */
8026135446Strhodesisc_result_t
8027204619Sdougbns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args,
8028204619Sdougb		 isc_buffer_t *text)
8029204619Sdougb{
8030170222Sdougb	isc_result_t result, tresult;
8031254897Serwin	dns_zone_t *zone = NULL, *raw = NULL;
8032135446Strhodes	dns_zonetype_t type;
8033135446Strhodes	char classstr[DNS_RDATACLASS_FORMATSIZE];
8034135446Strhodes	char zonename[DNS_NAME_FORMATSIZE];
8035135446Strhodes	dns_view_t *view;
8036135446Strhodes	const char *vname, *sep;
8037135446Strhodes	isc_boolean_t frozen;
8038204619Sdougb	const char *msg = NULL;
8039186462Sdougb
8040262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
8041262706Serwin				text, ISC_TRUE);
8042135446Strhodes	if (result != ISC_R_SUCCESS)
8043135446Strhodes		return (result);
8044170222Sdougb	if (zone == NULL) {
8045170222Sdougb		result = isc_task_beginexclusive(server->task);
8046170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
8047170222Sdougb		tresult = ISC_R_SUCCESS;
8048186462Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
8049170222Sdougb		     view != NULL;
8050170222Sdougb		     view = ISC_LIST_NEXT(view, link)) {
8051170222Sdougb			result = dns_view_freezezones(view, freeze);
8052170222Sdougb			if (result != ISC_R_SUCCESS &&
8053170222Sdougb			    tresult == ISC_R_SUCCESS)
8054170222Sdougb				tresult = result;
8055170222Sdougb		}
8056170222Sdougb		isc_task_endexclusive(server->task);
8057170222Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8058170222Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8059170222Sdougb			      "%s all zones: %s",
8060170222Sdougb			      freeze ? "freezing" : "thawing",
8061170222Sdougb			      isc_result_totext(tresult));
8062170222Sdougb		return (tresult);
8063170222Sdougb	}
8064254897Serwin	dns_zone_getraw(zone, &raw);
8065254897Serwin	if (raw != NULL) {
8066254897Serwin		dns_zone_detach(&zone);
8067254897Serwin		dns_zone_attach(raw, &zone);
8068254897Serwin		dns_zone_detach(&raw);
8069254897Serwin	}
8070135446Strhodes	type = dns_zone_gettype(zone);
8071135446Strhodes	if (type != dns_zone_master) {
8072135446Strhodes		dns_zone_detach(&zone);
8073224092Sdougb		return (DNS_R_NOTMASTER);
8074135446Strhodes	}
8075135446Strhodes
8076254897Serwin	if (freeze && !dns_zone_isdynamic(zone, ISC_TRUE)) {
8077254897Serwin		dns_zone_detach(&zone);
8078254897Serwin		return (DNS_R_NOTDYNAMIC);
8079254897Serwin	}
8080254897Serwin
8081204619Sdougb	result = isc_task_beginexclusive(server->task);
8082204619Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
8083135446Strhodes	frozen = dns_zone_getupdatedisabled(zone);
8084135446Strhodes	if (freeze) {
8085204619Sdougb		if (frozen) {
8086204619Sdougb			msg = "WARNING: The zone was already frozen.\n"
8087204619Sdougb			      "Someone else may be editing it or "
8088204619Sdougb			      "it may still be re-loading.";
8089135446Strhodes			result = DNS_R_FROZEN;
8090204619Sdougb		}
8091204619Sdougb		if (result == ISC_R_SUCCESS) {
8092135446Strhodes			result = dns_zone_flush(zone);
8093204619Sdougb			if (result != ISC_R_SUCCESS)
8094204619Sdougb				msg = "Flushing the zone updates to "
8095204619Sdougb				      "disk failed.";
8096204619Sdougb		}
8097204619Sdougb		if (result == ISC_R_SUCCESS)
8098204619Sdougb			dns_zone_setupdatedisabled(zone, freeze);
8099135446Strhodes	} else {
8100135446Strhodes		if (frozen) {
8101204619Sdougb			result = dns_zone_loadandthaw(zone);
8102204619Sdougb			switch (result) {
8103204619Sdougb			case ISC_R_SUCCESS:
8104204619Sdougb			case DNS_R_UPTODATE:
8105204619Sdougb				msg = "The zone reload and thaw was "
8106204619Sdougb				      "successful.";
8107135446Strhodes				result = ISC_R_SUCCESS;
8108204619Sdougb				break;
8109204619Sdougb			case DNS_R_CONTINUE:
8110204619Sdougb				msg = "A zone reload and thaw was started.\n"
8111204619Sdougb				      "Check the logs to see the result.";
8112204619Sdougb				result = ISC_R_SUCCESS;
8113204619Sdougb				break;
8114204619Sdougb			}
8115135446Strhodes		}
8116135446Strhodes	}
8117204619Sdougb	isc_task_endexclusive(server->task);
8118135446Strhodes
8119204619Sdougb	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
8120204619Sdougb		isc_buffer_putmem(text, (const unsigned char *)msg,
8121204619Sdougb				  strlen(msg) + 1);
8122204619Sdougb
8123135446Strhodes	view = dns_zone_getview(zone);
8124224092Sdougb	if (strcmp(view->name, "_default") == 0 ||
8125224092Sdougb	    strcmp(view->name, "_bind") == 0)
8126135446Strhodes	{
8127135446Strhodes		vname = "";
8128135446Strhodes		sep = "";
8129135446Strhodes	} else {
8130135446Strhodes		vname = view->name;
8131135446Strhodes		sep = " ";
8132135446Strhodes	}
8133135446Strhodes	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
8134135446Strhodes			      sizeof(classstr));
8135135446Strhodes	dns_name_format(dns_zone_getorigin(zone),
8136135446Strhodes			zonename, sizeof(zonename));
8137135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8138135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8139135446Strhodes		      "%s zone '%s/%s'%s%s: %s",
8140170222Sdougb		      freeze ? "freezing" : "thawing",
8141135446Strhodes		      zonename, classstr, sep, vname,
8142135446Strhodes		      isc_result_totext(result));
8143135446Strhodes	dns_zone_detach(&zone);
8144135446Strhodes	return (result);
8145135446Strhodes}
8146153816Sdougb
8147153816Sdougb#ifdef HAVE_LIBSCF
8148153816Sdougb/*
8149153816Sdougb * This function adds a message for rndc to echo if named
8150153816Sdougb * is managed by smf and is also running chroot.
8151153816Sdougb */
8152153816Sdougbisc_result_t
8153153816Sdougbns_smf_add_message(isc_buffer_t *text) {
8154153816Sdougb	unsigned int n;
8155153816Sdougb
8156153816Sdougb	n = snprintf((char *)isc_buffer_used(text),
8157153816Sdougb		isc_buffer_availablelength(text),
8158153816Sdougb		"use svcadm(1M) to manage named");
8159153816Sdougb	if (n >= isc_buffer_availablelength(text))
8160153816Sdougb		return (ISC_R_NOSPACE);
8161153816Sdougb	isc_buffer_add(text, n);
8162153816Sdougb	return (ISC_R_SUCCESS);
8163153816Sdougb}
8164153816Sdougb#endif /* HAVE_LIBSCF */
8165224092Sdougb
8166224092Sdougb/*
8167262706Serwin * Emit a comment at the top of the nzf file containing the viewname
8168262706Serwin * Expects the fp to already be open for writing
8169262706Serwin */
8170262706Serwin#define HEADER1 "# New zone file for view: "
8171262706Serwin#define HEADER2 "\n# This file contains configuration for zones added by\n" \
8172262706Serwin		"# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n"
8173262706Serwinisc_result_t
8174262706Serwinadd_comment(FILE *fp, const char *viewname) {
8175262706Serwin	isc_result_t result;
8176262706Serwin	CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL));
8177262706Serwin	CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL));
8178262706Serwin	CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL));
8179262706Serwin cleanup:
8180262706Serwin	return (result);
8181262706Serwin}
8182262706Serwin
8183262706Serwin/*
8184224092Sdougb * Act on an "addzone" command from the command channel.
8185224092Sdougb */
8186224092Sdougbisc_result_t
8187224092Sdougbns_server_add_zone(ns_server_t *server, char *args) {
8188224092Sdougb	isc_result_t	     result;
8189224092Sdougb	isc_buffer_t	     argbuf;
8190224092Sdougb	size_t		     arglen;
8191224092Sdougb	cfg_parser_t	    *parser = NULL;
8192224092Sdougb	cfg_obj_t	    *config = NULL;
8193224092Sdougb	const cfg_obj_t	    *vconfig = NULL;
8194224092Sdougb	const cfg_obj_t	    *views = NULL;
8195224092Sdougb	const cfg_obj_t     *parms = NULL;
8196224092Sdougb	const cfg_obj_t     *obj = NULL;
8197224092Sdougb	const cfg_listelt_t *element;
8198224092Sdougb	const char	    *zonename;
8199224092Sdougb	const char	    *classname = NULL;
8200224092Sdougb	const char	    *argp;
8201224092Sdougb	const char	    *viewname = NULL;
8202224092Sdougb	dns_rdataclass_t     rdclass;
8203224092Sdougb	dns_view_t	    *view = 0;
8204262706Serwin	isc_buffer_t	     buf;
8205262706Serwin	dns_fixedname_t	     fname;
8206262706Serwin	dns_name_t	    *dnsname;
8207224092Sdougb	dns_zone_t	    *zone = NULL;
8208224092Sdougb	FILE		    *fp = NULL;
8209224092Sdougb	struct cfg_context  *cfg = NULL;
8210262706Serwin	char 		    namebuf[DNS_NAME_FORMATSIZE];
8211262706Serwin	off_t		    offset;
8212224092Sdougb
8213224092Sdougb	/* Try to parse the argument string */
8214224092Sdougb	arglen = strlen(args);
8215262706Serwin	isc_buffer_init(&argbuf, args, (unsigned int)arglen);
8216224092Sdougb	isc_buffer_add(&argbuf, strlen(args));
8217224092Sdougb	CHECK(cfg_parser_create(server->mctx, ns_g_lctx, &parser));
8218224092Sdougb	CHECK(cfg_parse_buffer(parser, &argbuf, &cfg_type_addzoneconf,
8219224092Sdougb			       &config));
8220224092Sdougb	CHECK(cfg_map_get(config, "addzone", &parms));
8221224092Sdougb
8222224092Sdougb	zonename = cfg_obj_asstring(cfg_tuple_get(parms, "name"));
8223254402Serwin	isc_buffer_constinit(&buf, zonename, strlen(zonename));
8224224092Sdougb	isc_buffer_add(&buf, strlen(zonename));
8225224092Sdougb
8226262706Serwin	dns_fixedname_init(&fname);
8227262706Serwin	dnsname = dns_fixedname_name(&fname);
8228262706Serwin	CHECK(dns_name_fromtext(dnsname, &buf, dns_rootname, ISC_FALSE, NULL));
8229262706Serwin
8230224092Sdougb	/* Make sense of optional class argument */
8231224092Sdougb	obj = cfg_tuple_get(parms, "class");
8232224092Sdougb	CHECK(ns_config_getclass(obj, dns_rdataclass_in, &rdclass));
8233224092Sdougb	if (rdclass != dns_rdataclass_in && obj)
8234224092Sdougb		classname = cfg_obj_asstring(obj);
8235224092Sdougb
8236224092Sdougb	/* Make sense of optional view argument */
8237224092Sdougb	obj = cfg_tuple_get(parms, "view");
8238224092Sdougb	if (obj && cfg_obj_isstring(obj))
8239224092Sdougb		viewname = cfg_obj_asstring(obj);
8240224092Sdougb	if (viewname == NULL || *viewname == '\0')
8241224092Sdougb		viewname = "_default";
8242224092Sdougb	CHECK(dns_viewlist_find(&server->viewlist, viewname, rdclass, &view));
8243224092Sdougb
8244224092Sdougb	/* Are we accepting new zones? */
8245224092Sdougb	if (view->new_zone_file == NULL) {
8246224092Sdougb		result = ISC_R_NOPERM;
8247224092Sdougb		goto cleanup;
8248224092Sdougb	}
8249224092Sdougb
8250224092Sdougb	cfg = (struct cfg_context *) view->new_zone_config;
8251224092Sdougb	if (cfg == NULL) {
8252224092Sdougb		result = ISC_R_FAILURE;
8253224092Sdougb		goto cleanup;
8254224092Sdougb	}
8255224092Sdougb
8256224092Sdougb	/* Zone shouldn't already exist */
8257262706Serwin	result = dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone);
8258224092Sdougb	if (result == ISC_R_SUCCESS) {
8259224092Sdougb		result = ISC_R_EXISTS;
8260224092Sdougb		goto cleanup;
8261224092Sdougb	} else if (result == DNS_R_PARTIALMATCH) {
8262224092Sdougb		/* Create our sub-zone anyway */
8263224092Sdougb		dns_zone_detach(&zone);
8264224092Sdougb		zone = NULL;
8265224092Sdougb	}
8266224092Sdougb	else if (result != ISC_R_NOTFOUND)
8267224092Sdougb		goto cleanup;
8268224092Sdougb
8269224092Sdougb	/* Find the view statement */
8270224092Sdougb	cfg_map_get(cfg->config, "view", &views);
8271224092Sdougb	for (element = cfg_list_first(views);
8272224092Sdougb	     element != NULL;
8273224092Sdougb	     element = cfg_list_next(element))
8274224092Sdougb	{
8275224092Sdougb		const char *vname;
8276224092Sdougb		vconfig = cfg_listelt_value(element);
8277224092Sdougb		vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
8278224092Sdougb		if (vname && !strcasecmp(vname, viewname))
8279224092Sdougb			break;
8280224092Sdougb		vconfig = NULL;
8281224092Sdougb	}
8282224092Sdougb
8283224092Sdougb	/* Open save file for write configuration */
8284224092Sdougb	CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
8285262706Serwin	CHECK(isc_stdio_tell(fp, &offset));
8286262706Serwin	if (offset == 0)
8287262706Serwin		CHECK(add_comment(fp, view->name));
8288224092Sdougb
8289224092Sdougb	/* Mark view unfrozen so that zone can be added */
8290254402Serwin	result = isc_task_beginexclusive(server->task);
8291254402Serwin	RUNTIME_CHECK(result == ISC_R_SUCCESS);
8292224092Sdougb	dns_view_thaw(view);
8293224092Sdougb	result = configure_zone(cfg->config, parms, vconfig,
8294225361Sdougb				server->mctx, view, cfg->actx, ISC_FALSE);
8295224092Sdougb	dns_view_freeze(view);
8296234010Sdougb	isc_task_endexclusive(server->task);
8297234010Sdougb	if (result != ISC_R_SUCCESS)
8298224092Sdougb		goto cleanup;
8299224092Sdougb
8300224092Sdougb	/* Is it there yet? */
8301262706Serwin	CHECK(dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone));
8302224092Sdougb
8303224092Sdougb	/*
8304224092Sdougb	 * Load the zone from the master file.  If this fails, we'll
8305224092Sdougb	 * need to undo the configuration we've done already.
8306224092Sdougb	 */
8307224092Sdougb	result = dns_zone_loadnew(zone);
8308224092Sdougb	if (result != ISC_R_SUCCESS) {
8309224092Sdougb		dns_db_t *dbp = NULL;
8310224092Sdougb
8311224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8312224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8313224092Sdougb			      "addzone failed; reverting.");
8314224092Sdougb
8315224092Sdougb		/* If the zone loaded partially, unload it */
8316224092Sdougb		if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
8317224092Sdougb			dns_db_detach(&dbp);
8318224092Sdougb			dns_zone_unload(zone);
8319224092Sdougb		}
8320224092Sdougb
8321224092Sdougb		/* Remove the zone from the zone table */
8322224092Sdougb		dns_zt_unmount(view->zonetable, zone);
8323224092Sdougb		goto cleanup;
8324224092Sdougb	}
8325224092Sdougb
8326224092Sdougb	/* Flag the zone as having been added at runtime */
8327224092Sdougb	dns_zone_setadded(zone, ISC_TRUE);
8328224092Sdougb
8329262706Serwin	/* Emit the zone name, quoted and escaped */
8330262706Serwin	isc_buffer_init(&buf, namebuf, sizeof(namebuf));
8331262706Serwin	CHECK(dns_name_totext(dnsname, ISC_TRUE, &buf));
8332262706Serwin	isc_buffer_putuint8(&buf, 0);
8333262706Serwin	CHECK(isc_stdio_write("zone \"", 6, 1, fp, NULL));
8334262706Serwin	CHECK(isc_stdio_write(namebuf, strlen(namebuf), 1, fp, NULL));
8335262706Serwin	CHECK(isc_stdio_write("\" ", 2, 1, fp, NULL));
8336224092Sdougb
8337224092Sdougb	/* Classname, if not default */
8338224092Sdougb	if (classname != NULL && *classname != '\0') {
8339224092Sdougb		CHECK(isc_stdio_write(classname, strlen(classname), 1, fp,
8340224092Sdougb				      NULL));
8341224092Sdougb		CHECK(isc_stdio_write(" ", 1, 1, fp, NULL));
8342224092Sdougb	}
8343224092Sdougb
8344224092Sdougb	/* Find beginning of option block from args */
8345224092Sdougb	for (argp = args; *argp; argp++, arglen--) {
8346224092Sdougb		if (*argp == '{') {	/* Assume matching '}' */
8347224092Sdougb			/* Add that to our file */
8348224092Sdougb			CHECK(isc_stdio_write(argp, arglen, 1, fp, NULL));
8349224092Sdougb
8350224092Sdougb			/* Make sure we end with a LF */
8351224092Sdougb			if (argp[arglen-1] != '\n') {
8352224092Sdougb				CHECK(isc_stdio_write("\n", 1, 1, fp, NULL));
8353224092Sdougb			}
8354224092Sdougb			break;
8355224092Sdougb		}
8356224092Sdougb	}
8357224092Sdougb
8358224092Sdougb	CHECK(isc_stdio_close(fp));
8359224092Sdougb	fp = NULL;
8360224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8361224092Sdougb				  NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8362224092Sdougb				  "zone %s added to view %s via addzone",
8363224092Sdougb				  zonename, viewname);
8364224092Sdougb
8365224092Sdougb	result = ISC_R_SUCCESS;
8366224092Sdougb
8367224092Sdougb cleanup:
8368224092Sdougb	if (fp != NULL)
8369224092Sdougb		isc_stdio_close(fp);
8370224092Sdougb	if (parser != NULL) {
8371224092Sdougb		if (config != NULL)
8372224092Sdougb			cfg_obj_destroy(parser, &config);
8373224092Sdougb		cfg_parser_destroy(&parser);
8374224092Sdougb	}
8375224092Sdougb	if (zone != NULL)
8376224092Sdougb		dns_zone_detach(&zone);
8377224092Sdougb	if (view != NULL)
8378224092Sdougb		dns_view_detach(&view);
8379224092Sdougb
8380224092Sdougb	return (result);
8381224092Sdougb}
8382224092Sdougb
8383224092Sdougb/*
8384224092Sdougb * Act on a "delzone" command from the command channel.
8385224092Sdougb */
8386224092Sdougbisc_result_t
8387262706Serwinns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t *text) {
8388262706Serwin	isc_result_t result;
8389262706Serwin	dns_zone_t *zone = NULL;
8390262706Serwin	dns_view_t *view = NULL;
8391262706Serwin	dns_db_t *dbp = NULL;
8392262706Serwin	const char *filename = NULL;
8393262706Serwin	char *tmpname = NULL;
8394262706Serwin	char buf[1024];
8395262706Serwin	const char *zonename = NULL;
8396262706Serwin	size_t znamelen = 0;
8397262706Serwin	FILE *ifp = NULL, *ofp = NULL;
8398262706Serwin	isc_boolean_t inheader = ISC_TRUE;
8399224092Sdougb
8400224092Sdougb	/* Parse parameters */
8401262706Serwin	CHECK(zone_from_args(server, args, NULL, &zone, &zonename,
8402262706Serwin			     text, ISC_TRUE));
8403254402Serwin
8404224092Sdougb	if (zone == NULL) {
8405224092Sdougb		result = ISC_R_UNEXPECTEDEND;
8406224092Sdougb		goto cleanup;
8407224092Sdougb	}
8408224092Sdougb
8409224092Sdougb	/*
8410224092Sdougb	 * Was this zone originally added at runtime?
8411224092Sdougb	 * If not, we can't delete it now.
8412224092Sdougb	 */
8413224092Sdougb	if (!dns_zone_getadded(zone)) {
8414224092Sdougb		result = ISC_R_NOPERM;
8415224092Sdougb		goto cleanup;
8416224092Sdougb	}
8417224092Sdougb
8418254402Serwin	INSIST(zonename != NULL);
8419254402Serwin	znamelen = strlen(zonename);
8420224092Sdougb
8421224092Sdougb	/* Dig out configuration for this zone */
8422224092Sdougb	view = dns_zone_getview(zone);
8423224092Sdougb	filename = view->new_zone_file;
8424224092Sdougb	if (filename == NULL) {
8425224092Sdougb		/* No adding zones in this view */
8426224092Sdougb		result = ISC_R_FAILURE;
8427224092Sdougb		goto cleanup;
8428224092Sdougb	}
8429224092Sdougb
8430224092Sdougb	/* Rewrite zone list */
8431224092Sdougb	result = isc_stdio_open(filename, "r", &ifp);
8432224092Sdougb	if (ifp != NULL && result == ISC_R_SUCCESS) {
8433224092Sdougb		char *found = NULL, *p = NULL;
8434224092Sdougb		size_t n;
8435224092Sdougb
8436224092Sdougb		/* Create a temporary file */
8437224092Sdougb		CHECK(isc_string_printf(buf, 1023, "%s.%ld", filename,
8438224092Sdougb					(long)getpid()));
8439224092Sdougb		if (!(tmpname = isc_mem_strdup(server->mctx, buf))) {
8440224092Sdougb			result = ISC_R_NOMEMORY;
8441224092Sdougb			goto cleanup;
8442224092Sdougb		}
8443224092Sdougb		CHECK(isc_stdio_open(tmpname, "w", &ofp));
8444262706Serwin		CHECK(add_comment(ofp, view->name));
8445224092Sdougb
8446224092Sdougb		/* Look for the entry for that zone */
8447224092Sdougb		while (fgets(buf, 1024, ifp)) {
8448262706Serwin			/* Skip initial comment, if any */
8449262706Serwin			if (inheader && *buf == '#')
8450262706Serwin				continue;
8451262706Serwin			if (*buf != '#')
8452262706Serwin				inheader = ISC_FALSE;
8453262706Serwin
8454262706Serwin			/*
8455262706Serwin			 * Any other lines not starting with zone, copy
8456262706Serwin			 * them out and continue.
8457262706Serwin			 */
8458262706Serwin			if (strncasecmp(buf, "zone", 4) != 0) {
8459224092Sdougb				fputs(buf, ofp);
8460224092Sdougb				continue;
8461224092Sdougb			}
8462224092Sdougb			p = buf+4;
8463224092Sdougb
8464262706Serwin			/* This is a zone; find its name. */
8465224092Sdougb			while (*p &&
8466224092Sdougb			       ((*p == '"') || isspace((unsigned char)*p)))
8467224092Sdougb				p++;
8468224092Sdougb
8469262706Serwin			/*
8470262706Serwin			 * If it's not the zone we're looking for, copy
8471262706Serwin			 * it out and continue
8472262706Serwin			 */
8473262706Serwin			if (strncasecmp(p, zonename, znamelen) != 0) {
8474224092Sdougb				fputs(buf, ofp);
8475224092Sdougb				continue;
8476224092Sdougb			}
8477224092Sdougb
8478262706Serwin			/*
8479262706Serwin			 * But if it is the zone we want, skip over it
8480262706Serwin			 * so it will be omitted from the new file
8481262706Serwin			 */
8482224092Sdougb			p += znamelen;
8483224092Sdougb			if (isspace((unsigned char)*p) ||
8484224092Sdougb			    *p == '"' || *p == '{') {
8485224092Sdougb				/* This must be the entry */
8486224092Sdougb				found = p;
8487224092Sdougb				break;
8488224092Sdougb			}
8489224092Sdougb
8490262706Serwin			/* Copy the rest of the buffer out and continue */
8491224092Sdougb			fputs(buf, ofp);
8492224092Sdougb		}
8493224092Sdougb
8494224092Sdougb		/* Skip over an option block (matching # of braces) */
8495224092Sdougb		if (found) {
8496224092Sdougb			int obrace = 0, cbrace = 0;
8497224092Sdougb			for (;;) {
8498224092Sdougb				while (*p) {
8499224092Sdougb					if (*p == '{') obrace++;
8500224092Sdougb					if (*p == '}') cbrace++;
8501224092Sdougb					p++;
8502224092Sdougb				}
8503224092Sdougb				if (obrace && (obrace == cbrace))
8504224092Sdougb					break;
8505224092Sdougb				if (!fgets(buf, 1024, ifp))
8506224092Sdougb					break;
8507224092Sdougb				p = buf;
8508224092Sdougb			}
8509224092Sdougb
8510224092Sdougb			/* Just spool the remainder of the file out */
8511224092Sdougb			result = isc_stdio_read(buf, 1, 1024, ifp, &n);
8512224092Sdougb			while (n > 0U) {
8513224092Sdougb				if (result == ISC_R_EOF)
8514224092Sdougb					result = ISC_R_SUCCESS;
8515224092Sdougb				CHECK(result);
8516224092Sdougb				isc_stdio_write(buf, 1, n, ofp, NULL);
8517224092Sdougb				result = isc_stdio_read(buf, 1, 1024, ifp, &n);
8518224092Sdougb			}
8519224092Sdougb
8520224092Sdougb			/* Move temporary into place */
8521224092Sdougb			CHECK(isc_file_rename(tmpname, view->new_zone_file));
8522224092Sdougb		} else {
8523224092Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8524224092Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
8525224092Sdougb				      "deleted zone %s was missing from "
8526224092Sdougb				      "new zone file", zonename);
8527224092Sdougb			goto cleanup;
8528224092Sdougb		}
8529224092Sdougb	}
8530224092Sdougb
8531224092Sdougb	/* Stop answering for this zone */
8532224092Sdougb	if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
8533224092Sdougb		dns_db_detach(&dbp);
8534224092Sdougb		dns_zone_unload(zone);
8535224092Sdougb	}
8536224092Sdougb
8537224092Sdougb	CHECK(dns_zt_unmount(view->zonetable, zone));
8538224092Sdougb
8539224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8540224092Sdougb				  NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8541224092Sdougb				  "zone %s removed via delzone", zonename);
8542224092Sdougb
8543224092Sdougb	result = ISC_R_SUCCESS;
8544224092Sdougb
8545224092Sdougb cleanup:
8546224092Sdougb	if (ifp != NULL)
8547224092Sdougb		isc_stdio_close(ifp);
8548224092Sdougb	if (ofp != NULL) {
8549224092Sdougb		isc_stdio_close(ofp);
8550224092Sdougb		isc_file_remove(tmpname);
8551224092Sdougb	}
8552224092Sdougb	if (tmpname != NULL)
8553224092Sdougb		isc_mem_free(server->mctx, tmpname);
8554224092Sdougb	if (zone != NULL)
8555224092Sdougb		dns_zone_detach(&zone);
8556224092Sdougb
8557224092Sdougb	return (result);
8558224092Sdougb}
8559224092Sdougb
8560224092Sdougbstatic void
8561225361Sdougbnewzone_cfgctx_destroy(void **cfgp) {
8562224092Sdougb	struct cfg_context *cfg;
8563224092Sdougb
8564224092Sdougb	REQUIRE(cfgp != NULL && *cfgp != NULL);
8565225361Sdougb
8566224092Sdougb	cfg = *cfgp;
8567224092Sdougb
8568225361Sdougb	if (cfg->actx != NULL)
8569225361Sdougb		cfg_aclconfctx_detach(&cfg->actx);
8570225361Sdougb
8571224092Sdougb	if (cfg->parser != NULL) {
8572224092Sdougb		if (cfg->config != NULL)
8573224092Sdougb			cfg_obj_destroy(cfg->parser, &cfg->config);
8574224092Sdougb		cfg_parser_destroy(&cfg->parser);
8575224092Sdougb	}
8576225361Sdougb	if (cfg->nzparser != NULL) {
8577225361Sdougb		if (cfg->nzconfig != NULL)
8578225361Sdougb			cfg_obj_destroy(cfg->nzparser, &cfg->nzconfig);
8579225361Sdougb		cfg_parser_destroy(&cfg->nzparser);
8580225361Sdougb	}
8581224092Sdougb
8582225361Sdougb	isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg));
8583224092Sdougb	*cfgp = NULL;
8584224092Sdougb}
8585254897Serwin
8586254897Serwinisc_result_t
8587254897Serwinns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) {
8588254897Serwin	isc_result_t result = ISC_R_SUCCESS;
8589254897Serwin	dns_zone_t *zone = NULL;
8590254897Serwin	dns_name_t *origin;
8591254897Serwin	dns_db_t *db = NULL;
8592254897Serwin	dns_dbnode_t *node = NULL;
8593254897Serwin	dns_dbversion_t *version = NULL;
8594254897Serwin	dns_rdatatype_t privatetype;
8595254897Serwin	dns_rdataset_t privset;
8596254897Serwin	isc_boolean_t first = ISC_TRUE;
8597254897Serwin	isc_boolean_t list = ISC_FALSE, clear = ISC_FALSE;
8598254897Serwin	isc_boolean_t chain = ISC_FALSE;
8599254897Serwin	char keystr[DNS_SECALG_FORMATSIZE + 7];
8600254897Serwin	unsigned short hash = 0, flags = 0, iter = 0, saltlen = 0;
8601254897Serwin	unsigned char salt[255];
8602254897Serwin	const char *ptr;
8603254897Serwin	size_t n;
8604254897Serwin
8605254897Serwin	dns_rdataset_init(&privset);
8606254897Serwin
8607254897Serwin	/* Skip the command name. */
8608254897Serwin	ptr = next_token(&args, " \t");
8609254897Serwin	if (ptr == NULL)
8610254897Serwin		return (ISC_R_UNEXPECTEDEND);
8611254897Serwin
8612254897Serwin	/* Find out what we are to do. */
8613254897Serwin	ptr = next_token(&args, " \t");
8614254897Serwin	if (ptr == NULL)
8615254897Serwin		return (ISC_R_UNEXPECTEDEND);
8616254897Serwin
8617254897Serwin	if (strcasecmp(ptr, "-list") == 0)
8618254897Serwin		list = ISC_TRUE;
8619254897Serwin	else if ((strcasecmp(ptr, "-clear") == 0)  ||
8620254897Serwin		 (strcasecmp(ptr, "-clean") == 0)) {
8621254897Serwin		clear = ISC_TRUE;
8622254897Serwin		ptr = next_token(&args, " \t");
8623254897Serwin		if (ptr == NULL)
8624254897Serwin			return (ISC_R_UNEXPECTEDEND);
8625262706Serwin		memmove(keystr, ptr, sizeof(keystr));
8626262706Serwin	} else if (strcasecmp(ptr, "-nsec3param") == 0) {
8627254897Serwin		const char *hashstr, *flagstr, *iterstr;
8628254897Serwin		char nbuf[512];
8629254897Serwin
8630254897Serwin		chain = ISC_TRUE;
8631254897Serwin		hashstr = next_token(&args, " \t");
8632254897Serwin		if (hashstr == NULL)
8633254897Serwin			return (ISC_R_UNEXPECTEDEND);
8634254897Serwin
8635254897Serwin		if (strcasecmp(hashstr, "none") == 0)
8636254897Serwin			hash = 0;
8637254897Serwin		else {
8638254897Serwin			flagstr = next_token(&args, " \t");
8639254897Serwin			iterstr = next_token(&args, " \t");
8640254897Serwin			if (flagstr == NULL || iterstr == NULL)
8641254897Serwin				return (ISC_R_UNEXPECTEDEND);
8642254897Serwin
8643254897Serwin			n = snprintf(nbuf, sizeof(nbuf), "%s %s %s",
8644254897Serwin				     hashstr, flagstr, iterstr);
8645254897Serwin			if (n == sizeof(nbuf))
8646254897Serwin				return (ISC_R_NOSPACE);
8647254897Serwin			n = sscanf(nbuf, "%hu %hu %hu", &hash, &flags, &iter);
8648254897Serwin			if (n != 3U)
8649254897Serwin				return (ISC_R_BADNUMBER);
8650254897Serwin
8651254897Serwin			if (hash > 0xffU || flags > 0xffU)
8652254897Serwin				return (ISC_R_RANGE);
8653254897Serwin
8654254897Serwin			ptr = next_token(&args, " \t");
8655254897Serwin			if (ptr == NULL)
8656254897Serwin				return (ISC_R_UNEXPECTEDEND);
8657254897Serwin			if (strcmp(ptr, "-") != 0) {
8658254897Serwin				isc_buffer_t buf;
8659254897Serwin
8660254897Serwin				isc_buffer_init(&buf, salt, sizeof(salt));
8661254897Serwin				CHECK(isc_hex_decodestring(ptr, &buf));
8662254897Serwin				saltlen = isc_buffer_usedlength(&buf);
8663254897Serwin			}
8664254897Serwin		}
8665254897Serwin	} else
8666254897Serwin		CHECK(DNS_R_SYNTAX);
8667254897Serwin
8668262706Serwin	CHECK(zone_from_args(server, args, NULL, &zone, NULL,
8669262706Serwin			     text, ISC_FALSE));
8670254897Serwin	if (zone == NULL)
8671254897Serwin		CHECK(ISC_R_UNEXPECTEDEND);
8672254897Serwin
8673254897Serwin	if (clear) {
8674254897Serwin		CHECK(dns_zone_keydone(zone, keystr));
8675254897Serwin		isc_buffer_putstr(text, "request queued");
8676254897Serwin		isc_buffer_putuint8(text, 0);
8677254897Serwin	} else if (chain) {
8678254897Serwin		CHECK(dns_zone_setnsec3param(zone, (isc_uint8_t)hash,
8679254897Serwin					     (isc_uint8_t)flags, iter,
8680254897Serwin					     (isc_uint8_t)saltlen, salt,
8681254897Serwin					     ISC_TRUE));
8682254897Serwin		isc_buffer_putstr(text, "request queued");
8683254897Serwin		isc_buffer_putuint8(text, 0);
8684254897Serwin	} else if (list) {
8685254897Serwin		privatetype = dns_zone_getprivatetype(zone);
8686254897Serwin		origin = dns_zone_getorigin(zone);
8687254897Serwin		CHECK(dns_zone_getdb(zone, &db));
8688254897Serwin		CHECK(dns_db_findnode(db, origin, ISC_FALSE, &node));
8689254897Serwin		dns_db_currentversion(db, &version);
8690254897Serwin
8691254897Serwin		result = dns_db_findrdataset(db, node, version, privatetype,
8692254897Serwin					     dns_rdatatype_none, 0,
8693254897Serwin					     &privset, NULL);
8694254897Serwin		if (result == ISC_R_NOTFOUND) {
8695254897Serwin			isc_buffer_putstr(text, "No signing records found");
8696254897Serwin			isc_buffer_putuint8(text, 0);
8697254897Serwin			result = ISC_R_SUCCESS;
8698254897Serwin			goto cleanup;
8699254897Serwin		}
8700254897Serwin
8701254897Serwin		for (result = dns_rdataset_first(&privset);
8702254897Serwin		     result == ISC_R_SUCCESS;
8703254897Serwin		     result = dns_rdataset_next(&privset))
8704254897Serwin		{
8705254897Serwin			dns_rdata_t priv = DNS_RDATA_INIT;
8706254897Serwin			char output[BUFSIZ];
8707254897Serwin			isc_buffer_t buf;
8708254897Serwin
8709254897Serwin			dns_rdataset_current(&privset, &priv);
8710254897Serwin
8711254897Serwin			isc_buffer_init(&buf, output, sizeof(output));
8712254897Serwin			CHECK(dns_private_totext(&priv, &buf));
8713254897Serwin
8714254897Serwin			if (!first)
8715254897Serwin				isc_buffer_putstr(text, "\n");
8716254897Serwin			first = ISC_FALSE;
8717254897Serwin
8718254897Serwin			n = snprintf((char *)isc_buffer_used(text),
8719254897Serwin				     isc_buffer_availablelength(text),
8720254897Serwin				     "%s", output);
8721254897Serwin			if (n >= isc_buffer_availablelength(text))
8722254897Serwin				CHECK(ISC_R_NOSPACE);
8723254897Serwin
8724262706Serwin			isc_buffer_add(text, (unsigned int)n);
8725254897Serwin		}
8726262706Serwin		if (!first && isc_buffer_availablelength(text) > 0)
8727262706Serwin			isc_buffer_putuint8(text, 0);
8728254897Serwin
8729254897Serwin		if (result == ISC_R_NOMORE)
8730254897Serwin			result = ISC_R_SUCCESS;
8731254897Serwin	}
8732254897Serwin
8733254897Serwin cleanup:
8734254897Serwin	if (dns_rdataset_isassociated(&privset))
8735254897Serwin		dns_rdataset_disassociate(&privset);
8736254897Serwin	if (node != NULL)
8737254897Serwin		dns_db_detachnode(db, &node);
8738254897Serwin	if (version != NULL)
8739254897Serwin		dns_db_closeversion(db, &version, ISC_FALSE);
8740254897Serwin	if (db != NULL)
8741254897Serwin		dns_db_detach(&db);
8742254897Serwin	if (zone != NULL)
8743254897Serwin		dns_zone_detach(&zone);
8744254897Serwin
8745254897Serwin	return (result);
8746254897Serwin}
8747262706Serwin
8748262706Serwinstatic isc_result_t
8749262706Serwinputstr(isc_buffer_t *b, const char *str) {
8750262706Serwin	size_t l = strlen(str);
8751262706Serwin
8752262706Serwin	/*
8753262706Serwin	 * Use >= to leave space for NUL termination.
8754262706Serwin	 */
8755262706Serwin	if (l >= isc_buffer_availablelength(b))
8756262706Serwin		return (ISC_R_NOSPACE);
8757262706Serwin
8758262706Serwin	isc_buffer_putmem(b, (const unsigned char *)str, l);
8759262706Serwin	return (ISC_R_SUCCESS);
8760262706Serwin}
8761