server.c revision 1a69a1a78cfaa86f3b68bbc965232b7876d4da2a
/*
* 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.
*/
#include <config.h>
#include <stdlib.h>
#include <dns/confparser.h>
#include <dns/dispatch.h>
#include <dns/keytable.h>
#include <dns/rdatastruct.h>
#include <dns/resolver.h>
#include <dns/tkeyconf.h>
#include <dns/tsigconf.h>
#include <dns/zoneconf.h>
#include <named/interfacemgr.h>
/*
* Check an operation for failure. Assumes that the function
* using it has a 'result' variable and a 'cleanup' label.
*/
} while (0)
if (result != ISC_R_SUCCESS) { \
"%s: %s", msg, \
isc_result_totext(result)); \
goto cleanup; \
} \
} while (0) \
if (result != ISC_R_SUCCESS) \
} while (0) \
typedef struct {
} ns_load_t;
static isc_result_t
static isc_result_t
/*
* 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
(dns_c_view_t *, dns_c_ipmatchlist_t **),
(dns_c_ctx_t *, dns_c_ipmatchlist_t **),
{
/* No value available. *aclp == NULL. */
return (ISC_R_SUCCESS);
}
return (result);
}
/*
* Convert a null-terminated string of base64 text into
* binary, storing it in a buffer.
* 'mctx' is only used internally.
*/
static isc_result_t
{
if (isopen)
(void) isc_lex_close(lex);
return (result);
}
/*
* Configure the trusted keys or security roots of a view.
* The configuration values are read from 'cctx' and 'cview' using
* the function 'cget'. The variable to be configured is '*target'.
* XXX not really view specific yet
*/
static isc_result_t
isc_result_t (*cget)
(dns_c_ctx_t *, dns_c_tkeylist_t **),
{
if (result == ISC_R_SUCCESS) {
{
unsigned char keydata[4096];
unsigned char rrdata[4096];
isc_region_t r;
else
&viewclass));
/*
* The key data in keystruct is not
* dynamically allocated.
*/
&keydatabuf));
isc_buffer_usedregion(&keydatabuf, &r);
&dstkey));
}
} else if (result != ISC_R_NOTFOUND)
goto cleanup;
return (result);
}
/*
* Configure 'view' according to 'cview', taking defaults from 'cctx'
* where values are missing in cctx.
*
* When configuring the default view, cctx will be NULL and the
* glboal defaults in cview used exclusively.
*/
static isc_result_t
{
unsigned int i;
/*
* 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.
*/
&pview);
goto cleanup;
} else {
}
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS)
/*
* XXXRTH Temporary support for loading cache contents.
*/
if (ns_g_cachefile != NULL) {
}
/*
* Resolver.
*
* XXXRTH Hardwired number of tasks.
*/
0, dispatchv4, dispatchv6));
/*
* Set resolver forwarding policy.
*/
{
/*
* Ugh. Convert between list formats.
*/
for (i = 0; i < forwarders->nextidx; i++) {
goto cleanup;
}
}
/*
* XXXRTH The configuration type 'dns_c_forw_t' should be
* elminated.
*/
forward == dns_c_forw_only);
if (forward == dns_c_forw_only)
}
}
/*
* We have default hints for class IN if we need them.
*/
/*
* If we still have no hints, this is a non-IN view with no
* "hints zone" configured. That's an error.
*/
"no root hints for view '%s'",
goto cleanup;
}
/*
* Configure the view's TSIG keys.
*/
/*
* Configure the view's peer list.
*/
{
} else {
}
}
/*
* Configure the "match-clients" ACL.
*/
&view->matchclients));
/*
* Configure other configurable data.
*/
(void) dns_c_view_gettransferformat(cview,
&view->transfer_format);
&view->recursionacl));
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS)
/*
* For now, there is only one kind of trusted keys, the
* "security roots".
*/
}
return (result);
}
/*
* Create the special view 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
char *versiontext;
unsigned char buf[256];
isc_region_t r;
static unsigned char origindata[] = "\007version\004bind";
r.base = origindata;
r.length = sizeof(origindata);
dns_name_fromregion(&origin, &r);
if (versiontext == NULL)
if (len > 255)
&view));
/* Transfer ownership. */
dns_db_detach(&db);
return (result);
}
static isc_result_t
if (result == ISC_R_SUCCESS) {
dns_db_detach(&db);
}
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
dns_view_t **viewp)
{
char *viewname;
if (result != ISC_R_SUCCESS)
return (result);
} else {
viewname = "_default";
}
if (result == ISC_R_SUCCESS) {
return (ISC_R_SUCCESS);
}
if (result != ISC_R_NOTFOUND)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
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
void *uap)
{
char *corigin;
/*
* Get the zone origin as a dns_name_t.
*/
/* XXX casting away const */
/*
* Find or create the view in the new view list.
*/
"zone '%s': wrong class for view '%s'",
goto cleanup;
}
/*
* Master zones must have 'file' set.
*/
"zone '%s': 'file' not specified",
corigin);
goto cleanup;
}
/*
* "hints zones" aren't zones. If we've got one,
* configure it and return.
*/
"zone '%s': 'file' not specified",
corigin);
goto cleanup;
}
} else {
"ignoring non-root hint zone '%s'",
corigin);
}
goto cleanup;
}
/*
* "stub zones" aren't zones either. Eventually we'll
* create a "cache freshener" to keep the stub data in the
* cache.
*/
"stub zone '%s': stub zones are not supported in this release",
corigin);
goto cleanup;
}
/*
* "forward zones" aren't zones either. Eventually we'll
* translate this syntax into the appropriate selective forwarding
* configuration.
*/
"forward zone '%s': forward zones are not supported in this release",
corigin);
goto cleanup;
}
/*
* Check for duplicates in the new zone table.
*/
if (result == ISC_R_SUCCESS) {
/*
* We already have this zone!
*/
goto cleanup;
}
/*
* 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)
*/
&pview);
goto cleanup;
goto cleanup;
}
/*
* If we cannot reuse an existing zone, we will have to
* create a new one.
*/
zone));
}
/*
* Configure the zone.
*/
/*
* Add the zone to its view in the new view list.
*/
return (result);
}
/*
* Configure a single server quota.
*/
static void
{
}
static isc_result_t
dns_dispatch_t **dispatchp) {
/*
* Make compiler happy.
*/
switch (af) {
case AF_INET:
break;
case AF_INET6:
break;
default:
INSIST(0);
}
if (result != ISC_R_SUCCESS)
/*
* 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);
/*
* The query source is fully wild. No special dispatcher
* work needs to be done.
*/
return (ISC_R_SUCCESS);
}
/*
* If the interface manager has a dispatcher for this address,
* use it.
*/
switch (af) {
case AF_INET:
break;
case AF_INET6:
break;
default:
INSIST(0);
}
dispatchp) !=
/*
* The interface manager doesn't have a matching dispatcher.
*/
if (*server_dispatchp != NULL) {
/*
* We've already got a custom dispatcher. If it is
* compatible with the new configuration, use it.
*/
&sa)) {
return (ISC_R_SUCCESS);
}
/*
* The existing custom dispatcher is not compatible.
* We don't need it anymore.
*/
}
/*
* Create a custom dispatcher.
*/
&socket);
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* Regardless of whether dns_dispatch_create() succeeded or
* failed, we don't need to keep the reference to the socket.
*/
if (result != ISC_R_SUCCESS)
return (result);
} else {
/*
* We're sharing a UDP dispatcher with the interface manager
* now. Any prior custom dispatcher can be discarded.
*/
if (*server_dispatchp != NULL)
}
return (ISC_R_SUCCESS);
}
/*
* This function is called as soon as the 'options' statement has been
* parsed.
*/
static isc_result_t
/*
* Change directory.
*/
if (result != ISC_R_SUCCESS) {
ISC_LOG_ERROR, "change directory "
"to '%s' failed: %s",
return (result);
}
}
return (ISC_R_SUCCESS);
}
static void
}
/*
* This event callback is invoked to do periodic network
* interface scanning.
*/
static void
}
static isc_result_t
{
char *pidfilename;
ISC_LOG_INFO, "loading configuration from '%s'",
filename);
/*
* Parse the configuration file creating a parse tree. Any
* 'zone' statements are handled immediately by calling
* configure_zone() through 'callbacks'.
*/
&callbacks));
/*
* Configure various server options.
*/
/*
* Configure the zone manager.
*/
{
}
{
}
/*
* Configure the interface manager according to the "listen-on"
* statement.
*/
{
cctx,
&listenon);
} else {
/* Not specified, use default. */
&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.
*/
/*
* Arrange for further interface scanning to occur periodically
* as specified by the "interface-interval" option.
*/
if (interface_interval == 0) {
} else {
}
AF_INET, &dispatchv4));
AF_INET6, &dispatchv6));
/*
* 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.
*/
{
dispatchv4, dispatchv6));
}
}
/*
* Make sure we have a default view if and only if there
* were no explicit views.
*/
/*
* No explicit views; there ought to be a default view.
* There may already be one created as a size effect
* of zone statements, or we may have to create one.
* In either case, we need to configure and freeze it.
*/
dispatchv4, dispatchv6));
} else {
/*
* There are explicit views. There should not be
* a default view. If there is one, complain.
*/
if (result == ISC_R_SUCCESS) {
"when using 'view' statements, "
"all zones must be in views");
goto cleanup;
}
}
/*
* Create (or recreate) the version view.
*/
/*
* Swap our new view list with the production one.
*/
/*
* Load the TKEY information from the configuration.
*/
{
dns_tkey_ctx_t *t = NULL;
"configuring TKEY");
}
/*
* Relinquish root privileges.
*/
if (first_time)
/*
* 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) {
"ignoring config file logging "
"statement due to -g option");
} else {
"creating new logging configuration");
"configuring logging");
} else {
"setting up default logging channels");
"setting up default 'category default'");
}
if (result != ISC_R_SUCCESS) {
}
}
"now using logging configuration from "
"config file");
/*
* This cleans up either the old production view list
* or our temporary list depending on whether they
* were swapped above or not.
*/
}
if (dispatchv4 != NULL)
if (dispatchv6 != NULL)
return (result);
}
static isc_result_t
/*
* Load zone data from disk.
*/
{
}
/*
* 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.
*/
return (result);
}
static void
"creating client manager");
&server->interfacemgr),
"creating interface manager");
"creating interface timer");
"loading configuration");
"loading zones");
ISC_LOG_INFO, "running");
}
static void
ISC_LOG_INFO, "shutting down");
}
}
void
"initializing server configuration lock");
/* Initialize configuration data with default values. */
/* Initialize server data structures. */
&server->in_roothints),
"setting up root hints");
"initializing reload event lock");
sizeof(isc_event_t));
"allocating reload event");
"creating TKEY context");
/*
* Setup the server task, which is responsible for coordinating
* startup and shutdown of the server.
*/
"creating server task");
"isc_task_onshutdown");
"isc_app_onrun");
/*
* Create a timer for periodic interface scanning.
*/
"dns_zonemgr_create");
}
void
}
static void
ISC_LOG_CRITICAL, "exiting (due to fatal error)");
exit(1);
}
static void
if (result != ISC_R_SUCCESS) {
"reloading configuration failed: %s",
}
if (result != ISC_R_SUCCESS) {
"reloading zones failed: %s",
}
}
void
}
static isc_result_t
{
if (result != ISC_R_SUCCESS)
return (result);
{
if (result != ISC_R_SUCCESS)
goto cleanup;
}
return (ISC_R_SUCCESS);
return (result);
}
/*
* Create a listen list from the corresponding configuration
* data structure.
*/
static isc_result_t
{
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_SUCCESS);
}