diff options
Diffstat (limited to 'contrib/bind/named/ns_maint.c')
-rw-r--r-- | contrib/bind/named/ns_maint.c | 1100 |
1 files changed, 1100 insertions, 0 deletions
diff --git a/contrib/bind/named/ns_maint.c b/contrib/bind/named/ns_maint.c new file mode 100644 index 000000000000..631210b0f243 --- /dev/null +++ b/contrib/bind/named/ns_maint.c @@ -0,0 +1,1100 @@ +#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 8.16 1996/08/05 08:31:30 vixie 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 && (!NoRecurse || !NoFetchGlue)) + 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, (u_long)serial, + inet_ntoa(from_addr.sin_addr), + (u_long)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 + if (debug) { + for (i = 0; i < argc; i++) + fprintf(ddt, " %s", argv[i]); + fprintf(ddt, "\n"); + } +#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", + (u_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 && + !ns_wildcard(NAME(*np))) { + 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(); + } +} |