NetBSD/dist/bind/bin/named/ns_notify.c

465 lines
11 KiB
C

/* $NetBSD: ns_notify.c,v 1.5 2002/06/20 11:42:58 itojun Exp $ */
#if !defined(lint) && !defined(SABER)
static const char rcsid[] = "Id: ns_notify.c,v 8.20 2002/04/25 05:27:12 marka Exp";
#endif /* not lint */
/*
* Copyright (c) 1994-2000 by 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.
*/
/* Import. */
#include "port_before.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <errno.h>
#include <limits.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <isc/eventlib.h>
#include <isc/logging.h>
#include <isc/memcluster.h>
#include <isc/dst.h>
#include "port_after.h"
#include "named.h"
#ifdef BIND_NOTIFY
/* Types. */
struct pnotify {
char * name;
ns_class class;
ns_type type;
evTimerID timer;
LINK(struct pnotify) link;
};
/* Forward. */
static void sysnotify(const char *, ns_class, ns_type);
static void sysnotify_slaves(const char *, const char *,
ns_class, ns_type, int, int *, int *);
static void sysnotify_ns(const char *, const char *,
ns_class, ns_type, int, int *, int *);
static void free_notify(struct pnotify *);
static void notify_timer(evContext, void *,
struct timespec, struct timespec);
/* Local. */
static LIST(struct pnotify) pending_notifies;
static LIST(struct pnotify) loading_notifies;
/* Public. */
/*
* ns_notify(dname, class, type)
* call this when a zone has changed and its slaves need to know.
*/
void
ns_notify(const char *dname, ns_class class, ns_type type) {
static const char no_room[] = "%s failed, cannot notify for zone %s";
int delay, max_delay;
struct zoneinfo *zp;
struct pnotify *ni;
zp = find_auth_zone(dname, class);
if (zp == NULL) {
ns_warning(ns_log_notify,
"no zone found for notify (\"%s\" %s %s)",
(dname && *dname) ? dname : ".",
p_class(class), p_type(type));
return;
}
if (ns_samename(dname, zp->z_origin) != 1) {
ns_warning(ns_log_notify,
"notify not called with top of zone (\"%s\" %s %s)",
(dname && *dname) ? dname : ".",
p_class(class), p_type(type));
return;
}
if ((zp->z_flags & Z_NOTIFY) != 0) {
ns_info(ns_log_notify,
"suppressing duplicate notify (\"%s\" %s %s)",
(dname && *dname) ? dname : ".",
p_class(class), p_type(type));
return;
}
ni = memget(sizeof *ni);
if (ni == NULL) {
ns_info(ns_log_notify, no_room, "memget", dname);
return;
}
ni->name = savestr(dname, 0);
if (ni->name == NULL) {
memput(ni, sizeof *ni);
ni = NULL;
ns_info(ns_log_notify, no_room, "memget", dname);
return;
}
ni->class = class;
ni->type = type;
INIT_LINK(ni, link);
evInitID(&ni->timer);
if (loading != 0) {
APPEND(loading_notifies, ni, link);
return;
}
/* Delay notification for from five seconds up to fifteen minutes. */
max_delay = MIN(nzones, 895);
max_delay = MAX(max_delay, 25);
delay = 5 + (rand() % max_delay);
if (evSetTimer(ev, notify_timer, ni,
evAddTime(evNowTime(), evConsTime(delay, 0)),
evConsTime(0, 0), &ni->timer) < 0) {
ns_error(ns_log_notify, "evSetTimer() failed: %s",
strerror(errno));
ni->name = freestr(ni->name);
memput(ni, sizeof *ni);
return;
}
zp->z_flags |= Z_NOTIFY;
APPEND(pending_notifies, ni, link);
ns_debug(ns_log_notify, 3,
"ns_notify(%s, %s, %s): ni %p, zp %p, delay %d",
(dname && *dname) ? dname : ".",
p_class(class), p_type(type),
ni, zp, delay);
}
void
notify_afterload() {
struct pnotify *ni;
INSIST(loading == 0);
while ((ni = HEAD(loading_notifies)) != NULL) {
UNLINK(loading_notifies, ni, link);
ns_notify(ni->name, ni->class, ni->type);
ni->name = freestr(ni->name);
memput(ni, sizeof *ni);
}
}
/*
* ns_unnotify()
* call this when all pending notifies are now considered junque.
*/
void
ns_unnotify(void) {
while (!EMPTY(pending_notifies)) {
struct pnotify *ni = HEAD(pending_notifies);
INSIST(LINKED(ni, link));
UNLINK(pending_notifies, ni, link);
free_notify(ni);
}
}
/*
* ns_stopnotify(const char *dname, ns_class class)
* stop notifies for this particular zone.
*/
void
ns_stopnotify(const char *dname, ns_class class) {
struct pnotify *ni;
ni = HEAD(pending_notifies);
while (ni != NULL &&
(ni->class != class || ns_samename(ni->name, dname) != 1))
ni = NEXT(ni, link);
if (ni != NULL) {
UNLINK(pending_notifies, ni, link);
free_notify(ni);
}
}
/* Private. */
/*
* sysnotify(dname, class, type)
* cause a NOTIFY request to be sysquery()'d to each slave server
* of the zone that "dname" is within.
*/
static void
sysnotify(const char *dname, ns_class class, ns_type type) {
const char *zname;
u_int32_t zserial;
int nns, na, i;
struct zoneinfo *zp;
struct in_addr *also_addr;
ns_debug(ns_log_notify, 3, "sysnotify(%s, %s, %s)",
dname, p_class(class), p_type(type));
zp = find_auth_zone(dname, class);
if (zp == NULL) {
ns_warning(ns_log_notify, "sysnotify: can't find \"%s\" (%s)",
dname, p_class(class));
return;
}
if (ns_samename(dname, zp->z_origin) != 1) {
ns_warning(ns_log_notify, "sysnotify: not auth for zone %s",
dname);
return;
}
if (zp->z_notify == notify_no ||
(zp->z_notify == notify_use_default &&
server_options->notify == notify_no))
return;
if (zp->z_type != z_master && zp->z_type != z_slave) {
ns_warning(ns_log_notify, "sysnotify: %s not master or slave",
dname);
return;
}
zname = zp->z_origin;
zserial = zp->z_serial;
nns = na = 0;
if (zp->z_notify == notify_yes ||
(zp->z_notify == notify_use_default &&
server_options->notify == notify_yes))
sysnotify_slaves(dname, zname, class, type,
zp - zones, &nns, &na);
/*
* Handle any global or zone-specific also-notify clauses
*/
if (zp->z_notify_count != 0) {
/* zone-specific also notify */
ns_debug(ns_log_notify, 3, "zone notify ns = %d",
zp->z_notify_count);
also_addr = zp->z_also_notify;
for (i = 0; i < zp->z_notify_count; i++) {
ns_debug(ns_log_notify, 4, "notifying %s",
inet_ntoa(*also_addr));
sysquery(dname, class, type, also_addr, NULL, 1,
ns_port, NS_NOTIFY_OP, 0);
also_addr++;
}
nns += zp->z_notify_count;
na += zp->z_notify_count;
} else if (server_options->notify_count != 0) {
ns_debug(ns_log_notify, 4, "global notify ns = %d",
server_options->notify_count);
also_addr = server_options->also_notify;
for (i = 0; i < server_options->notify_count; i++) {
ns_debug(ns_log_notify, 3, "notifying %s",
inet_ntoa(*also_addr));
sysquery(dname, class, type, also_addr, NULL, 1,
ns_port, ns_o_notify, 0);
also_addr++;
}
nns += server_options->notify_count;
na += server_options->notify_count;
}
if (nns != 0 || na != 0)
ns_info(ns_log_notify,
"Sent NOTIFY for \"%s %s %s %u\" (%s); %d NS, %d A",
dname, p_class(class), p_type(type), zserial, zname, nns, na);
}
static void
sysnotify_slaves(const char *dname, const char *zname,
ns_class class, ns_type type,
int zn, int *nns, int *na)
{
const char *mname, *fname;
struct hashbuf *htp;
struct namebuf *np;
struct databuf *dp;
/*
* Master.
*/
htp = hashtab;
np = nlookup(zname, &htp, &fname, 0);
if (np == NULL) {
ns_warning(ns_log_notify,
"sysnotify: found name \"%s\" but not zone",
dname);
return;
}
mname = NULL;
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
if (dp->d_zone == DB_Z_CACHE || !match(dp, class, ns_t_soa))
continue;
if (dp->d_type == ns_t_sig)
continue;
if (mname) {
ns_notice(ns_log_notify,
"multiple SOA's for zone \"%s\"?",
zname);
return;
}
mname = (char *) dp->d_data;
}
if (mname == NULL) {
ns_notice(ns_log_notify, "no SOA found for zone \"%s\"",
zname);
return;
}
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
if (dp->d_zone == DB_Z_CACHE || !match(dp, class, ns_t_ns))
continue;
if (dp->d_type == ns_t_sig)
continue;
if (ns_samename((char*)dp->d_data, mname) == 1)
continue;
sysnotify_ns(dname, (char *)dp->d_data, class, type,
zn, nns, na);
}
}
static void
sysnotify_ns(const char *dname, const char *aname,
ns_class class, ns_type type,
int zn, int *nns, int *na)
{
struct databuf *adp;
struct namebuf *anp;
const char *fname;
struct in_addr nss[NSMAX];
struct hashbuf *htp;
int is_us, nsc, auth6, neg;
int cname = 0;
htp = hashtab;
anp = nlookup(aname, &htp, &fname, 0);
nsc = 0;
is_us = 0;
auth6 = 0;
neg = 0;
if (anp != NULL)
for (adp = anp->n_data; adp; adp = adp->d_next) {
struct in_addr ina;
if (adp->d_class != class)
continue;
if (adp->d_rcode == NXDOMAIN) {
neg = 1;
break;
}
if (adp->d_type == T_CNAME && adp->d_rcode == 0) {
cname = 1;
ns_error(ns_log_notify,
"NS '%s' for '%s/%s' is a CNAME",
*aname ? aname : ".",
*dname ? dname : ".",
p_class(class));
break;
}
if ((adp->d_type == T_AAAA || adp->d_type == ns_t_a6) &&
(zones[adp->d_class].z_type == z_master ||
zones[adp->d_class].z_type == z_slave)) {
auth6 = 1;
continue;
}
if (!match(adp, class, T_A))
continue;
if (adp->d_rcode) {
neg = 1;
continue;
}
if (adp->d_type == ns_t_sig)
continue;
ina = ina_get(adp->d_data);
if (aIsUs(ina)) {
is_us = 1;
continue;
}
if (nsc < NSMAX)
nss[nsc++] = ina;
} /*next A*/
if (nsc == 0) {
if (!is_us && !cname && !auth6 && !neg &&
!NS_OPTION_P(OPTION_NOFETCHGLUE)) {
struct qinfo *qp;
qp = sysquery(aname, class, ns_t_a, NULL, NULL, 0,
ns_port, ns_o_query, 0);
if (qp != NULL)
qp->q_notifyzone = zn;
}
return;
}
sysquery(dname, class, type, nss, NULL, nsc, ns_port, ns_o_notify, 0);
(*nns)++;
*na += nsc;
}
static void
free_notify(struct pnotify *ni) {
struct zoneinfo *zp;
INSIST(!LINKED(ni, link));
zp = find_auth_zone(ni->name, ni->class);
if (zp != NULL && ns_samename(ni->name, zp->z_origin) == 1) {
INSIST((zp->z_flags & Z_NOTIFY) != 0);
zp->z_flags &= ~Z_NOTIFY;
}
if (evTestID(ni->timer)) {
evClearTimer(ev, ni->timer);
evInitID(&ni->timer);
}
ni->name = freestr(ni->name);
memput(ni, sizeof *ni);
}
static void
notify_timer(evContext ctx, void *uap,
struct timespec due,
struct timespec inter)
{
struct pnotify *ni = uap;
UNUSED(ctx);
UNUSED(due);
UNUSED(inter);
INSIST(evTestID(ni->timer));
evInitID(&ni->timer);
INSIST(LINKED(ni, link));
UNLINK(pending_notifies, ni, link);
sysnotify(ni->name, ni->class, ni->type);
free_notify(ni);
}
#endif /*BIND_NOTIFY*/