bind9/bin/named/server.c
Andreas Gustafsson 113b8ef973 renamed dns_view_dumpcachetostream() to dns_view_dumpdbtostream()
and changed header comments to make it clear that it is for debugging/analysis purposes and may
dump other stuff in addition to the cache in the future.  Also changed the dump style to the
'explict TTL' one, which makes more sense than usin  when dumping cache files since the TTLs
tend to be different in each rrset, and added a comment banner at the top of each cache dump
containing the view name.
2000-12-15 21:11:38 +00:00

2424 lines
63 KiB
C

/*
* Copyright (C) 1999, 2000 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.c,v 1.276 2000/12/15 21:11:38 gson Exp $ */
#include <config.h>
#include <stdlib.h>
#include <isc/app.h>
#include <isc/base64.h>
#include <isc/dir.h>
#include <isc/entropy.h>
#include <isc/file.h>
#include <isc/lex.h>
#include <isc/print.h>
#include <isc/resource.h>
#include <isc/stdio.h>
#include <isc/string.h>
#include <isc/task.h>
#include <isc/timer.h>
#include <isc/util.h>
#include <dns/cache.h>
#include <dns/confparser.h>
#include <dns/db.h>
#include <dns/dispatch.h>
#include <dns/forward.h>
#include <dns/journal.h>
#include <dns/keytable.h>
#include <dns/master.h>
#include <dns/peer.h>
#include <dns/rdataclass.h>
#include <dns/rdatastruct.h>
#include <dns/resolver.h>
#include <dns/rootns.h>
#include <dns/stats.h>
#include <dns/tkey.h>
#include <dns/view.h>
#include <dns/zone.h>
#include <dns/zt.h>
#include <dst/dst.h>
#include <named/client.h>
#include <named/interfacemgr.h>
#include <named/log.h>
#include <named/logconf.h>
#include <named/lwresd.h>
#include <named/omapi.h>
#include <named/os.h>
#include <named/server.h>
#include <named/tkeyconf.h>
#include <named/tsigconf.h>
#include <named/zoneconf.h>
/*
* Check an operation for failure. Assumes that the function
* using it has a 'result' variable and a 'cleanup' label.
*/
#define CHECK(op) \
do { result = (op); \
if (result != ISC_R_SUCCESS) goto cleanup; \
} while (0)
#define CHECKM(op, msg) \
do { result = (op); \
if (result != ISC_R_SUCCESS) { \
isc_log_write(ns_g_lctx, \
NS_LOGCATEGORY_GENERAL, \
NS_LOGMODULE_SERVER, \
ISC_LOG_ERROR, \
"%s: %s", msg, \
isc_result_totext(result)); \
goto cleanup; \
} \
} while (0) \
#define CHECKFATAL(op, msg) \
do { result = (op); \
if (result != ISC_R_SUCCESS) \
fatal(msg, result); \
} while (0) \
typedef struct {
isc_mem_t * mctx;
dns_viewlist_t viewlist;
ns_aclconfctx_t *aclconf;
} ns_load_t;
static void
fatal(const char *msg, isc_result_t result);
static void
ns_server_reload(isc_task_t *task, isc_event_t *event);
static isc_result_t
ns_listenelt_fromconfig(dns_c_lstnon_t *celt, dns_c_ctx_t *cctx,
ns_aclconfctx_t *actx,
isc_mem_t *mctx, ns_listenelt_t **target);
static isc_result_t
ns_listenlist_fromconfig(dns_c_lstnlist_t *clist, dns_c_ctx_t *cctx,
ns_aclconfctx_t *actx,
isc_mem_t *mctx, ns_listenlist_t **target);
static isc_result_t
configure_forward(dns_c_ctx_t *cctx, dns_c_zone_t *czone, dns_c_view_t *cview,
dns_view_t *view, dns_name_t *origin,
dns_c_iplist_t *forwarders);
/*
* Configure a single view ACL at '*aclp'. Get its configuration by
* calling 'getvcacl' (for per-view configuration) and maybe 'getscacl'
* (for a global default).
*/
static isc_result_t
configure_view_acl(dns_c_view_t *cview,
dns_c_ctx_t *cctx,
ns_aclconfctx_t *actx, isc_mem_t *mctx,
isc_result_t (*getvcacl)
(dns_c_view_t *, dns_c_ipmatchlist_t **),
isc_result_t (*getscacl)
(dns_c_ctx_t *, dns_c_ipmatchlist_t **),
dns_acl_t **aclp)
{
isc_result_t result;
dns_c_ipmatchlist_t *cacl = NULL;
if (*aclp != NULL)
dns_acl_detach(aclp);
if (getvcacl != NULL && cview != NULL)
(void)(*getvcacl)(cview, &cacl);
if (cacl == NULL && getscacl != NULL)
(void)(*getscacl)(cctx, &cacl);
if (cacl == NULL) {
/*
* No value available. *aclp == NULL.
*/
return (ISC_R_SUCCESS);
}
result = ns_acl_fromconfig(cacl, cctx, actx, mctx, aclp);
dns_c_ipmatchlist_detach(&cacl);
return (result);
}
static isc_result_t
configure_view_dnsseckey(dns_c_view_t *cview, dns_c_tkey_t *ckey,
dns_keytable_t *keytable, isc_mem_t *mctx)
{
dns_rdataclass_t viewclass;
dns_rdata_key_t keystruct;
isc_int32_t flags, proto, alg;
unsigned char keydata[4096];
isc_buffer_t keydatabuf;
unsigned char rrdata[4096];
isc_buffer_t rrdatabuf;
isc_region_t r;
dns_fixedname_t fkeyname;
dns_name_t *keyname;
isc_buffer_t namebuf;
isc_result_t result;
dst_key_t *dstkey = NULL;
if (cview == NULL)
viewclass = dns_rdataclass_in;
else
CHECK(dns_c_view_getviewclass(cview,
&viewclass));
keystruct.common.rdclass = viewclass;
keystruct.common.rdtype = dns_rdatatype_key;
/*
* The key data in keystruct is not
* dynamically allocated.
*/
keystruct.mctx = NULL;
ISC_LINK_INIT(&keystruct.common, link);
flags = ckey->pubkey->flags;
proto = ckey->pubkey->protocol;
alg = ckey->pubkey->algorithm;
if (flags < 0 || flags > 0xffff)
CHECKM(ISC_R_RANGE, "key flags");
if (proto < 0 || proto > 0xff)
CHECKM(ISC_R_RANGE, "key protocol");
if (alg < 0 || alg > 0xff)
CHECKM(ISC_R_RANGE, "key algorithm");
keystruct.flags = flags;
keystruct.protocol = proto;
keystruct.algorithm = alg;
isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
CHECK(isc_base64_decodestring(mctx, ckey->pubkey->key,
&keydatabuf));
isc_buffer_usedregion(&keydatabuf, &r);
keystruct.datalen = r.length;
keystruct.data = r.base;
CHECK(dns_rdata_fromstruct(NULL,
keystruct.common.rdclass,
keystruct.common.rdtype,
&keystruct, &rrdatabuf));
dns_fixedname_init(&fkeyname);
keyname = dns_fixedname_name(&fkeyname);
isc_buffer_init(&namebuf, ckey->domain,
strlen(ckey->domain));
isc_buffer_add(&namebuf, strlen(ckey->domain));
CHECK(dns_name_fromtext(keyname, &namebuf,
dns_rootname, ISC_FALSE,
NULL));
CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf,
mctx, &dstkey));
CHECK(dns_keytable_add(keytable, &dstkey));
INSIST(dstkey == NULL);
return (ISC_R_SUCCESS);
cleanup:
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"configuring trusted key for '%s': "
"%s", ckey->domain,
isc_result_totext(result));
result = ISC_R_FAILURE;
if (dstkey != NULL)
dst_key_free(&dstkey);
return (result);
}
/*
* Configure DNSSEC keys for a view. Currently used only for
* the security roots.
*
* The per-view configuration values and their server-global
* defaults are are read from 'cview' and 'cctx' using
* the function 'cgetv' and 'cgets', respectively.
* The variable to be configured is '*target'.
*/
static isc_result_t
configure_view_dnsseckeys(dns_c_view_t *cview,
dns_c_ctx_t *cctx,
isc_mem_t *mctx,
isc_result_t (*cgetv)
(dns_c_view_t *, dns_c_tkeylist_t **),
isc_result_t (*cgets)
(dns_c_ctx_t *, dns_c_tkeylist_t **),
dns_keytable_t **target)
{
isc_result_t result;
dns_c_tkeylist_t *ckeys = NULL;
dns_c_tkey_t *ckey;
dns_keytable_t *keytable = NULL;
CHECK(dns_keytable_create(mctx, &keytable));
result = ISC_R_FAILURE;
if (cgetv != NULL && cview != NULL)
result = (*cgetv)(cview, &ckeys);
if (result != ISC_R_SUCCESS)
result = (*cgets)(cctx, &ckeys);
if (result == ISC_R_SUCCESS) {
for (ckey = ISC_LIST_HEAD(ckeys->tkeylist);
ckey != NULL;
ckey = ISC_LIST_NEXT(ckey, next)) {
CHECK(configure_view_dnsseckey(cview, ckey,
keytable, mctx));
}
} else if (result != ISC_R_NOTFOUND)
goto cleanup;
dns_keytable_detach(target);
*target = keytable; /* Transfer ownership. */
keytable = NULL;
result = ISC_R_SUCCESS;
cleanup:
return (result);
}
/*
* Get a dispatch appropriate for the resolver of a given view.
*/
static isc_result_t
get_view_querysource_dispatch(dns_c_ctx_t *cctx, dns_c_view_t *cview,
int af, dns_dispatch_t **dispatchp)
{
isc_result_t result;
dns_dispatch_t *disp;
isc_sockaddr_t sa;
unsigned int attrs, attrmask;
/*
* Make compiler happy.
*/
result = ISC_R_FAILURE;
switch (af) {
case AF_INET:
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getquerysource(cview, &sa);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getquerysource(cctx, &sa);
if (result != ISC_R_SUCCESS)
isc_sockaddr_any(&sa);
break;
case AF_INET6:
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getquerysourcev6(cview, &sa);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getquerysourcev6(cctx, &sa);
if (result != ISC_R_SUCCESS)
isc_sockaddr_any6(&sa);
break;
default:
INSIST(0);
}
INSIST(isc_sockaddr_pf(&sa) == af);
/*
* If we don't support this address family, we're done!
*/
switch (af) {
case AF_INET:
result = isc_net_probeipv4();
break;
case AF_INET6:
result = isc_net_probeipv6();
break;
default:
INSIST(0);
}
if (result != ISC_R_SUCCESS)
return (ISC_R_SUCCESS);
/*
* Try to find a dispatcher that we can share.
*/
attrs = 0;
attrs |= DNS_DISPATCHATTR_UDP;
switch (af) {
case AF_INET:
attrs |= DNS_DISPATCHATTR_IPV4;
break;
case AF_INET6:
attrs |= DNS_DISPATCHATTR_IPV6;
break;
}
attrmask = 0;
attrmask |= DNS_DISPATCHATTR_UDP;
attrmask |= DNS_DISPATCHATTR_TCP;
attrmask |= DNS_DISPATCHATTR_IPV4;
attrmask |= DNS_DISPATCHATTR_IPV6;
disp = NULL;
result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
ns_g_taskmgr, &sa, 4096,
1000, 32768, 16411, 16433,
attrs, attrmask, &disp);
if (result != ISC_R_SUCCESS) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"could not get query source dispatcher");
return (result);
}
*dispatchp = disp;
return (ISC_R_SUCCESS);
}
/*
* Configure 'view' according to 'cview', taking defaults from 'cctx'
* where values are missing in cview.
*
* When configuring the default view, cview will be NULL and the
* global defaults in cctx used exclusively.
*/
static isc_result_t
configure_view(dns_view_t *view, dns_c_ctx_t *cctx, dns_c_view_t *cview,
isc_mem_t *mctx, ns_aclconfctx_t *actx)
{
dns_cache_t *cache = NULL;
isc_result_t result;
isc_uint32_t cleaning_interval;
isc_uint32_t max_cache_size;
isc_uint32_t lame_ttl;
dns_tsig_keyring_t *ring;
dns_c_iplist_t *forwarders;
dns_view_t *pview = NULL; /* Production view */
isc_mem_t *cmctx;
dns_dispatch_t *dispatch4 = NULL;
dns_dispatch_t *dispatch6 = NULL;
in_port_t port;
REQUIRE(DNS_VIEW_VALID(view));
cmctx = NULL;
RWLOCK(&view->conflock, isc_rwlocktype_write);
/*
* Set the view's port number for outgoing queries.
*/
result = dns_c_ctx_getport(cctx, &port);
if (result != ISC_R_SUCCESS)
port = 53;
dns_view_setdstport(view, port);
/*
* Attach load manager to view.
*/
dns_view_setloadmgr(view, ns_g_server->loadmgr);
/*
* Configure the view's cache. Try to reuse an existing
* cache if possible, otherwise create a new cache.
* Note that the ADB is not preserved in either case.
*
* XXX Determining when it is safe to reuse a cache is
* tricky. When the view's configuration changes, the cached
* data may become invalid because it reflects our old
* view of the world. As more view attributes become
* configurable, we will have to add code here to check
* whether they have changed in ways that could
* invalidate the cache.
*/
result = dns_viewlist_find(&ns_g_server->viewlist,
view->name, view->rdclass,
&pview);
if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
goto cleanup;
if (pview != NULL) {
INSIST(pview->cache != NULL);
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3),
"reusing existing cache");
dns_cache_attach(pview->cache, &cache);
dns_view_detach(&pview);
} else {
CHECK(isc_mem_create(0, 0, &cmctx));
CHECK(dns_cache_create(cmctx, ns_g_taskmgr, ns_g_timermgr,
view->rdclass, "rbt", 0, NULL, &cache));
}
dns_view_setcache(view, cache);
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getcleaninterval(cview,
&cleaning_interval);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getcleaninterval(cctx, &cleaning_interval);
if (result != ISC_R_SUCCESS)
cleaning_interval = 3600; /* Default is 1 hour. */
dns_cache_setcleaninginterval(cache, cleaning_interval);
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getmaxcachesize(cview, &max_cache_size);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getmaxcachesize(cctx, &max_cache_size);
if (result != ISC_R_SUCCESS)
max_cache_size = 0;
/*
* XXX remove once rbt if fixed
*/
max_cache_size = 0;
dns_cache_setcachesize(cache, max_cache_size);
dns_cache_detach(&cache);
/*
* Resolver.
*
* XXXRTH Hardwired number of tasks.
*/
CHECK(get_view_querysource_dispatch(cctx, cview, AF_INET,
&dispatch4));
CHECK(get_view_querysource_dispatch(cctx, cview, AF_INET6,
&dispatch6));
CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
ns_g_socketmgr, ns_g_timermgr,
0, ns_g_dispatchmgr,
dispatch4, dispatch6));
if (dispatch4 != NULL)
dns_dispatch_detach(&dispatch4);
if (dispatch6 != NULL)
dns_dispatch_detach(&dispatch6);
/*
* Set resolver's lame-ttl.
*/
if (cview != NULL)
result = dns_c_view_getlamettl(cview, &lame_ttl);
else
result = ISC_R_NOTFOUND;
if (result == ISC_R_NOTFOUND)
result = dns_c_ctx_getlamettl(cctx, &lame_ttl);
if (result == ISC_R_NOTFOUND)
lame_ttl = 600;
if (lame_ttl > 18000)
lame_ttl = 18000;
dns_resolver_setlamettl(view->resolver, lame_ttl);
/*
* Set resolver forwarding policy.
*/
if ((cview != NULL &&
dns_c_view_getforwarders(cview, &forwarders) == ISC_R_SUCCESS) ||
(dns_c_ctx_getforwarders(cctx, &forwarders) == ISC_R_SUCCESS))
{
result = configure_forward(cctx, NULL, cview, view,
dns_rootname, forwarders);
dns_c_iplist_detach(&forwarders);
}
/*
* We have default hints for class IN if we need them.
*/
if (view->rdclass == dns_rdataclass_in && view->hints == NULL)
dns_view_sethints(view, ns_g_server->in_roothints);
/*
* If we still have no hints, this is a non-IN view with no
* "hints zone" configured. That's an error.
*/
if (view->hints == NULL) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"no root hints for view '%s'",
cview == NULL ? "<default>" : cview->name);
result = ISC_R_FAILURE;
goto cleanup;
}
/*
* Configure the view's TSIG keys.
*/
ring = NULL;
CHECK(ns_tsigkeyring_fromconfig(cview, cctx, view->mctx, &ring));
dns_view_setkeyring(view, ring);
/*
* Configure the view's peer list.
*/
{
dns_peerlist_t *newpeers = NULL;
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getpeerlist(cview, &newpeers);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getpeerlist(cctx, &newpeers);
if (result != ISC_R_SUCCESS)
result = dns_peerlist_new(mctx, &newpeers);
CHECK(result);
dns_peerlist_detach(&view->peers);
view->peers = newpeers; /* Transfer ownership. */
}
/*
* Configure the "match-clients" ACL.
*/
CHECK(configure_view_acl(cview, cctx, actx, ns_g_mctx,
dns_c_view_getmatchclients, NULL,
&view->matchclients));
/*
* Configure other configurable data.
*/
view->recursion = ISC_TRUE;
(void)dns_c_ctx_getrecursion(cctx, &view->recursion);
if (cview != NULL)
(void)dns_c_view_getrecursion(cview, &view->recursion);
view->auth_nxdomain = ISC_FALSE; /* Was true in BIND 8 */
(void)dns_c_ctx_getauthnxdomain(cctx, &view->auth_nxdomain);
if (cview != NULL)
(void)dns_c_view_getauthnxdomain(cview, &view->auth_nxdomain);
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_gettransferformat(cview,
&view->transfer_format);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_gettransferformat(cctx,
&view->transfer_format);
if (result != ISC_R_SUCCESS)
view->transfer_format = dns_many_answers;
/*
* Set sources where additional data, CNAMEs, and DNAMEs may be found.
*/
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getadditionalfromauth(cview,
&view->additionalfromauth);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getadditionalfromauth(cctx,
&view->additionalfromauth);
if (result != ISC_R_SUCCESS)
view->additionalfromauth = ISC_TRUE;
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getadditionalfromcache(cview,
&view->additionalfromcache);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getadditionalfromcache(cctx,
&view->additionalfromcache);
if (result != ISC_R_SUCCESS)
view->additionalfromcache = ISC_TRUE;
CHECK(configure_view_acl(cview, cctx, actx, ns_g_mctx,
dns_c_view_getallowquery,
dns_c_ctx_getallowquery,
&view->queryacl));
CHECK(configure_view_acl(cview, cctx, actx, ns_g_mctx,
dns_c_view_getrecursionacl,
dns_c_ctx_getallowrecursion,
&view->recursionacl));
CHECK(configure_view_acl(cview, cctx, actx, ns_g_mctx,
dns_c_view_getsortlist,
dns_c_ctx_getsortlist,
&view->sortlist));
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getrequestixfr(cview, &view->requestixfr);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getrequestixfr(cctx, &view->requestixfr);
if (result != ISC_R_SUCCESS)
view->requestixfr = ISC_TRUE;
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getprovideixfr(cview, &view->provideixfr);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getprovideixfr(cctx, &view->provideixfr);
if (result != ISC_R_SUCCESS)
view->provideixfr = ISC_TRUE;
/*
* For now, there is only one kind of trusted keys, the
* "security roots".
*/
CHECK(configure_view_dnsseckeys(cview, cctx, mctx,
dns_c_view_gettrustedkeys,
dns_c_ctx_gettrustedkeys,
&view->secroots));
{
isc_uint32_t val;
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getmaxcachettl(cview, &val);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getmaxcachettl(cctx, &val);
if (result != ISC_R_SUCCESS)
val = 7 * 24 * 3600;
view->maxcachettl = val;
}
{
isc_uint32_t val;
result = ISC_R_NOTFOUND;
if (cview != NULL)
result = dns_c_view_getmaxncachettl(cview, &val);
if (result != ISC_R_SUCCESS)
result = dns_c_ctx_getmaxncachettl(cctx, &val);
if (result != ISC_R_SUCCESS)
val = 3 * 3600;
if (val > 7 * 24 * 3600)
val = 7 * 24 * 3600;
view->maxncachettl = val;
}
{
char *cachefile = NULL, *p = NULL;
if (cview != NULL)
result = dns_c_view_getcachefile(cview, &cachefile);
else
result = dns_c_ctx_getcachefile(cctx, &cachefile);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
goto cleanup;
if (cachefile != NULL) {
p = isc_mem_strdup(view->mctx, cachefile);
if (p == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
}
if (view->cachefile != NULL)
isc_mem_free(view->mctx, view->cachefile);
view->cachefile = p;
if (view->cachefile != NULL) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
"loading cache '%s'", view->cachefile);
/* DNS_R_SEENINCLUDE should be impossible here. */
CHECK(dns_db_load(view->cachedb, view->cachefile));
}
}
result = ISC_R_SUCCESS;
cleanup:
RWUNLOCK(&view->conflock, isc_rwlocktype_write);
if (cmctx != NULL)
isc_mem_detach(&cmctx);
return (result);
}
/*
* Create the special view that handles queries under "bind. CH".
*/
static isc_result_t
create_bind_view(dns_view_t **viewp)
{
isc_result_t result;
dns_view_t *view = NULL;
REQUIRE(viewp != NULL && *viewp == NULL);
CHECK(dns_view_create(ns_g_mctx, dns_rdataclass_ch, "_bind", &view));
/* Transfer ownership. */
*viewp = view;
view = NULL;
result = ISC_R_SUCCESS;
cleanup:
if (view != NULL)
dns_view_detach(&view);
return (result);
}
/*
* Create the zone that handles queries for
* "version.bind. CH". The version string returned is that
* configured in 'cctx', or a compiled-in default if
* there is no "version" configuration option.
*/
static isc_result_t
create_version_zone(dns_c_ctx_t *cctx, dns_zonemgr_t *zmgr, dns_view_t *view)
{
isc_result_t result;
dns_db_t *db = NULL;
dns_zone_t *zone = NULL;
dns_dbversion_t *dbver = NULL;
dns_difftuple_t *tuple = NULL;
dns_diff_t diff;
char *versiontext;
unsigned char buf[256];
isc_region_t r;
size_t len;
dns_rdata_t rdata = DNS_RDATA_INIT;
static unsigned char origindata[] = "\007version\004bind";
dns_name_t origin;
dns_diff_init(ns_g_mctx, &diff);
dns_name_init(&origin, NULL);
r.base = origindata;
r.length = sizeof(origindata);
dns_name_fromregion(&origin, &r);
result = dns_c_ctx_getversion(cctx, &versiontext);
if (result != ISC_R_SUCCESS)
/*
* Removing the const qualifier from ns_g_version is ok
* because the resulting string is not modified, only
* copied into a new buffer.
*/
DE_CONST(ns_g_version, versiontext);
len = strlen(versiontext);
if (len > 255)
len = 255; /* Silently truncate. */
buf[0] = len;
memcpy(buf + 1, versiontext, len);
r.base = buf;
r.length = 1 + len;
dns_rdata_fromregion(&rdata, dns_rdataclass_ch, dns_rdatatype_txt, &r);
CHECK(dns_zone_create(&zone, ns_g_mctx));
CHECK(dns_zone_setorigin(zone, &origin));
dns_zone_settype(zone, dns_zone_master);
dns_zone_setclass(zone, dns_rdataclass_ch);
dns_zone_setview(zone, view);
CHECK(dns_zonemgr_managezone(zmgr, zone));
CHECK(dns_db_create(ns_g_mctx, "rbt", &origin, dns_dbtype_zone,
dns_rdataclass_ch, 0, NULL, &db));
CHECK(dns_db_newversion(db, &dbver));
CHECK(dns_difftuple_create(ns_g_mctx, DNS_DIFFOP_ADD, &origin,
0, &rdata, &tuple));
dns_diff_append(&diff, &tuple);
CHECK(dns_diff_apply(&diff, db, dbver));
dns_db_closeversion(db, &dbver, ISC_TRUE);
CHECK(dns_zone_replacedb(zone, db, ISC_FALSE));
CHECK(dns_view_addzone(view, zone));
result = ISC_R_SUCCESS;
cleanup:
if (zone != NULL)
dns_zone_detach(&zone);
if (dbver != NULL)
dns_db_closeversion(db, &dbver, ISC_FALSE);
if (db != NULL)
dns_db_detach(&db);
dns_diff_clear(&diff);
return (result);
}
/*
* Create the special view that handles queries for
* "authors.bind. CH". The strings returned list
* the BIND 9 authors.
*/
static isc_result_t
create_authors_zone(dns_zonemgr_t *zmgr, dns_view_t *view) {
isc_result_t result;
dns_db_t *db = NULL;
dns_zone_t *zone = NULL;
dns_dbversion_t *dbver = NULL;
dns_difftuple_t *tuple;
dns_diff_t diff;
isc_constregion_t r;
isc_constregion_t cr;
dns_rdata_t rdata = DNS_RDATA_INIT;
static const char origindata[] = "\007authors\004bind";
dns_name_t origin;
int i;
static const char *authors[] = {
"\014Mark Andrews",
"\015James Brister",
"\015Michael Graff",
"\022Andreas Gustafsson",
"\012Bob Halley",
"\016David Lawrence",
"\016Michael Sawyer",
"\020Brian Wellington",
NULL,
};
dns_diff_init(ns_g_mctx, &diff);
dns_name_init(&origin, NULL);
r.base = origindata;
r.length = sizeof(origindata);
dns_name_fromregion(&origin, (isc_region_t *)&r);
CHECK(dns_zone_create(&zone, ns_g_mctx));
CHECK(dns_zone_setorigin(zone, &origin));
dns_zone_settype(zone, dns_zone_master);
dns_zone_setclass(zone, dns_rdataclass_ch);
dns_zone_setview(zone, view);
CHECK(dns_zonemgr_managezone(zmgr, zone));
CHECK(dns_db_create(ns_g_mctx, "rbt", &origin, dns_dbtype_zone,
dns_rdataclass_ch, 0, NULL, &db));
CHECK(dns_db_newversion(db, &dbver));
for (i = 0; authors[i] != NULL; i++) {
cr.base = authors[i];
cr.length = strlen(authors[i]);
INSIST(cr.length == ((const unsigned char *)cr.base)[0] + 1U);
dns_rdata_fromregion(&rdata, dns_rdataclass_ch,
dns_rdatatype_txt, (isc_region_t *)&cr);
tuple = NULL;
CHECK(dns_difftuple_create(ns_g_mctx, DNS_DIFFOP_ADD, &origin,
0, &rdata, &tuple));
dns_diff_append(&diff, &tuple);
dns_rdata_reset(&rdata);
}
CHECK(dns_diff_apply(&diff, db, dbver));
dns_db_closeversion(db, &dbver, ISC_TRUE);
CHECK(dns_zone_replacedb(zone, db, ISC_FALSE));
CHECK(dns_view_addzone(view, zone));
result = ISC_R_SUCCESS;
cleanup:
if (zone != NULL)
dns_zone_detach(&zone);
if (dbver != NULL)
dns_db_closeversion(db, &dbver, ISC_FALSE);
if (db != NULL)
dns_db_detach(&db);
dns_diff_clear(&diff);
return (result);
}
static isc_result_t
configure_hints(dns_view_t *view, const char *filename) {
isc_result_t result;
dns_db_t *db;
db = NULL;
result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
if (result == ISC_R_SUCCESS) {
dns_view_sethints(view, db);
dns_db_detach(&db);
}
return (result);
}
static isc_result_t
configure_forward(dns_c_ctx_t *cctx, dns_c_zone_t *czone, dns_c_view_t *cview,
dns_view_t *view, dns_name_t *origin,
dns_c_iplist_t *forwarders)
{
dns_c_forw_t forward;
dns_fwdpolicy_t fwdpolicy;
isc_sockaddrlist_t addresses;
isc_sockaddr_t *sa;
isc_result_t result;
in_port_t port;
unsigned int i;
/*
* Determine which port to send forwarded requests to.
*/
if (ns_g_port != 0) {
port = ns_g_port;
} else {
result = dns_c_ctx_getport(cctx, &port);
if (result != ISC_R_SUCCESS)
port = 53;
}
ISC_LIST_INIT(addresses);
if (forwarders != NULL) {
for (i = 0; i < forwarders->nextidx; i++) {
sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t));
if (sa == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
*sa = forwarders->ips[i];
isc_sockaddr_setport(sa, port);
ISC_LINK_INIT(sa, link);
ISC_LIST_APPEND(addresses, sa, link);
}
}
if (ISC_LIST_EMPTY(addresses))
fwdpolicy = dns_fwdpolicy_none;
else
fwdpolicy = dns_fwdpolicy_first;
if ((czone != NULL &&
dns_c_zone_getforward(czone, &forward) == ISC_R_SUCCESS) ||
(cview != NULL &&
dns_c_view_getforward(cview, &forward) == ISC_R_SUCCESS) ||
dns_c_ctx_getforward(cctx, &forward) == ISC_R_SUCCESS)
{
INSIST(forward == dns_c_forw_first ||
forward == dns_c_forw_only);
if (forward == dns_c_forw_only)
fwdpolicy = dns_fwdpolicy_only;
}
result = dns_fwdtable_add(view->fwdtable, origin, &addresses,
fwdpolicy);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = ISC_R_SUCCESS;
cleanup:
while (!ISC_LIST_EMPTY(addresses)) {
sa = ISC_LIST_HEAD(addresses);
ISC_LIST_UNLINK(addresses, sa, link);
isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t));
}
return (result);
}
/*
* Find an existing view matching the name and class of 'cview'
* in 'viewlist', or create a new one and add it to the list.
*
* If 'cview' is NULL, find or create the default view.
*
* The view found or created is attached to '*viewp'.
*/
static isc_result_t
find_or_create_view(dns_c_view_t *cview, dns_viewlist_t *viewlist,
dns_view_t **viewp)
{
isc_result_t result;
const char *viewname;
dns_rdataclass_t viewclass;
dns_view_t *view = NULL;
if (cview != NULL) {
viewname = cview->name;
result = dns_c_view_getviewclass(cview, &viewclass);
if (result != ISC_R_SUCCESS)
return (result);
} else {
viewname = "_default";
viewclass = dns_rdataclass_in;
}
result = dns_viewlist_find(viewlist, viewname,
viewclass, &view);
if (result == ISC_R_SUCCESS) {
*viewp = view;
return (ISC_R_SUCCESS);
}
if (result != ISC_R_NOTFOUND)
return (result);
INSIST(view == NULL);
result = dns_view_create(ns_g_mctx, viewclass, viewname, &view);
if (result != ISC_R_SUCCESS)
return (result);
ISC_LIST_APPEND(*viewlist, view, link);
dns_view_attach(view, viewp);
return (ISC_R_SUCCESS);
}
/*
* Configure or reconfigure a zone. This callback function
* is called after parsing each "zone" statement in named.conf.
*/
static isc_result_t
configure_zone(dns_c_ctx_t *cctx, dns_c_zone_t *czone, dns_c_view_t *cview,
void *uap)
{
ns_load_t *lctx = (ns_load_t *) uap;
dns_view_t *view = NULL; /* New view */
dns_view_t *pview = NULL; /* Production view */
dns_zone_t *zone = NULL; /* New or reused zone */
dns_zone_t *dupzone = NULL;
dns_c_iplist_t *forwarders = NULL;
dns_c_forw_t tfwd;
isc_result_t result;
char *corigin;
isc_buffer_t buffer;
dns_fixedname_t fixorigin;
dns_name_t *origin;
/*
* Get the zone origin as a dns_name_t.
*/
corigin = NULL;
/* XXX casting away const */
CHECK(dns_c_zone_getname(czone, (const char **) &corigin));
isc_buffer_init(&buffer, corigin, strlen(corigin));
isc_buffer_add(&buffer, strlen(corigin));
dns_fixedname_init(&fixorigin);
CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin),
&buffer, dns_rootname, ISC_FALSE, NULL));
origin = dns_fixedname_name(&fixorigin);
/*
* Find or create the view in the new view list.
*/
view = NULL;
CHECK(find_or_create_view(cview, &lctx->viewlist, &view));
if (czone->zclass != view->rdclass) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"zone '%s': wrong class for view '%s'",
corigin, cview ? cview->name : "<default view>");
result = ISC_R_FAILURE;
goto cleanup;
}
/*
* "hints zones" aren't zones. If we've got one,
* configure it and return.
*/
if (czone->ztype == dns_c_zone_hint) {
if (czone->u.hzone.file == NULL) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"zone '%s': 'file' not specified",
corigin);
result = ISC_R_FAILURE;
goto cleanup;
}
if (dns_name_equal(origin, dns_rootname)) {
result = configure_hints(view, czone->u.hzone.file);
} else {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
"ignoring non-root hint zone '%s'",
corigin);
result = ISC_R_SUCCESS;
}
goto cleanup;
}
/*
* "forward zones" aren't zones either. Translate this syntax into
* the appropriate selective forwarding configuration and return.
*/
if (czone->ztype == dns_c_zone_forward) {
result = configure_forward(cctx, czone, cview, view, origin,
czone->u.fzone.forwarders);
goto cleanup;
}
/*
* Check for duplicates in the new zone table.
*/
result = dns_view_findzone(view, origin, &dupzone);
if (result == ISC_R_SUCCESS) {
/*
* We already have this zone!
*/
dns_zone_detach(&dupzone);
result = ISC_R_EXISTS;
goto cleanup;
}
INSIST(dupzone == NULL);
/*
* See if we can reuse an existing zone. This is
* only possible if all of these are true:
* - The zone's view exists
* - A zone with the right name exists in the view
* - The zone is compatible with the config
* options (e.g., an existing master zone cannot
* be reused if the options specify a slave zone)
*/
result = dns_viewlist_find(&ns_g_server->viewlist,
view->name, view->rdclass,
&pview);
if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
goto cleanup;
if (pview != NULL)
result = dns_view_findzone(pview, origin, &zone);
if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
goto cleanup;
if (zone != NULL) {
if (! ns_zone_reusable(zone, czone))
dns_zone_detach(&zone);
}
if (zone != NULL) {
/*
* We found a reusable zone. Make it use the
* new view.
*/
dns_zone_setview(zone, view);
} else {
/*
* We cannot reuse an existing zone, we have
* to create a new one.
*/
CHECK(dns_zone_create(&zone, lctx->mctx));
CHECK(dns_zone_setorigin(zone, origin));
dns_zone_setview(zone, view);
CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
}
/*
* If the zone contains 'forward' or 'forwarders' statements,
* configure selective forwarding.
*/
if (dns_c_zone_getforwarders(czone, &forwarders) == ISC_R_SUCCESS ||
dns_c_zone_getforward(czone, &tfwd) == ISC_R_SUCCESS)
CHECK(configure_forward(cctx, czone, cview, view,
origin, forwarders));
/*
* Configure the zone.
*/
CHECK(ns_zone_configure(cctx, cview, czone, lctx->aclconf, zone));
/*
* Add the zone to its view in the new view list.
*/
CHECK(dns_view_addzone(view, zone));
cleanup:
if (zone != NULL)
dns_zone_detach(&zone);
if (pview != NULL)
dns_view_detach(&pview);
if (view != NULL)
dns_view_detach(&view);
return (result);
}
/*
* Configure a single server quota.
*/
static void
configure_server_quota(dns_c_ctx_t *cctx,
isc_result_t (*getquota)(dns_c_ctx_t *, isc_uint32_t *),
isc_quota_t *quota, int defaultvalue)
{
isc_uint32_t val = defaultvalue;
(void)(*getquota)(cctx, &val);
quota->max = val;
}
/*
* This function is called as soon as the 'options' statement has been
* parsed.
*/
static isc_result_t
options_callback(dns_c_ctx_t *cctx, void *uap) {
isc_result_t result;
UNUSED(uap);
/*
* Change directory.
*/
if (cctx->options != NULL &&
cctx->options->directory != NULL) {
result = isc_dir_chdir(cctx->options->directory);
if (result != ISC_R_SUCCESS) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER,
ISC_LOG_ERROR, "change directory "
"to '%s' failed: %s",
cctx->options->directory,
isc_result_totext(result));
return (result);
}
}
return (ISC_R_SUCCESS);
}
static void
scan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
ns_interfacemgr_scan(server->interfacemgr, verbose);
/*
* Update the "localhost" and "localnets" ACLs to match the
* current set of network interfaces.
*/
dns_aclenv_copy(&server->aclenv,
ns_interfacemgr_getaclenv(server->interfacemgr));
}
/*
* This event callback is invoked to do periodic network
* interface scanning.
*/
static void
interface_timer_tick(isc_task_t *task, isc_event_t *event) {
ns_server_t *server = (ns_server_t *) event->ev_arg;
UNUSED(task);
isc_event_free(&event);
RWLOCK(&server->conflock, isc_rwlocktype_write);
scan_interfaces(server, ISC_FALSE);
RWUNLOCK(&server->conflock, isc_rwlocktype_write);
}
static void
heartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
ns_server_t *server = (ns_server_t *) event->ev_arg;
dns_view_t *view;
UNUSED(task);
isc_event_free(&event);
RWLOCK(&server->conflock, isc_rwlocktype_read);
view = ISC_LIST_HEAD(server->viewlist);
while (view != NULL) {
dns_view_dialup(view);
view = ISC_LIST_NEXT(view, link);
}
RWUNLOCK(&server->conflock, isc_rwlocktype_read);
}
static isc_result_t
setstatsfile(ns_server_t *server, const char *name) {
char *p;
REQUIRE(name != NULL);
p = isc_mem_strdup(server->mctx, name);
if (p == NULL)
return (ISC_R_NOMEMORY);
if (server->statsfile != NULL)
isc_mem_free(server->mctx, server->statsfile);
server->statsfile = p;
return (ISC_R_SUCCESS);
}
static isc_result_t
setdumpfile(ns_server_t *server, const char *name) {
char *p;
REQUIRE(name != NULL);
p = isc_mem_strdup(server->mctx, name);
if (p == NULL)
return (ISC_R_NOMEMORY);
if (server->dumpfile != NULL)
isc_mem_free(server->mctx, server->dumpfile);
server->dumpfile = p;
return (ISC_R_SUCCESS);
}
#define SETLIMIT(cfgvar, resource, description) \
if (dns_c_ctx_get ## cfgvar(cctx, &resource) == ISC_R_SUCCESS) { \
if (resource == DNS_C_SIZE_SPEC_DEFAULT) \
value = ns_g_init ## resource; \
else if (resource == DNS_C_SIZE_SPEC_UNLIM) \
value = ISC_RESOURCE_UNLIMITED; \
else \
value = resource; \
result = isc_resource_setlimit(isc_resource_ ## resource, \
value); \
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, \
NS_LOGMODULE_SERVER, \
result == ISC_R_SUCCESS ? \
ISC_LOG_DEBUG(1) : ISC_LOG_WARNING, \
"set maximum " description " to %" \
ISC_PRINT_QUADFORMAT "d: %s", value, \
isc_result_totext(result)); \
}
static void
set_limits(dns_c_ctx_t *cctx) {
isc_uint32_t stacksize;
isc_uint32_t datasize;
isc_uint32_t coresize;
isc_uint32_t openfiles;
isc_resourcevalue_t value;
isc_result_t result;
SETLIMIT(stacksize, stacksize, "stack size");
SETLIMIT(datasize, datasize, "data size");
SETLIMIT(coresize, coresize, "core size");
SETLIMIT(files, openfiles, "open files");
}
static isc_result_t
load_configuration(const char *filename, ns_server_t *server,
isc_boolean_t first_time)
{
isc_result_t result;
ns_load_t lctx;
dns_c_cbks_t callbacks;
dns_c_ctx_t *cctx;
dns_view_t *view = NULL;
dns_view_t *view_next;
dns_viewlist_t tmpviewlist;
ns_aclconfctx_t aclconfctx;
dns_dispatch_t *dispatchv4 = NULL;
dns_dispatch_t *dispatchv6 = NULL;
char *pidfilename;
char *statsfilename;
char *dumpfilename;
isc_uint32_t interface_interval;
isc_uint32_t heartbeat_interval;
in_port_t listen_port;
ns_aclconfctx_init(&aclconfctx);
RWLOCK(&server->conflock, isc_rwlocktype_write);
dns_zonemgr_lockconf(server->zonemgr, isc_rwlocktype_write);
lctx.mctx = ns_g_mctx;
lctx.aclconf = &aclconfctx;
ISC_LIST_INIT(lctx.viewlist);
callbacks.zonecbk = configure_zone;
callbacks.zonecbkuap = &lctx;
callbacks.optscbk = options_callback;
callbacks.optscbkuap = NULL;
/*
* Parse the configuration file creating a parse tree. Any
* 'zone' statements are handled immediately by calling
* configure_zone() through 'callbacks'.
*/
cctx = NULL;
if (ns_g_lwresdonly && lwresd_g_useresolvconf)
result = ISC_R_FILENOTFOUND;
else {
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO, "loading configuration from '%s'",
filename);
result = dns_c_parse_namedconf(filename, ns_g_mctx, &cctx,
&callbacks);
}
if (result == ISC_R_FILENOTFOUND &&
ns_g_lwresdonly && !ns_g_conffileset)
{
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO, "loading configuration from '%s'",
lwresd_g_resolvconffile);
result = ns_lwresd_parseresolvconf(ns_g_mctx, &cctx);
}
CHECK(result);
/*
* Set process limits, which (usually) needs to be done as root.
*/
set_limits(cctx);
/*
* Configure various server options.
*/
configure_server_quota(cctx, dns_c_ctx_gettransfersout,
&server->xfroutquota, 10);
configure_server_quota(cctx, dns_c_ctx_gettcpclients,
&server->tcpquota, 100);
configure_server_quota(cctx, dns_c_ctx_getrecursiveclients,
&server->recursionquota, 1000);
CHECK(configure_view_acl(NULL, cctx, &aclconfctx, ns_g_mctx, NULL,
dns_c_ctx_getblackhole,
&server->blackholeacl));
if (server->blackholeacl != NULL)
dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
server->blackholeacl);
/* dns_loadmgr_setlimit(server->loadmgr, 20); XXXMPA */
/*
* Configure the zone manager.
*/
{
isc_uint32_t transfersin = 10;
(void)dns_c_ctx_gettransfersin(cctx, &transfersin);
dns_zonemgr_settransfersin(server->zonemgr, transfersin);
}
{
isc_uint32_t transfersperns = 2;
(void)dns_c_ctx_gettransfersperns(cctx, &transfersperns);
dns_zonemgr_settransfersperns(server->zonemgr, transfersperns);
}
/*
* Determine which port to use for listening for incoming connections.
*/
if (ns_g_port != 0) {
listen_port = ns_g_port;
} else {
result = dns_c_ctx_getport(cctx, &listen_port);
if (result != ISC_R_SUCCESS)
listen_port = 53;
}
/*
* Configure the interface manager according to the "listen-on"
* statement.
*/
{
dns_c_lstnlist_t *clistenon = NULL;
ns_listenlist_t *listenon = NULL;
(void)dns_c_ctx_getlistenlist(cctx, &clistenon);
if (clistenon != NULL) {
result = ns_listenlist_fromconfig(clistenon,
cctx,
&aclconfctx,
ns_g_mctx,
&listenon);
} else if (!ns_g_lwresdonly) {
/*
* Not specified, use default.
*/
CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
ISC_TRUE, &listenon));
}
if (listenon != NULL) {
ns_interfacemgr_setlistenon4(server->interfacemgr,
listenon);
ns_listenlist_detach(&listenon);
}
}
/*
* Ditto for IPv6.
*/
{
dns_c_lstnlist_t *clistenon = NULL;
ns_listenlist_t *listenon = NULL;
(void)dns_c_ctx_getv6listenlist(cctx, &clistenon);
if (clistenon != NULL) {
result = ns_listenlist_fromconfig(clistenon,
cctx,
&aclconfctx,
ns_g_mctx,
&listenon);
} else if (!ns_g_lwresdonly) {
/*
* Not specified, use default.
*/
CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
ISC_FALSE, &listenon));
}
if (listenon != NULL) {
ns_interfacemgr_setlistenon6(server->interfacemgr,
listenon);
ns_listenlist_detach(&listenon);
}
}
/*
* Rescan the interface list to pick up changes in the
* listen-on option. It's important that we do this before we try
* to configure the query source, since the dispatcher we use might
* be shared with an interface.
*/
scan_interfaces(server, ISC_TRUE);
/*
* Arrange for further interface scanning to occur periodically
* as specified by the "interface-interval" option.
*/
interface_interval = 3600; /* Default is 1 hour. */
(void)dns_c_ctx_getinterfaceinterval(cctx, &interface_interval);
if (interface_interval == 0) {
isc_timer_reset(server->interface_timer,
isc_timertype_inactive,
NULL, NULL, ISC_TRUE);
} else {
isc_interval_t interval;
isc_interval_set(&interval, interface_interval, 0);
isc_timer_reset(server->interface_timer, isc_timertype_ticker,
NULL, &interval, ISC_FALSE);
}
/*
* Configure the dialup heartbeat timer.
*/
heartbeat_interval = 3600; /* Default is 1 hour. */
(void)dns_c_ctx_getheartbeatinterval(cctx, &heartbeat_interval);
if (heartbeat_interval == 0) {
isc_timer_reset(server->heartbeat_timer,
isc_timertype_inactive,
NULL, NULL, ISC_TRUE);
} else {
isc_interval_t interval;
isc_interval_set(&interval, heartbeat_interval, 0);
isc_timer_reset(server->heartbeat_timer, isc_timertype_ticker,
NULL, &interval, ISC_FALSE);
}
/*
* Configure and freeze all explicit views. Explicit
* views that have zones were already created at parsing
* time, but views with no zones must be created here.
*/
if (cctx->views != NULL) {
dns_c_view_t *cview;
for (cview = ISC_LIST_HEAD(cctx->views->views);
cview != NULL;
cview = ISC_LIST_NEXT(cview, next))
{
view = NULL;
CHECK(find_or_create_view(cview,
&lctx.viewlist, &view));
INSIST(view != NULL);
CHECK(configure_view(view, cctx, cview, ns_g_mctx,
&aclconfctx));
dns_view_freeze(view);
dns_view_detach(&view);
}
}
INSIST(view == NULL);
/*
* Make sure we have a default view if and only if there
* were no explicit views.
*/
if (cctx->views == NULL || ISC_LIST_EMPTY(cctx->views->views)) {
/*
* No explicit views; there ought to be a default view.
* There may already be one created as a side effect
* of zone statements, or we may have to create one.
* In either case, we need to configure and freeze it.
*/
CHECK(find_or_create_view(NULL, &lctx.viewlist, &view));
CHECK(configure_view(view, cctx, NULL,
ns_g_mctx, &aclconfctx));
dns_view_freeze(view);
dns_view_detach(&view);
} else {
/*
* There are explicit views. There should not be
* a default view. If there is one, complain.
*/
result = dns_viewlist_find(&lctx.viewlist, "_default",
dns_rdataclass_in, &view);
if (result == ISC_R_SUCCESS) {
dns_view_detach(&view);
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"when using 'view' statements, "
"all zones must be in views");
result = ISC_R_FAILURE;
goto cleanup;
}
}
/*
* Create (or recreate) the internal _bind view.
*/
CHECK(create_bind_view(&view));
ISC_LIST_APPEND(lctx.viewlist, view, link);
CHECK(create_version_zone(cctx, server->zonemgr, view));
CHECK(create_authors_zone(server->zonemgr, view));
dns_view_freeze(view);
view = NULL;
/*
* Swap our new view list with the production one.
*/
tmpviewlist = server->viewlist;
server->viewlist = lctx.viewlist;
lctx.viewlist = tmpviewlist;
/*
* Load the TKEY information from the configuration.
*/
{
dns_tkeyctx_t *t = NULL;
CHECKM(ns_tkeyctx_fromconfig(cctx, ns_g_mctx, ns_g_entropy,
&t),
"configuring TKEY");
if (server->tkeyctx != NULL)
dns_tkeyctx_destroy(&server->tkeyctx);
server->tkeyctx = t;
}
/*
* Bind the OMAPI port(s).
*/
CHECKM(ns_omapi_configure(ns_g_mctx, cctx, &aclconfctx),
"binding control channel(s)");
/*
* Bind the lwresd port(s).
*/
CHECKM(ns_lwresd_configure(ns_g_mctx, cctx),
"binding lightweight resolver ports");
/*
* Relinquish root privileges.
*/
if (first_time)
ns_os_changeuser();
/*
* Configure the logging system.
*
* Do this after changing UID to make sure that any log
* files specified in named.conf get created by the
* unprivileged user, not root.
*/
if (ns_g_logstderr) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
"ignoring config file logging "
"statement due to -g option");
} else {
dns_c_logginglist_t *clog = NULL;
isc_logconfig_t *logc = NULL;
CHECKM(isc_logconfig_create(ns_g_lctx, &logc),
"creating new logging configuration");
(void)dns_c_ctx_getlogging(cctx, &clog);
if (clog != NULL) {
CHECKM(ns_log_configure(logc, clog),
"configuring logging");
} else {
CHECKM(ns_log_setdefaultchannels(logc),
"setting up default logging channels");
CHECKM(ns_log_setdefaultcategory(logc),
"setting up default 'category default'");
}
result = isc_logconfig_use(ns_g_lctx, logc);
if (result != ISC_R_SUCCESS) {
isc_logconfig_destroy(&logc);
CHECKM(result, "installing logging configuration");
}
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
"now using logging configuration from "
"config file");
}
/*
* Set the default value of the query logging flag depending
* whether a "queries" category has been defined. This is
* a disgusting hack, but we need to do this for BIND 8
* compatibility.
*/
if (first_time) {
dns_c_logginglist_t *clog = NULL;
dns_c_logcat_t *ccat;
(void)dns_c_ctx_getlogging(cctx, &clog);
for (ccat = ISC_LIST_HEAD(clog->categories);
ccat != NULL;
ccat = ISC_LIST_NEXT(ccat, next)) {
if (strcmp(ccat->catname, "queries") == 0)
server->log_queries = ISC_TRUE;
}
}
if (dns_c_ctx_getpidfilename(cctx, &pidfilename) != ISC_R_NOTFOUND)
ns_os_writepidfile(pidfilename);
else if (ns_g_lwresdonly)
ns_os_writepidfile(lwresd_g_defaultpidfile);
else
ns_os_writepidfile(ns_g_defaultpidfile);
result = dns_c_ctx_getstatsfilename(cctx, &statsfilename);
if (result == ISC_R_NOTFOUND) {
CHECKM(setstatsfile(server, "named.stats"), "strdup");
} else {
CHECKM(setstatsfile(server, statsfilename), "strdup");
}
result = dns_c_ctx_getdumpfilename(cctx, &dumpfilename);
if (result == ISC_R_NOTFOUND) {
CHECKM(setdumpfile(server, "named_dump.db"), "strdup");
} else {
CHECKM(setdumpfile(server, dumpfilename), "strdup");
}
cleanup:
ns_aclconfctx_destroy(&aclconfctx);
if (cctx != NULL)
dns_c_ctx_delete(&cctx);
if (view != NULL)
dns_view_detach(&view);
/*
* This cleans up either the old production view list
* or our temporary list depending on whether they
* were swapped above or not.
*/
for (view = ISC_LIST_HEAD(lctx.viewlist);
view != NULL;
view = view_next) {
view_next = ISC_LIST_NEXT(view, link);
ISC_LIST_UNLINK(lctx.viewlist, view, link);
dns_view_detach(&view);
}
if (dispatchv4 != NULL)
dns_dispatch_detach(&dispatchv4);
if (dispatchv6 != NULL)
dns_dispatch_detach(&dispatchv6);
dns_zonemgr_unlockconf(server->zonemgr, isc_rwlocktype_write);
RWUNLOCK(&server->conflock, isc_rwlocktype_write);
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_DEBUG(1), "load_configuration: %s",
isc_result_totext(result));
return (result);
}
static isc_result_t
load_zones(ns_server_t *server, isc_boolean_t stop) {
isc_result_t result;
dns_view_t *view;
dns_zonemgr_lockconf(server->zonemgr, isc_rwlocktype_read);
/*
* Load zone data from disk.
*/
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link))
{
CHECK(dns_view_load(view, stop));
}
/*
* Force zone maintenance. Do this after loading
* so that we know when we need to force AXFR of
* slave zones whose master files are missing.
*/
CHECK(dns_zonemgr_forcemaint(server->zonemgr));
cleanup:
dns_zonemgr_unlockconf(server->zonemgr, isc_rwlocktype_read);
return (result);
}
static void
run_server(isc_task_t *task, isc_event_t *event) {
isc_result_t result;
ns_server_t *server = (ns_server_t *)event->ev_arg;
UNUSED(task);
isc_event_free(&event);
CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy,
&ns_g_dispatchmgr),
"creating dispatch manager");
CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr,
ns_g_socketmgr, ns_g_dispatchmgr,
&server->interfacemgr),
"creating interface manager");
CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
NULL, NULL, server->task,
interface_timer_tick,
server, &server->interface_timer),
"creating interface timer");
CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
NULL, NULL, server->task,
heartbeat_timer_tick,
server, &server->heartbeat_timer),
"creating heartbeat timer");
if (ns_g_lwresdonly)
CHECKFATAL(load_configuration(lwresd_g_conffile, server,
ISC_TRUE),
"loading configuration");
else
CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE),
"loading configuration");
CHECKFATAL(load_zones(server, ISC_FALSE),
"loading zones");
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO, "running");
}
void
ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) {
REQUIRE(NS_SERVER_VALID(server));
server->flushonshutdown = flush;
}
static void
shutdown_server(isc_task_t *task, isc_event_t *event) {
dns_view_t *view, *view_next;
ns_server_t *server = (ns_server_t *)event->ev_arg;
isc_boolean_t flush = server->flushonshutdown;
UNUSED(task);
RWLOCK(&server->conflock, isc_rwlocktype_write);
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_INFO, "shutting down%s",
flush ? ": flushing changes" : "");
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = view_next) {
view_next = ISC_LIST_NEXT(view, link);
ISC_LIST_UNLINK(server->viewlist, view, link);
if (flush)
dns_view_flushanddetach(&view);
else
dns_view_detach(&view);
}
isc_timer_detach(&server->interface_timer);
isc_timer_detach(&server->heartbeat_timer);
ns_interfacemgr_shutdown(server->interfacemgr);
ns_interfacemgr_detach(&server->interfacemgr);
dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
dns_zonemgr_shutdown(server->zonemgr);
if (server->blackholeacl != NULL)
dns_acl_detach(&server->blackholeacl);
isc_task_detach(&server->task);
isc_event_free(&event);
RWUNLOCK(&server->conflock, isc_rwlocktype_write);
}
void
ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
isc_result_t result;
ns_server_t *server = isc_mem_get(mctx, sizeof(*server));
if (server == NULL)
fatal("allocating server object", ISC_R_NOMEMORY);
server->mctx = mctx;
server->task = NULL;
CHECKFATAL(isc_rwlock_init(&server->conflock, 1, 1),
"initializing server configuration lock");
/* Initialize configuration data with default values. */
result = isc_quota_init(&server->xfroutquota, 10);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
result = isc_quota_init(&server->tcpquota, 10);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
result = isc_quota_init(&server->recursionquota, 100);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
result = dns_aclenv_init(mctx, &server->aclenv);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
/* Initialize server data structures. */
server->loadmgr = NULL;
server->zonemgr = NULL;
server->interfacemgr = NULL;
ISC_LIST_INIT(server->viewlist);
server->in_roothints = NULL;
server->blackholeacl = NULL;
CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
&server->in_roothints),
"setting up root hints");
CHECKFATAL(isc_mutex_init(&server->reload_event_lock),
"initializing reload event lock");
server->reload_event =
isc_event_allocate(ns_g_mctx, server,
NS_EVENT_RELOAD,
ns_server_reload,
server,
sizeof(isc_event_t));
CHECKFATAL(server->reload_event == NULL ?
ISC_R_NOMEMORY : ISC_R_SUCCESS,
"allocating reload event");
CHECKFATAL(dst_lib_init(ns_g_mctx, ns_g_entropy, ISC_ENTROPY_GOODONLY),
"initializing DST");
server->tkeyctx = NULL;
CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
&server->tkeyctx),
"creating TKEY context");
/*
* Setup the server task, which is responsible for coordinating
* startup and shutdown of the server.
*/
CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
"creating server task");
isc_task_setname(server->task, "server", server);
CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
"isc_task_onshutdown");
CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),
"isc_app_onrun");
server->interface_timer = NULL;
server->heartbeat_timer = NULL;
CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr,
ns_g_socketmgr, &server->zonemgr),
"dns_zonemgr_create");
CHECKFATAL(dns_loadmgr_create(ns_g_mctx, &server->loadmgr),
"dns_loadmgr_create");
server->statsfile = isc_mem_strdup(server->mctx, "named.stats");
CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
"isc_mem_strdup");
server->querystats = NULL;
CHECKFATAL(dns_stats_alloccounters(ns_g_mctx, &server->querystats),
"dns_stats_alloccounters");
server->dumpfile = isc_mem_strdup(server->mctx, "named.dump");
CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
"isc_mem_strdup");
server->flushonshutdown = ISC_FALSE;
server->log_queries = ISC_FALSE;
server->magic = NS_SERVER_MAGIC;
*serverp = server;
}
void
ns_server_destroy(ns_server_t **serverp) {
ns_server_t *server = *serverp;
REQUIRE(NS_SERVER_VALID(server));
dns_stats_freecounters(server->mctx, &server->querystats);
isc_mem_free(server->mctx, server->statsfile);
isc_mem_free(server->mctx, server->dumpfile);
dns_loadmgr_detach(&server->loadmgr);
dns_zonemgr_detach(&server->zonemgr);
if (server->tkeyctx != NULL)
dns_tkeyctx_destroy(&server->tkeyctx);
dst_lib_destroy();
isc_event_free(&server->reload_event);
INSIST(ISC_LIST_EMPTY(server->viewlist));
dns_db_detach(&server->in_roothints);
dns_aclenv_destroy(&server->aclenv);
isc_quota_destroy(&server->recursionquota);
isc_quota_destroy(&server->tcpquota);
isc_quota_destroy(&server->xfroutquota);
isc_rwlock_destroy(&server->conflock);
server->magic = 0;
isc_mem_put(server->mctx, server, sizeof(*server));
}
static void
fatal(const char *msg, isc_result_t result) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_CRITICAL, "%s: %s", msg,
isc_result_totext(result));
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_CRITICAL, "exiting (due to fatal error)");
exit(1);
}
static void
ns_server_reload(isc_task_t *task, isc_event_t *event) {
isc_result_t result;
ns_server_t *server = (ns_server_t *)event->ev_arg;
UNUSED(task);
if (ns_g_lwresdonly)
result = load_configuration(lwresd_g_conffile, server,
ISC_FALSE);
else
result = load_configuration(ns_g_conffile, server, ISC_FALSE);
if (result != ISC_R_SUCCESS) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"reloading configuration failed: %s",
isc_result_totext(result));
}
result = load_zones(server, ISC_FALSE);
if (result != ISC_R_SUCCESS) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"reloading zones failed: %s",
isc_result_totext(result));
}
LOCK(&server->reload_event_lock);
INSIST(server->reload_event == NULL);
server->reload_event = event;
UNLOCK(&server->reload_event_lock);
}
void
ns_server_reloadwanted(ns_server_t *server) {
LOCK(&server->reload_event_lock);
if (server->reload_event != NULL)
isc_task_send(server->task, &server->reload_event);
UNLOCK(&server->reload_event_lock);
}
static char *
next_token(char **stringp, const char *delim) {
char *res;
do {
res = strsep(stringp, delim);
if (res == NULL)
break;
} while (*res == '\0');
return (res);
}
/*
* Find the zone specified in the control channel command 'args',
* if any. If a zone is specified, point '*zonep' at it, otherwise
* set '*zonep' to NULL.
*/
static isc_result_t
zone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep) {
char *input, *ptr;
const char *zonetxt;
char *classtxt;
const char *viewtxt = NULL;
dns_fixedname_t name;
isc_result_t result;
isc_buffer_t buf;
dns_view_t *view = NULL;
dns_rdataclass_t rdclass;
REQUIRE(zonep != NULL && *zonep == NULL);
input = args;
/* Skip the command name. */
ptr = next_token(&input, " \t");
if (ptr == NULL)
return (ISC_R_UNEXPECTEDEND);
/* Look for the zone name. */
zonetxt = next_token(&input, " \t");
if (zonetxt == NULL) {
*zonep = NULL;
return (ISC_R_SUCCESS);
}
/* Look for the optional class name. */
classtxt = next_token(&input, " \t");
if (classtxt != NULL) {
/* Look for the optional view name. */
viewtxt = next_token(&input, " \t");
}
isc_buffer_init(&buf, zonetxt, strlen(zonetxt));
isc_buffer_add(&buf, strlen(zonetxt));
dns_fixedname_init(&name);
result = dns_name_fromtext(dns_fixedname_name(&name),
&buf, dns_rootname, ISC_FALSE, NULL);
if (result != ISC_R_SUCCESS)
goto fail1;
if (classtxt != NULL) {
isc_textregion_t r;
r.base = classtxt;
r.length = strlen(classtxt);
result = dns_rdataclass_fromtext(&rdclass, &r);
if (result != ISC_R_SUCCESS)
goto fail1;
} else {
rdclass = dns_rdataclass_in;
}
if (viewtxt == NULL)
viewtxt = "_default";
result = dns_viewlist_find(&server->viewlist, viewtxt,
rdclass, &view);
if (result != ISC_R_SUCCESS)
goto fail1;
result = dns_zt_find(view->zonetable, dns_fixedname_name(&name),
0, NULL, zonep);
if (result != ISC_R_SUCCESS)
goto fail2;
fail2:
dns_view_detach(&view);
fail1:
return (result);
}
/*
* Act on a "reload" command from the command channel.
*/
isc_result_t
ns_server_reloadcommand(ns_server_t *server, char *args) {
isc_result_t result;
dns_zone_t *zone = NULL;
dns_zonetype_t type;
UNUSED(server);
result = zone_from_args(server, args, &zone);
if (result != ISC_R_SUCCESS)
return (result);
if (zone == NULL) {
ns_server_reloadwanted(server);
} else {
type = dns_zone_gettype(zone);
if (type == dns_zone_slave || type == dns_zone_stub)
dns_zone_refresh(zone);
else
dns_zone_load(zone);
dns_zone_detach(&zone);
}
return (ISC_R_SUCCESS);
}
/*
* Act on a "refresh" command from the command channel.
*/
isc_result_t
ns_server_refreshcommand(ns_server_t *server, char *args) {
isc_result_t result;
dns_zone_t *zone = NULL;
result = zone_from_args(server, args, &zone);
if (result != ISC_R_SUCCESS)
return (result);
if (zone == NULL)
return (ISC_R_UNEXPECTEDEND);
dns_zone_refresh(zone);
dns_zone_detach(&zone);
return (ISC_R_SUCCESS);
}
isc_result_t
ns_server_togglequerylog(ns_server_t *server) {
server->log_queries = server->log_queries ? ISC_FALSE : ISC_TRUE;
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
"query logging is now %s",
server->log_queries ? "on" : "off");
return (ISC_R_SUCCESS);
}
static isc_result_t
ns_listenlist_fromconfig(dns_c_lstnlist_t *clist, dns_c_ctx_t *cctx,
ns_aclconfctx_t *actx,
isc_mem_t *mctx, ns_listenlist_t **target)
{
dns_c_lstnon_t *ce;
isc_result_t result;
ns_listenlist_t *dlist = NULL;
REQUIRE(target != NULL && *target == NULL);
result = ns_listenlist_create(mctx, &dlist);
if (result != ISC_R_SUCCESS)
return (result);
for (ce = ISC_LIST_HEAD(clist->elements);
ce != NULL;
ce = ISC_LIST_NEXT(ce, next))
{
ns_listenelt_t *delt = NULL;
result = ns_listenelt_fromconfig(ce, cctx, actx, mctx, &delt);
if (result != ISC_R_SUCCESS)
goto cleanup;
ISC_LIST_APPEND(dlist->elts, delt, link);
}
*target = dlist;
return (ISC_R_SUCCESS);
cleanup:
ns_listenlist_detach(&dlist);
return (result);
}
/*
* Create a listen list from the corresponding configuration
* data structure.
*/
static isc_result_t
ns_listenelt_fromconfig(dns_c_lstnon_t *celt, dns_c_ctx_t *cctx,
ns_aclconfctx_t *actx,
isc_mem_t *mctx, ns_listenelt_t **target)
{
isc_result_t result;
ns_listenelt_t *delt = NULL;
REQUIRE(target != NULL && *target == NULL);
result = ns_listenelt_create(mctx, celt->port, NULL, &delt);
if (result != ISC_R_SUCCESS)
return (result);
result = ns_acl_fromconfig(celt->iml, cctx, actx, mctx, &delt->acl);
if (result != ISC_R_SUCCESS) {
ns_listenelt_destroy(delt);
return (result);
}
*target = delt;
return (ISC_R_SUCCESS);
}
isc_result_t
ns_server_dumpstats(ns_server_t *server) {
isc_result_t result;
dns_zone_t *zone, *next;
isc_stdtime_t now;
FILE *fp = NULL;
int i;
int ncounters;
isc_stdtime_get(&now);
CHECKM(isc_stdio_open(server->statsfile, "a", &fp),
"could not open statistics dump file");
ncounters = dns_stats_ncounters();
fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now);
for (i = 0; i < ncounters; i++)
fprintf(fp, "%s %" ISC_PRINT_QUADFORMAT "d\n",
dns_statscounter_names[i],
server->querystats[i]);
dns_zonemgr_lockconf(server->zonemgr, isc_rwlocktype_read);
zone = NULL;
for (result = dns_zone_first(server->zonemgr, &zone);
result == ISC_R_SUCCESS;
next = NULL, result = dns_zone_next(zone, &next), zone = next)
{
isc_uint64_t *zonestats = dns_zone_getstatscounters(zone);
if (zonestats != NULL) {
char zonename[DNS_NAME_FORMATSIZE];
dns_view_t *view;
char *viewname;
dns_name_format(dns_zone_getorigin(zone),
zonename, sizeof(zonename));
view = dns_zone_getview(zone);
viewname = view->name;
for (i = 0; i < ncounters; i++) {
fprintf(fp, "%s %" ISC_PRINT_QUADFORMAT
"d %s",
dns_statscounter_names[i],
zonestats[i],
zonename);
if (strcmp(viewname, "_default") != 0)
fprintf(fp, " %s", viewname);
fprintf(fp, "\n");
}
}
}
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
CHECK(result);
fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now);
dns_zonemgr_unlockconf(server->zonemgr, isc_rwlocktype_read);
cleanup:
if (fp != NULL)
(void)isc_stdio_close(fp);
return (result);
}
isc_result_t
ns_server_dumpdb(ns_server_t *server) {
FILE *fp = NULL;
dns_view_t *view;
isc_result_t result;
CHECKM(isc_stdio_open(server->dumpfile, "w", &fp),
"could not open dump file");
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link))
{
if (view->cachedb != NULL)
CHECKM(dns_view_dumpdbtostream(view, fp),
"could not dump view databases");
}
cleanup:
if (fp != NULL)
(void)isc_stdio_close(fp);
return (result);
}