mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-23 11:18:54 +00:00
1102 lines
26 KiB
C
1102 lines
26 KiB
C
#if !defined(lint) && !defined(SABER)
|
|
static char sccsid[] = "@(#)ns_maint.c 4.39 (Berkeley) 3/2/91";
|
|
static char rcsid[] = "$Id: ns_maint.c,v 1.4 1995/10/23 11:11:48 peter Exp $";
|
|
#endif /* not lint */
|
|
|
|
/*
|
|
* ++Copyright++ 1986, 1988
|
|
* -
|
|
* Copyright (c) 1986, 1988
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
* -
|
|
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
|
|
*
|
|
* 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, and that
|
|
* the name of Digital Equipment Corporation not be used in advertising or
|
|
* publicity pertaining to distribution of the document or software without
|
|
* specific, written prior permission.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
|
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
|
|
* CORPORATION 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.
|
|
* -
|
|
* --Copyright--
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <arpa/nameser.h>
|
|
#include <sys/wait.h>
|
|
#include <stdio.h>
|
|
#include <syslog.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "named.h"
|
|
|
|
#ifdef USE_UTIME
|
|
# include <utime.h>
|
|
#endif
|
|
|
|
static int xfers_running, /* # of xfers running */
|
|
xfers_deferred, /* # of needed xfers not run yet */
|
|
qserials_running,
|
|
alarm_pending, /* flag */
|
|
nxfers __P((struct zoneinfo *, int));
|
|
|
|
static void startxfer __P((struct zoneinfo *)),
|
|
abortxfer __P((struct zoneinfo *)),
|
|
addxfer __P((struct zoneinfo *)),
|
|
tryxfer __P((void));
|
|
|
|
#define qserial_qfull() (qserials_running == MAXQSERIAL)
|
|
|
|
#ifdef CLEANCACHE
|
|
static time_t cache_time;
|
|
#endif
|
|
#ifdef XSTATS
|
|
static time_t stats_time;
|
|
#endif
|
|
/*
|
|
* Invoked at regular intervals by signal interrupt; refresh all secondary
|
|
* zones from primary name server and remove old cache entries. Also,
|
|
* ifdef'd ALLOW_UPDATES, dump database if it has changed since last
|
|
* dump/bootup.
|
|
*/
|
|
void
|
|
ns_maint()
|
|
{
|
|
register struct zoneinfo *zp;
|
|
int zonenum;
|
|
|
|
gettime(&tt);
|
|
|
|
dprintf(1, (ddt, "\nns_maint(); now %s", ctimel(tt.tv_sec)));
|
|
|
|
alarm_pending = 0;
|
|
for (zp = zones, zonenum = 0; zp < &zones[nzones]; zp++, zonenum++) {
|
|
#ifdef DEBUG
|
|
if (debug >= 2)
|
|
printzoneinfo(zonenum);
|
|
#endif
|
|
if (tt.tv_sec >= zp->z_time && zp->z_refresh > 0) {
|
|
switch (zp->z_type) {
|
|
|
|
case Z_CACHE:
|
|
doachkpt();
|
|
ns_refreshtime(zp, tt.tv_sec);
|
|
break;
|
|
|
|
case Z_SECONDARY:
|
|
#ifdef STUBS
|
|
case Z_STUB:
|
|
#endif
|
|
if (zp->z_serial != 0 &&
|
|
((zp->z_lastupdate + zp->z_expire) <
|
|
tt.tv_sec)
|
|
) {
|
|
zp->z_serial = 0;
|
|
}
|
|
if (zp->z_flags &
|
|
(Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) {
|
|
ns_refreshtime(zp, tt.tv_sec);
|
|
break;
|
|
}
|
|
if (zp->z_flags & Z_XFER_RUNNING) {
|
|
abortxfer(zp);
|
|
break;
|
|
}
|
|
qserial_query(zp);
|
|
break;
|
|
#ifdef ALLOW_UPDATES
|
|
case Z_PRIMARY:
|
|
/*
|
|
* Checkpoint the zone if it has changed
|
|
* since we last checkpointed
|
|
*/
|
|
if (zp->z_flags & Z_CHANGED) {
|
|
zonedump(zp);
|
|
ns_refreshtime(zp, tt.tv_sec);
|
|
}
|
|
break;
|
|
#endif /* ALLOW_UPDATES */
|
|
}
|
|
gettime(&tt);
|
|
}
|
|
}
|
|
#ifdef CLEANCACHE
|
|
if ((cache_time + cache_interval) <= tt.tv_sec) {
|
|
if (cache_time)
|
|
remove_zone(hashtab, 0, 0);
|
|
cache_time = tt.tv_sec;
|
|
}
|
|
#endif
|
|
#ifdef XSTATS
|
|
if (stats_time + stats_interval <= tt.tv_sec) {
|
|
if (stats_time)
|
|
ns_logstats();
|
|
stats_time = tt.tv_sec;
|
|
}
|
|
#endif
|
|
if (!needmaint)
|
|
sched_maint();
|
|
dprintf(1, (ddt, "exit ns_maint()\n"));
|
|
}
|
|
|
|
/*
|
|
* Find when the next refresh needs to be and set
|
|
* interrupt time accordingly.
|
|
*/
|
|
void
|
|
sched_maint()
|
|
{
|
|
register struct zoneinfo *zp;
|
|
struct itimerval ival;
|
|
#ifdef CLEANCACHE
|
|
time_t next_refresh = cache_time + cache_interval;
|
|
#else
|
|
time_t next_refresh = 0;
|
|
#endif
|
|
static time_t next_alarm;
|
|
|
|
for (zp = zones; zp < &zones[nzones]; zp++)
|
|
if (zp->z_time != 0 &&
|
|
(next_refresh == 0 || next_refresh > zp->z_time))
|
|
next_refresh = zp->z_time;
|
|
/*
|
|
* Schedule the next call to ns_maint.
|
|
* Don't visit any sooner than maint_interval.
|
|
*/
|
|
bzero((char *)&ival, sizeof ival);
|
|
if (next_refresh != 0) {
|
|
if (next_refresh == next_alarm && alarm_pending) {
|
|
dprintf(1, (ddt, "sched_maint: no schedule change\n"));
|
|
return;
|
|
}
|
|
/*
|
|
* tv_sec can be an unsigned long, so we can't let
|
|
* it go negative.
|
|
*/
|
|
if (next_refresh < tt.tv_sec)
|
|
next_refresh = tt.tv_sec;
|
|
ival.it_value.tv_sec = next_refresh - tt.tv_sec;
|
|
if ((long) ival.it_value.tv_sec < maint_interval)
|
|
ival.it_value.tv_sec = maint_interval;
|
|
next_alarm = next_refresh;
|
|
alarm_pending = 1;
|
|
}
|
|
(void) setitimer(ITIMER_REAL, &ival, (struct itimerval *)NULL);
|
|
dprintf(1, (ddt, "sched_maint: Next interrupt in %lu sec\n",
|
|
(u_long)ival.it_value.tv_sec));
|
|
}
|
|
|
|
/*
|
|
* Mark a zone "up to date" after named-xfer tells us this or we
|
|
* discover it through the qserial_*() logic.
|
|
*/
|
|
static void
|
|
markUpToDate(zp)
|
|
struct zoneinfo *zp;
|
|
{
|
|
struct stat f_time;
|
|
|
|
zp->z_flags &= ~Z_SYSLOGGED;
|
|
zp->z_lastupdate = tt.tv_sec;
|
|
ns_refreshtime(zp, tt.tv_sec);
|
|
/*
|
|
* Restore Z_AUTH in case expired,
|
|
* but only if there were no errors
|
|
* in the zone file.
|
|
*/
|
|
if ((zp->z_flags & Z_DB_BAD) == 0)
|
|
zp->z_flags |= Z_AUTH;
|
|
if (zp->z_source) {
|
|
#if defined(USE_UTIME)
|
|
struct utimbuf t;
|
|
|
|
t.actime = tt.tv_sec;
|
|
t.modtime = tt.tv_sec;
|
|
(void) utime(zp->z_source, &t);
|
|
#else
|
|
struct timeval t[2];
|
|
|
|
t[0] = tt;
|
|
t[1] = tt;
|
|
(void) utimes(zp->z_source, t);
|
|
#endif /* USE_UTIME */
|
|
}
|
|
/* we use "stat" to set zp->z_ftime instead of just
|
|
setting it to tt.tv_sec in order to avoid any
|
|
possible rounding problems in utimes(). */
|
|
if (stat(zp->z_source, &f_time) != -1)
|
|
zp->z_ftime = f_time.st_mtime;
|
|
/* XXX log if stat fails? */
|
|
}
|
|
|
|
/*
|
|
* Query for the serial number of a zone, so that
|
|
* we can check to see if we need to transfer it.
|
|
*/
|
|
void
|
|
qserial_query(zp)
|
|
struct zoneinfo *zp;
|
|
{
|
|
struct qinfo *qp;
|
|
|
|
dprintf(1, (ddt, "qserial_query(%s)\n", zp->z_origin));
|
|
|
|
if (qserial_qfull())
|
|
return;
|
|
|
|
qp = sysquery(zp->z_origin, zp->z_class, T_SOA,
|
|
zp->z_addr, zp->z_addrcnt, QUERY);
|
|
if (!qp) {
|
|
syslog(LOG_INFO, "qserial_query(%s): sysquery FAILED",
|
|
zp->z_origin);
|
|
return; /* XXX - this is bad, we should do something */
|
|
}
|
|
qp->q_flags |= Q_ZSERIAL;
|
|
qp->q_zquery = zp;
|
|
zp->z_flags |= Z_QSERIAL;
|
|
ns_refreshtime(zp, tt.tv_sec);
|
|
qserials_running++;
|
|
dprintf(1, (ddt, "qserial_query(%s) QUEUED\n", zp->z_origin));
|
|
}
|
|
|
|
void
|
|
qserial_answer(qp, serial)
|
|
struct qinfo *qp;
|
|
u_int32_t serial;
|
|
{
|
|
struct zoneinfo *zp = qp->q_zquery;
|
|
int was_qfull = qserial_qfull();
|
|
|
|
dprintf(1, (ddt, "qserial_answer(%s, %lu)\n",
|
|
zp->z_origin, (u_long)serial));
|
|
zp->z_flags &= ~Z_QSERIAL;
|
|
qp->q_flags &= ~Q_ZSERIAL; /* keeps us from being called twice */
|
|
qserials_running--;
|
|
if (serial == 0) {
|
|
/* an error occurred, or the query timed out.
|
|
*/
|
|
#ifdef GETSER_LOGGING
|
|
syslog(GETSER_LOGGING, "Err/TO getting serial# for \"%s\"",
|
|
zp->z_origin);
|
|
#endif /* GETSER_LOGGING */
|
|
addxfer(zp);
|
|
} else if (SEQ_GT(serial, zp->z_serial) || !zp->z_serial) {
|
|
dprintf(1, (ddt, "qserial_answer: zone is out of date\n"));
|
|
zp->z_xaddr = from_addr.sin_addr; /* don't use qp->q_from */
|
|
addxfer(zp);
|
|
} else if (SEQ_GT(zp->z_serial, serial)) {
|
|
if (!haveComplained((char*)zp, "went backward")) {
|
|
syslog(LOG_NOTICE,
|
|
"Zone \"%s\" (class %d) SOA serial# (%lu) rcvd from [%s] is < ours (%lu)\n",
|
|
zp->z_origin, zp->z_class, serial,
|
|
inet_ntoa(from_addr.sin_addr),
|
|
zp->z_serial);
|
|
}
|
|
} else {
|
|
dprintf(1, (ddt, "qserial_answer: zone serial is still OK\n"));
|
|
markUpToDate(zp);
|
|
}
|
|
if (was_qfull)
|
|
needmaint = 1;
|
|
}
|
|
|
|
/*
|
|
* Hold and release SIGCHLD
|
|
*/
|
|
#ifdef POSIX_SIGNALS
|
|
static sigset_t sset;
|
|
#else
|
|
#ifndef SYSV
|
|
static int omask;
|
|
#endif
|
|
#endif /* POSIX_SIGNALS */
|
|
|
|
void holdsigchld()
|
|
{
|
|
#ifdef POSIX_SIGNALS
|
|
sigemptyset(&sset);
|
|
sigaddset(&sset,SIGCHLD);
|
|
sigprocmask(SIG_BLOCK,&sset,NULL);
|
|
#else /* POSIX_SIGNALS */
|
|
#ifndef SYSV
|
|
omask = sigblock(sigmask(SIGCHLD));
|
|
#else /* SYSV */
|
|
/* XXX - out of luck? */
|
|
#endif /* SYSV */
|
|
#endif /* POSIX_SIGNALS */
|
|
}
|
|
|
|
void releasesigchld()
|
|
{
|
|
#ifdef POSIX_SIGNALS
|
|
sigprocmask(SIG_UNBLOCK,&sset,NULL);
|
|
#else
|
|
#ifndef SYSV
|
|
(void) sigsetmask(omask);
|
|
#endif
|
|
#endif /* POSIX_SIGNALS */
|
|
}
|
|
|
|
/* State of all running zone transfers */
|
|
static struct {
|
|
pid_t xfer_pid;
|
|
int xfer_state; /* see below */
|
|
#ifdef sequent
|
|
union wait xfer_status;
|
|
#else
|
|
int xfer_status;
|
|
#endif
|
|
} xferstatus[MAX_XFERS_RUNNING];
|
|
#define XFER_IDLE 0
|
|
#define XFER_RUNNING 1
|
|
#define XFER_DONE 2
|
|
|
|
/*
|
|
* Start an asynchronous zone transfer for a zone.
|
|
* Depends on current time being in tt.
|
|
* The caller must call sched_maint after startxfer.
|
|
*/
|
|
static void
|
|
startxfer(zp)
|
|
struct zoneinfo *zp;
|
|
{
|
|
static char *argv[NSMAX + 20], argv_ns[NSMAX][MAXDNAME];
|
|
int argc = 0, argc_ns = 0, pid, i;
|
|
unsigned int cnt;
|
|
char debug_str[10];
|
|
char serial_str[10];
|
|
char port_str[10];
|
|
#ifdef GEN_AXFR
|
|
char class_str[10];
|
|
#endif
|
|
|
|
dprintf(1, (ddt, "startxfer() %s\n", zp->z_origin));
|
|
|
|
argv[argc++] = _PATH_XFER;
|
|
argv[argc++] = "-z";
|
|
argv[argc++] = zp->z_origin;
|
|
argv[argc++] = "-f";
|
|
argv[argc++] = zp->z_source;
|
|
argv[argc++] = "-s";
|
|
sprintf(serial_str, "%lu", (u_long)zp->z_serial);
|
|
argv[argc++] = serial_str;
|
|
#ifdef GEN_AXFR
|
|
argv[argc++] = "-C";
|
|
sprintf(class_str, "%d", zp->z_class);
|
|
argv[argc++] = class_str;
|
|
#endif
|
|
if (zp->z_flags & Z_SYSLOGGED)
|
|
argv[argc++] = "-q";
|
|
argv[argc++] = "-P";
|
|
sprintf(port_str, "%d", ns_port);
|
|
argv[argc++] = port_str;
|
|
#ifdef STUBS
|
|
if (zp->z_type == Z_STUB)
|
|
argv[argc++] = "-S";
|
|
#endif
|
|
#ifdef DEBUG
|
|
if (debug) {
|
|
argv[argc++] = "-d";
|
|
sprintf(debug_str, "%d", debug);
|
|
argv[argc++] = debug_str;
|
|
argv[argc++] = "-l";
|
|
argv[argc++] = _PATH_XFERDDT;
|
|
if (debug > 5) {
|
|
argv[argc++] = "-t";
|
|
argv[argc++] = _PATH_XFERTRACE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (zp->z_xaddr.s_addr != 0) {
|
|
/* Address was specified by the qserial logic, use it. */
|
|
argv[argc++] = strcpy(argv_ns[argc_ns++],
|
|
inet_ntoa(zp->z_xaddr));
|
|
} else {
|
|
/*
|
|
* Copy the server ip addresses into argv, after converting
|
|
* to ascii and saving the static inet_ntoa result.
|
|
*/
|
|
for (cnt = 0; cnt < zp->z_addrcnt; cnt++) {
|
|
struct in_addr a;
|
|
|
|
a = zp->z_addr[cnt];
|
|
if (aIsUs(a) &&
|
|
!haveComplained(zp->z_origin, (char*)startxfer)) {
|
|
syslog(LOG_NOTICE,
|
|
"attempted to fetch zone %s from self (%s)",
|
|
zp->z_origin, inet_ntoa(a));
|
|
continue;
|
|
}
|
|
argv[argc++] = strcpy(argv_ns[argc_ns++],
|
|
inet_ntoa(a));
|
|
}
|
|
}
|
|
|
|
argv[argc] = 0;
|
|
|
|
#ifdef DEBUG
|
|
#ifdef ECHOARGS
|
|
if (debug) {
|
|
for (i = 0; i < argc; i++)
|
|
fprintf(ddt, "Arg %d=%s\n", i, argv[i]);
|
|
}
|
|
#endif /* ECHOARGS */
|
|
#endif /* DEBUG */
|
|
|
|
gettime(&tt);
|
|
holdsigchld();
|
|
for (i = 0; i < MAX_XFERS_RUNNING; i++) {
|
|
if (xferstatus[i].xfer_pid == 0) {
|
|
xferstatus[i].xfer_state = XFER_RUNNING;
|
|
break;
|
|
}
|
|
}
|
|
if ((pid = vfork()) == -1) {
|
|
syslog(LOG_ERR, "xfer vfork: %m");
|
|
releasesigchld();
|
|
zp->z_time = tt.tv_sec + 10;
|
|
return;
|
|
}
|
|
|
|
if (pid == 0) {
|
|
/* Child. */
|
|
execv(_PATH_XFER, argv);
|
|
syslog(LOG_ERR, "can't exec %s: %m", _PATH_XFER);
|
|
_exit(XFER_FAIL); /* Avoid duplicate buffer flushes. */
|
|
}
|
|
/* Parent. */
|
|
xferstatus[i].xfer_pid = pid; /* XXX - small race condition here if we
|
|
* can't hold signals */
|
|
dprintf(1, (ddt, "started xfer child %d\n", pid));
|
|
zp->z_flags &= ~Z_NEED_XFER;
|
|
zp->z_flags |= Z_XFER_RUNNING;
|
|
zp->z_xferpid = pid;
|
|
xfers_running++;
|
|
zp->z_time = tt.tv_sec + MAX_XFER_TIME;
|
|
releasesigchld();
|
|
}
|
|
|
|
const char *
|
|
zoneTypeString(zp)
|
|
const struct zoneinfo *zp;
|
|
{
|
|
static char ret[sizeof "(4294967296?)"]; /* 2^32 */
|
|
|
|
switch (zp->z_type) {
|
|
case Z_PRIMARY: return ("primary");
|
|
case Z_SECONDARY: return ("secondary");
|
|
#ifdef STUBS
|
|
case Z_STUB: return ("stub");
|
|
#endif
|
|
case Z_CACHE: return ("cache");
|
|
default:
|
|
sprintf(ret, "(%lu?)", (u_long)zp->z_type);
|
|
return (ret);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
printzoneinfo(zonenum)
|
|
int zonenum;
|
|
{
|
|
struct timeval tt;
|
|
struct zoneinfo *zp = &zones[zonenum];
|
|
|
|
if (!debug)
|
|
return;
|
|
|
|
if (!zp->z_origin)
|
|
return;
|
|
|
|
fprintf(ddt, "printzoneinfo(%d):\n", zonenum);
|
|
|
|
gettime(&tt);
|
|
fprintf(ddt, "origin ='%s'", zp->z_origin[0] ? zp->z_origin : ".");
|
|
#ifdef GEN_AXFR
|
|
fprintf(ddt, ", class = %d", zp->z_class);
|
|
#endif
|
|
fprintf(ddt, ", type = %s", zoneTypeString(zp));
|
|
if (zp->z_source)
|
|
fprintf(ddt,", source = %s\n", zp->z_source);
|
|
fprintf(ddt, "z_refresh = %lu", (u_long)zp->z_refresh);
|
|
fprintf(ddt, ", retry = %lu", (u_long)zp->z_retry);
|
|
fprintf(ddt, ", expire = %lu", (u_long)zp->z_expire);
|
|
fprintf(ddt, ", minimum = %lu", (u_long)zp->z_minimum);
|
|
fprintf(ddt, ", serial = %lu\n", (u_long)zp->z_serial);
|
|
fprintf(ddt, "z_time = %lu", (u_long)zp->z_time);
|
|
if (zp->z_time) {
|
|
fprintf(ddt, ", now time : %lu sec", (u_long)tt.tv_sec);
|
|
fprintf(ddt, ", time left: %lu sec",
|
|
(long)(zp->z_time - tt.tv_sec));
|
|
}
|
|
fprintf(ddt, "; flags %lx\n", (u_long)zp->z_flags);
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
/*
|
|
* remove_zone (htp, zone) --
|
|
* Delete all RR's in the zone "zone" under specified hash table.
|
|
*/
|
|
void
|
|
#ifdef CLEANCACHE
|
|
remove_zone(htp, zone, all)
|
|
#else
|
|
remove_zone(htp, zone)
|
|
#endif
|
|
register struct hashbuf *htp;
|
|
register int zone;
|
|
#ifdef CLEANCACHE
|
|
register int all;
|
|
#endif
|
|
{
|
|
register struct databuf *dp, *pdp;
|
|
register struct namebuf *np, *pnp, *npn;
|
|
struct namebuf **npp, **nppend;
|
|
|
|
nppend = htp->h_tab + htp->h_size;
|
|
for (npp = htp->h_tab; npp < nppend; npp++) {
|
|
for (pnp = NULL, np = *npp; np != NULL; np = npn) {
|
|
for (pdp = NULL, dp = np->n_data; dp != NULL; NULL) {
|
|
if (dp->d_zone == zone
|
|
#ifdef CLEANCACHE
|
|
&& (all || stale(dp))
|
|
#endif
|
|
) {
|
|
dp = rm_datum(dp, np, pdp);
|
|
} else {
|
|
pdp = dp;
|
|
dp = dp->d_next;
|
|
}
|
|
} /*for(pdp)*/
|
|
|
|
if (np->n_hash) {
|
|
/* call recursively to remove subdomains. */
|
|
remove_zone(np->n_hash, zone
|
|
#ifdef CLEANCACHE
|
|
, all
|
|
#endif
|
|
);
|
|
|
|
/* if now empty, free it */
|
|
if (np->n_hash->h_cnt == 0) {
|
|
free((char*)np->n_hash);
|
|
np->n_hash = NULL;
|
|
}
|
|
}
|
|
|
|
if ((np->n_hash == NULL) && (np->n_data == NULL)) {
|
|
npn = rm_name(np, npp, pnp);
|
|
htp->h_cnt--;
|
|
} else {
|
|
npn = np->n_next;
|
|
pnp = np;
|
|
}
|
|
} /*for(pnp)*/
|
|
} /*for(npp)*/
|
|
}
|
|
|
|
#ifdef PURGE_ZONE
|
|
static void purge_z_2 __P((struct hashbuf *, int));
|
|
static bottom_of_zone __P((struct databuf *, int));
|
|
|
|
void
|
|
purge_zone(dname, htp, class)
|
|
const char *dname;
|
|
register struct hashbuf *htp;
|
|
int class;
|
|
{
|
|
const char *fname;
|
|
struct databuf *dp, *pdp;
|
|
struct namebuf *np;
|
|
struct hashbuf *phtp = htp;
|
|
|
|
dprintf(1, (ddt, "purge_zone(%s,%d)\n", dname, class));
|
|
if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname &&
|
|
!WILDCARD_P(dname)) {
|
|
for (pdp = NULL, dp = np->n_data; dp != NULL; ) {
|
|
if (dp->d_class == class)
|
|
dp = rm_datum(dp, np, pdp);
|
|
else {
|
|
pdp = dp;
|
|
dp = dp->d_next;
|
|
}
|
|
}
|
|
|
|
if (np->n_hash) {
|
|
purge_z_2(np->n_hash, class);
|
|
if (np->n_hash->h_cnt == 0) {
|
|
free((char*)np->n_hash);
|
|
np->n_hash = NULL;
|
|
}
|
|
}
|
|
|
|
/* remove entry from cache, if required */
|
|
if ((np->n_hash == NULL) && (np->n_data == NULL)) {
|
|
struct namebuf **npp, **nppend;
|
|
struct namebuf *npn, *pnp, *nnp;
|
|
|
|
dprintf(3,(ddt, "purge_zone: cleaning cache\n"));
|
|
|
|
/* walk parent hashtable looking for ourself */
|
|
if (np->n_parent)
|
|
phtp = np->n_parent->n_hash;
|
|
else
|
|
phtp = htp; /* top / root zone */
|
|
|
|
if (phtp) {
|
|
nppend = phtp->h_tab + phtp->h_size;
|
|
for (npp = phtp->h_tab; npp < nppend; npp++) {
|
|
for (pnp = NULL, nnp = *npp;
|
|
nnp != NULL;
|
|
nnp = npn) {
|
|
if (nnp == np) {
|
|
dprintf(3, (ddt,
|
|
"purge_zone: found our selves\n"
|
|
));
|
|
npn = rm_name(nnp,npp,pnp);
|
|
phtp->h_cnt--;
|
|
} else {
|
|
npn = nnp->n_next;
|
|
pnp = nnp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
purge_z_2(htp, class)
|
|
register struct hashbuf *htp;
|
|
register int class;
|
|
{
|
|
register struct databuf *dp, *pdp;
|
|
register struct namebuf *np, *pnp, *npn;
|
|
struct namebuf **npp, **nppend;
|
|
|
|
nppend = htp->h_tab + htp->h_size;
|
|
for (npp = htp->h_tab; npp < nppend; npp++) {
|
|
for (pnp = NULL, np = *npp; np != NULL; np = npn) {
|
|
if (!bottom_of_zone(np->n_data, class)) {
|
|
for (pdp = NULL, dp = np->n_data; dp != NULL; ) {
|
|
if (dp->d_class == class)
|
|
dp = rm_datum(dp, np, pdp);
|
|
else {
|
|
pdp = dp;
|
|
dp = dp->d_next;
|
|
}
|
|
}
|
|
if (np->n_hash) {
|
|
/* call recursively to rm subdomains */
|
|
purge_z_2(np->n_hash, class);
|
|
|
|
/* if now empty, free it */
|
|
if (np->n_hash->h_cnt == 0) {
|
|
free((char*)np->n_hash);
|
|
np->n_hash = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((np->n_hash == NULL) && (np->n_data == NULL)) {
|
|
npn = rm_name(np, npp, pnp);
|
|
htp->h_cnt--;
|
|
} else {
|
|
npn = np->n_next;
|
|
pnp = np;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
bottom_of_zone(dp, class)
|
|
struct databuf *dp;
|
|
int class;
|
|
{
|
|
for ( ; dp ; dp = dp->d_next) {
|
|
if (dp->d_class != class)
|
|
continue;
|
|
if (dp->d_zone == 0)
|
|
continue;
|
|
#ifdef NCACHE
|
|
if (dp->d_rcode) /* this should not occur */
|
|
continue;
|
|
#endif
|
|
if (dp->d_type == T_SOA)
|
|
return (1);
|
|
}
|
|
dprintf(3, (ddt, "bottom_of_zone() == 0\n"));
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Handle XFER limit for a nameserver.
|
|
*/
|
|
static int
|
|
nxfers(zp, delta)
|
|
struct zoneinfo *zp;
|
|
int delta;
|
|
{
|
|
struct in_addr nsa;
|
|
struct nameser *nsp;
|
|
int ret;
|
|
|
|
if (zp->z_xaddr.s_addr)
|
|
nsa = zp->z_xaddr; /* qserial overrode address */
|
|
else if (!zp->z_addrcnt)
|
|
return (-1);
|
|
else
|
|
nsa = zp->z_addr[0]; /* first ns holds zone's xfer limit */
|
|
|
|
if (!(nsp = nameserFind(nsa, NS_F_INSERT)))
|
|
return (-1); /* probably ENOMEM */
|
|
|
|
ret = nsp->xfers;
|
|
if (delta < 0 && -delta > ret)
|
|
return (-1); /* taking more than we have */
|
|
|
|
nsp->xfers += delta;
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Abort an xfer that has taken too long.
|
|
*/
|
|
static void
|
|
abortxfer(zp)
|
|
struct zoneinfo *zp;
|
|
{
|
|
if (zp->z_flags & (Z_XFER_GONE|Z_XFER_ABORTED)) {
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_XFERS_RUNNING; i++) {
|
|
if (xferstatus[i].xfer_pid == zp->z_xferpid) {
|
|
xferstatus[i].xfer_pid = 0;
|
|
xferstatus[i].xfer_state = XFER_IDLE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (zp->z_flags & Z_XFER_GONE)
|
|
syslog(LOG_WARNING,
|
|
"zone transfer timeout for \"%s\"; pid %lu missing",
|
|
zp->z_origin, (u_long)zp->z_xferpid);
|
|
else if (kill(zp->z_xferpid, SIGKILL) == -1)
|
|
syslog(LOG_WARNING,
|
|
"zone transfer timeout for \"%s\"; kill pid %lu: %m",
|
|
zp->z_origin, (u_long)zp->z_xferpid);
|
|
else
|
|
syslog(LOG_WARNING,
|
|
"zone transfer timeout for \"%s\"; second kill\
|
|
pid %lu - forgetting, processes may accumulate",
|
|
zp->z_origin, (u_long)zp->z_xferpid);
|
|
|
|
zp->z_xferpid = 0;
|
|
xfers_running--;
|
|
(void)nxfers(zp, -1);
|
|
zp->z_flags &= ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE);
|
|
} else if (kill(zp->z_xferpid, SIGKILL) == -1) {
|
|
if (errno == ESRCH)
|
|
/* No warning on first time, it may have just exited */
|
|
zp->z_flags |= Z_XFER_GONE;
|
|
else {
|
|
syslog(LOG_WARNING,
|
|
"zone transfer timeout for \"%s\"; pid %lu kill failed %m",
|
|
zp->z_origin, (u_long)zp->z_xferpid);
|
|
zp->z_flags |= Z_XFER_ABORTED;
|
|
}
|
|
} else {
|
|
syslog(LOG_NOTICE,
|
|
"zone transfer timeout for \"%s\"; pid %lu killed",
|
|
zp->z_origin, (u_long)zp->z_xferpid);
|
|
zp->z_flags |= Z_XFER_ABORTED;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SIGCHLD signal handler: process exit of xfer's.
|
|
* (Note: also called when outgoing transfer completes.)
|
|
*/
|
|
SIG_FN
|
|
reapchild()
|
|
{
|
|
int pid, i, save_errno;
|
|
#if defined(sequent)
|
|
union wait status;
|
|
#else
|
|
int status;
|
|
#endif /* sequent */
|
|
|
|
#if defined(MUST_REARM_SIGS)
|
|
(void)signal(SIGCLD, (SIG_FN (*)()) reapchild);
|
|
#endif
|
|
save_errno = errno;
|
|
gettime(&tt);
|
|
#if defined(USE_WAITPID)
|
|
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
|
#else /* USE_WAITPID */
|
|
{
|
|
pid = wait(&status);
|
|
#endif /* USE_WAITPID */
|
|
for (i = 0; i < MAX_XFERS_RUNNING; i++) {
|
|
if (xferstatus[i].xfer_pid == pid) {
|
|
xferstatus[i].xfer_status = status;
|
|
xferstatus[i].xfer_state = XFER_DONE;
|
|
needendxfer++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
errno = save_errno;
|
|
}
|
|
|
|
/*
|
|
* Finish processing of of finished xfers
|
|
*/
|
|
void
|
|
endxfer()
|
|
{
|
|
register struct zoneinfo *zp;
|
|
int exitstatus, pid, i;
|
|
#if defined(sequent)
|
|
union wait status;
|
|
#else
|
|
int status;
|
|
#endif /* sequent */
|
|
|
|
gettime(&tt);
|
|
|
|
for (i = 0; i < MAX_XFERS_RUNNING; i++) {
|
|
if (xferstatus[i].xfer_state != XFER_DONE)
|
|
continue;
|
|
pid = xferstatus[i].xfer_pid;
|
|
status = xferstatus[i].xfer_status;
|
|
exitstatus = WIFEXITED(status) ?WEXITSTATUS(status) :0;
|
|
|
|
for (zp = zones; zp < &zones[nzones]; zp++) {
|
|
if (zp->z_xferpid != pid)
|
|
continue;
|
|
xfers_running--;
|
|
(void) nxfers(zp, -1);
|
|
zp->z_xferpid = 0;
|
|
zp->z_flags &=
|
|
~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE);
|
|
dprintf(1, (ddt,
|
|
"\nendxfer: child %d zone %s returned status=%d termsig=%d\n",
|
|
pid, zp->z_origin, exitstatus,
|
|
WIFSIGNALED(status) ?WTERMSIG(status) :-1
|
|
)
|
|
);
|
|
if (WIFSIGNALED(status)) {
|
|
if (WTERMSIG(status) != SIGKILL) {
|
|
syslog(LOG_NOTICE,
|
|
"named-xfer exited with signal %d\n",
|
|
WTERMSIG(status));
|
|
}
|
|
ns_retrytime(zp, tt.tv_sec);
|
|
} else {
|
|
switch (exitstatus) {
|
|
case XFER_UPTODATE:
|
|
markUpToDate(zp);
|
|
break;
|
|
|
|
case XFER_SUCCESS:
|
|
/* XXX should incorporate loadxfer() */
|
|
zp->z_flags |= Z_NEED_RELOAD;
|
|
zp->z_flags &= ~Z_SYSLOGGED;
|
|
needzoneload++;
|
|
break;
|
|
|
|
case XFER_TIMEOUT:
|
|
if (!(zp->z_flags & Z_SYSLOGGED)) {
|
|
zp->z_flags |= Z_SYSLOGGED;
|
|
syslog(LOG_NOTICE,
|
|
"zoneref: Masters for secondary zone \"%s\" unreachable",
|
|
zp->z_origin);
|
|
}
|
|
ns_retrytime(zp, tt.tv_sec);
|
|
break;
|
|
|
|
default:
|
|
if (!(zp->z_flags & Z_SYSLOGGED)) {
|
|
zp->z_flags |= Z_SYSLOGGED;
|
|
syslog(LOG_NOTICE,
|
|
"named-xfer for \"%s\" exited %d",
|
|
zp->z_origin,
|
|
exitstatus);
|
|
}
|
|
/* FALLTHROUGH */
|
|
case XFER_FAIL:
|
|
zp->z_flags |= Z_SYSLOGGED;
|
|
ns_retrytime(zp, tt.tv_sec);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
xferstatus[i].xfer_state = XFER_IDLE;
|
|
xferstatus[i].xfer_pid = 0;
|
|
}
|
|
releasesigchld();
|
|
tryxfer();
|
|
}
|
|
|
|
/*
|
|
* Try to start some xfers - new "fair scheduler" by Bob Heiney @DEC (1995)
|
|
*/
|
|
static void
|
|
tryxfer() {
|
|
static struct zoneinfo *zp = NULL;
|
|
static struct zoneinfo *lastzones = NULL;
|
|
static int lastnzones = 0;
|
|
struct zoneinfo *startzp, *stopzp;
|
|
|
|
/* initialize, and watch out for changes in zones! */
|
|
if (lastzones != zones) {
|
|
if (lastzones != NULL)
|
|
syslog(LOG_INFO, "zones changed: %p != %p",
|
|
lastzones, zones);
|
|
lastzones = zones;
|
|
zp = zones;
|
|
}
|
|
|
|
/* did zones shrink? */
|
|
if (lastnzones > nzones) {
|
|
syslog(LOG_INFO, "zones shrunk");
|
|
zp = zones;
|
|
}
|
|
lastnzones = nzones;
|
|
|
|
if (zp == zones)
|
|
stopzp = &zones[nzones-1];
|
|
else
|
|
stopzp = zp - 1;
|
|
|
|
dprintf(3, (ddt, "tryxfer start zp=%p stopzp=%p def=%d running=%d\n",
|
|
zp, stopzp, xfers_deferred, xfers_running));
|
|
|
|
startzp = zp;
|
|
for (;;) {
|
|
int xfers;
|
|
|
|
if (!xfers_deferred || xfers_running >= max_xfers_running)
|
|
break;
|
|
|
|
if ((xfers = nxfers(zp, 0)) != -1 &&
|
|
xfers < max_xfers_per_ns &&
|
|
(zp->z_flags & Z_NEED_XFER)) {
|
|
nxfers(zp, 1);
|
|
xfers_deferred--;
|
|
startxfer(zp);
|
|
}
|
|
|
|
if (zp == stopzp) {
|
|
dprintf(3, (ddt, "tryxfer stop mark\n"));
|
|
zp = startzp;
|
|
break;
|
|
}
|
|
|
|
zp++;
|
|
/* wrap around? */
|
|
if (zp == &zones[nzones])
|
|
zp = zones;
|
|
}
|
|
dprintf(3, (ddt, "tryxfer stop zp=%p\n", zp));
|
|
|
|
if (!needmaint)
|
|
sched_maint();
|
|
}
|
|
|
|
/*
|
|
* Reload zones whose transfers have completed.
|
|
*/
|
|
void
|
|
loadxfer() {
|
|
register struct zoneinfo *zp;
|
|
|
|
gettime(&tt);
|
|
for (zp = zones; zp < &zones[nzones]; zp++) {
|
|
if (zp->z_flags & Z_NEED_RELOAD) {
|
|
dprintf(1, (ddt, "loadxfer() \"%s\"\n",
|
|
zp->z_origin[0] ? zp->z_origin : "."));
|
|
zp->z_flags &= ~(Z_NEED_RELOAD|Z_AUTH);
|
|
remove_zone(hashtab, zp - zones
|
|
#ifdef CLEANCACHE
|
|
, 1
|
|
#endif
|
|
);
|
|
#ifdef PURGE_ZONE
|
|
purge_zone(zp->z_origin, hashtab, zp->z_class);
|
|
#endif
|
|
if (!db_load(zp->z_source, zp->z_origin, zp, NULL))
|
|
zp->z_flags |= Z_AUTH;
|
|
if (zp->z_flags & Z_TMP_FILE)
|
|
(void) unlink(zp->z_source);
|
|
}
|
|
}
|
|
if (!needmaint)
|
|
sched_maint();
|
|
}
|
|
|
|
/*
|
|
* Add this zone to the set of those needing transfers.
|
|
*/
|
|
static void
|
|
addxfer(zp)
|
|
struct zoneinfo *zp;
|
|
{
|
|
if (!(zp->z_flags & Z_NEED_XFER)) {
|
|
zp->z_flags |= Z_NEED_XFER;
|
|
xfers_deferred++;
|
|
tryxfer();
|
|
}
|
|
}
|