/* * 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 1.1.1.1 1998/10/05 18:02:00 tron Exp $"; #endif /* not lint */ #include "port_before.h" #include "fd_setsize.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 */