diff options
Diffstat (limited to 'contrib/bind/bin/named/ns_maint.c')
-rw-r--r-- | contrib/bind/bin/named/ns_maint.c | 901 |
1 files changed, 717 insertions, 184 deletions
diff --git a/contrib/bind/bin/named/ns_maint.c b/contrib/bind/bin/named/ns_maint.c index 75568ff21786..69a8fc360846 100644 --- a/contrib/bind/bin/named/ns_maint.c +++ b/contrib/bind/bin/named/ns_maint.c @@ -1,6 +1,6 @@ #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.39 1998/04/14 00:34:39 halley Exp $"; +static const char sccsid[] = "@(#)ns_maint.c 4.39 (Berkeley) 3/2/91"; +static const char rcsid[] = "$Id: ns_maint.c,v 8.95 1999/10/13 16:39:09 vixie Exp $"; #endif /* not lint */ /* @@ -57,7 +57,7 @@ static char rcsid[] = "$Id: ns_maint.c,v 8.39 1998/04/14 00:34:39 halley Exp $"; */ /* - * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * Portions Copyright (c) 1996-1999 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 @@ -73,6 +73,26 @@ static char rcsid[] = "$Id: ns_maint.c,v 8.39 1998/04/14 00:34:39 halley Exp $"; * SOFTWARE. */ +/* + * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. + * + * 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 Check Point Software Technologies Incorporated 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 CHECK POINT SOFTWARE TECHNOLOGIES + * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. + * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED + * 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> @@ -80,11 +100,13 @@ static char rcsid[] = "$Id: ns_maint.c,v 8.39 1998/04/14 00:34:39 halley Exp $"; #include <sys/socket.h> #include <sys/wait.h> #include <sys/stat.h> +#include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <arpa/nameser.h> +#include <assert.h> #include <errno.h> #include <signal.h> #include <resolv.h> @@ -99,25 +121,25 @@ static char rcsid[] = "$Id: ns_maint.c,v 8.39 1998/04/14 00:34:39 halley Exp $"; #include <isc/logging.h> #include <isc/memcluster.h> +#include <isc/dst.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), +static int 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) +#ifndef HAVE_SPAWNXFER +static pid_t spawnxfer(char **, struct zoneinfo *); +#endif -static time_t stats_time; +static time_t stats_time; /* Redundant ??? XXX ogud */ /* State of all running zone transfers */ static struct { @@ -132,23 +154,6 @@ static struct { /* - * 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 @@ -173,10 +178,16 @@ zone_maint(struct zoneinfo *zp) { #endif if (zp->z_serial != 0 && ((zp->z_lastupdate+zp->z_expire) < (u_int32_t)tt.tv_sec)) { + /* calls purge_zone */ + do_reload(zp->z_origin, zp->z_type, zp->z_class, 0); + /* reset zone state */ + zp->z_flags &= ~Z_AUTH; + zp->z_refresh = INIT_REFRESH; + zp->z_retry = INIT_REFRESH; zp->z_serial = 0; - /* XXX should we clear Z_AUTH here? */ } - if (zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) { + if ((zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) != 0) + { ns_retrytime(zp, tt.tv_sec); break; } @@ -190,12 +201,26 @@ zone_maint(struct zoneinfo *zp) { zp->z_time = tt.tv_sec + 30; break; } - qserial_query(zp); + /* + * If we don't have the zone loaded or dialup is off + * or we attempted a qserial_query before and the queue was + * full attempt to verify / load the zone. + */ + if ((zp->z_serial == 0) || (zp->z_flags & Z_NEED_QSERIAL) || + (zp->z_dialup == zdialup_no) || + (zp->z_dialup == zdialup_use_default && + NS_OPTION_P(OPTION_NODIALUP))) + qserial_query(zp); + else { + ns_info(ns_log_default, "Suppressed qserial_query(%s)", + *(zp->z_origin) ? zp->z_origin : "."); + ns_refreshtime(zp, tt.tv_sec); + } break; #ifdef BIND_UPDATE case Z_PRIMARY: - if (! (zp->z_flags & Z_DYNAMIC)) + if ((zp->z_flags & Z_DYNAMIC) == 0) break; if (tt.tv_sec >= zp->z_soaincrtime && zp->z_soaincrintvl > 0 && @@ -214,7 +239,7 @@ zone_maint(struct zoneinfo *zp) { if (tt.tv_sec >= zp->z_dumptime && zp->z_dumpintvl > 0 && zp->z_flags & Z_NEED_DUMP) { - if (zonedump(zp) < 0) { + if (zonedump(zp, ISNOTIXFR) < 0) { /* Try again later. */ ns_error(ns_log_maint, "zone dump for '%s' failed, rescheduling", @@ -222,6 +247,8 @@ zone_maint(struct zoneinfo *zp) { zp->z_dumptime = 0; (void)schedule_dump(zp); } + if (zp->z_maintain_ixfr_base) + ixfr_log_maint(zp); } break; #endif /* BIND_UPDATE */ @@ -249,11 +276,18 @@ do_zone_maint(evContext ctx, void *uap, struct timespec due, 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); + zp = find_zone(zti->name, 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, + zoneTypeString(zti->type), zti->name, + p_class(zti->class)); + return; + } + if (zp->z_type != zti->type) { + ns_error(ns_log_maint, + "do_zone_maint: %s zone '%s' (class %s) has changed its type", + zoneTypeString(zti->type), zti->name, p_class(zti->class)); return; } @@ -270,13 +304,12 @@ do_zone_maint(evContext ctx, void *uap, struct timespec due, 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_type == z_master && (zp->z_flags & Z_DYNAMIC) != 0) { if (zp->z_soaincrintvl > 0 && (next_maint == 0 || next_maint > zp->z_soaincrtime)) next_maint = zp->z_soaincrtime; @@ -363,10 +396,46 @@ ns_cleancache(evContext ctx, void *uap, gettime(&tt); INSIST(uap == NULL); deleted = clean_cache(hashtab, 0); - ns_info(ns_log_maint, "Cleaned cache of %d RR%s", + ns_info(ns_log_maint, "Cleaned cache of %d RRset%s", deleted, (deleted==1) ? "" : "s"); } +void +ns_heartbeat(evContext ctx, void *uap, struct timespec due, + struct timespec inter) +{ + struct zoneinfo *zp; + + gettime(&tt); + INSIST(uap == NULL); + + for (zp = zones; zp < &zones[nzones]; zp++) { + enum zonetype zt = zp->z_type; + + if ((zt == z_nil) || + (zp->z_dialup == zdialup_no) || + (zp->z_dialup == zdialup_use_default && + NS_OPTION_P(OPTION_NODIALUP))) + continue; +#ifdef BIND_NOTIFY + if ((zp->z_notify == znotify_no) || + ((zp->z_notify == znotify_use_default) && + NS_OPTION_P(OPTION_NONOTIFY))) + continue; +#endif + if ((zt == z_slave || zt == z_stub) && + (zp->z_flags & + (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL|Z_XFER_RUNNING) + ) == 0) { + ns_info(ns_log_default, + "Heartbeat: qserial \"%s\"", + *(zp->z_origin) ? zp->z_origin : "."); + qserial_query(zp); + } + } +} + + /* * Mark a zone "up to date" after named-xfer tells us this or we * discover it through the qserial_*() logic. @@ -418,57 +487,131 @@ qserial_query(struct zoneinfo *zp) { ns_debug(ns_log_default, 1, "qserial_query(%s)", zp->z_origin); - if (qserial_qfull()) { + if (qserials_running >= server_options->serial_queries) { qserial_retrytime(zp, tt.tv_sec); + zp->z_flags |= Z_NEED_QSERIAL; 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); + zp->z_addr, zp->z_addrcnt, + ntohs(zp->z_port) ? zp->z_port : ns_port, + QUERY); + if (qp == NULL) { + ns_debug(ns_log_default, 1, + "qserial_query(%s): sysquery FAILED", + zp->z_origin); /* XXX - this is bad, we should do something */ qserial_retrytime(zp, tt.tv_sec); + zp->z_flags |= Z_NEED_QSERIAL; return; } qp->q_flags |= Q_ZSERIAL; qp->q_zquery = zp; zp->z_flags |= Z_QSERIAL; - zp->z_xaddr = inaddr_any; + zp->z_flags &= ~Z_NEED_QSERIAL; + zp->z_xaddrcnt = 0; ns_refreshtime(zp, tt.tv_sec); qserials_running++; ns_debug(ns_log_default, 1, "qserial_query(%s) QUEUED", zp->z_origin); } +static int +qserv_compare(const void *a, const void *b) { + const struct qserv *qs1 = a, *qs2 = b; + u_int32_t s1 = qs1->serial, s2 = qs2->serial; + + /* Note that we sort the "best" serial numbers to the front. */ + if (s1 == s2) + return (0); + if (s1 == 0) + return (-1); + if (s2 == 0) + return (1); + if (!SEQ_GT(s1, s2)) + return (1); + assert(SEQ_GT(s1, s2)); + return (-1); +} + void -qserial_answer(struct qinfo *qp, u_int32_t serial, struct sockaddr_in from) { +qserial_answer(struct qinfo *qp) { struct zoneinfo *zp = qp->q_zquery; + struct qserv *qs = NULL; + u_int32_t serial = 0; + int n, cnt = 0; - ns_debug(ns_log_default, 1, "qserial_answer(%s, %u)", zp->z_origin, - serial); + /* Take this query out of the global quotas. */ zp->z_flags &= ~Z_QSERIAL; qp->q_flags &= ~Q_ZSERIAL; /* keeps us from being called twice */ qserials_running--; + + /* Find best serial among those returned. */ + for (n = 0; n < qp->q_naddr; n++) { + qs = &qp->q_addr[n]; + ns_debug(ns_log_default, 1, "qserial_answer(%s): [%s] -> %lu", + zp->z_origin, inet_ntoa(qs->ns_addr.sin_addr), + qs->serial); + /* Don't consider serials which weren't set by a response. */ + if (qs->serial == 0) + continue; + /* Count valid answers. */ + cnt++; + /* Remove from consideration serials which aren't "better." */ + if (zp->z_serial != 0 && !SEQ_GT(qs->serial, zp->z_serial)) { + if (serial == 0 && qs->serial == zp->z_serial) + serial = qs->serial; + + if (qs->serial != zp->z_serial) + ns_notice(ns_log_xfer_in, + "Zone \"%s\" (%s) SOA serial# (%lu) rcvd from [%s] is < ours (%lu)%s", + zp->z_origin, p_class(zp->z_class), + qs->serial, + inet_ntoa(qs->ns_addr.sin_addr), + zp->z_serial, qp->q_naddr != 1 ? + ": skipping" : ""); + qs->serial = 0; + continue; + } + if (serial == 0 || SEQ_GT(qs->serial, serial)) + serial = qs->serial; + } + + /* If we have an existing serial number, then sort by "better." */ + if (zp->z_serial != 0) { + qsort(qp->q_addr, qp->q_naddr, sizeof(struct qserv), + qserv_compare); + for (n = 0; n < qp->q_naddr; n++) { + qs = &qp->q_addr[n]; + ns_debug(ns_log_default, 1, + "qserial_answer after sort: [%s] -> %lu", + inet_ntoa(qs->ns_addr.sin_addr), + qs->serial); + } + } + + /* Now see about kicking off an inbound transfer. */ 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); + /* An error occurred, or the all queries timed out. */ + if (qp->q_naddr != cnt) + ns_info(ns_log_xfer_in, + "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, + } else if (zp->z_serial == 0 || SEQ_GT(serial, zp->z_serial)) { + ns_debug(ns_log_xfer_in, 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); + /* Use all servers whose serials are better than ours. */ + zp->z_xaddrcnt = 0; + for (n = 0; n < qp->q_naddr; n++) { + qs = &qp->q_addr[n]; + if (qs->serial != 0) + zp->z_xaddr[zp->z_xaddrcnt++] = + qs->ns_addr.sin_addr; } - } else { - ns_debug(ns_log_default, 1, + addxfer(zp); + } else if (zp->z_serial == serial) { + ns_debug(ns_log_xfer_in, 1, "qserial_answer: zone serial is still OK"); markUpToDate(zp); sched_zone_maint(zp); @@ -476,34 +619,86 @@ qserial_answer(struct qinfo *qp, u_int32_t serial, struct sockaddr_in from) { } /* - * 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. + * Writes TSIG key info for an address to a file, optionally opening it first. + */ +static int +write_tsig_info(struct in_addr addr, char *name, int *fd, int creat_failed) { + server_info si; + DST_KEY *dst_key; + int tsig_fd = *fd; + char tsig_str[1024], secret_buf64[172]; + u_char secret_buf[128]; + int secret_len; + + si = find_server(addr); + if (si == NULL || si->key_list == NULL || si->key_list->first == NULL) + return(0); + dst_key = si->key_list->first->key; + if (tsig_fd < 0 && creat_failed == 0) { + *fd = tsig_fd = creat(name, S_IRUSR); + if (tsig_fd < 0) { + ns_warning(ns_log_default, + "write_tsig_info: creat(%s) for TSIG info failed", + name); + return(-1); + } + } + if (creat_failed != 0) + return(-1); + memset(secret_buf, 0, sizeof(secret_buf)); + secret_len = dst_key_to_buffer(dst_key, secret_buf, sizeof(secret_buf)); + b64_ntop(secret_buf, secret_len, secret_buf64, sizeof(secret_buf64)); + sprintf(tsig_str, "%s\n%s\n%d\n%s\n", + inet_ntoa(addr), dst_key->dk_key_name, dst_key->dk_alg, + secret_buf64); + write(tsig_fd, tsig_str, strlen(tsig_str)); + return (0); +} + +/* + * Start an asynchronous zone transfer for a zone. Depends on current time + * being in tt. Caller must do a sched_zone_maint(zp) after we return. */ static void startxfer(struct zoneinfo *zp) { - char *argv[NSMAX + 20], argv_ns[NSMAX][MAXDNAME]; - int argc = 0, argc_ns = 0, pid, i; + char *argv[NSMAX*2 + 20], argv_ns[NSMAX][MAXDNAME]; + int argc = 0, argc_ns = 0, i; + pid_t pid; u_int cnt; char debug_str[10]; char serial_str[10]; char port_str[10]; char class_str[10]; char src_str[20]; + int tsig_fd = -1; + char tsig_name[MAXPATHLEN+1], *s; + int tsig_ret = 0; - ns_debug(ns_log_default, 1, "startxfer() %s", zp->z_origin); + ns_debug(ns_log_default, 1, "startxfer() %s", + zp->z_origin[0] != '\0' ? 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) { +#ifdef BIND_IXFR + if (zp->z_ixfr_tmp) { + argv[argc++] = "-i"; + argv[argc++] = zp->z_ixfr_tmp; + } +#endif + if (zp->z_serial != 0) { + argv[argc++] = "-s"; + sprintf(serial_str, "%u", zp->z_serial); + argv[argc++] = serial_str; + } + if (zp->z_axfr_src.s_addr != 0 || + server_options->axfr_src.s_addr != 0) { argv[argc++] = "-x"; - argv[argc++] = strcpy(src_str, inet_ntoa(zp->z_axfr_src)); + argv[argc++] = strcpy(src_str, inet_ntoa( + (zp->z_axfr_src.s_addr != 0) ? zp->z_axfr_src : + server_options->axfr_src)); } argv[argc++] = "-C"; sprintf(class_str, "%d", zp->z_class); @@ -511,8 +706,14 @@ startxfer(struct zoneinfo *zp) { if (zp->z_flags & Z_SYSLOGGED) argv[argc++] = "-q"; argv[argc++] = "-P"; - sprintf(port_str, "%d", ns_port); + sprintf(port_str, "%d", ntohs(zp->z_port) != 0 ? zp->z_port : ns_port); argv[argc++] = port_str; + argv[argc++] = "-T"; + sprintf(tsig_name, "%s.%d", zp->z_origin, getpid()); + s = tsig_name; + while ((s = strchr(s, '/')) != NULL) + *s = '_'; + argv[argc++] = tsig_name; #ifdef STUBS if (zp->z_type == Z_STUB) argv[argc++] = "-S"; @@ -531,40 +732,45 @@ startxfer(struct zoneinfo *zp) { } #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)); + if (zp->z_xaddrcnt == 0) { + for (zp->z_xaddrcnt = 0; + zp->z_xaddrcnt < zp->z_addrcnt; + zp->z_xaddrcnt++) + zp->z_xaddr[zp->z_xaddrcnt] = + zp->z_addr[zp->z_xaddrcnt]; } /* * Copy the server ip addresses into argv, after converting - * to ascii and saving the static inet_ntoa result. Skip zp->z_xaddr - * if seen. + * to ascii and saving the static inet_ntoa result. + * Also, send TSIG key info into a file for the child. */ - for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { + for (cnt = 0; cnt < zp->z_xaddrcnt; 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)); + a = zp->z_xaddr[cnt]; + if (aIsUs(a) && ns_port == zp->z_port) { + if (!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)); +#ifdef BIND_IXFR + if (zp->z_ixfr_tmp != NULL) { + server_info si = find_server(a); + + if (si != NULL && + (si->flags & SERVER_INFO_SUPPORT_IXFR) != 0) + argv[argc++] = "ixfr"; + else + argv[argc++] = "axfr"; + } +#endif + tsig_ret = write_tsig_info(a, tsig_name, &tsig_fd, tsig_ret); } + if (tsig_fd > 0) + close(tsig_fd); argv[argc] = NULL; @@ -594,26 +800,21 @@ startxfer(struct zoneinfo *zp) { #endif /* DEBUG */ gettime(&tt); - for (i = 0; i < MAX_XFERS_RUNNING; i++) { - if (xferstatus[i].xfer_pid == 0) { - xferstatus[i].xfer_state = XFER_RUNNING; + for (i = 0; i < MAX_XFERS_RUNNING; i++) + if (xferstatus[i].xfer_pid == 0) break; - } - } - if ((pid = vfork()) == -1) { - ns_error(ns_log_default, "xfer vfork: %s", strerror(errno)); + if (i == MAX_XFERS_RUNNING) { + ns_warning(ns_log_default, + "startxfer: too many xfers running"); zp->z_time = tt.tv_sec + 10; + (void)nxfers(zp, -1); 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. */ + + if ((pid = spawnxfer(argv, zp)) == -1) + unlink(tsig_name); + + xferstatus[i].xfer_state = XFER_RUNNING; 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); @@ -628,18 +829,20 @@ startxfer(struct zoneinfo *zp) { } const char * -zoneTypeString(const struct zoneinfo *zp) { +zoneTypeString(u_int type) { static char ret[sizeof "(4294967296?)"]; /* 2^32 */ - switch (zp->z_type) { + switch (type) { case Z_MASTER: return ("master"); case Z_SLAVE: return ("slave"); #ifdef STUBS case Z_STUB: return ("stub"); #endif + case Z_HINT: return ("hint"); case Z_CACHE: return ("cache"); + case Z_FORWARD: return ("forward"); default: - sprintf(ret, "(%u?)", (u_int32_t)zp->z_type); + sprintf(ret, "(%u?)", type); return (ret); } } @@ -660,7 +863,7 @@ printzoneinfo(int zonenum, int category, int level) { 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)); + p_class(zp->z_class), zoneTypeString(zp->z_type)); if (zp->z_source) ns_debug(category, level, "\tsource %s", zp->z_source); ns_debug(category, level, "\tflags %lx, serial %u, minimum %u", @@ -674,7 +877,7 @@ printzoneinfo(int zonenum, int category, int level) { 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) { + if (zp->z_type == z_master && (zp->z_flags & Z_DYNAMIC) != 0) { ns_debug(category, level, "\tdumpintvl %lu, soaincrintvl %lu deferupdcnt %lu", zp->z_dumpintvl, zp->z_soaincrintvl, @@ -700,13 +903,58 @@ printzoneinfo(int zonenum, int category, int level) { } #endif /* DEBUG */ +/* + * Remove all cached data below dname, class independent. + */ +void +clean_cache_from(char *dname, struct hashbuf *htp) { + const char *fname; + struct databuf *dp, *pdp; + struct namebuf *np; + struct hashbuf *phtp = htp; + int root_zone = 0; + + ns_debug(ns_log_default, 1, "clean_cache_from(%s)", dname); + 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_zone == DB_Z_CACHE) + 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; + (void)clean_cache(h, 1); + if (h->h_cnt == 0 && !root_zone) { + rm_hash(np->n_hash); + np->n_hash = NULL; + } + } + + if (!root_zone && np->n_hash == NULL && np->n_data == NULL) + (void) purge_node(htp, np); + } +} + /* 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. + * number of deleted RRs (all=1) or RRsets (all=0). */ int clean_cache(struct hashbuf *htp, int all) { @@ -718,12 +966,17 @@ clean_cache(struct hashbuf *htp, int all) { nppend = htp->h_tab + htp->h_size; for (npp = htp->h_tab; npp < nppend; npp++) { for (pnp = NULL, np = *npp; np != NULL; np = npn) { + again: for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { - if (dp->d_zone == DB_Z_CACHE && - (stale(dp) || all)) { + if (all && dp->d_zone == DB_Z_CACHE) { dp = rm_datum(dp, np, pdp, NULL); deleted++; + } else if (dp->d_zone == DB_Z_CACHE && + stale(dp)) { + delete_all(np, dp->d_class, dp->d_type); + deleted++; + goto again; } else { pdp = dp; dp = dp->d_next; @@ -753,6 +1006,78 @@ clean_cache(struct hashbuf *htp, int all) { return (deleted); } +/* struct namebuf * + * purge_node(htp, np) + * Remove entry from cache. + * Prerequisites: + * Node is empty and has no children. + * Paramters: + * htp - root of recursive hash table this node is part of. + * np - the node to be deleted. + * Return: + * pointer to parent. + */ +struct namebuf * +purge_node(struct hashbuf *htp, struct namebuf *np) { + struct namebuf **npp, **nppend; + struct namebuf *npn, *pnp, *nnp, *parent; + struct hashbuf *phtp; + + ns_debug(ns_log_default, 3, "purge_node: cleaning cache"); + INSIST(np->n_hash == NULL && np->n_data == NULL); + + /* Walk parent hashtable looking for ourself. */ + parent = np->n_parent; + if (parent != NULL) + phtp = parent->n_hash; + else + phtp = htp; + + if (phtp == NULL) { + /* XXX why shouldn't we panic? */ + } else { + 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_node: found ourself"); + npn = rm_name(nnp, npp, pnp); + phtp->h_cnt--; + } else { + npn = nnp->n_next; + pnp = nnp; + } + } + } + } + return (parent); +} + +void +remove_zone(struct zoneinfo *zp, const char *verb) { +#ifdef BIND_UPDATE + /* + * A dynamic zone might have changed, so we + * need to dump it before removing it. + */ + if ((zp->z_flags & Z_DYNAMIC) != 0 && + ((zp->z_flags & Z_NEED_SOAUPDATE) != 0 || + (zp->z_flags & Z_NEED_DUMP) != 0)) + (void) zonedump(zp, ISNOTIXFR); +#endif + ns_stopxfrs(zp); + do_reload(zp->z_origin, zp->z_type, zp->z_class, 1); + ns_notice(ns_log_config, "%s zone \"%s\" (%s) %s", + zoneTypeString(zp->z_type), zp->z_origin, + p_class(zp->z_class), verb); + free_zone_contents(zp, 1); + memset(zp, 0, sizeof(*zp)); + zp->z_type = z_nil; /* Pedantic; memset() did it. */ + INIT_LINK(zp, z_reloadlink); + free_zone(zp); +} + void purge_zone(const char *dname, struct hashbuf *htp, int class) { const char *fname; @@ -783,7 +1108,6 @@ purge_zone(const char *dname, struct hashbuf *htp, int class) { h = htp; else h = np->n_hash; - purge_z_2(h, class); if (h->h_cnt == 0 && !root_zone) { rm_hash(np->n_hash); @@ -791,39 +1115,8 @@ purge_zone(const char *dname, struct hashbuf *htp, int class) { } } - /* 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; - } - } - } - } - } + if (!root_zone && np->n_hash == NULL && np->n_data == NULL) + (void) purge_node(htp, np); } } @@ -903,12 +1196,12 @@ nxfers(struct zoneinfo *zp, int delta) { 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 + if (zp->z_xaddrcnt != 0) + nsa = zp->z_xaddr[0]; /* first ns holds zone's xfer limit */ + else if (zp->z_addrcnt != 0) nsa = zp->z_addr[0]; /* first ns holds zone's xfer limit */ + else + return (-1); if (!(nsp = nameserFind(nsa, NS_F_INSERT))) return (-1); /* probably ENOMEM */ @@ -976,27 +1269,25 @@ pid %lu - forgetting, processes may accumulate", } /* - * SIGCHLD signal handler: process exit of xfer's. + * Process exit of xfer's. */ void -reapchild(evContext ctx, void *uap, int sig) { - int pid, i; +reapchild(void) { + int i; + pid_t pid; WAIT_T status; - int saved_errno = errno; gettime(&tt); - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + while ((pid = (pid_t)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); + ns_need(main_need_endxfer); break; } } } - - errno = saved_errno; } /* @@ -1005,7 +1296,8 @@ reapchild(evContext ctx, void *uap, int sig) { void endxfer() { struct zoneinfo *zp; - int exitstatus, pid, i; + int exitstatus, i; + pid_t pid; WAIT_T status; gettime(&tt); @@ -1045,11 +1337,43 @@ endxfer() { sched_zone_maint(zp); break; - case XFER_SUCCESS: + case XFER_SUCCESSAXFR: + case XFER_SUCCESSAXFRIXFRFILE: + zp->z_xferpid = XFER_ISAXFR; + if (exitstatus == XFER_SUCCESSAXFRIXFRFILE) { + zp->z_xferpid = XFER_ISAXFRIXFR; + } + movefile(zp->z_ixfr_tmp, zp->z_source); /* XXX should incorporate loadxfer() */ zp->z_flags |= Z_NEED_RELOAD; zp->z_flags &= ~Z_SYSLOGGED; - ns_need(MAIN_NEED_ZONELOAD); + ns_need(main_need_zoneload); + break; + + case XFER_SUCCESSIXFR: + zp->z_xferpid = XFER_ISIXFR; + zp->z_log_size_ixfr++; + ns_notice(ns_log_default, + "IXFR Success %s", + zp->z_ixfr_tmp); + if (merge_logs(zp, zp->z_ixfr_tmp) >= 0) { + ns_notice(ns_log_default, + "IXFR Merge success %s", + zp->z_ixfr_tmp); + + (void)unlink(zp->z_updatelog); + (void)unlink(zp->z_ixfr_base); + movefile(zp->z_ixfr_tmp, + zp->z_ixfr_base); + (void)unlink(zp->z_ixfr_tmp); + if (zonedump(zp, ISIXFR) < 0) + ns_warning(ns_log_db, + "error in write ixfr updates to zone file %s", + zp ->z_source); + } else + ns_notice(ns_log_default, + "IXFR Merge failed %s", + zp->z_ixfr_tmp); break; case XFER_TIMEOUT: @@ -1157,8 +1481,11 @@ tryxfer() { * Reload zones whose transfers have completed. */ void -loadxfer() { +loadxfer(void) { struct zoneinfo *zp; + u_int32_t old_serial,new_serial; + char *tmpnom; + int isixfr; gettime(&tt); for (zp = zones; zp < &zones[nzones]; zp++) { @@ -1166,11 +1493,35 @@ loadxfer() { 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. */ +/* XXX this is bad, should be done in ns_zreload() 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)) + old_serial = zp->z_serial; + if (zp->z_xferpid == XFER_ISIXFR) { + tmpnom = zp->z_ixfr_tmp; + isixfr = ISIXFR; + } else { + tmpnom = zp->z_source; + purge_zone(zp->z_origin, hashtab, zp->z_class); + isixfr = ISNOTIXFR; + } + if (zp->z_xferpid == XFER_ISAXFRIXFR) { + tmpnom= zp->z_source; + purge_zone(zp->z_origin, hashtab, zp->z_class); + isixfr = ISNOTIXFR; + } + + if (!db_load(tmpnom, zp->z_origin, zp, NULL, isixfr)) { zp->z_flags |= Z_AUTH; + if (isixfr == ISIXFR) { + new_serial= zp ->z_serial; + ns_warning(ns_log_db, "ISIXFR"); + ns_warning(ns_log_db, "error in updating ixfr data base file %s from %s", zp -> z_ixfr_base, zp ->z_ixfr_tmp); + if (zonedump(zp,ISIXFR)<0) + ns_warning(ns_log_db, "error in write ixfr updates to zone file %s", zp ->z_source); + + } + } + zp->z_xferpid = 0; if (zp->z_flags & Z_TMP_FILE) (void) unlink(zp->z_source); sched_zone_maint(zp); @@ -1181,7 +1532,7 @@ loadxfer() { /* * Add this zone to the set of those needing transfers. */ -static void +void addxfer(struct zoneinfo *zp) { if (!(zp->z_flags & Z_NEED_XFER)) { zp->z_flags |= Z_NEED_XFER; @@ -1191,21 +1542,203 @@ addxfer(struct zoneinfo *zp) { } /* - * Flush and reload data base. + * Mark one zone as requiring a reload. + * Note that it should be called with signals blocked, + * and should not allocate memory (since it can be called from a sighandler). + */ +const char * +deferred_reload_unsafe(struct zoneinfo *zp) { + INSIST(zp->z_type != z_nil); + if (!zonefile_changed_p(zp)) + return ("Zone file has not changed."); + if (LINKED(zp, z_reloadlink)) + return ("Zone is already scheduled for reloading."); + APPEND(reloadingzones, zp, z_reloadlink); + ns_need_unsafe(main_need_zreload); + return ("Zone is now scheduled for reloading."); +} + +/* + * If we've loaded this file, and the file has not been modified and contains + * no $INCLUDE, then there's no need to reload. + */ +int +zonefile_changed_p(struct zoneinfo *zp) { + struct stat sb; + + INSIST(zp->z_type != z_nil); + return ((zp->z_flags & Z_INCLUDE) != 0 || + stat(zp->z_source, &sb) == -1 || + zp->z_ftime != sb.st_mtime); +} + +int +reload_master(struct zoneinfo *zp) { + INSIST(zp->z_type == z_master); + zp->z_flags &= ~Z_AUTH; + ns_stopxfrs(zp); + /* XXX what about parent zones? */ + purge_zone(zp->z_origin, hashtab, zp->z_class); + ns_debug(ns_log_config, 1, "reloading zone"); +#ifdef BIND_UPDATE + if ((zp->z_flags & Z_DYNAMIC) != 0) { + struct stat sb; + + if (stat(zp->z_source, &sb) < 0) + ns_error(ns_log_config, "stat(%s) failed: %s", + zp->z_source, strerror(errno)); + else { + if ((sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0) + ns_warning(ns_log_config, + "dynamic zone file '%s' is writable", + zp->z_source); + } + } +#endif + if (!db_load(zp->z_source, zp->z_origin, zp, NULL, ISNOTIXFR)) + zp->z_flags |= Z_AUTH; + zp->z_refresh = 0; /* no maintenance needed */ + zp->z_time = 0; +#ifdef BIND_UPDATE + zp->z_lastupdate = 0; + if ((zp->z_flags & Z_DYNAMIC) != 0) + if (merge_logs(zp, zp->z_updatelog) == 1) + return (1); +#endif + return (0); +} + +/* + * Called by main() when main_need_zreload has been set. Should pull one + * zone off of the reloadingzones list and reload it, then if the list is + * not then empty, should turn main_need_zreload on again for the next call. + * It is not an error to call this when the reloadingzones list is empty. + */ +void +ns_zreload(void) { + struct zoneinfo *zp; + + block_signals(); + if (EMPTY(reloadingzones)) { + unblock_signals(); + return; + } + zp = HEAD(reloadingzones); + UNLINK(reloadingzones, zp, z_reloadlink); + unblock_signals(); + + reload_master(zp); + + block_signals(); + if (!EMPTY(reloadingzones)) + ns_need_unsafe(main_need_zreload); + unblock_signals(); +} + +/* + * Flush and reload configuration file and data base. */ void -ns_reload() { +ns_reload(void) { ns_notice(ns_log_default, "reloading nameserver"); + INSIST(reloading == 0); qflush(); sq_flush(NULL); -#ifdef FORCED_RELOAD - reloading = 1; /* to force transfer if secondary and backing up */ -#endif + reloading++; /* To force transfer if secondary and backing up. */ ns_init(conffile); time(&resettime); -#ifdef FORCED_RELOAD - reloading = 0; -#endif /* FORCED_RELOAD */ + reloading--; ns_notice(ns_log_default, "Ready to answer queries."); } + +/* + * Reload configuration, look for new or deleted zones, not changed ones. + */ +void +ns_reconfig(void) { + INSIST(reconfiging == 0); + reconfiging++; /* To ignore zones which aren't new or deleted. */ + ns_reload(); + reconfiging--; +} + +void +make_new_zones(void) { + struct zoneinfo *zp; + int n; + + ns_debug(ns_log_config, 1, "Adding %d template zones", NEWZONES); + zp = (struct zoneinfo *) + memget((nzones + NEWZONES) * sizeof(struct zoneinfo)); + if (zp == NULL) + panic("no memory for more zones", NULL); + memset(zp, 0, (nzones + NEWZONES) * sizeof(struct zoneinfo)); + if (zones != NULL) { + memcpy(zp, zones, nzones * sizeof(struct zoneinfo)); + memput(zones, nzones * sizeof(struct zoneinfo)); + } + zones = zp; + block_signals(); + for (n = 0; n < NEWZONES; n++) { + INIT_LINK(&zones[nzones], z_reloadlink); + if (nzones != 0) + free_zone(&zones[nzones]); + nzones++; + } + unblock_signals(); +} + +void +free_zone(struct zoneinfo *zp) { + if (LINKED(zp, z_reloadlink)) + panic("freeing reloading zone", NULL); + if (zp->z_type != z_nil) + panic("freeing unfree zone", NULL); + APPEND(freezones, zp, z_freelink); +} + +#ifndef HAVE_SPAWNXFER +static pid_t +spawnxfer(char **argv, struct zoneinfo *zp) { + pid_t pid = (pid_t)vfork(); + + if (pid == -1) { + ns_error(ns_log_default, "xfer vfork: %s", strerror(errno)); + zp->z_time = tt.tv_sec + 10; + return (pid); + } + 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)); + (void)nxfers(zp, -1); + _exit(XFER_FAIL); /* Avoid duplicate buffer flushes. */ + } + return (pid); +} +#endif + +struct zoneinfo * +find_auth_zone(const char *zname, ns_class zclass) { + struct zoneinfo *zp; + struct hashbuf *htp; + struct namebuf *np; + const char *fname; + int zn; + + zp = find_zone(zname, zclass); + if (zp != NULL && + (zp->z_type == z_slave || + zp->z_type == z_master || + zp->z_type == z_stub)) + return (zp); + + htp = hashtab; + np = nlookup(zname, &htp, &fname, 0); + if (np != NULL && (zn = findMyZone(np, zclass)) != DB_Z_CACHE) + return (&zones[zn]); + + return (NULL); +} |