server.c revision 262706
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
3144224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
3145135446Strhodes	obj = NULL;
3146224092Sdougb	result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
3147224092Sdougb	INSIST(result == ISC_R_SUCCESS);
3148224092Sdougb	if (cfg_obj_isboolean(obj)) {
3149224092Sdougb		if (cfg_obj_asboolean(obj))
3150224092Sdougb			view->v4_aaaa = dns_v4_aaaa_filter;
3151224092Sdougb		else
3152224092Sdougb			view->v4_aaaa = dns_v4_aaaa_ok;
3153224092Sdougb	} else {
3154224092Sdougb		const char *v4_aaaastr = cfg_obj_asstring(obj);
3155224092Sdougb		if (strcasecmp(v4_aaaastr, "break-dnssec") == 0)
3156224092Sdougb			view->v4_aaaa = dns_v4_aaaa_break_dnssec;
3157224092Sdougb		else
3158224092Sdougb			INSIST(0);
3159224092Sdougb	}
3160224092Sdougb	CHECK(configure_view_acl(vconfig, config, "filter-aaaa", NULL,
3161224092Sdougb				 actx, ns_g_mctx, &view->v4_aaaa_acl));
3162224092Sdougb#endif
3163224092Sdougb
3164224092Sdougb	obj = NULL;
3165135446Strhodes	result = ns_config_get(maps, "dnssec-enable", &obj);
3166135446Strhodes	INSIST(result == ISC_R_SUCCESS);
3167135446Strhodes	view->enablednssec = cfg_obj_asboolean(obj);
3168135446Strhodes
3169135446Strhodes	obj = NULL;
3170224092Sdougb	result = ns_config_get(optionmaps, "dnssec-lookaside", &obj);
3171135446Strhodes	if (result == ISC_R_SUCCESS) {
3172224092Sdougb		/* If set to "auto", use the version from the defaults */
3173224092Sdougb		const cfg_obj_t *dlvobj;
3174234010Sdougb		const char *dom;
3175224092Sdougb		dlvobj = cfg_listelt_value(cfg_list_first(obj));
3176234010Sdougb		dom = cfg_obj_asstring(cfg_tuple_get(dlvobj, "domain"));
3177234010Sdougb		if (cfg_obj_isvoid(cfg_tuple_get(dlvobj, "trust-anchor"))) {
3178234010Sdougb			/* If "no", skip; if "auto", use global default */
3179234010Sdougb			if (!strcasecmp(dom, "no"))
3180234010Sdougb				result = ISC_R_NOTFOUND;
3181234010Sdougb			else if (!strcasecmp(dom, "auto")) {
3182234010Sdougb				auto_dlv = ISC_TRUE;
3183234010Sdougb				obj = NULL;
3184234010Sdougb				result = cfg_map_get(ns_g_defaults,
3185234010Sdougb						     "dnssec-lookaside", &obj);
3186234010Sdougb			}
3187224092Sdougb		}
3188224092Sdougb	}
3189224092Sdougb
3190224092Sdougb	if (result == ISC_R_SUCCESS) {
3191135446Strhodes		for (element = cfg_list_first(obj);
3192135446Strhodes		     element != NULL;
3193135446Strhodes		     element = cfg_list_next(element))
3194135446Strhodes		{
3195135446Strhodes			const char *str;
3196135446Strhodes			isc_buffer_t b;
3197135446Strhodes			dns_name_t *dlv;
3198135446Strhodes
3199135446Strhodes			obj = cfg_listelt_value(element);
3200135446Strhodes			str = cfg_obj_asstring(cfg_tuple_get(obj,
3201135446Strhodes							     "trust-anchor"));
3202254402Serwin			isc_buffer_constinit(&b, str, strlen(str));
3203135446Strhodes			isc_buffer_add(&b, strlen(str));
3204135446Strhodes			dlv = dns_fixedname_name(&view->dlv_fixed);
3205135446Strhodes			CHECK(dns_name_fromtext(dlv, &b, dns_rootname,
3206224092Sdougb						DNS_NAME_DOWNCASE, NULL));
3207135446Strhodes			view->dlv = dns_fixedname_name(&view->dlv_fixed);
3208135446Strhodes		}
3209135446Strhodes	} else
3210135446Strhodes		view->dlv = NULL;
3211135446Strhodes
3212135446Strhodes	/*
3213135446Strhodes	 * For now, there is only one kind of trusted keys, the
3214135446Strhodes	 * "security roots".
3215135446Strhodes	 */
3216224092Sdougb	CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys,
3217224092Sdougb					auto_dlv, auto_root, mctx));
3218170222Sdougb	dns_resolver_resetmustbesecure(view->resolver);
3219170222Sdougb	obj = NULL;
3220170222Sdougb	result = ns_config_get(maps, "dnssec-must-be-secure", &obj);
3221170222Sdougb	if (result == ISC_R_SUCCESS)
3222170222Sdougb		CHECK(mustbesecure(obj, view->resolver));
3223135446Strhodes
3224135446Strhodes	obj = NULL;
3225135446Strhodes	result = ns_config_get(maps, "preferred-glue", &obj);
3226135446Strhodes	if (result == ISC_R_SUCCESS) {
3227135446Strhodes		str = cfg_obj_asstring(obj);
3228135446Strhodes		if (strcasecmp(str, "a") == 0)
3229135446Strhodes			view->preferred_glue = dns_rdatatype_a;
3230135446Strhodes		else if (strcasecmp(str, "aaaa") == 0)
3231135446Strhodes			view->preferred_glue = dns_rdatatype_aaaa;
3232135446Strhodes		else
3233135446Strhodes			view->preferred_glue = 0;
3234135446Strhodes	} else
3235135446Strhodes		view->preferred_glue = 0;
3236135446Strhodes
3237135446Strhodes	obj = NULL;
3238135446Strhodes	result = ns_config_get(maps, "root-delegation-only", &obj);
3239135446Strhodes	if (result == ISC_R_SUCCESS) {
3240135446Strhodes		dns_view_setrootdelonly(view, ISC_TRUE);
3241135446Strhodes		if (!cfg_obj_isvoid(obj)) {
3242135446Strhodes			dns_fixedname_t fixed;
3243135446Strhodes			dns_name_t *name;
3244135446Strhodes			isc_buffer_t b;
3245165071Sdougb			const char *str;
3246165071Sdougb			const cfg_obj_t *exclude;
3247135446Strhodes
3248135446Strhodes			dns_fixedname_init(&fixed);
3249135446Strhodes			name = dns_fixedname_name(&fixed);
3250135446Strhodes			for (element = cfg_list_first(obj);
3251135446Strhodes			     element != NULL;
3252135446Strhodes			     element = cfg_list_next(element)) {
3253135446Strhodes				exclude = cfg_listelt_value(element);
3254135446Strhodes				str = cfg_obj_asstring(exclude);
3255254402Serwin				isc_buffer_constinit(&b, str, strlen(str));
3256135446Strhodes				isc_buffer_add(&b, strlen(str));
3257135446Strhodes				CHECK(dns_name_fromtext(name, &b, dns_rootname,
3258224092Sdougb							0, NULL));
3259135446Strhodes				CHECK(dns_view_excludedelegationonly(view,
3260135446Strhodes								     name));
3261135446Strhodes			}
3262135446Strhodes		}
3263135446Strhodes	} else
3264135446Strhodes		dns_view_setrootdelonly(view, ISC_FALSE);
3265135446Strhodes
3266170222Sdougb	/*
3267170222Sdougb	 * Setup automatic empty zones.  If recursion is off then
3268170222Sdougb	 * they are disabled by default.
3269170222Sdougb	 */
3270170222Sdougb	obj = NULL;
3271170222Sdougb	(void)ns_config_get(maps, "empty-zones-enable", &obj);
3272170222Sdougb	(void)ns_config_get(maps, "disable-empty-zone", &disablelist);
3273170222Sdougb	if (obj == NULL && disablelist == NULL &&
3274170222Sdougb	    view->rdclass == dns_rdataclass_in) {
3275170222Sdougb		empty_zones_enable = view->recursion;
3276170222Sdougb	} else if (view->rdclass == dns_rdataclass_in) {
3277170222Sdougb		if (obj != NULL)
3278170222Sdougb			empty_zones_enable = cfg_obj_asboolean(obj);
3279170222Sdougb		else
3280170222Sdougb			empty_zones_enable = view->recursion;
3281170222Sdougb	} else {
3282170222Sdougb		empty_zones_enable = ISC_FALSE;
3283170222Sdougb	}
3284234010Sdougb	if (empty_zones_enable && !lwresd_g_useresolvconf) {
3285170222Sdougb		const char *empty;
3286170222Sdougb		int empty_zone = 0;
3287170222Sdougb		dns_fixedname_t fixed;
3288170222Sdougb		dns_name_t *name;
3289170222Sdougb		isc_buffer_t buffer;
3290170222Sdougb		const char *str;
3291170222Sdougb		char server[DNS_NAME_FORMATSIZE + 1];
3292170222Sdougb		char contact[DNS_NAME_FORMATSIZE + 1];
3293170222Sdougb		const char *empty_dbtype[4] =
3294170222Sdougb				    { "_builtin", "empty", NULL, NULL };
3295170222Sdougb		int empty_dbtypec = 4;
3296254897Serwin		dns_zonestat_level_t statlevel;
3297170222Sdougb
3298170222Sdougb		dns_fixedname_init(&fixed);
3299170222Sdougb		name = dns_fixedname_name(&fixed);
3300170222Sdougb
3301170222Sdougb		obj = NULL;
3302170222Sdougb		result = ns_config_get(maps, "empty-server", &obj);
3303170222Sdougb		if (result == ISC_R_SUCCESS) {
3304170222Sdougb			str = cfg_obj_asstring(obj);
3305254402Serwin			isc_buffer_constinit(&buffer, str, strlen(str));
3306170222Sdougb			isc_buffer_add(&buffer, strlen(str));
3307224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3308224092Sdougb						NULL));
3309170222Sdougb			isc_buffer_init(&buffer, server, sizeof(server) - 1);
3310170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
3311170222Sdougb			server[isc_buffer_usedlength(&buffer)] = 0;
3312170222Sdougb			empty_dbtype[2] = server;
3313170222Sdougb		} else
3314170222Sdougb			empty_dbtype[2] = "@";
3315170222Sdougb
3316170222Sdougb		obj = NULL;
3317170222Sdougb		result = ns_config_get(maps, "empty-contact", &obj);
3318170222Sdougb		if (result == ISC_R_SUCCESS) {
3319170222Sdougb			str = cfg_obj_asstring(obj);
3320254402Serwin			isc_buffer_constinit(&buffer, str, strlen(str));
3321170222Sdougb			isc_buffer_add(&buffer, strlen(str));
3322224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3323224092Sdougb						NULL));
3324170222Sdougb			isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
3325170222Sdougb			CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
3326170222Sdougb			contact[isc_buffer_usedlength(&buffer)] = 0;
3327170222Sdougb			empty_dbtype[3] = contact;
3328170222Sdougb		} else
3329170222Sdougb			empty_dbtype[3] = ".";
3330170222Sdougb
3331193149Sdougb		obj = NULL;
3332193149Sdougb		result = ns_config_get(maps, "zone-statistics", &obj);
3333193149Sdougb		INSIST(result == ISC_R_SUCCESS);
3334254897Serwin		if (cfg_obj_isboolean(obj)) {
3335254897Serwin			if (cfg_obj_asboolean(obj))
3336254897Serwin				statlevel = dns_zonestat_full;
3337254897Serwin			else
3338254897Serwin				statlevel = dns_zonestat_terse; /* XXX */
3339254897Serwin		} else {
3340254897Serwin			const char *levelstr = cfg_obj_asstring(obj);
3341254897Serwin			if (strcasecmp(levelstr, "full") == 0)
3342254897Serwin				statlevel = dns_zonestat_full;
3343254897Serwin			else if (strcasecmp(levelstr, "terse") == 0)
3344254897Serwin				statlevel = dns_zonestat_terse;
3345254897Serwin			else if (strcasecmp(levelstr, "none") == 0)
3346254897Serwin				statlevel = dns_zonestat_none;
3347254897Serwin			else
3348254897Serwin				INSIST(0);
3349254897Serwin		}
3350193149Sdougb
3351254897Serwin		for (empty = empty_zones[empty_zone];
3352170222Sdougb		     empty != NULL;
3353254897Serwin		     empty = empty_zones[++empty_zone])
3354170222Sdougb		{
3355170222Sdougb			dns_forwarders_t *forwarders = NULL;
3356170222Sdougb			dns_view_t *pview = NULL;
3357170222Sdougb
3358254402Serwin			isc_buffer_constinit(&buffer, empty, strlen(empty));
3359170222Sdougb			isc_buffer_add(&buffer, strlen(empty));
3360170222Sdougb			/*
3361170222Sdougb			 * Look for zone on drop list.
3362170222Sdougb			 */
3363224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3364224092Sdougb						NULL));
3365170222Sdougb			if (disablelist != NULL &&
3366170222Sdougb			    on_disable_list(disablelist, name))
3367170222Sdougb				continue;
3368170222Sdougb
3369170222Sdougb			/*
3370170222Sdougb			 * This zone already exists.
3371170222Sdougb			 */
3372170222Sdougb			(void)dns_view_findzone(view, name, &zone);
3373170222Sdougb			if (zone != NULL) {
3374170222Sdougb				dns_zone_detach(&zone);
3375170222Sdougb				continue;
3376170222Sdougb			}
3377170222Sdougb
3378170222Sdougb			/*
3379170222Sdougb			 * If we would forward this name don't add a
3380170222Sdougb			 * empty zone for it.
3381170222Sdougb			 */
3382170222Sdougb			result = dns_fwdtable_find(view->fwdtable, name,
3383170222Sdougb						   &forwarders);
3384170222Sdougb			if (result == ISC_R_SUCCESS &&
3385170222Sdougb			    forwarders->fwdpolicy == dns_fwdpolicy_only)
3386170222Sdougb				continue;
3387186462Sdougb
3388170222Sdougb			/*
3389170222Sdougb			 * See if we can re-use a existing zone.
3390170222Sdougb			 */
3391170222Sdougb			result = dns_viewlist_find(&ns_g_server->viewlist,
3392170222Sdougb						   view->name, view->rdclass,
3393170222Sdougb						   &pview);
3394170222Sdougb			if (result != ISC_R_NOTFOUND &&
3395170222Sdougb			    result != ISC_R_SUCCESS)
3396170222Sdougb				goto cleanup;
3397170222Sdougb
3398170222Sdougb			if (pview != NULL) {
3399170222Sdougb				(void)dns_view_findzone(pview, name, &zone);
3400170222Sdougb				dns_view_detach(&pview);
3401170222Sdougb			}
3402170222Sdougb
3403262706Serwin			CHECK(create_empty_zone(zone, name, view, zonelist,
3404262706Serwin						empty_dbtype, empty_dbtypec,
3405262706Serwin						statlevel));
3406262706Serwin			if (zone != NULL)
3407262706Serwin				dns_zone_detach(&zone);
3408170222Sdougb		}
3409170222Sdougb	}
3410186462Sdougb
3411262706Serwin#ifdef USE_RRL
3412262706Serwin	obj = NULL;
3413262706Serwin	result = ns_config_get(maps, "rate-limit", &obj);
3414262706Serwin	if (result == ISC_R_SUCCESS) {
3415262706Serwin		result = configure_rrl(view, config, obj);
3416262706Serwin		if (result != ISC_R_SUCCESS)
3417262706Serwin			goto cleanup;
3418262706Serwin	}
3419262706Serwin#endif /* USE_RRL */
3420262706Serwin
3421135446Strhodes	result = ISC_R_SUCCESS;
3422135446Strhodes
3423135446Strhodes cleanup:
3424224092Sdougb	if (clients != NULL)
3425224092Sdougb		dns_acl_detach(&clients);
3426224092Sdougb	if (mapped != NULL)
3427224092Sdougb		dns_acl_detach(&mapped);
3428224092Sdougb	if (excluded != NULL)
3429224092Sdougb		dns_acl_detach(&excluded);
3430224092Sdougb	if (ring != NULL)
3431224092Sdougb		dns_tsigkeyring_detach(&ring);
3432170222Sdougb	if (zone != NULL)
3433170222Sdougb		dns_zone_detach(&zone);
3434135446Strhodes	if (dispatch4 != NULL)
3435135446Strhodes		dns_dispatch_detach(&dispatch4);
3436135446Strhodes	if (dispatch6 != NULL)
3437135446Strhodes		dns_dispatch_detach(&dispatch6);
3438193149Sdougb	if (resstats != NULL)
3439193149Sdougb		isc_stats_detach(&resstats);
3440193149Sdougb	if (resquerystats != NULL)
3441193149Sdougb		dns_stats_detach(&resquerystats);
3442135446Strhodes	if (order != NULL)
3443135446Strhodes		dns_order_detach(&order);
3444135446Strhodes	if (cmctx != NULL)
3445135446Strhodes		isc_mem_detach(&cmctx);
3446225361Sdougb	if (hmctx != NULL)
3447225361Sdougb		isc_mem_detach(&hmctx);
3448135446Strhodes
3449135446Strhodes	if (cache != NULL)
3450135446Strhodes		dns_cache_detach(&cache);
3451135446Strhodes
3452135446Strhodes	return (result);
3453135446Strhodes}
3454135446Strhodes
3455135446Strhodesstatic isc_result_t
3456135446Strhodesconfigure_hints(dns_view_t *view, const char *filename) {
3457135446Strhodes	isc_result_t result;
3458135446Strhodes	dns_db_t *db;
3459135446Strhodes
3460135446Strhodes	db = NULL;
3461135446Strhodes	result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
3462135446Strhodes	if (result == ISC_R_SUCCESS) {
3463135446Strhodes		dns_view_sethints(view, db);
3464135446Strhodes		dns_db_detach(&db);
3465135446Strhodes	}
3466135446Strhodes
3467135446Strhodes	return (result);
3468135446Strhodes}
3469135446Strhodes
3470135446Strhodesstatic isc_result_t
3471165071Sdougbconfigure_alternates(const cfg_obj_t *config, dns_view_t *view,
3472165071Sdougb		     const cfg_obj_t *alternates)
3473135446Strhodes{
3474165071Sdougb	const cfg_obj_t *portobj;
3475165071Sdougb	const cfg_obj_t *addresses;
3476165071Sdougb	const cfg_listelt_t *element;
3477135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
3478135446Strhodes	in_port_t port;
3479135446Strhodes
3480135446Strhodes	/*
3481135446Strhodes	 * Determine which port to send requests to.
3482135446Strhodes	 */
3483135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
3484135446Strhodes		port = ns_g_port;
3485135446Strhodes	else
3486135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
3487135446Strhodes
3488135446Strhodes	if (alternates != NULL) {
3489135446Strhodes		portobj = cfg_tuple_get(alternates, "port");
3490135446Strhodes		if (cfg_obj_isuint32(portobj)) {
3491135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
3492135446Strhodes			if (val > ISC_UINT16_MAX) {
3493135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
3494135446Strhodes					    "port '%u' out of range", val);
3495135446Strhodes				return (ISC_R_RANGE);
3496135446Strhodes			}
3497135446Strhodes			port = (in_port_t) val;
3498135446Strhodes		}
3499135446Strhodes	}
3500135446Strhodes
3501135446Strhodes	addresses = NULL;
3502135446Strhodes	if (alternates != NULL)
3503135446Strhodes		addresses = cfg_tuple_get(alternates, "addresses");
3504135446Strhodes
3505135446Strhodes	for (element = cfg_list_first(addresses);
3506135446Strhodes	     element != NULL;
3507135446Strhodes	     element = cfg_list_next(element))
3508135446Strhodes	{
3509165071Sdougb		const cfg_obj_t *alternate = cfg_listelt_value(element);
3510135446Strhodes		isc_sockaddr_t sa;
3511135446Strhodes
3512135446Strhodes		if (!cfg_obj_issockaddr(alternate)) {
3513135446Strhodes			dns_fixedname_t fixed;
3514135446Strhodes			dns_name_t *name;
3515165071Sdougb			const char *str = cfg_obj_asstring(cfg_tuple_get(
3516165071Sdougb							   alternate, "name"));
3517135446Strhodes			isc_buffer_t buffer;
3518135446Strhodes			in_port_t myport = port;
3519135446Strhodes
3520254402Serwin			isc_buffer_constinit(&buffer, str, strlen(str));
3521135446Strhodes			isc_buffer_add(&buffer, strlen(str));
3522135446Strhodes			dns_fixedname_init(&fixed);
3523135446Strhodes			name = dns_fixedname_name(&fixed);
3524224092Sdougb			CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
3525224092Sdougb						NULL));
3526135446Strhodes
3527135446Strhodes			portobj = cfg_tuple_get(alternate, "port");
3528135446Strhodes			if (cfg_obj_isuint32(portobj)) {
3529135446Strhodes				isc_uint32_t val = cfg_obj_asuint32(portobj);
3530135446Strhodes				if (val > ISC_UINT16_MAX) {
3531135446Strhodes					cfg_obj_log(portobj, ns_g_lctx,
3532135446Strhodes						    ISC_LOG_ERROR,
3533135446Strhodes						    "port '%u' out of range",
3534135446Strhodes						     val);
3535135446Strhodes					return (ISC_R_RANGE);
3536135446Strhodes				}
3537135446Strhodes				myport = (in_port_t) val;
3538135446Strhodes			}
3539135446Strhodes			CHECK(dns_resolver_addalternate(view->resolver, NULL,
3540135446Strhodes							name, myport));
3541135446Strhodes			continue;
3542135446Strhodes		}
3543135446Strhodes
3544135446Strhodes		sa = *cfg_obj_assockaddr(alternate);
3545135446Strhodes		if (isc_sockaddr_getport(&sa) == 0)
3546135446Strhodes			isc_sockaddr_setport(&sa, port);
3547135446Strhodes		CHECK(dns_resolver_addalternate(view->resolver, &sa,
3548135446Strhodes						NULL, 0));
3549135446Strhodes	}
3550135446Strhodes
3551135446Strhodes cleanup:
3552135446Strhodes	return (result);
3553135446Strhodes}
3554135446Strhodes
3555135446Strhodesstatic isc_result_t
3556165071Sdougbconfigure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
3557165071Sdougb		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype)
3558135446Strhodes{
3559165071Sdougb	const cfg_obj_t *portobj;
3560165071Sdougb	const cfg_obj_t *faddresses;
3561165071Sdougb	const cfg_listelt_t *element;
3562135446Strhodes	dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
3563135446Strhodes	isc_sockaddrlist_t addresses;
3564135446Strhodes	isc_sockaddr_t *sa;
3565135446Strhodes	isc_result_t result;
3566135446Strhodes	in_port_t port;
3567135446Strhodes
3568193149Sdougb	ISC_LIST_INIT(addresses);
3569193149Sdougb
3570135446Strhodes	/*
3571135446Strhodes	 * Determine which port to send forwarded requests to.
3572135446Strhodes	 */
3573135446Strhodes	if (ns_g_lwresdonly && ns_g_port != 0)
3574135446Strhodes		port = ns_g_port;
3575135446Strhodes	else
3576135446Strhodes		CHECKM(ns_config_getport(config, &port), "port");
3577135446Strhodes
3578135446Strhodes	if (forwarders != NULL) {
3579135446Strhodes		portobj = cfg_tuple_get(forwarders, "port");
3580135446Strhodes		if (cfg_obj_isuint32(portobj)) {
3581135446Strhodes			isc_uint32_t val = cfg_obj_asuint32(portobj);
3582135446Strhodes			if (val > ISC_UINT16_MAX) {
3583135446Strhodes				cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
3584135446Strhodes					    "port '%u' out of range", val);
3585135446Strhodes				return (ISC_R_RANGE);
3586135446Strhodes			}
3587135446Strhodes			port = (in_port_t) val;
3588135446Strhodes		}
3589135446Strhodes	}
3590135446Strhodes
3591135446Strhodes	faddresses = NULL;
3592135446Strhodes	if (forwarders != NULL)
3593135446Strhodes		faddresses = cfg_tuple_get(forwarders, "addresses");
3594135446Strhodes
3595135446Strhodes	for (element = cfg_list_first(faddresses);
3596135446Strhodes	     element != NULL;
3597135446Strhodes	     element = cfg_list_next(element))
3598135446Strhodes	{
3599165071Sdougb		const cfg_obj_t *forwarder = cfg_listelt_value(element);
3600135446Strhodes		sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t));
3601135446Strhodes		if (sa == NULL) {
3602135446Strhodes			result = ISC_R_NOMEMORY;
3603135446Strhodes			goto cleanup;
3604135446Strhodes		}
3605135446Strhodes		*sa = *cfg_obj_assockaddr(forwarder);
3606135446Strhodes		if (isc_sockaddr_getport(sa) == 0)
3607135446Strhodes			isc_sockaddr_setport(sa, port);
3608135446Strhodes		ISC_LINK_INIT(sa, link);
3609135446Strhodes		ISC_LIST_APPEND(addresses, sa, link);
3610135446Strhodes	}
3611135446Strhodes
3612135446Strhodes	if (ISC_LIST_EMPTY(addresses)) {
3613135446Strhodes		if (forwardtype != NULL)
3614135446Strhodes			cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
3615135446Strhodes				    "no forwarders seen; disabling "
3616135446Strhodes				    "forwarding");
3617135446Strhodes		fwdpolicy = dns_fwdpolicy_none;
3618135446Strhodes	} else {
3619135446Strhodes		if (forwardtype == NULL)
3620135446Strhodes			fwdpolicy = dns_fwdpolicy_first;
3621135446Strhodes		else {
3622165071Sdougb			const char *forwardstr = cfg_obj_asstring(forwardtype);
3623135446Strhodes			if (strcasecmp(forwardstr, "first") == 0)
3624135446Strhodes				fwdpolicy = dns_fwdpolicy_first;
3625135446Strhodes			else if (strcasecmp(forwardstr, "only") == 0)
3626135446Strhodes				fwdpolicy = dns_fwdpolicy_only;
3627135446Strhodes			else
3628135446Strhodes				INSIST(0);
3629135446Strhodes		}
3630135446Strhodes	}
3631135446Strhodes
3632135446Strhodes	result = dns_fwdtable_add(view->fwdtable, origin, &addresses,
3633135446Strhodes				  fwdpolicy);
3634135446Strhodes	if (result != ISC_R_SUCCESS) {
3635135446Strhodes		char namebuf[DNS_NAME_FORMATSIZE];
3636135446Strhodes		dns_name_format(origin, namebuf, sizeof(namebuf));
3637135446Strhodes		cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
3638135446Strhodes			    "could not set up forwarding for domain '%s': %s",
3639135446Strhodes			    namebuf, isc_result_totext(result));
3640135446Strhodes		goto cleanup;
3641135446Strhodes	}
3642135446Strhodes
3643135446Strhodes	result = ISC_R_SUCCESS;
3644135446Strhodes
3645135446Strhodes cleanup:
3646135446Strhodes
3647135446Strhodes	while (!ISC_LIST_EMPTY(addresses)) {
3648135446Strhodes		sa = ISC_LIST_HEAD(addresses);
3649135446Strhodes		ISC_LIST_UNLINK(addresses, sa, link);
3650135446Strhodes		isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t));
3651135446Strhodes	}
3652135446Strhodes
3653135446Strhodes	return (result);
3654135446Strhodes}
3655135446Strhodes
3656135446Strhodesstatic isc_result_t
3657225361Sdougbget_viewinfo(const cfg_obj_t *vconfig, const char **namep,
3658225361Sdougb	     dns_rdataclass_t *classp)
3659165071Sdougb{
3660225361Sdougb	isc_result_t result = ISC_R_SUCCESS;
3661135446Strhodes	const char *viewname;
3662135446Strhodes	dns_rdataclass_t viewclass;
3663135446Strhodes
3664225361Sdougb	REQUIRE(namep != NULL && *namep == NULL);
3665225361Sdougb	REQUIRE(classp != NULL);
3666225361Sdougb
3667135446Strhodes	if (vconfig != NULL) {
3668165071Sdougb		const cfg_obj_t *classobj = NULL;
3669135446Strhodes
3670135446Strhodes		viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
3671135446Strhodes		classobj = cfg_tuple_get(vconfig, "class");
3672135446Strhodes		result = ns_config_getclass(classobj, dns_rdataclass_in,
3673135446Strhodes					    &viewclass);
3674135446Strhodes	} else {
3675135446Strhodes		viewname = "_default";
3676135446Strhodes		viewclass = dns_rdataclass_in;
3677135446Strhodes	}
3678225361Sdougb
3679225361Sdougb	*namep = viewname;
3680225361Sdougb	*classp = viewclass;
3681225361Sdougb
3682225361Sdougb	return (result);
3683225361Sdougb}
3684225361Sdougb
3685225361Sdougb/*
3686225361Sdougb * Find a view based on its configuration info and attach to it.
3687225361Sdougb *
3688225361Sdougb * If 'vconfig' is NULL, attach to the default view.
3689225361Sdougb */
3690225361Sdougbstatic isc_result_t
3691225361Sdougbfind_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
3692225361Sdougb	  dns_view_t **viewp)
3693225361Sdougb{
3694225361Sdougb	isc_result_t result;
3695225361Sdougb	const char *viewname = NULL;
3696225361Sdougb	dns_rdataclass_t viewclass;
3697225361Sdougb	dns_view_t *view = NULL;
3698225361Sdougb
3699225361Sdougb	result = get_viewinfo(vconfig, &viewname, &viewclass);
3700225361Sdougb	if (result != ISC_R_SUCCESS)
3701225361Sdougb		return (result);
3702225361Sdougb
3703135446Strhodes	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
3704225361Sdougb	if (result != ISC_R_SUCCESS)
3705225361Sdougb		return (result);
3706225361Sdougb
3707225361Sdougb	*viewp = view;
3708225361Sdougb	return (ISC_R_SUCCESS);
3709225361Sdougb}
3710225361Sdougb
3711225361Sdougb/*
3712225361Sdougb * Create a new view and add it to the list.
3713225361Sdougb *
3714225361Sdougb * If 'vconfig' is NULL, create the default view.
3715225361Sdougb *
3716225361Sdougb * The view created is attached to '*viewp'.
3717225361Sdougb */
3718225361Sdougbstatic isc_result_t
3719225361Sdougbcreate_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
3720225361Sdougb	    dns_view_t **viewp)
3721225361Sdougb{
3722225361Sdougb	isc_result_t result;
3723225361Sdougb	const char *viewname = NULL;
3724225361Sdougb	dns_rdataclass_t viewclass;
3725225361Sdougb	dns_view_t *view = NULL;
3726225361Sdougb
3727225361Sdougb	result = get_viewinfo(vconfig, &viewname, &viewclass);
3728225361Sdougb	if (result != ISC_R_SUCCESS)
3729225361Sdougb		return (result);
3730225361Sdougb
3731225361Sdougb	result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
3732135446Strhodes	if (result == ISC_R_SUCCESS)
3733135446Strhodes		return (ISC_R_EXISTS);
3734135446Strhodes	if (result != ISC_R_NOTFOUND)
3735135446Strhodes		return (result);
3736135446Strhodes	INSIST(view == NULL);
3737135446Strhodes
3738135446Strhodes	result = dns_view_create(ns_g_mctx, viewclass, viewname, &view);
3739135446Strhodes	if (result != ISC_R_SUCCESS)
3740135446Strhodes		return (result);
3741135446Strhodes
3742135446Strhodes	ISC_LIST_APPEND(*viewlist, view, link);
3743135446Strhodes	dns_view_attach(view, viewp);
3744135446Strhodes	return (ISC_R_SUCCESS);
3745135446Strhodes}
3746135446Strhodes
3747135446Strhodes/*
3748135446Strhodes * Configure or reconfigure a zone.
3749135446Strhodes */
3750135446Strhodesstatic isc_result_t
3751165071Sdougbconfigure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
3752165071Sdougb	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
3753224092Sdougb	       cfg_aclconfctx_t *aclconf, isc_boolean_t added)
3754135446Strhodes{
3755135446Strhodes	dns_view_t *pview = NULL;	/* Production view */
3756135446Strhodes	dns_zone_t *zone = NULL;	/* New or reused zone */
3757254897Serwin	dns_zone_t *raw = NULL;		/* New or reused raw zone */
3758135446Strhodes	dns_zone_t *dupzone = NULL;
3759165071Sdougb	const cfg_obj_t *options = NULL;
3760165071Sdougb	const cfg_obj_t *zoptions = NULL;
3761165071Sdougb	const cfg_obj_t *typeobj = NULL;
3762165071Sdougb	const cfg_obj_t *forwarders = NULL;
3763165071Sdougb	const cfg_obj_t *forwardtype = NULL;
3764165071Sdougb	const cfg_obj_t *only = NULL;
3765254897Serwin	const cfg_obj_t *signing = NULL;
3766135446Strhodes	isc_result_t result;
3767135446Strhodes	isc_result_t tresult;
3768135446Strhodes	isc_buffer_t buffer;
3769135446Strhodes	dns_fixedname_t fixorigin;
3770135446Strhodes	dns_name_t *origin;
3771135446Strhodes	const char *zname;
3772135446Strhodes	dns_rdataclass_t zclass;
3773135446Strhodes	const char *ztypestr;
3774254402Serwin	isc_boolean_t is_rpz;
3775254402Serwin	dns_rpz_zone_t *rpz;
3776135446Strhodes
3777135446Strhodes	options = NULL;
3778135446Strhodes	(void)cfg_map_get(config, "options", &options);
3779135446Strhodes
3780135446Strhodes	zoptions = cfg_tuple_get(zconfig, "options");
3781135446Strhodes
3782135446Strhodes	/*
3783135446Strhodes	 * Get the zone origin as a dns_name_t.
3784135446Strhodes	 */
3785135446Strhodes	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
3786254402Serwin	isc_buffer_constinit(&buffer, zname, strlen(zname));
3787135446Strhodes	isc_buffer_add(&buffer, strlen(zname));
3788135446Strhodes	dns_fixedname_init(&fixorigin);
3789135446Strhodes	CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin),
3790224092Sdougb				&buffer, dns_rootname, 0, NULL));
3791135446Strhodes	origin = dns_fixedname_name(&fixorigin);
3792135446Strhodes
3793135446Strhodes	CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
3794135446Strhodes				 view->rdclass, &zclass));
3795135446Strhodes	if (zclass != view->rdclass) {
3796135446Strhodes		const char *vname = NULL;
3797135446Strhodes		if (vconfig != NULL)
3798135446Strhodes			vname = cfg_obj_asstring(cfg_tuple_get(vconfig,
3799135446Strhodes							       "name"));
3800135446Strhodes		else
3801135446Strhodes			vname = "<default view>";
3802186462Sdougb
3803135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3804135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3805135446Strhodes			      "zone '%s': wrong class for view '%s'",
3806135446Strhodes			      zname, vname);
3807135446Strhodes		result = ISC_R_FAILURE;
3808135446Strhodes		goto cleanup;
3809135446Strhodes	}
3810135446Strhodes
3811135446Strhodes	(void)cfg_map_get(zoptions, "type", &typeobj);
3812135446Strhodes	if (typeobj == NULL) {
3813135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3814135446Strhodes			    "zone '%s' 'type' not specified", zname);
3815135446Strhodes		return (ISC_R_FAILURE);
3816135446Strhodes	}
3817135446Strhodes	ztypestr = cfg_obj_asstring(typeobj);
3818135446Strhodes
3819135446Strhodes	/*
3820224092Sdougb	 * "hints zones" aren't zones.	If we've got one,
3821135446Strhodes	 * configure it and return.
3822135446Strhodes	 */
3823135446Strhodes	if (strcasecmp(ztypestr, "hint") == 0) {
3824165071Sdougb		const cfg_obj_t *fileobj = NULL;
3825135446Strhodes		if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) {
3826135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3827135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
3828135446Strhodes				      "zone '%s': 'file' not specified",
3829135446Strhodes				      zname);
3830135446Strhodes			result = ISC_R_FAILURE;
3831135446Strhodes			goto cleanup;
3832135446Strhodes		}
3833135446Strhodes		if (dns_name_equal(origin, dns_rootname)) {
3834165071Sdougb			const char *hintsfile = cfg_obj_asstring(fileobj);
3835135446Strhodes
3836135446Strhodes			result = configure_hints(view, hintsfile);
3837135446Strhodes			if (result != ISC_R_SUCCESS) {
3838135446Strhodes				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3839135446Strhodes					      NS_LOGMODULE_SERVER,
3840135446Strhodes					      ISC_LOG_ERROR,
3841135446Strhodes					      "could not configure root hints "
3842135446Strhodes					      "from '%s': %s", hintsfile,
3843135446Strhodes					      isc_result_totext(result));
3844135446Strhodes				goto cleanup;
3845135446Strhodes			}
3846135446Strhodes			/*
3847135446Strhodes			 * Hint zones may also refer to delegation only points.
3848135446Strhodes			 */
3849135446Strhodes			only = NULL;
3850135446Strhodes			tresult = cfg_map_get(zoptions, "delegation-only",
3851135446Strhodes					      &only);
3852135446Strhodes			if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only))
3853135446Strhodes				CHECK(dns_view_adddelegationonly(view, origin));
3854135446Strhodes		} else {
3855135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
3856135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
3857135446Strhodes				      "ignoring non-root hint zone '%s'",
3858135446Strhodes				      zname);
3859135446Strhodes			result = ISC_R_SUCCESS;
3860135446Strhodes		}
3861135446Strhodes		/* Skip ordinary zone processing. */
3862135446Strhodes		goto cleanup;
3863135446Strhodes	}
3864135446Strhodes
3865135446Strhodes	/*
3866135446Strhodes	 * "forward zones" aren't zones either.  Translate this syntax into
3867135446Strhodes	 * the appropriate selective forwarding configuration and return.
3868135446Strhodes	 */
3869135446Strhodes	if (strcasecmp(ztypestr, "forward") == 0) {
3870135446Strhodes		forwardtype = NULL;
3871135446Strhodes		forwarders = NULL;
3872135446Strhodes
3873135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
3874135446Strhodes		(void)cfg_map_get(zoptions, "forwarders", &forwarders);
3875135446Strhodes		result = configure_forward(config, view, origin, forwarders,
3876135446Strhodes					   forwardtype);
3877135446Strhodes		goto cleanup;
3878135446Strhodes	}
3879135446Strhodes
3880135446Strhodes	/*
3881135446Strhodes	 * "delegation-only zones" aren't zones either.
3882135446Strhodes	 */
3883135446Strhodes	if (strcasecmp(ztypestr, "delegation-only") == 0) {
3884135446Strhodes		result = dns_view_adddelegationonly(view, origin);
3885135446Strhodes		goto cleanup;
3886135446Strhodes	}
3887135446Strhodes
3888135446Strhodes	/*
3889254897Serwin	 * Redirect zones only require minimal configuration.
3890254897Serwin	 */
3891254897Serwin	if (strcasecmp(ztypestr, "redirect") == 0) {
3892254897Serwin		if (view->redirect != NULL) {
3893254897Serwin			cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3894254897Serwin				    "redirect zone already exists");
3895254897Serwin			result = ISC_R_EXISTS;
3896254897Serwin			goto cleanup;
3897254897Serwin		}
3898254897Serwin		result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
3899254897Serwin					   view->rdclass, &pview);
3900254897Serwin		if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3901254897Serwin			goto cleanup;
3902254897Serwin		if (pview != NULL && pview->redirect != NULL) {
3903254897Serwin			dns_zone_attach(pview->redirect, &zone);
3904254897Serwin			dns_zone_setview(zone, view);
3905254897Serwin		} else {
3906254897Serwin			CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr,
3907254897Serwin						     &zone));
3908254897Serwin			CHECK(dns_zone_setorigin(zone, origin));
3909254897Serwin			dns_zone_setview(zone, view);
3910254897Serwin			CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr,
3911254897Serwin						     zone));
3912254897Serwin			dns_zone_setstats(zone, ns_g_server->zonestats);
3913254897Serwin		}
3914254897Serwin		CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf,
3915254897Serwin					zone, NULL));
3916254897Serwin		dns_zone_attach(zone, &view->redirect);
3917254897Serwin		goto cleanup;
3918254897Serwin	}
3919254897Serwin
3920254897Serwin	/*
3921135446Strhodes	 * Check for duplicates in the new zone table.
3922135446Strhodes	 */
3923135446Strhodes	result = dns_view_findzone(view, origin, &dupzone);
3924135446Strhodes	if (result == ISC_R_SUCCESS) {
3925135446Strhodes		/*
3926135446Strhodes		 * We already have this zone!
3927135446Strhodes		 */
3928135446Strhodes		cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
3929135446Strhodes			    "zone '%s' already exists", zname);
3930135446Strhodes		dns_zone_detach(&dupzone);
3931135446Strhodes		result = ISC_R_EXISTS;
3932135446Strhodes		goto cleanup;
3933135446Strhodes	}
3934135446Strhodes	INSIST(dupzone == NULL);
3935135446Strhodes
3936135446Strhodes	/*
3937254402Serwin	 * Note whether this is a response policy zone.
3938254402Serwin	 */
3939254402Serwin	is_rpz = ISC_FALSE;
3940254402Serwin	for (rpz = ISC_LIST_HEAD(view->rpz_zones);
3941254402Serwin	     rpz != NULL;
3942254402Serwin	     rpz = ISC_LIST_NEXT(rpz, link))
3943254402Serwin	{
3944254402Serwin		if (dns_name_equal(&rpz->origin, origin)) {
3945254402Serwin			is_rpz = ISC_TRUE;
3946254402Serwin			rpz->defined = ISC_TRUE;
3947254402Serwin			break;
3948254402Serwin		}
3949254402Serwin	}
3950254402Serwin
3951254402Serwin	/*
3952135446Strhodes	 * See if we can reuse an existing zone.  This is
3953135446Strhodes	 * only possible if all of these are true:
3954135446Strhodes	 *   - The zone's view exists
3955135446Strhodes	 *   - A zone with the right name exists in the view
3956135446Strhodes	 *   - The zone is compatible with the config
3957135446Strhodes	 *     options (e.g., an existing master zone cannot
3958135446Strhodes	 *     be reused if the options specify a slave zone)
3959254402Serwin	 *   - The zone was and is or was not and is not a policy zone
3960135446Strhodes	 */
3961254897Serwin	result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
3962254897Serwin				   view->rdclass, &pview);
3963135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3964135446Strhodes		goto cleanup;
3965135446Strhodes	if (pview != NULL)
3966135446Strhodes		result = dns_view_findzone(pview, origin, &zone);
3967135446Strhodes	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
3968135446Strhodes		goto cleanup;
3969254897Serwin
3970170222Sdougb	if (zone != NULL && !ns_zone_reusable(zone, zconfig))
3971170222Sdougb		dns_zone_detach(&zone);
3972135446Strhodes
3973254402Serwin	if (zone != NULL && is_rpz != dns_zone_get_rpz(zone))
3974254402Serwin		dns_zone_detach(&zone);
3975254402Serwin
3976135446Strhodes	if (zone != NULL) {
3977135446Strhodes		/*
3978135446Strhodes		 * We found a reusable zone.  Make it use the
3979135446Strhodes		 * new view.
3980135446Strhodes		 */
3981135446Strhodes		dns_zone_setview(zone, view);
3982170222Sdougb		if (view->acache != NULL)
3983170222Sdougb			dns_zone_setacache(zone, view->acache);
3984135446Strhodes	} else {
3985135446Strhodes		/*
3986135446Strhodes		 * We cannot reuse an existing zone, we have
3987135446Strhodes		 * to create a new one.
3988135446Strhodes		 */
3989254897Serwin		CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone));
3990135446Strhodes		CHECK(dns_zone_setorigin(zone, origin));
3991135446Strhodes		dns_zone_setview(zone, view);
3992170222Sdougb		if (view->acache != NULL)
3993170222Sdougb			dns_zone_setacache(zone, view->acache);
3994135446Strhodes		CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
3995193149Sdougb		dns_zone_setstats(zone, ns_g_server->zonestats);
3996135446Strhodes	}
3997135446Strhodes
3998254402Serwin	if (is_rpz) {
3999254402Serwin		result = dns_zone_rpz_enable(zone);
4000254402Serwin		if (result != ISC_R_SUCCESS) {
4001254402Serwin			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4002254402Serwin				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4003254402Serwin				      "zone '%s': incompatible"
4004254402Serwin				      " masterfile-format or database"
4005254402Serwin				      " for a response policy zone",
4006254402Serwin				      zname);
4007254402Serwin			goto cleanup;
4008254402Serwin		}
4009254402Serwin	}
4010254402Serwin
4011135446Strhodes	/*
4012135446Strhodes	 * If the zone contains a 'forwarders' statement, configure
4013135446Strhodes	 * selective forwarding.
4014135446Strhodes	 */
4015135446Strhodes	forwarders = NULL;
4016135446Strhodes	if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS)
4017135446Strhodes	{
4018135446Strhodes		forwardtype = NULL;
4019135446Strhodes		(void)cfg_map_get(zoptions, "forward", &forwardtype);
4020135446Strhodes		CHECK(configure_forward(config, view, origin, forwarders,
4021135446Strhodes					forwardtype));
4022135446Strhodes	}
4023135446Strhodes
4024135446Strhodes	/*
4025135446Strhodes	 * Stub and forward zones may also refer to delegation only points.
4026135446Strhodes	 */
4027135446Strhodes	only = NULL;
4028135446Strhodes	if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS)
4029135446Strhodes	{
4030135446Strhodes		if (cfg_obj_asboolean(only))
4031135446Strhodes			CHECK(dns_view_adddelegationonly(view, origin));
4032135446Strhodes	}
4033135446Strhodes
4034135446Strhodes	/*
4035224092Sdougb	 * Mark whether the zone was originally added at runtime or not
4036224092Sdougb	 */
4037224092Sdougb	dns_zone_setadded(zone, added);
4038224092Sdougb
4039254897Serwin	signing = NULL;
4040254897Serwin	if ((strcasecmp(ztypestr, "master") == 0 ||
4041254897Serwin	     strcasecmp(ztypestr, "slave") == 0) &&
4042254897Serwin	    cfg_map_get(zoptions, "inline-signing", &signing) == ISC_R_SUCCESS &&
4043254897Serwin	    cfg_obj_asboolean(signing))
4044254897Serwin	{
4045254897Serwin		dns_zone_getraw(zone, &raw);
4046254897Serwin		if (raw == NULL) {
4047254897Serwin			CHECK(dns_zone_create(&raw, mctx));
4048254897Serwin			CHECK(dns_zone_setorigin(raw, origin));
4049254897Serwin			dns_zone_setview(raw, view);
4050254897Serwin			if (view->acache != NULL)
4051254897Serwin				dns_zone_setacache(raw, view->acache);
4052254897Serwin			dns_zone_setstats(raw, ns_g_server->zonestats);
4053254897Serwin			CHECK(dns_zone_link(zone, raw));
4054254897Serwin		}
4055254897Serwin	}
4056254897Serwin
4057224092Sdougb	/*
4058135446Strhodes	 * Configure the zone.
4059135446Strhodes	 */
4060254897Serwin	CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone, raw));
4061135446Strhodes
4062135446Strhodes	/*
4063135446Strhodes	 * Add the zone to its view in the new view list.
4064135446Strhodes	 */
4065135446Strhodes	CHECK(dns_view_addzone(view, zone));
4066135446Strhodes
4067234010Sdougb	/*
4068234010Sdougb	 * Ensure that zone keys are reloaded on reconfig
4069234010Sdougb	 */
4070234010Sdougb	if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0)
4071234010Sdougb		dns_zone_rekey(zone, ISC_FALSE);
4072234010Sdougb
4073135446Strhodes cleanup:
4074135446Strhodes	if (zone != NULL)
4075135446Strhodes		dns_zone_detach(&zone);
4076254897Serwin	if (raw != NULL)
4077254897Serwin		dns_zone_detach(&raw);
4078135446Strhodes	if (pview != NULL)
4079135446Strhodes		dns_view_detach(&pview);
4080135446Strhodes
4081135446Strhodes	return (result);
4082135446Strhodes}
4083135446Strhodes
4084135446Strhodes/*
4085224092Sdougb * Configure built-in zone for storing managed-key data.
4086224092Sdougb */
4087224092Sdougb
4088224092Sdougb#define KEYZONE "managed-keys.bind"
4089224092Sdougb#define MKEYS ".mkeys"
4090224092Sdougb
4091224092Sdougbstatic isc_result_t
4092224092Sdougbadd_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
4093224092Sdougb	isc_result_t result;
4094224092Sdougb	dns_view_t *pview = NULL;
4095224092Sdougb	dns_zone_t *zone = NULL;
4096224092Sdougb	dns_acl_t *none = NULL;
4097224092Sdougb	char filename[PATH_MAX];
4098224092Sdougb	char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(MKEYS)];
4099224092Sdougb	int n;
4100224092Sdougb
4101224092Sdougb	REQUIRE(view != NULL);
4102224092Sdougb
4103224092Sdougb	/* See if we can re-use an existing keydata zone. */
4104224092Sdougb	result = dns_viewlist_find(&ns_g_server->viewlist,
4105224092Sdougb				   view->name, view->rdclass,
4106224092Sdougb				   &pview);
4107224092Sdougb	if (result != ISC_R_NOTFOUND &&
4108224092Sdougb	    result != ISC_R_SUCCESS)
4109224092Sdougb		return (result);
4110224092Sdougb
4111224092Sdougb	if (pview != NULL && pview->managed_keys != NULL) {
4112224092Sdougb		dns_zone_attach(pview->managed_keys, &view->managed_keys);
4113224092Sdougb		dns_zone_setview(pview->managed_keys, view);
4114224092Sdougb		dns_view_detach(&pview);
4115234010Sdougb		dns_zone_synckeyzone(view->managed_keys);
4116224092Sdougb		return (ISC_R_SUCCESS);
4117224092Sdougb	}
4118224092Sdougb
4119224092Sdougb	/* No existing keydata zone was found; create one */
4120254897Serwin	CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone));
4121224092Sdougb	CHECK(dns_zone_setorigin(zone, dns_rootname));
4122224092Sdougb
4123224092Sdougb	isc_sha256_data((void *)view->name, strlen(view->name), buffer);
4124224092Sdougb	strcat(buffer, MKEYS);
4125224092Sdougb	n = snprintf(filename, sizeof(filename), "%s%s%s",
4126224092Sdougb		     directory ? directory : "", directory ? "/" : "",
4127224092Sdougb		     strcmp(view->name, "_default") == 0 ? KEYZONE : buffer);
4128224092Sdougb	if (n < 0 || (size_t)n >= sizeof(filename)) {
4129224092Sdougb		result = (n < 0) ? ISC_R_FAILURE : ISC_R_NOSPACE;
4130224092Sdougb		goto cleanup;
4131224092Sdougb	}
4132224092Sdougb	CHECK(dns_zone_setfile(zone, filename));
4133224092Sdougb
4134224092Sdougb	dns_zone_setview(zone, view);
4135224092Sdougb	dns_zone_settype(zone, dns_zone_key);
4136224092Sdougb	dns_zone_setclass(zone, view->rdclass);
4137224092Sdougb
4138224092Sdougb	CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
4139224092Sdougb
4140224092Sdougb	if (view->acache != NULL)
4141224092Sdougb		dns_zone_setacache(zone, view->acache);
4142224092Sdougb
4143224092Sdougb	CHECK(dns_acl_none(mctx, &none));
4144224092Sdougb	dns_zone_setqueryacl(zone, none);
4145224092Sdougb	dns_zone_setqueryonacl(zone, none);
4146224092Sdougb	dns_acl_detach(&none);
4147224092Sdougb
4148224092Sdougb	dns_zone_setdialup(zone, dns_dialuptype_no);
4149224092Sdougb	dns_zone_setnotifytype(zone, dns_notifytype_no);
4150224092Sdougb	dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE);
4151224092Sdougb	dns_zone_setjournalsize(zone, 0);
4152224092Sdougb
4153224092Sdougb	dns_zone_setstats(zone, ns_g_server->zonestats);
4154254897Serwin	CHECK(setquerystats(zone, mctx, dns_zonestat_none));
4155224092Sdougb
4156224092Sdougb	if (view->managed_keys != NULL)
4157224092Sdougb		dns_zone_detach(&view->managed_keys);
4158224092Sdougb	dns_zone_attach(zone, &view->managed_keys);
4159224092Sdougb
4160224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4161224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4162224092Sdougb		      "set up managed keys zone for view %s, file '%s'",
4163224092Sdougb		      view->name, filename);
4164224092Sdougb
4165224092Sdougbcleanup:
4166224092Sdougb	if (zone != NULL)
4167224092Sdougb		dns_zone_detach(&zone);
4168224092Sdougb	if (none != NULL)
4169224092Sdougb		dns_acl_detach(&none);
4170224092Sdougb
4171224092Sdougb	return (result);
4172224092Sdougb}
4173224092Sdougb
4174224092Sdougb/*
4175135446Strhodes * Configure a single server quota.
4176135446Strhodes */
4177135446Strhodesstatic void
4178165071Sdougbconfigure_server_quota(const cfg_obj_t **maps, const char *name,
4179165071Sdougb		       isc_quota_t *quota)
4180135446Strhodes{
4181165071Sdougb	const cfg_obj_t *obj = NULL;
4182135446Strhodes	isc_result_t result;
4183135446Strhodes
4184135446Strhodes	result = ns_config_get(maps, name, &obj);
4185135446Strhodes	INSIST(result == ISC_R_SUCCESS);
4186153816Sdougb	isc_quota_max(quota, cfg_obj_asuint32(obj));
4187135446Strhodes}
4188135446Strhodes
4189135446Strhodes/*
4190135446Strhodes * This function is called as soon as the 'directory' statement has been
4191135446Strhodes * parsed.  This can be extended to support other options if necessary.
4192135446Strhodes */
4193135446Strhodesstatic isc_result_t
4194165071Sdougbdirectory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
4195135446Strhodes	isc_result_t result;
4196165071Sdougb	const char *directory;
4197135446Strhodes
4198135446Strhodes	REQUIRE(strcasecmp("directory", clausename) == 0);
4199135446Strhodes
4200135446Strhodes	UNUSED(arg);
4201135446Strhodes	UNUSED(clausename);
4202135446Strhodes
4203135446Strhodes	/*
4204135446Strhodes	 * Change directory.
4205135446Strhodes	 */
4206135446Strhodes	directory = cfg_obj_asstring(obj);
4207135446Strhodes
4208135446Strhodes	if (! isc_file_ischdiridempotent(directory))
4209135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
4210135446Strhodes			    "option 'directory' contains relative path '%s'",
4211135446Strhodes			    directory);
4212135446Strhodes
4213135446Strhodes	result = isc_dir_chdir(directory);
4214135446Strhodes	if (result != ISC_R_SUCCESS) {
4215135446Strhodes		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
4216135446Strhodes			    "change directory to '%s' failed: %s",
4217135446Strhodes			    directory, isc_result_totext(result));
4218135446Strhodes		return (result);
4219135446Strhodes	}
4220135446Strhodes
4221135446Strhodes	return (ISC_R_SUCCESS);
4222135446Strhodes}
4223135446Strhodes
4224135446Strhodesstatic void
4225135446Strhodesscan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
4226135446Strhodes	isc_boolean_t match_mapped = server->aclenv.match_mapped;
4227135446Strhodes
4228135446Strhodes	ns_interfacemgr_scan(server->interfacemgr, verbose);
4229135446Strhodes	/*
4230135446Strhodes	 * Update the "localhost" and "localnets" ACLs to match the
4231135446Strhodes	 * current set of network interfaces.
4232135446Strhodes	 */
4233135446Strhodes	dns_aclenv_copy(&server->aclenv,
4234135446Strhodes			ns_interfacemgr_getaclenv(server->interfacemgr));
4235135446Strhodes
4236135446Strhodes	server->aclenv.match_mapped = match_mapped;
4237135446Strhodes}
4238135446Strhodes
4239135446Strhodesstatic isc_result_t
4240180477Sdougbadd_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
4241180477Sdougb	      isc_boolean_t wcardport_ok)
4242180477Sdougb{
4243135446Strhodes	ns_listenelt_t *lelt = NULL;
4244135446Strhodes	dns_acl_t *src_acl = NULL;
4245135446Strhodes	isc_result_t result;
4246135446Strhodes	isc_sockaddr_t any_sa6;
4247193149Sdougb	isc_netaddr_t netaddr;
4248135446Strhodes
4249135446Strhodes	REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
4250135446Strhodes
4251135446Strhodes	isc_sockaddr_any6(&any_sa6);
4252180477Sdougb	if (!isc_sockaddr_equal(&any_sa6, addr) &&
4253180477Sdougb	    (wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
4254193149Sdougb		isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr);
4255135446Strhodes
4256193149Sdougb		result = dns_acl_create(mctx, 0, &src_acl);
4257135446Strhodes		if (result != ISC_R_SUCCESS)
4258135446Strhodes			return (result);
4259193149Sdougb
4260193149Sdougb		result = dns_iptable_addprefix(src_acl->iptable,
4261193149Sdougb					       &netaddr, 128, ISC_TRUE);
4262135446Strhodes		if (result != ISC_R_SUCCESS)
4263135446Strhodes			goto clean;
4264135446Strhodes
4265135446Strhodes		result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr),
4266135446Strhodes					     src_acl, &lelt);
4267135446Strhodes		if (result != ISC_R_SUCCESS)
4268135446Strhodes			goto clean;
4269135446Strhodes		ISC_LIST_APPEND(list->elts, lelt, link);
4270135446Strhodes	}
4271135446Strhodes
4272135446Strhodes	return (ISC_R_SUCCESS);
4273135446Strhodes
4274135446Strhodes clean:
4275135446Strhodes	INSIST(lelt == NULL);
4276165071Sdougb	dns_acl_detach(&src_acl);
4277135446Strhodes
4278135446Strhodes	return (result);
4279135446Strhodes}
4280135446Strhodes
4281135446Strhodes/*
4282135446Strhodes * Make a list of xxx-source addresses and call ns_interfacemgr_adjust()
4283135446Strhodes * to update the listening interfaces accordingly.
4284135446Strhodes * We currently only consider IPv6, because this only affects IPv6 wildcard
4285135446Strhodes * sockets.
4286135446Strhodes */
4287135446Strhodesstatic void
4288135446Strhodesadjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
4289135446Strhodes	isc_result_t result;
4290135446Strhodes	ns_listenlist_t *list = NULL;
4291135446Strhodes	dns_view_t *view;
4292135446Strhodes	dns_zone_t *zone, *next;
4293135446Strhodes	isc_sockaddr_t addr, *addrp;
4294135446Strhodes
4295135446Strhodes	result = ns_listenlist_create(mctx, &list);
4296135446Strhodes	if (result != ISC_R_SUCCESS)
4297135446Strhodes		return;
4298135446Strhodes
4299135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
4300135446Strhodes	     view != NULL;
4301135446Strhodes	     view = ISC_LIST_NEXT(view, link)) {
4302135446Strhodes		dns_dispatch_t *dispatch6;
4303135446Strhodes
4304135446Strhodes		dispatch6 = dns_resolver_dispatchv6(view->resolver);
4305143731Sdougb		if (dispatch6 == NULL)
4306143731Sdougb			continue;
4307135446Strhodes		result = dns_dispatch_getlocaladdress(dispatch6, &addr);
4308135446Strhodes		if (result != ISC_R_SUCCESS)
4309135446Strhodes			goto fail;
4310180477Sdougb
4311180477Sdougb		/*
4312180477Sdougb		 * We always add non-wildcard address regardless of whether
4313180477Sdougb		 * the port is 'any' (the fourth arg is TRUE): if the port is
4314180477Sdougb		 * specific, we need to add it since it may conflict with a
4315180477Sdougb		 * listening interface; if it's zero, we'll dynamically open
4316180477Sdougb		 * query ports, and some of them may override an existing
4317180477Sdougb		 * wildcard IPv6 port.
4318180477Sdougb		 */
4319180477Sdougb		result = add_listenelt(mctx, list, &addr, ISC_TRUE);
4320135446Strhodes		if (result != ISC_R_SUCCESS)
4321135446Strhodes			goto fail;
4322135446Strhodes	}
4323135446Strhodes
4324135446Strhodes	zone = NULL;
4325135446Strhodes	for (result = dns_zone_first(server->zonemgr, &zone);
4326135446Strhodes	     result == ISC_R_SUCCESS;
4327135446Strhodes	     next = NULL, result = dns_zone_next(zone, &next), zone = next) {
4328135446Strhodes		dns_view_t *zoneview;
4329135446Strhodes
4330135446Strhodes		/*
4331135446Strhodes		 * At this point the zone list may contain a stale zone
4332135446Strhodes		 * just removed from the configuration.  To see the validity,
4333135446Strhodes		 * check if the corresponding view is in our current view list.
4334153816Sdougb		 * There may also be old zones that are still in the process
4335153816Sdougb		 * of shutting down and have detached from their old view
4336153816Sdougb		 * (zoneview == NULL).
4337135446Strhodes		 */
4338135446Strhodes		zoneview = dns_zone_getview(zone);
4339153816Sdougb		if (zoneview == NULL)
4340153816Sdougb			continue;
4341135446Strhodes		for (view = ISC_LIST_HEAD(server->viewlist);
4342135446Strhodes		     view != NULL && view != zoneview;
4343135446Strhodes		     view = ISC_LIST_NEXT(view, link))
4344135446Strhodes			;
4345135446Strhodes		if (view == NULL)
4346135446Strhodes			continue;
4347135446Strhodes
4348135446Strhodes		addrp = dns_zone_getnotifysrc6(zone);
4349180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
4350135446Strhodes		if (result != ISC_R_SUCCESS)
4351135446Strhodes			goto fail;
4352135446Strhodes
4353135446Strhodes		addrp = dns_zone_getxfrsource6(zone);
4354180477Sdougb		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
4355135446Strhodes		if (result != ISC_R_SUCCESS)
4356135446Strhodes			goto fail;
4357135446Strhodes	}
4358135446Strhodes
4359135446Strhodes	ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE);
4360186462Sdougb
4361135446Strhodes clean:
4362135446Strhodes	ns_listenlist_detach(&list);
4363135446Strhodes	return;
4364135446Strhodes
4365135446Strhodes fail:
4366135446Strhodes	/*
4367135446Strhodes	 * Even when we failed the procedure, most of other interfaces
4368135446Strhodes	 * should work correctly.  We therefore just warn it.
4369135446Strhodes	 */
4370135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4371135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
4372135446Strhodes		      "could not adjust the listen-on list; "
4373135446Strhodes		      "some interfaces may not work");
4374135446Strhodes	goto clean;
4375135446Strhodes}
4376135446Strhodes
4377135446Strhodes/*
4378135446Strhodes * This event callback is invoked to do periodic network
4379135446Strhodes * interface scanning.
4380135446Strhodes */
4381135446Strhodesstatic void
4382135446Strhodesinterface_timer_tick(isc_task_t *task, isc_event_t *event) {
4383135446Strhodes	isc_result_t result;
4384135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
4385135446Strhodes	INSIST(task == server->task);
4386135446Strhodes	UNUSED(task);
4387135446Strhodes	isc_event_free(&event);
4388135446Strhodes	/*
4389135446Strhodes	 * XXX should scan interfaces unlocked and get exclusive access
4390135446Strhodes	 * only to replace ACLs.
4391135446Strhodes	 */
4392135446Strhodes	result = isc_task_beginexclusive(server->task);
4393135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
4394135446Strhodes	scan_interfaces(server, ISC_FALSE);
4395135446Strhodes	isc_task_endexclusive(server->task);
4396135446Strhodes}
4397135446Strhodes
4398135446Strhodesstatic void
4399135446Strhodesheartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
4400135446Strhodes	ns_server_t *server = (ns_server_t *) event->ev_arg;
4401135446Strhodes	dns_view_t *view;
4402135446Strhodes
4403135446Strhodes	UNUSED(task);
4404135446Strhodes	isc_event_free(&event);
4405135446Strhodes	view = ISC_LIST_HEAD(server->viewlist);
4406135446Strhodes	while (view != NULL) {
4407135446Strhodes		dns_view_dialup(view);
4408135446Strhodes		view = ISC_LIST_NEXT(view, link);
4409135446Strhodes	}
4410135446Strhodes}
4411135446Strhodes
4412170222Sdougbstatic void
4413170222Sdougbpps_timer_tick(isc_task_t *task, isc_event_t *event) {
4414170222Sdougb	static unsigned int oldrequests = 0;
4415170222Sdougb	unsigned int requests = ns_client_requests;
4416170222Sdougb
4417170222Sdougb	UNUSED(task);
4418170222Sdougb	isc_event_free(&event);
4419170222Sdougb
4420170222Sdougb	/*
4421170222Sdougb	 * Don't worry about wrapping as the overflow result will be right.
4422170222Sdougb	 */
4423170222Sdougb	dns_pps = (requests - oldrequests) / 1200;
4424170222Sdougb	oldrequests = requests;
4425170222Sdougb}
4426170222Sdougb
4427135446Strhodes/*
4428135446Strhodes * Replace the current value of '*field', a dynamically allocated
4429135446Strhodes * string or NULL, with a dynamically allocated copy of the
4430135446Strhodes * null-terminated string pointed to by 'value', or NULL.
4431135446Strhodes */
4432135446Strhodesstatic isc_result_t
4433135446Strhodessetstring(ns_server_t *server, char **field, const char *value) {
4434135446Strhodes	char *copy;
4435135446Strhodes
4436135446Strhodes	if (value != NULL) {
4437135446Strhodes		copy = isc_mem_strdup(server->mctx, value);
4438135446Strhodes		if (copy == NULL)
4439135446Strhodes			return (ISC_R_NOMEMORY);
4440135446Strhodes	} else {
4441135446Strhodes		copy = NULL;
4442135446Strhodes	}
4443135446Strhodes
4444135446Strhodes	if (*field != NULL)
4445135446Strhodes		isc_mem_free(server->mctx, *field);
4446135446Strhodes
4447135446Strhodes	*field = copy;
4448135446Strhodes	return (ISC_R_SUCCESS);
4449186462Sdougb}
4450135446Strhodes
4451135446Strhodes/*
4452135446Strhodes * Replace the current value of '*field', a dynamically allocated
4453135446Strhodes * string or NULL, with another dynamically allocated string
4454135446Strhodes * or NULL if whether 'obj' is a string or void value, respectively.
4455135446Strhodes */
4456135446Strhodesstatic isc_result_t
4457165071Sdougbsetoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) {
4458135446Strhodes	if (cfg_obj_isvoid(obj))
4459135446Strhodes		return (setstring(server, field, NULL));
4460135446Strhodes	else
4461135446Strhodes		return (setstring(server, field, cfg_obj_asstring(obj)));
4462135446Strhodes}
4463135446Strhodes
4464135446Strhodesstatic void
4465165071Sdougbset_limit(const cfg_obj_t **maps, const char *configname,
4466165071Sdougb	  const char *description, isc_resource_t resourceid,
4467165071Sdougb	  isc_resourcevalue_t defaultvalue)
4468135446Strhodes{
4469165071Sdougb	const cfg_obj_t *obj = NULL;
4470165071Sdougb	const char *resource;
4471135446Strhodes	isc_resourcevalue_t value;
4472135446Strhodes	isc_result_t result;
4473135446Strhodes
4474135446Strhodes	if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS)
4475135446Strhodes		return;
4476135446Strhodes
4477135446Strhodes	if (cfg_obj_isstring(obj)) {
4478135446Strhodes		resource = cfg_obj_asstring(obj);
4479135446Strhodes		if (strcasecmp(resource, "unlimited") == 0)
4480135446Strhodes			value = ISC_RESOURCE_UNLIMITED;
4481135446Strhodes		else {
4482135446Strhodes			INSIST(strcasecmp(resource, "default") == 0);
4483135446Strhodes			value = defaultvalue;
4484135446Strhodes		}
4485135446Strhodes	} else
4486135446Strhodes		value = cfg_obj_asuint64(obj);
4487135446Strhodes
4488135446Strhodes	result = isc_resource_setlimit(resourceid, value);
4489135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4490135446Strhodes		      result == ISC_R_SUCCESS ?
4491186462Sdougb			ISC_LOG_DEBUG(3) : ISC_LOG_WARNING,
4492204619Sdougb		      "set maximum %s to %" ISC_PRINT_QUADFORMAT "u: %s",
4493135446Strhodes		      description, value, isc_result_totext(result));
4494135446Strhodes}
4495135446Strhodes
4496135446Strhodes#define SETLIMIT(cfgvar, resource, description) \
4497135446Strhodes	set_limit(maps, cfgvar, description, isc_resource_ ## resource, \
4498135446Strhodes		  ns_g_init ## resource)
4499135446Strhodes
4500135446Strhodesstatic void
4501165071Sdougbset_limits(const cfg_obj_t **maps) {
4502135446Strhodes	SETLIMIT("stacksize", stacksize, "stack size");
4503135446Strhodes	SETLIMIT("datasize", datasize, "data size");
4504135446Strhodes	SETLIMIT("coresize", coresize, "core size");
4505135446Strhodes	SETLIMIT("files", openfiles, "open files");
4506135446Strhodes}
4507135446Strhodes
4508186462Sdougbstatic void
4509186462Sdougbportset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports,
4510186462Sdougb		 isc_boolean_t positive)
4511135446Strhodes{
4512165071Sdougb	const cfg_listelt_t *element;
4513135446Strhodes
4514135446Strhodes	for (element = cfg_list_first(ports);
4515135446Strhodes	     element != NULL;
4516135446Strhodes	     element = cfg_list_next(element)) {
4517165071Sdougb		const cfg_obj_t *obj = cfg_listelt_value(element);
4518186462Sdougb
4519186462Sdougb		if (cfg_obj_isuint32(obj)) {
4520186462Sdougb			in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
4521186462Sdougb
4522186462Sdougb			if (positive)
4523186462Sdougb				isc_portset_add(portset, port);
4524186462Sdougb			else
4525186462Sdougb				isc_portset_remove(portset, port);
4526186462Sdougb		} else {
4527186462Sdougb			const cfg_obj_t *obj_loport, *obj_hiport;
4528186462Sdougb			in_port_t loport, hiport;
4529186462Sdougb
4530186462Sdougb			obj_loport = cfg_tuple_get(obj, "loport");
4531186462Sdougb			loport = (in_port_t)cfg_obj_asuint32(obj_loport);
4532186462Sdougb			obj_hiport = cfg_tuple_get(obj, "hiport");
4533186462Sdougb			hiport = (in_port_t)cfg_obj_asuint32(obj_hiport);
4534186462Sdougb
4535186462Sdougb			if (positive)
4536186462Sdougb				isc_portset_addrange(portset, loport, hiport);
4537186462Sdougb			else {
4538186462Sdougb				isc_portset_removerange(portset, loport,
4539186462Sdougb							hiport);
4540186462Sdougb			}
4541186462Sdougb		}
4542135446Strhodes	}
4543135446Strhodes}
4544135446Strhodes
4545135446Strhodesstatic isc_result_t
4546170222Sdougbremoved(dns_zone_t *zone, void *uap) {
4547170222Sdougb	const char *type;
4548170222Sdougb
4549186462Sdougb	if (dns_zone_getview(zone) != uap)
4550170222Sdougb		return (ISC_R_SUCCESS);
4551170222Sdougb
4552170222Sdougb	switch (dns_zone_gettype(zone)) {
4553170222Sdougb	case dns_zone_master:
4554170222Sdougb		type = "master";
4555170222Sdougb		break;
4556170222Sdougb	case dns_zone_slave:
4557170222Sdougb		type = "slave";
4558170222Sdougb		break;
4559170222Sdougb	case dns_zone_stub:
4560170222Sdougb		type = "stub";
4561170222Sdougb		break;
4562254897Serwin	case dns_zone_redirect:
4563254897Serwin		type = "redirect";
4564254897Serwin		break;
4565170222Sdougb	default:
4566170222Sdougb		type = "other";
4567170222Sdougb		break;
4568170222Sdougb	}
4569170222Sdougb	dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type);
4570170222Sdougb	return (ISC_R_SUCCESS);
4571170222Sdougb}
4572170222Sdougb
4573224092Sdougbstatic void
4574224092Sdougbcleanup_session_key(ns_server_t *server, isc_mem_t *mctx) {
4575224092Sdougb	if (server->session_keyfile != NULL) {
4576224092Sdougb		isc_file_remove(server->session_keyfile);
4577224092Sdougb		isc_mem_free(mctx, server->session_keyfile);
4578224092Sdougb		server->session_keyfile = NULL;
4579224092Sdougb	}
4580224092Sdougb
4581224092Sdougb	if (server->session_keyname != NULL) {
4582224092Sdougb		if (dns_name_dynamic(server->session_keyname))
4583224092Sdougb			dns_name_free(server->session_keyname, mctx);
4584224092Sdougb		isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t));
4585224092Sdougb		server->session_keyname = NULL;
4586224092Sdougb	}
4587224092Sdougb
4588224092Sdougb	if (server->sessionkey != NULL)
4589224092Sdougb		dns_tsigkey_detach(&server->sessionkey);
4590224092Sdougb
4591224092Sdougb	server->session_keyalg = DST_ALG_UNKNOWN;
4592224092Sdougb	server->session_keybits = 0;
4593224092Sdougb}
4594224092Sdougb
4595170222Sdougbstatic isc_result_t
4596224092Sdougbgenerate_session_key(const char *filename, const char *keynamestr,
4597224092Sdougb		     dns_name_t *keyname, const char *algstr,
4598224092Sdougb		     dns_name_t *algname, unsigned int algtype,
4599224092Sdougb		     isc_uint16_t bits, isc_mem_t *mctx,
4600224092Sdougb		     dns_tsigkey_t **tsigkeyp)
4601224092Sdougb{
4602224092Sdougb	isc_result_t result = ISC_R_SUCCESS;
4603224092Sdougb	dst_key_t *key = NULL;
4604224092Sdougb	isc_buffer_t key_txtbuffer;
4605224092Sdougb	isc_buffer_t key_rawbuffer;
4606224092Sdougb	char key_txtsecret[256];
4607224092Sdougb	char key_rawsecret[64];
4608224092Sdougb	isc_region_t key_rawregion;
4609224092Sdougb	isc_stdtime_t now;
4610224092Sdougb	dns_tsigkey_t *tsigkey = NULL;
4611224092Sdougb	FILE *fp = NULL;
4612224092Sdougb
4613224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4614224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4615224092Sdougb		      "generating session key for dynamic DNS");
4616224092Sdougb
4617224092Sdougb	/* generate key */
4618224092Sdougb	result = dst_key_generate(keyname, algtype, bits, 1, 0,
4619224092Sdougb				  DNS_KEYPROTO_ANY, dns_rdataclass_in,
4620224092Sdougb				  mctx, &key);
4621224092Sdougb	if (result != ISC_R_SUCCESS)
4622224092Sdougb		return (result);
4623224092Sdougb
4624224092Sdougb	/*
4625224092Sdougb	 * Dump the key to the buffer for later use.  Should be done before
4626224092Sdougb	 * we transfer the ownership of key to tsigkey.
4627224092Sdougb	 */
4628224092Sdougb	isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));
4629224092Sdougb	CHECK(dst_key_tobuffer(key, &key_rawbuffer));
4630224092Sdougb
4631224092Sdougb	isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);
4632224092Sdougb	isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret));
4633224092Sdougb	CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer));
4634224092Sdougb
4635224092Sdougb	/* Store the key in tsigkey. */
4636224092Sdougb	isc_stdtime_get(&now);
4637224092Sdougb	CHECK(dns_tsigkey_createfromkey(dst_key_name(key), algname, key,
4638224092Sdougb					ISC_FALSE, NULL, now, now, mctx, NULL,
4639224092Sdougb					&tsigkey));
4640224092Sdougb
4641224092Sdougb	/* Dump the key to the key file. */
4642224092Sdougb	fp = ns_os_openfile(filename, S_IRUSR|S_IWUSR, ISC_TRUE);
4643224092Sdougb	if (fp == NULL) {
4644224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4645224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4646224092Sdougb			      "could not create %s", filename);
4647224092Sdougb		result = ISC_R_NOPERM;
4648224092Sdougb		goto cleanup;
4649224092Sdougb	}
4650224092Sdougb
4651224092Sdougb	fprintf(fp, "key \"%s\" {\n"
4652224092Sdougb		"\talgorithm %s;\n"
4653224092Sdougb		"\tsecret \"%.*s\";\n};\n", keynamestr, algstr,
4654224092Sdougb		(int) isc_buffer_usedlength(&key_txtbuffer),
4655224092Sdougb		(char*) isc_buffer_base(&key_txtbuffer));
4656224092Sdougb
4657224092Sdougb	RUNTIME_CHECK(isc_stdio_flush(fp) == ISC_R_SUCCESS);
4658224092Sdougb	RUNTIME_CHECK(isc_stdio_close(fp) == ISC_R_SUCCESS);
4659224092Sdougb
4660224092Sdougb	dst_key_free(&key);
4661224092Sdougb
4662224092Sdougb	*tsigkeyp = tsigkey;
4663224092Sdougb
4664224092Sdougb	return (ISC_R_SUCCESS);
4665224092Sdougb
4666224092Sdougb  cleanup:
4667224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4668224092Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
4669224092Sdougb		      "failed to generate session key "
4670224092Sdougb		      "for dynamic DNS: %s", isc_result_totext(result));
4671224092Sdougb	if (tsigkey != NULL)
4672224092Sdougb		dns_tsigkey_detach(&tsigkey);
4673224092Sdougb	if (key != NULL)
4674224092Sdougb		dst_key_free(&key);
4675224092Sdougb
4676224092Sdougb	return (result);
4677224092Sdougb}
4678224092Sdougb
4679224092Sdougbstatic isc_result_t
4680224092Sdougbconfigure_session_key(const cfg_obj_t **maps, ns_server_t *server,
4681224092Sdougb		      isc_mem_t *mctx)
4682224092Sdougb{
4683224092Sdougb	const char *keyfile, *keynamestr, *algstr;
4684224092Sdougb	unsigned int algtype;
4685224092Sdougb	dns_fixedname_t fname;
4686224092Sdougb	dns_name_t *keyname, *algname;
4687224092Sdougb	isc_buffer_t buffer;
4688224092Sdougb	isc_uint16_t bits;
4689224092Sdougb	const cfg_obj_t *obj;
4690224092Sdougb	isc_boolean_t need_deleteold = ISC_FALSE;
4691224092Sdougb	isc_boolean_t need_createnew = ISC_FALSE;
4692224092Sdougb	isc_result_t result;
4693224092Sdougb
4694224092Sdougb	obj = NULL;
4695224092Sdougb	result = ns_config_get(maps, "session-keyfile", &obj);
4696224092Sdougb	if (result == ISC_R_SUCCESS) {
4697224092Sdougb		if (cfg_obj_isvoid(obj))
4698224092Sdougb			keyfile = NULL; /* disable it */
4699224092Sdougb		else
4700224092Sdougb			keyfile = cfg_obj_asstring(obj);
4701224092Sdougb	} else
4702224092Sdougb		keyfile = ns_g_defaultsessionkeyfile;
4703224092Sdougb
4704224092Sdougb	obj = NULL;
4705224092Sdougb	result = ns_config_get(maps, "session-keyname", &obj);
4706224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4707224092Sdougb	keynamestr = cfg_obj_asstring(obj);
4708224092Sdougb	dns_fixedname_init(&fname);
4709254402Serwin	isc_buffer_constinit(&buffer, keynamestr, strlen(keynamestr));
4710224092Sdougb	isc_buffer_add(&buffer, strlen(keynamestr));
4711224092Sdougb	keyname = dns_fixedname_name(&fname);
4712224092Sdougb	result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL);
4713224092Sdougb	if (result != ISC_R_SUCCESS)
4714224092Sdougb		return (result);
4715224092Sdougb
4716224092Sdougb	obj = NULL;
4717224092Sdougb	result = ns_config_get(maps, "session-keyalg", &obj);
4718224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4719224092Sdougb	algstr = cfg_obj_asstring(obj);
4720224092Sdougb	algname = NULL;
4721224092Sdougb	result = ns_config_getkeyalgorithm2(algstr, &algname, &algtype, &bits);
4722224092Sdougb	if (result != ISC_R_SUCCESS) {
4723224092Sdougb		const char *s = " (keeping current key)";
4724224092Sdougb
4725224092Sdougb		cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "session-keyalg: "
4726224092Sdougb			    "unsupported or unknown algorithm '%s'%s",
4727224092Sdougb			    algstr,
4728224092Sdougb			    server->session_keyfile != NULL ? s : "");
4729224092Sdougb		return (result);
4730224092Sdougb	}
4731224092Sdougb
4732224092Sdougb	/* See if we need to (re)generate a new key. */
4733224092Sdougb	if (keyfile == NULL) {
4734224092Sdougb		if (server->session_keyfile != NULL)
4735224092Sdougb			need_deleteold = ISC_TRUE;
4736224092Sdougb	} else if (server->session_keyfile == NULL)
4737224092Sdougb		need_createnew = ISC_TRUE;
4738224092Sdougb	else if (strcmp(keyfile, server->session_keyfile) != 0 ||
4739224092Sdougb		 !dns_name_equal(server->session_keyname, keyname) ||
4740224092Sdougb		 server->session_keyalg != algtype ||
4741224092Sdougb		 server->session_keybits != bits) {
4742224092Sdougb		need_deleteold = ISC_TRUE;
4743224092Sdougb		need_createnew = ISC_TRUE;
4744224092Sdougb	}
4745224092Sdougb
4746224092Sdougb	if (need_deleteold) {
4747224092Sdougb		INSIST(server->session_keyfile != NULL);
4748224092Sdougb		INSIST(server->session_keyname != NULL);
4749224092Sdougb		INSIST(server->sessionkey != NULL);
4750224092Sdougb
4751224092Sdougb		cleanup_session_key(server, mctx);
4752224092Sdougb	}
4753224092Sdougb
4754224092Sdougb	if (need_createnew) {
4755224092Sdougb		INSIST(server->sessionkey == NULL);
4756224092Sdougb		INSIST(server->session_keyfile == NULL);
4757224092Sdougb		INSIST(server->session_keyname == NULL);
4758224092Sdougb		INSIST(server->session_keyalg == DST_ALG_UNKNOWN);
4759224092Sdougb		INSIST(server->session_keybits == 0);
4760224092Sdougb
4761224092Sdougb		server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t));
4762224092Sdougb		if (server->session_keyname == NULL)
4763224092Sdougb			goto cleanup;
4764224092Sdougb		dns_name_init(server->session_keyname, NULL);
4765224092Sdougb		CHECK(dns_name_dup(keyname, mctx, server->session_keyname));
4766224092Sdougb
4767224092Sdougb		server->session_keyfile = isc_mem_strdup(mctx, keyfile);
4768224092Sdougb		if (server->session_keyfile == NULL)
4769224092Sdougb			goto cleanup;
4770224092Sdougb
4771224092Sdougb		server->session_keyalg = algtype;
4772224092Sdougb		server->session_keybits = bits;
4773224092Sdougb
4774224092Sdougb		CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr,
4775224092Sdougb					   algname, algtype, bits, mctx,
4776224092Sdougb					   &server->sessionkey));
4777224092Sdougb	}
4778224092Sdougb
4779224092Sdougb	return (result);
4780224092Sdougb
4781224092Sdougb  cleanup:
4782224092Sdougb	cleanup_session_key(server, mctx);
4783224092Sdougb	return (result);
4784224092Sdougb}
4785224092Sdougb
4786224092Sdougbstatic isc_result_t
4787225361Sdougbsetup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
4788225361Sdougb	       cfg_parser_t *parser, cfg_aclconfctx_t *actx)
4789225361Sdougb{
4790225361Sdougb	isc_result_t result = ISC_R_SUCCESS;
4791225361Sdougb	isc_boolean_t allow = ISC_FALSE;
4792225361Sdougb	struct cfg_context *nzcfg = NULL;
4793225361Sdougb	cfg_parser_t *nzparser = NULL;
4794225361Sdougb	cfg_obj_t *nzconfig = NULL;
4795225361Sdougb	const cfg_obj_t *maps[4];
4796225361Sdougb	const cfg_obj_t *options = NULL, *voptions = NULL;
4797225361Sdougb	const cfg_obj_t *nz = NULL;
4798225361Sdougb	int i = 0;
4799225361Sdougb
4800225361Sdougb	REQUIRE (config != NULL);
4801225361Sdougb
4802225361Sdougb	if (vconfig != NULL)
4803225361Sdougb		voptions = cfg_tuple_get(vconfig, "options");
4804225361Sdougb	if (voptions != NULL)
4805225361Sdougb		maps[i++] = voptions;
4806225361Sdougb	result = cfg_map_get(config, "options", &options);
4807225361Sdougb	if (result == ISC_R_SUCCESS)
4808225361Sdougb		maps[i++] = options;
4809225361Sdougb	maps[i++] = ns_g_defaults;
4810225361Sdougb	maps[i] = NULL;
4811225361Sdougb
4812225361Sdougb	result = ns_config_get(maps, "allow-new-zones", &nz);
4813225361Sdougb	if (result == ISC_R_SUCCESS)
4814225361Sdougb		allow = cfg_obj_asboolean(nz);
4815225361Sdougb
4816225361Sdougb	if (!allow) {
4817225361Sdougb		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
4818225361Sdougb		return (ISC_R_SUCCESS);
4819225361Sdougb	}
4820225361Sdougb
4821225361Sdougb	nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg));
4822225361Sdougb	if (nzcfg == NULL) {
4823225361Sdougb		dns_view_setnewzones(view, ISC_FALSE, NULL, NULL);
4824225361Sdougb		return (ISC_R_NOMEMORY);
4825225361Sdougb	}
4826225361Sdougb
4827225361Sdougb	dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy);
4828225361Sdougb
4829225361Sdougb	memset(nzcfg, 0, sizeof(*nzcfg));
4830225361Sdougb	isc_mem_attach(view->mctx, &nzcfg->mctx);
4831225361Sdougb	cfg_obj_attach(config, &nzcfg->config);
4832225361Sdougb	cfg_parser_attach(parser, &nzcfg->parser);
4833225361Sdougb	cfg_aclconfctx_attach(actx, &nzcfg->actx);
4834225361Sdougb
4835225361Sdougb	/*
4836225361Sdougb	 * Attempt to create a parser and parse the newzones
4837225361Sdougb	 * file.  If successful, preserve both; otherwise leave
4838225361Sdougb	 * them NULL.
4839225361Sdougb	 */
4840225361Sdougb	result = cfg_parser_create(view->mctx, ns_g_lctx, &nzparser);
4841225361Sdougb	if (result == ISC_R_SUCCESS)
4842225361Sdougb		result = cfg_parse_file(nzparser, view->new_zone_file,
4843225361Sdougb					&cfg_type_newzones, &nzconfig);
4844225361Sdougb	if (result == ISC_R_SUCCESS) {
4845225361Sdougb		cfg_parser_attach(nzparser, &nzcfg->nzparser);
4846225361Sdougb		cfg_obj_attach(nzconfig, &nzcfg->nzconfig);
4847225361Sdougb	}
4848225361Sdougb
4849225361Sdougb	if (nzparser != NULL) {
4850225361Sdougb		if (nzconfig != NULL)
4851225361Sdougb			cfg_obj_destroy(nzparser, &nzconfig);
4852225361Sdougb		cfg_parser_destroy(&nzparser);
4853225361Sdougb	}
4854225361Sdougb
4855225361Sdougb	return (ISC_R_SUCCESS);
4856225361Sdougb}
4857225361Sdougb
4858225361Sdougbstatic int
4859225361Sdougbcount_zones(const cfg_obj_t *conf) {
4860225361Sdougb	const cfg_obj_t *zonelist = NULL;
4861225361Sdougb	const cfg_listelt_t *element;
4862225361Sdougb	int n = 0;
4863225361Sdougb
4864225361Sdougb	REQUIRE(conf != NULL);
4865225361Sdougb
4866225361Sdougb	cfg_map_get(conf, "zone", &zonelist);
4867225361Sdougb	for (element = cfg_list_first(zonelist);
4868225361Sdougb	     element != NULL;
4869225361Sdougb	     element = cfg_list_next(element))
4870225361Sdougb		n++;
4871225361Sdougb
4872225361Sdougb	return (n);
4873225361Sdougb}
4874225361Sdougb
4875225361Sdougbstatic isc_result_t
4876135446Strhodesload_configuration(const char *filename, ns_server_t *server,
4877135446Strhodes		   isc_boolean_t first_time)
4878135446Strhodes{
4879224092Sdougb	cfg_obj_t *config = NULL, *bindkeys = NULL;
4880224092Sdougb	cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL;
4881182645Sdougb	const cfg_listelt_t *element;
4882182645Sdougb	const cfg_obj_t *builtin_views;
4883182645Sdougb	const cfg_obj_t *maps[3];
4884182645Sdougb	const cfg_obj_t *obj;
4885165071Sdougb	const cfg_obj_t *options;
4886186462Sdougb	const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports;
4887165071Sdougb	const cfg_obj_t *views;
4888135446Strhodes	dns_view_t *view = NULL;
4889135446Strhodes	dns_view_t *view_next;
4890182645Sdougb	dns_viewlist_t tmpviewlist;
4891224092Sdougb	dns_viewlist_t viewlist, builtin_viewlist;
4892186462Sdougb	in_port_t listen_port, udpport_low, udpport_high;
4893182645Sdougb	int i;
4894262706Serwin	int num_zones = 0;
4895262706Serwin	isc_boolean_t exclusive = ISC_FALSE;
4896182645Sdougb	isc_interval_t interval;
4897262706Serwin	isc_logconfig_t *logc = NULL;
4898186462Sdougb	isc_portset_t *v4portset = NULL;
4899186462Sdougb	isc_portset_t *v6portset = NULL;
4900186462Sdougb	isc_resourcevalue_t nfiles;
4901182645Sdougb	isc_result_t result;
4902182645Sdougb	isc_uint32_t heartbeat_interval;
4903135446Strhodes	isc_uint32_t interface_interval;
4904182645Sdougb	isc_uint32_t reserved;
4905135446Strhodes	isc_uint32_t udpsize;
4906262706Serwin	ns_cache_t *nsc;
4907224092Sdougb	ns_cachelist_t cachelist, tmpcachelist;
4908262706Serwin	struct cfg_context *nzctx;
4909186462Sdougb	unsigned int maxsocks;
4910135446Strhodes
4911135446Strhodes	ISC_LIST_INIT(viewlist);
4912224092Sdougb	ISC_LIST_INIT(builtin_viewlist);
4913224092Sdougb	ISC_LIST_INIT(cachelist);
4914135446Strhodes
4915225361Sdougb	/* Create the ACL configuration context */
4916225361Sdougb	if (ns_g_aclconfctx != NULL)
4917225361Sdougb		cfg_aclconfctx_detach(&ns_g_aclconfctx);
4918225361Sdougb	CHECK(cfg_aclconfctx_create(ns_g_mctx, &ns_g_aclconfctx));
4919225361Sdougb
4920135446Strhodes	/*
4921135446Strhodes	 * Parse the global default pseudo-config file.
4922135446Strhodes	 */
4923135446Strhodes	if (first_time) {
4924135446Strhodes		CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config));
4925135446Strhodes		RUNTIME_CHECK(cfg_map_get(ns_g_config, "options",
4926224092Sdougb					  &ns_g_defaults) == ISC_R_SUCCESS);
4927135446Strhodes	}
4928135446Strhodes
4929135446Strhodes	/*
4930135446Strhodes	 * Parse the configuration file using the new config code.
4931135446Strhodes	 */
4932135446Strhodes	result = ISC_R_FAILURE;
4933135446Strhodes	config = NULL;
4934135446Strhodes
4935135446Strhodes	/*
4936135446Strhodes	 * Unless this is lwresd with the -C option, parse the config file.
4937135446Strhodes	 */
4938135446Strhodes	if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) {
4939135446Strhodes		isc_log_write(ns_g_lctx,
4940135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4941135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
4942135446Strhodes			      filename);
4943224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
4944224092Sdougb		cfg_parser_setcallback(conf_parser, directory_callback, NULL);
4945224092Sdougb		result = cfg_parse_file(conf_parser, filename,
4946224092Sdougb					&cfg_type_namedconf, &config);
4947135446Strhodes	}
4948135446Strhodes
4949135446Strhodes	/*
4950135446Strhodes	 * If this is lwresd with the -C option, or lwresd with no -C or -c
4951135446Strhodes	 * option where the above parsing failed, parse resolv.conf.
4952135446Strhodes	 */
4953135446Strhodes	if (ns_g_lwresdonly &&
4954135446Strhodes	    (lwresd_g_useresolvconf ||
4955135446Strhodes	     (!ns_g_conffileset && result == ISC_R_FILENOTFOUND)))
4956135446Strhodes	{
4957135446Strhodes		isc_log_write(ns_g_lctx,
4958135446Strhodes			      NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
4959135446Strhodes			      ISC_LOG_INFO, "loading configuration from '%s'",
4960135446Strhodes			      lwresd_g_resolvconffile);
4961224092Sdougb		if (conf_parser != NULL)
4962224092Sdougb			cfg_parser_destroy(&conf_parser);
4963224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
4964224092Sdougb		result = ns_lwresd_parseeresolvconf(ns_g_mctx, conf_parser,
4965135446Strhodes						    &config);
4966135446Strhodes	}
4967135446Strhodes	CHECK(result);
4968135446Strhodes
4969135446Strhodes	/*
4970135446Strhodes	 * Check the validity of the configuration.
4971135446Strhodes	 */
4972135446Strhodes	CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx));
4973135446Strhodes
4974135446Strhodes	/*
4975135446Strhodes	 * Fill in the maps array, used for resolving defaults.
4976135446Strhodes	 */
4977135446Strhodes	i = 0;
4978135446Strhodes	options = NULL;
4979135446Strhodes	result = cfg_map_get(config, "options", &options);
4980135446Strhodes	if (result == ISC_R_SUCCESS)
4981135446Strhodes		maps[i++] = options;
4982135446Strhodes	maps[i++] = ns_g_defaults;
4983225361Sdougb	maps[i] = NULL;
4984135446Strhodes
4985135446Strhodes	/*
4986224092Sdougb	 * If bind.keys exists, load it.  If "dnssec-lookaside auto"
4987224092Sdougb	 * is turned on, the keys found there will be used as default
4988224092Sdougb	 * trust anchors.
4989224092Sdougb	 */
4990224092Sdougb	obj = NULL;
4991224092Sdougb	result = ns_config_get(maps, "bindkeys-file", &obj);
4992224092Sdougb	INSIST(result == ISC_R_SUCCESS);
4993224092Sdougb	CHECKM(setstring(server, &server->bindkeysfile,
4994224092Sdougb	       cfg_obj_asstring(obj)), "strdup");
4995224092Sdougb
4996224092Sdougb	if (access(server->bindkeysfile, R_OK) == 0) {
4997224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
4998224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
4999224092Sdougb			      "reading built-in trusted "
5000224092Sdougb			      "keys from file '%s'", server->bindkeysfile);
5001224092Sdougb
5002224092Sdougb		CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx,
5003224092Sdougb					&bindkeys_parser));
5004224092Sdougb
5005224092Sdougb		result = cfg_parse_file(bindkeys_parser, server->bindkeysfile,
5006224092Sdougb					&cfg_type_bindkeys, &bindkeys);
5007224092Sdougb		CHECK(result);
5008224092Sdougb	}
5009224092Sdougb
5010234010Sdougb	/* Ensure exclusive access to configuration data. */
5011234010Sdougb	if (!exclusive) {
5012234010Sdougb		result = isc_task_beginexclusive(server->task);
5013234010Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5014234010Sdougb		exclusive = ISC_TRUE;
5015234010Sdougb	}
5016234010Sdougb
5017224092Sdougb	/*
5018135446Strhodes	 * Set process limits, which (usually) needs to be done as root.
5019135446Strhodes	 */
5020135446Strhodes	set_limits(maps);
5021135446Strhodes
5022135446Strhodes	/*
5023186462Sdougb	 * Check if max number of open sockets that the system allows is
5024224092Sdougb	 * sufficiently large.	Failing this condition is not necessarily fatal,
5025186462Sdougb	 * but may cause subsequent runtime failures for a busy recursive
5026186462Sdougb	 * server.
5027182645Sdougb	 */
5028186462Sdougb	result = isc_socketmgr_getmaxsockets(ns_g_socketmgr, &maxsocks);
5029186462Sdougb	if (result != ISC_R_SUCCESS)
5030186462Sdougb		maxsocks = 0;
5031186462Sdougb	result = isc_resource_getcurlimit(isc_resource_openfiles, &nfiles);
5032186462Sdougb	if (result == ISC_R_SUCCESS && (isc_resourcevalue_t)maxsocks > nfiles) {
5033182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5034182645Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
5035186462Sdougb			      "max open files (%" ISC_PRINT_QUADFORMAT "u)"
5036186462Sdougb			      " is smaller than max sockets (%u)",
5037186462Sdougb			      nfiles, maxsocks);
5038186462Sdougb	}
5039182645Sdougb
5040182645Sdougb	/*
5041182645Sdougb	 * Set the number of socket reserved for TCP, stdio etc.
5042182645Sdougb	 */
5043182645Sdougb	obj = NULL;
5044182645Sdougb	result = ns_config_get(maps, "reserved-sockets", &obj);
5045182645Sdougb	INSIST(result == ISC_R_SUCCESS);
5046182645Sdougb	reserved = cfg_obj_asuint32(obj);
5047186462Sdougb	if (maxsocks != 0) {
5048186462Sdougb		if (maxsocks < 128U)			/* Prevent underflow. */
5049186462Sdougb			reserved = 0;
5050186462Sdougb		else if (reserved > maxsocks - 128U)	/* Minimum UDP space. */
5051186462Sdougb			reserved = maxsocks - 128;
5052186462Sdougb	}
5053186462Sdougb	/* Minimum TCP/stdio space. */
5054186462Sdougb	if (reserved < 128U)
5055182645Sdougb		reserved = 128;
5056186462Sdougb	if (reserved + 128U > maxsocks && maxsocks != 0) {
5057182645Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5058186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
5059182645Sdougb			      "less than 128 UDP sockets available after "
5060186462Sdougb			      "applying 'reserved-sockets' and 'maxsockets'");
5061182645Sdougb	}
5062182645Sdougb	isc__socketmgr_setreserved(ns_g_socketmgr, reserved);
5063186462Sdougb
5064182645Sdougb	/*
5065135446Strhodes	 * Configure various server options.
5066135446Strhodes	 */
5067135446Strhodes	configure_server_quota(maps, "transfers-out", &server->xfroutquota);
5068135446Strhodes	configure_server_quota(maps, "tcp-clients", &server->tcpquota);
5069135446Strhodes	configure_server_quota(maps, "recursive-clients",
5070135446Strhodes			       &server->recursionquota);
5071153816Sdougb	if (server->recursionquota.max > 1000)
5072153816Sdougb		isc_quota_soft(&server->recursionquota,
5073153816Sdougb			       server->recursionquota.max - 100);
5074153816Sdougb	else
5075153816Sdougb		isc_quota_soft(&server->recursionquota, 0);
5076135446Strhodes
5077225361Sdougb	CHECK(configure_view_acl(NULL, config, "blackhole", NULL,
5078225361Sdougb				 ns_g_aclconfctx, ns_g_mctx,
5079225361Sdougb				 &server->blackholeacl));
5080135446Strhodes	if (server->blackholeacl != NULL)
5081135446Strhodes		dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
5082135446Strhodes					     server->blackholeacl);
5083135446Strhodes
5084135446Strhodes	obj = NULL;
5085135446Strhodes	result = ns_config_get(maps, "match-mapped-addresses", &obj);
5086135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5087135446Strhodes	server->aclenv.match_mapped = cfg_obj_asboolean(obj);
5088135446Strhodes
5089225361Sdougb	CHECKM(ns_statschannels_configure(ns_g_server, config, ns_g_aclconfctx),
5090193149Sdougb	       "configuring statistics server(s)");
5091193149Sdougb
5092186462Sdougb	/*
5093186462Sdougb	 * Configure sets of UDP query source ports.
5094186462Sdougb	 */
5095186462Sdougb	CHECKM(isc_portset_create(ns_g_mctx, &v4portset),
5096186462Sdougb	       "creating UDP port set");
5097186462Sdougb	CHECKM(isc_portset_create(ns_g_mctx, &v6portset),
5098186462Sdougb	       "creating UDP port set");
5099135446Strhodes
5100186462Sdougb	usev4ports = NULL;
5101186462Sdougb	usev6ports = NULL;
5102186462Sdougb	avoidv4ports = NULL;
5103186462Sdougb	avoidv6ports = NULL;
5104186462Sdougb
5105186462Sdougb	(void)ns_config_get(maps, "use-v4-udp-ports", &usev4ports);
5106186462Sdougb	if (usev4ports != NULL)
5107186462Sdougb		portset_fromconf(v4portset, usev4ports, ISC_TRUE);
5108186462Sdougb	else {
5109186462Sdougb		CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low,
5110186462Sdougb					       &udpport_high),
5111186462Sdougb		       "get the default UDP/IPv4 port range");
5112186462Sdougb		if (udpport_low == udpport_high)
5113186462Sdougb			isc_portset_add(v4portset, udpport_low);
5114186462Sdougb		else {
5115186462Sdougb			isc_portset_addrange(v4portset, udpport_low,
5116186462Sdougb					     udpport_high);
5117186462Sdougb		}
5118186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5119186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5120186462Sdougb			      "using default UDP/IPv4 port range: [%d, %d]",
5121186462Sdougb			      udpport_low, udpport_high);
5122186462Sdougb	}
5123186462Sdougb	(void)ns_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports);
5124186462Sdougb	if (avoidv4ports != NULL)
5125186462Sdougb		portset_fromconf(v4portset, avoidv4ports, ISC_FALSE);
5126186462Sdougb
5127186462Sdougb	(void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports);
5128186462Sdougb	if (usev6ports != NULL)
5129186462Sdougb		portset_fromconf(v6portset, usev6ports, ISC_TRUE);
5130186462Sdougb	else {
5131186462Sdougb		CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low,
5132186462Sdougb					       &udpport_high),
5133186462Sdougb		       "get the default UDP/IPv6 port range");
5134186462Sdougb		if (udpport_low == udpport_high)
5135186462Sdougb			isc_portset_add(v6portset, udpport_low);
5136186462Sdougb		else {
5137186462Sdougb			isc_portset_addrange(v6portset, udpport_low,
5138186462Sdougb					     udpport_high);
5139186462Sdougb		}
5140186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5141186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5142186462Sdougb			      "using default UDP/IPv6 port range: [%d, %d]",
5143186462Sdougb			      udpport_low, udpport_high);
5144186462Sdougb	}
5145186462Sdougb	(void)ns_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports);
5146186462Sdougb	if (avoidv6ports != NULL)
5147186462Sdougb		portset_fromconf(v6portset, avoidv6ports, ISC_FALSE);
5148186462Sdougb
5149186462Sdougb	dns_dispatchmgr_setavailports(ns_g_dispatchmgr, v4portset, v6portset);
5150186462Sdougb
5151135446Strhodes	/*
5152135446Strhodes	 * Set the EDNS UDP size when we don't match a view.
5153135446Strhodes	 */
5154135446Strhodes	obj = NULL;
5155135446Strhodes	result = ns_config_get(maps, "edns-udp-size", &obj);
5156135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5157135446Strhodes	udpsize = cfg_obj_asuint32(obj);
5158135446Strhodes	if (udpsize < 512)
5159135446Strhodes		udpsize = 512;
5160135446Strhodes	if (udpsize > 4096)
5161135446Strhodes		udpsize = 4096;
5162135446Strhodes	ns_g_udpsize = (isc_uint16_t)udpsize;
5163135446Strhodes
5164135446Strhodes	/*
5165135446Strhodes	 * Configure the zone manager.
5166135446Strhodes	 */
5167135446Strhodes	obj = NULL;
5168135446Strhodes	result = ns_config_get(maps, "transfers-in", &obj);
5169135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5170135446Strhodes	dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj));
5171135446Strhodes
5172135446Strhodes	obj = NULL;
5173135446Strhodes	result = ns_config_get(maps, "transfers-per-ns", &obj);
5174135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5175135446Strhodes	dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj));
5176135446Strhodes
5177135446Strhodes	obj = NULL;
5178135446Strhodes	result = ns_config_get(maps, "serial-query-rate", &obj);
5179135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5180135446Strhodes	dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj));
5181135446Strhodes
5182135446Strhodes	/*
5183135446Strhodes	 * Determine which port to use for listening for incoming connections.
5184135446Strhodes	 */
5185135446Strhodes	if (ns_g_port != 0)
5186135446Strhodes		listen_port = ns_g_port;
5187135446Strhodes	else
5188135446Strhodes		CHECKM(ns_config_getport(config, &listen_port), "port");
5189135446Strhodes
5190135446Strhodes	/*
5191135446Strhodes	 * Find the listen queue depth.
5192135446Strhodes	 */
5193135446Strhodes	obj = NULL;
5194135446Strhodes	result = ns_config_get(maps, "tcp-listen-queue", &obj);
5195135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5196135446Strhodes	ns_g_listen = cfg_obj_asuint32(obj);
5197262706Serwin	if ((ns_g_listen > 0) && (ns_g_listen < 10))
5198262706Serwin		ns_g_listen = 10;
5199135446Strhodes
5200135446Strhodes	/*
5201135446Strhodes	 * Configure the interface manager according to the "listen-on"
5202135446Strhodes	 * statement.
5203135446Strhodes	 */
5204135446Strhodes	{
5205165071Sdougb		const cfg_obj_t *clistenon = NULL;
5206135446Strhodes		ns_listenlist_t *listenon = NULL;
5207135446Strhodes
5208135446Strhodes		clistenon = NULL;
5209135446Strhodes		/*
5210135446Strhodes		 * Even though listen-on is present in the default
5211135446Strhodes		 * configuration, we can't use it here, since it isn't
5212135446Strhodes		 * used if we're in lwresd mode.  This way is easier.
5213135446Strhodes		 */
5214135446Strhodes		if (options != NULL)
5215135446Strhodes			(void)cfg_map_get(options, "listen-on", &clistenon);
5216135446Strhodes		if (clistenon != NULL) {
5217225361Sdougb			/* check return code? */
5218225361Sdougb			(void)ns_listenlist_fromconfig(clistenon, config,
5219225361Sdougb						       ns_g_aclconfctx,
5220225361Sdougb						       ns_g_mctx, &listenon);
5221135446Strhodes		} else if (!ns_g_lwresdonly) {
5222135446Strhodes			/*
5223135446Strhodes			 * Not specified, use default.
5224135446Strhodes			 */
5225135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
5226135446Strhodes						    ISC_TRUE, &listenon));
5227135446Strhodes		}
5228135446Strhodes		if (listenon != NULL) {
5229135446Strhodes			ns_interfacemgr_setlistenon4(server->interfacemgr,
5230135446Strhodes						     listenon);
5231135446Strhodes			ns_listenlist_detach(&listenon);
5232135446Strhodes		}
5233135446Strhodes	}
5234135446Strhodes	/*
5235135446Strhodes	 * Ditto for IPv6.
5236135446Strhodes	 */
5237135446Strhodes	{
5238165071Sdougb		const cfg_obj_t *clistenon = NULL;
5239135446Strhodes		ns_listenlist_t *listenon = NULL;
5240135446Strhodes
5241135446Strhodes		if (options != NULL)
5242135446Strhodes			(void)cfg_map_get(options, "listen-on-v6", &clistenon);
5243135446Strhodes		if (clistenon != NULL) {
5244225361Sdougb			/* check return code? */
5245225361Sdougb			(void)ns_listenlist_fromconfig(clistenon, config,
5246225361Sdougb						       ns_g_aclconfctx,
5247225361Sdougb						       ns_g_mctx, &listenon);
5248135446Strhodes		} else if (!ns_g_lwresdonly) {
5249193149Sdougb			isc_boolean_t enable;
5250135446Strhodes			/*
5251135446Strhodes			 * Not specified, use default.
5252135446Strhodes			 */
5253193149Sdougb			enable = ISC_TF(isc_net_probeipv4() != ISC_R_SUCCESS);
5254135446Strhodes			CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
5255193149Sdougb						    enable, &listenon));
5256135446Strhodes		}
5257135446Strhodes		if (listenon != NULL) {
5258135446Strhodes			ns_interfacemgr_setlistenon6(server->interfacemgr,
5259135446Strhodes						     listenon);
5260135446Strhodes			ns_listenlist_detach(&listenon);
5261135446Strhodes		}
5262135446Strhodes	}
5263135446Strhodes
5264135446Strhodes	/*
5265135446Strhodes	 * Rescan the interface list to pick up changes in the
5266135446Strhodes	 * listen-on option.  It's important that we do this before we try
5267135446Strhodes	 * to configure the query source, since the dispatcher we use might
5268135446Strhodes	 * be shared with an interface.
5269135446Strhodes	 */
5270135446Strhodes	scan_interfaces(server, ISC_TRUE);
5271135446Strhodes
5272135446Strhodes	/*
5273135446Strhodes	 * Arrange for further interface scanning to occur periodically
5274135446Strhodes	 * as specified by the "interface-interval" option.
5275135446Strhodes	 */
5276135446Strhodes	obj = NULL;
5277135446Strhodes	result = ns_config_get(maps, "interface-interval", &obj);
5278135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5279135446Strhodes	interface_interval = cfg_obj_asuint32(obj) * 60;
5280135446Strhodes	if (interface_interval == 0) {
5281135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
5282135446Strhodes				      isc_timertype_inactive,
5283135446Strhodes				      NULL, NULL, ISC_TRUE));
5284135446Strhodes	} else if (server->interface_interval != interface_interval) {
5285135446Strhodes		isc_interval_set(&interval, interface_interval, 0);
5286135446Strhodes		CHECK(isc_timer_reset(server->interface_timer,
5287135446Strhodes				      isc_timertype_ticker,
5288135446Strhodes				      NULL, &interval, ISC_FALSE));
5289135446Strhodes	}
5290135446Strhodes	server->interface_interval = interface_interval;
5291135446Strhodes
5292135446Strhodes	/*
5293135446Strhodes	 * Configure the dialup heartbeat timer.
5294135446Strhodes	 */
5295135446Strhodes	obj = NULL;
5296135446Strhodes	result = ns_config_get(maps, "heartbeat-interval", &obj);
5297135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5298135446Strhodes	heartbeat_interval = cfg_obj_asuint32(obj) * 60;
5299135446Strhodes	if (heartbeat_interval == 0) {
5300135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
5301135446Strhodes				      isc_timertype_inactive,
5302135446Strhodes				      NULL, NULL, ISC_TRUE));
5303135446Strhodes	} else if (server->heartbeat_interval != heartbeat_interval) {
5304135446Strhodes		isc_interval_set(&interval, heartbeat_interval, 0);
5305135446Strhodes		CHECK(isc_timer_reset(server->heartbeat_timer,
5306135446Strhodes				      isc_timertype_ticker,
5307135446Strhodes				      NULL, &interval, ISC_FALSE));
5308135446Strhodes	}
5309135446Strhodes	server->heartbeat_interval = heartbeat_interval;
5310186462Sdougb
5311170222Sdougb	isc_interval_set(&interval, 1200, 0);
5312170222Sdougb	CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
5313170222Sdougb			      &interval, ISC_FALSE));
5314135446Strhodes
5315135446Strhodes	/*
5316224092Sdougb	 * Write the PID file.
5317224092Sdougb	 */
5318224092Sdougb	obj = NULL;
5319224092Sdougb	if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS)
5320224092Sdougb		if (cfg_obj_isvoid(obj))
5321224092Sdougb			ns_os_writepidfile(NULL, first_time);
5322224092Sdougb		else
5323224092Sdougb			ns_os_writepidfile(cfg_obj_asstring(obj), first_time);
5324224092Sdougb	else if (ns_g_lwresdonly)
5325224092Sdougb		ns_os_writepidfile(lwresd_g_defaultpidfile, first_time);
5326224092Sdougb	else
5327224092Sdougb		ns_os_writepidfile(ns_g_defaultpidfile, first_time);
5328224092Sdougb
5329224092Sdougb	/*
5330224092Sdougb	 * Configure the server-wide session key.  This must be done before
5331224092Sdougb	 * configure views because zone configuration may need to know
5332224092Sdougb	 * session-keyname.
5333224092Sdougb	 *
5334224092Sdougb	 * Failure of session key generation isn't fatal at this time; if it
5335224092Sdougb	 * turns out that a session key is really needed but doesn't exist,
5336224092Sdougb	 * we'll treat it as a fatal error then.
5337224092Sdougb	 */
5338224092Sdougb	(void)configure_session_key(maps, server, ns_g_mctx);
5339224092Sdougb
5340225361Sdougb	views = NULL;
5341225361Sdougb	(void)cfg_map_get(config, "view", &views);
5342225361Sdougb
5343224092Sdougb	/*
5344225361Sdougb	 * Create the views and count all the configured zones in
5345225361Sdougb	 * order to correctly size the zone manager's task table.
5346225361Sdougb	 * (We only count zones for configured views; the built-in
5347225361Sdougb	 * "bind" view can be ignored as it only adds a negligible
5348225361Sdougb	 * number of zones.)
5349225361Sdougb	 *
5350225361Sdougb	 * If we're allowing new zones, we need to be able to find the
5351225361Sdougb	 * new zone file and count those as well.  So we setup the new
5352225361Sdougb	 * zone configuration context, but otherwise view configuration
5353225361Sdougb	 * waits until after the zone manager's task list has been sized.
5354225361Sdougb	 */
5355225361Sdougb	for (element = cfg_list_first(views);
5356225361Sdougb	     element != NULL;
5357225361Sdougb	     element = cfg_list_next(element))
5358225361Sdougb	{
5359225361Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
5360225361Sdougb		const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options");
5361225361Sdougb		view = NULL;
5362225361Sdougb
5363225361Sdougb		CHECK(create_view(vconfig, &viewlist, &view));
5364225361Sdougb		INSIST(view != NULL);
5365225361Sdougb
5366225361Sdougb		num_zones += count_zones(voptions);
5367225361Sdougb		CHECK(setup_newzones(view, config, vconfig, conf_parser,
5368225361Sdougb				     ns_g_aclconfctx));
5369225361Sdougb
5370225361Sdougb		nzctx = view->new_zone_config;
5371225361Sdougb		if (nzctx != NULL && nzctx->nzconfig != NULL)
5372225361Sdougb			num_zones += count_zones(nzctx->nzconfig);
5373225361Sdougb
5374225361Sdougb		dns_view_detach(&view);
5375225361Sdougb	}
5376225361Sdougb
5377225361Sdougb	/*
5378225361Sdougb	 * If there were no explicit views then we do the default
5379225361Sdougb	 * view here.
5380225361Sdougb	 */
5381225361Sdougb	if (views == NULL) {
5382225361Sdougb		CHECK(create_view(NULL, &viewlist, &view));
5383225361Sdougb		INSIST(view != NULL);
5384225361Sdougb
5385225361Sdougb		num_zones = count_zones(config);
5386225361Sdougb
5387225361Sdougb		CHECK(setup_newzones(view, config, NULL,  conf_parser,
5388225361Sdougb				     ns_g_aclconfctx));
5389225361Sdougb
5390225361Sdougb		nzctx = view->new_zone_config;
5391225361Sdougb		if (nzctx != NULL && nzctx->nzconfig != NULL)
5392225361Sdougb			num_zones += count_zones(nzctx->nzconfig);
5393225361Sdougb
5394225361Sdougb		dns_view_detach(&view);
5395225361Sdougb	}
5396225361Sdougb
5397225361Sdougb	/*
5398225361Sdougb	 * Zones have been counted; set the zone manager task pool size.
5399225361Sdougb	 */
5400225361Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5401225361Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5402225361Sdougb		      "sizing zone task pool based on %d zones", num_zones);
5403225361Sdougb	CHECK(dns_zonemgr_setsize(ns_g_server->zonemgr, num_zones));
5404225361Sdougb
5405225361Sdougb	/*
5406135446Strhodes	 * Configure and freeze all explicit views.  Explicit
5407135446Strhodes	 * views that have zones were already created at parsing
5408135446Strhodes	 * time, but views with no zones must be created here.
5409135446Strhodes	 */
5410135446Strhodes	for (element = cfg_list_first(views);
5411135446Strhodes	     element != NULL;
5412135446Strhodes	     element = cfg_list_next(element))
5413135446Strhodes	{
5414224092Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
5415225361Sdougb
5416135446Strhodes		view = NULL;
5417225361Sdougb		CHECK(find_view(vconfig, &viewlist, &view));
5418225361Sdougb		CHECK(configure_view(view, config, vconfig,
5419225361Sdougb				     &cachelist, bindkeys, ns_g_mctx,
5420225361Sdougb				     ns_g_aclconfctx, ISC_TRUE));
5421135446Strhodes		dns_view_freeze(view);
5422135446Strhodes		dns_view_detach(&view);
5423135446Strhodes	}
5424135446Strhodes
5425135446Strhodes	/*
5426135446Strhodes	 * Make sure we have a default view if and only if there
5427135446Strhodes	 * were no explicit views.
5428135446Strhodes	 */
5429135446Strhodes	if (views == NULL) {
5430225361Sdougb		view = NULL;
5431225361Sdougb		CHECK(find_view(NULL, &viewlist, &view));
5432225361Sdougb		CHECK(configure_view(view, config, NULL,
5433224092Sdougb				     &cachelist, bindkeys,
5434225361Sdougb				     ns_g_mctx, ns_g_aclconfctx, ISC_TRUE));
5435135446Strhodes		dns_view_freeze(view);
5436135446Strhodes		dns_view_detach(&view);
5437135446Strhodes	}
5438135446Strhodes
5439135446Strhodes	/*
5440224092Sdougb	 * Create (or recreate) the built-in views.
5441135446Strhodes	 */
5442135446Strhodes	builtin_views = NULL;
5443135446Strhodes	RUNTIME_CHECK(cfg_map_get(ns_g_config, "view",
5444135446Strhodes				  &builtin_views) == ISC_R_SUCCESS);
5445135446Strhodes	for (element = cfg_list_first(builtin_views);
5446135446Strhodes	     element != NULL;
5447135446Strhodes	     element = cfg_list_next(element))
5448135446Strhodes	{
5449224092Sdougb		cfg_obj_t *vconfig = cfg_listelt_value(element);
5450224092Sdougb
5451224092Sdougb		CHECK(create_view(vconfig, &builtin_viewlist, &view));
5452225361Sdougb		CHECK(configure_view(view, config, vconfig,
5453224092Sdougb				     &cachelist, bindkeys,
5454225361Sdougb				     ns_g_mctx, ns_g_aclconfctx, ISC_FALSE));
5455135446Strhodes		dns_view_freeze(view);
5456135446Strhodes		dns_view_detach(&view);
5457135446Strhodes		view = NULL;
5458135446Strhodes	}
5459135446Strhodes
5460224092Sdougb	/* Now combine the two viewlists into one */
5461224092Sdougb	ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link);
5462224092Sdougb
5463224092Sdougb	/* Swap our new view list with the production one. */
5464135446Strhodes	tmpviewlist = server->viewlist;
5465135446Strhodes	server->viewlist = viewlist;
5466135446Strhodes	viewlist = tmpviewlist;
5467135446Strhodes
5468224092Sdougb	/* Make the view list available to each of the views */
5469224092Sdougb	view = ISC_LIST_HEAD(server->viewlist);
5470224092Sdougb	while (view != NULL) {
5471224092Sdougb		view->viewlist = &server->viewlist;
5472224092Sdougb		view = ISC_LIST_NEXT(view, link);
5473224092Sdougb	}
5474224092Sdougb
5475224092Sdougb	/* Swap our new cache list with the production one. */
5476224092Sdougb	tmpcachelist = server->cachelist;
5477224092Sdougb	server->cachelist = cachelist;
5478224092Sdougb	cachelist = tmpcachelist;
5479224092Sdougb
5480224092Sdougb	/* Load the TKEY information from the configuration. */
5481135446Strhodes	if (options != NULL) {
5482135446Strhodes		dns_tkeyctx_t *t = NULL;
5483135446Strhodes		CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy,
5484135446Strhodes					     &t),
5485135446Strhodes		       "configuring TKEY");
5486135446Strhodes		if (server->tkeyctx != NULL)
5487135446Strhodes			dns_tkeyctx_destroy(&server->tkeyctx);
5488135446Strhodes		server->tkeyctx = t;
5489135446Strhodes	}
5490135446Strhodes
5491135446Strhodes	/*
5492135446Strhodes	 * Bind the control port(s).
5493135446Strhodes	 */
5494135446Strhodes	CHECKM(ns_controls_configure(ns_g_server->controls, config,
5495225361Sdougb				     ns_g_aclconfctx),
5496135446Strhodes	       "binding control channel(s)");
5497135446Strhodes
5498135446Strhodes	/*
5499135446Strhodes	 * Bind the lwresd port(s).
5500135446Strhodes	 */
5501135446Strhodes	CHECKM(ns_lwresd_configure(ns_g_mctx, config),
5502135446Strhodes	       "binding lightweight resolver ports");
5503135446Strhodes
5504135446Strhodes	/*
5505135446Strhodes	 * Open the source of entropy.
5506135446Strhodes	 */
5507135446Strhodes	if (first_time) {
5508135446Strhodes		obj = NULL;
5509135446Strhodes		result = ns_config_get(maps, "random-device", &obj);
5510135446Strhodes		if (result != ISC_R_SUCCESS) {
5511135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5512135446Strhodes				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5513135446Strhodes				      "no source of entropy found");
5514135446Strhodes		} else {
5515135446Strhodes			const char *randomdev = cfg_obj_asstring(obj);
5516135446Strhodes			result = isc_entropy_createfilesource(ns_g_entropy,
5517135446Strhodes							      randomdev);
5518135446Strhodes			if (result != ISC_R_SUCCESS)
5519135446Strhodes				isc_log_write(ns_g_lctx,
5520135446Strhodes					      NS_LOGCATEGORY_GENERAL,
5521135446Strhodes					      NS_LOGMODULE_SERVER,
5522135446Strhodes					      ISC_LOG_INFO,
5523135446Strhodes					      "could not open entropy source "
5524135446Strhodes					      "%s: %s",
5525135446Strhodes					      randomdev,
5526135446Strhodes					      isc_result_totext(result));
5527135446Strhodes#ifdef PATH_RANDOMDEV
5528135446Strhodes			if (ns_g_fallbackentropy != NULL) {
5529135446Strhodes				if (result != ISC_R_SUCCESS) {
5530135446Strhodes					isc_log_write(ns_g_lctx,
5531135446Strhodes						      NS_LOGCATEGORY_GENERAL,
5532135446Strhodes						      NS_LOGMODULE_SERVER,
5533135446Strhodes						      ISC_LOG_INFO,
5534135446Strhodes						      "using pre-chroot entropy source "
5535135446Strhodes						      "%s",
5536135446Strhodes						      PATH_RANDOMDEV);
5537135446Strhodes					isc_entropy_detach(&ns_g_entropy);
5538135446Strhodes					isc_entropy_attach(ns_g_fallbackentropy,
5539135446Strhodes							   &ns_g_entropy);
5540135446Strhodes				}
5541135446Strhodes				isc_entropy_detach(&ns_g_fallbackentropy);
5542135446Strhodes			}
5543135446Strhodes#endif
5544135446Strhodes		}
5545135446Strhodes	}
5546135446Strhodes
5547135446Strhodes	/*
5548135446Strhodes	 * Relinquish root privileges.
5549135446Strhodes	 */
5550135446Strhodes	if (first_time)
5551135446Strhodes		ns_os_changeuser();
5552135446Strhodes
5553135446Strhodes	/*
5554186462Sdougb	 * Check that the working directory is writable.
5555186462Sdougb	 */
5556186462Sdougb	if (access(".", W_OK) != 0) {
5557186462Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5558186462Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
5559186462Sdougb			      "the working directory is not writable");
5560186462Sdougb	}
5561186462Sdougb
5562186462Sdougb	/*
5563135446Strhodes	 * Configure the logging system.
5564135446Strhodes	 *
5565135446Strhodes	 * Do this after changing UID to make sure that any log
5566135446Strhodes	 * files specified in named.conf get created by the
5567135446Strhodes	 * unprivileged user, not root.
5568135446Strhodes	 */
5569135446Strhodes	if (ns_g_logstderr) {
5570262706Serwin		const cfg_obj_t *logobj = NULL;
5571262706Serwin
5572135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5573135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
5574262706Serwin			      "not using config file logging "
5575262706Serwin			      "statement for logging due to "
5576262706Serwin			      "-g option");
5577262706Serwin
5578262706Serwin		(void)cfg_map_get(config, "logging", &logobj);
5579262706Serwin		if (logobj != NULL) {
5580262706Serwin			result = ns_log_configure(NULL, logobj);
5581262706Serwin			if (result != ISC_R_SUCCESS) {
5582262706Serwin				isc_log_write(ns_g_lctx,
5583262706Serwin					      NS_LOGCATEGORY_GENERAL,
5584262706Serwin					      NS_LOGMODULE_SERVER,
5585262706Serwin					      ISC_LOG_ERROR,
5586262706Serwin					      "checking logging configuration "
5587262706Serwin					      "failed: %s",
5588262706Serwin					      isc_result_totext(result));
5589262706Serwin				goto cleanup;
5590262706Serwin			}
5591262706Serwin		}
5592135446Strhodes	} else {
5593165071Sdougb		const cfg_obj_t *logobj = NULL;
5594135446Strhodes
5595135446Strhodes		CHECKM(isc_logconfig_create(ns_g_lctx, &logc),
5596135446Strhodes		       "creating new logging configuration");
5597135446Strhodes
5598135446Strhodes		logobj = NULL;
5599135446Strhodes		(void)cfg_map_get(config, "logging", &logobj);
5600135446Strhodes		if (logobj != NULL) {
5601135446Strhodes			CHECKM(ns_log_configure(logc, logobj),
5602135446Strhodes			       "configuring logging");
5603135446Strhodes		} else {
5604135446Strhodes			CHECKM(ns_log_setdefaultchannels(logc),
5605135446Strhodes			       "setting up default logging channels");
5606135446Strhodes			CHECKM(ns_log_setunmatchedcategory(logc),
5607135446Strhodes			       "setting up default 'category unmatched'");
5608135446Strhodes			CHECKM(ns_log_setdefaultcategory(logc),
5609135446Strhodes			       "setting up default 'category default'");
5610135446Strhodes		}
5611135446Strhodes
5612262706Serwin		CHECKM(isc_logconfig_use(ns_g_lctx, logc),
5613262706Serwin		       "installing logging configuration");
5614262706Serwin		logc = NULL;
5615135446Strhodes
5616135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
5617135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
5618135446Strhodes			      "now using logging configuration from "
5619135446Strhodes			      "config file");
5620135446Strhodes	}
5621135446Strhodes
5622135446Strhodes	/*
5623135446Strhodes	 * Set the default value of the query logging flag depending
5624135446Strhodes	 * whether a "queries" category has been defined.  This is
5625135446Strhodes	 * a disgusting hack, but we need to do this for BIND 8
5626135446Strhodes	 * compatibility.
5627135446Strhodes	 */
5628135446Strhodes	if (first_time) {
5629165071Sdougb		const cfg_obj_t *logobj = NULL;
5630165071Sdougb		const cfg_obj_t *categories = NULL;
5631135446Strhodes
5632135446Strhodes		obj = NULL;
5633135446Strhodes		if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) {
5634135446Strhodes			server->log_queries = cfg_obj_asboolean(obj);
5635135446Strhodes		} else {
5636135446Strhodes
5637135446Strhodes			(void)cfg_map_get(config, "logging", &logobj);
5638135446Strhodes			if (logobj != NULL)
5639135446Strhodes				(void)cfg_map_get(logobj, "category",
5640135446Strhodes						  &categories);
5641135446Strhodes			if (categories != NULL) {
5642165071Sdougb				const cfg_listelt_t *element;
5643135446Strhodes				for (element = cfg_list_first(categories);
5644135446Strhodes				     element != NULL;
5645135446Strhodes				     element = cfg_list_next(element))
5646135446Strhodes				{
5647165071Sdougb					const cfg_obj_t *catobj;
5648165071Sdougb					const char *str;
5649135446Strhodes
5650135446Strhodes					obj = cfg_listelt_value(element);
5651135446Strhodes					catobj = cfg_tuple_get(obj, "name");
5652135446Strhodes					str = cfg_obj_asstring(catobj);
5653135446Strhodes					if (strcasecmp(str, "queries") == 0)
5654135446Strhodes						server->log_queries = ISC_TRUE;
5655135446Strhodes				}
5656135446Strhodes			}
5657135446Strhodes		}
5658135446Strhodes	}
5659135446Strhodes
5660186462Sdougb
5661135446Strhodes	obj = NULL;
5662135446Strhodes	if (options != NULL &&
5663193149Sdougb	    cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS)
5664193149Sdougb		ns_g_memstatistics = cfg_obj_asboolean(obj);
5665193149Sdougb	else
5666193149Sdougb		ns_g_memstatistics =
5667193149Sdougb			ISC_TF((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0);
5668193149Sdougb
5669193149Sdougb	obj = NULL;
5670193149Sdougb	if (ns_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS)
5671135446Strhodes		ns_main_setmemstats(cfg_obj_asstring(obj));
5672193149Sdougb	else if (ns_g_memstatistics)
5673193149Sdougb		ns_main_setmemstats("named.memstats");
5674135446Strhodes	else
5675135446Strhodes		ns_main_setmemstats(NULL);
5676135446Strhodes
5677135446Strhodes	obj = NULL;
5678135446Strhodes	result = ns_config_get(maps, "statistics-file", &obj);
5679135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5680135446Strhodes	CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)),
5681135446Strhodes	       "strdup");
5682135446Strhodes
5683135446Strhodes	obj = NULL;
5684135446Strhodes	result = ns_config_get(maps, "dump-file", &obj);
5685135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5686135446Strhodes	CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)),
5687135446Strhodes	       "strdup");
5688135446Strhodes
5689135446Strhodes	obj = NULL;
5690224092Sdougb	result = ns_config_get(maps, "secroots-file", &obj);
5691224092Sdougb	INSIST(result == ISC_R_SUCCESS);
5692224092Sdougb	CHECKM(setstring(server, &server->secrootsfile, cfg_obj_asstring(obj)),
5693224092Sdougb	       "strdup");
5694224092Sdougb
5695224092Sdougb	obj = NULL;
5696135446Strhodes	result = ns_config_get(maps, "recursing-file", &obj);
5697135446Strhodes	INSIST(result == ISC_R_SUCCESS);
5698135446Strhodes	CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)),
5699135446Strhodes	       "strdup");
5700135446Strhodes
5701135446Strhodes	obj = NULL;
5702135446Strhodes	result = ns_config_get(maps, "version", &obj);
5703135446Strhodes	if (result == ISC_R_SUCCESS) {
5704135446Strhodes		CHECKM(setoptstring(server, &server->version, obj), "strdup");
5705135446Strhodes		server->version_set = ISC_TRUE;
5706135446Strhodes	} else {
5707135446Strhodes		server->version_set = ISC_FALSE;
5708135446Strhodes	}
5709135446Strhodes
5710135446Strhodes	obj = NULL;
5711135446Strhodes	result = ns_config_get(maps, "hostname", &obj);
5712135446Strhodes	if (result == ISC_R_SUCCESS) {
5713135446Strhodes		CHECKM(setoptstring(server, &server->hostname, obj), "strdup");
5714135446Strhodes		server->hostname_set = ISC_TRUE;
5715135446Strhodes	} else {
5716135446Strhodes		server->hostname_set = ISC_FALSE;
5717135446Strhodes	}
5718135446Strhodes
5719135446Strhodes	obj = NULL;
5720135446Strhodes	result = ns_config_get(maps, "server-id", &obj);
5721135446Strhodes	server->server_usehostname = ISC_FALSE;
5722135446Strhodes	if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
5723193149Sdougb		/* The parser translates "hostname" to ISC_TRUE */
5724193149Sdougb		server->server_usehostname = cfg_obj_asboolean(obj);
5725193149Sdougb		result = setstring(server, &server->server_id, NULL);
5726193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5727135446Strhodes	} else if (result == ISC_R_SUCCESS) {
5728193149Sdougb		/* Found a quoted string */
5729135446Strhodes		CHECKM(setoptstring(server, &server->server_id, obj), "strdup");
5730135446Strhodes	} else {
5731170222Sdougb		result = setstring(server, &server->server_id, NULL);
5732135446Strhodes		RUNTIME_CHECK(result == ISC_R_SUCCESS);
5733135446Strhodes	}
5734135446Strhodes
5735135446Strhodes	obj = NULL;
5736135446Strhodes	result = ns_config_get(maps, "flush-zones-on-shutdown", &obj);
5737135446Strhodes	if (result == ISC_R_SUCCESS) {
5738135446Strhodes		server->flushonshutdown = cfg_obj_asboolean(obj);
5739135446Strhodes	} else {
5740135446Strhodes		server->flushonshutdown = ISC_FALSE;
5741135446Strhodes	}
5742135446Strhodes
5743135446Strhodes	result = ISC_R_SUCCESS;
5744135446Strhodes
5745135446Strhodes cleanup:
5746262706Serwin	if (logc != NULL)
5747262706Serwin		isc_logconfig_destroy(&logc);
5748262706Serwin
5749186462Sdougb	if (v4portset != NULL)
5750186462Sdougb		isc_portset_destroy(ns_g_mctx, &v4portset);
5751186462Sdougb
5752186462Sdougb	if (v6portset != NULL)
5753186462Sdougb		isc_portset_destroy(ns_g_mctx, &v6portset);
5754186462Sdougb
5755224092Sdougb	if (conf_parser != NULL) {
5756135446Strhodes		if (config != NULL)
5757224092Sdougb			cfg_obj_destroy(conf_parser, &config);
5758224092Sdougb		cfg_parser_destroy(&conf_parser);
5759135446Strhodes	}
5760135446Strhodes
5761224092Sdougb	if (bindkeys_parser != NULL) {
5762224092Sdougb		if (bindkeys  != NULL)
5763224092Sdougb			cfg_obj_destroy(bindkeys_parser, &bindkeys);
5764224092Sdougb		cfg_parser_destroy(&bindkeys_parser);
5765224092Sdougb	}
5766224092Sdougb
5767135446Strhodes	if (view != NULL)
5768135446Strhodes		dns_view_detach(&view);
5769135446Strhodes
5770135446Strhodes	/*
5771135446Strhodes	 * This cleans up either the old production view list
5772135446Strhodes	 * or our temporary list depending on whether they
5773135446Strhodes	 * were swapped above or not.
5774135446Strhodes	 */
5775135446Strhodes	for (view = ISC_LIST_HEAD(viewlist);
5776135446Strhodes	     view != NULL;
5777135446Strhodes	     view = view_next) {
5778135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
5779135446Strhodes		ISC_LIST_UNLINK(viewlist, view, link);
5780170222Sdougb		if (result == ISC_R_SUCCESS &&
5781170222Sdougb		    strcmp(view->name, "_bind") != 0)
5782170222Sdougb			(void)dns_zt_apply(view->zonetable, ISC_FALSE,
5783170222Sdougb					   removed, view);
5784135446Strhodes		dns_view_detach(&view);
5785135446Strhodes	}
5786135446Strhodes
5787224092Sdougb	/* Same cleanup for cache list. */
5788224092Sdougb	while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) {
5789224092Sdougb		ISC_LIST_UNLINK(cachelist, nsc, link);
5790224092Sdougb		dns_cache_detach(&nsc->cache);
5791224092Sdougb		isc_mem_put(server->mctx, nsc, sizeof(*nsc));
5792224092Sdougb	}
5793224092Sdougb
5794135446Strhodes	/*
5795135446Strhodes	 * Adjust the listening interfaces in accordance with the source
5796135446Strhodes	 * addresses specified in views and zones.
5797135446Strhodes	 */
5798135446Strhodes	if (isc_net_probeipv6() == ISC_R_SUCCESS)
5799135446Strhodes		adjust_interfaces(server, ns_g_mctx);
5800135446Strhodes
5801135446Strhodes	/* Relinquish exclusive access to configuration data. */
5802234010Sdougb	if (exclusive)
5803234010Sdougb		isc_task_endexclusive(server->task);
5804135446Strhodes
5805135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5806135446Strhodes		      ISC_LOG_DEBUG(1), "load_configuration: %s",
5807135446Strhodes		      isc_result_totext(result));
5808135446Strhodes
5809135446Strhodes	return (result);
5810135446Strhodes}
5811135446Strhodes
5812135446Strhodesstatic isc_result_t
5813254897Serwinview_loaded(void *arg) {
5814135446Strhodes	isc_result_t result;
5815254897Serwin	ns_zoneload_t *zl = (ns_zoneload_t *) arg;
5816254897Serwin	ns_server_t *server = zl->server;
5817254897Serwin	unsigned int refs;
5818254897Serwin
5819254897Serwin
5820254897Serwin	/*
5821254897Serwin	 * Force zone maintenance.  Do this after loading
5822254897Serwin	 * so that we know when we need to force AXFR of
5823254897Serwin	 * slave zones whose master files are missing.
5824254897Serwin	 *
5825254897Serwin	 * We use the zoneload reference counter to let us
5826254897Serwin	 * know when all views are finished.
5827254897Serwin	 */
5828254897Serwin	isc_refcount_decrement(&zl->refs, &refs);
5829254897Serwin	if (refs != 0)
5830254897Serwin		return (ISC_R_SUCCESS);
5831254897Serwin
5832254897Serwin	isc_refcount_destroy(&zl->refs);
5833254897Serwin	isc_mem_put(server->mctx, zl, sizeof (*zl));
5834254897Serwin
5835254897Serwin	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5836254897Serwin		      ISC_LOG_NOTICE, "all zones loaded");
5837254897Serwin	CHECKFATAL(dns_zonemgr_forcemaint(server->zonemgr),
5838254897Serwin		   "forcing zone maintenance");
5839254897Serwin
5840254897Serwin	ns_os_started();
5841254897Serwin	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
5842254897Serwin		      ISC_LOG_NOTICE, "running");
5843254897Serwin
5844254897Serwin	return (ISC_R_SUCCESS);
5845254897Serwin}
5846254897Serwin
5847254897Serwinstatic isc_result_t
5848262706Serwinload_zones(ns_server_t *server, isc_boolean_t init) {
5849254897Serwin	isc_result_t result;
5850135446Strhodes	dns_view_t *view;
5851254897Serwin	ns_zoneload_t *zl;
5852254897Serwin	unsigned int refs = 0;
5853135446Strhodes
5854254897Serwin	zl = isc_mem_get(server->mctx, sizeof (*zl));
5855254897Serwin	if (zl == NULL)
5856254897Serwin		return (ISC_R_NOMEMORY);
5857254897Serwin	zl->server = server;
5858254897Serwin
5859135446Strhodes	result = isc_task_beginexclusive(server->task);
5860135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5861135446Strhodes
5862254897Serwin	isc_refcount_init(&zl->refs, 1);
5863254897Serwin
5864135446Strhodes	/*
5865254897Serwin	 * Schedule zones to be loaded from disk.
5866135446Strhodes	 */
5867135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5868135446Strhodes	     view != NULL;
5869135446Strhodes	     view = ISC_LIST_NEXT(view, link))
5870135446Strhodes	{
5871254897Serwin		if (view->managed_keys != NULL) {
5872254897Serwin			result = dns_zone_load(view->managed_keys);
5873254897Serwin			if (result != ISC_R_SUCCESS &&
5874254897Serwin			    result != DNS_R_UPTODATE &&
5875254897Serwin			    result != DNS_R_CONTINUE)
5876254897Serwin				goto cleanup;
5877254897Serwin		}
5878254897Serwin		if (view->redirect != NULL) {
5879254897Serwin			result = dns_zone_load(view->redirect);
5880254897Serwin			if (result != ISC_R_SUCCESS &&
5881254897Serwin			    result != DNS_R_UPTODATE &&
5882254897Serwin			    result != DNS_R_CONTINUE)
5883254897Serwin				goto cleanup;
5884254897Serwin		}
5885254897Serwin
5886254897Serwin		/*
5887254897Serwin		 * 'dns_view_asyncload' calls view_loaded if there are no
5888254897Serwin		 * zones.
5889254897Serwin		 */
5890254897Serwin		isc_refcount_increment(&zl->refs, NULL);
5891254897Serwin		CHECK(dns_view_asyncload(view, view_loaded, zl));
5892135446Strhodes	}
5893135446Strhodes
5894135446Strhodes cleanup:
5895254897Serwin	isc_refcount_decrement(&zl->refs, &refs);
5896254897Serwin	if (refs == 0) {
5897254897Serwin		isc_refcount_destroy(&zl->refs);
5898254897Serwin		isc_mem_put(server->mctx, zl, sizeof (*zl));
5899262706Serwin	} else if (init) {
5900254897Serwin		/*
5901254897Serwin		 * Place the task manager into privileged mode.  This
5902254897Serwin		 * ensures that after we leave task-exclusive mode, no
5903254897Serwin		 * other tasks will be able to run except for the ones
5904262706Serwin		 * that are loading zones. (This should only be done during
5905262706Serwin		 * the initial server setup; it isn't necessary during
5906262706Serwin		 * a reload.)
5907254897Serwin		 */
5908254897Serwin		isc_taskmgr_setmode(ns_g_taskmgr, isc_taskmgrmode_privileged);
5909254897Serwin	}
5910254897Serwin
5911186462Sdougb	isc_task_endexclusive(server->task);
5912135446Strhodes	return (result);
5913135446Strhodes}
5914135446Strhodes
5915135446Strhodesstatic isc_result_t
5916135446Strhodesload_new_zones(ns_server_t *server, isc_boolean_t stop) {
5917135446Strhodes	isc_result_t result;
5918135446Strhodes	dns_view_t *view;
5919135446Strhodes
5920135446Strhodes	result = isc_task_beginexclusive(server->task);
5921135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
5922135446Strhodes
5923135446Strhodes	/*
5924135446Strhodes	 * Load zone data from disk.
5925135446Strhodes	 */
5926135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
5927135446Strhodes	     view != NULL;
5928135446Strhodes	     view = ISC_LIST_NEXT(view, link))
5929135446Strhodes	{
5930135446Strhodes		CHECK(dns_view_loadnew(view, stop));
5931224092Sdougb
5932224092Sdougb		/* Load managed-keys data */
5933224092Sdougb		if (view->managed_keys != NULL)
5934224092Sdougb			CHECK(dns_zone_loadnew(view->managed_keys));
5935254897Serwin		if (view->redirect != NULL)
5936254897Serwin			CHECK(dns_zone_loadnew(view->redirect));
5937135446Strhodes	}
5938224092Sdougb
5939135446Strhodes	/*
5940224092Sdougb	 * Resume zone XFRs.
5941135446Strhodes	 */
5942135446Strhodes	dns_zonemgr_resumexfrs(server->zonemgr);
5943135446Strhodes cleanup:
5944186462Sdougb	isc_task_endexclusive(server->task);
5945135446Strhodes	return (result);
5946135446Strhodes}
5947135446Strhodes
5948135446Strhodesstatic void
5949135446Strhodesrun_server(isc_task_t *task, isc_event_t *event) {
5950135446Strhodes	isc_result_t result;
5951135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
5952135446Strhodes
5953143731Sdougb	INSIST(task == server->task);
5954135446Strhodes
5955135446Strhodes	isc_event_free(&event);
5956135446Strhodes
5957135446Strhodes	CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy,
5958135446Strhodes					  &ns_g_dispatchmgr),
5959135446Strhodes		   "creating dispatch manager");
5960135446Strhodes
5961193149Sdougb	dns_dispatchmgr_setstats(ns_g_dispatchmgr, server->resolverstats);
5962193149Sdougb
5963135446Strhodes	CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr,
5964135446Strhodes					  ns_g_socketmgr, ns_g_dispatchmgr,
5965135446Strhodes					  &server->interfacemgr),
5966135446Strhodes		   "creating interface manager");
5967135446Strhodes
5968135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5969135446Strhodes				    NULL, NULL, server->task,
5970135446Strhodes				    interface_timer_tick,
5971135446Strhodes				    server, &server->interface_timer),
5972135446Strhodes		   "creating interface timer");
5973135446Strhodes
5974135446Strhodes	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5975135446Strhodes				    NULL, NULL, server->task,
5976135446Strhodes				    heartbeat_timer_tick,
5977135446Strhodes				    server, &server->heartbeat_timer),
5978135446Strhodes		   "creating heartbeat timer");
5979135446Strhodes
5980170222Sdougb	CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
5981170222Sdougb				    NULL, NULL, server->task, pps_timer_tick,
5982170222Sdougb				    server, &server->pps_timer),
5983170222Sdougb		   "creating pps timer");
5984170222Sdougb
5985135446Strhodes	CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser),
5986135446Strhodes		   "creating default configuration parser");
5987135446Strhodes
5988135446Strhodes	if (ns_g_lwresdonly)
5989135446Strhodes		CHECKFATAL(load_configuration(lwresd_g_conffile, server,
5990135446Strhodes					      ISC_TRUE),
5991135446Strhodes			   "loading configuration");
5992135446Strhodes	else
5993135446Strhodes		CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE),
5994135446Strhodes			   "loading configuration");
5995135446Strhodes
5996135446Strhodes	isc_hash_init();
5997135446Strhodes
5998262706Serwin	CHECKFATAL(load_zones(server, ISC_TRUE), "loading zones");
5999135446Strhodes}
6000135446Strhodes
6001186462Sdougbvoid
6002135446Strhodesns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) {
6003135446Strhodes
6004135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6005135446Strhodes
6006135446Strhodes	server->flushonshutdown = flush;
6007135446Strhodes}
6008135446Strhodes
6009135446Strhodesstatic void
6010135446Strhodesshutdown_server(isc_task_t *task, isc_event_t *event) {
6011135446Strhodes	isc_result_t result;
6012135446Strhodes	dns_view_t *view, *view_next;
6013135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
6014135446Strhodes	isc_boolean_t flush = server->flushonshutdown;
6015224092Sdougb	ns_cache_t *nsc;
6016135446Strhodes
6017135446Strhodes	UNUSED(task);
6018135446Strhodes	INSIST(task == server->task);
6019135446Strhodes
6020135446Strhodes	result = isc_task_beginexclusive(server->task);
6021135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6022135446Strhodes
6023135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
6024135446Strhodes		      ISC_LOG_INFO, "shutting down%s",
6025135446Strhodes		      flush ? ": flushing changes" : "");
6026135446Strhodes
6027193149Sdougb	ns_statschannels_shutdown(server);
6028135446Strhodes	ns_controls_shutdown(server->controls);
6029135446Strhodes	end_reserved_dispatches(server, ISC_TRUE);
6030224092Sdougb	cleanup_session_key(server, server->mctx);
6031135446Strhodes
6032225361Sdougb	if (ns_g_aclconfctx != NULL)
6033225361Sdougb		cfg_aclconfctx_detach(&ns_g_aclconfctx);
6034225361Sdougb
6035135446Strhodes	cfg_obj_destroy(ns_g_parser, &ns_g_config);
6036135446Strhodes	cfg_parser_destroy(&ns_g_parser);
6037135446Strhodes
6038135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
6039135446Strhodes	     view != NULL;
6040135446Strhodes	     view = view_next) {
6041135446Strhodes		view_next = ISC_LIST_NEXT(view, link);
6042135446Strhodes		ISC_LIST_UNLINK(server->viewlist, view, link);
6043135446Strhodes		if (flush)
6044135446Strhodes			dns_view_flushanddetach(&view);
6045135446Strhodes		else
6046135446Strhodes			dns_view_detach(&view);
6047135446Strhodes	}
6048135446Strhodes
6049224092Sdougb	while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) {
6050224092Sdougb		ISC_LIST_UNLINK(server->cachelist, nsc, link);
6051224092Sdougb		dns_cache_detach(&nsc->cache);
6052224092Sdougb		isc_mem_put(server->mctx, nsc, sizeof(*nsc));
6053224092Sdougb	}
6054224092Sdougb
6055135446Strhodes	isc_timer_detach(&server->interface_timer);
6056135446Strhodes	isc_timer_detach(&server->heartbeat_timer);
6057170222Sdougb	isc_timer_detach(&server->pps_timer);
6058135446Strhodes
6059135446Strhodes	ns_interfacemgr_shutdown(server->interfacemgr);
6060135446Strhodes	ns_interfacemgr_detach(&server->interfacemgr);
6061135446Strhodes
6062135446Strhodes	dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
6063135446Strhodes
6064135446Strhodes	dns_zonemgr_shutdown(server->zonemgr);
6065135446Strhodes
6066224092Sdougb	if (ns_g_sessionkey != NULL) {
6067224092Sdougb		dns_tsigkey_detach(&ns_g_sessionkey);
6068224092Sdougb		dns_name_free(&ns_g_sessionkeyname, server->mctx);
6069224092Sdougb	}
6070224092Sdougb
6071135446Strhodes	if (server->blackholeacl != NULL)
6072135446Strhodes		dns_acl_detach(&server->blackholeacl);
6073135446Strhodes
6074135446Strhodes	dns_db_detach(&server->in_roothints);
6075135446Strhodes
6076135446Strhodes	isc_task_endexclusive(server->task);
6077135446Strhodes
6078135446Strhodes	isc_task_detach(&server->task);
6079135446Strhodes
6080135446Strhodes	isc_event_free(&event);
6081135446Strhodes}
6082135446Strhodes
6083135446Strhodesvoid
6084135446Strhodesns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
6085135446Strhodes	isc_result_t result;
6086225361Sdougb	ns_server_t *server = isc_mem_get(mctx, sizeof(*server));
6087135446Strhodes
6088135446Strhodes	if (server == NULL)
6089135446Strhodes		fatal("allocating server object", ISC_R_NOMEMORY);
6090135446Strhodes
6091135446Strhodes	server->mctx = mctx;
6092135446Strhodes	server->task = NULL;
6093135446Strhodes
6094135446Strhodes	/* Initialize configuration data with default values. */
6095135446Strhodes
6096135446Strhodes	result = isc_quota_init(&server->xfroutquota, 10);
6097135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6098135446Strhodes	result = isc_quota_init(&server->tcpquota, 10);
6099135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6100135446Strhodes	result = isc_quota_init(&server->recursionquota, 100);
6101135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6102135446Strhodes
6103135446Strhodes	result = dns_aclenv_init(mctx, &server->aclenv);
6104135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
6105135446Strhodes
6106135446Strhodes	/* Initialize server data structures. */
6107135446Strhodes	server->zonemgr = NULL;
6108135446Strhodes	server->interfacemgr = NULL;
6109135446Strhodes	ISC_LIST_INIT(server->viewlist);
6110135446Strhodes	server->in_roothints = NULL;
6111135446Strhodes	server->blackholeacl = NULL;
6112135446Strhodes
6113135446Strhodes	CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
6114135446Strhodes				     &server->in_roothints),
6115135446Strhodes		   "setting up root hints");
6116135446Strhodes
6117135446Strhodes	CHECKFATAL(isc_mutex_init(&server->reload_event_lock),
6118135446Strhodes		   "initializing reload event lock");
6119135446Strhodes	server->reload_event =
6120135446Strhodes		isc_event_allocate(ns_g_mctx, server,
6121135446Strhodes				   NS_EVENT_RELOAD,
6122135446Strhodes				   ns_server_reload,
6123135446Strhodes				   server,
6124135446Strhodes				   sizeof(isc_event_t));
6125135446Strhodes	CHECKFATAL(server->reload_event == NULL ?
6126135446Strhodes		   ISC_R_NOMEMORY : ISC_R_SUCCESS,
6127135446Strhodes		   "allocating reload event");
6128135446Strhodes
6129224092Sdougb	CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy,
6130224092Sdougb				 ns_g_engine, ISC_ENTROPY_GOODONLY),
6131135446Strhodes		   "initializing DST");
6132135446Strhodes
6133135446Strhodes	server->tkeyctx = NULL;
6134135446Strhodes	CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
6135135446Strhodes				      &server->tkeyctx),
6136135446Strhodes		   "creating TKEY context");
6137135446Strhodes
6138135446Strhodes	/*
6139135446Strhodes	 * Setup the server task, which is responsible for coordinating
6140245163Serwin	 * startup and shutdown of the server, as well as all exclusive
6141245163Serwin	 * tasks.
6142135446Strhodes	 */
6143135446Strhodes	CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
6144135446Strhodes		   "creating server task");
6145135446Strhodes	isc_task_setname(server->task, "server", server);
6146245163Serwin	isc_taskmgr_setexcltask(ns_g_taskmgr, server->task);
6147135446Strhodes	CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
6148135446Strhodes		   "isc_task_onshutdown");
6149135446Strhodes	CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),
6150135446Strhodes		   "isc_app_onrun");
6151135446Strhodes
6152135446Strhodes	server->interface_timer = NULL;
6153135446Strhodes	server->heartbeat_timer = NULL;
6154170222Sdougb	server->pps_timer = NULL;
6155186462Sdougb
6156135446Strhodes	server->interface_interval = 0;
6157135446Strhodes	server->heartbeat_interval = 0;
6158135446Strhodes
6159135446Strhodes	CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr,
6160135446Strhodes				      ns_g_socketmgr, &server->zonemgr),
6161135446Strhodes		   "dns_zonemgr_create");
6162225361Sdougb	CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000),
6163225361Sdougb		   "dns_zonemgr_setsize");
6164135446Strhodes
6165135446Strhodes	server->statsfile = isc_mem_strdup(server->mctx, "named.stats");
6166135446Strhodes	CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
6167135446Strhodes		   "isc_mem_strdup");
6168193149Sdougb	server->nsstats = NULL;
6169193149Sdougb	server->rcvquerystats = NULL;
6170193149Sdougb	server->opcodestats = NULL;
6171193149Sdougb	server->zonestats = NULL;
6172193149Sdougb	server->resolverstats = NULL;
6173193149Sdougb	server->sockstats = NULL;
6174193149Sdougb	CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats,
6175193149Sdougb				    isc_sockstatscounter_max),
6176193149Sdougb		   "isc_stats_create");
6177193149Sdougb	isc_socketmgr_setstats(ns_g_socketmgr, server->sockstats);
6178135446Strhodes
6179224092Sdougb	server->bindkeysfile = isc_mem_strdup(server->mctx, "bind.keys");
6180224092Sdougb	CHECKFATAL(server->bindkeysfile == NULL ? ISC_R_NOMEMORY :
6181224092Sdougb						  ISC_R_SUCCESS,
6182224092Sdougb		   "isc_mem_strdup");
6183224092Sdougb
6184135446Strhodes	server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db");
6185135446Strhodes	CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
6186135446Strhodes		   "isc_mem_strdup");
6187135446Strhodes
6188224092Sdougb	server->secrootsfile = isc_mem_strdup(server->mctx, "named.secroots");
6189224092Sdougb	CHECKFATAL(server->secrootsfile == NULL ? ISC_R_NOMEMORY :
6190224092Sdougb						  ISC_R_SUCCESS,
6191224092Sdougb		   "isc_mem_strdup");
6192224092Sdougb
6193135446Strhodes	server->recfile = isc_mem_strdup(server->mctx, "named.recursing");
6194135446Strhodes	CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
6195135446Strhodes		   "isc_mem_strdup");
6196135446Strhodes
6197135446Strhodes	server->hostname_set = ISC_FALSE;
6198135446Strhodes	server->hostname = NULL;
6199186462Sdougb	server->version_set = ISC_FALSE;
6200135446Strhodes	server->version = NULL;
6201135446Strhodes	server->server_usehostname = ISC_FALSE;
6202135446Strhodes	server->server_id = NULL;
6203135446Strhodes
6204193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->nsstats,
6205193149Sdougb				    dns_nsstatscounter_max),
6206193149Sdougb		   "dns_stats_create (server)");
6207135446Strhodes
6208193149Sdougb	CHECKFATAL(dns_rdatatypestats_create(ns_g_mctx,
6209193149Sdougb					     &server->rcvquerystats),
6210193149Sdougb		   "dns_stats_create (rcvquery)");
6211193149Sdougb
6212193149Sdougb	CHECKFATAL(dns_opcodestats_create(ns_g_mctx, &server->opcodestats),
6213193149Sdougb		   "dns_stats_create (opcode)");
6214193149Sdougb
6215193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->zonestats,
6216193149Sdougb				    dns_zonestatscounter_max),
6217193149Sdougb		   "dns_stats_create (zone)");
6218193149Sdougb
6219193149Sdougb	CHECKFATAL(isc_stats_create(ns_g_mctx, &server->resolverstats,
6220193149Sdougb				    dns_resstatscounter_max),
6221193149Sdougb		   "dns_stats_create (resolver)");
6222193149Sdougb
6223135446Strhodes	server->flushonshutdown = ISC_FALSE;
6224135446Strhodes	server->log_queries = ISC_FALSE;
6225135446Strhodes
6226135446Strhodes	server->controls = NULL;
6227135446Strhodes	CHECKFATAL(ns_controls_create(server, &server->controls),
6228135446Strhodes		   "ns_controls_create");
6229135446Strhodes	server->dispatchgen = 0;
6230135446Strhodes	ISC_LIST_INIT(server->dispatches);
6231135446Strhodes
6232193149Sdougb	ISC_LIST_INIT(server->statschannels);
6233193149Sdougb
6234224092Sdougb	ISC_LIST_INIT(server->cachelist);
6235224092Sdougb
6236224092Sdougb	server->sessionkey = NULL;
6237224092Sdougb	server->session_keyfile = NULL;
6238224092Sdougb	server->session_keyname = NULL;
6239224092Sdougb	server->session_keyalg = DST_ALG_UNKNOWN;
6240224092Sdougb	server->session_keybits = 0;
6241224092Sdougb
6242135446Strhodes	server->magic = NS_SERVER_MAGIC;
6243135446Strhodes	*serverp = server;
6244135446Strhodes}
6245135446Strhodes
6246135446Strhodesvoid
6247135446Strhodesns_server_destroy(ns_server_t **serverp) {
6248135446Strhodes	ns_server_t *server = *serverp;
6249135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6250135446Strhodes
6251135446Strhodes	ns_controls_destroy(&server->controls);
6252135446Strhodes
6253193149Sdougb	isc_stats_detach(&server->nsstats);
6254193149Sdougb	dns_stats_detach(&server->rcvquerystats);
6255193149Sdougb	dns_stats_detach(&server->opcodestats);
6256193149Sdougb	isc_stats_detach(&server->zonestats);
6257193149Sdougb	isc_stats_detach(&server->resolverstats);
6258193149Sdougb	isc_stats_detach(&server->sockstats);
6259135446Strhodes
6260135446Strhodes	isc_mem_free(server->mctx, server->statsfile);
6261224092Sdougb	isc_mem_free(server->mctx, server->bindkeysfile);
6262135446Strhodes	isc_mem_free(server->mctx, server->dumpfile);
6263224092Sdougb	isc_mem_free(server->mctx, server->secrootsfile);
6264135446Strhodes	isc_mem_free(server->mctx, server->recfile);
6265135446Strhodes
6266135446Strhodes	if (server->version != NULL)
6267135446Strhodes		isc_mem_free(server->mctx, server->version);
6268135446Strhodes	if (server->hostname != NULL)
6269135446Strhodes		isc_mem_free(server->mctx, server->hostname);
6270135446Strhodes	if (server->server_id != NULL)
6271135446Strhodes		isc_mem_free(server->mctx, server->server_id);
6272135446Strhodes
6273225361Sdougb	if (server->zonemgr != NULL)
6274225361Sdougb		dns_zonemgr_detach(&server->zonemgr);
6275135446Strhodes
6276135446Strhodes	if (server->tkeyctx != NULL)
6277135446Strhodes		dns_tkeyctx_destroy(&server->tkeyctx);
6278135446Strhodes
6279135446Strhodes	dst_lib_destroy();
6280135446Strhodes
6281135446Strhodes	isc_event_free(&server->reload_event);
6282135446Strhodes
6283135446Strhodes	INSIST(ISC_LIST_EMPTY(server->viewlist));
6284224092Sdougb	INSIST(ISC_LIST_EMPTY(server->cachelist));
6285135446Strhodes
6286135446Strhodes	dns_aclenv_destroy(&server->aclenv);
6287135446Strhodes
6288135446Strhodes	isc_quota_destroy(&server->recursionquota);
6289135446Strhodes	isc_quota_destroy(&server->tcpquota);
6290135446Strhodes	isc_quota_destroy(&server->xfroutquota);
6291135446Strhodes
6292135446Strhodes	server->magic = 0;
6293135446Strhodes	isc_mem_put(server->mctx, server, sizeof(*server));
6294135446Strhodes	*serverp = NULL;
6295135446Strhodes}
6296135446Strhodes
6297135446Strhodesstatic void
6298135446Strhodesfatal(const char *msg, isc_result_t result) {
6299135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
6300135446Strhodes		      ISC_LOG_CRITICAL, "%s: %s", msg,
6301135446Strhodes		      isc_result_totext(result));
6302135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
6303135446Strhodes		      ISC_LOG_CRITICAL, "exiting (due to fatal error)");
6304135446Strhodes	exit(1);
6305135446Strhodes}
6306135446Strhodes
6307135446Strhodesstatic void
6308135446Strhodesstart_reserved_dispatches(ns_server_t *server) {
6309135446Strhodes
6310135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6311135446Strhodes
6312135446Strhodes	server->dispatchgen++;
6313135446Strhodes}
6314135446Strhodes
6315135446Strhodesstatic void
6316135446Strhodesend_reserved_dispatches(ns_server_t *server, isc_boolean_t all) {
6317135446Strhodes	ns_dispatch_t *dispatch, *nextdispatch;
6318135446Strhodes
6319135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6320135446Strhodes
6321135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
6322135446Strhodes	     dispatch != NULL;
6323135446Strhodes	     dispatch = nextdispatch) {
6324135446Strhodes		nextdispatch = ISC_LIST_NEXT(dispatch, link);
6325135446Strhodes		if (!all && server->dispatchgen == dispatch-> dispatchgen)
6326135446Strhodes			continue;
6327135446Strhodes		ISC_LIST_UNLINK(server->dispatches, dispatch, link);
6328135446Strhodes		dns_dispatch_detach(&dispatch->dispatch);
6329135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
6330135446Strhodes	}
6331135446Strhodes}
6332135446Strhodes
6333135446Strhodesvoid
6334165071Sdougbns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) {
6335135446Strhodes	ns_dispatch_t *dispatch;
6336135446Strhodes	in_port_t port;
6337135446Strhodes	char addrbuf[ISC_SOCKADDR_FORMATSIZE];
6338135446Strhodes	isc_result_t result;
6339135446Strhodes	unsigned int attrs, attrmask;
6340135446Strhodes
6341135446Strhodes	REQUIRE(NS_SERVER_VALID(server));
6342135446Strhodes
6343135446Strhodes	port = isc_sockaddr_getport(addr);
6344135446Strhodes	if (port == 0 || port >= 1024)
6345135446Strhodes		return;
6346135446Strhodes
6347135446Strhodes	for (dispatch = ISC_LIST_HEAD(server->dispatches);
6348135446Strhodes	     dispatch != NULL;
6349135446Strhodes	     dispatch = ISC_LIST_NEXT(dispatch, link)) {
6350135446Strhodes		if (isc_sockaddr_equal(&dispatch->addr, addr))
6351135446Strhodes			break;
6352135446Strhodes	}
6353135446Strhodes	if (dispatch != NULL) {
6354135446Strhodes		dispatch->dispatchgen = server->dispatchgen;
6355135446Strhodes		return;
6356135446Strhodes	}
6357135446Strhodes
6358135446Strhodes	dispatch = isc_mem_get(server->mctx, sizeof(*dispatch));
6359135446Strhodes	if (dispatch == NULL) {
6360135446Strhodes		result = ISC_R_NOMEMORY;
6361135446Strhodes		goto cleanup;
6362135446Strhodes	}
6363135446Strhodes
6364135446Strhodes	dispatch->addr = *addr;
6365135446Strhodes	dispatch->dispatchgen = server->dispatchgen;
6366135446Strhodes	dispatch->dispatch = NULL;
6367135446Strhodes
6368135446Strhodes	attrs = 0;
6369135446Strhodes	attrs |= DNS_DISPATCHATTR_UDP;
6370135446Strhodes	switch (isc_sockaddr_pf(addr)) {
6371135446Strhodes	case AF_INET:
6372135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
6373135446Strhodes		break;
6374135446Strhodes	case AF_INET6:
6375135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
6376135446Strhodes		break;
6377135446Strhodes	default:
6378135446Strhodes		result = ISC_R_NOTIMPLEMENTED;
6379135446Strhodes		goto cleanup;
6380135446Strhodes	}
6381135446Strhodes	attrmask = 0;
6382135446Strhodes	attrmask |= DNS_DISPATCHATTR_UDP;
6383135446Strhodes	attrmask |= DNS_DISPATCHATTR_TCP;
6384135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4;
6385135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV6;
6386135446Strhodes
6387135446Strhodes	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
6388135446Strhodes				     ns_g_taskmgr, &dispatch->addr, 4096,
6389135446Strhodes				     1000, 32768, 16411, 16433,
6390186462Sdougb				     attrs, attrmask, &dispatch->dispatch);
6391135446Strhodes	if (result != ISC_R_SUCCESS)
6392135446Strhodes		goto cleanup;
6393135446Strhodes
6394135446Strhodes	ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link);
6395135446Strhodes
6396135446Strhodes	return;
6397135446Strhodes
6398135446Strhodes cleanup:
6399135446Strhodes	if (dispatch != NULL)
6400135446Strhodes		isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
6401135446Strhodes	isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
6402135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6403135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
6404135446Strhodes		      "unable to create dispatch for reserved port %s: %s",
6405135446Strhodes		      addrbuf, isc_result_totext(result));
6406135446Strhodes}
6407135446Strhodes
6408135446Strhodes
6409135446Strhodesstatic isc_result_t
6410135446Strhodesloadconfig(ns_server_t *server) {
6411135446Strhodes	isc_result_t result;
6412135446Strhodes	start_reserved_dispatches(server);
6413135446Strhodes	result = load_configuration(ns_g_lwresdonly ?
6414135446Strhodes				    lwresd_g_conffile : ns_g_conffile,
6415143731Sdougb				    server, ISC_FALSE);
6416193149Sdougb	if (result == ISC_R_SUCCESS) {
6417135446Strhodes		end_reserved_dispatches(server, ISC_FALSE);
6418135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6419193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6420193149Sdougb			      "reloading configuration succeeded");
6421193149Sdougb	} else {
6422193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6423135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6424135446Strhodes			      "reloading configuration failed: %s",
6425135446Strhodes			      isc_result_totext(result));
6426193149Sdougb	}
6427135446Strhodes	return (result);
6428135446Strhodes}
6429135446Strhodes
6430135446Strhodesstatic isc_result_t
6431135446Strhodesreload(ns_server_t *server) {
6432135446Strhodes	isc_result_t result;
6433135446Strhodes	CHECK(loadconfig(server));
6434135446Strhodes
6435262706Serwin	result = load_zones(server, ISC_FALSE);
6436193149Sdougb	if (result == ISC_R_SUCCESS)
6437135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6438193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6439193149Sdougb			      "reloading zones succeeded");
6440193149Sdougb	else
6441193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6442135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6443135446Strhodes			      "reloading zones failed: %s",
6444135446Strhodes			      isc_result_totext(result));
6445193149Sdougb
6446135446Strhodes cleanup:
6447135446Strhodes	return (result);
6448135446Strhodes}
6449135446Strhodes
6450135446Strhodesstatic void
6451135446Strhodesreconfig(ns_server_t *server) {
6452135446Strhodes	isc_result_t result;
6453135446Strhodes	CHECK(loadconfig(server));
6454135446Strhodes
6455135446Strhodes	result = load_new_zones(server, ISC_FALSE);
6456193149Sdougb	if (result == ISC_R_SUCCESS)
6457135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6458193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6459193149Sdougb			      "any newly configured zones are now loaded");
6460193149Sdougb	else
6461193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6462135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6463135446Strhodes			      "loading new zones failed: %s",
6464135446Strhodes			      isc_result_totext(result));
6465193149Sdougb
6466135446Strhodes cleanup: ;
6467135446Strhodes}
6468135446Strhodes
6469135446Strhodes/*
6470135446Strhodes * Handle a reload event (from SIGHUP).
6471135446Strhodes */
6472135446Strhodesstatic void
6473135446Strhodesns_server_reload(isc_task_t *task, isc_event_t *event) {
6474135446Strhodes	ns_server_t *server = (ns_server_t *)event->ev_arg;
6475135446Strhodes
6476135446Strhodes	INSIST(task = server->task);
6477135446Strhodes	UNUSED(task);
6478135446Strhodes
6479193149Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6480193149Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6481193149Sdougb		      "received SIGHUP signal to reload zones");
6482135446Strhodes	(void)reload(server);
6483135446Strhodes
6484135446Strhodes	LOCK(&server->reload_event_lock);
6485135446Strhodes	INSIST(server->reload_event == NULL);
6486135446Strhodes	server->reload_event = event;
6487135446Strhodes	UNLOCK(&server->reload_event_lock);
6488135446Strhodes}
6489135446Strhodes
6490135446Strhodesvoid
6491135446Strhodesns_server_reloadwanted(ns_server_t *server) {
6492135446Strhodes	LOCK(&server->reload_event_lock);
6493135446Strhodes	if (server->reload_event != NULL)
6494135446Strhodes		isc_task_send(server->task, &server->reload_event);
6495135446Strhodes	UNLOCK(&server->reload_event_lock);
6496135446Strhodes}
6497135446Strhodes
6498135446Strhodesstatic char *
6499135446Strhodesnext_token(char **stringp, const char *delim) {
6500135446Strhodes	char *res;
6501135446Strhodes
6502135446Strhodes	do {
6503135446Strhodes		res = strsep(stringp, delim);
6504135446Strhodes		if (res == NULL)
6505135446Strhodes			break;
6506135446Strhodes	} while (*res == '\0');
6507135446Strhodes	return (res);
6508186462Sdougb}
6509135446Strhodes
6510135446Strhodes/*
6511135446Strhodes * Find the zone specified in the control channel command 'args',
6512135446Strhodes * if any.  If a zone is specified, point '*zonep' at it, otherwise
6513135446Strhodes * set '*zonep' to NULL.
6514135446Strhodes */
6515135446Strhodesstatic isc_result_t
6516254897Serwinzone_from_args(ns_server_t *server, char *args, const char *zonetxt,
6517262706Serwin	       dns_zone_t **zonep, const char **zonename,
6518262706Serwin	       isc_buffer_t *text, isc_boolean_t skip)
6519224092Sdougb{
6520135446Strhodes	char *input, *ptr;
6521135446Strhodes	char *classtxt;
6522135446Strhodes	const char *viewtxt = NULL;
6523262706Serwin	dns_fixedname_t fname;
6524262706Serwin	dns_name_t *name;
6525135446Strhodes	isc_result_t result;
6526135446Strhodes	dns_view_t *view = NULL;
6527135446Strhodes	dns_rdataclass_t rdclass;
6528262706Serwin	char problem[DNS_NAME_FORMATSIZE + 500] = "";
6529135446Strhodes
6530135446Strhodes	REQUIRE(zonep != NULL && *zonep == NULL);
6531254402Serwin	REQUIRE(zonename == NULL || *zonename == NULL);
6532135446Strhodes
6533135446Strhodes	input = args;
6534135446Strhodes
6535254897Serwin	if (skip) {
6536254897Serwin		/* Skip the command name. */
6537254897Serwin		ptr = next_token(&input, " \t");
6538254897Serwin		if (ptr == NULL)
6539254897Serwin			return (ISC_R_UNEXPECTEDEND);
6540254897Serwin	}
6541135446Strhodes
6542135446Strhodes	/* Look for the zone name. */
6543135446Strhodes	if (zonetxt == NULL)
6544254897Serwin		zonetxt = next_token(&input, " \t");
6545254897Serwin	if (zonetxt == NULL)
6546135446Strhodes		return (ISC_R_SUCCESS);
6547254402Serwin	if (zonename != NULL)
6548224092Sdougb		*zonename = zonetxt;
6549135446Strhodes
6550135446Strhodes	/* Look for the optional class name. */
6551135446Strhodes	classtxt = next_token(&input, " \t");
6552135446Strhodes	if (classtxt != NULL) {
6553135446Strhodes		/* Look for the optional view name. */
6554135446Strhodes		viewtxt = next_token(&input, " \t");
6555135446Strhodes	}
6556135446Strhodes
6557262706Serwin	dns_fixedname_init(&fname);
6558262706Serwin	name = dns_fixedname_name(&fname);
6559262706Serwin	CHECK(dns_name_fromstring(name, zonetxt, 0, NULL));
6560135446Strhodes
6561135446Strhodes	if (classtxt != NULL) {
6562135446Strhodes		isc_textregion_t r;
6563135446Strhodes		r.base = classtxt;
6564135446Strhodes		r.length = strlen(classtxt);
6565262706Serwin		CHECK(dns_rdataclass_fromtext(&rdclass, &r));
6566193149Sdougb	} else
6567193149Sdougb		rdclass = dns_rdataclass_in;
6568193149Sdougb
6569193149Sdougb	if (viewtxt == NULL) {
6570262706Serwin		result = dns_viewlist_findzone(&server->viewlist, name,
6571193149Sdougb					       ISC_TF(classtxt == NULL),
6572193149Sdougb					       rdclass, zonep);
6573262706Serwin		if (result == ISC_R_NOTFOUND)
6574262706Serwin			snprintf(problem, sizeof(problem),
6575262706Serwin				 "no matching zone '%s' in any view",
6576262706Serwin				 zonetxt);
6577135446Strhodes	} else {
6578193149Sdougb		result = dns_viewlist_find(&server->viewlist, viewtxt,
6579193149Sdougb					   rdclass, &view);
6580262706Serwin		if (result != ISC_R_SUCCESS) {
6581262706Serwin			snprintf(problem, sizeof(problem),
6582262706Serwin				 "no matching view '%s'", viewtxt);
6583262706Serwin			goto report;
6584262706Serwin		}
6585262706Serwin
6586262706Serwin		result = dns_zt_find(view->zonetable, name, 0, NULL, zonep);
6587193149Sdougb		if (result != ISC_R_SUCCESS)
6588262706Serwin			snprintf(problem, sizeof(problem),
6589262706Serwin				 "no matching zone '%s' in view '%s'",
6590262706Serwin				 zonetxt, viewtxt);
6591135446Strhodes	}
6592186462Sdougb
6593135446Strhodes	/* Partial match? */
6594135446Strhodes	if (result != ISC_R_SUCCESS && *zonep != NULL)
6595135446Strhodes		dns_zone_detach(zonep);
6596204619Sdougb	if (result == DNS_R_PARTIALMATCH)
6597204619Sdougb		result = ISC_R_NOTFOUND;
6598262706Serwin report:
6599262706Serwin	if (result != ISC_R_SUCCESS) {
6600262706Serwin		isc_result_t tresult;
6601262706Serwin
6602262706Serwin		tresult = putstr(text, problem);
6603262706Serwin		if (tresult == ISC_R_SUCCESS &&
6604262706Serwin		    isc_buffer_availablelength(text) > 0U)
6605262706Serwin			isc_buffer_putuint8(text, 0);
6606262706Serwin	}
6607262706Serwin
6608262706Serwin cleanup:
6609262706Serwin	if (view != NULL)
6610262706Serwin		dns_view_detach(&view);
6611262706Serwin
6612135446Strhodes	return (result);
6613135446Strhodes}
6614135446Strhodes
6615135446Strhodes/*
6616135446Strhodes * Act on a "retransfer" command from the command channel.
6617135446Strhodes */
6618135446Strhodesisc_result_t
6619262706Serwinns_server_retransfercommand(ns_server_t *server, char *args,
6620262706Serwin			    isc_buffer_t *text)
6621262706Serwin{
6622135446Strhodes	isc_result_t result;
6623135446Strhodes	dns_zone_t *zone = NULL;
6624254897Serwin	dns_zone_t *raw = NULL;
6625135446Strhodes	dns_zonetype_t type;
6626186462Sdougb
6627262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
6628262706Serwin				text, ISC_TRUE);
6629135446Strhodes	if (result != ISC_R_SUCCESS)
6630135446Strhodes		return (result);
6631135446Strhodes	if (zone == NULL)
6632135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6633254897Serwin	dns_zone_getraw(zone, &raw);
6634254897Serwin	if (raw != NULL) {
6635254897Serwin		dns_zone_detach(&zone);
6636254897Serwin		dns_zone_attach(raw, &zone);
6637254897Serwin		dns_zone_detach(&raw);
6638254897Serwin	}
6639135446Strhodes	type = dns_zone_gettype(zone);
6640135446Strhodes	if (type == dns_zone_slave || type == dns_zone_stub)
6641135446Strhodes		dns_zone_forcereload(zone);
6642135446Strhodes	else
6643135446Strhodes		result = ISC_R_NOTFOUND;
6644135446Strhodes	dns_zone_detach(&zone);
6645135446Strhodes	return (result);
6646186462Sdougb}
6647135446Strhodes
6648135446Strhodes/*
6649135446Strhodes * Act on a "reload" command from the command channel.
6650135446Strhodes */
6651135446Strhodesisc_result_t
6652135446Strhodesns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6653135446Strhodes	isc_result_t result;
6654135446Strhodes	dns_zone_t *zone = NULL;
6655135446Strhodes	dns_zonetype_t type;
6656135446Strhodes	const char *msg = NULL;
6657186462Sdougb
6658262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
6659262706Serwin				text, ISC_TRUE);
6660135446Strhodes	if (result != ISC_R_SUCCESS)
6661135446Strhodes		return (result);
6662135446Strhodes	if (zone == NULL) {
6663135446Strhodes		result = reload(server);
6664135446Strhodes		if (result == ISC_R_SUCCESS)
6665135446Strhodes			msg = "server reload successful";
6666135446Strhodes	} else {
6667135446Strhodes		type = dns_zone_gettype(zone);
6668135446Strhodes		if (type == dns_zone_slave || type == dns_zone_stub) {
6669135446Strhodes			dns_zone_refresh(zone);
6670174187Sdougb			dns_zone_detach(&zone);
6671135446Strhodes			msg = "zone refresh queued";
6672135446Strhodes		} else {
6673135446Strhodes			result = dns_zone_load(zone);
6674135446Strhodes			dns_zone_detach(&zone);
6675186462Sdougb			switch (result) {
6676135446Strhodes			case ISC_R_SUCCESS:
6677135446Strhodes				 msg = "zone reload successful";
6678135446Strhodes				 break;
6679135446Strhodes			case DNS_R_CONTINUE:
6680135446Strhodes				msg = "zone reload queued";
6681135446Strhodes				result = ISC_R_SUCCESS;
6682135446Strhodes				break;
6683135446Strhodes			case DNS_R_UPTODATE:
6684135446Strhodes				msg = "zone reload up-to-date";
6685135446Strhodes				result = ISC_R_SUCCESS;
6686135446Strhodes				break;
6687135446Strhodes			default:
6688135446Strhodes				/* failure message will be generated by rndc */
6689135446Strhodes				break;
6690135446Strhodes			}
6691135446Strhodes		}
6692135446Strhodes	}
6693135446Strhodes	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
6694135446Strhodes		isc_buffer_putmem(text, (const unsigned char *)msg,
6695135446Strhodes				  strlen(msg) + 1);
6696135446Strhodes	return (result);
6697186462Sdougb}
6698135446Strhodes
6699135446Strhodes/*
6700135446Strhodes * Act on a "reconfig" command from the command channel.
6701135446Strhodes */
6702135446Strhodesisc_result_t
6703135446Strhodesns_server_reconfigcommand(ns_server_t *server, char *args) {
6704135446Strhodes	UNUSED(args);
6705135446Strhodes
6706135446Strhodes	reconfig(server);
6707135446Strhodes	return (ISC_R_SUCCESS);
6708135446Strhodes}
6709135446Strhodes
6710135446Strhodes/*
6711170222Sdougb * Act on a "notify" command from the command channel.
6712170222Sdougb */
6713170222Sdougbisc_result_t
6714170222Sdougbns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6715170222Sdougb	isc_result_t result;
6716170222Sdougb	dns_zone_t *zone = NULL;
6717170222Sdougb	const unsigned char msg[] = "zone notify queued";
6718170222Sdougb
6719262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
6720262706Serwin				text, ISC_TRUE);
6721170222Sdougb	if (result != ISC_R_SUCCESS)
6722170222Sdougb		return (result);
6723170222Sdougb	if (zone == NULL)
6724170222Sdougb		return (ISC_R_UNEXPECTEDEND);
6725186462Sdougb
6726170222Sdougb	dns_zone_notify(zone);
6727170222Sdougb	dns_zone_detach(&zone);
6728170222Sdougb	if (sizeof(msg) <= isc_buffer_availablelength(text))
6729170222Sdougb		isc_buffer_putmem(text, msg, sizeof(msg));
6730170222Sdougb
6731170222Sdougb	return (ISC_R_SUCCESS);
6732186462Sdougb}
6733170222Sdougb
6734170222Sdougb/*
6735135446Strhodes * Act on a "refresh" command from the command channel.
6736135446Strhodes */
6737135446Strhodesisc_result_t
6738135446Strhodesns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
6739135446Strhodes	isc_result_t result;
6740262706Serwin	dns_zone_t *zone = NULL, *raw = NULL;
6741165071Sdougb	const unsigned char msg1[] = "zone refresh queued";
6742165071Sdougb	const unsigned char msg2[] = "not a slave or stub zone";
6743165071Sdougb	dns_zonetype_t type;
6744135446Strhodes
6745262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
6746262706Serwin				text, ISC_TRUE);
6747135446Strhodes	if (result != ISC_R_SUCCESS)
6748135446Strhodes		return (result);
6749135446Strhodes	if (zone == NULL)
6750135446Strhodes		return (ISC_R_UNEXPECTEDEND);
6751165071Sdougb
6752262706Serwin	dns_zone_getraw(zone, &raw);
6753262706Serwin	if (raw != NULL) {
6754262706Serwin		dns_zone_detach(&zone);
6755262706Serwin		dns_zone_attach(raw, &zone);
6756262706Serwin		dns_zone_detach(&raw);
6757262706Serwin	}
6758262706Serwin
6759165071Sdougb	type = dns_zone_gettype(zone);
6760165071Sdougb	if (type == dns_zone_slave || type == dns_zone_stub) {
6761165071Sdougb		dns_zone_refresh(zone);
6762165071Sdougb		dns_zone_detach(&zone);
6763165071Sdougb		if (sizeof(msg1) <= isc_buffer_availablelength(text))
6764165071Sdougb			isc_buffer_putmem(text, msg1, sizeof(msg1));
6765165071Sdougb		return (ISC_R_SUCCESS);
6766165071Sdougb	}
6767186462Sdougb
6768135446Strhodes	dns_zone_detach(&zone);
6769165071Sdougb	if (sizeof(msg2) <= isc_buffer_availablelength(text))
6770165071Sdougb		isc_buffer_putmem(text, msg2, sizeof(msg2));
6771165071Sdougb	return (ISC_R_FAILURE);
6772186462Sdougb}
6773135446Strhodes
6774135446Strhodesisc_result_t
6775254897Serwinns_server_togglequerylog(ns_server_t *server, char *args) {
6776254897Serwin	isc_boolean_t value;
6777254897Serwin	char *ptr;
6778186462Sdougb
6779254897Serwin	/* Skip the command name. */
6780254897Serwin	ptr = next_token(&args, " \t");
6781254897Serwin	if (ptr == NULL)
6782254897Serwin		return (ISC_R_UNEXPECTEDEND);
6783254897Serwin
6784254897Serwin	ptr = next_token(&args, " \t");
6785254897Serwin	if (ptr == NULL)
6786254897Serwin		value = server->log_queries ? ISC_FALSE : ISC_TRUE;
6787254897Serwin	else if (strcasecmp(ptr, "yes") == 0 || strcasecmp(ptr, "on") == 0)
6788254897Serwin		value = ISC_TRUE;
6789254897Serwin	else if (strcasecmp(ptr, "no") == 0 || strcasecmp(ptr, "off") == 0)
6790254897Serwin		value = ISC_FALSE;
6791254897Serwin	else
6792254897Serwin		return (ISC_R_NOTFOUND);
6793254897Serwin
6794254897Serwin	if (server->log_queries == value)
6795254897Serwin		return (ISC_R_SUCCESS);
6796254897Serwin
6797254897Serwin	server->log_queries = value;
6798254897Serwin
6799135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6800135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6801135446Strhodes		      "query logging is now %s",
6802135446Strhodes		      server->log_queries ? "on" : "off");
6803135446Strhodes	return (ISC_R_SUCCESS);
6804135446Strhodes}
6805135446Strhodes
6806135446Strhodesstatic isc_result_t
6807165071Sdougbns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
6808170222Sdougb			 cfg_aclconfctx_t *actx,
6809135446Strhodes			 isc_mem_t *mctx, ns_listenlist_t **target)
6810135446Strhodes{
6811135446Strhodes	isc_result_t result;
6812165071Sdougb	const cfg_listelt_t *element;
6813135446Strhodes	ns_listenlist_t *dlist = NULL;
6814135446Strhodes
6815135446Strhodes	REQUIRE(target != NULL && *target == NULL);
6816135446Strhodes
6817135446Strhodes	result = ns_listenlist_create(mctx, &dlist);
6818135446Strhodes	if (result != ISC_R_SUCCESS)
6819135446Strhodes		return (result);
6820135446Strhodes
6821135446Strhodes	for (element = cfg_list_first(listenlist);
6822135446Strhodes	     element != NULL;
6823135446Strhodes	     element = cfg_list_next(element))
6824135446Strhodes	{
6825135446Strhodes		ns_listenelt_t *delt = NULL;
6826165071Sdougb		const cfg_obj_t *listener = cfg_listelt_value(element);
6827135446Strhodes		result = ns_listenelt_fromconfig(listener, config, actx,
6828135446Strhodes						 mctx, &delt);
6829135446Strhodes		if (result != ISC_R_SUCCESS)
6830135446Strhodes			goto cleanup;
6831135446Strhodes		ISC_LIST_APPEND(dlist->elts, delt, link);
6832135446Strhodes	}
6833135446Strhodes	*target = dlist;
6834135446Strhodes	return (ISC_R_SUCCESS);
6835135446Strhodes
6836135446Strhodes cleanup:
6837135446Strhodes	ns_listenlist_detach(&dlist);
6838135446Strhodes	return (result);
6839135446Strhodes}
6840135446Strhodes
6841135446Strhodes/*
6842135446Strhodes * Create a listen list from the corresponding configuration
6843135446Strhodes * data structure.
6844135446Strhodes */
6845135446Strhodesstatic isc_result_t
6846165071Sdougbns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
6847170222Sdougb			cfg_aclconfctx_t *actx,
6848135446Strhodes			isc_mem_t *mctx, ns_listenelt_t **target)
6849135446Strhodes{
6850135446Strhodes	isc_result_t result;
6851165071Sdougb	const cfg_obj_t *portobj;
6852135446Strhodes	in_port_t port;
6853135446Strhodes	ns_listenelt_t *delt = NULL;
6854135446Strhodes	REQUIRE(target != NULL && *target == NULL);
6855135446Strhodes
6856135446Strhodes	portobj = cfg_tuple_get(listener, "port");
6857135446Strhodes	if (!cfg_obj_isuint32(portobj)) {
6858135446Strhodes		if (ns_g_port != 0) {
6859135446Strhodes			port = ns_g_port;
6860135446Strhodes		} else {
6861135446Strhodes			result = ns_config_getport(config, &port);
6862135446Strhodes			if (result != ISC_R_SUCCESS)
6863135446Strhodes				return (result);
6864135446Strhodes		}
6865135446Strhodes	} else {
6866135446Strhodes		if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
6867135446Strhodes			cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
6868135446Strhodes				    "port value '%u' is out of range",
6869135446Strhodes				    cfg_obj_asuint32(portobj));
6870135446Strhodes			return (ISC_R_RANGE);
6871135446Strhodes		}
6872135446Strhodes		port = (in_port_t)cfg_obj_asuint32(portobj);
6873135446Strhodes	}
6874135446Strhodes
6875135446Strhodes	result = ns_listenelt_create(mctx, port, NULL, &delt);
6876135446Strhodes	if (result != ISC_R_SUCCESS)
6877135446Strhodes		return (result);
6878135446Strhodes
6879170222Sdougb	result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
6880193149Sdougb				   config, ns_g_lctx, actx, mctx, 0,
6881193149Sdougb				   &delt->acl);
6882135446Strhodes	if (result != ISC_R_SUCCESS) {
6883135446Strhodes		ns_listenelt_destroy(delt);
6884135446Strhodes		return (result);
6885135446Strhodes	}
6886135446Strhodes	*target = delt;
6887135446Strhodes	return (ISC_R_SUCCESS);
6888135446Strhodes}
6889135446Strhodes
6890135446Strhodesisc_result_t
6891135446Strhodesns_server_dumpstats(ns_server_t *server) {
6892135446Strhodes	isc_result_t result;
6893135446Strhodes	FILE *fp = NULL;
6894135446Strhodes
6895135446Strhodes	CHECKMF(isc_stdio_open(server->statsfile, "a", &fp),
6896135446Strhodes		"could not open statistics dump file", server->statsfile);
6897186462Sdougb
6898193149Sdougb	result = ns_stats_dump(server, fp);
6899186462Sdougb
6900135446Strhodes cleanup:
6901135446Strhodes	if (fp != NULL)
6902135446Strhodes		(void)isc_stdio_close(fp);
6903193149Sdougb	if (result == ISC_R_SUCCESS)
6904193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6905193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
6906193149Sdougb			      "dumpstats complete");
6907193149Sdougb	else
6908193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
6909193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
6910193149Sdougb			      "dumpstats failed: %s",
6911193149Sdougb			      dns_result_totext(result));
6912135446Strhodes	return (result);
6913135446Strhodes}
6914135446Strhodes
6915135446Strhodesstatic isc_result_t
6916135446Strhodesadd_zone_tolist(dns_zone_t *zone, void *uap) {
6917135446Strhodes	struct dumpcontext *dctx = uap;
6918135446Strhodes	struct zonelistentry *zle;
6919135446Strhodes
6920135446Strhodes	zle = isc_mem_get(dctx->mctx, sizeof *zle);
6921135446Strhodes	if (zle ==  NULL)
6922135446Strhodes		return (ISC_R_NOMEMORY);
6923135446Strhodes	zle->zone = NULL;
6924135446Strhodes	dns_zone_attach(zone, &zle->zone);
6925135446Strhodes	ISC_LINK_INIT(zle, link);
6926135446Strhodes	ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link);
6927135446Strhodes	return (ISC_R_SUCCESS);
6928135446Strhodes}
6929135446Strhodes
6930135446Strhodesstatic isc_result_t
6931135446Strhodesadd_view_tolist(struct dumpcontext *dctx, dns_view_t *view) {
6932135446Strhodes	struct viewlistentry *vle;
6933135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
6934186462Sdougb
6935153816Sdougb	/*
6936153816Sdougb	 * Prevent duplicate views.
6937153816Sdougb	 */
6938153816Sdougb	for (vle = ISC_LIST_HEAD(dctx->viewlist);
6939153816Sdougb	     vle != NULL;
6940153816Sdougb	     vle = ISC_LIST_NEXT(vle, link))
6941153816Sdougb		if (vle->view == view)
6942153816Sdougb			return (ISC_R_SUCCESS);
6943153816Sdougb
6944135446Strhodes	vle = isc_mem_get(dctx->mctx, sizeof *vle);
6945135446Strhodes	if (vle == NULL)
6946135446Strhodes		return (ISC_R_NOMEMORY);
6947135446Strhodes	vle->view = NULL;
6948135446Strhodes	dns_view_attach(view, &vle->view);
6949135446Strhodes	ISC_LINK_INIT(vle, link);
6950135446Strhodes	ISC_LIST_INIT(vle->zonelist);
6951135446Strhodes	ISC_LIST_APPEND(dctx->viewlist, vle, link);
6952135446Strhodes	if (dctx->dumpzones)
6953135446Strhodes		result = dns_zt_apply(view->zonetable, ISC_TRUE,
6954135446Strhodes				      add_zone_tolist, dctx);
6955135446Strhodes	return (result);
6956135446Strhodes}
6957135446Strhodes
6958135446Strhodesstatic void
6959135446Strhodesdumpcontext_destroy(struct dumpcontext *dctx) {
6960135446Strhodes	struct viewlistentry *vle;
6961135446Strhodes	struct zonelistentry *zle;
6962135446Strhodes
6963135446Strhodes	vle = ISC_LIST_HEAD(dctx->viewlist);
6964135446Strhodes	while (vle != NULL) {
6965135446Strhodes		ISC_LIST_UNLINK(dctx->viewlist, vle, link);
6966135446Strhodes		zle = ISC_LIST_HEAD(vle->zonelist);
6967135446Strhodes		while (zle != NULL) {
6968135446Strhodes			ISC_LIST_UNLINK(vle->zonelist, zle, link);
6969135446Strhodes			dns_zone_detach(&zle->zone);
6970135446Strhodes			isc_mem_put(dctx->mctx, zle, sizeof *zle);
6971135446Strhodes			zle = ISC_LIST_HEAD(vle->zonelist);
6972135446Strhodes		}
6973135446Strhodes		dns_view_detach(&vle->view);
6974135446Strhodes		isc_mem_put(dctx->mctx, vle, sizeof *vle);
6975135446Strhodes		vle = ISC_LIST_HEAD(dctx->viewlist);
6976135446Strhodes	}
6977135446Strhodes	if (dctx->version != NULL)
6978135446Strhodes		dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
6979135446Strhodes	if (dctx->db != NULL)
6980135446Strhodes		dns_db_detach(&dctx->db);
6981135446Strhodes	if (dctx->cache != NULL)
6982135446Strhodes		dns_db_detach(&dctx->cache);
6983135446Strhodes	if (dctx->task != NULL)
6984135446Strhodes		isc_task_detach(&dctx->task);
6985135446Strhodes	if (dctx->fp != NULL)
6986135446Strhodes		(void)isc_stdio_close(dctx->fp);
6987135446Strhodes	if (dctx->mdctx != NULL)
6988135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
6989135446Strhodes	isc_mem_put(dctx->mctx, dctx, sizeof *dctx);
6990135446Strhodes}
6991135446Strhodes
6992135446Strhodesstatic void
6993135446Strhodesdumpdone(void *arg, isc_result_t result) {
6994135446Strhodes	struct dumpcontext *dctx = arg;
6995135446Strhodes	char buf[1024+32];
6996135446Strhodes	const dns_master_style_t *style;
6997186462Sdougb
6998135446Strhodes	if (result != ISC_R_SUCCESS)
6999135446Strhodes		goto cleanup;
7000135446Strhodes	if (dctx->mdctx != NULL)
7001135446Strhodes		dns_dumpctx_detach(&dctx->mdctx);
7002135446Strhodes	if (dctx->view == NULL) {
7003135446Strhodes		dctx->view = ISC_LIST_HEAD(dctx->viewlist);
7004135446Strhodes		if (dctx->view == NULL)
7005135446Strhodes			goto done;
7006135446Strhodes		INSIST(dctx->zone == NULL);
7007153816Sdougb	} else
7008153816Sdougb		goto resume;
7009135446Strhodes nextview:
7010135446Strhodes	fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
7011153816Sdougb resume:
7012224092Sdougb	if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) {
7013224092Sdougb		fprintf(dctx->fp,
7014224092Sdougb			";\n; Cache of view '%s' is shared as '%s'\n",
7015224092Sdougb			dctx->view->view->name,
7016224092Sdougb			dns_cache_getname(dctx->view->view->cache));
7017224092Sdougb	} else if (dctx->zone == NULL && dctx->cache == NULL &&
7018224092Sdougb		   dctx->dumpcache)
7019224092Sdougb	{
7020135446Strhodes		style = &dns_master_style_cache;
7021135446Strhodes		/* start cache dump */
7022135446Strhodes		if (dctx->view->view->cachedb != NULL)
7023135446Strhodes			dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
7024135446Strhodes		if (dctx->cache != NULL) {
7025224092Sdougb			fprintf(dctx->fp,
7026224092Sdougb				";\n; Cache dump of view '%s' (cache %s)\n;\n",
7027224092Sdougb				dctx->view->view->name,
7028224092Sdougb				dns_cache_getname(dctx->view->view->cache));
7029135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
7030135446Strhodes							    dctx->cache, NULL,
7031135446Strhodes							    style, dctx->fp,
7032135446Strhodes							    dctx->task,
7033135446Strhodes							    dumpdone, dctx,
7034135446Strhodes							    &dctx->mdctx);
7035135446Strhodes			if (result == DNS_R_CONTINUE)
7036135446Strhodes				return;
7037135446Strhodes			if (result == ISC_R_NOTIMPLEMENTED)
7038135446Strhodes				fprintf(dctx->fp, "; %s\n",
7039135446Strhodes					dns_result_totext(result));
7040135446Strhodes			else if (result != ISC_R_SUCCESS)
7041135446Strhodes				goto cleanup;
7042135446Strhodes		}
7043135446Strhodes	}
7044135446Strhodes	if (dctx->cache != NULL) {
7045135446Strhodes		dns_adb_dump(dctx->view->view->adb, dctx->fp);
7046205292Sdougb		dns_resolver_printbadcache(dctx->view->view->resolver,
7047205292Sdougb					   dctx->fp);
7048135446Strhodes		dns_db_detach(&dctx->cache);
7049135446Strhodes	}
7050135446Strhodes	if (dctx->dumpzones) {
7051135446Strhodes		style = &dns_master_style_full;
7052135446Strhodes nextzone:
7053135446Strhodes		if (dctx->version != NULL)
7054135446Strhodes			dns_db_closeversion(dctx->db, &dctx->version,
7055135446Strhodes					    ISC_FALSE);
7056135446Strhodes		if (dctx->db != NULL)
7057135446Strhodes			dns_db_detach(&dctx->db);
7058135446Strhodes		if (dctx->zone == NULL)
7059135446Strhodes			dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist);
7060135446Strhodes		else
7061135446Strhodes			dctx->zone = ISC_LIST_NEXT(dctx->zone, link);
7062135446Strhodes		if (dctx->zone != NULL) {
7063135446Strhodes			/* start zone dump */
7064135446Strhodes			dns_zone_name(dctx->zone->zone, buf, sizeof(buf));
7065135446Strhodes			fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf);
7066135446Strhodes			result = dns_zone_getdb(dctx->zone->zone, &dctx->db);
7067135446Strhodes			if (result != ISC_R_SUCCESS) {
7068135446Strhodes				fprintf(dctx->fp, "; %s\n",
7069135446Strhodes					dns_result_totext(result));
7070135446Strhodes				goto nextzone;
7071135446Strhodes			}
7072135446Strhodes			dns_db_currentversion(dctx->db, &dctx->version);
7073135446Strhodes			result = dns_master_dumptostreaminc(dctx->mctx,
7074135446Strhodes							    dctx->db,
7075135446Strhodes							    dctx->version,
7076135446Strhodes							    style, dctx->fp,
7077135446Strhodes							    dctx->task,
7078135446Strhodes							    dumpdone, dctx,
7079135446Strhodes							    &dctx->mdctx);
7080135446Strhodes			if (result == DNS_R_CONTINUE)
7081135446Strhodes				return;
7082153816Sdougb			if (result == ISC_R_NOTIMPLEMENTED) {
7083135446Strhodes				fprintf(dctx->fp, "; %s\n",
7084135446Strhodes					dns_result_totext(result));
7085153816Sdougb				result = ISC_R_SUCCESS;
7086225361Sdougb				POST(result);
7087153816Sdougb				goto nextzone;
7088153816Sdougb			}
7089135446Strhodes			if (result != ISC_R_SUCCESS)
7090135446Strhodes				goto cleanup;
7091135446Strhodes		}
7092135446Strhodes	}
7093135446Strhodes	if (dctx->view != NULL)
7094135446Strhodes		dctx->view = ISC_LIST_NEXT(dctx->view, link);
7095135446Strhodes	if (dctx->view != NULL)
7096135446Strhodes		goto nextview;
7097135446Strhodes done:
7098135446Strhodes	fprintf(dctx->fp, "; Dump complete\n");
7099135446Strhodes	result = isc_stdio_flush(dctx->fp);
7100135446Strhodes	if (result == ISC_R_SUCCESS)
7101135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7102135446Strhodes			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7103135446Strhodes			      "dumpdb complete");
7104135446Strhodes cleanup:
7105135446Strhodes	if (result != ISC_R_SUCCESS)
7106135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7107193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7108135446Strhodes			      "dumpdb failed: %s", dns_result_totext(result));
7109135446Strhodes	dumpcontext_destroy(dctx);
7110135446Strhodes}
7111135446Strhodes
7112135446Strhodesisc_result_t
7113135446Strhodesns_server_dumpdb(ns_server_t *server, char *args) {
7114135446Strhodes	struct dumpcontext *dctx = NULL;
7115135446Strhodes	dns_view_t *view;
7116135446Strhodes	isc_result_t result;
7117135446Strhodes	char *ptr;
7118135446Strhodes	const char *sep;
7119135446Strhodes
7120165071Sdougb	/* Skip the command name. */
7121165071Sdougb	ptr = next_token(&args, " \t");
7122165071Sdougb	if (ptr == NULL)
7123165071Sdougb		return (ISC_R_UNEXPECTEDEND);
7124165071Sdougb
7125135446Strhodes	dctx = isc_mem_get(server->mctx, sizeof(*dctx));
7126135446Strhodes	if (dctx == NULL)
7127135446Strhodes		return (ISC_R_NOMEMORY);
7128135446Strhodes
7129135446Strhodes	dctx->mctx = server->mctx;
7130135446Strhodes	dctx->dumpcache = ISC_TRUE;
7131135446Strhodes	dctx->dumpzones = ISC_FALSE;
7132135446Strhodes	dctx->fp = NULL;
7133135446Strhodes	ISC_LIST_INIT(dctx->viewlist);
7134135446Strhodes	dctx->view = NULL;
7135135446Strhodes	dctx->zone = NULL;
7136135446Strhodes	dctx->cache = NULL;
7137135446Strhodes	dctx->mdctx = NULL;
7138135446Strhodes	dctx->db = NULL;
7139135446Strhodes	dctx->cache = NULL;
7140135446Strhodes	dctx->task = NULL;
7141135446Strhodes	dctx->version = NULL;
7142135446Strhodes	isc_task_attach(server->task, &dctx->task);
7143135446Strhodes
7144135446Strhodes	CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp),
7145135446Strhodes		"could not open dump file", server->dumpfile);
7146135446Strhodes
7147135446Strhodes	sep = (args == NULL) ? "" : ": ";
7148135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7149135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7150135446Strhodes		      "dumpdb started%s%s", sep, (args != NULL) ? args : "");
7151135446Strhodes
7152135446Strhodes	ptr = next_token(&args, " \t");
7153135446Strhodes	if (ptr != NULL && strcmp(ptr, "-all") == 0) {
7154135446Strhodes		dctx->dumpzones = ISC_TRUE;
7155135446Strhodes		dctx->dumpcache = ISC_TRUE;
7156135446Strhodes		ptr = next_token(&args, " \t");
7157135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
7158135446Strhodes		dctx->dumpzones = ISC_FALSE;
7159135446Strhodes		dctx->dumpcache = ISC_TRUE;
7160135446Strhodes		ptr = next_token(&args, " \t");
7161135446Strhodes	} else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
7162135446Strhodes		dctx->dumpzones = ISC_TRUE;
7163135446Strhodes		dctx->dumpcache = ISC_FALSE;
7164135446Strhodes		ptr = next_token(&args, " \t");
7165186462Sdougb	}
7166135446Strhodes
7167153816Sdougb nextview:
7168135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
7169135446Strhodes	     view != NULL;
7170135446Strhodes	     view = ISC_LIST_NEXT(view, link))
7171135446Strhodes	{
7172135446Strhodes		if (ptr != NULL && strcmp(view->name, ptr) != 0)
7173135446Strhodes			continue;
7174135446Strhodes		CHECK(add_view_tolist(dctx, view));
7175135446Strhodes	}
7176153816Sdougb	if (ptr != NULL) {
7177153816Sdougb		ptr = next_token(&args, " \t");
7178153816Sdougb		if (ptr != NULL)
7179153816Sdougb			goto nextview;
7180153816Sdougb	}
7181135446Strhodes	dumpdone(dctx, ISC_R_SUCCESS);
7182135446Strhodes	return (ISC_R_SUCCESS);
7183135446Strhodes
7184135446Strhodes cleanup:
7185135446Strhodes	if (dctx != NULL)
7186135446Strhodes		dumpcontext_destroy(dctx);
7187135446Strhodes	return (result);
7188135446Strhodes}
7189135446Strhodes
7190135446Strhodesisc_result_t
7191224092Sdougbns_server_dumpsecroots(ns_server_t *server, char *args) {
7192224092Sdougb	dns_view_t *view;
7193224092Sdougb	dns_keytable_t *secroots = NULL;
7194224092Sdougb	isc_result_t result;
7195224092Sdougb	char *ptr;
7196224092Sdougb	FILE *fp = NULL;
7197224092Sdougb	isc_time_t now;
7198224092Sdougb	char tbuf[64];
7199224092Sdougb
7200224092Sdougb	/* Skip the command name. */
7201224092Sdougb	ptr = next_token(&args, " \t");
7202224092Sdougb	if (ptr == NULL)
7203224092Sdougb		return (ISC_R_UNEXPECTEDEND);
7204254897Serwin
7205224092Sdougb	ptr = next_token(&args, " \t");
7206224092Sdougb
7207224092Sdougb	CHECKMF(isc_stdio_open(server->secrootsfile, "w", &fp),
7208224092Sdougb		"could not open secroots dump file", server->secrootsfile);
7209224092Sdougb	TIME_NOW(&now);
7210224092Sdougb	isc_time_formattimestamp(&now, tbuf, sizeof(tbuf));
7211224092Sdougb	fprintf(fp, "%s\n", tbuf);
7212224092Sdougb
7213225361Sdougb	do {
7214225361Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
7215225361Sdougb		     view != NULL;
7216225361Sdougb		     view = ISC_LIST_NEXT(view, link))
7217225361Sdougb		{
7218225361Sdougb			if (ptr != NULL && strcmp(view->name, ptr) != 0)
7219225361Sdougb				continue;
7220225361Sdougb			if (secroots != NULL)
7221225361Sdougb				dns_keytable_detach(&secroots);
7222225361Sdougb			result = dns_view_getsecroots(view, &secroots);
7223225361Sdougb			if (result == ISC_R_NOTFOUND) {
7224225361Sdougb				result = ISC_R_SUCCESS;
7225225361Sdougb				continue;
7226225361Sdougb			}
7227225361Sdougb			fprintf(fp, "\n Start view %s\n\n", view->name);
7228225361Sdougb			result = dns_keytable_dump(secroots, fp);
7229225361Sdougb			if (result != ISC_R_SUCCESS)
7230225361Sdougb				fprintf(fp, " dumpsecroots failed: %s\n",
7231225361Sdougb					isc_result_totext(result));
7232224092Sdougb		}
7233224092Sdougb		if (ptr != NULL)
7234225361Sdougb			ptr = next_token(&args, " \t");
7235225361Sdougb	} while (ptr != NULL);
7236224092Sdougb
7237224092Sdougb cleanup:
7238224092Sdougb	if (secroots != NULL)
7239224092Sdougb		dns_keytable_detach(&secroots);
7240224092Sdougb	if (fp != NULL)
7241224092Sdougb		(void)isc_stdio_close(fp);
7242224092Sdougb	if (result == ISC_R_SUCCESS)
7243224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7244224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7245224092Sdougb			      "dumpsecroots complete");
7246224092Sdougb	else
7247224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7248224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7249224092Sdougb			      "dumpsecroots failed: %s",
7250224092Sdougb			      dns_result_totext(result));
7251224092Sdougb	return (result);
7252224092Sdougb}
7253224092Sdougb
7254224092Sdougbisc_result_t
7255135446Strhodesns_server_dumprecursing(ns_server_t *server) {
7256135446Strhodes	FILE *fp = NULL;
7257135446Strhodes	isc_result_t result;
7258135446Strhodes
7259135446Strhodes	CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
7260135446Strhodes		"could not open dump file", server->recfile);
7261135446Strhodes	fprintf(fp,";\n; Recursing Queries\n;\n");
7262135446Strhodes	ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
7263135446Strhodes	fprintf(fp, "; Dump complete\n");
7264135446Strhodes
7265135446Strhodes cleanup:
7266135446Strhodes	if (fp != NULL)
7267135446Strhodes		result = isc_stdio_close(fp);
7268193149Sdougb	if (result == ISC_R_SUCCESS)
7269193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7270193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7271193149Sdougb			      "dumprecursing complete");
7272193149Sdougb	else
7273193149Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7274193149Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7275193149Sdougb			      "dumprecursing failed: %s",
7276193149Sdougb			      dns_result_totext(result));
7277135446Strhodes	return (result);
7278135446Strhodes}
7279135446Strhodes
7280135446Strhodesisc_result_t
7281135446Strhodesns_server_setdebuglevel(ns_server_t *server, char *args) {
7282135446Strhodes	char *ptr;
7283135446Strhodes	char *levelstr;
7284135446Strhodes	char *endp;
7285135446Strhodes	long newlevel;
7286135446Strhodes
7287135446Strhodes	UNUSED(server);
7288135446Strhodes
7289135446Strhodes	/* Skip the command name. */
7290135446Strhodes	ptr = next_token(&args, " \t");
7291135446Strhodes	if (ptr == NULL)
7292135446Strhodes		return (ISC_R_UNEXPECTEDEND);
7293135446Strhodes
7294135446Strhodes	/* Look for the new level name. */
7295135446Strhodes	levelstr = next_token(&args, " \t");
7296135446Strhodes	if (levelstr == NULL) {
7297135446Strhodes		if (ns_g_debuglevel < 99)
7298135446Strhodes			ns_g_debuglevel++;
7299135446Strhodes	} else {
7300135446Strhodes		newlevel = strtol(levelstr, &endp, 10);
7301135446Strhodes		if (*endp != '\0' || newlevel < 0 || newlevel > 99)
7302135446Strhodes			return (ISC_R_RANGE);
7303135446Strhodes		ns_g_debuglevel = (unsigned int)newlevel;
7304135446Strhodes	}
7305135446Strhodes	isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
7306193149Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7307193149Sdougb		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7308193149Sdougb		      "debug level is now %d", ns_g_debuglevel);
7309135446Strhodes	return (ISC_R_SUCCESS);
7310135446Strhodes}
7311135446Strhodes
7312135446Strhodesisc_result_t
7313170222Sdougbns_server_validation(ns_server_t *server, char *args) {
7314170222Sdougb	char *ptr, *viewname;
7315170222Sdougb	dns_view_t *view;
7316170222Sdougb	isc_boolean_t changed = ISC_FALSE;
7317170222Sdougb	isc_result_t result;
7318170222Sdougb	isc_boolean_t enable;
7319170222Sdougb
7320170222Sdougb	/* Skip the command name. */
7321170222Sdougb	ptr = next_token(&args, " \t");
7322170222Sdougb	if (ptr == NULL)
7323170222Sdougb		return (ISC_R_UNEXPECTEDEND);
7324170222Sdougb
7325170222Sdougb	/* Find out what we are to do. */
7326170222Sdougb	ptr = next_token(&args, " \t");
7327170222Sdougb	if (ptr == NULL)
7328170222Sdougb		return (ISC_R_UNEXPECTEDEND);
7329170222Sdougb
7330170222Sdougb	if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
7331170222Sdougb	    !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
7332170222Sdougb		enable = ISC_TRUE;
7333170222Sdougb	else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
7334170222Sdougb		 !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
7335170222Sdougb		enable = ISC_FALSE;
7336170222Sdougb	else
7337170222Sdougb		return (DNS_R_SYNTAX);
7338170222Sdougb
7339170222Sdougb	/* Look for the view name. */
7340170222Sdougb	viewname = next_token(&args, " \t");
7341170222Sdougb
7342170222Sdougb	result = isc_task_beginexclusive(server->task);
7343170222Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7344170222Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7345170222Sdougb	     view != NULL;
7346170222Sdougb	     view = ISC_LIST_NEXT(view, link))
7347170222Sdougb	{
7348170222Sdougb		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
7349170222Sdougb			continue;
7350170222Sdougb		result = dns_view_flushcache(view);
7351170222Sdougb		if (result != ISC_R_SUCCESS)
7352170222Sdougb			goto out;
7353170222Sdougb		view->enablevalidation = enable;
7354170222Sdougb		changed = ISC_TRUE;
7355170222Sdougb	}
7356170222Sdougb	if (changed)
7357170222Sdougb		result = ISC_R_SUCCESS;
7358170222Sdougb	else
7359170222Sdougb		result = ISC_R_FAILURE;
7360170222Sdougb out:
7361186462Sdougb	isc_task_endexclusive(server->task);
7362170222Sdougb	return (result);
7363170222Sdougb}
7364170222Sdougb
7365170222Sdougbisc_result_t
7366135446Strhodesns_server_flushcache(ns_server_t *server, char *args) {
7367135446Strhodes	char *ptr, *viewname;
7368135446Strhodes	dns_view_t *view;
7369174187Sdougb	isc_boolean_t flushed;
7370174187Sdougb	isc_boolean_t found;
7371135446Strhodes	isc_result_t result;
7372224092Sdougb	ns_cache_t *nsc;
7373135446Strhodes
7374135446Strhodes	/* Skip the command name. */
7375135446Strhodes	ptr = next_token(&args, " \t");
7376135446Strhodes	if (ptr == NULL)
7377135446Strhodes		return (ISC_R_UNEXPECTEDEND);
7378135446Strhodes
7379135446Strhodes	/* Look for the view name. */
7380135446Strhodes	viewname = next_token(&args, " \t");
7381135446Strhodes
7382135446Strhodes	result = isc_task_beginexclusive(server->task);
7383135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7384174187Sdougb	flushed = ISC_TRUE;
7385174187Sdougb	found = ISC_FALSE;
7386224092Sdougb
7387224092Sdougb	/*
7388224092Sdougb	 * Flushing a cache is tricky when caches are shared by multiple views.
7389224092Sdougb	 * We first identify which caches should be flushed in the local cache
7390224092Sdougb	 * list, flush these caches, and then update other views that refer to
7391224092Sdougb	 * the flushed cache DB.
7392224092Sdougb	 */
7393224092Sdougb	if (viewname != NULL) {
7394224092Sdougb		/*
7395224092Sdougb		 * Mark caches that need to be flushed.  This is an O(#view^2)
7396224092Sdougb		 * operation in the very worst case, but should be normally
7397224092Sdougb		 * much more lightweight because only a few (most typically just
7398224092Sdougb		 * one) views will match.
7399224092Sdougb		 */
7400224092Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
7401224092Sdougb		     view != NULL;
7402224092Sdougb		     view = ISC_LIST_NEXT(view, link))
7403224092Sdougb		{
7404224092Sdougb			if (strcasecmp(viewname, view->name) != 0)
7405224092Sdougb				continue;
7406224092Sdougb			found = ISC_TRUE;
7407224092Sdougb			for (nsc = ISC_LIST_HEAD(server->cachelist);
7408224092Sdougb			     nsc != NULL;
7409224092Sdougb			     nsc = ISC_LIST_NEXT(nsc, link)) {
7410224092Sdougb				if (nsc->cache == view->cache)
7411224092Sdougb					break;
7412224092Sdougb			}
7413224092Sdougb			INSIST(nsc != NULL);
7414224092Sdougb			nsc->needflush = ISC_TRUE;
7415224092Sdougb		}
7416224092Sdougb	} else
7417224092Sdougb		found = ISC_TRUE;
7418224092Sdougb
7419224092Sdougb	/* Perform flush */
7420224092Sdougb	for (nsc = ISC_LIST_HEAD(server->cachelist);
7421224092Sdougb	     nsc != NULL;
7422224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
7423224092Sdougb		if (viewname != NULL && !nsc->needflush)
7424135446Strhodes			continue;
7425224092Sdougb		nsc->needflush = ISC_TRUE;
7426224092Sdougb		result = dns_view_flushcache2(nsc->primaryview, ISC_FALSE);
7427193149Sdougb		if (result != ISC_R_SUCCESS) {
7428174187Sdougb			flushed = ISC_FALSE;
7429193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7430193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7431193149Sdougb				      "flushing cache in view '%s' failed: %s",
7432224092Sdougb				      nsc->primaryview->name,
7433224092Sdougb				      isc_result_totext(result));
7434193149Sdougb		}
7435135446Strhodes	}
7436224092Sdougb
7437224092Sdougb	/*
7438224092Sdougb	 * Fix up views that share a flushed cache: let the views update the
7439224092Sdougb	 * cache DB they're referring to.  This could also be an expensive
7440224092Sdougb	 * operation, but should typically be marginal: the inner loop is only
7441224092Sdougb	 * necessary for views that share a cache, and if there are many such
7442224092Sdougb	 * views the number of shared cache should normally be small.
7443224092Sdougb	 * A worst case is that we have n views and n/2 caches, each shared by
7444224092Sdougb	 * two views.  Then this will be a O(n^2/4) operation.
7445224092Sdougb	 */
7446224092Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7447224092Sdougb	     view != NULL;
7448224092Sdougb	     view = ISC_LIST_NEXT(view, link))
7449224092Sdougb	{
7450224092Sdougb		if (!dns_view_iscacheshared(view))
7451224092Sdougb			continue;
7452224092Sdougb		for (nsc = ISC_LIST_HEAD(server->cachelist);
7453224092Sdougb		     nsc != NULL;
7454224092Sdougb		     nsc = ISC_LIST_NEXT(nsc, link)) {
7455224092Sdougb			if (!nsc->needflush || nsc->cache != view->cache)
7456224092Sdougb				continue;
7457224092Sdougb			result = dns_view_flushcache2(view, ISC_TRUE);
7458224092Sdougb			if (result != ISC_R_SUCCESS) {
7459224092Sdougb				flushed = ISC_FALSE;
7460224092Sdougb				isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7461224092Sdougb					      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7462224092Sdougb					      "fixing cache in view '%s' "
7463224092Sdougb					      "failed: %s", view->name,
7464224092Sdougb					      isc_result_totext(result));
7465224092Sdougb			}
7466224092Sdougb		}
7467224092Sdougb	}
7468224092Sdougb
7469224092Sdougb	/* Cleanup the cache list. */
7470224092Sdougb	for (nsc = ISC_LIST_HEAD(server->cachelist);
7471224092Sdougb	     nsc != NULL;
7472224092Sdougb	     nsc = ISC_LIST_NEXT(nsc, link)) {
7473224092Sdougb		nsc->needflush = ISC_FALSE;
7474224092Sdougb	}
7475224092Sdougb
7476174187Sdougb	if (flushed && found) {
7477193149Sdougb		if (viewname != NULL)
7478193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7479193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7480193149Sdougb				      "flushing cache in view '%s' succeeded",
7481193149Sdougb				      viewname);
7482193149Sdougb		else
7483193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7484193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7485193149Sdougb				      "flushing caches in all views succeeded");
7486135446Strhodes		result = ISC_R_SUCCESS;
7487174187Sdougb	} else {
7488193149Sdougb		if (!found) {
7489193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7490193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7491193149Sdougb				      "flushing cache in view '%s' failed: "
7492193149Sdougb				      "view not found", viewname);
7493174187Sdougb			result = ISC_R_NOTFOUND;
7494193149Sdougb		} else
7495174187Sdougb			result = ISC_R_FAILURE;
7496174187Sdougb	}
7497186462Sdougb	isc_task_endexclusive(server->task);
7498135446Strhodes	return (result);
7499135446Strhodes}
7500135446Strhodes
7501135446Strhodesisc_result_t
7502254897Serwinns_server_flushnode(ns_server_t *server, char *args, isc_boolean_t tree) {
7503135446Strhodes	char *ptr, *target, *viewname;
7504135446Strhodes	dns_view_t *view;
7505174187Sdougb	isc_boolean_t flushed;
7506174187Sdougb	isc_boolean_t found;
7507135446Strhodes	isc_result_t result;
7508135446Strhodes	isc_buffer_t b;
7509135446Strhodes	dns_fixedname_t fixed;
7510135446Strhodes	dns_name_t *name;
7511135446Strhodes
7512135446Strhodes	/* Skip the command name. */
7513135446Strhodes	ptr = next_token(&args, " \t");
7514135446Strhodes	if (ptr == NULL)
7515135446Strhodes		return (ISC_R_UNEXPECTEDEND);
7516135446Strhodes
7517135446Strhodes	/* Find the domain name to flush. */
7518135446Strhodes	target = next_token(&args, " \t");
7519135446Strhodes	if (target == NULL)
7520135446Strhodes		return (ISC_R_UNEXPECTEDEND);
7521135446Strhodes
7522254402Serwin	isc_buffer_constinit(&b, target, strlen(target));
7523135446Strhodes	isc_buffer_add(&b, strlen(target));
7524135446Strhodes	dns_fixedname_init(&fixed);
7525135446Strhodes	name = dns_fixedname_name(&fixed);
7526224092Sdougb	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
7527135446Strhodes	if (result != ISC_R_SUCCESS)
7528135446Strhodes		return (result);
7529135446Strhodes
7530135446Strhodes	/* Look for the view name. */
7531135446Strhodes	viewname = next_token(&args, " \t");
7532135446Strhodes
7533135446Strhodes	result = isc_task_beginexclusive(server->task);
7534135446Strhodes	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7535135446Strhodes	flushed = ISC_TRUE;
7536174187Sdougb	found = ISC_FALSE;
7537135446Strhodes	for (view = ISC_LIST_HEAD(server->viewlist);
7538135446Strhodes	     view != NULL;
7539135446Strhodes	     view = ISC_LIST_NEXT(view, link))
7540135446Strhodes	{
7541135446Strhodes		if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
7542135446Strhodes			continue;
7543174187Sdougb		found = ISC_TRUE;
7544224092Sdougb		/*
7545224092Sdougb		 * It's a little inefficient to try flushing name for all views
7546224092Sdougb		 * if some of the views share a single cache.  But since the
7547224092Sdougb		 * operation is lightweight we prefer simplicity here.
7548224092Sdougb		 */
7549254897Serwin		result = dns_view_flushnode(view, name, tree);
7550193149Sdougb		if (result != ISC_R_SUCCESS) {
7551135446Strhodes			flushed = ISC_FALSE;
7552193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7553193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7554254897Serwin				      "flushing %s '%s' in cache view '%s' "
7555254897Serwin				      "failed: %s",
7556254897Serwin				      tree ? "tree" : "name",
7557254897Serwin				      target, view->name,
7558193149Sdougb				      isc_result_totext(result));
7559193149Sdougb		}
7560135446Strhodes	}
7561193149Sdougb	if (flushed && found) {
7562193149Sdougb		if (viewname != NULL)
7563193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7564193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7565254897Serwin				      "flushing %s '%s' in cache view '%s' "
7566254897Serwin				      "succeeded",
7567254897Serwin				      tree ? "tree" : "name",
7568254897Serwin				      target, viewname);
7569193149Sdougb		else
7570193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7571193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7572254897Serwin				      "flushing %s '%s' in all cache views "
7573254897Serwin				      "succeeded",
7574254897Serwin				      tree ? "tree" : "name",
7575254897Serwin				      target);
7576135446Strhodes		result = ISC_R_SUCCESS;
7577193149Sdougb	} else {
7578193149Sdougb		if (!found)
7579193149Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7580193149Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
7581254897Serwin				      "flushing %s '%s' in cache view '%s' "
7582254897Serwin				      "failed: view not found",
7583254897Serwin				      tree ? "tree" : "name",
7584254897Serwin				      target, viewname);
7585135446Strhodes		result = ISC_R_FAILURE;
7586193149Sdougb	}
7587186462Sdougb	isc_task_endexclusive(server->task);
7588135446Strhodes	return (result);
7589135446Strhodes}
7590135446Strhodes
7591135446Strhodesisc_result_t
7592135446Strhodesns_server_status(ns_server_t *server, isc_buffer_t *text) {
7593135446Strhodes	int zonecount, xferrunning, xferdeferred, soaqueries;
7594135446Strhodes	unsigned int n;
7595193149Sdougb	const char *ob = "", *cb = "", *alt = "";
7596135446Strhodes
7597193149Sdougb	if (ns_g_server->version_set) {
7598193149Sdougb		ob = " (";
7599193149Sdougb		cb = ")";
7600193149Sdougb		if (ns_g_server->version == NULL)
7601193149Sdougb			alt = "version.bind/txt/ch disabled";
7602193149Sdougb		else
7603193149Sdougb			alt = ns_g_server->version;
7604193149Sdougb	}
7605135446Strhodes	zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY);
7606135446Strhodes	xferrunning = dns_zonemgr_getcount(server->zonemgr,
7607135446Strhodes					   DNS_ZONESTATE_XFERRUNNING);
7608135446Strhodes	xferdeferred = dns_zonemgr_getcount(server->zonemgr,
7609135446Strhodes					    DNS_ZONESTATE_XFERDEFERRED);
7610135446Strhodes	soaqueries = dns_zonemgr_getcount(server->zonemgr,
7611135446Strhodes					  DNS_ZONESTATE_SOAQUERY);
7612193149Sdougb
7613135446Strhodes	n = snprintf((char *)isc_buffer_used(text),
7614135446Strhodes		     isc_buffer_availablelength(text),
7615262706Serwin		     "version: %s%s%s%s <id:%s>\n"
7616193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
7617193149Sdougb		     "CPUs found: %u\n"
7618193149Sdougb		     "worker threads: %u\n"
7619254897Serwin		     "UDP listeners per interface: %u\n"
7620193149Sdougb#endif
7621135446Strhodes		     "number of zones: %u\n"
7622135446Strhodes		     "debug level: %d\n"
7623135446Strhodes		     "xfers running: %u\n"
7624135446Strhodes		     "xfers deferred: %u\n"
7625135446Strhodes		     "soa queries in progress: %u\n"
7626135446Strhodes		     "query logging is %s\n"
7627170222Sdougb		     "recursive clients: %d/%d/%d\n"
7628135446Strhodes		     "tcp clients: %d/%d\n"
7629135446Strhodes		     "server is up and running",
7630262706Serwin		     ns_g_version, ob, alt, cb, ns_g_srcid,
7631193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
7632254897Serwin		     ns_g_cpus_detected, ns_g_cpus, ns_g_udpdisp,
7633193149Sdougb#endif
7634135446Strhodes		     zonecount, ns_g_debuglevel, xferrunning, xferdeferred,
7635135446Strhodes		     soaqueries, server->log_queries ? "ON" : "OFF",
7636170222Sdougb		     server->recursionquota.used, server->recursionquota.soft,
7637170222Sdougb		     server->recursionquota.max,
7638135446Strhodes		     server->tcpquota.used, server->tcpquota.max);
7639135446Strhodes	if (n >= isc_buffer_availablelength(text))
7640135446Strhodes		return (ISC_R_NOSPACE);
7641135446Strhodes	isc_buffer_add(text, n);
7642135446Strhodes	return (ISC_R_SUCCESS);
7643135446Strhodes}
7644135446Strhodes
7645193149Sdougbstatic isc_result_t
7646193149Sdougbdelete_keynames(dns_tsig_keyring_t *ring, char *target,
7647193149Sdougb		unsigned int *foundkeys)
7648193149Sdougb{
7649193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
7650193149Sdougb	isc_result_t result;
7651193149Sdougb	dns_rbtnodechain_t chain;
7652193149Sdougb	dns_name_t foundname;
7653193149Sdougb	dns_fixedname_t fixedorigin;
7654193149Sdougb	dns_name_t *origin;
7655193149Sdougb	dns_rbtnode_t *node;
7656193149Sdougb	dns_tsigkey_t *tkey;
7657193149Sdougb
7658193149Sdougb	dns_name_init(&foundname, NULL);
7659193149Sdougb	dns_fixedname_init(&fixedorigin);
7660193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
7661193149Sdougb
7662193149Sdougb again:
7663193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
7664193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
7665193149Sdougb					origin);
7666193149Sdougb	if (result == ISC_R_NOTFOUND) {
7667193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7668193149Sdougb		return (ISC_R_SUCCESS);
7669193149Sdougb	}
7670193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7671193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7672193149Sdougb		return (result);
7673193149Sdougb	}
7674193149Sdougb
7675193149Sdougb	for (;;) {
7676193149Sdougb		node = NULL;
7677193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
7678193149Sdougb		tkey = node->data;
7679193149Sdougb
7680193149Sdougb		if (tkey != NULL) {
7681193149Sdougb			if (!tkey->generated)
7682193149Sdougb				goto nextkey;
7683193149Sdougb
7684193149Sdougb			dns_name_format(&tkey->name, namestr, sizeof(namestr));
7685193149Sdougb			if (strcmp(namestr, target) == 0) {
7686193149Sdougb				(*foundkeys)++;
7687193149Sdougb				dns_rbtnodechain_invalidate(&chain);
7688193149Sdougb				(void)dns_rbt_deletename(ring->keys,
7689193149Sdougb							 &tkey->name,
7690193149Sdougb							 ISC_FALSE);
7691193149Sdougb				goto again;
7692193149Sdougb			}
7693193149Sdougb		}
7694193149Sdougb
7695193149Sdougb	nextkey:
7696193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname, origin);
7697193149Sdougb		if (result == ISC_R_NOMORE)
7698193149Sdougb			break;
7699193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7700193149Sdougb			dns_rbtnodechain_invalidate(&chain);
7701193149Sdougb			return (result);
7702193149Sdougb		}
7703193149Sdougb	}
7704193149Sdougb
7705193149Sdougb	return (ISC_R_SUCCESS);
7706193149Sdougb}
7707193149Sdougb
7708193149Sdougbisc_result_t
7709193149Sdougbns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) {
7710193149Sdougb	isc_result_t result;
7711193149Sdougb	unsigned int n;
7712193149Sdougb	dns_view_t *view;
7713193149Sdougb	unsigned int foundkeys = 0;
7714193149Sdougb	char *target;
7715193149Sdougb	char *viewname;
7716193149Sdougb
7717193149Sdougb	(void)next_token(&command, " \t");  /* skip command name */
7718193149Sdougb	target = next_token(&command, " \t");
7719193149Sdougb	if (target == NULL)
7720193149Sdougb		return (ISC_R_UNEXPECTEDEND);
7721193149Sdougb	viewname = next_token(&command, " \t");
7722193149Sdougb
7723193149Sdougb	result = isc_task_beginexclusive(server->task);
7724193149Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7725193149Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7726193149Sdougb	     view != NULL;
7727193149Sdougb	     view = ISC_LIST_NEXT(view, link)) {
7728193149Sdougb		if (viewname == NULL || strcmp(view->name, viewname) == 0) {
7729193149Sdougb			RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write);
7730193149Sdougb			result = delete_keynames(view->dynamickeys, target,
7731193149Sdougb						 &foundkeys);
7732193149Sdougb			RWUNLOCK(&view->dynamickeys->lock,
7733193149Sdougb				 isc_rwlocktype_write);
7734193149Sdougb			if (result != ISC_R_SUCCESS) {
7735193149Sdougb				isc_task_endexclusive(server->task);
7736193149Sdougb				return (result);
7737193149Sdougb			}
7738193149Sdougb		}
7739193149Sdougb	}
7740193149Sdougb	isc_task_endexclusive(server->task);
7741193149Sdougb
7742193149Sdougb	n = snprintf((char *)isc_buffer_used(text),
7743193149Sdougb		     isc_buffer_availablelength(text),
7744193149Sdougb		     "%d tsig keys deleted.\n", foundkeys);
7745218384Sdougb	if (n >= isc_buffer_availablelength(text))
7746193149Sdougb		return (ISC_R_NOSPACE);
7747193149Sdougb	isc_buffer_add(text, n);
7748193149Sdougb
7749193149Sdougb	return (ISC_R_SUCCESS);
7750193149Sdougb}
7751193149Sdougb
7752193149Sdougbstatic isc_result_t
7753193149Sdougblist_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text,
7754193149Sdougb	     unsigned int *foundkeys)
7755193149Sdougb{
7756193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
7757193149Sdougb	char creatorstr[DNS_NAME_FORMATSIZE];
7758193149Sdougb	isc_result_t result;
7759193149Sdougb	dns_rbtnodechain_t chain;
7760193149Sdougb	dns_name_t foundname;
7761193149Sdougb	dns_fixedname_t fixedorigin;
7762193149Sdougb	dns_name_t *origin;
7763193149Sdougb	dns_rbtnode_t *node;
7764193149Sdougb	dns_tsigkey_t *tkey;
7765193149Sdougb	unsigned int n;
7766193149Sdougb	const char *viewname;
7767193149Sdougb
7768193149Sdougb	if (view != NULL)
7769193149Sdougb		viewname = view->name;
7770193149Sdougb	else
7771193149Sdougb		viewname = "(global)";
7772193149Sdougb
7773193149Sdougb	dns_name_init(&foundname, NULL);
7774193149Sdougb	dns_fixedname_init(&fixedorigin);
7775193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
7776193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
7777193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
7778193149Sdougb					origin);
7779193149Sdougb	if (result == ISC_R_NOTFOUND) {
7780193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7781193149Sdougb		return (ISC_R_SUCCESS);
7782193149Sdougb	}
7783193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7784193149Sdougb		dns_rbtnodechain_invalidate(&chain);
7785193149Sdougb		return (result);
7786193149Sdougb	}
7787193149Sdougb
7788193149Sdougb	for (;;) {
7789193149Sdougb		node = NULL;
7790193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
7791193149Sdougb		tkey = node->data;
7792193149Sdougb
7793193149Sdougb		if (tkey != NULL) {
7794193149Sdougb			(*foundkeys)++;
7795193149Sdougb			dns_name_format(&tkey->name, namestr, sizeof(namestr));
7796193149Sdougb			if (tkey->generated) {
7797193149Sdougb				dns_name_format(tkey->creator, creatorstr,
7798193149Sdougb						sizeof(creatorstr));
7799193149Sdougb				n = snprintf((char *)isc_buffer_used(text),
7800193149Sdougb					     isc_buffer_availablelength(text),
7801193149Sdougb					     "view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n",
7802193149Sdougb					     viewname, namestr, creatorstr);
7803193149Sdougb			} else {
7804193149Sdougb				n = snprintf((char *)isc_buffer_used(text),
7805193149Sdougb					     isc_buffer_availablelength(text),
7806193149Sdougb					     "view \"%s\"; type \"static\"; key \"%s\";\n",
7807193149Sdougb					     viewname, namestr);
7808193149Sdougb			}
7809193149Sdougb			if (n >= isc_buffer_availablelength(text)) {
7810193149Sdougb				dns_rbtnodechain_invalidate(&chain);
7811193149Sdougb				return (ISC_R_NOSPACE);
7812193149Sdougb			}
7813193149Sdougb			isc_buffer_add(text, n);
7814193149Sdougb		}
7815193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname, origin);
7816193149Sdougb		if (result == ISC_R_NOMORE)
7817193149Sdougb			break;
7818193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
7819193149Sdougb			dns_rbtnodechain_invalidate(&chain);
7820193149Sdougb			return (result);
7821193149Sdougb		}
7822193149Sdougb	}
7823193149Sdougb
7824193149Sdougb	return (ISC_R_SUCCESS);
7825193149Sdougb}
7826193149Sdougb
7827193149Sdougbisc_result_t
7828193149Sdougbns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) {
7829193149Sdougb	isc_result_t result;
7830193149Sdougb	unsigned int n;
7831193149Sdougb	dns_view_t *view;
7832193149Sdougb	unsigned int foundkeys = 0;
7833193149Sdougb
7834193149Sdougb	result = isc_task_beginexclusive(server->task);
7835193149Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7836193149Sdougb	for (view = ISC_LIST_HEAD(server->viewlist);
7837193149Sdougb	     view != NULL;
7838193149Sdougb	     view = ISC_LIST_NEXT(view, link)) {
7839193149Sdougb		RWLOCK(&view->statickeys->lock, isc_rwlocktype_read);
7840193149Sdougb		result = list_keynames(view, view->statickeys, text,
7841193149Sdougb				       &foundkeys);
7842193149Sdougb		RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read);
7843193149Sdougb		if (result != ISC_R_SUCCESS) {
7844193149Sdougb			isc_task_endexclusive(server->task);
7845193149Sdougb			return (result);
7846193149Sdougb		}
7847193149Sdougb		RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
7848193149Sdougb		result = list_keynames(view, view->dynamickeys, text,
7849193149Sdougb				       &foundkeys);
7850193149Sdougb		RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
7851193149Sdougb		if (result != ISC_R_SUCCESS) {
7852193149Sdougb			isc_task_endexclusive(server->task);
7853193149Sdougb			return (result);
7854193149Sdougb		}
7855193149Sdougb	}
7856193149Sdougb	isc_task_endexclusive(server->task);
7857193149Sdougb
7858193149Sdougb	if (foundkeys == 0) {
7859193149Sdougb		n = snprintf((char *)isc_buffer_used(text),
7860193149Sdougb			     isc_buffer_availablelength(text),
7861193149Sdougb			     "no tsig keys found.\n");
7862218384Sdougb		if (n >= isc_buffer_availablelength(text))
7863193149Sdougb			return (ISC_R_NOSPACE);
7864193149Sdougb		isc_buffer_add(text, n);
7865193149Sdougb	}
7866193149Sdougb
7867193149Sdougb	return (ISC_R_SUCCESS);
7868193149Sdougb}
7869193149Sdougb
7870135446Strhodes/*
7871224092Sdougb * Act on a "sign" or "loadkeys" command from the command channel.
7872224092Sdougb */
7873224092Sdougbisc_result_t
7874262706Serwinns_server_rekey(ns_server_t *server, char *args, isc_buffer_t *text) {
7875224092Sdougb	isc_result_t result;
7876224092Sdougb	dns_zone_t *zone = NULL;
7877224092Sdougb	dns_zonetype_t type;
7878224092Sdougb	isc_uint16_t keyopts;
7879224092Sdougb	isc_boolean_t fullsign = ISC_FALSE;
7880224092Sdougb
7881224092Sdougb	if (strncasecmp(args, NS_COMMAND_SIGN, strlen(NS_COMMAND_SIGN)) == 0)
7882224092Sdougb	    fullsign = ISC_TRUE;
7883224092Sdougb
7884262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
7885262706Serwin				text, ISC_TRUE);
7886224092Sdougb	if (result != ISC_R_SUCCESS)
7887224092Sdougb		return (result);
7888224092Sdougb	if (zone == NULL)
7889224092Sdougb		return (ISC_R_UNEXPECTEDEND);   /* XXX: or do all zones? */
7890224092Sdougb
7891224092Sdougb	type = dns_zone_gettype(zone);
7892224092Sdougb	if (type != dns_zone_master) {
7893224092Sdougb		dns_zone_detach(&zone);
7894224092Sdougb		return (DNS_R_NOTMASTER);
7895224092Sdougb	}
7896224092Sdougb
7897224092Sdougb	keyopts = dns_zone_getkeyopts(zone);
7898224092Sdougb
7899224092Sdougb	/* "rndc loadkeys" requires "auto-dnssec maintain". */
7900224092Sdougb	if ((keyopts & DNS_ZONEKEY_ALLOW) == 0)
7901224092Sdougb		result = ISC_R_NOPERM;
7902224092Sdougb	else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign)
7903224092Sdougb		result = ISC_R_NOPERM;
7904224092Sdougb	else
7905224092Sdougb		dns_zone_rekey(zone, fullsign);
7906224092Sdougb
7907224092Sdougb	dns_zone_detach(&zone);
7908224092Sdougb	return (result);
7909224092Sdougb}
7910224092Sdougb
7911224092Sdougb/*
7912254897Serwin * Act on a "sync" command from the command channel.
7913254897Serwin*/
7914254897Serwinstatic isc_result_t
7915254897Serwinsynczone(dns_zone_t *zone, void *uap) {
7916254897Serwin	isc_boolean_t cleanup = *(isc_boolean_t *)uap;
7917254897Serwin	isc_result_t result;
7918254897Serwin	dns_zone_t *raw = NULL;
7919254897Serwin	char *journal;
7920254897Serwin
7921254897Serwin	dns_zone_getraw(zone, &raw);
7922254897Serwin	if (raw != NULL) {
7923254897Serwin		synczone(raw, uap);
7924254897Serwin		dns_zone_detach(&raw);
7925254897Serwin	}
7926254897Serwin
7927254897Serwin	result = dns_zone_flush(zone);
7928254897Serwin	if (result != ISC_R_SUCCESS)
7929254897Serwin		cleanup = ISC_FALSE;
7930254897Serwin	if (cleanup) {
7931254897Serwin		journal = dns_zone_getjournal(zone);
7932254897Serwin		if (journal != NULL)
7933254897Serwin			(void)isc_file_remove(journal);
7934254897Serwin	}
7935254897Serwin
7936254897Serwin	return (result);
7937254897Serwin}
7938254897Serwin
7939254897Serwinisc_result_t
7940254897Serwinns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text) {
7941254897Serwin	isc_result_t result, tresult;
7942254897Serwin	dns_view_t *view;
7943254897Serwin	dns_zone_t *zone = NULL;
7944254897Serwin	char classstr[DNS_RDATACLASS_FORMATSIZE];
7945254897Serwin	char zonename[DNS_NAME_FORMATSIZE];
7946254897Serwin	const char *vname, *sep, *msg = NULL, *arg;
7947254897Serwin	isc_boolean_t cleanup = ISC_FALSE;
7948254897Serwin
7949254897Serwin	(void) next_token(&args, " \t");
7950254897Serwin
7951254897Serwin	arg = next_token(&args, " \t");
7952254897Serwin	if (arg != NULL &&
7953254897Serwin	    (strcmp(arg, "-clean") == 0 || strcmp(arg, "-clear") == 0)) {
7954254897Serwin		cleanup = ISC_TRUE;
7955254897Serwin		arg = next_token(&args, " \t");
7956254897Serwin	}
7957254897Serwin
7958262706Serwin	result = zone_from_args(server, args, arg, &zone, NULL,
7959262706Serwin				text, ISC_FALSE);
7960254897Serwin	if (result != ISC_R_SUCCESS)
7961254897Serwin		return (result);
7962254897Serwin
7963254897Serwin	if (zone == NULL) {
7964254897Serwin		result = isc_task_beginexclusive(server->task);
7965254897Serwin		RUNTIME_CHECK(result == ISC_R_SUCCESS);
7966254897Serwin		tresult = ISC_R_SUCCESS;
7967254897Serwin		for (view = ISC_LIST_HEAD(server->viewlist);
7968254897Serwin		     view != NULL;
7969254897Serwin		     view = ISC_LIST_NEXT(view, link)) {
7970254897Serwin			result = dns_zt_apply(view->zonetable, ISC_FALSE,
7971254897Serwin					      synczone, &cleanup);
7972254897Serwin			if (result != ISC_R_SUCCESS &&
7973254897Serwin			    tresult == ISC_R_SUCCESS)
7974254897Serwin				tresult = result;
7975254897Serwin		}
7976254897Serwin		isc_task_endexclusive(server->task);
7977254897Serwin		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
7978254897Serwin			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
7979254897Serwin			      "dumping all zones%s: %s",
7980254897Serwin			      cleanup ? ", removing journal files" : "",
7981254897Serwin			      isc_result_totext(result));
7982254897Serwin		return (tresult);
7983254897Serwin	}
7984254897Serwin
7985254897Serwin	result = isc_task_beginexclusive(server->task);
7986254897Serwin	RUNTIME_CHECK(result == ISC_R_SUCCESS);
7987254897Serwin	result = synczone(zone, &cleanup);
7988254897Serwin	isc_task_endexclusive(server->task);
7989254897Serwin
7990254897Serwin	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
7991254897Serwin		isc_buffer_putmem(text, (const unsigned char *)msg,
7992254897Serwin				  strlen(msg) + 1);
7993254897Serwin
7994254897Serwin	view = dns_zone_getview(zone);
7995254897Serwin	if (strcmp(view->name, "_default") == 0 ||
7996254897Serwin	    strcmp(view->name, "_bind") == 0)
7997254897Serwin	{
7998254897Serwin		vname = "";
7999254897Serwin		sep = "";
8000254897Serwin	} else {
8001254897Serwin		vname = view->name;
8002254897Serwin		sep = " ";
8003254897Serwin	}
8004254897Serwin	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
8005254897Serwin			      sizeof(classstr));
8006254897Serwin	dns_name_format(dns_zone_getorigin(zone),
8007254897Serwin			zonename, sizeof(zonename));
8008254897Serwin	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8009254897Serwin		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8010254897Serwin		      "sync: dumping zone '%s/%s'%s%s%s: %s",
8011254897Serwin		      zonename, classstr, sep, vname,
8012254897Serwin		      cleanup ? ", removing journal file" : "",
8013254897Serwin		      isc_result_totext(result));
8014254897Serwin	dns_zone_detach(&zone);
8015254897Serwin	return (result);
8016254897Serwin}
8017254897Serwin
8018254897Serwin/*
8019170222Sdougb * Act on a "freeze" or "thaw" command from the command channel.
8020135446Strhodes */
8021135446Strhodesisc_result_t
8022204619Sdougbns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args,
8023204619Sdougb		 isc_buffer_t *text)
8024204619Sdougb{
8025170222Sdougb	isc_result_t result, tresult;
8026254897Serwin	dns_zone_t *zone = NULL, *raw = NULL;
8027135446Strhodes	dns_zonetype_t type;
8028135446Strhodes	char classstr[DNS_RDATACLASS_FORMATSIZE];
8029135446Strhodes	char zonename[DNS_NAME_FORMATSIZE];
8030135446Strhodes	dns_view_t *view;
8031135446Strhodes	const char *vname, *sep;
8032135446Strhodes	isc_boolean_t frozen;
8033204619Sdougb	const char *msg = NULL;
8034186462Sdougb
8035262706Serwin	result = zone_from_args(server, args, NULL, &zone, NULL,
8036262706Serwin				text, ISC_TRUE);
8037135446Strhodes	if (result != ISC_R_SUCCESS)
8038135446Strhodes		return (result);
8039170222Sdougb	if (zone == NULL) {
8040170222Sdougb		result = isc_task_beginexclusive(server->task);
8041170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
8042170222Sdougb		tresult = ISC_R_SUCCESS;
8043186462Sdougb		for (view = ISC_LIST_HEAD(server->viewlist);
8044170222Sdougb		     view != NULL;
8045170222Sdougb		     view = ISC_LIST_NEXT(view, link)) {
8046170222Sdougb			result = dns_view_freezezones(view, freeze);
8047170222Sdougb			if (result != ISC_R_SUCCESS &&
8048170222Sdougb			    tresult == ISC_R_SUCCESS)
8049170222Sdougb				tresult = result;
8050170222Sdougb		}
8051170222Sdougb		isc_task_endexclusive(server->task);
8052170222Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8053170222Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8054170222Sdougb			      "%s all zones: %s",
8055170222Sdougb			      freeze ? "freezing" : "thawing",
8056170222Sdougb			      isc_result_totext(tresult));
8057170222Sdougb		return (tresult);
8058170222Sdougb	}
8059254897Serwin	dns_zone_getraw(zone, &raw);
8060254897Serwin	if (raw != NULL) {
8061254897Serwin		dns_zone_detach(&zone);
8062254897Serwin		dns_zone_attach(raw, &zone);
8063254897Serwin		dns_zone_detach(&raw);
8064254897Serwin	}
8065135446Strhodes	type = dns_zone_gettype(zone);
8066135446Strhodes	if (type != dns_zone_master) {
8067135446Strhodes		dns_zone_detach(&zone);
8068224092Sdougb		return (DNS_R_NOTMASTER);
8069135446Strhodes	}
8070135446Strhodes
8071254897Serwin	if (freeze && !dns_zone_isdynamic(zone, ISC_TRUE)) {
8072254897Serwin		dns_zone_detach(&zone);
8073254897Serwin		return (DNS_R_NOTDYNAMIC);
8074254897Serwin	}
8075254897Serwin
8076204619Sdougb	result = isc_task_beginexclusive(server->task);
8077204619Sdougb	RUNTIME_CHECK(result == ISC_R_SUCCESS);
8078135446Strhodes	frozen = dns_zone_getupdatedisabled(zone);
8079135446Strhodes	if (freeze) {
8080204619Sdougb		if (frozen) {
8081204619Sdougb			msg = "WARNING: The zone was already frozen.\n"
8082204619Sdougb			      "Someone else may be editing it or "
8083204619Sdougb			      "it may still be re-loading.";
8084135446Strhodes			result = DNS_R_FROZEN;
8085204619Sdougb		}
8086204619Sdougb		if (result == ISC_R_SUCCESS) {
8087135446Strhodes			result = dns_zone_flush(zone);
8088204619Sdougb			if (result != ISC_R_SUCCESS)
8089204619Sdougb				msg = "Flushing the zone updates to "
8090204619Sdougb				      "disk failed.";
8091204619Sdougb		}
8092204619Sdougb		if (result == ISC_R_SUCCESS)
8093204619Sdougb			dns_zone_setupdatedisabled(zone, freeze);
8094135446Strhodes	} else {
8095135446Strhodes		if (frozen) {
8096204619Sdougb			result = dns_zone_loadandthaw(zone);
8097204619Sdougb			switch (result) {
8098204619Sdougb			case ISC_R_SUCCESS:
8099204619Sdougb			case DNS_R_UPTODATE:
8100204619Sdougb				msg = "The zone reload and thaw was "
8101204619Sdougb				      "successful.";
8102135446Strhodes				result = ISC_R_SUCCESS;
8103204619Sdougb				break;
8104204619Sdougb			case DNS_R_CONTINUE:
8105204619Sdougb				msg = "A zone reload and thaw was started.\n"
8106204619Sdougb				      "Check the logs to see the result.";
8107204619Sdougb				result = ISC_R_SUCCESS;
8108204619Sdougb				break;
8109204619Sdougb			}
8110135446Strhodes		}
8111135446Strhodes	}
8112204619Sdougb	isc_task_endexclusive(server->task);
8113135446Strhodes
8114204619Sdougb	if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
8115204619Sdougb		isc_buffer_putmem(text, (const unsigned char *)msg,
8116204619Sdougb				  strlen(msg) + 1);
8117204619Sdougb
8118135446Strhodes	view = dns_zone_getview(zone);
8119224092Sdougb	if (strcmp(view->name, "_default") == 0 ||
8120224092Sdougb	    strcmp(view->name, "_bind") == 0)
8121135446Strhodes	{
8122135446Strhodes		vname = "";
8123135446Strhodes		sep = "";
8124135446Strhodes	} else {
8125135446Strhodes		vname = view->name;
8126135446Strhodes		sep = " ";
8127135446Strhodes	}
8128135446Strhodes	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
8129135446Strhodes			      sizeof(classstr));
8130135446Strhodes	dns_name_format(dns_zone_getorigin(zone),
8131135446Strhodes			zonename, sizeof(zonename));
8132135446Strhodes	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8133135446Strhodes		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8134135446Strhodes		      "%s zone '%s/%s'%s%s: %s",
8135170222Sdougb		      freeze ? "freezing" : "thawing",
8136135446Strhodes		      zonename, classstr, sep, vname,
8137135446Strhodes		      isc_result_totext(result));
8138135446Strhodes	dns_zone_detach(&zone);
8139135446Strhodes	return (result);
8140135446Strhodes}
8141153816Sdougb
8142153816Sdougb#ifdef HAVE_LIBSCF
8143153816Sdougb/*
8144153816Sdougb * This function adds a message for rndc to echo if named
8145153816Sdougb * is managed by smf and is also running chroot.
8146153816Sdougb */
8147153816Sdougbisc_result_t
8148153816Sdougbns_smf_add_message(isc_buffer_t *text) {
8149153816Sdougb	unsigned int n;
8150153816Sdougb
8151153816Sdougb	n = snprintf((char *)isc_buffer_used(text),
8152153816Sdougb		isc_buffer_availablelength(text),
8153153816Sdougb		"use svcadm(1M) to manage named");
8154153816Sdougb	if (n >= isc_buffer_availablelength(text))
8155153816Sdougb		return (ISC_R_NOSPACE);
8156153816Sdougb	isc_buffer_add(text, n);
8157153816Sdougb	return (ISC_R_SUCCESS);
8158153816Sdougb}
8159153816Sdougb#endif /* HAVE_LIBSCF */
8160224092Sdougb
8161224092Sdougb/*
8162262706Serwin * Emit a comment at the top of the nzf file containing the viewname
8163262706Serwin * Expects the fp to already be open for writing
8164262706Serwin */
8165262706Serwin#define HEADER1 "# New zone file for view: "
8166262706Serwin#define HEADER2 "\n# This file contains configuration for zones added by\n" \
8167262706Serwin		"# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n"
8168262706Serwinisc_result_t
8169262706Serwinadd_comment(FILE *fp, const char *viewname) {
8170262706Serwin	isc_result_t result;
8171262706Serwin	CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL));
8172262706Serwin	CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL));
8173262706Serwin	CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL));
8174262706Serwin cleanup:
8175262706Serwin	return (result);
8176262706Serwin}
8177262706Serwin
8178262706Serwin/*
8179224092Sdougb * Act on an "addzone" command from the command channel.
8180224092Sdougb */
8181224092Sdougbisc_result_t
8182224092Sdougbns_server_add_zone(ns_server_t *server, char *args) {
8183224092Sdougb	isc_result_t	     result;
8184224092Sdougb	isc_buffer_t	     argbuf;
8185224092Sdougb	size_t		     arglen;
8186224092Sdougb	cfg_parser_t	    *parser = NULL;
8187224092Sdougb	cfg_obj_t	    *config = NULL;
8188224092Sdougb	const cfg_obj_t	    *vconfig = NULL;
8189224092Sdougb	const cfg_obj_t	    *views = NULL;
8190224092Sdougb	const cfg_obj_t     *parms = NULL;
8191224092Sdougb	const cfg_obj_t     *obj = NULL;
8192224092Sdougb	const cfg_listelt_t *element;
8193224092Sdougb	const char	    *zonename;
8194224092Sdougb	const char	    *classname = NULL;
8195224092Sdougb	const char	    *argp;
8196224092Sdougb	const char	    *viewname = NULL;
8197224092Sdougb	dns_rdataclass_t     rdclass;
8198224092Sdougb	dns_view_t	    *view = 0;
8199262706Serwin	isc_buffer_t	     buf;
8200262706Serwin	dns_fixedname_t	     fname;
8201262706Serwin	dns_name_t	    *dnsname;
8202224092Sdougb	dns_zone_t	    *zone = NULL;
8203224092Sdougb	FILE		    *fp = NULL;
8204224092Sdougb	struct cfg_context  *cfg = NULL;
8205262706Serwin	char 		    namebuf[DNS_NAME_FORMATSIZE];
8206262706Serwin	off_t		    offset;
8207224092Sdougb
8208224092Sdougb	/* Try to parse the argument string */
8209224092Sdougb	arglen = strlen(args);
8210262706Serwin	isc_buffer_init(&argbuf, args, (unsigned int)arglen);
8211224092Sdougb	isc_buffer_add(&argbuf, strlen(args));
8212224092Sdougb	CHECK(cfg_parser_create(server->mctx, ns_g_lctx, &parser));
8213224092Sdougb	CHECK(cfg_parse_buffer(parser, &argbuf, &cfg_type_addzoneconf,
8214224092Sdougb			       &config));
8215224092Sdougb	CHECK(cfg_map_get(config, "addzone", &parms));
8216224092Sdougb
8217224092Sdougb	zonename = cfg_obj_asstring(cfg_tuple_get(parms, "name"));
8218254402Serwin	isc_buffer_constinit(&buf, zonename, strlen(zonename));
8219224092Sdougb	isc_buffer_add(&buf, strlen(zonename));
8220224092Sdougb
8221262706Serwin	dns_fixedname_init(&fname);
8222262706Serwin	dnsname = dns_fixedname_name(&fname);
8223262706Serwin	CHECK(dns_name_fromtext(dnsname, &buf, dns_rootname, ISC_FALSE, NULL));
8224262706Serwin
8225224092Sdougb	/* Make sense of optional class argument */
8226224092Sdougb	obj = cfg_tuple_get(parms, "class");
8227224092Sdougb	CHECK(ns_config_getclass(obj, dns_rdataclass_in, &rdclass));
8228224092Sdougb	if (rdclass != dns_rdataclass_in && obj)
8229224092Sdougb		classname = cfg_obj_asstring(obj);
8230224092Sdougb
8231224092Sdougb	/* Make sense of optional view argument */
8232224092Sdougb	obj = cfg_tuple_get(parms, "view");
8233224092Sdougb	if (obj && cfg_obj_isstring(obj))
8234224092Sdougb		viewname = cfg_obj_asstring(obj);
8235224092Sdougb	if (viewname == NULL || *viewname == '\0')
8236224092Sdougb		viewname = "_default";
8237224092Sdougb	CHECK(dns_viewlist_find(&server->viewlist, viewname, rdclass, &view));
8238224092Sdougb
8239224092Sdougb	/* Are we accepting new zones? */
8240224092Sdougb	if (view->new_zone_file == NULL) {
8241224092Sdougb		result = ISC_R_NOPERM;
8242224092Sdougb		goto cleanup;
8243224092Sdougb	}
8244224092Sdougb
8245224092Sdougb	cfg = (struct cfg_context *) view->new_zone_config;
8246224092Sdougb	if (cfg == NULL) {
8247224092Sdougb		result = ISC_R_FAILURE;
8248224092Sdougb		goto cleanup;
8249224092Sdougb	}
8250224092Sdougb
8251224092Sdougb	/* Zone shouldn't already exist */
8252262706Serwin	result = dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone);
8253224092Sdougb	if (result == ISC_R_SUCCESS) {
8254224092Sdougb		result = ISC_R_EXISTS;
8255224092Sdougb		goto cleanup;
8256224092Sdougb	} else if (result == DNS_R_PARTIALMATCH) {
8257224092Sdougb		/* Create our sub-zone anyway */
8258224092Sdougb		dns_zone_detach(&zone);
8259224092Sdougb		zone = NULL;
8260224092Sdougb	}
8261224092Sdougb	else if (result != ISC_R_NOTFOUND)
8262224092Sdougb		goto cleanup;
8263224092Sdougb
8264224092Sdougb	/* Find the view statement */
8265224092Sdougb	cfg_map_get(cfg->config, "view", &views);
8266224092Sdougb	for (element = cfg_list_first(views);
8267224092Sdougb	     element != NULL;
8268224092Sdougb	     element = cfg_list_next(element))
8269224092Sdougb	{
8270224092Sdougb		const char *vname;
8271224092Sdougb		vconfig = cfg_listelt_value(element);
8272224092Sdougb		vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
8273224092Sdougb		if (vname && !strcasecmp(vname, viewname))
8274224092Sdougb			break;
8275224092Sdougb		vconfig = NULL;
8276224092Sdougb	}
8277224092Sdougb
8278224092Sdougb	/* Open save file for write configuration */
8279224092Sdougb	CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
8280262706Serwin	CHECK(isc_stdio_tell(fp, &offset));
8281262706Serwin	if (offset == 0)
8282262706Serwin		CHECK(add_comment(fp, view->name));
8283224092Sdougb
8284224092Sdougb	/* Mark view unfrozen so that zone can be added */
8285254402Serwin	result = isc_task_beginexclusive(server->task);
8286254402Serwin	RUNTIME_CHECK(result == ISC_R_SUCCESS);
8287224092Sdougb	dns_view_thaw(view);
8288224092Sdougb	result = configure_zone(cfg->config, parms, vconfig,
8289225361Sdougb				server->mctx, view, cfg->actx, ISC_FALSE);
8290224092Sdougb	dns_view_freeze(view);
8291234010Sdougb	isc_task_endexclusive(server->task);
8292234010Sdougb	if (result != ISC_R_SUCCESS)
8293224092Sdougb		goto cleanup;
8294224092Sdougb
8295224092Sdougb	/* Is it there yet? */
8296262706Serwin	CHECK(dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone));
8297224092Sdougb
8298224092Sdougb	/*
8299224092Sdougb	 * Load the zone from the master file.  If this fails, we'll
8300224092Sdougb	 * need to undo the configuration we've done already.
8301224092Sdougb	 */
8302224092Sdougb	result = dns_zone_loadnew(zone);
8303224092Sdougb	if (result != ISC_R_SUCCESS) {
8304224092Sdougb		dns_db_t *dbp = NULL;
8305224092Sdougb
8306224092Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8307224092Sdougb			      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8308224092Sdougb			      "addzone failed; reverting.");
8309224092Sdougb
8310224092Sdougb		/* If the zone loaded partially, unload it */
8311224092Sdougb		if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
8312224092Sdougb			dns_db_detach(&dbp);
8313224092Sdougb			dns_zone_unload(zone);
8314224092Sdougb		}
8315224092Sdougb
8316224092Sdougb		/* Remove the zone from the zone table */
8317224092Sdougb		dns_zt_unmount(view->zonetable, zone);
8318224092Sdougb		goto cleanup;
8319224092Sdougb	}
8320224092Sdougb
8321224092Sdougb	/* Flag the zone as having been added at runtime */
8322224092Sdougb	dns_zone_setadded(zone, ISC_TRUE);
8323224092Sdougb
8324262706Serwin	/* Emit the zone name, quoted and escaped */
8325262706Serwin	isc_buffer_init(&buf, namebuf, sizeof(namebuf));
8326262706Serwin	CHECK(dns_name_totext(dnsname, ISC_TRUE, &buf));
8327262706Serwin	isc_buffer_putuint8(&buf, 0);
8328262706Serwin	CHECK(isc_stdio_write("zone \"", 6, 1, fp, NULL));
8329262706Serwin	CHECK(isc_stdio_write(namebuf, strlen(namebuf), 1, fp, NULL));
8330262706Serwin	CHECK(isc_stdio_write("\" ", 2, 1, fp, NULL));
8331224092Sdougb
8332224092Sdougb	/* Classname, if not default */
8333224092Sdougb	if (classname != NULL && *classname != '\0') {
8334224092Sdougb		CHECK(isc_stdio_write(classname, strlen(classname), 1, fp,
8335224092Sdougb				      NULL));
8336224092Sdougb		CHECK(isc_stdio_write(" ", 1, 1, fp, NULL));
8337224092Sdougb	}
8338224092Sdougb
8339224092Sdougb	/* Find beginning of option block from args */
8340224092Sdougb	for (argp = args; *argp; argp++, arglen--) {
8341224092Sdougb		if (*argp == '{') {	/* Assume matching '}' */
8342224092Sdougb			/* Add that to our file */
8343224092Sdougb			CHECK(isc_stdio_write(argp, arglen, 1, fp, NULL));
8344224092Sdougb
8345224092Sdougb			/* Make sure we end with a LF */
8346224092Sdougb			if (argp[arglen-1] != '\n') {
8347224092Sdougb				CHECK(isc_stdio_write("\n", 1, 1, fp, NULL));
8348224092Sdougb			}
8349224092Sdougb			break;
8350224092Sdougb		}
8351224092Sdougb	}
8352224092Sdougb
8353224092Sdougb	CHECK(isc_stdio_close(fp));
8354224092Sdougb	fp = NULL;
8355224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8356224092Sdougb				  NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8357224092Sdougb				  "zone %s added to view %s via addzone",
8358224092Sdougb				  zonename, viewname);
8359224092Sdougb
8360224092Sdougb	result = ISC_R_SUCCESS;
8361224092Sdougb
8362224092Sdougb cleanup:
8363224092Sdougb	if (fp != NULL)
8364224092Sdougb		isc_stdio_close(fp);
8365224092Sdougb	if (parser != NULL) {
8366224092Sdougb		if (config != NULL)
8367224092Sdougb			cfg_obj_destroy(parser, &config);
8368224092Sdougb		cfg_parser_destroy(&parser);
8369224092Sdougb	}
8370224092Sdougb	if (zone != NULL)
8371224092Sdougb		dns_zone_detach(&zone);
8372224092Sdougb	if (view != NULL)
8373224092Sdougb		dns_view_detach(&view);
8374224092Sdougb
8375224092Sdougb	return (result);
8376224092Sdougb}
8377224092Sdougb
8378224092Sdougb/*
8379224092Sdougb * Act on a "delzone" command from the command channel.
8380224092Sdougb */
8381224092Sdougbisc_result_t
8382262706Serwinns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t *text) {
8383262706Serwin	isc_result_t result;
8384262706Serwin	dns_zone_t *zone = NULL;
8385262706Serwin	dns_view_t *view = NULL;
8386262706Serwin	dns_db_t *dbp = NULL;
8387262706Serwin	const char *filename = NULL;
8388262706Serwin	char *tmpname = NULL;
8389262706Serwin	char buf[1024];
8390262706Serwin	const char *zonename = NULL;
8391262706Serwin	size_t znamelen = 0;
8392262706Serwin	FILE *ifp = NULL, *ofp = NULL;
8393262706Serwin	isc_boolean_t inheader = ISC_TRUE;
8394224092Sdougb
8395224092Sdougb	/* Parse parameters */
8396262706Serwin	CHECK(zone_from_args(server, args, NULL, &zone, &zonename,
8397262706Serwin			     text, ISC_TRUE));
8398254402Serwin
8399224092Sdougb	if (zone == NULL) {
8400224092Sdougb		result = ISC_R_UNEXPECTEDEND;
8401224092Sdougb		goto cleanup;
8402224092Sdougb	}
8403224092Sdougb
8404224092Sdougb	/*
8405224092Sdougb	 * Was this zone originally added at runtime?
8406224092Sdougb	 * If not, we can't delete it now.
8407224092Sdougb	 */
8408224092Sdougb	if (!dns_zone_getadded(zone)) {
8409224092Sdougb		result = ISC_R_NOPERM;
8410224092Sdougb		goto cleanup;
8411224092Sdougb	}
8412224092Sdougb
8413254402Serwin	INSIST(zonename != NULL);
8414254402Serwin	znamelen = strlen(zonename);
8415224092Sdougb
8416224092Sdougb	/* Dig out configuration for this zone */
8417224092Sdougb	view = dns_zone_getview(zone);
8418224092Sdougb	filename = view->new_zone_file;
8419224092Sdougb	if (filename == NULL) {
8420224092Sdougb		/* No adding zones in this view */
8421224092Sdougb		result = ISC_R_FAILURE;
8422224092Sdougb		goto cleanup;
8423224092Sdougb	}
8424224092Sdougb
8425224092Sdougb	/* Rewrite zone list */
8426224092Sdougb	result = isc_stdio_open(filename, "r", &ifp);
8427224092Sdougb	if (ifp != NULL && result == ISC_R_SUCCESS) {
8428224092Sdougb		char *found = NULL, *p = NULL;
8429224092Sdougb		size_t n;
8430224092Sdougb
8431224092Sdougb		/* Create a temporary file */
8432224092Sdougb		CHECK(isc_string_printf(buf, 1023, "%s.%ld", filename,
8433224092Sdougb					(long)getpid()));
8434224092Sdougb		if (!(tmpname = isc_mem_strdup(server->mctx, buf))) {
8435224092Sdougb			result = ISC_R_NOMEMORY;
8436224092Sdougb			goto cleanup;
8437224092Sdougb		}
8438224092Sdougb		CHECK(isc_stdio_open(tmpname, "w", &ofp));
8439262706Serwin		CHECK(add_comment(ofp, view->name));
8440224092Sdougb
8441224092Sdougb		/* Look for the entry for that zone */
8442224092Sdougb		while (fgets(buf, 1024, ifp)) {
8443262706Serwin			/* Skip initial comment, if any */
8444262706Serwin			if (inheader && *buf == '#')
8445262706Serwin				continue;
8446262706Serwin			if (*buf != '#')
8447262706Serwin				inheader = ISC_FALSE;
8448262706Serwin
8449262706Serwin			/*
8450262706Serwin			 * Any other lines not starting with zone, copy
8451262706Serwin			 * them out and continue.
8452262706Serwin			 */
8453262706Serwin			if (strncasecmp(buf, "zone", 4) != 0) {
8454224092Sdougb				fputs(buf, ofp);
8455224092Sdougb				continue;
8456224092Sdougb			}
8457224092Sdougb			p = buf+4;
8458224092Sdougb
8459262706Serwin			/* This is a zone; find its name. */
8460224092Sdougb			while (*p &&
8461224092Sdougb			       ((*p == '"') || isspace((unsigned char)*p)))
8462224092Sdougb				p++;
8463224092Sdougb
8464262706Serwin			/*
8465262706Serwin			 * If it's not the zone we're looking for, copy
8466262706Serwin			 * it out and continue
8467262706Serwin			 */
8468262706Serwin			if (strncasecmp(p, zonename, znamelen) != 0) {
8469224092Sdougb				fputs(buf, ofp);
8470224092Sdougb				continue;
8471224092Sdougb			}
8472224092Sdougb
8473262706Serwin			/*
8474262706Serwin			 * But if it is the zone we want, skip over it
8475262706Serwin			 * so it will be omitted from the new file
8476262706Serwin			 */
8477224092Sdougb			p += znamelen;
8478224092Sdougb			if (isspace((unsigned char)*p) ||
8479224092Sdougb			    *p == '"' || *p == '{') {
8480224092Sdougb				/* This must be the entry */
8481224092Sdougb				found = p;
8482224092Sdougb				break;
8483224092Sdougb			}
8484224092Sdougb
8485262706Serwin			/* Copy the rest of the buffer out and continue */
8486224092Sdougb			fputs(buf, ofp);
8487224092Sdougb		}
8488224092Sdougb
8489224092Sdougb		/* Skip over an option block (matching # of braces) */
8490224092Sdougb		if (found) {
8491224092Sdougb			int obrace = 0, cbrace = 0;
8492224092Sdougb			for (;;) {
8493224092Sdougb				while (*p) {
8494224092Sdougb					if (*p == '{') obrace++;
8495224092Sdougb					if (*p == '}') cbrace++;
8496224092Sdougb					p++;
8497224092Sdougb				}
8498224092Sdougb				if (obrace && (obrace == cbrace))
8499224092Sdougb					break;
8500224092Sdougb				if (!fgets(buf, 1024, ifp))
8501224092Sdougb					break;
8502224092Sdougb				p = buf;
8503224092Sdougb			}
8504224092Sdougb
8505224092Sdougb			/* Just spool the remainder of the file out */
8506224092Sdougb			result = isc_stdio_read(buf, 1, 1024, ifp, &n);
8507224092Sdougb			while (n > 0U) {
8508224092Sdougb				if (result == ISC_R_EOF)
8509224092Sdougb					result = ISC_R_SUCCESS;
8510224092Sdougb				CHECK(result);
8511224092Sdougb				isc_stdio_write(buf, 1, n, ofp, NULL);
8512224092Sdougb				result = isc_stdio_read(buf, 1, 1024, ifp, &n);
8513224092Sdougb			}
8514224092Sdougb
8515224092Sdougb			/* Move temporary into place */
8516224092Sdougb			CHECK(isc_file_rename(tmpname, view->new_zone_file));
8517224092Sdougb		} else {
8518224092Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8519224092Sdougb				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
8520224092Sdougb				      "deleted zone %s was missing from "
8521224092Sdougb				      "new zone file", zonename);
8522224092Sdougb			goto cleanup;
8523224092Sdougb		}
8524224092Sdougb	}
8525224092Sdougb
8526224092Sdougb	/* Stop answering for this zone */
8527224092Sdougb	if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
8528224092Sdougb		dns_db_detach(&dbp);
8529224092Sdougb		dns_zone_unload(zone);
8530224092Sdougb	}
8531224092Sdougb
8532224092Sdougb	CHECK(dns_zt_unmount(view->zonetable, zone));
8533224092Sdougb
8534224092Sdougb	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
8535224092Sdougb				  NS_LOGMODULE_SERVER, ISC_LOG_INFO,
8536224092Sdougb				  "zone %s removed via delzone", zonename);
8537224092Sdougb
8538224092Sdougb	result = ISC_R_SUCCESS;
8539224092Sdougb
8540224092Sdougb cleanup:
8541224092Sdougb	if (ifp != NULL)
8542224092Sdougb		isc_stdio_close(ifp);
8543224092Sdougb	if (ofp != NULL) {
8544224092Sdougb		isc_stdio_close(ofp);
8545224092Sdougb		isc_file_remove(tmpname);
8546224092Sdougb	}
8547224092Sdougb	if (tmpname != NULL)
8548224092Sdougb		isc_mem_free(server->mctx, tmpname);
8549224092Sdougb	if (zone != NULL)
8550224092Sdougb		dns_zone_detach(&zone);
8551224092Sdougb
8552224092Sdougb	return (result);
8553224092Sdougb}
8554224092Sdougb
8555224092Sdougbstatic void
8556225361Sdougbnewzone_cfgctx_destroy(void **cfgp) {
8557224092Sdougb	struct cfg_context *cfg;
8558224092Sdougb
8559224092Sdougb	REQUIRE(cfgp != NULL && *cfgp != NULL);
8560225361Sdougb
8561224092Sdougb	cfg = *cfgp;
8562224092Sdougb
8563225361Sdougb	if (cfg->actx != NULL)
8564225361Sdougb		cfg_aclconfctx_detach(&cfg->actx);
8565225361Sdougb
8566224092Sdougb	if (cfg->parser != NULL) {
8567224092Sdougb		if (cfg->config != NULL)
8568224092Sdougb			cfg_obj_destroy(cfg->parser, &cfg->config);
8569224092Sdougb		cfg_parser_destroy(&cfg->parser);
8570224092Sdougb	}
8571225361Sdougb	if (cfg->nzparser != NULL) {
8572225361Sdougb		if (cfg->nzconfig != NULL)
8573225361Sdougb			cfg_obj_destroy(cfg->nzparser, &cfg->nzconfig);
8574225361Sdougb		cfg_parser_destroy(&cfg->nzparser);
8575225361Sdougb	}
8576224092Sdougb
8577225361Sdougb	isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg));
8578224092Sdougb	*cfgp = NULL;
8579224092Sdougb}
8580254897Serwin
8581254897Serwinisc_result_t
8582254897Serwinns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) {
8583254897Serwin	isc_result_t result = ISC_R_SUCCESS;
8584254897Serwin	dns_zone_t *zone = NULL;
8585254897Serwin	dns_name_t *origin;
8586254897Serwin	dns_db_t *db = NULL;
8587254897Serwin	dns_dbnode_t *node = NULL;
8588254897Serwin	dns_dbversion_t *version = NULL;
8589254897Serwin	dns_rdatatype_t privatetype;
8590254897Serwin	dns_rdataset_t privset;
8591254897Serwin	isc_boolean_t first = ISC_TRUE;
8592254897Serwin	isc_boolean_t list = ISC_FALSE, clear = ISC_FALSE;
8593254897Serwin	isc_boolean_t chain = ISC_FALSE;
8594254897Serwin	char keystr[DNS_SECALG_FORMATSIZE + 7];
8595254897Serwin	unsigned short hash = 0, flags = 0, iter = 0, saltlen = 0;
8596254897Serwin	unsigned char salt[255];
8597254897Serwin	const char *ptr;
8598254897Serwin	size_t n;
8599254897Serwin
8600254897Serwin	dns_rdataset_init(&privset);
8601254897Serwin
8602254897Serwin	/* Skip the command name. */
8603254897Serwin	ptr = next_token(&args, " \t");
8604254897Serwin	if (ptr == NULL)
8605254897Serwin		return (ISC_R_UNEXPECTEDEND);
8606254897Serwin
8607254897Serwin	/* Find out what we are to do. */
8608254897Serwin	ptr = next_token(&args, " \t");
8609254897Serwin	if (ptr == NULL)
8610254897Serwin		return (ISC_R_UNEXPECTEDEND);
8611254897Serwin
8612254897Serwin	if (strcasecmp(ptr, "-list") == 0)
8613254897Serwin		list = ISC_TRUE;
8614254897Serwin	else if ((strcasecmp(ptr, "-clear") == 0)  ||
8615254897Serwin		 (strcasecmp(ptr, "-clean") == 0)) {
8616254897Serwin		clear = ISC_TRUE;
8617254897Serwin		ptr = next_token(&args, " \t");
8618254897Serwin		if (ptr == NULL)
8619254897Serwin			return (ISC_R_UNEXPECTEDEND);
8620262706Serwin		memmove(keystr, ptr, sizeof(keystr));
8621262706Serwin	} else if (strcasecmp(ptr, "-nsec3param") == 0) {
8622254897Serwin		const char *hashstr, *flagstr, *iterstr;
8623254897Serwin		char nbuf[512];
8624254897Serwin
8625254897Serwin		chain = ISC_TRUE;
8626254897Serwin		hashstr = next_token(&args, " \t");
8627254897Serwin		if (hashstr == NULL)
8628254897Serwin			return (ISC_R_UNEXPECTEDEND);
8629254897Serwin
8630254897Serwin		if (strcasecmp(hashstr, "none") == 0)
8631254897Serwin			hash = 0;
8632254897Serwin		else {
8633254897Serwin			flagstr = next_token(&args, " \t");
8634254897Serwin			iterstr = next_token(&args, " \t");
8635254897Serwin			if (flagstr == NULL || iterstr == NULL)
8636254897Serwin				return (ISC_R_UNEXPECTEDEND);
8637254897Serwin
8638254897Serwin			n = snprintf(nbuf, sizeof(nbuf), "%s %s %s",
8639254897Serwin				     hashstr, flagstr, iterstr);
8640254897Serwin			if (n == sizeof(nbuf))
8641254897Serwin				return (ISC_R_NOSPACE);
8642254897Serwin			n = sscanf(nbuf, "%hu %hu %hu", &hash, &flags, &iter);
8643254897Serwin			if (n != 3U)
8644254897Serwin				return (ISC_R_BADNUMBER);
8645254897Serwin
8646254897Serwin			if (hash > 0xffU || flags > 0xffU)
8647254897Serwin				return (ISC_R_RANGE);
8648254897Serwin
8649254897Serwin			ptr = next_token(&args, " \t");
8650254897Serwin			if (ptr == NULL)
8651254897Serwin				return (ISC_R_UNEXPECTEDEND);
8652254897Serwin			if (strcmp(ptr, "-") != 0) {
8653254897Serwin				isc_buffer_t buf;
8654254897Serwin
8655254897Serwin				isc_buffer_init(&buf, salt, sizeof(salt));
8656254897Serwin				CHECK(isc_hex_decodestring(ptr, &buf));
8657254897Serwin				saltlen = isc_buffer_usedlength(&buf);
8658254897Serwin			}
8659254897Serwin		}
8660254897Serwin	} else
8661254897Serwin		CHECK(DNS_R_SYNTAX);
8662254897Serwin
8663262706Serwin	CHECK(zone_from_args(server, args, NULL, &zone, NULL,
8664262706Serwin			     text, ISC_FALSE));
8665254897Serwin	if (zone == NULL)
8666254897Serwin		CHECK(ISC_R_UNEXPECTEDEND);
8667254897Serwin
8668254897Serwin	if (clear) {
8669254897Serwin		CHECK(dns_zone_keydone(zone, keystr));
8670254897Serwin		isc_buffer_putstr(text, "request queued");
8671254897Serwin		isc_buffer_putuint8(text, 0);
8672254897Serwin	} else if (chain) {
8673254897Serwin		CHECK(dns_zone_setnsec3param(zone, (isc_uint8_t)hash,
8674254897Serwin					     (isc_uint8_t)flags, iter,
8675254897Serwin					     (isc_uint8_t)saltlen, salt,
8676254897Serwin					     ISC_TRUE));
8677254897Serwin		isc_buffer_putstr(text, "request queued");
8678254897Serwin		isc_buffer_putuint8(text, 0);
8679254897Serwin	} else if (list) {
8680254897Serwin		privatetype = dns_zone_getprivatetype(zone);
8681254897Serwin		origin = dns_zone_getorigin(zone);
8682254897Serwin		CHECK(dns_zone_getdb(zone, &db));
8683254897Serwin		CHECK(dns_db_findnode(db, origin, ISC_FALSE, &node));
8684254897Serwin		dns_db_currentversion(db, &version);
8685254897Serwin
8686254897Serwin		result = dns_db_findrdataset(db, node, version, privatetype,
8687254897Serwin					     dns_rdatatype_none, 0,
8688254897Serwin					     &privset, NULL);
8689254897Serwin		if (result == ISC_R_NOTFOUND) {
8690254897Serwin			isc_buffer_putstr(text, "No signing records found");
8691254897Serwin			isc_buffer_putuint8(text, 0);
8692254897Serwin			result = ISC_R_SUCCESS;
8693254897Serwin			goto cleanup;
8694254897Serwin		}
8695254897Serwin
8696254897Serwin		for (result = dns_rdataset_first(&privset);
8697254897Serwin		     result == ISC_R_SUCCESS;
8698254897Serwin		     result = dns_rdataset_next(&privset))
8699254897Serwin		{
8700254897Serwin			dns_rdata_t priv = DNS_RDATA_INIT;
8701254897Serwin			char output[BUFSIZ];
8702254897Serwin			isc_buffer_t buf;
8703254897Serwin
8704254897Serwin			dns_rdataset_current(&privset, &priv);
8705254897Serwin
8706254897Serwin			isc_buffer_init(&buf, output, sizeof(output));
8707254897Serwin			CHECK(dns_private_totext(&priv, &buf));
8708254897Serwin
8709254897Serwin			if (!first)
8710254897Serwin				isc_buffer_putstr(text, "\n");
8711254897Serwin			first = ISC_FALSE;
8712254897Serwin
8713254897Serwin			n = snprintf((char *)isc_buffer_used(text),
8714254897Serwin				     isc_buffer_availablelength(text),
8715254897Serwin				     "%s", output);
8716254897Serwin			if (n >= isc_buffer_availablelength(text))
8717254897Serwin				CHECK(ISC_R_NOSPACE);
8718254897Serwin
8719262706Serwin			isc_buffer_add(text, (unsigned int)n);
8720254897Serwin		}
8721262706Serwin		if (!first && isc_buffer_availablelength(text) > 0)
8722262706Serwin			isc_buffer_putuint8(text, 0);
8723254897Serwin
8724254897Serwin		if (result == ISC_R_NOMORE)
8725254897Serwin			result = ISC_R_SUCCESS;
8726254897Serwin	}
8727254897Serwin
8728254897Serwin cleanup:
8729254897Serwin	if (dns_rdataset_isassociated(&privset))
8730254897Serwin		dns_rdataset_disassociate(&privset);
8731254897Serwin	if (node != NULL)
8732254897Serwin		dns_db_detachnode(db, &node);
8733254897Serwin	if (version != NULL)
8734254897Serwin		dns_db_closeversion(db, &version, ISC_FALSE);
8735254897Serwin	if (db != NULL)
8736254897Serwin		dns_db_detach(&db);
8737254897Serwin	if (zone != NULL)
8738254897Serwin		dns_zone_detach(&zone);
8739254897Serwin
8740254897Serwin	return (result);
8741254897Serwin}
8742262706Serwin
8743262706Serwinstatic isc_result_t
8744262706Serwinputstr(isc_buffer_t *b, const char *str) {
8745262706Serwin	size_t l = strlen(str);
8746262706Serwin
8747262706Serwin	/*
8748262706Serwin	 * Use >= to leave space for NUL termination.
8749262706Serwin	 */
8750262706Serwin	if (l >= isc_buffer_availablelength(b))
8751262706Serwin		return (ISC_R_NOSPACE);
8752262706Serwin
8753262706Serwin	isc_buffer_putmem(b, (const unsigned char *)str, l);
8754262706Serwin	return (ISC_R_SUCCESS);
8755262706Serwin}
8756