diff options
author | Peter Wemm <peter@FreeBSD.org> | 1998-05-03 04:11:49 +0000 |
---|---|---|
committer | Peter Wemm <peter@FreeBSD.org> | 1998-05-03 04:11:49 +0000 |
commit | 4e0ffe0baef832be2a8161229e9e4f7cb18a586a (patch) | |
tree | 759849259eae9f7cb0d3ddbd7a131081c6688068 /contrib/bind/bin/named-xfer/named-xfer.c | |
parent | e2f1a81473c07cf46c3104b8dcfdd992a2fe7ca8 (diff) |
Import (trimmed) ISC bind-8.1.2-t3b. This will be updated to 8.1.2 on
final release.
Obtained from: ftp.isc.org
Notes
Notes:
svn path=/vendor/bind/dist/; revision=35629
Diffstat (limited to 'contrib/bind/bin/named-xfer/named-xfer.c')
-rw-r--r-- | contrib/bind/bin/named-xfer/named-xfer.c | 2177 |
1 files changed, 2177 insertions, 0 deletions
diff --git a/contrib/bind/bin/named-xfer/named-xfer.c b/contrib/bind/bin/named-xfer/named-xfer.c new file mode 100644 index 000000000000..292714e444f0 --- /dev/null +++ b/contrib/bind/bin/named-xfer/named-xfer.c @@ -0,0 +1,2177 @@ +/* + * 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. + * Modified extensively since then by just about everybody. + */ + +/* + * 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. + */ + +/* + * 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) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* 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. + */ + +#if !defined(lint) && !defined(SABER) +char copyright[] = +"@(#) Copyright (c) 1988, 1990 The Regents of the University of California.\n\ + portions Copyright (c) 1993 Digital Equipment Corporation\n\ + portions Copyright (c) 1995, 1996 Internet Software Consorium\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)named-xfer.c 4.18 (Berkeley) 3/7/91"; +static char rcsid[] = "$Id: named-xfer.c,v 8.38 1998/03/27 00:19:28 halley Exp $"; +#endif /* not lint */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <limits.h> +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <resolv.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <stdarg.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.h" + +#define MAIN_PROGRAM +#include "../named/named.h" +#undef MAIN_PROGRAM + +#define MAX_XFER_RESTARTS 2 + +# ifdef SHORT_FNAMES +extern long pathconf __P((const char *path, int name)); /* XXX */ +# endif + +static struct zoneinfo zone; /* zone information */ + +static char ddtfilename[] = _PATH_TMPXFER, + *ddtfile = ddtfilename, + *tmpname, + *domain; /* domain being xfered */ + +static int quiet = 0, + read_interrupted = 0, + curclass, + domain_len; /* strlen(domain) */ + +static FILE *fp = NULL, + *dbfp = NULL; + +static char *ProgName; + +static void usage(const char *); +static int getzone(struct zoneinfo *, u_int32_t, int), + print_output(struct zoneinfo *, u_int32_t, + u_char *, int, u_char *), + netread(int, char *, int, int), + writemsg(int, const u_char *, int); +static SIG_FN read_alarm(void); +static SIG_FN term_handler(void); +static const char *soa_zinfo(struct zoneinfo *, u_char *, u_char*); + +struct zoneinfo zp_start, zp_finish; + +static int restarts = 0; + +FILE *ddt = NULL; + +/* + * Debugging printf. + */ +#ifdef DEBUG +void +dprintf(int level, const char *format, ...) { + va_list ap; + + va_start(ap, format); + if (ddt != NULL && debug >= level) + (void) vfprintf(ddt, format, ap); + va_end(ap); +} +#endif /*DEBUG*/ + +static +int init_xfer_logging() { + log_channel chan; + + if (log_new_context(ns_log_max_category, NULL, &log_ctx) < 0) { + perror("log_new_context"); + return (0); + } + log_option(log_ctx, LOG_OPTION_DEBUG, debug); + log_option(log_ctx, LOG_OPTION_LEVEL, debug); + + log_ctx_valid = 1; + + chan = log_new_syslog_channel(0, 0, LOG_DAEMON); + if (chan == NULL) + return (0); + if (log_add_channel(log_ctx, ns_log_default, chan) < 0) { + perror("log_add_channel syslog"); + return (0); + } + + if (debug) { + unsigned int flags = LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; + + chan = log_new_file_channel(flags, 0, NULL, ddt, 0, ULONG_MAX); + if (chan == NULL) + return (0); + if (log_add_channel(log_ctx, ns_log_default, chan) < 0) { + perror("log_add_channel debug"); + return (0); + } + } + + return (1); +} + +void cleanup_for_exit(void) { +#ifdef DEBUG + if (!debug) +#endif + (void) unlink(tmpname); +} + + +int +main(int argc, char *argv[]) { + struct zoneinfo *zp; + struct hostent *hp; + struct in_addr axfr_src; + char *dbfile = NULL, *tracefile = NULL, *tm = NULL; + int dbfd, ddtd, result, c, fd, closed = 0; + u_int32_t serial_no = 0; + u_int port = htons(NAMESERVER_PORT); + struct stat statbuf; +#ifdef STUBS + int stub_only = 0; +#endif + int class = C_IN; + int n; + long num_files; + + ProgName = strrchr(argv[0], '/'); + if (ProgName != NULL) + ProgName++; + else + ProgName = argv[0]; + + (void) umask(022); + + /* this is a hack; closing everything in the parent is hard. */ + num_files = MIN(sysconf(_SC_OPEN_MAX), FD_SETSIZE); + for (fd = num_files - 1; fd > STDERR_FILENO; fd--) + closed += (close(fd) == 0); + +#ifdef RENICE + nice(-40); /* this is the recommended procedure to */ + nice(20); /* reset the priority of the current process */ + nice(0); /* to "normal" (== 0) - see nice(3) */ +#endif + +#ifdef LOG_PERROR + n = LOG_PERROR; +#else + n = 0; +#endif +#ifdef SYSLOG_42BSD + openlog(ProgName, LOG_PID); +#else + openlog(ProgName, LOG_PID|LOG_CONS|n, LOG_DAEMON); +#endif + axfr_src.s_addr = 0; +#ifdef STUBS + while ((c = getopt(argc, argv, "C:d:l:s:t:z:f:p:P:qx:S")) != EOF) +#else + while ((c = getopt(argc, argv, "C:d:l:s:t:z:f:p:P:qx:")) != EOF) +#endif + switch (c) { + case 'C': + class = get_class(optarg); + break; + case 'd': +#ifdef DEBUG + debug = atoi(optarg); +#endif + break; + case 'l': + ddtfile = (char *)malloc(strlen(optarg) + + sizeof(".XXXXXX") + 1); + if (!ddtfile) + panic("malloc(ddtfile)", NULL); +#ifdef SHORT_FNAMES + filenamecpy(ddtfile, optarg); +#else + (void) strcpy(ddtfile, optarg); +#endif /* SHORT_FNAMES */ + (void) strcat(ddtfile, ".XXXXXX"); + break; + case 's': + serial_no = strtoul(optarg, (char **)NULL, 10); + break; + case 't': + tracefile = optarg; + break; + case 'z': /* zone == domain */ + domain = optarg; + domain_len = strlen(domain); + while ((domain_len > 0) && + (domain[domain_len-1] == '.')) + domain[--domain_len] = '\0'; + break; + case 'f': + dbfile = optarg; + tmpname = (char *)malloc((unsigned)strlen(optarg) + + sizeof(".XXXXXX") + 1); + if (!tmpname) + panic("malloc(tmpname)", NULL); +#ifdef SHORT_FNAMES + filenamecpy(tmpname, optarg); +#else + (void) strcpy(tmpname, optarg); +#endif /* SHORT_FNAMES */ + break; + case 'p': + port = htons((u_int16_t)atoi(optarg)); + break; + case 'P': + port = (u_int16_t)atoi(optarg); + break; +#ifdef STUBS + case 'S': + stub_only = 1; + break; +#endif + case 'q': + quiet++; + break; + case 'x': + if (!inet_aton(optarg, &axfr_src)) + panic("bad -x addr: %s", optarg); + break; + case '?': + default: + usage("unrecognized argument"); + /* NOTREACHED */ + } + + if (!domain || !dbfile || optind >= argc) { + if (!domain) + usage("no domain"); + if (!dbfile) + usage("no dbfile"); + if (optind >= argc) + usage("not enough arguments"); + /* NOTREACHED */ + } + if (stat(dbfile, &statbuf) != -1 && + !S_ISREG(statbuf.st_mode) && + !S_ISFIFO(statbuf.st_mode)) + usage("dbfile must be a regular file or FIFO"); + 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); + } +#ifdef HAVE_FCHMOD /* XXX */ + if (fchmod(dbfd, 0644) == -1) +#else + if (chmod(tmpname, 0644) == -1) +#endif + { + perror(tmpname); + if (!quiet) + syslog(LOG_ERR, "can't [f]chmod 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; + } +#ifdef HAVE_FCHMOD + else if (fchmod(ddtd, 0644) == -1) +#else + else if (chmod(ddtfile, 0644) == -1) +#endif + { + perror(ddtfile); + debug = 0; + } else if ((ddt = fdopen(ddtd, "w")) == NULL) { + perror(ddtfile); + debug = 0; + } else + setvbuf(ddt, NULL, _IOLBF, 0); + } +#endif + + if (!init_xfer_logging()) { + perror("init_xfer_logging"); + } + + /* + * 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); +#ifdef SIGSYS + (void) signal(SIGSYS, SIG_IGN); +#endif +#ifdef DEBUG + if (debug == 0) +#endif + { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + (void) signal(SIGILL, 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 */ + + dprintf(1, "domain `%s'; file `%s'; serial %u; closed %d\n", + domain, dbfile, serial_no, closed); + + buildservicelist(); + buildprotolist(); + + /* init zone data */ + + zp = &zone; +#ifdef STUBS + if (stub_only) + zp->z_type = Z_STUB; + else +#endif + zp->z_type = Z_SECONDARY; + zp->z_class = class; + zp->z_origin = domain; + zp->z_source = dbfile; + zp->z_axfr_src = axfr_src; + zp->z_addrcnt = 0; + dprintf(1, "zone found (%d): \"%s\", source = %s\n", + zp->z_type, + (zp->z_origin[0] == '\0') ? "." : zp->z_origin, + zp->z_source); + + for (; optind != argc; optind++) { + tm = argv[optind]; + if (!inet_aton(tm, &zp->z_addr[zp->z_addrcnt])) { + hp = gethostbyname(tm); + if (hp == NULL) { + syslog(LOG_NOTICE, + "uninterpretable server (%s) for %s\n", + tm, zp->z_origin); + continue; + } + memcpy(&zp->z_addr[zp->z_addrcnt], + hp->h_addr, + INADDRSZ); + dprintf(1, "Arg: \"%s\"\n", tm); + } + if (++zp->z_addrcnt >= NSMAX) { + zp->z_addrcnt = NSMAX; + dprintf(1, "NSMAX reached\n"); + break; + } + } + dprintf(1, "addrcnt = %d\n", zp->z_addrcnt); + + res_init(); + _res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE); + result = getzone(zp, serial_no, port); + (void) my_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); + + default: + result = XFER_FAIL; + /* fall through */ + case XFER_TIMEOUT: + case XFER_FAIL: + cleanup_for_exit(); + exit(result); /* error or timeout */ + } + /*NOTREACHED*/ + return (0); /* Make gcc happy. */ +} + +static char *UsageText[] = { + "\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]\n", + "\t[-t trace_file]\n", + "\t[-p port]\n", +#ifdef STUBS + "\t[-S]\n", +#endif + "\t[-C class]\n", + "\t[-x axfr-src]\n", + "\tservers...\n", + NULL +}; + +static void +usage(const char *msg) { + char * const *line; + + fprintf(stderr, "Usage error: %s\n", msg); + fprintf(stderr, "Usage: %s\n", ProgName); + for (line = UsageText; *line; line++) + fputs(*line, stderr); + exit(XFER_FAIL); +} + +#define DEF_DNAME '\001' /* '\0' means the root domain */ +/* XXX: The following variables should probably all be "static" */ +u_int32_t minimum_ttl = 0; +int soa_cnt = 0; +#ifdef STUBS +int ns_cnt = 0; +#endif +int query_type = 0; +int prev_comment = 0; /* was previous record a comment? */ +char zone_top[MAXDNAME]; /* the top of the zone */ +char prev_origin[MAXDNAME]; /* from most recent $ORIGIN line */ +char prev_dname[MAXDNAME] = { DEF_DNAME }; /* from previous record */ +char prev_ns_dname[MAXDNAME] = { DEF_DNAME }; /* from most recent NS record */ + +static int +getzone(struct zoneinfo *zp, u_int32_t serial_no, int port) { + HEADER *hp; + u_int len; + u_int32_t serial; + int s, n, l, error = 0; + u_int cnt; + u_char *cp, *nmp, *eom, *tmp ; + u_char *buf = NULL; + u_int bufsize = 0; + char name[MAXDNAME], name2[MAXDNAME]; + struct sockaddr_in sin; +#ifdef POSIX_SIGNALS + struct sigaction sv, osv; +#else + struct sigvec sv, osv; +#endif + int qdcount, ancount, aucount, class, type; + const char *badsoa_msg = "Nil"; + +#ifdef DEBUG + if (debug) { + (void)fprintf(ddt,"getzone() %s ", zp->z_origin); + switch (zp->z_type) { + case Z_STUB: + fprintf(ddt,"stub\n"); + break; + case Z_SECONDARY: + fprintf(ddt,"secondary\n"); + break; + default: + fprintf(ddt,"unknown type\n"); + } + } +#endif +#ifdef POSIX_SIGNALS + memset(&sv, 0, sizeof sv); + sv.sa_handler = (SIG_FN (*)()) read_alarm; + /* SA_ONSTACK isn't recommended for strict POSIX code */ + /* is it absolutely necessary? */ + /* sv.sa_flags = SA_ONSTACK; */ + sigfillset(&sv.sa_mask); + (void) sigaction(SIGALRM, &sv, &osv); + memset(&sv, 0, sizeof sv); + sv.sa_handler = (SIG_FN (*)()) term_handler; + sigfillset(&sv.sa_mask); + (void) sigaction(SIGTERM, &sv, &osv); +#else + memset(&sv, 0, sizeof sv); + sv.sv_handler = read_alarm; + sv.sv_mask = ~0; + (void) sigvec(SIGALRM, &sv, &osv); + memset(&sv, 0, sizeof sv); + sv.sv_handler = term_handler; + sv.sv_mask = ~0; + (void) sigvec(SIGTERM, &sv, &osv); +#endif + + strcpy(zone_top, zp->z_origin); + if ((l = strlen(zone_top)) != 0 && zone_top[l - 1] == '.') + zone_top[l - 1] = '\0'; + strcpy(prev_origin, zone_top); + + for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { + curclass = zp->z_class; + error = 0; + if (buf == NULL) { + if ((buf = (u_char *)malloc(2 * PACKETSZ)) == NULL) { + syslog(LOG_INFO, "malloc(%u) failed", + 2 * PACKETSZ); + error++; + break; + } + bufsize = 2 * PACKETSZ; + } + if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) { + syslog(LOG_INFO, "socket: %m"); + error++; + break; + } + if (zp->z_axfr_src.s_addr != 0) { + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = 0; /* "ANY" */ + sin.sin_addr = zp->z_axfr_src; + dprintf(2, "binding to address [%s]\n", + inet_ntoa(sin.sin_addr)); + if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) + syslog(LOG_INFO, "warning: bind(%s) failed", + inet_ntoa(zp->z_axfr_src)); + } + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = port; + sin.sin_addr = zp->z_addr[cnt]; + dprintf(2, "connecting to server #%d [%s].%d\n", + cnt+1, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + if (!quiet) + syslog(LOG_INFO, + "connect(%s) for zone %s failed: %m", + inet_ntoa(sin.sin_addr), zp->z_origin); + error++; + (void) my_close(s); + continue; + } + n = res_mkquery(QUERY, zp->z_origin, curclass, + T_SOA, NULL, 0, NULL, buf, bufsize); + if (n < 0) { + if (!quiet) + syslog(LOG_INFO, + "zone %s: res_mkquery T_SOA failed", + zp->z_origin); + (void) my_close(s); +#ifdef POSIX_SIGNALS + (void) sigaction(SIGALRM, &osv, (struct sigaction *)0); +#else + (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); +#endif + return (XFER_FAIL); + } + /* + * Send length & message for SOA query + */ + if (writemsg(s, buf, n) < 0) { + syslog(LOG_INFO, "writemsg: %m"); + error++; + (void) my_close(s); + continue; + } + /* + * Get out your butterfly net and catch the SOA + */ + if (netread(s, (char *)buf, INT16SZ, XFER_TIMER) < 0) { + error++; + (void) my_close(s); + continue; + } + if ((len = ns_get16(buf)) == 0) { + (void) my_close(s); + continue; + } + if (len > bufsize) { + if ((buf = (u_char *)realloc(buf, len)) == NULL) { + syslog(LOG_INFO, + "malloc(%u) failed for SOA from server [%s], zone %s\n", + len, + inet_ntoa(sin.sin_addr), + zp->z_origin); + (void) my_close(s); + continue; + } + bufsize = len; + } + if (netread(s, (char *)buf, len, XFER_TIMER) < 0) { + error++; + (void) my_close(s); + continue; + } +#ifdef DEBUG + if (debug >= 3) { + (void)fprintf(ddt,"len = %d\n", len); + fp_nquery(buf, len, ddt); + } +#endif + hp = (HEADER *) buf; + qdcount = ntohs(hp->qdcount); + ancount = ntohs(hp->ancount); + aucount = ntohs(hp->nscount); + + /* + * close socket if any of these apply: + * 1) rcode != NOERROR + * 2) not an authority response + * 3) not an answer to our question + * 4) both the number of answers and authority count < 1) + */ + if (hp->rcode != NOERROR || !hp->aa || qdcount != 1 || + (ancount < 1 && aucount < 1)) { +#ifndef ultrix /*XXX*/ + syslog(LOG_NOTICE, + "[%s] %s for %s, SOA query got rcode %d, aa %d, ancount %d, aucount %d", + inet_ntoa(sin.sin_addr), + (hp->aa + ? (qdcount==1 ?"no SOA found" :"bad response") + : "not authoritative"), + zp->z_origin[0] != '\0' ? zp->z_origin : ".", + hp->rcode, hp->aa, ancount, aucount); +#endif + error++; + (void) my_close(s); + continue; + } + zp_start = *zp; + if ((int)len < HFIXEDSZ + QFIXEDSZ) { + badsoa_msg = "too short"; + badsoa: + syslog(LOG_INFO, + "malformed SOA from [%s], zone %s: %s", + inet_ntoa(sin.sin_addr), zp->z_origin, + badsoa_msg); + error++; + (void) my_close(s); + continue; + } + /* + * Step through response. + */ + tmp = buf + HFIXEDSZ; + eom = buf + len; + /* Query Section. */ + n = dn_expand(buf, eom, tmp, name2, sizeof name2); + if (n < 0) { + badsoa_msg = "qname error"; + goto badsoa; + } + tmp += n; + if (tmp + 2 * INT16SZ > eom) { + badsoa_msg = "query error"; + goto badsoa; + } + NS_GET16(type, tmp); + NS_GET16(class, tmp); + if (class != curclass || type != T_SOA || + strcasecmp(zp->z_origin, name2) != 0) { + syslog(LOG_INFO, + "wrong query in resp from [%s], zone %s: [%s %s %s]\n", + inet_ntoa(sin.sin_addr), zp->z_origin, + name2, p_class(class), p_type(type)); + error++; + (void) my_close(s); + continue; + } + /* ... Answer Section. + * We may have to loop a little, to bypass SIG SOA's in + * the response. + */ + do { + u_char *cp4; + u_short type, class, dlen; + u_int32_t ttl; + + n = dn_expand(buf, eom, tmp, name2, sizeof name2); + if (n < 0) { + badsoa_msg = "aname error"; + goto badsoa; + } + tmp += n; + + /* Are type, class, and ttl OK? */ + cp4 = tmp; /* Leave tmp pointing to type field */ + if (eom - cp4 < 3 * INT16SZ + INT32SZ) { + badsoa_msg = "zinfo too short"; + goto badsoa; + } + NS_GET16(type, cp4); + NS_GET16(class, cp4); + NS_GET32(ttl, cp4); + NS_GET16(dlen, cp4); + if (cp4 + dlen > eom) { + badsoa_msg = "zinfo dlen too big"; + goto badsoa; + } + if (type == T_SOA) + break; + /* Skip to next record, if any. */ + dprintf(1, "skipping %s %s RR in response\n", + name2, p_type(type)); + tmp = cp4 + dlen; + } while (1); + + if (strcasecmp(zp->z_origin, name2) != 0) { + syslog(LOG_INFO, + "wrong answer in resp from [%s], zone %s: [%s %s %s]\n", + inet_ntoa(sin.sin_addr), zp->z_origin, + name2, p_class(class), p_type(type)); + error++; + (void) my_close(s); + continue; + } + badsoa_msg = soa_zinfo(&zp_start, tmp, eom); + if (badsoa_msg) + goto badsoa; + if (SEQ_GT(zp_start.z_serial, serial_no) || !serial_no) { + const char *l, *nl; + dprintf(1, "need update, serial %u\n", + zp_start.z_serial); + hp = (HEADER *) buf; + soa_cnt = 0; +#ifdef STUBS + ns_cnt = 0; +#endif + gettime(&tt); + for (l = Version; l; l = nl) { + size_t len; + if ((nl = strchr(l, '\n')) != NULL) { + len = nl - l; + nl = nl + 1; + } else { + len = strlen(l); + nl = NULL; + } + while (isspace((unsigned char) *l)) + l++; + if (*l) + fprintf(dbfp, "; BIND version %.*s\n", + (int)len, l); + } + fprintf(dbfp, "; zone '%s' last serial %u\n", + domain, serial_no); + fprintf(dbfp, "; from %s at %s", + inet_ntoa(sin.sin_addr), + ctimel(tt.tv_sec)); + for (;;) { + if ((soa_cnt == 0) || (zp->z_type == Z_STUB)) { +#ifdef STUBS + if (zp->z_type == Z_STUB) { + if (soa_cnt == 1 && + ns_cnt == 0) + query_type = T_NS; + else + query_type = T_SOA; + } else +#endif + query_type = T_AXFR; + n = res_mkquery(QUERY, zp->z_origin, + curclass, query_type, + NULL, 0, + NULL, buf, bufsize); + if (n < 0) { + if (!quiet) { +#ifdef STUBS + if (zp->z_type == Z_STUB) + syslog(LOG_INFO, + (query_type == T_SOA) + ? "zone %s: res_mkquery T_SOA failed" + : "zone %s: res_mkquery T_NS failed", + zp->z_origin); + else +#endif + syslog(LOG_INFO, + "zone %s: res_mkquery T_AXFR failed", + zp->z_origin); + } + (void) my_close(s); +#ifdef POSIX_SIGNALS + sigaction(SIGALRM, &osv, + (struct sigaction *)0); +#else + sigvec(SIGALRM, &osv, + (struct sigvec *)0); +#endif + return (XFER_FAIL); + } + /* + * Send length & msg for zone transfer + */ + if (writemsg(s, buf, n) < 0) { + syslog(LOG_INFO, + "writemsg: %m"); + error++; + (void) my_close(s); + break; + } + } + /* + * Receive length & response + */ + if (netread(s, (char *)buf, INT16SZ, + (soa_cnt == 0) ?300 :XFER_TIMER) + < 0) { + error++; + break; + } + if ((len = ns_get16(buf)) == 0) + break; + if (len > bufsize) { + buf = (u_char *)realloc(buf, len); + if (buf == NULL) { + syslog(LOG_INFO, + "malloc(%u) failed for packet from server [%s], zone %s\n", + len, + inet_ntoa(sin.sin_addr), + zp->z_origin); + error++; + break; + } + bufsize = len; + } + hp = (HEADER *)buf; + eom = buf + len; + if (netread(s, (char *)buf, len, XFER_TIMER) + < 0) { + error++; + break; + } +#ifdef DEBUG + if (debug >= 3) { + (void)fprintf(ddt,"len = %d\n", len); + fp_nquery(buf, len, ddt); + } + if (fp) + fp_nquery(buf, len, fp); +#endif + if (len < HFIXEDSZ) { + struct sockaddr_in my_addr; + char my_addr_text[30]; + int alen; + + badrec: + error++; + alen = sizeof my_addr; + if (getsockname(s, (struct sockaddr *) + &my_addr, &alen) < 0) + sprintf(my_addr_text, + "[errno %d]", errno); + else + sprintf(my_addr_text, + "[%s].%u", + inet_ntoa(my_addr. + sin_addr), + ntohs(my_addr.sin_port) + ); + syslog(LOG_INFO, + "[%s] record too short from [%s], zone %s\n", + my_addr_text, + inet_ntoa(sin.sin_addr), + zp->z_origin); + break; + } + cp = buf + HFIXEDSZ; + 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; +#ifdef STUBS + if (zp->z_type == Z_STUB) { + ancount = ntohs(hp->ancount); + n = 0; + for (cnt = 0; cnt < (u_int)ancount; cnt++) { + n = print_output(zp, serial_no, buf, + len, cp); + if (n < 0) + break; + cp += n; + } + /* + * If we've processed the answer section and + * didn't get any useful answers, bail out. + */ + if (query_type == T_SOA && soa_cnt == 0) { + syslog(LOG_ERR, + "stubs: no SOA in answer"); + error++; + break; + } + if (query_type == T_NS && ns_cnt == 0) { + syslog(LOG_ERR, + "stubs: no NS in answer"); + error++; + break; + } + if (n >= 0 && hp->nscount) { + ancount = ntohs(hp->nscount); + for (cnt = 0; + cnt < (u_int)ancount; + cnt++) { + n = print_output(zp, serial_no, + buf, len, + cp); + if (n < 0) + break; + cp += n; + } + } + ancount = ntohs(hp->arcount); + for (cnt = 0; + n > 0 && cnt < (u_int)ancount; + cnt++) { + n = print_output(zp, serial_no, buf, + len, cp); + cp += n; + } + if (n < 0) { + syslog(LOG_INFO, + "print_output: unparseable answer (%d), zone %s", + hp->rcode, zp->z_origin); + error++; + break; + } + if (cp != eom) { + syslog(LOG_INFO, + "print_output: short answer (%d, %d), zone %s", + cp - buf, eom - buf, + zp->z_origin); + error++; + break; + } + } else { +#endif /*STUBS*/ + ancount = ntohs(hp->ancount); + for (n = cnt = 0; + cnt < (u_int)ancount; + cnt++) { + n = print_output(zp, serial_no, buf, + len, cp); + if (n < 0) + break; + cp += n; + } + if (n < 0) { + syslog(LOG_INFO, + "print_output: unparseable answer (%d), zone %s", + hp->rcode, zp->z_origin); + error++; + break; + } + if (cp != eom) { + syslog(LOG_INFO, + "print_output: short answer (%d, %d), zone %s", + cp - buf, eom - buf, + zp->z_origin); + error++; + break; + } +#ifdef STUBS + } +#endif + + if (soa_cnt >= 2) + break; + + } + (void) my_close(s); + if (error == 0) { +#ifdef POSIX_SIGNALS + (void) sigaction(SIGALRM, &osv, + (struct sigaction *)0); +#else + (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); +#endif + return (XFER_SUCCESS); + } + dprintf(2, "error receiving zone transfer\n"); + } else if (zp_start.z_serial == serial_no) { + (void) my_close(s); + dprintf(1, "zone up-to-date, serial %u\n", zp_start.z_serial); + return (XFER_UPTODATE); + } else { + (void) my_close(s); + if (!quiet) + syslog(LOG_NOTICE, + "serial from [%s], zone %s: %u lower than current: %u\n", + inet_ntoa(sin.sin_addr), zp->z_origin, + zp_start.z_serial, serial_no); + return (XFER_FAIL); + } + } +#ifdef POSIX_SIGNALS + (void) sigaction(SIGALRM, &osv, (struct sigaction *)0); +#else + (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); +#endif + if (!error) + return (XFER_TIMEOUT); + return (XFER_FAIL); +} + +static SIG_FN +term_handler() { + cleanup_for_exit(); + _exit(XFER_FAIL); /* not safe to call exit() from a signal handler */ +} + +/* + * Set flag saying to read was interrupted + * used for a read timer + */ +static SIG_FN +read_alarm() { + read_interrupted = 1; +} + +static int +netread(int fd, char *buf, int len, int timeout) { + static const char setitimerStr[] = "setitimer: %m"; + struct itimerval ival, zeroival; + struct sockaddr_in sa; + int n, salen; +#if defined(NETREAD_BROKEN) + int retries = 0; +#endif + + memset(&zeroival, 0, sizeof zeroival); + ival = zeroival; + ival.it_value.tv_sec = timeout; + while (len > 0) { + if (setitimer(ITIMER_REAL, &ival, NULL) < 0) { + syslog(LOG_INFO, setitimerStr); + return (-1); + } + errno = 0; + salen = sizeof sa; + n = recvfrom(fd, buf, len, 0, (struct sockaddr *)&sa, &salen); + if (n == 0 && errno == 0) { +#if defined(NETREAD_BROKEN) + if (++retries < 42) /* doug adams */ + continue; +#endif + syslog(LOG_INFO, "premature EOF, fetching \"%s\"", + domain); + return (-1); + } + if (n < 0) { + if (errno == 0) { +#if defined(NETREAD_BROKEN) + if (++retries < 42) /* doug adams */ + continue; +#endif + syslog(LOG_INFO, + "recv(len=%d): n=%d && !errno", + len, n); + return (-1); + } + if (errno == EINTR) { + if (!read_interrupted) { + /* It wasn't a timeout; ignore it. */ + continue; + } + errno = ETIMEDOUT; + } + syslog(LOG_INFO, "recv(len=%d): %m", len); + return (-1); + } + buf += n; + len -= n; + } + if (setitimer(ITIMER_REAL, &zeroival, NULL) < 0) { + syslog(LOG_INFO, setitimerStr); + return (-1); + } + return (0); +} + +/* + * Write a counted buffer to a file descriptor preceded by a length word. + */ +static int +writemsg(int rfd, const u_char *msg, int msglen) { + struct iovec iov[2]; + u_char len[INT16SZ]; + int ret; + + __putshort(msglen, len); + iov[0].iov_base = (char *)len; + iov[0].iov_len = INT16SZ; + iov[1].iov_base = (char *)msg; + iov[1].iov_len = msglen; + ret = writev(rfd, iov, 2); + if (ret != INT16SZ + msglen) { + syslog(LOG_DEBUG, "writemsg(%d,%#x,%d) failed: %s", + rfd, msg, msglen, strerror(errno)); + return (-1); + } + return (ret); +} + +static const char * +soa_zinfo(struct zoneinfo *zp, u_char *cp, u_char *eom) { + int n, type, class; + u_int32_t ttl; + u_int16_t dlen; + u_char *rdatap; + + /* Are type, class, and ttl OK? */ + if (eom - cp < 3 * INT16SZ + INT32SZ) + return ("zinfo too short"); + NS_GET16(type, cp); + NS_GET16(class, cp); + NS_GET32(ttl, cp); + NS_GET16(dlen, cp); + rdatap = cp; + if (type != T_SOA || class != curclass) + return ("zinfo wrong typ/cla/ttl"); + /* Skip master name and contact name, we can't validate them. */ + if ((n = dn_skipname(cp, eom)) == -1) + return ("zinfo mname"); + cp += n; + if ((n = dn_skipname(cp, eom)) == -1) + return ("zinfo hname"); + cp += n; + /* Grab the data fields. */ + if (eom - cp < 5 * INT32SZ) + return ("zinfo dlen"); + NS_GET32(zp->z_serial, cp); + NS_GET32(zp->z_refresh, cp); + NS_GET32(zp->z_retry, cp); + NS_GET32(zp->z_expire, cp); + NS_GET32(zp->z_minimum, cp); + if (cp != rdatap + dlen) + return ("bad soa dlen"); + return (NULL); +} + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eom) { \ + hp->rcode = FORMERR; \ + return (-1); \ + } \ + } while (0) + +/* + * 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. + * + * XXX why aren't we using ns_sprintrr() ? + */ +static int +print_output(struct zoneinfo *zp, u_int32_t serial_no, u_char *msg, + int msglen, u_char *rrp) { + u_char *cp; + HEADER *hp = (HEADER *) msg; + u_int32_t addr, ttl, tmpnum; + int i, j, tab, result, n1, n; + u_int class, type, dlen; + char data[MAXDATA]; + u_char *cp1, *cp2, *temp_ptr, *eom, *rr_type_ptr; + u_char *cdata, *rdatap; + char *origin, dname[MAXDNAME]; + const char *proto; + const char *ignore = ""; + const char *badsoa_msg; + int escaped = 0; + + eom = msg + msglen; + cp = rrp; + n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); + rr_type_ptr = cp; + NS_GET16(type, cp); + NS_GET16(class, cp); + NS_GET32(ttl, cp); + /* + * Following the Clarification draft's direction, we treat TTLs with + * the MSB set as if they were 0. + */ + if (ttl > MAXIMUM_TTL) { + syslog(LOG_INFO, "%s: TTL > %u, converted to 0", dname, + MAXIMUM_TTL); + ttl = 0; + } + NS_GET16(dlen, cp); + BOUNDS_CHECK(cp, dlen); + rdatap = cp; + + origin = dname; + while (*origin) { + if (!escaped && *origin == '.') { + origin++; /* skip over '.' */ + break; + } + escaped = (*origin++ == '\\') && !escaped; + } + dprintf(3, "print_output: dname %s type %d class %d ttl %u\n", + dname, type, class, ttl); + /* + * Convert the resource record data into the internal database format. + * CP points to the raw resource record. + * After this switch: + * CP has been updated to point past the RR. + * CP1 points to the internal database version. + * N is the length of the internal database version. + */ + switch (type) { + case T_A: + case T_WKS: + case T_HINFO: + case T_TXT: + case T_X25: + case T_ISDN: + case T_LOC: + case T_NSAP: + case T_AAAA: + case T_KEY: + 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: + n = dn_expand(msg, msg + msglen, cp, data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = (u_char *)data; + n = strlen(data) + 1; + break; + + case T_MINFO: + case T_SOA: + case T_RP: + n = dn_expand(msg, msg + msglen, cp, data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + n = strlen(data) + 1; + cp1 = (u_char *)data + n; + n1 = sizeof data - n; + if (type == T_SOA) + n1 -= 5 * INT32SZ; + n = dn_expand(msg, msg + msglen, cp, (char *)cp1, n1); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 += strlen((char *) cp1) + 1; + if (type == T_SOA) { + BOUNDS_CHECK(cp, 5 * INT32SZ); + temp_ptr = cp + 4 * INT32SZ; + NS_GET32(minimum_ttl, temp_ptr); + /* + * Following the Clarification draft's direction, + * we treat TTLs with the MSB set as if they were 0. + */ + if (minimum_ttl > MAXIMUM_TTL) { + syslog(LOG_INFO, + "%s: SOA minimum TTL > %u, converted to 0", + dname, MAXIMUM_TTL); + minimum_ttl = 0; + } + n = 5 * INT32SZ; + memcpy(cp1, cp, n); + cp += n; + cp1 += n; + } + n = cp1 - (u_char *)data; + cp1 = (u_char *)data; + break; + + case T_NAPTR: + /* Grab weight and port. */ + BOUNDS_CHECK(cp, INT16SZ*2); + memcpy(data, cp, INT16SZ*2); + cp1 = (u_char *)data + INT16SZ*2; + cp += INT16SZ*2; + + /* Flags */ + BOUNDS_CHECK(cp, 1); + n = *cp++; + BOUNDS_CHECK(cp, n); + *cp1++ = n; + memcpy(cp1, cp, n); + cp += n; cp1 += n; + + /* Service */ + BOUNDS_CHECK(cp, 1); + n = *cp++; + BOUNDS_CHECK(cp, n); + *cp1++ = n; + memcpy(cp1, cp, n); + cp += n; cp1 += n; + + /* Regexp */ + BOUNDS_CHECK(cp, 1); + n = *cp++; + BOUNDS_CHECK(cp, n); + *cp1++ = n; + memcpy(cp1, cp, n); + cp += n; cp1 += n; + + /* Replacement */ + n = dn_expand(msg, msg + msglen, cp, (char *)cp1, + sizeof data - ((char *)cp1 - data)); + if (n < 0) + return (-1); + cp += n; + + /* compute end of data */ + cp1 += strlen((char *)cp1) + 1; + /* compute size of data */ + n = cp1 - (u_char *)data; + cp1 = (u_char *)data; + break; + + case T_MX: + case T_AFSDB: + case T_RT: + case T_SRV: + /* grab preference */ + BOUNDS_CHECK(cp, INT16SZ); + memcpy(data, cp, INT16SZ); + cp1 = (u_char *)data + INT16SZ; + cp += INT16SZ; + + if (type == T_SRV) { + BOUNDS_CHECK(cp, INT16SZ*2); + memcpy(cp1, cp, INT16SZ*2); + cp1 += INT16SZ*2; + cp += INT16SZ*2; + } + + /* get name */ + n = dn_expand(msg, msg + msglen, cp, + (char *)cp1, + sizeof data - (cp1 - (u_char *)data)); + if (n < 0) + return (-1); + cp += n; + + /* compute end of data */ + cp1 += strlen((char *) cp1) + 1; + /* compute size of data */ + n = cp1 - (u_char *)data; + cp1 = (u_char *)data; + break; + + case T_PX: + /* grab preference */ + BOUNDS_CHECK(cp, INT16SZ); + memcpy(data, cp, INT16SZ); + cp1 = (u_char *)data + INT16SZ; + cp += INT16SZ; + + /* get MAP822 name */ + n = dn_expand(msg, msg + msglen, cp, + (char *)cp1, sizeof data - INT16SZ); + if (n < 0) + return (-1); + cp += n; + cp1 += (n = (strlen((char *) cp1) + 1)); + n1 = sizeof data - n; + + /* get MAPX400 name */ + n = dn_expand(msg, msg + msglen, cp, (char *)cp1, n1); + if (n < 0) + return (-1); + cp += n; + cp1 += strlen((char *) cp1) + 1; + n = cp1 - (u_char *)data; + cp1 = (u_char *)data; + break; + + case T_SIG: + /* CP is the raw resource record as it arrived. + * CP1, after this switch, points to the internal database version. */ + cp1 = (u_char *)data; + + /* first just copy over the type_covered, algorithm, */ + /* labels, orig ttl, two timestamps, and the footprint */ + BOUNDS_CHECK(cp, NS_SIG_SIGNER); + memcpy(cp1, cp, NS_SIG_SIGNER); + cp += NS_SIG_SIGNER; + cp1 += NS_SIG_SIGNER; + + /* then the signer's name */ + n = dn_expand(msg, msg + msglen, cp, + (char *)cp1, (sizeof data) - 18); + if (n < 0) + return (-1); + cp += n; + cp1 += strlen((char*)cp1)+1; + + /* finally, we copy over the variable-length signature. + Its size is the total data length, minus what we copied. */ + n = dlen - (NS_SIG_SIGNER + n); + if (n > ((int)(sizeof data) - (int)(cp1 - (u_char *)data))) { + hp->rcode = FORMERR; + return (-1); /* out of room! */ + } + memcpy(cp1, cp, n); + cp += n; + cp1 += n; + + /* compute size of data */ + n = cp1 - (u_char *)data; + cp1 = (u_char *)data; + break; + + case T_NXT: + n = dn_expand(msg, msg + msglen, cp, + (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = (u_char *)data + strlen(data) + 1; + n = dlen - n; + if (n > ((int)(sizeof data) - (int)(cp1 - (u_char *)data))) { + hp->rcode = FORMERR; + return (-1); /* out of room! */ + } + if (n > 0) { /* Actually, n should never be less than 4 */ + memcpy(cp1, cp, n); + cp += n; + } else { + hp->rcode = FORMERR; + return (-1); + } + n += cp1 - (u_char *)data; + cp1 = (u_char *)data; + break; + + default: + syslog(LOG_INFO, "\"%s %s %s\" - unknown type (%d)", + dname, p_class(class), p_type(type), type); + hp->rcode = NOTIMP; + return (-1); + } + + if (n > MAXDATA) { + dprintf(1, "update type %d: %d bytes is too much data\n", + type, n); + hp->rcode = FORMERR; + return (-1); + } + if (cp != rdatap + dlen) { + dprintf(1, + "encoded rdata length is %u, but actual length was %u\n", + dlen, (u_int)(cp - rdatap)); + hp->rcode = FORMERR; + return (-1); + } + + cdata = cp1; + result = cp - rrp; + + /* + * Special handling for SOA records. + */ + + if (type == T_SOA) { + if (strcasecmp(dname, zp->z_origin) != 0) { + syslog(LOG_INFO, + "wrong zone name in AXFR (wanted \"%s\", got \"%s\")", + zp->z_origin, dname); + hp->rcode = FORMERR; + return (-1); + } + if (!soa_cnt) { + badsoa_msg = soa_zinfo(&zp_start, rr_type_ptr, eom); + if (badsoa_msg) { + syslog(LOG_INFO, + "malformed SOA for zone %s: %s", + zp->z_origin, badsoa_msg); + hp->rcode = FORMERR; + return (-1); + } + if (SEQ_GT(zp_start.z_serial, serial_no) || + !serial_no) + soa_cnt++; + else { + syslog(LOG_INFO, + "serial went backwards after transfer started"); + return (-1); + } + } else { + badsoa_msg = soa_zinfo(&zp_finish, rr_type_ptr, eom); + if (badsoa_msg) { + syslog(LOG_INFO, + "malformed SOA for zone %s: %s", + zp->z_origin, badsoa_msg); + hp->rcode = FORMERR; + return (-1); + } + dprintf(2, "SOA, serial %u\n", zp_finish.z_serial); + if (zp_start.z_serial != zp_finish.z_serial) { + dprintf(1, "serial changed, restart\n"); + restarts++; + if (restarts > MAX_XFER_RESTARTS) { + syslog(LOG_INFO, + "too many transfer restarts for zone %s", + zp->z_origin); + hp->rcode = FORMERR; + return (-1); + } + soa_cnt = 0; +#ifdef STUBS + ns_cnt = 0; +#endif + minimum_ttl = 0; + strcpy(prev_origin, zp->z_origin); + prev_dname[0] = DEF_DNAME; + /* + * Flush buffer, truncate file + * and seek to beginning to restart. + */ + fflush(dbfp); + if (ftruncate(fileno(dbfp), 0) != 0) { + if (!quiet) + syslog(LOG_INFO, + "ftruncate %s: %m\n", + tmpname); + return (-1); + } + fseek(dbfp, 0L, 0); + return (result); + } + soa_cnt++; + return (result); + } + } + +#ifdef STUBS + if (zp->z_type == Z_STUB) { + if (query_type == T_NS && type == T_NS) + ns_cnt++; + /* + * If we're processing a response to an SOA query, we don't + * want to print anything from the response except for the SOA. + * We do want to check everything in the packet, which is + * why we do this check now instead of earlier. + */ + if (query_type == T_SOA && type != T_SOA) + return (result); + } +#endif + + if (!soa_cnt || soa_cnt >= 2) { + char *gripe; + + if (!soa_cnt) + gripe = "got RR before first SOA"; + else + gripe = "got RR after second SOA"; + syslog(LOG_INFO, "%s in zone %s", gripe, zp->z_origin); + hp->rcode = FORMERR; + return (-1); + } + + /* + * If they are trying to tell us info about something that is + * not in the zone that we are transfering, then ignore it! + * They don't have the authority to tell us this info. + * + * We have to do a bit of checking here - the name that we are + * checking is is fully qualified & may be in a subdomain of the + * zone in question. We also need to ignore any final dots. + * + * If a domain has both NS records and non-NS records, (for + * example, NS and MX records), then we should ignore the non-NS + * records (except that we should not ignore glue A records). + * XXX: It is difficult to do this properly, so we just compare + * the current dname with that in the most recent NS record. + * This defends against the most common error case, + * where the remote server sends MX records soon after the + * NS records for a particular domain. If sent earlier, we lose. XXX + */ + if (!samedomain(dname, domain)) { + (void) fprintf(dbfp, "; Ignoring info about %s, not in zone %s.\n", + dname, domain); + ignore = "; "; + } else if (type != T_NS && type != T_A && + strcasecmp(zone_top, dname) != 0 && + strcasecmp(prev_ns_dname, dname) == 0) + { + (void) fprintf(dbfp, "; Ignoring extra info about %s, invalid after NS delegation.\n", + dname); + ignore = "; "; + } + + /* + * If the current record is not being ignored, but the + * previous record was ignored, then we invalidate information + * that might have been altered by ignored records. + * (This means that we sometimes output unnecessary $ORIGIN + * lines, but that is harmless.) + * + * Also update prev_comment now. + */ + if (prev_comment && ignore[0] == '\0') { + prev_dname[0] = DEF_DNAME; + prev_origin[0] = DEF_DNAME; + } + prev_comment = (ignore[0] != '\0'); + + /* + * set prev_ns_dname if necessary + */ + if (type == T_NS) { + (void) strcpy(prev_ns_dname, dname); + } + + /* + * If the origin has changed, print the new origin + */ + if (strcasecmp(prev_origin, origin)) { + (void) strcpy(prev_origin, origin); + (void) fprintf(dbfp, "%s$ORIGIN %s.\n", ignore, 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; + + (void) strcpy(prev_dname, dname); + escaped = 0; + cutp = dname; + while (*cutp) { + if (!escaped && *cutp == '.') + break; + escaped = (*cutp++ == '\\') && !escaped; + } + *cutp = '\0'; + + if (dname[0] == 0) { + if (origin[0] == 0) + (void) fprintf(dbfp, "%s.\t", ignore); + else + (void) fprintf(dbfp, "%s.%s.\t", + ignore, origin); /* ??? */ + } else { + char *backslash; + backslash = (*dname == '@' || *dname == '$') ? + "\\" : ""; + (void) fprintf(dbfp, "%s%s%s\t", ignore, + backslash, dname); + } + if (strlen(dname) < (size_t)8) + tab = 1; + } else { + (void) fprintf(dbfp, "%s\t", ignore); + tab = 1; + } + + if (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 = cdata; + + /* + * Print type specific data + */ + switch (type) { + + case T_A: + switch (class) { + case C_IN: + case C_HS: + fputs(inet_ntoa(ina_get(cp)), dbfp); + 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 = cdata; + if (cp[0] == '\0') + (void) fprintf(dbfp, ".\t"); + else + (void) fprintf(dbfp, "%s.", cp); + (void) fprintf(dbfp, "\n"); + break; + + case T_HINFO: + case T_ISDN: + cp2 = cp + n; + for (i = 0; i < 2; i++) { + if (i != 0) + (void) putc(' ', dbfp); + n = *cp++; + cp1 = cp + n; + if (cp1 > cp2) + cp1 = cp2; + (void) putc('"', dbfp); + j = 0; + while (cp < cp1) { + if (*cp == '\0') { + cp = cp1; + break; + } + if (strchr("\n\"\\", *cp)) + (void) putc('\\', dbfp); + (void) putc(*cp++, dbfp); + j++; + } + if (j == 0 && (type != T_ISDN || i == 0)) + (void) putc('?', dbfp); + (void) putc('"', 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; + NS_GET32(tmpnum, cp); + (void) fprintf(dbfp, "%s\t\t%u", ignore, tmpnum); + NS_GET32(tmpnum, cp); + (void) fprintf(dbfp, " %u", tmpnum); + NS_GET32(tmpnum, cp); + (void) fprintf(dbfp, " %u", tmpnum); + NS_GET32(tmpnum, cp); + (void) fprintf(dbfp, " %u", tmpnum); + NS_GET32(tmpnum, cp); + (void) fprintf(dbfp, " %u )\n", tmpnum); + break; + + case T_MX: + case T_AFSDB: + case T_RT: + NS_GET16(tmpnum, cp); + (void) fprintf(dbfp, "%u", tmpnum); + (void) fprintf(dbfp, " %s.\n", cp); + break; + + case T_PX: + NS_GET16(tmpnum, cp); + (void) fprintf(dbfp, "%u", tmpnum); + (void) fprintf(dbfp, " %s.", cp); + cp += strlen((char *) cp) + 1; + (void) fprintf(dbfp, " %s.\n", cp); + break; + + case T_TXT: + case T_X25: + cp1 = cp + n; + while (cp < cp1) { + (void) putc('"', dbfp); + if ((i = *cp++) != 0) { + for (j = i; j > 0 && cp < cp1; j--) { + if (strchr("\n\"\\", *cp)) + (void) putc('\\', dbfp); + (void) putc(*cp++, dbfp); + } + } + (void) putc('"', dbfp); + if (cp < cp1) + (void) putc(' ', dbfp); + } + (void) putc('\n', dbfp); + break; + + case T_NSAP: + fprintf(dbfp, "%s\n", inet_nsap_ntoa(n, cp, NULL)); + break; + + case T_AAAA: { + char t[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; + + fprintf(dbfp, "%s\n", inet_ntop(AF_INET6, cp, t, sizeof t)); + break; + } + + case T_LOC: { + char t[255]; + + (void) fprintf(dbfp, "%s\n", loc_ntoa(cp, t)); + break; + } + + case T_NAPTR: { + u_int32_t order, preference; + + /* Order */ + NS_GET16(order, cp); + fprintf(dbfp, "%u", order); + + /* Preference */ + NS_GET16(preference, cp); + fprintf(dbfp, " %u", preference); + + /* Flags */ + if ((n = *cp++) != 0) { + fprintf(dbfp, " \"%.*s\"", (int)n, cp); + cp += n; + } + + /* Service */ + if ((n = *cp++) != 0) { + fprintf(dbfp, " \"%.*s\"", (int)n, cp); + cp += n; + } + + /* Regexp */ + if ((n = *cp++) != 0) { + fprintf(dbfp, " \"%.*s\"", (int)n, cp); + cp += n; + } + + /* Replacement */ + fprintf(dbfp, " %s.\n", cp); + + break; + } + case T_SRV: { + u_int priority, weight, port; + + NS_GET16(priority, cp); + NS_GET16(weight, cp); + NS_GET16(port, cp); + fprintf(dbfp, "\t%u %u %u %s.\n", + priority, weight, port, cp); + break; + } + + case T_WKS: + fputs(inet_ntoa(ina_get(cp)), dbfp); + cp += INADDRSZ; + fputc(' ', dbfp); + proto = protocolname(*cp); + cp += sizeof(char); + (void) fprintf(dbfp, "%s ", proto); + i = 0; + while (cp < 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: + case T_RP: + (void) fprintf(dbfp, "%s.", cp); + cp += strlen((char *) cp) + 1; + (void) fprintf(dbfp, " %s.\n", cp); + break; + + case T_KEY: { + char databuf[16+NS_MD5RSA_MAX_BASE64]; /* 16 for slop */ + u_int keyflags; + + /* get & format key flags */ + keyflags = ns_get16(cp); + (void) fprintf(dbfp, "0x%04x ", keyflags); + cp += INT16SZ; + + /* protocol id */ + (void) fprintf(dbfp, " %u", *cp++); + + /* algorithm id */ + (void) fprintf(dbfp, " %u ", *cp++); + + /* key itself (which may have zero length) */ + n = b64_ntop(cp, (cp1 + n) - cp, databuf, sizeof databuf); + if (n < 0) + fprintf(dbfp, "; BAD BASE64\n"); + else + fprintf(dbfp, "%s\n", databuf); + break; + } + + case T_SIG: { + char databuf[16+NS_MD5RSA_MAX_BASE64]; /* 16 for slop */ + + /* get & format rr type which signature covers */ + (void) fprintf(dbfp,"%s", p_type(ns_get16((u_char*)cp))); + cp += INT16SZ; + + /* algorithm id */ + (void) fprintf(dbfp," %d",*cp++); + + /* labels (# of labels in name) - skip in textual record */ + cp++; + + /* orig time to live (TTL)) */ + (void) fprintf(dbfp," %u", (u_int32_t)ns_get32((u_char*)cp)); + cp += INT32SZ; + + /* expiration time */ + (void) fprintf(dbfp," %s", p_secstodate(ns_get32((u_char*)cp))); + cp += INT32SZ; + + /* time signed */ + (void) fprintf(dbfp," %s", p_secstodate(ns_get32((u_char*)cp))); + cp += INT32SZ; + + /* Key footprint */ + (void) fprintf(dbfp," %d", ns_get16((u_char*)cp)); + cp += INT16SZ; + + /* signer's name */ + (void) fprintf(dbfp, " %s. ", cp); + cp += strlen((char *) cp) + 1; + + /* signature itself */ + n = b64_ntop(cp, (cdata + n) - cp, databuf, sizeof databuf); + if (n < 0) + fprintf (dbfp, "; BAD BASE64\n"); + else + fprintf (dbfp, "%s\n", databuf); + break; + } + + case T_NXT: + fprintf(dbfp, "%s.", (char *)cp); + i = strlen((char *)cp)+1; + cp += i; + n -= i; + for (i=0; i < n*NS_NXT_BITS; i++) + if (NS_NXT_BIT_ISSET (i, cp)) + fprintf(dbfp, " %s", p_type(i)); + fprintf(dbfp,"\n"); + break; + + default: + cp1 = cp + n; + while (cp < cp1) + fprintf(dbfp, "0x%02.2X ", *cp++ & 0xFF); + (void) fprintf(dbfp, "???\n"); + } + if (ferror(dbfp)) { + syslog(LOG_ERR, "%s: %m", tmpname); + cleanup_for_exit(); + exit(XFER_FAIL); + } + return (result); +} + +#ifdef SHORT_FNAMES +/* +** This routine handles creating temporary files with mkstemp +** in the presence of a 14 char filename system. Pathconf() +** does not work over NFS. +*/ +filenamecpy(char *ddtfile, char *optarg) { + int namelen, extra, len; + char *dirname, *filename; + + /* determine the length of filename allowed */ + if((dirname = strrchr(optarg, '/')) == NULL){ + filename = optarg; + } else { + *dirname++ = '\0'; + filename = dirname; + } + namelen = pathconf(dirname == NULL? "." : optarg, _PC_NAME_MAX); + if(namelen <= 0) + namelen = 255; /* length could not be determined */ + if(dirname != NULL) + *--dirname = '/'; + + /* copy a shorter name if it will be longer than allowed */ + extra = (strlen(filename)+strlen(".XXXXXX")) - namelen; + if(extra > 0){ + len = strlen(optarg) - extra; + (void) strncpy(ddtfile, optarg, len); + ddtfile[len] = '\0'; + } else + (void) strcpy(ddtfile, optarg); +} +#endif /* SHORT_FNAMES */ |