diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /usr.sbin/named/xfer |
initial import of NetBSD tree
Diffstat (limited to 'usr.sbin/named/xfer')
-rw-r--r-- | usr.sbin/named/xfer/Makefile | 12 | ||||
-rw-r--r-- | usr.sbin/named/xfer/named-xfer.8 | 148 | ||||
-rw-r--r-- | usr.sbin/named/xfer/named-xfer.c | 1233 |
3 files changed, 1393 insertions, 0 deletions
diff --git a/usr.sbin/named/xfer/Makefile b/usr.sbin/named/xfer/Makefile new file mode 100644 index 00000000000..3f63adf5908 --- /dev/null +++ b/usr.sbin/named/xfer/Makefile @@ -0,0 +1,12 @@ +# from: @(#)Makefile 5.1 (Berkeley) 5/28/90 +# $Id: Makefile,v 1.1 1995/10/18 08:47:56 deraadt Exp $ + +PROG= named-xfer +SRCS= named-xfer.c db_glue.c +CFLAGS+=-I${.CURDIR}/.. +.PATH: ${.CURDIR}/.. +BINDIR= /usr/libexec +MAN= named-xfer.8 + +.include "../../Makefile.inc" +.include <bsd.prog.mk> diff --git a/usr.sbin/named/xfer/named-xfer.8 b/usr.sbin/named/xfer/named-xfer.8 new file mode 100644 index 00000000000..d12700ea823 --- /dev/null +++ b/usr.sbin/named/xfer/named-xfer.8 @@ -0,0 +1,148 @@ +.\" ++Copyright++ 1985 +.\" - +.\" Copyright (c) 1985 +.\" 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-- +.\" +.\" from: named.8 6.6 (Berkeley) 2/14/89 +.\" $NetBSD: named-xfer.8,v 1.2 1994/11/29 23:56:29 glass Exp $ +.\" +.TH NAMED-XFER 8 "June 26, 1993" +.UC 4 +.SH NAME +named-xfer \- ancillary agent for inbound zone transfers +.SH SYNOPSIS +.B named-xfer +.B \-z +.I zone_to_transfer +.B \-f +.I db_file +.B \-s +.I serial_no +[ +.B \-d +.I debuglevel +] [ +.B \-l +.I debug_log_file +] [ +.B \-t +.I trace_file +] [ +.B \-p +.I port# +] [ +.B \-S +] +.I nameserver +... +.SH DESCRIPTION +.I Named-xfer +is an ancillary program executed by +.IR named (8) +to perform an inbound zone transfer. It is rarely executed directly, and +only by system administrators who are trying to debug a zone transfer problem. +See RFC's 1033, 1034, and 1035 for more information on the Internet +name-domain system. +.PP +Options are: +.TP +.B \-z +specifies the name of the zone to be transferred. +.TP +.B \-f +specifies the name of the file into which the zone should be dumped +when it is received from the primary server. +.TP +.B \-s +specifies the serial number of our current copy of this zone. If the +\s-1SOA RR\s+1 we get from the primary server does not have a serial +number higher than this, the transfer will be aborted. +.TP +.B \-d +Print debugging information. +A number after the ``d'' determines the level of +messages printed. +.TP +.B \-l +Specifies a log file for debugging messages. The default is system- +dependent but is usually in +.I /var/tmp +or +.IR /usr/tmp . +Note that this only applies if +.I \-d +is also specified. +.TP +.B \-t +Specifies a trace file which will contain a protocol trace of the zone +transfer. This is probably only of interest to people debugging the name +server itself. +.TP +.B \-p +Use a different port number. The default is the standard port number +as returned by getservbyname(3) for service ``domain''. +.TP +.B \-S +Perform a restricted transfer of only the SOA, NS records and glue A +records for the zone. The SOA record will not be loaded by named but +will be used to determine when to verify the NS records. +the ``stubs'' directive in +.IR named (8) +for more information. +.PP +Additional arguments are taken as name server addresses in so-called +``dotted-quad'' syntax only; no host name are allowed here. At least +one address must be specified. Any additional addresses will be tried +in order if the first one fails to transfer to us successfully. +.SH "SEE ALSO" +named(8), resolver(3), resolver(5), hostname(7), +RFC 882, RFC 883, RFC 973, RFC 974, RFC 1033, RFC 1034, RFC 1035, RFC 1123, +\fIName Server Operations Guide for \s-1BIND\s+1\fR diff --git a/usr.sbin/named/xfer/named-xfer.c b/usr.sbin/named/xfer/named-xfer.c new file mode 100644 index 00000000000..db41f0ccbf5 --- /dev/null +++ b/usr.sbin/named/xfer/named-xfer.c @@ -0,0 +1,1233 @@ +/*- + * Copyright (c) 1988, 1990 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. + * + * The original version of xfer by Kevin Dunlap. + * Completed and integrated with named by David Waitzman + * (dwaitzman@bbn.com) 3/14/88. + * Modified by M. Karels and O. Kure 10-88. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1988, 1990 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)named-xfer.c 4.18 (Berkeley) 3/7/91";*/ +static char rcsid[] = "$Id: named-xfer.c,v 1.1 1995/10/18 08:47:56 deraadt Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/signal.h> + +#include <netinet/in.h> +#include <net/if.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> + +#define XFER /* modifies the ns.h include file */ +#include "ns.h" +#include "pathnames.h" + +char *savestr(); + +/* max length of data in RR data field */ +#define MAXDATA 2048 /* from db.h */ + +int debug = 0; +int quiet = 0; +int read_interrupted = 0; +struct zoneinfo zones; /* zone information */ +struct timeval tt; + +static char ddtfilename[] = _PATH_TMPXFER; +static char *ddtfile = ddtfilename; +static char *tmpname; +FILE *fp = 0, *ddt, *dbfp; +char *domain; /* domain being xfered */ +int domain_len; /* strlen(domain) */ + +extern int errno; + +main(argc, argv) + int argc; + char *argv[]; +{ + register struct zoneinfo *zp; + register struct hostent *hp; + char *dbfile = NULL, *tracefile = NULL, *tm = NULL; + int dbfd, ddtd, result, c; + u_long serial_no = 0; + extern char *optarg; + extern int optind, getopt(); + u_short port = htons(NAMESERVER_PORT); + + (void) umask(022); +#ifdef LOG_DAEMON + openlog("named-xfer", LOG_PID|LOG_CONS, LOG_DAEMON); +#else + openlog("named-xfer", LOG_PID); +#endif + while ((c = getopt(argc, argv, "d:l:s:t:z:f:p:P:q")) != EOF) + switch (c) { + case 'd': + debug = atoi(optarg); + break; + case 'l': + ddtfile = (char *)malloc(strlen(optarg) + + sizeof(".XXXXXX") + 1); + (void) strcpy(ddtfile, optarg); + (void) strcat(ddtfile, ".XXXXXX"); + break; + case 's': + serial_no = (u_long) atol(optarg); + break; + case 't': + tracefile = optarg; + break; + case 'z': /* zone == domain */ + domain = optarg; + domain_len = strlen(domain); + break; + case 'f': + dbfile = optarg; + tmpname = (char *)malloc((unsigned)strlen(optarg) + + sizeof(".XXXXXX") + 1); + (void) strcpy(tmpname, optarg); + break; + case 'p': + port = htons((u_short)atoi(optarg)); + break; + case 'P': + port = (u_short)atoi(optarg); + break; + case 'q': + quiet++; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + + if (!domain || !dbfile || optind >= argc) { + usage(); + /* NOTREACHED */ + } + if (tracefile && (fp = fopen(tracefile, "w")) == NULL) + perror(tracefile); + (void) strcat(tmpname, ".XXXXXX"); + /* tmpname is now something like "/etc/named/named.bu.db.XXXXXX" */ + if ((dbfd = mkstemp(tmpname)) == -1) { + perror(tmpname); + if (!quiet) + syslog(LOG_ERR, "can't make tmpfile (%s): %m\n", + tmpname); + exit(XFER_FAIL); + } + if (fchmod(dbfd, 0644) == -1) { + perror(tmpname); + if (!quiet) + syslog(LOG_ERR, "can't fchmod tmpfile (%s): %m\n", + tmpname); + exit(XFER_FAIL); + } + if ((dbfp = fdopen(dbfd, "r+")) == NULL) { + perror(tmpname); + if (!quiet) + syslog(LOG_ERR, "can't fdopen tmpfile (%s)", tmpname); + exit(XFER_FAIL); + } +#ifdef DEBUG + if (debug) { + /* ddtfile is now something like "/usr/tmp/xfer.ddt.XXXXXX" */ + if ((ddtd = mkstemp(ddtfile)) == -1) { + perror(ddtfile); + debug = 0; + } else if (fchmod(ddtd, 0644) == -1) { + perror(ddtfile); + debug = 0; + } else if ((ddt = fdopen(ddtd, "w")) == NULL) { + perror(ddtfile); + debug = 0; + } else { +#if defined(SYSV) + setvbuf(ddt, NULL, _IOLBF, BUFSIZ); +#else + setlinebuf(ddt); +#endif + } + } +#endif + /* + * Ignore many types of signals that named (assumed to be our parent) + * considers important- if not, the user controlling named with + * signals usually kills us. + */ + (void) signal(SIGHUP, SIG_IGN); + (void) signal(SIGSYS, SIG_IGN); + if (debug == 0) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + (void) signal(SIGIOT, SIG_IGN); + +#if defined(SIGUSR1) && defined(SIGUSR2) + (void) signal(SIGUSR1, SIG_IGN); + (void) signal(SIGUSR2, SIG_IGN); +#else SIGUSR1&&SIGUSR2 + (void) signal(SIGEMT, SIG_IGN); + (void) signal(SIGFPE, SIG_IGN); +#endif SIGUSR1&&SIGUSR2 + +#ifdef DEBUG + if (debug) (void)fprintf(ddt, "domain `%s' file `%s' ser no %lu \n", + domain, dbfile,serial_no); +#endif + buildservicelist(); + buildprotolist(); + + /* init zone data */ + + zp = &zones; + zp->z_type = Z_SECONDARY; + zp->z_origin = domain; + zp->z_source = dbfile; + zp->z_addrcnt = 0; +#ifdef DEBUG + if (debug) { + (void)fprintf(ddt,"zone found (%d): ", zp->z_type); + if (zp->z_origin[0] == '\0') + (void)fprintf(ddt,"'.'"); + else + (void)fprintf(ddt,"'%s'", zp->z_origin); + (void)fprintf(ddt,", source = %s\n", zp->z_source); + } +#endif + for (; optind != argc; optind++,zp->z_addrcnt++) { + tm = argv[optind]; + zp->z_addr[zp->z_addrcnt].s_addr = inet_addr(tm); + + if (zp->z_addr[zp->z_addrcnt].s_addr == (unsigned)-1) { + hp = gethostbyname(tm); + if (hp == NULL) { + syslog(LOG_ERR, "uninterpretable server %s\n", + tm); + continue; + } + bcopy(hp->h_addr, + (char *)&zp->z_addr[zp->z_addrcnt].s_addr, + sizeof(zp->z_addr[zp->z_addrcnt].s_addr)); +#ifdef DEBUG + if (debug) + (void)fprintf(ddt,", %s",tm); +#endif + } + if (zp->z_addrcnt >= NSMAX) { + zp->z_addrcnt = NSMAX; +#ifdef DEBUG + if (debug) + (void)fprintf(ddt, "\nns.h NSMAX reached\n"); +#endif + break; + } + } +#ifdef DEBUG + if (debug) (void)fprintf(ddt," (addrcnt) = %d\n", zp->z_addrcnt); +#endif + + _res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE); + result = getzone(zp, serial_no, port); + (void) fclose(dbfp); + switch (result) { + + case XFER_SUCCESS: /* ok exit */ + if (rename(tmpname, dbfile) == -1) { + perror("rename"); + if (!quiet) + syslog(LOG_ERR, "rename %s to %s: %m", + tmpname, dbfile); + exit(XFER_FAIL); + } + exit(XFER_SUCCESS); + + case XFER_UPTODATE: /* the zone was already uptodate */ + (void) unlink(tmpname); + exit(XFER_UPTODATE); + + case XFER_TIMEOUT: +#ifdef DEBUG + if (!debug) +#endif + (void) unlink(tmpname); + exit(XFER_TIMEOUT); /* servers not reachable exit */ + + case XFER_FAIL: + default: +#ifdef DEBUG + if (!debug) +#endif + (void) unlink(tmpname); + exit(XFER_FAIL); /* yuck exit */ + } +} + +usage() +{ + (void)fprintf(stderr, +"Usage: xfer\n\ +\t-z zone_to_transfer\n\ +\t-f db_file\n\ +\t-s serial_no\n\ +\t[-d debug_level]\n\ +\t[-l debug_log_file (default %s)]\n\ +\t[-t trace_file]\n\ +\t[-p port]\n\ +\tservers...\n", ddtfile); + exit(XFER_FAIL); +} + +int minimum_ttl = 0, got_soa = 0; +char prev_origin[MAXDNAME]; +char prev_dname[MAXDNAME]; + +getzone(zp, serial_no, port) + struct zoneinfo *zp; + u_long serial_no; + u_short port; +{ + HEADER *hp; + u_short len; + u_long serial; + int s, n, l, cnt, soacnt, error = 0; + u_char *cp, *nmp, *eom, *tmp ; + u_char *buf = NULL; + int bufsize; + u_char name[MAXDNAME], name2[MAXDNAME]; + struct sockaddr_in sin; + struct zoneinfo zp_start, zp_finish; + struct itimerval ival, zeroival; + extern SIG_FN read_alarm(); + struct sigvec sv, osv; + int ancount, aucount; +#ifdef DEBUG + if (debug) + (void)fprintf(ddt,"getzone() %s\n", zp->z_origin); +#endif + bzero((char *)&zeroival, sizeof(zeroival)); + ival = zeroival; + ival.it_value.tv_sec = 120; + sv.sv_handler = read_alarm; + sv.sv_onstack = 0; + sv.sv_mask = ~0; + (void) sigvec(SIGALRM, &sv, &osv); + + strcpy(prev_origin, zp->z_origin); + + for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { + error = 0; + if (buf == NULL) { + if ((buf = (u_char *)malloc(2 * PACKETSZ)) == NULL) { + syslog(LOG_ERR, "malloc(%u) failed", + 2 * PACKETSZ); + error++; + break; + } + bufsize = 2 * PACKETSZ; + } + bzero((char *)&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = port; + sin.sin_addr = zp->z_addr[cnt]; + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + syslog(LOG_ERR, "socket: %m"); + error++; + break; + } +#ifdef DEBUG + if (debug >= 2) { + (void)fprintf(ddt,"connecting to server #%d %s, %d\n", + cnt+1, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + } +#endif + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + (void) close(s); + error++; +#ifdef DEBUG + if (debug >= 2) + (void)fprintf(ddt, "connect failed, %s\n", + strerror(errno)); +#endif + continue; + } + if ((n = res_mkquery(QUERY, zp->z_origin, C_IN, + T_SOA, (char *)NULL, 0, NULL, (char *)buf, bufsize)) < 0) { + if (!quiet) + syslog(LOG_ERR, "zone %s: res_mkquery T_SOA failed", + zp->z_origin); + (void) close(s); + (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); + return XFER_FAIL; + } + /* + * Send length & message for zone transfer + */ + if (writemsg(s, buf, n) < 0) { + (void) close(s); + error++; +#ifdef DEBUG + if (debug >= 2) + (void)fprintf(ddt,"writemsg failed\n"); +#endif + continue; + } + /* + * Get out your butterfly net and catch the SOA + */ + cp = buf; + l = sizeof(u_short); + read_interrupted = 0; + while (l > 0) { +#ifdef DEBUG + if (debug > 10) (void)fprintf(ddt,"Before setitimer\n"); +#endif + (void) setitimer(ITIMER_REAL, &ival, + (struct itimerval *)NULL); +#ifdef DEBUG + if (debug > 10) (void)fprintf(ddt,"Before recv(l = %d)\n",n); +#endif + errno = 0; + if ((n = recv(s, (char *)cp, l, 0)) > 0) { + cp += n; + l -= n; + } else { +#ifdef DEBUG + if (debug > 10) + (void)fprintf(ddt, +"bad recv->%d, errno= %d, read_interrupt=%d\n", n, errno, read_interrupted); +#endif + if (n == -1 && errno == EINTR + && !read_interrupted) + continue; + error++; + break; + } + } + + (void) setitimer(ITIMER_REAL, &zeroival, + (struct itimerval *)NULL); + if (error) { + (void) close(s); + continue; + } + if ((len = htons(*(u_short *)buf)) == 0) { + (void) close(s); + continue; + } + if (len > bufsize) { + if ((buf = (u_char *)realloc(buf, len)) == NULL) { + syslog(LOG_ERR, + "malloc(%u) failed for SOA from server %s, zone %s\n", + len, inet_ntoa(sin.sin_addr), zp->z_origin); + (void) close(s); + continue; + } + bufsize = len; + } + l = len; + cp = buf; + while (l > 0) { + (void) setitimer(ITIMER_REAL, &ival, + (struct itimerval *)NULL); + errno = 0; + if ((n = recv(s, (char *)cp, l, 0)) > 0) { + cp += n; + l -= n; + } else { + if (errno == EINTR && !read_interrupted) + continue; + error++; +#ifdef DEBUG + if (debug > 10) + (void)fprintf(ddt, + "recv failed: n= %d, errno = %d\n", + n, errno); +#endif + break; + } + } + (void) setitimer(ITIMER_REAL, &zeroival, + (struct itimerval *)NULL); + if (error) { + (void) close(s); + continue; + } +#ifdef DEBUG + if (debug >= 3) { + (void)fprintf(ddt,"len = %d\n", len); + fp_query(buf, ddt); + } +#endif DEBUG + hp = (HEADER *) buf; + ancount = ntohs(hp->ancount); + aucount = ntohs(hp->nscount); + + /* + * close socket if: + * 1) rcode != NOERROR + * 2) not an authority response + * 3) both the number of answers and authority count < 1) + */ + if (hp->rcode != NOERROR || !(hp->aa) || + (ancount < 1 && aucount < 1)) { + if (!quiet) + syslog(LOG_ERR, + "%s from %s, zone %s: rcode %d, aa %d, ancount %d, aucount %d\n", + "bad response to SOA query", + inet_ntoa(sin.sin_addr), zp->z_origin, + hp->rcode, hp->aa, ancount, aucount); +#ifdef DEBUG + if (debug) + fprintf(ddt, + "%s from %s, zone %s: rcode %d, aa %d, ancount %d, aucount %d\n", + "bad response to SOA query", + inet_ntoa(sin.sin_addr), zp->z_origin, + hp->rcode, hp->aa, ancount, aucount); +#endif DEBUG + (void) close(s); + error++; + continue; + } + zp_start = *zp; + if (len < sizeof(HEADER) + QFIXEDSZ) { + badsoa: + if (!quiet) + syslog(LOG_ERR, + "malformed SOA from %s, zone %s: too short\n", + inet_ntoa(sin.sin_addr), zp->z_origin); +#ifdef DEBUG + if (debug) + fprintf(ddt, + "malformed SOA from %s: too short\n", + inet_ntoa(sin.sin_addr)); +#endif DEBUG + (void) close(s); + error++; + continue; + } + tmp = buf + sizeof(HEADER); + eom = buf + len; + if ((n = dn_skipname(tmp, eom)) == -1) + goto badsoa; + tmp += n + QFIXEDSZ; + if ((n = dn_skipname(tmp, eom)) == -1) + goto badsoa; + tmp += n; + if (soa_zinfo(&zp_start, tmp, eom) == -1) + goto badsoa; + if (zp_start.z_serial > serial_no || serial_no == 0) { +#ifdef DEBUG + if (debug) + (void)fprintf(ddt, "need update, serial %d\n", + zp_start.z_serial); +#endif DEBUG + hp = (HEADER *) buf; + soacnt = 0; + for (;;) { + if (soacnt == 0) { + if ((n = res_mkquery(QUERY, zp->z_origin, C_IN, + T_AXFR, (char *)NULL, 0, NULL, + (char *)buf, bufsize)) < 0) { + if (!quiet) + syslog(LOG_ERR, + "zone %s: res_mkquery T_AXFR failed", + zp->z_origin); + (void) close(s); + (void) sigvec(SIGALRM, &osv, + (struct sigvec *)0); + return XFER_FAIL; + } + /* + * Send length & message for zone transfer + */ + if (writemsg(s, buf, n) < 0) { + (void) close(s); + error++; +#ifdef DEBUG + if (debug >= 2) + (void)fprintf(ddt,"writemsg failed\n"); +#endif + break; + } + } + /* + * Receive length & response + */ + cp = buf; + l = sizeof(u_short); + /* allow extra time for the fork on first read */ + if (soacnt == 0) + ival.it_value.tv_sec = 300; + while (l > 0) { + (void) setitimer(ITIMER_REAL, &ival, + (struct itimerval *)NULL); + errno = 0; + if ((n = recv(s, (char *)cp, l, 0)) > 0) { + cp += n; + l -= n; + } else { + if (errno == EINTR && !read_interrupted) + continue; + error++; +#ifdef DEBUG + if (debug >= 2) + (void)fprintf(ddt, + "recv failed: n= %d, errno = %d\n", + n, errno); +#endif + break; + } + } + if (soacnt == 0) + ival.it_value.tv_sec = 120; + (void) setitimer(ITIMER_REAL, &zeroival, + (struct itimerval *)NULL); + if (error) + break; + if ((len = htons(*(u_short *)buf)) == 0) + break; + l = len; + cp = buf; + eom = buf + len; + while (l > 0) { + (void) setitimer(ITIMER_REAL, &ival, + (struct itimerval *)NULL); + errno = 0; + if ((n = recv(s, (char *)cp, l, 0)) > 0) { + cp += n; + l -= n; + } else { + if (errno == EINTR && !read_interrupted) + continue; + error++; +#ifdef DEBUG + if (debug >= 2) + (void)fprintf(ddt,"recv failed\n"); +#endif + break; + } + } + (void) setitimer(ITIMER_REAL, &zeroival, + (struct itimerval *)NULL); + if (error) + break; +#ifdef DEBUG + if (debug >= 3) { + (void)fprintf(ddt,"len = %d\n", len); + fp_query(buf, ddt); + } + if (fp) fp_query(buf,fp); +#endif + if (len < sizeof(HEADER)) { + badrec: + error++; + if (!quiet) + syslog(LOG_ERR, + "record too short from %s, zone %s\n", + inet_ntoa(sin.sin_addr), + zp->z_source); +#ifdef DEBUG + if (debug) + fprintf(ddt, + "record too short from %s\n", + inet_ntoa(sin.sin_addr)); +#endif DEBUG + break; + } + cp = buf + sizeof(HEADER); + if (hp->qdcount) { + if ((n = dn_skipname(cp, eom)) == -1 || + n + QFIXEDSZ >= eom - cp) + goto badrec; + cp += n + QFIXEDSZ; + } + nmp = cp; + if ((n = dn_skipname(cp, eom)) == -1) + goto badrec; + tmp = cp + n; + + n = print_output(buf, bufsize, cp); + if (cp + n != eom) { +#ifdef DEBUG + if (debug) + (void)fprintf(ddt, + "getzone: print_update failed (%d, %d)\n", + cp - buf, n); +#endif + error++; + break; + } + GETSHORT(n, tmp); + if (n == T_SOA) { + if (soacnt == 0) { + soacnt++; + if (dn_expand(buf, buf + 512, nmp, + name, sizeof(name)) == -1) + goto badsoa; + if (eom - tmp <= 2 * sizeof(u_short) + + sizeof(u_long)) + goto badsoa; + tmp += 2 * sizeof(u_short) + + sizeof(u_long); + if ((n = dn_skipname(tmp, eom)) == -1) + goto badsoa; + tmp += n; + if ((n = dn_skipname(tmp, eom)) == -1) + goto badsoa; + tmp += n; + if (eom - tmp <= sizeof(u_long)) + goto badsoa; + GETLONG(serial, tmp); +#ifdef DEBUG + if (debug > 2) + (void)fprintf(ddt, + "first SOA for %s, serial %d\n", + name, serial); +#endif DEBUG + continue; + } + if (dn_expand(buf, buf + 512, nmp, name2, + sizeof(name2)) == -1) + goto badsoa; + if (strcasecmp((char *)name, (char *)name2) != 0) { +#ifdef DEBUG + if (debug > 1) + (void)fprintf(ddt, + "extraneous SOA for %s\n", + name2); +#endif DEBUG + continue; + } + tmp -= sizeof(u_short); + if (soa_zinfo(&zp_finish, tmp, eom) == -1) + goto badsoa; +#ifdef DEBUG + if (debug > 1) + (void)fprintf(ddt, + "SOA, serial %d\n", zp_finish.z_serial); +#endif DEBUG + if (serial != zp_finish.z_serial) { + soacnt = 0; + got_soa = 0; + minimum_ttl = 0; + strcpy(prev_origin, zp->z_origin); + prev_dname[0] = 0; +#ifdef DEBUG + if (debug) + (void)fprintf(ddt, + "serial changed, restart\n"); +#endif DEBUG + /* + * Flush buffer, truncate file + * and seek to beginning to restart. + */ + fflush(dbfp); + if (ftruncate(fileno(dbfp), 0) != 0) { + if (!quiet) + syslog(LOG_ERR, + "ftruncate %s: %m\n", + tmpname); + return(XFER_FAIL); + } + fseek(dbfp, 0L, 0); + } else + break; + } + } + (void) close(s); + if (error == 0) { + (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); + return XFER_SUCCESS; + } +#ifdef DEBUG + if (debug >= 2) + (void)fprintf(ddt,"error receiving zone transfer\n"); +#endif + } else { + (void) close(s); +#ifdef DEBUG + if (debug) + (void)fprintf(ddt, + "zone up-to-date, serial %d\n", zp_start.z_serial); +#endif DEBUG + return XFER_UPTODATE; + } + } + (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); + if (error) + return XFER_TIMEOUT; + return XFER_FAIL; +} + +/* + * Set flag saying to read was interrupted + * used for a read timer + */ +SIG_FN +read_alarm() +{ + extern int read_interrupted; + read_interrupted = 1; +} + +writemsg(rfd, msg, msglen) + int rfd; + u_char *msg; + int msglen; +{ + struct iovec iov[2]; + u_short len = htons((u_short)msglen); + + iov[0].iov_base = (caddr_t)&len; + iov[0].iov_len = sizeof(len); + iov[1].iov_base = (caddr_t)msg; + iov[1].iov_len = msglen; + if (writev(rfd, iov, 2) != sizeof(len) + msglen) { +#ifdef DEBUG + if (debug) + (void)fprintf(ddt,"write failed %d\n", errno); +#endif + return (-1); + } + return (0); +} + + +soa_zinfo(zp, cp, eom) + register struct zoneinfo *zp; + register u_char *cp; + u_char *eom; +{ + register int n; + + if (eom - cp < 3 * sizeof(u_short) + sizeof(u_long)) + return (-1); + cp += 3 * sizeof(u_short) + sizeof(u_long); + if ((n = dn_skipname(cp, eom)) == -1) + return (-1); + cp += n; + if ((n = dn_skipname(cp, eom)) == -1) + return (-1); + cp += n; + if (eom - cp < 5 * sizeof(u_long)) + return (-1); + GETLONG(zp->z_serial, cp); + GETLONG(zp->z_refresh, cp); + gettime(&tt); + zp->z_time = tt.tv_sec + zp->z_refresh; + GETLONG(zp->z_retry, cp); + GETLONG(zp->z_expire, cp); + GETLONG(zp->z_minimum, cp); + return (0); +} + +gettime(ttp) +struct timeval *ttp; +{ + if (gettimeofday(ttp, (struct timezone *)0) < 0) + syslog(LOG_ERR, "gettimeofday failed: %m"); +} + +/* + * Parse the message, determine if it should be printed, and if so, print it + * in .db file form. + * Does minimal error checking on the message content. + */ +print_output(msg, msglen, rrp) + u_char *msg; + int msglen; + u_char *rrp; +{ + register u_char *cp; + register HEADER *hp = (HEADER *) msg; + u_long addr, ttl; + int i, j, tab, result, class, type, dlen, n1; + long n; + u_char *cp1, data[BUFSIZ]; + u_char *temp_ptr; /* used to get ttl for RR */ + char *cdata, *origin, *proto, dname[MAXDNAME]; + extern char *inet_ntoa(), *protocolname(), *servicename(); + + cp = rrp; + if ((n = dn_expand(msg, msg + msglen, cp, (u_char *) dname, + sizeof(dname))) < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + + origin = index(dname, '.'); + if (origin == NULL) + origin = ""; + else + origin++; /* move past the '.' */ +#ifdef DEBUG + if (debug > 2) + (void) fprintf(ddt, "print_output: dname %s type %d class %d ttl %d\n", + dname, type, class, ttl); +#endif + /* + * Convert the resource record data into the internal database format. + */ + switch (type) { + case T_A: + case T_WKS: + case T_HINFO: + case T_UINFO: + case T_TXT: + case T_UID: + case T_GID: + cp1 = cp; + n = dlen; + cp += n; + break; + + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + if ((n = dn_expand(msg, msg + msglen, cp, data, + sizeof(data))) < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = data; + n = strlen((char *) data) + 1; + break; + + case T_MINFO: + case T_SOA: + if ((n = dn_expand(msg, msg + msglen, cp, data, + sizeof(data))) < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = data + (n = strlen((char *) data) + 1); + n1 = sizeof(data) - n; + if (type == T_SOA) + n1 -= 5 * sizeof(u_long); + if ((n = dn_expand(msg, msg + msglen, cp, cp1, n1)) < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 += strlen((char *) cp1) + 1; + if (type == T_SOA) { + temp_ptr = cp + 4 * sizeof(u_long); + GETLONG(minimum_ttl, temp_ptr); + bcopy((char *) cp, (char *) cp1, + n = 5 * sizeof(u_long)); + cp += n; + cp1 += n; + } + n = cp1 - data; + cp1 = data; + break; + + case T_MX: + /* grab preference */ + bcopy((char *) cp, (char *) data, sizeof(u_short)); + cp1 = data + sizeof(u_short); + cp += sizeof(u_short); + + /* get name */ + if ((n = dn_expand(msg, msg + msglen, cp, cp1, + sizeof(data) - sizeof(u_short))) < 0) + return (-1); + cp += n; + + /* compute end of data */ + cp1 += strlen((char *) cp1) + 1; + /* compute size of data */ + n = cp1 - data; + cp1 = data; + break; + + default: +#ifdef DEBUG + if (debug >= 3) + (void) fprintf(ddt, "unknown type %d\n", type); +#endif + return ((cp - rrp) + dlen); + } + if (n > MAXDATA) { +#ifdef DEBUG + if (debug) + (void) fprintf(ddt, + "update type %d: %d bytes is too much data\n", + type, n); +#endif + hp->rcode = NOCHANGE; /* XXX - FORMERR ??? */ + return (-1); + } + cdata = (char *) cp1; + result = cp - rrp; + + /* + * Only print one SOA per db file + */ + if (type == T_SOA) { + if (got_soa) + return result; + else + got_soa++; + } + /* + * If the origin has changed, print the new origin + */ + if (strcasecmp(prev_origin, origin)) { + (void) strcpy(prev_origin, origin); + (void) fprintf(dbfp, "$ORIGIN %s.\n", origin); + } + tab = 0; + + if (strcasecmp(prev_dname, dname)) { + /* + * set the prev_dname to be the current dname, then cut off all + * characters of dname after (and including) the first '.' + */ + char *cutp = index(dname, '.'); + + (void) strcpy(prev_dname, dname); + if (cutp) + *cutp = NULL; + + if (dname[0] == 0) { + if (origin[0] == 0) + (void) fprintf(dbfp, ".\t"); + else + (void) fprintf(dbfp, ".%s.\t", origin); /* ??? */ + } else + (void) fprintf(dbfp, "%s\t", dname); + if (strlen(dname) < 8) + tab = 1; + } else { + (void) putc('\t', dbfp); + tab = 1; + } + + if (ttl != 0 && ttl != minimum_ttl) + (void) fprintf(dbfp, "%d\t", (int) ttl); + else if (tab) + (void) putc('\t', dbfp); + + (void) fprintf(dbfp, "%s\t%s\t", p_class(class), p_type(type)); + cp = (u_char *) cdata; + + /* + * Print type specific data + */ + switch (type) { + + case T_A: + switch (class) { + case C_IN: + case C_HS: + GETLONG(n, cp); + n = htonl(n); + (void) fprintf(dbfp, "%s", + inet_ntoa(*(struct in_addr *) & n)); + break; + } + (void) fprintf(dbfp, "\n"); + break; + + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_PTR: + if (cp[0] == '\0') + (void) fprintf(dbfp, ".\n"); + else + (void) fprintf(dbfp, "%s.\n", cp); + break; + + case T_NS: + cp = (u_char *) cdata; + if (cp[0] == '\0') + (void) fprintf(dbfp, ".\t"); + else + (void) fprintf(dbfp, "%s.", cp); + (void) fprintf(dbfp, "\n"); + break; + + case T_HINFO: + if (n = *cp++) { + (void) fprintf(dbfp, "\"%.*s\"", (int) n, cp); + cp += n; + } else + (void) fprintf(dbfp, "\"\""); + if (n = *cp++) + (void) fprintf(dbfp, " \"%.*s\"", (int) n, cp); + else + (void) fprintf(dbfp, "\"\""); + (void) putc('\n', dbfp); + break; + + case T_SOA: + (void) fprintf(dbfp, "%s.", cp); + cp += strlen((char *) cp) + 1; + (void) fprintf(dbfp, " %s. (\n", cp); + cp += strlen((char *) cp) + 1; + GETLONG(n, cp); + (void) fprintf(dbfp, "\t\t%lu", n); + GETLONG(n, cp); + (void) fprintf(dbfp, " %lu", n); + GETLONG(n, cp); + (void) fprintf(dbfp, " %lu", n); + GETLONG(n, cp); + (void) fprintf(dbfp, " %lu", n); + GETLONG(n, cp); + (void) fprintf(dbfp, " %lu )\n", n); + break; + + case T_MX: + GETSHORT(n, cp); + (void) fprintf(dbfp, "%lu", n); + (void) fprintf(dbfp, " %s.\n", cp); + break; + + case T_TXT: + cp1 = cp + n; + (void) putc('"', dbfp); + while (cp < cp1) { + if (i = *cp++) { + for (j = i ; j > 0 && cp < cp1 ; j--) + if (*cp == '\n') { + (void) putc('\\', dbfp); + (void) putc(*cp++, dbfp); + } else + (void) putc(*cp++, dbfp); + } + } + (void) fputs("\"\n", dbfp); + break; + + case T_UINFO: + (void) fprintf(dbfp, "\"%s\"\n", cp); + break; + + case T_UID: + case T_GID: + if (n == sizeof(u_long)) { + GETLONG(n, cp); + (void) fprintf(dbfp, "%lu\n", n); + } + break; + + case T_WKS: + GETLONG(addr, cp); + addr = htonl(addr); + (void) fprintf(dbfp, "%s ", + inet_ntoa(*(struct in_addr *) & addr)); + proto = protocolname(*cp); + cp += sizeof(char); + (void) fprintf(dbfp, "%s ", proto); + i = 0; + while (cp < (u_char *) cdata + n) { + j = *cp++; + do { + if (j & 0200) + (void) fprintf(dbfp, " %s", + servicename(i, proto)); + j <<= 1; + } while (++i & 07); + } + (void) fprintf(dbfp, "\n"); + break; + + case T_MINFO: + (void) fprintf(dbfp, "%s.", cp); + cp += strlen((char *) cp) + 1; + (void) fprintf(dbfp, " %s.\n", cp); + break; + + default: + (void) fprintf(dbfp, "???\n"); + } + if (ferror(dbfp)) { + syslog(LOG_ERR, "%s: %m", tmpname); + exit(XFER_FAIL); + } + return result; +} + +/* + * Make a copy of a string and return a pointer to it. + */ +char * +savestr(str) + char *str; +{ + char *cp; + + cp = (char *)malloc((unsigned)strlen(str) + 1); + if (cp == NULL) { + syslog(LOG_ERR, "savestr: %m"); + exit(XFER_FAIL); + } + (void) strcpy(cp, str); + return (cp); +} |