diff options
author | peter <peter@FreeBSD.org> | 1998-05-03 04:11:49 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1998-05-03 04:11:49 +0000 |
commit | 0666320b4eda500556d2c671c9527c9000057492 (patch) | |
tree | 759849259eae9f7cb0d3ddbd7a131081c6688068 /contrib/bind/bin/named/ns_maint.c | |
parent | 58ca52f41726d17758909ddafba7b6b6766c789c (diff) | |
download | FreeBSD-src-0666320b4eda500556d2c671c9527c9000057492.zip FreeBSD-src-0666320b4eda500556d2c671c9527c9000057492.tar.gz |
Import (trimmed) ISC bind-8.1.2-t3b. This will be updated to 8.1.2 on
final release.
Obtained from: ftp.isc.org
Diffstat (limited to 'contrib/bind/bin/named/ns_maint.c')
-rw-r--r-- | contrib/bind/bin/named/ns_maint.c | 1210 |
1 files changed, 1210 insertions, 0 deletions
diff --git a/contrib/bind/bin/named/ns_maint.c b/contrib/bind/bin/named/ns_maint.c new file mode 100644 index 0000000..ad6e7f8 --- /dev/null +++ b/contrib/bind/bin/named/ns_maint.c @@ -0,0 +1,1210 @@ +#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.38 1998/03/16 19:40:25 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 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. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <signal.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +static int xfers_running, /* # of xfers running */ + xfers_deferred, /* # of needed xfers not run yet */ + qserials_running, + nxfers(struct zoneinfo *, int), + bottom_of_zone(struct databuf *, int); + +static void startxfer(struct zoneinfo *), + abortxfer(struct zoneinfo *), + addxfer(struct zoneinfo *), + tryxfer(void), + purge_z_2(struct hashbuf *, int); + +#define qserial_qfull() (qserials_running == MAXQSERIAL) + +static time_t stats_time; + + /* State of all running zone transfers */ +static struct { + pid_t xfer_pid; + int xfer_state; /* see below */ + WAIT_T xfer_status; +} xferstatus[MAX_XFERS_RUNNING]; + +#define XFER_IDLE 0 +#define XFER_RUNNING 1 +#define XFER_DONE 2 + + +/* + * Perform maintenance on all zones that need it. + */ +void +ns_maint() { + struct zoneinfo *zp; + int zonenum, deleted; + + gettime(&tt); + + ns_debug(ns_log_maint, 1, "ns_maint()"); + + for (zp = zones, zonenum = 0; zp < &zones[nzones]; zp++, zonenum++) + zone_maint(zp); + ns_debug(ns_log_maint, 1, "exit ns_maint()"); +} + +/* + * Perform routine zone maintenance. + */ +void +zone_maint(struct zoneinfo *zp) { + gettime(&tt); + + ns_debug(ns_log_maint, 1, "zone_maint('%s'); now %lu", + zp->z_origin[0] == '\0' ? "." : zp->z_origin, + (u_long)tt.tv_sec); + +#ifdef DEBUG + if (debug >= 2) + printzoneinfo((zp - zones), ns_log_maint, 2); +#endif + + switch (zp->z_type) { + + case Z_SECONDARY: + /*FALLTHROUGH*/ +#ifdef STUBS + case Z_STUB: +#endif + if (zp->z_serial != 0 && + ((zp->z_lastupdate+zp->z_expire) < (u_int32_t)tt.tv_sec)) { + zp->z_serial = 0; + } + if (zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) { + ns_retrytime(zp, tt.tv_sec); + break; + } + if (zp->z_flags & Z_XFER_RUNNING) { + abortxfer(zp); + /* + * Check again in 30 seconds in case the first + * abort doesn't work. + */ + if (zp->z_time != 0 && zp->z_time <= tt.tv_sec) + zp->z_time = tt.tv_sec + 30; + break; + } + qserial_query(zp); + break; + +#ifdef BIND_UPDATE + case Z_PRIMARY: + if (! (zp->z_flags & Z_DYNAMIC)) + break; + if (tt.tv_sec >= zp->z_soaincrtime && + zp->z_soaincrintvl > 0 && + zp->z_flags & Z_NEED_SOAUPDATE) { + if (incr_serial(zp) < 0) { + /* Try again later. */ + ns_error(ns_log_maint, + "error updating serial number for %s from %d", + zp->z_origin, + zp->z_serial); + zp->z_soaincrtime = 0; + (void)schedule_soa_update(zp, 0); + } + + } + if (tt.tv_sec >= zp->z_dumptime && + zp->z_dumpintvl > 0 && + zp->z_flags & Z_NEED_DUMP) { + if (zonedump(zp) < 0) { + /* Try again later. */ + ns_error(ns_log_maint, + "zone dump for '%s' failed, rescheduling", + zp->z_origin); + zp->z_dumptime = 0; + (void)schedule_dump(zp); + } + } + break; +#endif /* BIND_UPDATE */ + + default: + break; + } + + /* + * It is essential that we never try to set a timer in the past + * or for now because doing so could cause an infinite loop. + */ + INSIST(zp->z_time == 0 || zp->z_time > tt.tv_sec); + + sched_zone_maint(zp); +} + +static void +do_zone_maint(evContext ctx, void *uap, struct timespec due, + struct timespec inter) { + ztimer_info zti = uap; + struct zoneinfo *zp; + + INSIST(zti != NULL); + + ns_debug(ns_log_maint, 1, "do_zone_maint for zone %s (class %s)", + zti->name, p_class(zti->class)); + zp = find_zone(zti->name, zti->type, zti->class); + if (zp == NULL) { + ns_error(ns_log_maint, + "do_zone_maint: %s zone '%s' (class %s) is not authoritative", + zoneTypeString(zp), zti->name, + p_class(zti->class)); + return; + } + + free_zone_timerinfo(zp); + + zp->z_flags &= ~Z_TIMER_SET; + zone_maint(zp); +} + +/* + * Figure out the next maintenance time for the zone and set a timer. + */ +void +sched_zone_maint(struct zoneinfo *zp) { + time_t next_maint = (time_t)0; + char *zone_name; + ztimer_info zti; + + if (zp->z_time != 0) + next_maint = zp->z_time; +#ifdef BIND_UPDATE + if (zp->z_type == z_master && (zp->z_flags & Z_DYNAMIC)) { + if (zp->z_soaincrintvl > 0 && + (next_maint == 0 || next_maint > zp->z_soaincrtime)) + next_maint = zp->z_soaincrtime; + if (zp->z_dumpintvl > 0 && + (next_maint == 0 || next_maint > zp->z_dumptime)) + next_maint = zp->z_dumptime; + } +#endif + + if (next_maint != 0) { + if (next_maint < tt.tv_sec) + next_maint = tt.tv_sec; + + if (zp->z_flags & Z_TIMER_SET) { + if (next_maint == zp->z_nextmaint) { + ns_debug(ns_log_maint, 1, + "no schedule change for zone '%s'", + zp->z_origin[0] == '\0' ? "." : + zp->z_origin); + return; + } + + if (evResetTimer(ev, zp->z_timer, + do_zone_maint, zp->z_timerinfo, + evConsTime(next_maint, 0), + evConsTime(0, 0)) < 0) { + ns_error(ns_log_maint, + "evChangeTimer failed in sched_zone_maint for zone '%s': %s", + zp->z_origin[0] == '\0' ? "." : + zp->z_origin, + strerror(errno)); + return; + } + } else { + zti = (ztimer_info)memget(sizeof *zti); + if (zti == NULL) + ns_panic(ns_log_maint, 1, + "memget failed in sched_zone_maint"); + zti->name = savestr(zp->z_origin, 1); + zti->class = zp->z_class; + zti->type = zp->z_type; + if (evSetTimer(ev, do_zone_maint, zti, + evConsTime(next_maint, 0), + evConsTime(0, 0), &zp->z_timer) < 0) { + ns_error(ns_log_maint, + "evSetTimer failed in sched_zone_maint for zone '%s': %s", + zp->z_origin[0] == '\0' ? "." : + zp->z_origin, + strerror(errno)); + return; + } + zp->z_flags |= Z_TIMER_SET; + zp->z_timerinfo = zti; + } + ns_debug(ns_log_maint, 1, + "next maintenance for zone '%s' in %lu sec", + zp->z_origin[0] == '\0' ? "." : zp->z_origin, + (u_long)(next_maint - tt.tv_sec)); + } else { + if (zp->z_flags & Z_TIMER_SET) { + free_zone_timerinfo(zp); + if (evClearTimer(ev, zp->z_timer) < 0) + ns_error(ns_log_maint, + "evClearTimer failed in sched_zone_maint for zone '%s': %s", + zp->z_origin[0] == '\0' ? "." : + zp->z_origin, + strerror(errno)); + zp->z_flags &= ~Z_TIMER_SET; + } + ns_debug(ns_log_maint, 1, + "no scheduled maintenance for zone '%s'", + zp->z_origin[0] == '\0' ? "." : zp->z_origin); + } + zp->z_nextmaint = next_maint; +} + +void +ns_cleancache(evContext ctx, void *uap, + struct timespec due, + struct timespec inter) +{ + int deleted; + + gettime(&tt); + INSIST(uap == NULL); + deleted = clean_cache(hashtab, 0); + ns_info(ns_log_maint, "Cleaned cache of %d RR%s", + deleted, (deleted==1) ? "" : "s"); +} + +/* + * Mark a zone "up to date" after named-xfer tells us this or we + * discover it through the qserial_*() logic. + * The caller is responsible for calling sched_zone_maint(zp). + */ +static void +markUpToDate(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) { + struct timeval t[2]; + + t[0] = tt; + t[1] = tt; + (void) utimes(zp->z_source, t); + } + /* 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? */ +} + +void +qserial_retrytime(struct zoneinfo *zp, time_t timebase) { + zp->z_time = timebase + 5 + (rand() % 25); +} + +/* + * Query for the serial number of a zone, so that we can check to see if + * we need to transfer it. If there are too many outstanding serial + * number queries, we'll try again later. + * The caller is responsible for calling sched_zone_maint(zp). + */ +void +qserial_query(struct zoneinfo *zp) { + struct qinfo *qp; + + ns_debug(ns_log_default, 1, "qserial_query(%s)", zp->z_origin); + + if (qserial_qfull()) { + qserial_retrytime(zp, tt.tv_sec); + return; + } + + qp = sysquery(zp->z_origin, zp->z_class, T_SOA, + zp->z_addr, zp->z_addrcnt, QUERY); + if (!qp) { + ns_info(ns_log_default, "qserial_query(%s): sysquery FAILED", + zp->z_origin); + /* XXX - this is bad, we should do something */ + qserial_retrytime(zp, tt.tv_sec); + return; + } + qp->q_flags |= Q_ZSERIAL; + qp->q_zquery = zp; + zp->z_flags |= Z_QSERIAL; + zp->z_xaddr = inaddr_any; + ns_refreshtime(zp, tt.tv_sec); + qserials_running++; + ns_debug(ns_log_default, 1, "qserial_query(%s) QUEUED", zp->z_origin); +} + +void +qserial_answer(struct qinfo *qp, u_int32_t serial, struct sockaddr_in from) { + struct zoneinfo *zp = qp->q_zquery; + + ns_debug(ns_log_default, 1, "qserial_answer(%s, %u)", zp->z_origin, + 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. */ + ns_info(ns_log_default, "Err/TO getting serial# for \"%s\"", + zp->z_origin); + addxfer(zp); + } else if (SEQ_GT(serial, zp->z_serial) || !zp->z_serial) { + ns_debug(ns_log_default, 1, + "qserial_answer: zone is out of date"); + zp->z_xaddr = from.sin_addr; /* don't use qp->q_from */ + addxfer(zp); + } else if (SEQ_GT(zp->z_serial, serial)) { + if (!haveComplained((u_long)zp, (u_long)"went backward")) { + ns_notice(ns_log_default, + "Zone \"%s\" (class %d) SOA serial# (%u) rcvd from [%s] is < ours (%u)", + zp->z_origin, zp->z_class, serial, + inet_ntoa(from.sin_addr), zp->z_serial); + } + } else { + ns_debug(ns_log_default, 1, + "qserial_answer: zone serial is still OK"); + markUpToDate(zp); + sched_zone_maint(zp); + } +} + +/* + * Start an asynchronous zone transfer for a zone. + * Depends on current time being in tt. + * Caller must do sched_zone_maint(zp) after startxfer returns. + */ +static void +startxfer(struct zoneinfo *zp) { + char *argv[NSMAX + 20], argv_ns[NSMAX][MAXDNAME]; + int argc = 0, argc_ns = 0, pid, i; + u_int cnt; + char debug_str[10]; + char serial_str[10]; + char port_str[10]; + char class_str[10]; + char src_str[20]; + + ns_debug(ns_log_default, 1, "startxfer() %s", zp->z_origin); + + argv[argc++] = server_options->named_xfer; + argv[argc++] = "-z"; + argv[argc++] = zp->z_origin; + argv[argc++] = "-f"; + argv[argc++] = zp->z_source; + argv[argc++] = "-s"; + sprintf(serial_str, "%u", zp->z_serial); + argv[argc++] = serial_str; + if (zp->z_axfr_src.s_addr != 0) { + argv[argc++] = "-x"; + argv[argc++] = strcpy(src_str, inet_ntoa(zp->z_axfr_src)); + } + argv[argc++] = "-C"; + sprintf(class_str, "%d", zp->z_class); + argv[argc++] = class_str; + 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 (ina_hlong(zp->z_xaddr) != INADDR_ANY) { + /* + * Address was specified by the qserial logic, use it + * first. + */ + if (aIsUs(zp->z_xaddr) && + !haveComplained((u_long)zp, (u_long)startxfer)) { + ns_notice(ns_log_default, + "attempted to fetch zone %s from self (%s)", + zp->z_origin, inet_ntoa(zp->z_xaddr)); + } else + argv[argc++] = strcpy(argv_ns[argc_ns++], + inet_ntoa(zp->z_xaddr)); + } + /* + * Copy the server ip addresses into argv, after converting + * to ascii and saving the static inet_ntoa result. Skip zp->z_xaddr + * if seen. + */ + for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { + struct in_addr a; + + a = zp->z_addr[cnt]; + if (ina_equal(a, zp->z_xaddr)) + continue; + if (aIsUs(a) && + !haveComplained((u_long)zp, (u_long)startxfer)) { + ns_notice(ns_log_default, + "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] = NULL; + +#ifdef DEBUG + if (debug >= 1) { + char buffer[1024]; + char *curr, *last; + int len; + + curr = buffer; + last = &buffer[sizeof buffer - 1]; /* leave room for \0 */ + for (i = 0; i < argc; i++) { + len = strlen(argv[i]); + if (curr + len + 1 >= last) { + ns_debug(ns_log_xfer_in, 1, + "xfer args debug printout truncated"); + break; + } + strncpy(curr, argv[i], len); + curr += len; + *curr = ' '; + curr++; + } + *curr = '\0'; + ns_debug(ns_log_xfer_in, 1, buffer); + } +#endif /* DEBUG */ + + gettime(&tt); + 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) { + ns_error(ns_log_default, "xfer vfork: %s", strerror(errno)); + zp->z_time = tt.tv_sec + 10; + return; + } + + if (pid == 0) { + /* Child. */ + execv(server_options->named_xfer, argv); + ns_error(ns_log_default, "can't exec %s: %s", + server_options->named_xfer, strerror(errno)); + _exit(XFER_FAIL); /* Avoid duplicate buffer flushes. */ + } + /* Parent. */ + xferstatus[i].xfer_pid = pid; /* XXX - small race condition here if we + * can't hold signals */ + ns_debug(ns_log_default, 1, "started xfer child %d", pid); + zp->z_flags &= ~Z_NEED_XFER; + zp->z_flags |= Z_XFER_RUNNING; + zp->z_xferpid = pid; + xfers_running++; + if (zp->z_max_transfer_time_in) + zp->z_time = tt.tv_sec + zp->z_max_transfer_time_in; + else + zp->z_time = tt.tv_sec + server_options->max_transfer_time_in; +} + +const char * +zoneTypeString(const struct zoneinfo *zp) { + static char ret[sizeof "(4294967296?)"]; /* 2^32 */ + + switch (zp->z_type) { + case Z_MASTER: return ("master"); + case Z_SLAVE: return ("slave"); +#ifdef STUBS + case Z_STUB: return ("stub"); +#endif + case Z_CACHE: return ("cache"); + default: + sprintf(ret, "(%u?)", (u_int32_t)zp->z_type); + return (ret); + } +} + +#ifdef DEBUG +void +printzoneinfo(int zonenum, int category, int level) { + struct timeval tt; + struct zoneinfo *zp = &zones[zonenum]; + + if (debug == 0) + return; + + if (!zp->z_origin) + return; + + gettime(&tt); + + ns_debug(category, level, "zone %d: %s, class %s, type %s", zonenum, + zp->z_origin[0] ? zp->z_origin : ".", + p_class(zp->z_class), zoneTypeString(zp)); + if (zp->z_source) + ns_debug(category, level, "\tsource %s", zp->z_source); + ns_debug(category, level, "\tflags %lx, serial %u, minimum %u", + (u_long)zp->z_flags, zp->z_serial, zp->z_minimum); + ns_debug(category, level, "\trefresh %u, retry %u, expire %u", + zp->z_refresh, zp->z_retry, zp->z_expire); + if (zp->z_time) + ns_debug(category, level, "\tz_time %lu (now %lu, left: %lu)", + zp->z_time, (u_long)tt.tv_sec, + (u_long)(zp->z_time - tt.tv_sec)); + else + ns_debug(category, level, "\tz_time %lu", zp->z_time); +#ifdef BIND_UPDATE + if (zp->z_type == z_master && zp->z_flags & Z_DYNAMIC) { + ns_debug(category, level, + "\tdumpintvl %lu, soaincrintvl %lu deferupdcnt %lu", + zp->z_dumpintvl, zp->z_soaincrintvl, + zp->z_deferupdcnt); + if (zp->z_soaincrtime) + ns_debug(category, level, + "\tz_soaincrtime %lu (now %lu, left: %lu)", + zp->z_soaincrtime, (u_long)tt.tv_sec, + (u_long)(zp->z_soaincrtime - tt.tv_sec)); + else + ns_debug(category, level, "\tz_soaincrtime %lu", + zp->z_soaincrtime); + if (zp->z_dumptime) + ns_debug(category, level, + "\tz_dumptime %lu (now %lu, left: %lu)", + zp->z_dumptime, (u_long)tt.tv_sec, + (u_long)(zp->z_dumptime - tt.tv_sec)); + else + ns_debug(category, level, "\tz_dumptime %lu", + zp->z_dumptime); + } +#endif +} +#endif /* DEBUG */ + +/* clean_cache(htp, all) + * Scan the entire cache looking for expired TTL's on nonauthoritative + * data, and remove it. if `all' is true, ignore TTL and rm everything. + * notes: + * this should be lazy and eventlib driven. + * return: + * number of deleted RRs. + */ +int +clean_cache(struct hashbuf *htp, int all) { + struct databuf *dp, *pdp; + struct namebuf *np, *pnp, *npn; + struct namebuf **npp, **nppend; + int deleted = 0; + + 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; + (void)NULL) { + if (dp->d_zone == DB_Z_CACHE && + (stale(dp) || all)) { + dp = rm_datum(dp, np, pdp, NULL); + deleted++; + } else { + pdp = dp; + dp = dp->d_next; + } + } /*for(pdp)*/ + + if (np->n_hash) { + /* Call recursively to remove subdomains. */ + deleted += clean_cache(np->n_hash, all); + + /* If now empty, free it */ + if (np->n_hash->h_cnt == 0) { + rm_hash(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)*/ + return (deleted); +} + +void +purge_zone(const char *dname, struct hashbuf *htp, int class) { + const char *fname; + struct databuf *dp, *pdp; + struct namebuf *np; + struct hashbuf *phtp = htp; + int root_zone = 0; + + ns_debug(ns_log_default, 1, "purge_zone(%s,%d)", dname, class); + if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname && + !ns_wildcard(NAME(*np))) { + for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { + if (dp->d_class == class) + dp = rm_datum(dp, np, pdp, NULL); + else { + pdp = dp; + dp = dp->d_next; + } + } + + if (*dname == '\0') + root_zone = 1; + + if (np->n_hash != NULL || root_zone) { + struct hashbuf *h; + + if (root_zone) + h = htp; + else + h = np->n_hash; + + purge_z_2(h, class); + if (h->h_cnt == 0 && !root_zone) { + rm_hash(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; + + ns_debug(ns_log_default, 3, + "purge_zone: cleaning cache"); + + /* 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) { + ns_debug(ns_log_default, 3, + "purge_zone: found our selves"); + npn = rm_name(nnp,npp,pnp); + phtp->h_cnt--; + } else { + npn = nnp->n_next; + pnp = nnp; + } + } + } + } + } + } +} + +static void +purge_z_2(htp, class) + struct hashbuf *htp; + int class; +{ + struct databuf *dp, *pdp; + 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; + (void)NULL) { + if (dp->d_class == class) + dp = rm_datum(dp, np, pdp, + NULL); + 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) { + rm_hash(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(struct databuf *dp, int class) { + int ret = 0; + + for ((void)NULL; dp; dp = dp->d_next) { + if (dp->d_class != class) + continue; + if (dp->d_zone == DB_Z_CACHE) + continue; + if (dp->d_rcode) /* This should not occur. */ + continue; + if (dp->d_type != T_SOA) + continue; + ret = 1; + break; + } + ns_debug(ns_log_default, 3, "bottom_of_zone() == %d", ret); + return (ret); +} + +/* + * Handle XFER limit for a nameserver. + */ +static int +nxfers(struct zoneinfo *zp, int delta) { + struct in_addr nsa; + struct nameser *nsp; + int ret; + + if (ina_hlong(zp->z_xaddr) != INADDR_ANY) + 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(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) + ns_warning(ns_log_default, + "zone transfer timeout for \"%s\"; pid %lu missing", + zp->z_origin, (u_long)zp->z_xferpid); + else if (kill(zp->z_xferpid, SIGKILL) == -1) + ns_warning(ns_log_default, + "zone transfer timeout for \"%s\"; kill pid %lu: %s", + zp->z_origin, (u_long)zp->z_xferpid, + strerror(errno)); + else + ns_warning(ns_log_default, +"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, SIGTERM) == -1) { + if (errno == ESRCH) + /* No warning on first time, it may have just exited */ + zp->z_flags |= Z_XFER_GONE; + else { + ns_warning(ns_log_default, + "zone transfer timeout for \"%s\"; pid %lu kill failed %s", + zp->z_origin, (u_long)zp->z_xferpid, + strerror(errno)); + zp->z_flags |= Z_XFER_ABORTED; + } + } else { + ns_notice(ns_log_default, + "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. + */ +void +reapchild(evContext ctx, void *uap, int sig) { + int pid, i; + WAIT_T status; + int saved_errno = errno; + + gettime(&tt); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + 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; + ns_need(MAIN_NEED_ENDXFER); + break; + } + } + } + + errno = saved_errno; +} + +/* + * Finish processing of of finished xfers + */ +void +endxfer() { + struct zoneinfo *zp; + int exitstatus, pid, i; + WAIT_T status; + + 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); + ns_debug(ns_log_default, 1, + "\nendxfer: child %d zone %s returned status=%d termsig=%d", + pid, zp->z_origin, exitstatus, + WIFSIGNALED(status) ? WTERMSIG(status) : -1); + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) != SIGKILL) { + ns_notice(ns_log_default, + "named-xfer \"%s\" exited with signal %d", + zp->z_origin[0]?zp->z_origin:".", + WTERMSIG(status)); + } + ns_retrytime(zp, tt.tv_sec); + sched_zone_maint(zp); + } else { + switch (exitstatus) { + case XFER_UPTODATE: + markUpToDate(zp); + sched_zone_maint(zp); + break; + + case XFER_SUCCESS: + /* XXX should incorporate loadxfer() */ + zp->z_flags |= Z_NEED_RELOAD; + zp->z_flags &= ~Z_SYSLOGGED; + ns_need(MAIN_NEED_ZONELOAD); + break; + + case XFER_TIMEOUT: + if (!(zp->z_flags & Z_SYSLOGGED)) { + zp->z_flags |= Z_SYSLOGGED; + ns_notice(ns_log_default, + "zoneref: Masters for secondary zone \"%s\" unreachable", + zp->z_origin); + } + ns_retrytime(zp, tt.tv_sec); + sched_zone_maint(zp); + break; + + default: + if (!(zp->z_flags & Z_SYSLOGGED)) { + zp->z_flags |= Z_SYSLOGGED; + ns_notice(ns_log_default, + "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); + sched_zone_maint(zp); + break; + } + break; + } + } + xferstatus[i].xfer_state = XFER_IDLE; + xferstatus[i].xfer_pid = 0; + } + tryxfer(); +} + +/* + * Try to start some xfers - new "fair scheduler" by Bob Halley @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) + ns_debug(ns_log_default, 3, "zones changed: %p != %p", + lastzones, zones); + lastzones = zones; + zp = zones; + } + + /* did zones shrink? */ + if (lastnzones > nzones) { + ns_debug(ns_log_default, 3, "zones shrunk"); + zp = zones; + } + lastnzones = nzones; + + if (zp == zones) + stopzp = &zones[nzones-1]; + else + stopzp = zp - 1; + + ns_debug(ns_log_default, 3, + "tryxfer start zp=%p stopzp=%p def=%d running=%d", + zp, stopzp, xfers_deferred, xfers_running); + + startzp = zp; + for (;;) { + int xfers; + + if (!xfers_deferred || + xfers_running >= server_options->transfers_in) + break; + + if ((xfers = nxfers(zp, 0)) != -1 && + xfers < server_options->transfers_per_ns && + (zp->z_flags & Z_NEED_XFER)) { + nxfers(zp, 1); + xfers_deferred--; + startxfer(zp); + sched_zone_maint(zp); + } + + if (zp == stopzp) { + ns_debug(ns_log_default, 3, "tryxfer stop mark"); + zp = startzp; + break; + } + + zp++; + /* wrap around? */ + if (zp == &zones[nzones]) + zp = zones; + } + ns_debug(ns_log_default, 3, "tryxfer stop zp=%p", zp); +} + +/* + * Reload zones whose transfers have completed. + */ +void +loadxfer() { + struct zoneinfo *zp; + + gettime(&tt); + for (zp = zones; zp < &zones[nzones]; zp++) { + if (zp->z_flags & Z_NEED_RELOAD) { + ns_debug(ns_log_default, 1, "loadxfer() \"%s\"", + zp->z_origin[0] ? zp->z_origin : "."); + zp->z_flags &= ~(Z_NEED_RELOAD|Z_AUTH); +/* XXX this is bad, should be done in ns_reload() for primary changes. */ + ns_stopxfrs(zp); + purge_zone(zp->z_origin, hashtab, zp->z_class); + 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); + sched_zone_maint(zp); + } + } +} + +/* + * Add this zone to the set of those needing transfers. + */ +static void +addxfer(struct zoneinfo *zp) { + if (!(zp->z_flags & Z_NEED_XFER)) { + zp->z_flags |= Z_NEED_XFER; + xfers_deferred++; + tryxfer(); + } +} + +/* + * Flush and reload data base. + */ +void +ns_reload() { + ns_notice(ns_log_default, "reloading nameserver"); + + qflush(); + sq_flush(NULL); +#ifdef FORCED_RELOAD + reloading = 1; /* to force transfer if secondary and backing up */ +#endif + ns_init(conffile); + time(&resettime); +#ifdef FORCED_RELOAD + reloading = 0; +#endif /* FORCED_RELOAD */ + ns_notice(ns_log_default, "Ready to answer queries."); +} |