diff options
author | Paul Traina <pst@FreeBSD.org> | 1994-09-22 19:46:15 +0000 |
---|---|---|
committer | Paul Traina <pst@FreeBSD.org> | 1994-09-22 19:46:15 +0000 |
commit | 735b43167c9cceefd565968c942376de0dbd0908 (patch) | |
tree | 5c7b0371cc85c7edb0788111dc3bbac4da2df709 |
NAMED from BIND 4.9.3 BETA9 pl1 (no local changes)vendor/bind4/4.9.3b9-p1
Notes
Notes:
svn path=/vendor/bind4/dist/; revision=2980
svn path=/vendor/bind4/4.9.3b9-p1/; revision=2982; tag=vendor/bind4/4.9.3b9-p1
38 files changed, 18802 insertions, 0 deletions
diff --git a/usr.sbin/named/Version.c b/usr.sbin/named/Version.c new file mode 100644 index 000000000000..7cbce2820e86 --- /dev/null +++ b/usr.sbin/named/Version.c @@ -0,0 +1,88 @@ +/* + * @(#)Version.c 4.9 (Berkeley) 7/21/90 + * $Id: Version.c,v 4.9.1.3 1994/06/01 21:09:39 vixie Exp $ + */ + +#ifndef lint +char sccsid[] = "@(#)named %VERSION% %WHEN% %WHOANDWHERE%"; +char rcsid[] = "$Id: Version.c,v 4.9.1.3 1994/06/01 21:09:39 vixie Exp $"; +#endif /* not lint */ + +char Version[] = "named %VERSION% %WHEN%\n\t%WHOANDWHERE%"; + +#ifdef COMMENT + +SCCS/s.Version.c: + +D 4.8.3 90/06/27 17:05:21 bloom 37 35 00031/00028/00079 +Version distributed with 4.3 Reno tape (June 1990) + +D 4.8.2 89/09/18 13:57:11 bloom 35 34 00020/00014/00087 +Interim fixes release + +D 4.8.1 89/02/08 17:12:15 karels 34 33 00026/00017/00075 +branch for 4.8.1 + +D 4.8 88/07/09 14:27:00 karels 33 28 00043/00031/00049 +4.8 is here! + +D 4.7 87/11/20 13:15:52 karels 25 24 00000/00000/00062 +4.7.3 beta + +D 4.6 87/07/21 12:15:52 karels 25 24 00000/00000/00062 +4.6 declared stillborn + +D 4.5 87/02/10 12:33:25 kjd 24 18 00000/00000/00062 +February 1987, Network Release. Child (bind) grows up, parent (kevin) leaves home. + +D 4.4 86/10/01 10:06:26 kjd 18 12 00020/00017/00042 +October 1, 1986 Network Distribution + +D 4.3 86/06/04 12:12:18 kjd 12 7 00015/00028/00044 +Version distributed with 4.3BSD + +D 4.2 86/04/30 20:57:16 kjd 7 1 00056/00000/00016 +Network distribution Freeze and one more version until 4.3BSD + +D 1.1 86/04/30 19:30:00 kjd 1 0 00016/00000/00000 +date and time created 86/04/30 19:30:00 by kjd + +code versions: + +Makefile + Makefile 4.14 (Berkeley) 2/28/88 +db.h + db.h 4.13 (Berkeley) 2/17/88 +db_dump.c + db_dump.c 4.20 (Berkeley) 2/17/88 +db_load.c + db_load.c 4.26 (Berkeley) 2/28/88 +db_lookup.c + db_lookup.c 4.14 (Berkeley) 2/17/88 +db_reload.c + db_reload.c 4.15 (Berkeley) 2/28/88 +db_save.c + db_save.c 4.13 (Berkeley) 2/17/88 +db_update.c + db_update.c 4.16 (Berkeley) 2/28/88 +ns_forw.c + ns_forw.c 4.26 (Berkeley) 3/28/88 +ns_init.c + ns_init.c 4.23 (Berkeley) 2/28/88 +ns_main.c + Copyright (c) 1986 Regents of the University of California.\n\ + ns_main.c 4.30 (Berkeley) 3/7/88 +ns_maint.c + ns_maint.c 4.23 (Berkeley) 2/28/88 +ns_req.c + ns_req.c 4.32 (Berkeley) 3/31/88 +ns_resp.c + ns_resp.c 4.50 (Berkeley) 4/7/88 +ns_sort.c + ns_sort.c 4.3 (Berkeley) 2/17/88 +ns_stats.c + ns_stats.c 4.3 (Berkeley) 2/17/88 +newvers.sh + newvers.sh 4.4 (Berkeley) 3/28/88 + +#endif /* COMMENT */ diff --git a/usr.sbin/named/db_defs.h b/usr.sbin/named/db_defs.h new file mode 100644 index 000000000000..db9b32c2e256 --- /dev/null +++ b/usr.sbin/named/db_defs.h @@ -0,0 +1,172 @@ +/* + * from db.h 4.16 (Berkeley) 6/1/90 + * $Id: db_defs.h,v 1.11 1994/07/22 08:42:39 vixie Exp $ + */ + +/* + * ++Copyright++ 1985, 1990 + * - + * Copyright (c) 1985, 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. + * - + * --Copyright-- + */ + +/* + * Global definitions for data base routines. + */ + +#define INVBLKSZ 7 /* # of namebuf pointers per block */ +#define INVHASHSZ 919 /* size of inverse hash table */ + + /* max length of data in RR data field */ +#define MAXDATA 2048 + +#define DB_ROOT_TIMBUF 3600 +#define TIMBUF 300 + +/* + * Hash table structures. + */ +struct databuf { + struct databuf *d_next; /* linked list */ + u_int32_t d_ttl; /* time to live */ + /* if d_zone == DB_Z_CACHE, then + * d_ttl is actually the time when + * the record will expire. + * otherwise (for authoritative + * primary and secondary zones), + * d_ttl is the time to live. + */ + unsigned d_flags :7; /* see below */ + unsigned d_cred :3; /* DB_C_{??????} */ + unsigned d_clev :6; + int16_t d_zone; /* zone number or 0 for the cache */ + int16_t d_class; /* class number */ + int16_t d_type; /* type number */ + int16_t d_mark; /* place to mark data */ + int16_t d_size; /* size of data area */ +#ifdef NCACHE + int16_t d_rcode; /* rcode added for negative caching */ +#endif + int16_t d_rcnt; +#ifdef STATS + struct nameser *d_ns; /* NS from whence this came */ +#endif +/*XXX*/ u_int32_t d_nstime; /* NS response time, milliseconds */ + u_char d_data[sizeof(char*)]; /* malloc'd (padded) */ +}; +#define DATASIZE(n) (sizeof(struct databuf) - sizeof(char*) + n) + +/* + * d_flags definitions + */ +#define DB_F_HINT 0x01 /* databuf belongs to fcachetab */ + +/* + * d_cred definitions + */ +#define DB_C_ZONE 4 /* authoritative zone - best */ +#define DB_C_AUTH 3 /* authoritative answer */ +#define DB_C_ANSWER 2 /* non-authoritative answer */ +#define DB_C_ADDITIONAL 1 /* additional data */ +#define DB_C_CACHE 0 /* cache - worst */ + +struct namebuf { + char *n_dname; /* domain name */ + u_int n_hashval; /* hash value of n_dname */ + struct namebuf *n_next; /* linked list */ + struct databuf *n_data; /* data records */ + struct namebuf *n_parent; /* parent domain */ + struct hashbuf *n_hash; /* hash table for children */ +}; + +#ifdef INVQ +struct invbuf { + struct invbuf *i_next; /* linked list */ + struct namebuf *i_dname[INVBLKSZ]; /* domain name */ +}; +#endif + +struct hashbuf { + int h_size; /* size of hash table */ + int h_cnt; /* number of entries */ + struct namebuf *h_tab[1]; /* malloc'ed as needed */ +}; +#define HASHSIZE(s) (s*sizeof(struct namebuf *) + 2*sizeof(int)) + +#define HASHSHIFT 3 +#define HASHMASK 0x1f + +/* + * Flags to updatedb + */ +#define DB_NODATA 0x01 /* data should not exist */ +#define DB_MEXIST 0x02 /* data must exist */ +#define DB_DELETE 0x04 /* delete data if it exists */ +#define DB_NOTAUTH 0x08 /* must not update authoritative data */ +#define DB_NOHINTS 0x10 /* don't reflect update in fcachetab */ + +#define DB_Z_CACHE (0) /* cache-zone-only db_dump() */ +#define DB_Z_ALL (-1) /* normal db_dump() */ + +/* + * Error return codes + */ +#define OK 0 +#define NONAME -1 +#define NOCLASS -2 +#define NOTYPE -3 +#define NODATA -4 +#define DATAEXISTS -5 +#define NODBFILE -6 +#define TOOMANYZONES -7 +#define GOODDB -8 +#define NEWDB -9 +#define AUTH -10 diff --git a/usr.sbin/named/db_dump.c b/usr.sbin/named/db_dump.c new file mode 100644 index 000000000000..4a0d2c93f1f0 --- /dev/null +++ b/usr.sbin/named/db_dump.c @@ -0,0 +1,892 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)db_dump.c 4.33 (Berkeley) 3/3/91"; +static char rcsid[] = "$Id: db_dump.c,v 4.9.1.12 1994/07/22 08:42:39 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1988, 1990 + * - + * Copyright (c) 1986, 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. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <syslog.h> +#include <resolv.h> +#include <errno.h> + +#include "named.h" + +static int scan_root __P((struct hashbuf *)); +static const char *MkCredStr __P((int)); + +#ifdef ALLOW_T_UNSPEC +static void putbyte __P((int, char **)); +#endif + +/* + * Dump current cache in a format similar to RFC 883. + * + * We try to be careful and determine whether the operation succeeded + * so that the new cache file can be installed. + */ + +void +doachkpt() +{ + FILE *fp; + char tmpcheckfile[256]; + + /* nowhere to checkpoint cache... */ + if (cache_file == NULL) { + dprintf(3, (ddt, "skipping doachkpt (cache_file == NULL)\n")); + return; + } + + dprintf(3, (ddt, "doachkpt()\n")); + + (void) sprintf(tmpcheckfile, "%s.chk", cache_file); + if ((fp = fopen(tmpcheckfile, "w")) == NULL) { + dprintf(3, (ddt, + "doachkpt(can't open %s for write)\n", tmpcheckfile)); + return; + } + + (void) gettime(&tt); + fprintf(fp, "; Dumped at %s", ctime(&tt.tv_sec)); + fflush(fp); + if (ferror(fp)) { + dprintf(3, (ddt, "doachkpt(write to checkpoint file failed)\n")); + return; + } + + if (fcachetab != NULL) { + int n = scan_root(hashtab); + + if (n < MINROOTS) { + syslog(LOG_ERR, "%d root hints... (too low)", n); + fprintf(fp, "; ---- Root hint cache dump ----\n"); + (void) db_dump(fcachetab, fp, DB_Z_CACHE, ""); + } + } + + if (hashtab != NULL) { + fprintf(fp, "; ---- Cache dump ----\n"); + if (db_dump(hashtab, fp, DB_Z_CACHE, "") == NODBFILE) { + dprintf(3, (ddt, "doachkpt(checkpoint failed)\n")); + (void) my_fclose(fp); + return; + } + } + + (void) fsync(fileno(fp)); + if (my_fclose(fp) == EOF) { + return; + } + + if (rename(tmpcheckfile, cache_file)) { + dprintf(3, (ddt, "doachkpt(install %s to %s failed, %d)\n", + tmpcheckfile, cache_file, errno)); + } +} + +/* + * What we do is scan the root hint cache to make sure there are at least + * MINROOTS root pointers with non-0 TTL's so that the checkpoint will not + * lose the root. Failing this, all pointers are written out w/ TTL ~0 + * (root pointers timed out and prime_cache() not done or failed). + */ + +static int +scan_root(htp) + struct hashbuf *htp; +{ + register struct databuf *dp; + register struct namebuf *np; + struct timeval soon; + int roots = 0; + + dprintf(1, (ddt, "scan_root(0x%x)\n", htp)); + + /* metric by which we determine whether a root NS pointer is still */ + /* valid (will be written out if we do a dump). we also add some */ + /* time buffer for safety... */ + (void) gettime(&soon); + soon.tv_sec += TIMBUF; + + for (np = htp->h_tab[0]; np != NULL; np = np->n_next) { + if (np->n_dname[0] == '\0') { + dp = np->n_data; + while (dp != NULL) { + if (dp->d_type == T_NS && + dp->d_ttl > soon.tv_sec) { + roots++; + if (roots >= MINROOTS) + return (roots); + } + dp = dp->d_next; + } + } + } + return (roots); +} + +#ifdef notdef +mark_cache(htp, ttl) + struct hashbuf *htp; + int ttl; +{ + register struct databuf *dp; + register struct namebuf *np; + struct namebuf **npp, **nppend; + struct timeval soon; + + dprintf(1, (ddt, "mark_cache()\n")); + + (void) gettime(&soon); + soon.tv_sec += TIMBUF; + + npp = htp->h_tab; + nppend = npp + htp->h_size; + while (npp < nppend) { + for (np = *npp++; np != NULL; np = np->n_next) { + if (np->n_data == NULL) + continue; + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (dp->d_ttl < soon.tv_sec) + dp->d_ttl = ttl; + } + } + } + + npp = htp->h_tab; + nppend = npp + htp->h_size; + while (npp < nppend) { + for (np = *npp++; np != NULL; np = np->n_next) { + if (np->n_hash == NULL) + continue; + mark_cache(np->n_hash, ttl); + } + } +} +#endif /* notdef */ + +/* + * Dump current data base in a format similar to RFC 883. + */ + +void +doadump() +{ + FILE *fp; + + dprintf(3, (ddt, "doadump()\n")); + + if ((fp = fopen(dumpfile, "w")) == NULL) + return; + gettime(&tt); + fprintf(fp, "; Dumped at %s", ctime(&tt.tv_sec)); + if (zones && nzones) + zt_dump(fp); + fputs( +"; Note: Cr=(auth,answer,addtnl,cache) tag only shown for non-auth RR's\n", + fp); + fputs( +"; Note: NT=milliseconds for any A RR which we've used as a nameserver\n", + fp); + fprintf(fp, "; --- Cache & Data ---\n"); + if (hashtab != NULL) + (void) db_dump(hashtab, fp, DB_Z_ALL, ""); + fprintf(fp, "; --- Hints ---\n"); + if (fcachetab != NULL) + (void) db_dump(fcachetab, fp, DB_Z_ALL, ""); + (void) my_fclose(fp); +} + +#ifdef ALLOW_UPDATES +/* Create a disk database to back up zones + */ +void +zonedump(zp) + register struct zoneinfo *zp; +{ + FILE *fp; + char *fname; + struct hashbuf *htp; + char *op; + struct stat st; + + /* Only dump zone if there is a cache specified */ + if (zp->z_source && *(zp->z_source)) { + dprintf(1, (ddt, "zonedump(%s)\n", zp->z_source)); + + if ((fp = fopen(zp->z_source, "w")) == NULL) + return; + if (op = strchr(zp->z_origin, '.')) + op++; + gettime(&tt); + htp = hashtab; + if (nlookup(zp->z_origin, &htp, &fname, 0) != NULL) { + db_dump(htp, fp, zp-zones, (op == NULL ? "" : op)); + zp->z_flags &= ~Z_CHANGED; /* Checkpointed */ + } + (void) my_fclose(fp); + if (stat(zp->z_source, &st) == 0) + zp->z_ftime = st.st_mtime; + } else { + dprintf(1, (ddt, "zonedump: no zone to dump\n")); + } +} +#endif + +int +zt_dump(fp) + FILE *fp; +{ + register struct zoneinfo *zp; + + fprintf(fp, ";; ++zone table++\n"); + for (zp = &zones[1]; zp < &zones[nzones]; zp++) { + char *pre, buf[64]; + u_int cnt; + + fprintf(fp, "; %s (type %d, class %d, source %s)\n", + zp->z_origin, zp->z_type, zp->z_class, + zp->z_source ? zp->z_source : "Nil"); + fprintf(fp, ";\ttime=%ld, lastupdate=%ld, serial=%u,\n", + zp->z_time, zp->z_lastupdate, zp->z_serial); + fprintf(fp, ";\trefresh=%u, retry=%u, expire=%u, minimum=%u\n", + zp->z_refresh, zp->z_retry, + zp->z_expire, zp->z_minimum); + fprintf(fp, ";\tftime=%ld, xaddr=[%s], state=%04x, pid=%d\n", + zp->z_ftime, inet_ntoa(zp->z_xaddr), + zp->z_flags, zp->z_xferpid); + sprintf(buf, ";\tz_addr[%d]: ", zp->z_addrcnt); + pre = buf; + for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { + fprintf(fp, "%s[%s]", pre, inet_ntoa(zp->z_addr[cnt])); + pre = ", "; + } + if (zp->z_addrcnt) + fputc('\n', fp); + } + fprintf(fp, ";; --zone table--\n"); +} + +int +db_dump(htp, fp, zone, origin) + struct hashbuf *htp; + FILE *fp; + int zone; + char *origin; +{ + register struct databuf *dp = NULL; + register struct namebuf *np; + struct namebuf **npp, **nppend; + char dname[MAXDNAME]; + u_int32_t n; + u_int32_t addr; + int j, i; + register u_char *cp; + u_char *end; + char *proto, *sep; + int found_data = 0, tab, printed_origin = 0; + + npp = htp->h_tab; + nppend = npp + htp->h_size; + while (npp < nppend) { + for (np = *npp++; np != NULL; np = np->n_next) { + if (np->n_data == NULL) + continue; + /* Blecch - can't tell if there is data here for the + * right zone, so can't print name yet + */ + found_data = 0; + /* we want a snapshot in time... */ + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + /* Is the data for this zone? */ + if (zone != DB_Z_ALL && dp->d_zone != zone) + continue; + if (dp->d_zone == DB_Z_CACHE && + dp->d_ttl <= tt.tv_sec && + (dp->d_flags & DB_F_HINT) == 0) + continue; + if (!printed_origin) { + fprintf(fp, "$ORIGIN %s.\n", origin); + printed_origin++; + } + tab = 0; +#ifdef NCACHE + if (dp->d_rcode == NXDOMAIN || + dp->d_rcode == NOERROR_NODATA) { + fputc(';', fp); + } else if (found_data == 0 || found_data == 1) { + found_data = 2; + } +#endif /*NCACHE*/ + if (found_data == 0 || found_data == 2) { + if (np->n_dname[0] == 0) { + if (origin[0] == 0) + fprintf(fp, ".\t"); + else + fprintf(fp, ".%s.\t", origin); /* ??? */ + } else + fprintf(fp, "%s\t", np->n_dname); + if (strlen(np->n_dname) < 8) + tab = 1; + found_data++; + } else { + (void) putc('\t', fp); + tab = 1; + } + if (dp->d_zone == DB_Z_CACHE) { + if (dp->d_flags & DB_F_HINT + && (int32_t)(dp->d_ttl - tt.tv_sec) + < DB_ROOT_TIMBUF) + fprintf(fp, "%d\t", DB_ROOT_TIMBUF); + else + fprintf(fp, "%d\t", + (int)(dp->d_ttl - tt.tv_sec)); + } else if (dp->d_ttl != 0 && + dp->d_ttl != zones[dp->d_zone].z_minimum) + fprintf(fp, "%d\t", (int)dp->d_ttl); + else if (tab) + (void) putc('\t', fp); + fprintf(fp, "%s\t%s\t", + p_class(dp->d_class), + p_type(dp->d_type)); + cp = (u_char *)dp->d_data; + sep = "\t;"; +#ifdef NCACHE +#ifdef RETURNSOA + if (dp->d_rcode == NOERROR_NODATA) { + fprintf(fp, "NODATA%s-$", sep); + goto eoln; + } +#else + if (dp->d_rcode == NXDOMAIN || + dp->d_rcode == NOERROR_NODATA) { + fprintf(fp, "%s%s-$", + (dp->d_rcode == NXDOMAIN) + ?"NXDOMAIN" :"NODATA", + sep); + goto eoln; + } +#endif +#endif + /* + * Print type specific data + */ + switch (dp->d_type) { + case T_A: + switch (dp->d_class) { + case C_IN: + case C_HS: + GETLONG(n, cp); + n = htonl(n); + fprintf(fp, "%s", + inet_ntoa(*(struct in_addr *)&n)); + break; + } + if (dp->d_nstime) { + fprintf(fp, "%sNT=%d", + sep, dp->d_nstime); + sep = " "; + } + break; + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_PTR: + fprintf(fp, "%s.", cp); + break; + + case T_NS: + cp = (u_char *)dp->d_data; + if (cp[0] == '\0') + fprintf(fp, ".\t"); + else + fprintf(fp, "%s.", cp); + break; + + case T_HINFO: + case T_ISDN: + if (n = *cp++) { + fprintf(fp, "\"%.*s\"", (int)n, cp); + cp += n; + } else + fprintf(fp, "\"\""); + if (n = *cp++) + fprintf(fp, " \"%.*s\"", (int)n, cp); + else + fprintf(fp, " \"\""); + break; + + case T_SOA: + fprintf(fp, "%s.", cp); + cp += strlen((char *)cp) + 1; + fprintf(fp, " %s. (\n", cp); +#if defined(RETURNSOA) && defined(NCACHE) + if (dp->d_rcode == NXDOMAIN) + fputs(";", fp); +#endif + cp += strlen((char *)cp) + 1; + GETLONG(n, cp); + fprintf(fp, "\t\t%lu", n); + GETLONG(n, cp); + fprintf(fp, " %lu", n); + GETLONG(n, cp); + fprintf(fp, " %lu", n); + GETLONG(n, cp); + fprintf(fp, " %lu", n); + GETLONG(n, cp); + fprintf(fp, " %lu )", n); +#if defined(RETURNSOA) && defined(NCACHE) + if (dp->d_rcode == NXDOMAIN) { + fprintf(fp,";%s.;NXDOMAIN%s-$",cp,sep); + } +#endif + break; + + case T_MX: + case T_AFSDB: + case T_RT: + GETSHORT(n, cp); + fprintf(fp,"%lu", n); + fprintf(fp," %s.", cp); + break; + + case T_TXT: + case T_X25: + end = (u_char *)dp->d_data + dp->d_size; + (void) putc('"', fp); + while (cp < end) { + if (n = *cp++) { + for (j = n ; j > 0 && cp < end ; j--) + if (*cp == '\n') { + (void) putc('\\', fp); + (void) putc(*cp++, fp); + } else + (void) putc(*cp++, fp); + } + } + (void) fputs("\"", fp); + break; + + case T_NSAP: + (void) fputs(inet_nsap_ntoa(dp->d_size, + dp->d_data, NULL), + fp); + break; + + case T_UINFO: + fprintf(fp, "\"%s\"", cp); + break; + + case T_UID: + case T_GID: + if (dp->d_size == INT32SZ) { + GETLONG(n, cp); + } else { + n = -2; /* XXX - hack */ + } + fprintf(fp, "%u", n); + break; + + case T_WKS: + GETLONG(addr, cp); + addr = htonl(addr); + fprintf(fp, "%s ", + inet_ntoa(*(struct in_addr *)&addr)); + proto = protocolname(*cp); + cp += sizeof(char); + fprintf(fp, "%s ", proto); + i = 0; + while(cp < (u_char *)dp->d_data + dp->d_size) { + j = *cp++; + do { + if (j & 0200) + fprintf(fp, " %s", + servicename(i, proto)); + j <<= 1; + } while (++i & 07); + } + break; + + case T_MINFO: + case T_RP: + fprintf(fp, "%s.", cp); + cp += strlen((char *)cp) + 1; + fprintf(fp, " %s.", cp); + break; +#ifdef ALLOW_T_UNSPEC + case T_UNSPEC: + /* Dump binary data out in an ASCII-encoded + format */ + { + /* Allocate more than enough space: + * actually need 5/4 size + 20 or so + */ + int TmpSize = 2 * dp->d_size + 30; + char *TmpBuf = (char *) malloc(TmpSize); + if (TmpBuf == NULL) { + dprintf(1, + (ddt, + "Dump T_UNSPEC: bad malloc\n" + ) + ); + syslog(LOG_ERR, + "Dump T_UNSPEC: malloc: %m"); + TmpBuf = "BAD_MALLOC"; + } + if (btoa(cp, dp->d_size, TmpBuf, TmpSize) + == CONV_OVERFLOW) { + dprintf(1, (ddt, + "Dump T_UNSPEC: Output buffer overflow\n" + ) + ); + syslog(LOG_ERR, + "Dump T_UNSPEC: Output buffer overflow\n"); + TmpBuf = "OVERFLOW"; + } + fprintf(fp, "%s", TmpBuf); + } + break; +#endif /* ALLOW_T_UNSPEC */ + default: + fprintf(fp, "%s?d_type=%d?", + sep, dp->d_type); + sep = " "; + } + if (dp->d_cred < DB_C_ZONE) { + fprintf(fp, "%sCr=%s", + sep, MkCredStr(dp->d_cred)); + sep = " "; + } else { + fprintf(fp, "%sCl=%d", + sep, dp->d_clev); + sep = " "; + } +eoln: +#ifdef STATS + if (dp->d_ns) { + fprintf(fp, "%s[%s]", + sep, inet_ntoa(dp->d_ns->addr)); + sep = " "; + } +#endif + putc('\n', fp); + } + } + } + if (ferror(fp)) + return(NODBFILE); + + npp = htp->h_tab; + nppend = npp + htp->h_size; + while (npp < nppend) { + for (np = *npp++; np != NULL; np = np->n_next) { + if (np->n_hash == NULL) + continue; + getname(np, dname, sizeof(dname)); + if (db_dump(np->n_hash, fp, zone, dname) == NODBFILE) + return(NODBFILE); + } + } + return(OK); +} + +static const char * +MkCredStr(cred) + int cred; +{ + static char badness[20]; + + switch (cred) { + case DB_C_ZONE: return "zone"; + case DB_C_AUTH: return "auth"; + case DB_C_ANSWER: return "answer"; + case DB_C_ADDITIONAL: return "addtnl"; + case DB_C_CACHE: return "cache"; + default: break; + } + sprintf(badness, "?%d?", cred); + return (badness); +} + +#ifdef ALLOW_T_UNSPEC +/* + * Subroutines to convert between 8 bit binary bytes and printable ASCII. + * Computes the number of bytes, and three kinds of simple checksums. + * Incoming bytes are collected into 32-bit words, then printed in base 85: + * exp(85,5) > exp(2,32) + * The ASCII characters used are between '!' and 'u'; + * 'z' encodes 32-bit zero; 'x' is used to mark the end of encoded data. + * + * Originally by Paul Rutter (philabs!per) and Joe Orost (petsd!joe) for + * the atob/btoa programs, released with the compress program, in mod.sources. + * Modified by Mike Schwartz 8/19/86 for use in BIND. + */ + +/* Make sure global variable names are unique */ +#define Ceor T_UNSPEC_Ceor +#define Csum T_UNSPEC_Csum +#define Crot T_UNSPEC_Crot +#define word T_UNSPEC_word +#define bcount T_UNSPEC_bcount + +static int32_t Ceor, Csum, Crot, word, bcount; + +#define EN(c) ((int) ((c) + '!')) +#define DE(c) ((c) - '!') +#define AddToBuf(bufp, c) **bufp = c; (*bufp)++; +#define times85(x) ((((((x<<2)+x)<<2)+x)<<2)+x) + +/* Decode ASCII-encoded byte c into binary representation and + * place into *bufp, advancing bufp + */ +static int +byte_atob(c, bufp) + register c; + char **bufp; +{ + if (c == 'z') { + if (bcount != 0) + return(CONV_BADFMT); + else { + putbyte(0, bufp); + putbyte(0, bufp); + putbyte(0, bufp); + putbyte(0, bufp); + } + } else if ((c >= '!') && (c < ('!' + 85))) { + if (bcount == 0) { + word = DE(c); + ++bcount; + } else if (bcount < 4) { + word = times85(word); + word += DE(c); + ++bcount; + } else { + word = times85(word) + DE(c); + putbyte((int)((word >> 24) & 255), bufp); + putbyte((int)((word >> 16) & 255), bufp); + putbyte((int)((word >> 8) & 255), bufp); + putbyte((int)(word & 255), bufp); + word = 0; + bcount = 0; + } + } else + return(CONV_BADFMT); + return(CONV_SUCCESS); +} + +/* Compute checksum info and place c into *bufp, advancing bufp */ +static void +putbyte(c, bufp) + register c; + char **bufp; +{ + Ceor ^= c; + Csum += c; + Csum += 1; + if ((Crot & 0x80000000)) { + Crot <<= 1; + Crot += 1; + } else { + Crot <<= 1; + } + Crot += c; + AddToBuf(bufp, c); +} + +/* Read the ASCII-encoded data from inbuf, of length inbuflen, and convert + it into T_UNSPEC (binary data) in outbuf, not to exceed outbuflen bytes; + outbuflen must be divisible by 4. (Note: this is because outbuf is filled + in 4 bytes at a time. If the actual data doesn't end on an even 4-byte + boundary, there will be no problem...it will be padded with 0 bytes, and + numbytes will indicate the correct number of bytes. The main point is + that since the buffer is filled in 4 bytes at a time, even if there is + not a full 4 bytes of data at the end, there has to be room to 0-pad the + data, so the buffer must be of size divisible by 4). Place the number of + output bytes in numbytes, and return a failure/success status */ +int +atob(inbuf, inbuflen, outbuf, outbuflen, numbytes) + char *inbuf; + int inbuflen; + char *outbuf; + int outbuflen; + int *numbytes; +{ + int inc, nb; + int32_t oeor, osum, orot; + char *inp, *outp = outbuf, *endoutp = &outbuf[outbuflen]; + + if ( (outbuflen % 4) != 0) + return(CONV_BADBUFLEN); + Ceor = Csum = Crot = word = bcount = 0; + for (inp = inbuf, inc = 0; inc < inbuflen; inp++, inc++) { + if (outp > endoutp) + return(CONV_OVERFLOW); + if (*inp == 'x') { + inp +=2; + break; + } else { + if (byte_atob(*inp, &outp) == CONV_BADFMT) + return(CONV_BADFMT); + } + } + + /* Get byte count and checksum information from end of buffer */ + if(sscanf(inp, "%ld %lx %lx %lx", numbytes, &oeor, &osum, &orot) != 4) + return(CONV_BADFMT); + if ((oeor != Ceor) || (osum != Csum) || (orot != Crot)) + return(CONV_BADCKSUM); + return(CONV_SUCCESS); +} + +/* Encode binary byte c into ASCII representation and place into *bufp, + advancing bufp */ +static void +byte_btoa(c, bufp) + register c; + char **bufp; +{ + Ceor ^= c; + Csum += c; + Csum += 1; + if ((Crot & 0x80000000)) { + Crot <<= 1; + Crot += 1; + } else { + Crot <<= 1; + } + Crot += c; + + word <<= 8; + word |= c; + if (bcount == 3) { + if (word == 0) { + AddToBuf(bufp, 'z'); + } else { + register int tmp = 0; + register int32_t tmpword = word; + + if (tmpword < 0) { + /* Because some don't support unsigned long */ + tmp = 32; + tmpword -= (int32_t)(85 * 85 * 85 * 85 * 32); + } + if (tmpword < 0) { + tmp = 64; + tmpword -= (int32_t)(85 * 85 * 85 * 85 * 32); + } + AddToBuf(bufp, + EN((tmpword / (int32_t)(85 * 85 * 85 * 85)) + tmp)); + tmpword %= (int32_t)(85 * 85 * 85 * 85); + AddToBuf(bufp, EN(tmpword / (85 * 85 * 85))); + tmpword %= (85 * 85 * 85); + AddToBuf(bufp, EN(tmpword / (85 * 85))); + tmpword %= (85 * 85); + AddToBuf(bufp, EN(tmpword / 85)); + tmpword %= 85; + AddToBuf(bufp, EN(tmpword)); + } + bcount = 0; + } else { + bcount += 1; + } +} + + +/* + * Encode the binary data from inbuf, of length inbuflen, into a + * null-terminated ASCII representation in outbuf, not to exceed outbuflen + * bytes. Return success/failure status + */ +static int +btoa(inbuf, inbuflen, outbuf, outbuflen) + char *inbuf; + int inbuflen; + char *outbuf; + int outbuflen; +{ + int32_t inc, nb; + int32_t oeor, osum, orot; + char *inp, *outp = outbuf, *endoutp = &outbuf[outbuflen -1]; + + Ceor = Csum = Crot = word = bcount = 0; + for (inp = inbuf, inc = 0; inc < inbuflen; inp++, inc++) { + byte_btoa((unsigned char) (*inp), &outp); + if (outp >= endoutp) + return(CONV_OVERFLOW); + } + while (bcount != 0) { + byte_btoa(0, &outp); + if (outp >= endoutp) + return(CONV_OVERFLOW); + } + /* Put byte count and checksum information at end of buffer, delimited + by 'x' */ + (void) sprintf(outp, "x %ld %lx %lx %lx", inbuflen, Ceor, Csum, Crot); + if (&outp[strlen(outp) - 1] >= endoutp) + return(CONV_OVERFLOW); + else + return(CONV_SUCCESS); +} +#endif /* ALLOW_T_UNSPEC */ diff --git a/usr.sbin/named/db_func.h b/usr.sbin/named/db_func.h new file mode 100644 index 000000000000..1c7275cb46e0 --- /dev/null +++ b/usr.sbin/named/db_func.h @@ -0,0 +1,102 @@ +/* db_proc.h - prototypes for functions in db_*.c + * + * $Id: db_func.h,v 1.8 1994/07/23 23:23:56 vixie Exp $ + */ + +/* ++from db_update.c++ */ +extern int db_update __P((char name[], + struct databuf *odp, + struct databuf *newdp, + int flags, + struct hashbuf *htp)); +/* --from db_update.c-- */ + +/* ++from db_reload.c++ */ +extern void db_reload __P((void)); +/* --from db_reload.c-- */ + +/* ++from db_save.c++ */ +extern struct namebuf *savename __P((char *)); +#ifdef DMALLOC +extern struct databuf *savedata_tagged __P((char *, int, + int, int, u_int32_t, + u_char *, int)); +#define savedata(class, type, ttl, data, size) \ + savedata_tagged(__FILE__, __LINE__, class, type, ttl, data, size) +#else +extern struct databuf *savedata __P((int, int, u_int32_t, + u_char *, int)); +#endif +extern struct hashbuf *savehash __P((struct hashbuf *)); +/* --from db_save.c-- */ + +/* ++from db_dump.c++ */ +extern int db_dump __P((struct hashbuf *, FILE *, int, char *)), + zt_dump __P((FILE *)), + atob __P((char *, int, char *, int, int *)); +extern void doachkpt __P((void)), + doadump __P((void)); +#ifdef ALLOW_UPDATES +extern void zonedump __P((struct zoneinfo *)); +#endif +/* --from db_dump.c-- */ + +/* ++from db_load.c++ */ +extern void endline __P((FILE *)), + get_netlist __P((FILE *, struct netinfo **, + int, char *)), + free_netlist __P((struct netinfo **)); +extern int getword __P((char *, int, FILE *)), + getnum __P((FILE *, char *, int)), + db_load __P((char *, char *, struct zoneinfo *, int)), + position_on_netlist __P((struct in_addr, + struct netinfo *)); +extern struct netinfo *addr_on_netlist __P((struct in_addr, + struct netinfo *)); +/* --from db_load.c-- */ + +/* ++from db_glue.c++ */ +extern void buildservicelist __P((void)), + buildprotolist __P((void)), + gettime __P((struct timeval *)), + getname __P((struct namebuf *, char *, int)); +extern int servicenumber __P((char *)), + protocolnumber __P((char *)), + my_close __P((int)), + my_fclose __P((FILE *)), +#ifdef GEN_AXFR + get_class __P((char *)), +#endif + writemsg __P((int, u_char *, int)), + dhash __P((u_char *, int)), + samedomain __P((const char *, const char *)); +extern char *protocolname __P((int)), + *servicename __P((u_int16_t, char *)), + *savestr __P((char *)); +#ifndef BSD +extern int getdtablesize __P((void)); +#endif +extern struct databuf *rm_datum __P((struct databuf *, + struct namebuf *, + struct databuf *)); +extern struct namebuf *rm_name __P((struct namebuf *, + struct namebuf **, + struct namebuf *)); +#ifdef INVQ +extern void addinv __P((struct namebuf *, struct databuf *)), + rminv __P((struct databuf *)); +struct invbuf *saveinv __P((void)); +#endif +/* --from db_glue.c-- */ + +/* ++from db_lookup.c++ */ +extern struct namebuf *nlookup __P((char *, struct hashbuf **, + char **, int)); +extern int match __P((struct databuf *, int, int)); +/* --from db_lookup.c-- */ + +/* ++from db_secure.c++ */ +#ifdef SECURE_ZONES +extern int build_secure_netlist __P((struct zoneinfo *)); +#endif +/* --from db_secure.c-- */ diff --git a/usr.sbin/named/db_glob.h b/usr.sbin/named/db_glob.h new file mode 100644 index 000000000000..74ae45d45763 --- /dev/null +++ b/usr.sbin/named/db_glob.h @@ -0,0 +1,93 @@ +/* + * from db.h 4.16 (Berkeley) 6/1/90 + * $Id: db_glob.h,v 1.2 1994/04/12 08:57:50 vixie Exp $ + */ + +/* + * ++Copyright++ 1985, 1990 + * - + * Copyright (c) 1985, 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. + * - + * --Copyright-- + */ + +/* + * Global variables for data base routines. + */ + + /* ONE_WEEK maximum ttl */ +DECL int max_cache_ttl INIT(7*24*60*60); + + /* 5 minute minimum ttl */ +DECL int min_cache_ttl INIT(5*60); + + /* current line number */ +DECL int lineno; + +#ifdef DUMPFILE +DECL char *dumpfile INIT(DUMPFILE); +#else +DECL char *dumpfile INIT(_PATH_DUMPFILE); +#endif + + /* root hash table */ +DECL struct hashbuf *hashtab INIT(NULL); + + /* hash table of cache read from file */ +DECL struct hashbuf *fcachetab INIT(NULL); + +#ifdef INVQ + /* Inverse query hash table */ +DECL struct invbuf *invtab[INVHASHSZ]; +#endif + +#ifdef FORCED_RELOAD +DECL int reloading INIT(0); +#endif /* FORCED_RELOAD */ diff --git a/usr.sbin/named/db_glue.c b/usr.sbin/named/db_glue.c new file mode 100644 index 000000000000..f3f306941ba4 --- /dev/null +++ b/usr.sbin/named/db_glue.c @@ -0,0 +1,710 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)db_glue.c 4.4 (Berkeley) 6/1/90"; +static char rcsid[] = "$Id: db_glue.c,v 4.9.1.12 1994/07/22 08:42:39 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1988 + * - + * Copyright (c) 1986, 1988 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <stdio.h> +#include <syslog.h> +#include <ctype.h> +#include <netdb.h> +#include <resolv.h> +#include <errno.h> + +#include "named.h" + +struct valuelist { + struct valuelist *next, *prev; + char *name; + char *proto; + int port; +} *servicelist, *protolist; + +#if defined(ultrix) +/* ultrix 4.0 has some icky packaging details. work around them here. + * since this module is linked into named and named-xfer, we end up + * forcing both to drag in our own res_send rather than ultrix's hesiod + * version of that. + */ +static const int (*unused_junk)__P((const u_char *, int, u_char *, int)) = + res_send; +; +#endif + +void +buildservicelist() +{ + struct servent *sp; + struct valuelist *slp; + +#ifdef MAYBE_HESIOD + setservent(0); +#else + setservent(1); +#endif + while (sp = getservent()) { + slp = (struct valuelist *)malloc(sizeof(struct valuelist)); + slp->name = savestr(sp->s_name); + slp->proto = savestr(sp->s_proto); + slp->port = ntohs((u_int16_t)sp->s_port); + slp->next = servicelist; + slp->prev = NULL; + if (servicelist) + servicelist->prev = slp; + servicelist = slp; + } + endservent(); +} + +void +buildprotolist() +{ + struct protoent *pp; + struct valuelist *slp; + +#ifdef MAYBE_HESIOD + setprotoent(0); +#else + setprotoent(1); +#endif + while (pp = getprotoent()) { + slp = (struct valuelist *)malloc(sizeof(struct valuelist)); + slp->name = savestr(pp->p_name); + slp->port = pp->p_proto; + slp->next = protolist; + slp->prev = NULL; + if (protolist) + protolist->prev = slp; + protolist = slp; + } + endprotoent(); +} + +static int +findservice(s, list) + register char *s; + register struct valuelist **list; +{ + register struct valuelist *lp = *list; + int n; + + for (; lp != NULL; lp = lp->next) + if (strcasecmp(lp->name, s) == 0) { + if (lp != *list) { + lp->prev->next = lp->next; + if (lp->next) + lp->next->prev = lp->prev; + (*list)->prev = lp; + lp->next = *list; + *list = lp; + } + return (lp->port); + } + if (sscanf(s, "%d", &n) != 1 || n <= 0) + n = -1; + return (n); +} + +/* + * Convert service name or (ascii) number to int. + */ +int +servicenumber(p) + char *p; +{ + return (findservice(p, &servicelist)); +} + +/* + * Convert protocol name or (ascii) number to int. + */ +int +protocolnumber(p) + char *p; +{ + return (findservice(p, &protolist)); +} + +#if defined(__STDC__) || defined(__GNUC__) +static struct servent * +cgetservbyport(u_int16_t port, + char *proto) +#else +static struct servent * +cgetservbyport(port, proto) + u_int16_t port; + char *proto; +#endif +{ + register struct valuelist **list = &servicelist; + register struct valuelist *lp = *list; + static struct servent serv; + + port = htons(port); + for (; lp != NULL; lp = lp->next) { + if (port != (u_int16_t)lp->port) + continue; + if (strcasecmp(lp->proto, proto) == 0) { + if (lp != *list) { + lp->prev->next = lp->next; + if (lp->next) + lp->next->prev = lp->prev; + (*list)->prev = lp; + lp->next = *list; + *list = lp; + } + serv.s_name = lp->name; + serv.s_port = htons((u_int16_t)lp->port); + serv.s_proto = lp->proto; + return (&serv); + } + } + return (0); +} + +static struct protoent * +cgetprotobynumber(proto) + register int proto; +{ + register struct valuelist **list = &protolist; + register struct valuelist *lp = *list; + static struct protoent prot; + + for (; lp != NULL; lp = lp->next) + if (lp->port == proto) { + if (lp != *list) { + lp->prev->next = lp->next; + if (lp->next) + lp->next->prev = lp->prev; + (*list)->prev = lp; + lp->next = *list; + *list = lp; + } + prot.p_name = lp->name; + prot.p_proto = lp->port; + return (&prot); + } + return (0); +} + +char * +protocolname(num) + int num; +{ + static char number[8]; + struct protoent *pp; + + pp = cgetprotobynumber(num); + if(pp == 0) { + (void) sprintf(number, "%d", num); + return (number); + } + return (pp->p_name); +} + +#if defined(__STDC__) || defined(__GNUC__) +char * +servicename(u_int16_t port, char *proto) +#else +char * +servicename(port, proto) + u_int16_t port; + char *proto; +#endif +{ + static char number[8]; + struct servent *ss; + + ss = cgetservbyport(htons(port), proto); + if (ss == 0) { + (void) sprintf(number, "%d", port); + return (number); + } + return (ss->s_name); +} + +int +db_getclev(origin) + char *origin; +{ + int lev = 0; + dprintf(1, (ddt, "db_getclev of \"%s\"", origin)); + if (origin && *origin) + lev++; + while (origin && (origin = strchr(origin, '.'))) { + origin++; + lev++; + } + dprintf(1, (ddt, " = %d\n", lev)); + return (lev); +} + +void +gettime(ttp) + struct timeval *ttp; +{ + if (gettimeofday(ttp, NULL) < 0) + syslog(LOG_ERR, "gettimeofday: %m"); + return; +} + +#if !defined(BSD) +int +getdtablesize() +{ +#if defined(USE_POSIX) + int j = (int) sysconf(_SC_OPEN_MAX); + + if (j >= 0) + return (j); +#endif /* POSIX */ + return (FD_SETSIZE); +} +#endif /* BSD */ + +int +my_close(fd) + int fd; +{ + int s = close(fd); + + if (s < 0) { + syslog(LOG_ERR, "close(%d) failed: %m", fd); + dprintf(3, (ddt, "close(%d) failed: %s\n", + fd, strerror(errno))); + } else { + dprintf(3, (ddt, "close(%d) succeeded\n", fd)); + } + return (s); +} + +#ifdef GEN_AXFR +/* + * Map class names to number + */ +struct map { + char *token; + int val; +}; + +static struct map map_class[] = { + "in", C_IN, + "chaos", C_CHAOS, + "hs", C_HS, + NULL, 0, +}; + +int +get_class(class) + char *class; +{ + struct map *mp; + + if (isdigit(*class)) + return (atoi(class)); + for (mp = map_class; mp->token != NULL; mp++) + if (strcasecmp(class, mp->token) == 0) + return (mp->val); + return (C_IN); +} +#endif + +int +my_fclose(fp) + FILE *fp; +{ + int fd = fileno(fp), + s = fclose(fp); + + if (s < 0) { + syslog(LOG_ERR, "fclose(%d) failed: %m", fd); + dprintf(3, (ddt, "fclose(%d) failed: %s\n", + fd, strerror(errno))); + } else { + dprintf(3, (ddt, "fclose(%d) succeeded\n", fd)); + } + return (s); +} + +/* + * Make a copy of a string and return a pointer to it. + */ +char * +savestr(str) + char *str; +{ + char *cp; + + cp = (char *)malloc(strlen(str) + 1); + if (cp == NULL) { + syslog(LOG_ERR, "savestr: %m"); + exit(1); + } + (void) strcpy(cp, str); + return (cp); +} + +int +writemsg(rfd, msg, msglen) + int rfd; + u_char *msg; + int msglen; +{ + struct iovec iov[2]; + u_char len[INT16SZ]; + + __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; + if (writev(rfd, iov, 2) != INT16SZ + msglen) { + dprintf(1, (ddt, "write failed %d\n", errno)); + return (-1); + } + return (0); +} + +/* rm_datum(dp, np, pdp) + * remove datum 'dp' from name 'np'. pdp is previous data pointer. + * return value: + * "next" field from removed datum, suitable for relinking + */ +struct databuf * +rm_datum(dp, np, pdp) + register struct databuf *dp; + register struct namebuf *np; + register struct databuf *pdp; +{ + register struct databuf *ndp = dp->d_next; + + dprintf(3, (ddt, "rm_datum(%x, %x, %x) -> %x\n", + dp, np->n_data, pdp, ndp)); +#ifdef INVQ + rminv(dp); +#endif + if (pdp == NULL) + np->n_data = ndp; + else + pdp->d_next = ndp; +#ifdef DATUMREFCNT + if (--(dp->d_rcnt)) { + switch(dp->d_type) { + case T_NS: + dprintf(1, (ddt, "rm_datum: %s rcnt = %d\n", + dp->d_data, dp->d_rcnt)); + break; + case T_A: + dprintf(1, (ddt, "rm_datum: %08.8X rcnt = %d\n", + *(int32_t*)(dp->d_data), dp->d_rcnt)); + break; + default: + dprintf(1, (ddt, "rm_datum: rcnt = %d\n", dp->d_rcnt)); + } + } else +#endif + free((char *)dp); + return (ndp); +} + +/* rm_name(np, he, pnp) + * remove name 'np' from parent 'pp'. pnp is previous name pointer. + * return value: + * "next" field from removed name, suitable for relinking + */ +struct namebuf * +rm_name(np, pp, pnp) + struct namebuf *np, **pp, *pnp; +{ + struct namebuf *nnp = np->n_next; + char *msg; + + /* verify */ + if ( (np->n_data && (msg = "data")) + || (np->n_hash && (msg = "hash")) + ) { + dprintf(1, (ddt, + "rm_name(%x(%s)): non-nil %s pointer\n", + np, np->n_dname?np->n_dname:"Nil", msg)); + syslog(LOG_ERR, + "rm_name(%x(%s)): non-nil %s pointer\n", + np, np->n_dname?np->n_dname:"Nil", msg); + abort(); + } + + /* unlink */ + if (pnp) { + pnp->n_next = nnp; + } else { + *pp = nnp; + } + + /* deallocate */ + free(np->n_dname); + free((char*) np); + + /* done */ + return (nnp); +} + +/* + * Get the domain name of 'np' and put in 'buf'. Bounds checking is done. + */ +void +getname(np, buf, buflen) + struct namebuf *np; + char *buf; + int buflen; +{ + register char *cp; + register int i; + + cp = buf; + while (np != NULL) { + if ((i = strlen(np->n_dname))+1 >= buflen) { + *cp = '\0'; + syslog(LOG_ERR, "domain name too long: %s...\n", buf); + strcpy(buf, "Name_Too_Long"); + return; + } + if (cp != buf) + *cp++ = '.'; + (void) strcpy(cp, np->n_dname); + cp += i; + buflen -= (i+1); + np = np->n_parent; + } + *cp = '\0'; +} + +#ifdef INVQ +/* + * Add data 'dp' to inverse query tables for name 'np'. + */ +void +addinv(np, dp) + struct namebuf *np; + struct databuf *dp; +{ + register struct invbuf *ip; + register int hval, i; + + switch (dp->d_type) { + case T_A: + case T_UID: + case T_GID: + break; + + default: + return; + } + + hval = dhash(dp->d_data, dp->d_size); + for (ip = invtab[hval]; ip != NULL; ip = ip->i_next) + for (i = 0; i < INVBLKSZ; i++) + if (ip->i_dname[i] == NULL) { + ip->i_dname[i] = np; + return; + } + ip = saveinv(); + ip->i_next = invtab[hval]; + invtab[hval] = ip; + ip->i_dname[0] = np; +} + +/* + * Remove data 'odp' from inverse query table. + */ +void +rminv(odp) + struct databuf *odp; +{ + register struct invbuf *ip; + register struct databuf *dp; + struct namebuf *np; + register int i; + + for (ip = invtab[dhash(odp->d_data, odp->d_size)]; ip != NULL; + ip = ip->i_next) { + for (i = 0; i < INVBLKSZ; i++) { + if ((np = ip->i_dname[i]) == NULL) + break; + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (dp != odp) + continue; + while (i < INVBLKSZ-1) { + ip->i_dname[i] = ip->i_dname[i+1]; + i++; + } + ip->i_dname[i] = NULL; + return; + } + } + } +} + +/* + * Allocate an inverse query buffer. + */ +struct invbuf * +saveinv() +{ + register struct invbuf *ip; + + ip = (struct invbuf *) malloc(sizeof(struct invbuf)); + if (ip == NULL) { + syslog(LOG_ERR, "saveinv: %m"); + exit(1); + } + ip->i_next = NULL; + bzero((char *)ip->i_dname, sizeof(ip->i_dname)); + return (ip); +} +#endif /*INVQ*/ + +/* + * Compute hash value from data. + */ +int +dhash(dp, dlen) + u_char *dp; + int dlen; +{ + register u_char *cp; + register unsigned hval; + register int n; + + n = dlen; + if (n > 8) + n = 8; + hval = 0; + for (cp = dp; --n >= 0; ) { + hval <<= 1; + hval += *cp++; + } + return (hval % INVHASHSZ); +} + +/* +** SAMEDOMAIN -- Check whether a name belongs to a domain +** ------------------------------------------------------ +** +** Returns: +** TRUE if the given name lies in the domain. +** FALSE otherwise. +** +** Trailing dots are first removed from name and domain. +** Always compare complete subdomains, not only whether the +** domain name is the trailing string of the given name. +** +** "host.foobar.top" lies in "foobar.top" and in "top" and in "" +** but NOT in "bar.top" +** +** this implementation of samedomain() is thanks to Bob Heiney. +*/ + +int +samedomain(a, b) + const char *a, *b; +{ + size_t la, lb; + const char *cp; + + la = strlen(a); + lb = strlen(b); + + /* don't count trailing dots, if any. */ + if (la && a[la-1]=='.') + la--; + if (lb && b[lb-1]=='.') + lb--; + + /* lb==0 means b is the root domain, so a must be in b. */ + if (lb == 0) + return (1); + + /* b longer than a means a can't be in b. */ + if (lb > la) + return (0); + + /* We use strncasecmp because we might be trying to + * ignore trailing dots. */ + if (lb == la) + return (strncasecmp(a, b, lb) == 0); + + /* Ok, we know la > lb. */ + + /* Point at the character before the last 'lb' characters of a. */ + cp = a + (la - lb - 1); + + /* If it isn't '.', can't be a match (this lets us avoid + * having "foobar.com" match "bar.com"). */ + if (*cp != '.') + return (0); + + cp++; + + /* We use strncasecmp because we might be trying to + * ignore trailing dots. */ + return (strncasecmp(cp, b, lb)==0); +} diff --git a/usr.sbin/named/db_load.c b/usr.sbin/named/db_load.c new file mode 100644 index 000000000000..dfd46e3af38f --- /dev/null +++ b/usr.sbin/named/db_load.c @@ -0,0 +1,1214 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)db_load.c 4.38 (Berkeley) 3/2/91"; +static char rcsid[] = "$Id: db_load.c,v 4.9.1.18 1994/07/23 23:23:56 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1988, 1990 + * - + * Copyright (c) 1986, 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. + * - + * --Copyright-- + */ + +/* + * Load data base from ascii backupfile. Format similar to RFC 883. + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <syslog.h> +#include <ctype.h> +#include <netdb.h> + +#include "named.h" + +static int gettoken __P((register FILE *, char *)), + getnonblank __P((FILE *, char *)), + getprotocol __P((FILE *, char *)), + getservices __P((int, char *, FILE *, char *)); +static void makename __P((char *, char *)); +static int empty_token = 0; + +/* + * Map class and type names to number + */ +struct map { + char token[8]; + int val; +}; + +struct map m_class[] = { + "in", C_IN, +#ifdef notdef + "any", C_ANY, /* any is a QCLASS, not CLASS */ +#endif + "chaos", C_CHAOS, + "hs", C_HS, +}; +#define NCLASS (sizeof(m_class) / sizeof(struct map)) + +struct map m_type[] = { + "a", T_A, + "ns", T_NS, + "cname", T_CNAME, + "soa", T_SOA, + "mb", T_MB, + "mg", T_MG, + "mr", T_MR, + "null", T_NULL, + "wks", T_WKS, + "ptr", T_PTR, + "hinfo", T_HINFO, + "minfo", T_MINFO, + "mx", T_MX, + "uinfo", T_UINFO, + "txt", T_TXT, + "rp", T_RP, + "afsdb", T_AFSDB, + "x25", T_X25, + "isdn", T_ISDN, + "rt", T_RT, + "nsap", T_NSAP, + "uid", T_UID, + "gid", T_GID, +#ifdef notdef + "any", T_ANY, /* any is a QTYPE, not TYPE */ +#endif +#ifdef ALLOW_T_UNSPEC + "unspec", T_UNSPEC, +#endif /* ALLOW_T_UNSPEC */ +}; +#define NTYPE (sizeof(m_type) / sizeof(struct map)) + +/* + * Parser token values + */ +#define CURRENT 1 +#define DOT 2 +#define AT 3 +#define DNAME 4 +#define INCLUDE 5 +#define ORIGIN 6 +#define ERROR 7 + +static int clev; /* a zone deeper in a heirachy has more credability */ + +/* int + * db_load(filename, in_origin, zp, doinginclude) + * load a database from `filename' into zone `zp'. append `origin' + * to all nonterminal domain names in the file. `doinginclude' is + * true if this is a $INCLUDE file. + * returns: + * -1 = can't open file + * 0 = success + * >0 = number of errors encountered + */ +int +db_load(filename, in_origin, zp, doinginclude) + char *filename, *in_origin; + struct zoneinfo *zp; + int doinginclude; +{ + register char *cp; + register struct map *mp; + char domain[MAXDNAME]; + char origin[MAXDNAME]; + char tmporigin[MAXDNAME]; + char buf[MAXDATA]; + char data[MAXDATA]; + char *cp1; + char *op; + int c, class, type, ttl, dbflags, dataflags, multiline; + static int read_soa; /* number of soa's read */ + struct databuf *dp; + FILE *fp; + int slineno, i, errs = 0, didinclude = 0; + register u_int32_t n; + struct stat sb; + struct in_addr ina; + + if (!doinginclude) { + read_soa = 0; + clev = db_getclev(in_origin); + } + + dprintf(1, (ddt,"db_load(%s, %s, %d, %d)\n", + filename, in_origin, zp - zones, doinginclude)); + + (void) strcpy(origin, in_origin); + if ((fp = fopen(filename, "r")) == NULL) { + syslog(LOG_ERR, "%s: %m", filename); + dprintf(1, (ddt, "db_load: error opening file %s\n", + filename)); + return (-1); + } + if (zp->z_type == Z_CACHE) { + dbflags = DB_NODATA | DB_NOHINTS; + dataflags = DB_F_HINT; + } else { + dbflags = DB_NODATA; + dataflags = 0; + } + gettime(&tt); + if (fstat(fileno(fp), &sb) < 0) { + syslog(LOG_ERR, "%s: %m", filename); + sb.st_mtime = (int)tt.tv_sec; + } + slineno = lineno; + lineno = 1; + domain[0] = '\0'; + class = zp->z_class; + zp->z_flags &= ~(Z_INCLUDE|Z_DB_BAD); + while ((c = gettoken(fp, filename)) != EOF) { + switch (c) { + case INCLUDE: + if (!getword((char *)buf, sizeof(buf), fp)) + /* file name*/ + break; + if (!getword(tmporigin, sizeof(tmporigin), fp)) + strcpy(tmporigin, origin); + else { + makename(tmporigin, origin); + endline(fp); + } + didinclude = 1; + errs += db_load((char *)buf, tmporigin, zp, 1); + continue; + + case ORIGIN: + (void) strcpy((char *)buf, origin); + if (!getword(origin, sizeof(origin), fp)) + break; + dprintf(3, (ddt, "db_load: origin %s, buf %s\n", + origin, buf)); + makename(origin, buf); + dprintf(3, (ddt, "db_load: origin now %s\n", origin)); + continue; + + case DNAME: + if (!getword(domain, sizeof(domain), fp)) + break; + n = strlen(domain) - 1; + if (domain[n] == '.') + domain[n] = '\0'; + else if (*origin) { + (void) strcat(domain, "."); + (void) strcat(domain, origin); + } + goto gotdomain; + + case AT: + (void) strcpy(domain, origin); + goto gotdomain; + + case DOT: + domain[0] = '\0'; + /* fall thru ... */ + case CURRENT: + gotdomain: + if (!getword((char *)buf, sizeof(buf), fp)) { + if (c == CURRENT) + continue; + break; + } + cp = buf; + ttl = 0; + if (isdigit(*cp)) { + n = 0; + do { + if (n > (INT_MAX - (*cp - '0')) / 10) { + syslog(LOG_ERR, + "%s: line %d: number > %lu\n", + filename, lineno, INT_MAX); + dprintf(1, (ddt, + "%s: line %d: number > %lu\n", + filename, lineno, INT_MAX)); + n = INT_MAX; + cp++; + } else + n = n * 10 + (*cp++ - '0'); + } + while (isdigit(*cp)); + if (zp->z_type == Z_CACHE) { + /* this allows the cache entry to age */ + /* while sitting on disk (powered off) */ + if (n > max_cache_ttl) + n = max_cache_ttl; + n += sb.st_mtime; + } + ttl = n; + if (!getword((char *)buf, sizeof(buf), fp)) + break; + } + for (mp = m_class; mp < m_class+NCLASS; mp++) + if (!strcasecmp((char *)buf, mp->token)) { + class = mp->val; + (void) getword((char *)buf, + sizeof(buf), fp); + break; + } + for (mp = m_type; mp < m_type+NTYPE; mp++) + if (!strcasecmp((char *)buf, mp->token)) { + type = mp->val; + goto fndtype; + } + dprintf(1, (ddt, "%s: Line %d: Unknown type: %s.\n", + filename, lineno, buf)); + errs++; + syslog(LOG_ERR, "%s: Line %d: Unknown type: %s.\n", + filename, lineno, buf); + break; + fndtype: +#ifdef ALLOW_T_UNSPEC + /* Don't do anything here for T_UNSPEC... + * read input separately later + */ + if (type != T_UNSPEC) { +#endif + if (!getword((char *)buf, sizeof(buf), fp)) + break; + dprintf(3, + (ddt, + "d='%s', c=%d, t=%d, ttl=%d, data='%s'\n", + domain, class, type, ttl, buf)); +#ifdef ALLOW_T_UNSPEC + } +#endif + /* + * Convert the ascii data 'buf' to the proper format + * based on the type and pack into 'data'. + */ + switch (type) { + case T_A: + if (!inet_aton(buf, &ina)) + goto err; + n = ntohl(ina.s_addr); + cp = data; + PUTLONG(n, cp); + n = INT32SZ; + break; + + case T_HINFO: + case T_ISDN: + n = strlen((char *)buf); + if (n > 255) { + syslog(LOG_WARNING, + "%s: line %d: CPU type too long", + filename, lineno); + n = 255; + } + data[0] = n; + bcopy(buf, (char *)data + 1, (int)n); + if (n == 0) + goto err; + n++; + if (!getword((char *)buf, sizeof(buf), fp)) + i = 0; + else { + endline(fp); + i = strlen((char *)buf); + } + if (i == 0) { + /* goto err; */ + /* XXX tolerate for now */ + data[n++] = 1; + data[n++] = '?'; + break; + } + if (i > 255) { + syslog(LOG_WARNING, + "%s:%d: OS type too long", + filename, lineno); + i = 255; + } + data[n] = i; + bcopy(buf, data + n + 1, i); + n += i + 1; + break; + + case T_SOA: + case T_MINFO: + case T_RP: + (void) strcpy((char *)data, (char *)buf); + makename(data, origin); + cp = data + strlen((char *)data) + 1; + if (!getword((char *)cp, + sizeof(data) - (cp - data), fp)) + goto err; + makename(cp, origin); + cp += strlen((char *)cp) + 1; + if (type != T_SOA) { + n = cp - data; + break; + } + if (class != zp->z_class) { + syslog(LOG_WARNING, + "%s:%d: %s", + filename, lineno, + "SOA class not same as zone's"); + } + c = getnonblank(fp, filename); + if (c == '(') { + multiline = 1; + } else { + multiline = 0; + ungetc(c, fp); + } + zp->z_serial = getnum(fp, filename, 1); + n = (u_int32_t) zp->z_serial; + PUTLONG(n, cp); + zp->z_refresh = getnum(fp, filename, 0); + n = (u_int32_t) zp->z_refresh; + PUTLONG(n, cp); + if (zp->z_type == Z_SECONDARY +#if defined(STUBS) + || zp->z_type == Z_STUB +#endif + ) { + zp->z_time = sb.st_mtime + + zp->z_refresh; + } + zp->z_retry = getnum(fp, filename, 0); + n = (u_int32_t) zp->z_retry; + PUTLONG(n, cp); + zp->z_expire = getnum(fp, filename, 0); + n = (u_int32_t) zp->z_expire; + PUTLONG (n, cp); + zp->z_minimum = getnum(fp, filename, 0); + n = (u_int32_t) zp->z_minimum; + PUTLONG (n, cp); + n = cp - data; + if (multiline) { + if (getnonblank(fp, filename) != ')') + goto err; + } + read_soa++; + endline(fp); + break; + + case T_UID: + case T_GID: + n = 0; + cp = buf; + while (isdigit(*cp)) + n = n * 10 + (*cp++ - '0'); + if (cp == buf) + goto err; + cp = data; + PUTLONG(n, cp); + n = INT32SZ; + break; + + case T_WKS: + /* Address */ + if (!inet_aton(buf, &ina)) + goto err; + n = ntohl(ina.s_addr); + cp = data; + PUTLONG(n, cp); + *cp = (char)getprotocol(fp, filename); + /* Protocol */ + n = INT32SZ + sizeof(char); + /* Services */ + n = getservices((int)n, data, fp, filename); + break; + + case T_NS: + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_PTR: + (void) strcpy((char *)data, (char *)buf); + makename(data, origin); + n = strlen((char *)data) + 1; + break; + + case T_UINFO: + cp = strchr((char *)buf, '&'); + bzero(data, sizeof(data)); + if ( cp != NULL) { + (void) strncpy((char *)data, + (char *)buf, cp - buf); + op = strchr(domain, '.'); + if ( op != NULL) + (void) strncat((char *)data, + domain,op-domain); + else + (void) strcat((char *)data, + domain); + (void) strcat((char *)data, + (char *)++cp); + } else + (void) strcpy((char *)data, + (char *)buf); + n = strlen((char *)data) + 1; + break; + case T_MX: + case T_AFSDB: + case T_RT: + n = 0; + cp = buf; + while (isdigit(*cp)) + n = n * 10 + (*cp++ - '0'); + /* catch bad values */ + if ((cp == buf) || (n > 65535)) + goto err; + + cp = data; + PUTSHORT((u_int16_t)n, cp); + + if (!getword((char *)buf, sizeof(buf), fp)) + goto err; + (void) strcpy((char *)cp, (char *)buf); + makename(cp, origin); + /* advance pointer to end of data */ + cp += strlen((char *)cp) +1; + + /* now save length */ + n = (cp - data); + break; + + case T_TXT: + case T_X25: + cp = buf + (n = strlen(buf)); + while ((i = getc(fp), *cp = i, i != EOF) + && *cp != '\n' + && (n < MAXDATA)) { + cp++; n++; + } + if (*cp == '\n') /* leave \n for getword */ + ungetc(*cp, fp); + *cp = '\0'; + /* now do normal processing */ + + i = strlen((char *)buf); + cp = data; + cp1 = buf; + /* + * there is expansion here so make sure we + * don't overflow data + */ + if (i > sizeof(data) * 255 / 256) { + syslog(LOG_WARNING, + "%s: line %d: TXT record truncated", + filename, lineno); + i = sizeof(data) * 255 / 256; + } + while (i > 255) { + *cp++ = 255; + bcopy(cp1, cp, 255); + cp += 255; + cp1 += 255; + i -= 255; + } + *cp++ = i; + bcopy(cp1, cp, i); + cp += i; + n = cp - data; + endline(fp); + break; + + case T_NSAP: + n = inet_nsap_addr(buf, data, MAXDATA); + if (n == 0) + goto err; + endline(fp); + break; +#ifdef ALLOW_T_UNSPEC + case T_UNSPEC: + { + int rcode; + fgets(buf, sizeof(buf), fp); + dprintf(1, (ddt, "loading T_UNSPEC\n")); + if (rcode = atob(buf, + strlen((char*)buf), + data, sizeof(data), + &n)) { + if (rcode == CONV_OVERFLOW) { + dprintf(1, + (ddt, + "Load T_UNSPEC: input buffer overflow\n" + ) + ); + errs++; + syslog(LOG_ERR, + "Load T_UNSPEC: input buffer overflow"); + } else { + dprintf(1, + (ddt, + "Load T_UNSPEC: Data in bad atob format\n" + ) + ); + errs++; + syslog(LOG_ERR, + "Load T_UNSPEC: Data in bad atob format"); + } + } + } + break; +#endif /* ALLOW_T_UNSPEC */ + + default: + goto err; + } +#ifdef STUBS + if (type == T_SOA && zp->z_type == Z_STUB) + continue; +#endif +#ifdef NO_GLUE + /* + * Ignore data outside the zone. + */ + if (zp->z_type != Z_CACHE && + !samedomain(domain, zp->z_origin)) + { + syslog(LOG_WARNING, + "%s:%d: data \"%s\" outside zone \"%s\" (ignored)", + filename, lineno, domain, zp->z_origin); + continue; + } +#endif /*NO_GLUE*/ + + dp = savedata(class, type, (u_int32_t)ttl, + (u_char *)data, (int)n); + dp->d_zone = zp - zones; + dp->d_flags = dataflags; + dp->d_cred = DB_C_ZONE; + dp->d_clev = clev; + if ((c = db_update(domain, dp, dp, dbflags, + (zp->z_type == Z_CACHE) + ? fcachetab + : hashtab)) + != OK) { +#ifdef DEBUG + if (debug && (c != DATAEXISTS)) + fprintf(ddt, "update failed %s %d\n", + domain, type); +#endif + free((char*) dp); + } + continue; + + case ERROR: + break; + } + err: + errs++; + syslog(LOG_ERR, "%s: line %d: database format error (%s)", + filename, empty_token ? (lineno - 1) : lineno, buf); + dprintf(1, (ddt, + "%s: line %d: database format error ('%s', %d)\n", + filename, empty_token ? (lineno - 1) : lineno, + buf, n)); + while ((c = getc(fp)) != EOF && c != '\n') + ; + if (c == '\n') + lineno++; + } + (void) my_fclose(fp); + lineno = slineno; + if (doinginclude == 0) { + if (didinclude) { + zp->z_flags |= Z_INCLUDE; + zp->z_ftime = 0; + } else + zp->z_ftime = sb.st_mtime; + zp->z_lastupdate = sb.st_mtime; + if (zp->z_type != Z_CACHE && read_soa != 1) { + errs++; + if (read_soa == 0) + syslog(LOG_ERR, "%s: no SOA record", filename); + else + syslog(LOG_ERR, "%s: multiple SOA records", + filename); + } + } +#ifdef SECURE_ZONES + build_secure_netlist(zp); +#endif + if (errs) + zp->z_flags |= Z_DB_BAD; + return (errs); +} + +static int +gettoken(fp, src) + register FILE *fp; + char *src; +{ + register int c; + char op[32]; + + for (;;) { + c = getc(fp); + top: + switch (c) { + case EOF: + return (EOF); + + case '$': + if (getword(op, sizeof(op), fp)) { + if (!strcasecmp("include", op)) + return (INCLUDE); + if (!strcasecmp("origin", op)) + return (ORIGIN); + } + dprintf(1, (ddt, + "%s: line %d: Unknown $ option: $%s\n", + src, lineno, op)); + syslog(LOG_ERR,"%s: line %d: Unknown $ option: $%s\n", + src, lineno, op); + return (ERROR); + + case ';': + while ((c = getc(fp)) != EOF && c != '\n') + ; + goto top; + + case ' ': + case '\t': + return (CURRENT); + + case '.': + return (DOT); + + case '@': + return (AT); + + case '\n': + lineno++; + continue; + + default: + (void) ungetc(c, fp); + return (DNAME); + } + } +} + +/* int + * getword(buf, size, fp) + * get next word, skipping blanks & comments. + * parameters: + * buf - destination + * size - of destination + * fp - file to read from + * return value: + * 0 = no word; perhaps EOL or EOF + * 1 = word was read + */ +int +getword(buf, size, fp) + char *buf; + int size; + FILE *fp; +{ + register char *cp; + register int c; + + empty_token = 0; + for (cp = buf; (c = getc(fp)) != EOF; ) { + if (c == ';') { + while ((c = getc(fp)) != EOF && c != '\n') + ; + c = '\n'; + } + if (c == '\n') { + if (cp != buf) + ungetc(c, fp); + else + lineno++; + break; + } + if (isspace(c)) { + while (isspace(c = getc(fp)) && c != '\n') + ; + ungetc(c, fp); + if (cp != buf) /* Trailing whitespace */ + break; + continue; /* Leading whitespace */ + } + if (c == '"') { + while ((c = getc(fp)) != EOF && c != '"' && c != '\n') { + if (c == '\\') { + if ((c = getc(fp)) == EOF) + c = '\\'; + if (c == '\n') + lineno++; + } + if (cp >= buf+size-1) + break; + *cp++ = c; + } + if (c == '\n') { + lineno++; + break; + } + if ((c = getc(fp)) != EOF) + ungetc(c, fp); + if (c == EOF || isspace(c) || c == '\n') { + *cp = '\0'; + return (1); + } + else + continue; + } + if (c == '\\') { + if ((c = getc(fp)) == EOF) + c = '\\'; + if (c == '\n') + lineno++; + } + if (cp >= buf+size-1) + break; + *cp++ = (char)c; + } + *cp = '\0'; + if (cp == buf) + empty_token = 1; + return (cp != buf); +} + +/* +From: kagotani@cs.titech.ac.jp +Message-Id: <9007040716.AA26646@saeko.cs.titech.ac.jp> +Subject: named bug report and fix +Date: Wed, 04 Jul 90 16:16:52 JST + +I found a bug in the BIND source code. Named with this bug parses +the serial_no field of SOA records incorrectly. For example: + expression internal + in files expression I expect + 1. 1000 10000 + 1.2 10002 10002 + 1.23 100023 10023 + 2.3 20003 20003 +Especially I can not accept that "2.3" is treated as if it is +smaller than "1.23" in their internal expressions. + +[ if you define SENSIBLE_DOTS in ../conf/options.h, you get + m. kagotani's expected behaviour. this is NOT compatible + with pre-4.9 versions of BIND. --vix ] +*/ + +int +getnum(fp, src, is_serial) + FILE *fp; + char *src; + int is_serial; +{ + register int c, n; + int seendigit = 0; + int seendecimal = 0; + int m = 0; + int allow_dots = 0; + +#ifdef DOTTED_SERIAL + allow_dots += is_serial; +#endif + for (n = 0; (c = getc(fp)) != EOF; ) { + if (isspace(c)) { + if (c == '\n') + lineno++; + if (seendigit) + break; + continue; + } + if (c == ';') { + while ((c = getc(fp)) != EOF && c != '\n') + ; + if (c == '\n') + lineno++; + if (seendigit) + break; + continue; + } + if (!isdigit(c)) { + if (c == ')' && seendigit) { + (void) ungetc(c, fp); + break; + } + if (seendecimal || c != '.' || !allow_dots) { + syslog(LOG_ERR, "%s:%d: expected a number", + src, lineno); + dprintf(1, (ddt, "%s:%d: expected a number", + src, lineno)); + exit(1); /* XXX why exit here?? */ + } else { + if (!seendigit) + n = 1; +#ifdef SENSIBLE_DOTS + n = n * 10000; +#else + n = n * 1000; +#endif + seendigit = 1; + seendecimal = 1; + } + continue; + } +#ifdef SENSIBLE_DOTS + if (seendecimal) + m = m * 10 + (c - '0'); + else + n = n * 10 + (c - '0'); +#else + n = n * 10 + (c - '0'); +#endif + seendigit = 1; + } + if (m > 9999) { + syslog(LOG_ERR, + "%s:%d: number after the decimal point exceeds 9999", + src, lineno); + dprintf(1, (ddt, + "%s:%d: number after the decimal point exceeds 9999", + src, lineno)); + exit(1); /* XXX why exit here?? */ + } + if (seendecimal) { + syslog(LOG_INFO, + "%s:%d: decimal serial number interpreted as %d", + src, lineno, n+m); + } + return (n + m); +} + +static int +getnonblank(fp, src) + FILE *fp; + char *src; +{ + register int c; + + while ( (c = getc(fp)) != EOF ) { + if (isspace(c)) { + if (c == '\n') + lineno++; + continue; + } + if (c == ';') { + while ((c = getc(fp)) != EOF && c != '\n') + ; + if (c == '\n') + lineno++; + continue; + } + return(c); + } + syslog(LOG_ERR, "%s: line %d: unexpected EOF", src, lineno); + dprintf(1, (ddt, "%s: line %d: unexpected EOF", src, lineno)); + return (EOF); +} + +/* + * Take name and fix it according to following rules: + * "." means root. + * "@" means current origin. + * "name." means no changes. + * "name" means append origin. + */ +static void +makename(name, origin) + char *name, *origin; +{ + int n; + + if (origin[0] == '.') + origin++; + n = strlen(name); + if (n == 1) { + if (name[0] == '.') { + name[0] = '\0'; + return; + } + if (name[0] == '@') { + (void) strcpy(name, origin); + return; + } + } + if (n > 0) { + if (name[n - 1] == '.') + name[n - 1] = '\0'; + else if (origin[0] != '\0') { + name[n] = '.'; + (void) strcpy(name + n + 1, origin); + } + } +} + +void +endline(fp) + register FILE *fp; +{ + register int c; + + while (c = getc(fp)) { + if (c == '\n') { + (void) ungetc(c,fp); + break; + } else if (c == EOF) { + break; + } + } +} + +#define MAXPORT 256 +#define MAXLEN 24 + +static int +getprotocol(fp, src) + FILE *fp; + char *src; +{ + int k; + char b[MAXLEN]; + + (void) getword(b, sizeof(b), fp); + + k = protocolnumber(b); + if(k == -1) + syslog(LOG_ERR, "%s: line %d: unknown protocol: %s.", + src, lineno, b); + return(k); +} + +static int +getservices(n, data, fp, src) + int n; + char *data, *src; + FILE *fp; +{ + int j, ch; + int k; + int maxl; + int bracket; + char b[MAXLEN]; + char bm[MAXPORT/8]; + + for (j = 0; j < MAXPORT/8; j++) + bm[j] = 0; + maxl = 0; + bracket = 0; + while (getword(b, sizeof(b), fp) || bracket) { + if (feof(fp) || ferror(fp)) + break; + if (strlen(b) == 0) + continue; + if ( b[0] == '(') { + bracket++; + continue; + } + if ( b[0] == ')') { + bracket = 0; + while ((ch = getc(fp)) != EOF && ch != '\n') + ; + if (ch == '\n') + lineno++; + break; + } + k = servicenumber(b); + if (k == -1) { + syslog(LOG_WARNING, + "%s: line %d: Unknown service '%s'", + src, lineno, b); + continue; + } + if ((k < MAXPORT) && (k)) { + bm[k/8] |= (0x80>>(k%8)); + if (k > maxl) + maxl=k; + } + else { + syslog(LOG_WARNING, + "%s: line %d: port no. (%d) too big\n", + src, lineno, k); + dprintf(1, (ddt, + "%s: line %d: port no. (%d) too big\n", + src, lineno, k)); + } + } + if (bracket) + syslog(LOG_WARNING, "%s: line %d: missing close paren\n", + src, lineno); + maxl = maxl/8+1; + bcopy(bm, data+n, maxl); + return(maxl+n); +} + +/* get_netlist(fp, netlistp, allow) + * get list of nets from 'fp', put on *netlistp, 'allow' controls + * whether hosts, nets, or both shall be accepted without warnings. + * (note that they are always accepted; 'allow' just controls the + * warnings.) + */ +void +get_netlist(fp, netlistp, allow, print_tag) + FILE *fp; + struct netinfo **netlistp; + int allow; + char *print_tag; +{ + struct netinfo *ntp = NULL, **end = netlistp; + char buf[BUFSIZ], *maskp; + struct in_addr ina; + + dprintf(1, (ddt, "get_netlist(%s)", print_tag)); + while (getword(buf, sizeof(buf), fp)) { + if (strlen(buf) == 0) + break; + if ((maskp = strchr(buf, '&')) != NULL) + *maskp++ = '\0'; + dprintf(1, (ddt," %s", buf)); + if (ntp == NULL) { + ntp = (struct netinfo *)malloc(sizeof(struct netinfo)); + } + if (!inet_aton(buf, &ntp->my_addr)) { + syslog(LOG_ERR, "%s contains bogus element (%s)", + print_tag, buf); + continue; + } + if (maskp) { + if (!inet_aton(maskp, &ina)) { + syslog(LOG_ERR, + "%s element %s has bad mask (%s)", + print_tag, buf, maskp); + continue; + } + } else { + if (allow & ALLOW_HOSTS) + ina.s_addr = 0xffffffff; /* "exact" */ + else + ina.s_addr = net_mask(ntp->my_addr); + } + ntp->next = NULL; + ntp->mask = ina.s_addr; + ntp->addr = ntp->my_addr.s_addr & ntp->mask; + + /* Check for duplicates */ + if (addr_on_netlist(ntp->my_addr, *netlistp)) + continue; + + if (ntp->addr != ntp->my_addr.s_addr) { + ina.s_addr = ntp->addr; + syslog(LOG_WARNING, + "%s element (%s) mask problem (%s)", + print_tag, buf, inet_ntoa(ina)); + } + + *end = ntp; + end = &ntp->next; + ntp = NULL; + } + if (ntp) + free((char *)ntp); + + dprintf(1, (ddt, "\n")); +#ifdef DEBUG + if (debug > 2) + for (ntp = *netlistp; ntp != NULL; ntp = ntp->next) { + fprintf(ddt, "ntp x%x addr x%x mask x%x", + ntp, ntp->addr, ntp->mask); + fprintf(ddt, " my_addr x%x", ntp->my_addr); + fprintf(ddt, " %s", inet_ntoa(ntp->my_addr)); + fprintf(ddt, " next x%x\n", ntp->next); + } +#endif +} + +struct netinfo * +addr_on_netlist(addr, netlist) + struct in_addr addr; + struct netinfo *netlist; +{ + u_int32_t a = addr.s_addr; + struct netinfo *t; + + for (t = netlist; t != NULL; t = t->next) + if (t->addr == (a & t->mask)) + return t; + return NULL; +} + +int +position_on_netlist(addr, netlist) + struct in_addr addr; + struct netinfo *netlist; +{ + u_int32_t a = addr.s_addr; + struct netinfo *t; + int position = 0; + + for (t = netlist; t != NULL; t = t->next) + if (t->addr == (a & t->mask)) + break; + else + position++; + return position; +} + +void +free_netlist(netlistp) + struct netinfo **netlistp; +{ + register struct netinfo *ntp, *next; + + for (ntp = *netlistp; ntp != NULL; ntp = next) { + next = ntp->next; + free((char *)ntp); + } + *netlistp = NULL; +} diff --git a/usr.sbin/named/db_lookup.c b/usr.sbin/named/db_lookup.c new file mode 100644 index 000000000000..9084491c9137 --- /dev/null +++ b/usr.sbin/named/db_lookup.c @@ -0,0 +1,196 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)db_lookup.c 4.18 (Berkeley) 3/21/91"; +static char rcsid[] = "$Id: db_lookup.c,v 4.9.1.5 1994/06/01 21:09:39 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986 + * - + * Copyright (c) 1986 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/* + * Table lookup routines. + */ + +#include <syslog.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <stdio.h> +#include <ctype.h> + +#include "named.h" + +/* + * Lookup 'name' and return a pointer to the namebuf; + * NULL otherwise. If 'insert', insert name into tables. + * Wildcard lookups are handled. + */ +struct namebuf * +nlookup(name, htpp, fname, insert) + char *name; + struct hashbuf **htpp; + char **fname; + int insert; +{ + register struct namebuf *np; + register char *cp; + register int c; + register unsigned hval; + register struct hashbuf *htp; + struct namebuf *parent = NULL; + + htp = *htpp; + hval = 0; + *fname = "???"; + for (cp = name; c = *cp++; ) { + if (c == '.') { + parent = np = nlookup(cp, htpp, fname, insert); + if (np == NULL) + return (NULL); + if (*fname != cp) + return (np); + if ((htp = np->n_hash) == NULL) { + if (!insert) { + if (np->n_dname[0] == '*' && + np->n_dname[1] == '\0') + *fname = name; + return (np); + } + htp = savehash((struct hashbuf *)NULL); + np->n_hash = htp; + } + *htpp = htp; + break; + } + hval <<= HASHSHIFT; + hval += (isupper(c) ? tolower(c) : c) & HASHMASK; + } + c = *--cp; + *cp = '\0'; + /* + * Lookup this label in current hash table. + */ + for (np = htp->h_tab[hval % htp->h_size]; + np != NULL; + np = np->n_next) { + if (np->n_hashval == hval && + strcasecmp(name, np->n_dname) == 0) { + *fname = name; + *cp = c; + return (np); + } + } + if (!insert) { + /* + * Look for wildcard in this hash table. + * Don't use a cached "*" name as a wildcard, + * only authoritative. + */ + hval = ('*' & HASHMASK) % htp->h_size; + for (np = htp->h_tab[hval]; np != NULL; np = np->n_next) { + if (np->n_dname[0] == '*' && np->n_dname[1] == '\0' && + np->n_data && np->n_data->d_zone != 0) { + *fname = name; + *cp = c; + return (np); + } + } + *cp = c; + return (parent); + } + np = savename(name); + np->n_parent = parent; + np->n_hashval = hval; + hval %= htp->h_size; + np->n_next = htp->h_tab[hval]; + htp->h_tab[hval] = np; + /* Increase hash table size. */ + if (++htp->h_cnt > htp->h_size * 2) { + *htpp = savehash(htp); + if (parent == NULL) { + if (htp == hashtab) { + hashtab = *htpp; + } else { + fcachetab = *htpp; + } + } + else + parent->n_hash = *htpp; + htp = *htpp; + } + *fname = name; + *cp = c; + return (np); +} + +/* int + * match(dp, class, type) + * Does data record `dp' match the class and type? + * return value: + * boolean + */ +int +match(dp, class, type) + register struct databuf *dp; + register int class, type; +{ + dprintf(5, (ddt, "match(0x%x, %d, %d) %d, %d\n", + dp, class, type, dp->d_class, dp->d_type)); + if (dp->d_class != class && class != C_ANY) + return (0); + if (dp->d_type != type && type != T_ANY) + return (0); + return (1); +} diff --git a/usr.sbin/named/db_reload.c b/usr.sbin/named/db_reload.c new file mode 100644 index 000000000000..1b962a63fdc2 --- /dev/null +++ b/usr.sbin/named/db_reload.c @@ -0,0 +1,125 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)db_reload.c 4.22 (Berkeley) 3/21/91"; +static char rcsid[] = "$Id: db_reload.c,v 4.9.1.4 1994/07/02 16:28:11 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1988 + * - + * Copyright (c) 1986, 1988 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <stdio.h> +#include <syslog.h> + +#include "named.h" + +/* + * Flush and reload data base. + */ +void +db_reload() +{ + dprintf(3, (ddt, "reload()\n")); + syslog(LOG_NOTICE, "reloading nameserver\n"); + + qflush(); + sqflush(NULL); + getnetconf(); +#ifdef FORCED_RELOAD + reloading = 1; /* to force transfer if secondary and backing up */ +#endif + ns_init(bootfile); + time(&resettime); +#ifdef FORCED_RELOAD + reloading = 0; + if (!needmaint) + sched_maint(); +#endif /* FORCED_RELOAD */ + + dprintf(1, (ddt, "Ready to answer queries.\n")); + syslog(LOG_NOTICE, "Ready to answer queries.\n"); +} + +#if 0 +/* someday we'll need this.. (untested since before 1990) */ +void +db_free(htp) + struct hashbuf *htp; +{ + register struct databuf *dp, *nextdp; + register struct namebuf *np, *nextnp; + struct namebuf **npp, **nppend; + + npp = htp->h_tab; + nppend = npp + htp->h_size; + while (npp < nppend) { + for (np = *npp++; np != NULL; np = nextnp) { + if (np->n_hash != NULL) + db_free(np->n_hash); + (void) free((char *)np->n_dname); + for (dp = np->n_data; dp != NULL; ) { + nextdp = dp->d_next; + (void) free((char *)dp); + dp = nextdp; + } + nextnp = np->n_next; + free((char *)np); + } + } + (void) free((char *)htp); +} +#endif diff --git a/usr.sbin/named/db_save.c b/usr.sbin/named/db_save.c new file mode 100644 index 000000000000..cd1639d6ec76 --- /dev/null +++ b/usr.sbin/named/db_save.c @@ -0,0 +1,214 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)db_save.c 4.16 (Berkeley) 3/21/91"; +static char rcsid[] = "$Id: db_save.c,v 4.9.1.7 1994/07/22 08:42:39 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986 + * - + * Copyright (c) 1986 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/* + * Buffer allocation and deallocation routines. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <syslog.h> +#include <stdio.h> + +#include "named.h" + +/* + * Allocate a name buffer & save name. + */ +struct namebuf * +savename(name) + char *name; +{ + register struct namebuf *np; + + np = (struct namebuf *) malloc(sizeof(struct namebuf)); + if (np == NULL) { + syslog(LOG_ERR, "savename: %m"); + exit(1); + } + np->n_dname = savestr(name); + np->n_next = NULL; + np->n_data = NULL; + np->n_hash = NULL; + return (np); +} + +/* + * Allocate a data buffer & save data. + */ +struct databuf * +#ifdef DMALLOC +savedata_tagged(file, line, class, type, ttl, data, size) + char *file; + int line; +#else +savedata(class, type, ttl, data, size) +#endif + int class, type; + u_int32_t ttl; + u_char *data; + int size; +{ + register struct databuf *dp; + + if (type == T_NS) + dp = (struct databuf *) +#ifdef DMALLOC + dmalloc(file, line, +#else + malloc( +#endif + (unsigned)DATASIZE(size)+INT32SZ); + else + dp = (struct databuf *) +#ifdef DMALLOC + dmalloc(file, line, +#else + malloc( +#endif + (unsigned)DATASIZE(size)); + if (dp == NULL) { + syslog(LOG_ERR, "savedata: %m"); + exit(1); + } + dp->d_next = NULL; + dp->d_type = type; + dp->d_class = class; + dp->d_ttl = ttl; + dp->d_size = size; + dp->d_mark = 0; + dp->d_flags = 0; + dp->d_cred = 0; + dp->d_clev = 0; +#ifdef NCACHE + dp->d_rcode = NOERROR; +#endif +#ifdef STATS + dp->d_ns = NULL; +#endif + dp->d_nstime = 0; + bcopy(data, dp->d_data, dp->d_size); + return (dp); +} + +int hashsizes[] = { /* hashtable sizes */ + 2, + 11, + 113, + 337, + 977, + 2053, + 4073, + 8011, + 16001, + 0 +}; + +/* + * Allocate a data buffer & save data. + */ +struct hashbuf * +savehash(oldhtp) + register struct hashbuf *oldhtp; +{ + register struct hashbuf *htp; + register struct namebuf *np, *nnp, **hp; + register int n; + int newsize; + + if (oldhtp == NULL) + newsize = hashsizes[0]; + else { + for (n = 0; newsize = hashsizes[n++]; ) + if (oldhtp->h_size == newsize) { + newsize = hashsizes[n]; + break; + } + if (newsize == 0) + newsize = oldhtp->h_size * 2 + 1; + } + dprintf(4, (ddt, "savehash GROWING to %d\n", newsize)); + htp = (struct hashbuf *) malloc((unsigned)HASHSIZE(newsize)); + if (htp == NULL) { + syslog(LOG_ERR, "savehash: %m"); + exit(1); + } + htp->h_size = newsize; + bzero((char *) htp->h_tab, newsize * sizeof(struct namebuf *)); + if (oldhtp == NULL) { + htp->h_cnt = 0; + return (htp); + } + dprintf(4, (ddt, "savehash(%#x) cnt=%d, sz=%d, newsz=%d\n", + oldhtp, oldhtp->h_cnt, oldhtp->h_size, newsize)); + htp->h_cnt = oldhtp->h_cnt; + for (n = 0; n < oldhtp->h_size; n++) { + for (np = oldhtp->h_tab[n]; np != NULL; np = nnp) { + nnp = np->n_next; + hp = &htp->h_tab[np->n_hashval % htp->h_size]; + np->n_next = *hp; + *hp = np; + } + } + free((char *) oldhtp); + return (htp); +} diff --git a/usr.sbin/named/db_secure.c b/usr.sbin/named/db_secure.c new file mode 100644 index 000000000000..b3eb73148560 --- /dev/null +++ b/usr.sbin/named/db_secure.c @@ -0,0 +1,170 @@ +#ifndef LINT +static char rcsid[] = "$Id: db_secure.c,v 1.6 1994/07/23 23:23:56 vixie Exp $"; +#endif + +/* this file was contributed by Gregory Neil Shapiro of WPI in August 1993 */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <syslog.h> + +#include "named.h" + +#ifdef SECURE_ZONES + +#ifndef SECURE_ZONE_RR +#define SECURE_ZONE_RR "secure_zone" +#endif +#ifndef MASK_SEP +#define MASK_SEP ':' +#endif + +int +build_secure_netlist(zp) + struct zoneinfo *zp; +{ + struct netinfo *ntp = NULL, **netlistp, **end; + char buf[BUFSIZ]; + struct hashbuf *htp; + struct namebuf *snp; + struct databuf *dp; + char *fname, *dname, dnbuf[MAXDNAME]; + int errs = 0, securezone = 0; + + if (zp->secure_nets) { + free_netlist(&zp->secure_nets); + } + netlistp = &zp->secure_nets; + end = netlistp; + strcat(strcat(strcpy(dnbuf, SECURE_ZONE_RR), "."), zp->z_origin); + + dname = dnbuf; + htp = hashtab; + if ((snp = nlookup(dname, &htp, &fname, 0)) == NULL) { + dprintf(1, (ddt, + "build_secure_netlist(%s): FAIL on nlookup %s\n", + zp->z_origin, dname)); + zp->secure_nets=NULL; + return(0); + } + /* A parent's RR's aren't valid */ + if (strcasecmp(snp->n_dname, SECURE_ZONE_RR)) { + zp->secure_nets=NULL; + return(0); + } + /* Collect secure nets into secure_nets */ + for (dp = snp->n_data; dp != NULL; dp = dp->d_next) { + char *maskptr = NULL; + if (!match(dp, C_ANY, T_TXT)) { + continue; + } + bzero(buf, sizeof(buf)); + bcopy(dp->d_data+1, buf, dp->d_size-1); + maskptr=strchr(buf, MASK_SEP); + if (maskptr) { + *maskptr++ = 0; + } + dprintf(3, (ddt, + "build_secure_netlist(%s): Found secure zone %s\n", + zp->z_origin, buf)); + if (ntp == NULL) { + ntp = (struct netinfo *)malloc(sizeof(struct netinfo)); + if (!ntp) { + dprintf(1, (ddt, + "build_secure_netlist (%s): malloc fail\n", + zp->z_origin)); + syslog(LOG_ERR, + "build_secure_netlist (%s): Out of Memory", + zp->z_origin); + if (!securezone) { + zp->secure_nets=NULL; + } + return(1); + } + } + if (!inet_aton(buf, &ntp->my_addr)) { + dprintf(1, (ddt, + "build_secure_netlist (%s): Bad address: %s\n", + zp->z_origin, buf)); + syslog(LOG_ERR, + "build_secure_netlist (%s): Bad address: %s", + zp->z_origin, buf); + errs++; + continue; + } + if (maskptr && *maskptr) { + if (*maskptr == 'h' || *maskptr == 'H') { + ntp->mask = (u_int32_t)-1; + } else { + if (!inet_aton(maskptr, + (struct in_addr *)&ntp->mask)) { + dprintf(1, (ddt, + "build_secure_netlist (%s): Bad mask: %s\n", + zp->z_origin, maskptr)); + syslog(LOG_ERR, + "build_secure_netlist (%s): Bad mask: %s", + zp->z_origin, maskptr); + errs++; + continue; + } + } + } else { + ntp->mask = net_mask(ntp->my_addr); + } + if (ntp->my_addr.s_addr & ~(ntp->mask)) { + dprintf(1, (ddt, + "build_secure_netlist (%s): addr (%s) is not in mask (x%x)\n", + zp->z_origin, + inet_ntoa(ntp->my_addr), + ntp->mask)); + syslog(LOG_WARNING, + "build_secure_netlist (%s): addr (%s) is not in mask (x%x)", + zp->z_origin, + inet_ntoa(ntp->my_addr), + ntp->mask); + errs++; + } + ntp->next = NULL; + ntp->addr = ntp->my_addr.s_addr & ntp->mask; + + /* Check for duplicates */ + if (addr_on_netlist(ntp->my_addr, *netlistp)) { + dprintf(1, (ddt, + "build_secure_netlist (%s): duplicate address %s\n", + zp->z_origin, inet_ntoa(ntp->my_addr))); + syslog(LOG_WARNING, + "build_secure_netlist (%s): duplicate address %s\n", + zp->z_origin, inet_ntoa(ntp->my_addr)); + errs++; + continue; + } + *end = ntp; + end = &ntp->next; + ntp = NULL; + securezone++; + } + if (ntp) { + free((char *)ntp); + } + if (!securezone) { + zp->secure_nets=NULL; + } + +#ifdef DEBUG + if (debug > 1) { + for (ntp = *netlistp; ntp != NULL; ntp = ntp->next) { + fprintf(ddt, "ntp x%x addr x%x mask x%x", + ntp, ntp->addr, ntp->mask); + fprintf(ddt, " my_addr x%x", ntp->my_addr); + fprintf(ddt, " %s", inet_ntoa(ntp->my_addr)); + fprintf(ddt, " next x%x\n", ntp->next); + } + } +#endif + return(errs); +} +#endif /*SECURE_ZONES*/ diff --git a/usr.sbin/named/db_update.c b/usr.sbin/named/db_update.c new file mode 100644 index 000000000000..93244930d561 --- /dev/null +++ b/usr.sbin/named/db_update.c @@ -0,0 +1,638 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)db_update.c 4.28 (Berkeley) 3/21/91"; +static char rcsid[] = "$Id: db_update.c,v 4.9.1.19 1994/07/23 23:23:56 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1990 + * - + * Copyright (c) 1986, 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. + * - + * --Copyright-- + */ + +#include <stdio.h> +#include <syslog.h> + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include "named.h" + +static void fixttl __P((struct databuf *)); +static int db_cmp __P((struct databuf *, + struct databuf *)); + +/* int + * isRefByNS(name, htp) + * recurse through all of `htp' looking for NS RR's that refer to `name'. + * returns: + * nonzero if at least one such NS RR exists + * cautions: + * this is very expensive; probably you only want to use on fcachetab. + */ +static int +isRefByNS(name, htp) + char name[]; + struct hashbuf *htp; +{ + register struct namebuf *np; + register struct databuf *dp; + + for (np = htp->h_tab[0]; np != NULL; np = np->n_next) { + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if ((dp->d_class == C_ANY || dp->d_class == C_IN) && + (dp->d_type == T_NS) && +#ifdef NCACHE + (!dp->d_rcode) && +#endif + !strcasecmp(name, (char *)dp->d_data)) { + return (1); + } + } + if (np->n_hash && isRefByNS(name, np->n_hash)) { + return (1); + } + } + return (0); +} + + +/* int + * findMyZone(struct namebuf *np) + * surf the zone cuts and find this zone the hard way + * return value: + * zone number or DB_Z_CACHE if it's outside a zone + * interesting cases: + * DEC.COM SOA (primary) + * CRL.DEC.COM NS (in primary) + * if you start at CRL.. here, you find the DEC.COM zone + * if you start at NS.CRL.. here, you're in the cache + * DEC.COM SOA (primary) + * CRL.DEC.COM NS (in primary) + * CRL.DEC.COM SOA (secondary) + * CRL.DEC.COM NS (in secondary) + * if you start at CRL.. here, you find the CRL.DEC.COM zone + * if you start at NS.CRL.. here, you're in the CRL.. zone + */ +int +findMyZone(np, class) + struct namebuf *np; + register int class; +{ + for (; np; np = np->n_parent) { + register struct databuf *dp; + + /* if we encounter an SOA, we're in its zone (which can be + * the cache or an authoritative zone, depending). + */ + for (dp = np->n_data; dp; dp = dp->d_next) + if (match(dp, class, T_SOA)) + return (dp->d_zone); + + /* if we find an NS at some node without having seen an SOA + * (above), then we're out in the cache somewhere. + */ + for (dp = np->n_data; dp; dp = dp->d_next) + if (match(dp, class, T_NS)) + return (DB_Z_CACHE); + } + + /* getting all the way to the root without finding an NS or SOA + * probably means that we are in deep dip, but we'll treat it as + * being in the cache. (XXX?) + */ + return (DB_Z_CACHE); +} + + +/* int + * db_update(name, odp, newdp, flags, htp) + * update data base node at `name'. `flags' controls the action. + * side effects: + * inverse query tables modified, if we're using them. + * return value: + * OK - success + * NONAME - name doesn't exist + * AUTH - you can't do that + * DATAEXISTS - there's something there and DB_NODATA was specified + * NODATA - there's no data, and (DB_DELETE or DB_MEXIST) was spec'd + * + * Policy: How to add data if one more RR is -ve data + * + * NEND NOERROR_NODATA + * NXD NXDOMAIN + * + * match + * old + * Data NEND NXD + * Data Merge Data Data + * new NEND NEND NEND NEND + * NXD NXD NXD NXD + * + * no match + * old + * Data NEND NXD + * Data Merge Merge Data + * new NEND Merge Merge NEND + * NXD NXD NXD NXD + * + */ +/* XXX: this code calls nlookup, which can create namebuf's. if this code + * has to exit with a fatal error, it should scan from the new np upward + * and for each node which has no children and no data it should remove + * the namebuf. design notes: (1) there's no harm in doing this even if + * success occurred; (2) stopping on the first nonremovable np is optimal; + * the code for removal should be taken out of remove_zone() and made + * general enough for this use, and for remove_zone()'s continued use. + * vix, 21jul94 + */ +int +db_update(name, odp, newdp, flags, htp) + char name[]; + struct databuf *odp, *newdp; + int flags; + struct hashbuf *htp; +{ + register struct databuf *dp, *pdp; + register struct namebuf *np; + int zn, isHintNS; + char *fname; + + dprintf(3, (ddt, "db_update(%s, 0x%x, 0x%x, 0%o, 0x%x)%s\n", + name, odp, newdp, flags, htp, + (odp && (odp->d_flags&DB_F_HINT)) ? " hint":"" )); + np = nlookup(name, &htp, &fname, newdp != NULL); + if (np == NULL || fname != name) + return (NONAME); + + /* don't let nonauthoritative updates write in authority zones */ + if (newdp && (flags & DB_NOTAUTH) && + (zn = findMyZone(np, newdp->d_class)) != DB_Z_CACHE) { + int foundRR = 0; + + /* don't generate the warning if we've done so recently or + * if the update would have been harmless (identical data). + */ + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!db_cmp(dp, newdp)) { + foundRR++; + break; + } + } + if (!foundRR && + !haveComplained((char*)from_addr.sin_addr.s_addr, + (char*)dhash((u_char*)name, strlen(name)))) + syslog(LOG_NOTICE, + "[%s].%d attempted update to auth zone \"%s\" (%s)", + inet_ntoa(from_addr.sin_addr), + ntohs(from_addr.sin_port), + zones[zn].z_origin, + name); + return (AUTH); + } + + /* some special checks for root NS' A RR's */ + isHintNS = isRefByNS(name, fcachetab); + if (newdp && isHintNS && newdp->d_type == T_A) { + /* obviously bogus addresses die here */ + if ( +#ifdef NCACHE + (!newdp->d_rcode) && +#endif + (((struct in_addr *)newdp->d_data)->s_addr == INADDR_ANY)) + { + syslog(LOG_INFO, "bogus (0.0.0.0) root A RR received"); + return (AUTH); + } + /* upgrade credibility of additional data for rootsrv addrs */ + if (newdp->d_cred == DB_C_ADDITIONAL) { + dprintf(3, (ddt, + "upgrading credibility for A RR (%s)\n", + name)); + /* XXX: should copy NS RR's, but we really just want + * to prevent deprecation later so this will do. + */ + newdp->d_cred = DB_C_ANSWER; + newdp->d_clev = 0; + } + } + + /* Reflect certain updates in hint cache also... */ + /* Don't stick data we are authoritative for in hints. */ + if (!(flags & DB_NOHINTS) && + (odp != NULL) && + (htp != fcachetab) && + (odp->d_zone <= 0) && + !(odp->d_flags & DB_F_HINT) && +#ifdef NCACHE + (!newdp || !newdp->d_rcode) && +#endif + ((name[0] == '\0' && odp->d_type == T_NS) || + (odp->d_type == T_A && isHintNS) + ) + ) + { + dprintf(3, (ddt, "db_update: hint '%s' %d\n", + name, odp->d_ttl)); + dp = savedata(odp->d_class, odp->d_type, odp->d_ttl, + odp->d_data, odp->d_size); + dp->d_zone = DB_Z_CACHE; + dp->d_flags = DB_F_HINT; + dp->d_cred = DB_C_CACHE; + dp->d_clev = 0; + if (db_update(name, + dp, dp, + (flags|DB_NOHINTS), + fcachetab) + != OK) { + dprintf(3, (ddt, "db_update: hint %x freed\n", dp)); + (void) free((char *)dp); + } + } + + if (odp != NULL) { + int foundRR = 0; + + pdp = NULL; + for (dp = np->n_data; dp != NULL; ) { + if (!match(dp, odp->d_class, odp->d_type)) { + /* {class,type} doesn't match. these are + * the aggregation cases. + */ + if ((dp->d_type == T_CNAME || + odp->d_type == T_CNAME) && + odp->d_class == dp->d_class && + odp->d_mark == dp->d_mark && +#ifdef NCACHE + /* neither the odp nor the new dp are + * negatively cached records... + */ + !dp->d_rcode && + !odp->d_rcode && +#endif /*NCACHE*/ + zones[odp->d_zone].z_type != Z_CACHE) { + syslog(LOG_ERR, + "%s has CNAME and other data (illegal)\n", + name); + dprintf(1, (ddt, + "db_update: %s: CNAME and more (%d, %d)\n", + name, odp->d_type, + dp->d_type)); + goto skip; + } + if (!newdp || newdp->d_class != dp->d_class) + goto skip; + + /* XXX: + * The next three clauses do not deal + * correctly with glue records. mpa. + */ + + /* if the new data is authorative + * remove any data for this domain with + * the same class that isn't as credable + */ + if (newdp->d_cred == DB_C_ZONE && + newdp->d_cred > dp->d_cred) + /* better credibility and the old datum + * was not from a zone file. remove + * the old datum. + */ + goto delete; + + /* if we have authoritative data for a + * node, don't add in other data. + */ + if (dp->d_cred == DB_C_ZONE && + newdp->d_cred < dp->d_cred) + return (AUTH); + + /* if the new data is authoritative but + * but isn't as credible, reject it. + */ + if (newdp->d_cred == DB_C_ZONE && + newdp->d_cred == dp->d_cred && + newdp->d_clev < dp->d_clev) + return (AUTH); +#ifdef NCACHE + /* process NXDOMAIN */ + /* policy */ + if (newdp->d_rcode == NXDOMAIN) { + if (dp->d_cred < DB_C_AUTH) + goto delete; + else + return (DATAEXISTS); + } + + if (dp->d_rcode == NXDOMAIN) + goto delete; + + /* process NOERROR_NODATA */ + /* NO PROCESSING REQUIRED */ +#endif /*NCACHE*/ + goto skip; + } /*if {class,type} did not match*/ + + /* {type,class} did match. this is the replace case. + */ + dprintf(5, (ddt, + "db_update: flags = %#x, sizes = %d, %d (cmp %d)\n", + flags, odp->d_size, dp->d_size, + db_cmp(dp, odp))); + if (newdp) { + dprintf(4, (ddt, + "credibility for %s is %d(%d) from [%s].%d, is %d(%d) in cache\n", + *name? name : ".", + newdp->d_cred, + newdp->d_clev, + inet_ntoa(from_addr.sin_addr), + ntohs(from_addr.sin_port), + dp->d_cred, + dp->d_clev)); + if (newdp->d_cred > dp->d_cred || + (newdp->d_cred == DB_C_ZONE && + newdp->d_clev > dp->d_clev)) { + /* better credibility and the old datum + * was not from a zone file. remove + * the old datum. + */ + goto delete; + } + if (newdp->d_cred < dp->d_cred) { + /* credibility is worse. ignore it. */ + return (AUTH); + } + if (newdp->d_cred == DB_C_ZONE && + newdp->d_cred == dp->d_cred && + newdp->d_clev < dp->d_clev) + return (AUTH); + + /* credibility is the same. + * let it aggregate in the normal way. + */ +#ifdef NCACHE + /* + * if the new or old RR is -ve, delete old. + */ + if (dp->d_rcode || newdp->d_rcode) { + /* XXX: how can a zone rr be neg? */ + if (dp->d_cred != DB_C_ZONE) + goto delete; + else + return (DATAEXISTS); + } +#endif + /* + * Some RR types should not be aggregated. + */ + if (dp->d_type == T_SOA) + goto delete; + if (dp->d_type == T_WKS && + !bcmp(dp->d_data, newdp->d_data, INT16SZ)) + goto delete; + } + if ((flags & DB_NODATA) && !db_cmp(dp, odp)) { + /* refresh ttl if cache entry */ + if (dp->d_zone == 0) { + if (odp->d_zone != 0) { /* XXX */ + /* changing cache->auth */ + dp->d_zone = odp->d_zone; + dp->d_ttl = odp->d_ttl; + dprintf(4, (ddt, + "db_update: cache entry now in auth zone\n" + )); + return (DATAEXISTS); + } + fixttl(odp); + if (odp->d_ttl > dp->d_ttl) + dp->d_ttl = odp->d_ttl; + dprintf(3, (ddt, + "db_update: new ttl %d, +%d\n", + dp->d_ttl, + dp->d_ttl - tt.tv_sec)); + } + return (DATAEXISTS); + } + /* + * If the old databuf has some data, check that the + * data matches that in the new databuf (so UPDATED + * will delete only the matching RR) + */ + if (odp->d_size > 0) + if (db_cmp(dp, odp)) + goto skip; + foundRR = 1; + if (flags & DB_DELETE) { + delete: dp = rm_datum(dp, np, pdp); + } else { + skip: pdp = dp; + dp = dp->d_next; + } + } + if (!foundRR) { + if (flags & DB_DELETE) + return (NODATA); + if (flags & DB_MEXIST) + return (NODATA); + } + } + if (newdp == NULL) + return (OK); + /* XXX: empty nodes bypass credibility checks above; should check + * response source address here if flags&NOTAUTH. + */ + fixttl(newdp); + dprintf(3, (ddt, "db_update: adding%s %x\n", + (newdp->d_flags&DB_F_HINT) ? " hint":"", newdp)); +#ifdef INVQ + if (!(newdp->d_flags & DB_F_HINT)) + addinv(np, newdp); /* modify inverse query tables */ +#endif + +#ifdef STATS + if (!newdp->d_zone && !(newdp->d_flags & DB_F_HINT)) + newdp->d_ns = nameserFind(from_addr.sin_addr, NS_F_INSERT); +#endif + + /* Add to end of list, generally preserving order */ + newdp->d_next = NULL; + if ((dp = np->n_data) == NULL) { +#ifdef DATUMREFCNT + newdp->d_rcnt = 1; +#endif + np->n_data = newdp; + return (OK); + } + while (dp->d_next != NULL) { + if ((flags & DB_NODATA) && !db_cmp(dp, newdp)) + return (DATAEXISTS); + dp = dp->d_next; + } + if ((flags & DB_NODATA) && !db_cmp(dp, newdp)) + return (DATAEXISTS); +#ifdef DATUMREFCNT + newdp->d_rcnt = 1; +#endif + dp->d_next = newdp; + return (OK); +} + +static void +fixttl(dp) + register struct databuf *dp; +{ + if (dp->d_zone == 0 && !(dp->d_flags & DB_F_HINT)) { + if (dp->d_ttl <= tt.tv_sec) + return; + else if (dp->d_ttl < tt.tv_sec+min_cache_ttl) + dp->d_ttl = tt.tv_sec+min_cache_ttl; + else if (dp->d_ttl > tt.tv_sec+max_cache_ttl) + dp->d_ttl = tt.tv_sec+max_cache_ttl; + } + return; +} + +/* + * Compare type, class and data from databufs for equivalence. + * Must be case insensitive for some domain names. + * Return 0 if equivalent, nonzero otherwise. + */ +static int +db_cmp(dp1, dp2) + register struct databuf *dp1, *dp2; +{ + register u_char *cp1, *cp2; + int len; + + if (dp1->d_type != dp2->d_type || dp1->d_class != dp2->d_class) + return (1); + if (dp1->d_size != dp2->d_size) + return (1); + if (dp1->d_mark != dp2->d_mark) + return (1); /* old and new RR's are distinct */ +#ifdef NCACHE + if (dp1->d_rcode && dp2->d_rcode) + return ((dp1->d_rcode == dp1->d_rcode)?0:1); + if (dp1->d_rcode || dp2->d_rcode) + return (1); +#endif + + switch (dp1->d_type) { + + case T_A: + case T_UID: + case T_GID: + case T_WKS: + case T_NULL: + case T_NSAP: +#ifdef ALLOW_T_UNSPEC + case T_UNSPEC: +#endif + return (bcmp(dp1->d_data, dp2->d_data, dp1->d_size)); + + case T_NS: + case T_CNAME: + case T_PTR: + case T_MB: + case T_MG: + case T_MR: + case T_UINFO: + return (strcasecmp((char *)dp1->d_data, (char *)dp2->d_data)); + + case T_HINFO: + case T_ISDN: + cp1 = dp1->d_data; + cp2 = dp2->d_data; + len = *cp1; + if (strncasecmp((char *)++cp1, (char *)++cp2, len)) + return (1); + cp1 += len; + cp2 += len; + len = *cp1; + return (strncasecmp((char *)++cp1, (char *)++cp2, len)); + + case T_SOA: + case T_MINFO: + case T_RP: + if (strcasecmp((char *)dp1->d_data, (char *)dp2->d_data)) + return (1); + cp1 = dp1->d_data + strlen((char *)dp1->d_data) + 1; + cp2 = dp2->d_data + strlen((char *)dp2->d_data) + 1; + if (dp1->d_type != T_SOA) + return (strcasecmp((char *)cp1, (char *)cp2)); + if (strcasecmp((char *)cp1, (char *)cp2)) + return (1); + cp1 += strlen((char *)cp1) + 1; + cp2 += strlen((char *)cp2) + 1; + return (bcmp(cp1, cp2, INT32SZ * 5)); + + case T_MX: + case T_AFSDB: + case T_RT: + cp1 = dp1->d_data; + cp2 = dp2->d_data; + if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* cmp prio */ + return (1); + return (strcasecmp((char *)cp1, (char *)cp2)); + + case T_TXT: + case T_X25: + if (dp1->d_size != dp2->d_size) + return (1); + return (bcmp(dp1->d_data, dp2->d_data, dp1->d_size)); + + default: + return (1); + } +} diff --git a/usr.sbin/named/dmalloc.c b/usr.sbin/named/dmalloc.c new file mode 100644 index 000000000000..4373024a889f --- /dev/null +++ b/usr.sbin/named/dmalloc.c @@ -0,0 +1,312 @@ +/* dmalloc - debugging layer on top of malloc + * vix 25mar92 [fixed bug in round-up calcs in alloc()] + * vix 24mar92 [added size calcs, improved printout] + * vix 22mar92 [original work] + * + * $Id: dmalloc.c,v 4.9.1.3 1994/07/02 16:28:11 vixie Exp $ + */ + +/* + * ++Copyright++ 1993 + * - + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#include <stdio.h> +#include <signal.h> +#include "../conf/portability.h" +#include "../conf/options.h" + +#ifdef DMALLOC + +#define TRUE 1 +#define FALSE 0 +typedef unsigned bool; + +#define MAX_MEMORY 65536 /* must fit in typeof(datum.size) */ +#define MAX_CALLERS 256 /* must be **2 */ + +typedef struct caller { + struct caller *next; + struct filenam *file; + struct calltab *frees; + unsigned line; + unsigned calls; + unsigned blocks; + unsigned bytes; +} caller; + +typedef struct filenam { + struct filenam *next; + char *name; +} filenam; + +typedef struct calltab { + struct caller *callers[MAX_CALLERS]; +} calltab; + +typedef struct datum { + unsigned size; /* size of malloc'd item */ + unsigned caller; /* offset into memory[] */ + /* user data follows */ +} datum; + +static char memory[MAX_MEMORY]; +static char *nextmem = memory; +static char *alloc(size) unsigned size; { + char *thismem = nextmem; + int oddness = (size % sizeof(char*)); + if (oddness) + size += (sizeof(char*) - oddness); + nextmem += size; + if (nextmem >= &memory[MAX_MEMORY]) { + fprintf(stderr, "dmalloc.alloc: out of mem\n"); + kill(0, SIGBUS); + } + return thismem; + } + +static filenam *Files; +static calltab Callers; + +/*--------------------------------------------------- imports + */ + +#undef malloc +#undef calloc +#undef realloc +#undef free + +char *malloc(), *calloc(), *realloc(); + +#if defined(sun) +int free(); +#else +void free(); +#endif + +/*--------------------------------------------------- private + */ + +#define STR_EQ(l,r) (((l)[0] == (r)[0]) && !strcmp(l, r)) + +static filenam * +findFile(file, addflag) + char *file; + bool addflag; +{ + filenam *f; + + for (f = Files; f; f = f->next) + if (STR_EQ(file, f->name)) + return f; + if (!addflag) + return NULL; + f = (filenam*) alloc(sizeof(filenam)); + f->next = Files; + Files = f; + f->name = alloc(strlen(file) + 1); + strcpy(f->name, file); + return f; +} + +static caller * +findCaller(ctab, file, line, addflag) + calltab *ctab; + char *file; + unsigned line; + bool addflag; +{ + unsigned hash = line & (MAX_CALLERS - 1); + caller *c; + + for (c = ctab->callers[hash]; c; c = c->next) + if ((c->line == line) && STR_EQ(c->file->name, file)) + return c; + if (!addflag) + return NULL; + c = (caller*) alloc(sizeof(caller)); + c->next = ctab->callers[hash]; + c->file = findFile(file, TRUE); + c->line = line; + c->calls = 0; + c->frees = (calltab *) alloc(sizeof(calltab)); + ctab->callers[hash] = c; + return c; +} + +/*--------------------------------------------------- public + */ + +char * +dmalloc(file, line, size) + char *file; + unsigned line; + unsigned size; +{ + caller *c; + datum *d; + + c = findCaller(&Callers, file, line, TRUE); + d = (datum *) malloc(sizeof(datum) + size); + d->size = size; + d->caller = ((char *)c) - memory; + c->calls++; + c->blocks++; + c->bytes += size; + return (char *) (d+1); +} + +void +dfree(file, line, ptr) + char *file; + unsigned line; + char *ptr; +{ + caller *c, *a; + datum *d; + + d = (datum *) ptr; d--; + a = (caller *) (memory + d->caller); + a->bytes -= d->size; + a->blocks--; + c = findCaller(a->frees, file, line, TRUE); + c->calls++; + free((char*) d); +} + +char * +dcalloc(file, line, nelems, elsize) + char *file; + unsigned line; + unsigned nelems, elsize; +{ + unsigned size = (nelems * elsize); + char *ptr; + + ptr = dmalloc(file, line, size); + bzero(ptr, size); + return ptr; +} + +char * +drealloc(file, line, ptr, size) + char *file; + unsigned line; + char *ptr; + unsigned size; +{ + caller *c, *a; + datum *d; + + d = (datum *) ptr; d--; + /* fix up stats from allocation */ + a = (caller *) (memory + d->caller); + a->bytes -= d->size; + a->blocks--; + /* we are a "freer" of this allocation */ + c = findCaller(a->frees, file, line, TRUE); + c->calls++; + /* get new allocation and stat it */ + c = findCaller(&Callers, file, line, TRUE); + d = (datum *) realloc((char *) d, sizeof(datum) + size); + d->size = size; + d->caller = ((char *)c) - memory; + c->calls++; + c->blocks++; + c->bytes += size; + return (char *) (d+1); +} + +static void +dmalloccallers(outf, prefix, ctab) + FILE *outf; + char *prefix; + calltab *ctab; +{ + /* this bizarre logic is to print all of a file's entries together */ + filenam *f; + + for (f = Files; f; f = f->next) { + int i; + + for (i = MAX_CALLERS-1; i >= 0; i--) { + caller *c; + + for (c = ctab->callers[i]; c; c = c->next) { + if (f != c->file) + continue; + fprintf(outf, "%s\"%s\":%u calls=%u", + prefix, c->file->name, c->line, + c->calls); + if (c->blocks || c->bytes) + fprintf(outf, " blocks=%u bytes=%u", + c->blocks, c->bytes); + fputc('\n', outf); + if (c->frees) + dmalloccallers(outf, + "\t\t", c->frees); + } + } + } +} + +void +dmallocstats(outf) + FILE *outf; +{ + fprintf(outf, "dallocstats [ private mem used=%u, avail=%u ]\n", + nextmem - memory, &memory[MAX_MEMORY] - nextmem); + dmalloccallers(outf, "\t", &Callers); +} + +#endif /*DMALLOC*/ diff --git a/usr.sbin/named/dmalloc.h b/usr.sbin/named/dmalloc.h new file mode 100644 index 000000000000..54e68f9887a2 --- /dev/null +++ b/usr.sbin/named/dmalloc.h @@ -0,0 +1,68 @@ +/* dmalloc - debugging layer on top of malloc + * vix 22mar92 [written] + * + * $Id: dmalloc.h,v 4.9.1.2 1993/09/08 00:01:17 vixie Exp $ + */ + +/* + * ++Copyright++ + * - + * Copyright (c) + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#define malloc(s) dmalloc(__FILE__, __LINE__, s) +#define free(p) dfree(__FILE__, __LINE__, p) +#define calloc(n, s) dcalloc(__FILE__, __LINE__, n, s) +#define realloc(p, s) drealloc(__FILE__, __LINE__, p, s) + +char *dmalloc(), *dcalloc(), *drealloc(); +void dfree(), dmallocstats(); diff --git a/usr.sbin/named/named.h b/usr.sbin/named/named.h new file mode 100644 index 000000000000..4e597d7a89e0 --- /dev/null +++ b/usr.sbin/named/named.h @@ -0,0 +1,19 @@ +/* named.h - include the local definitions in the right order + * vix 28aug93 [original] + * + * $Id: named.h,v 1.1 1993/09/08 04:57:40 vixie Exp $ + */ + +#include "../conf/portability.h" +#include "../conf/options.h" + +#include "pathnames.h" + +#include "ns_defs.h" +#include "db_defs.h" + +#include "ns_glob.h" +#include "db_glob.h" + +#include "ns_func.h" +#include "db_func.h" diff --git a/usr.sbin/named/ns_defs.h b/usr.sbin/named/ns_defs.h new file mode 100644 index 000000000000..bb9f18fbacb1 --- /dev/null +++ b/usr.sbin/named/ns_defs.h @@ -0,0 +1,368 @@ +/* + * from ns.h 4.33 (Berkeley) 8/23/90 + * $Id: ns_defs.h,v 1.12 1994/07/23 23:23:56 vixie Exp $ + */ + +/* + * ++Copyright++ 1986 + * - + * Copyright (c) 1986 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/* + * Global definitions for the name server. + */ + +/* + * Effort has been expended here to make all structure members 32 bits or + * larger land on 32-bit boundaries; smaller structure members have been + * deliberately shuffled and smaller integer sizes chosen where possible + * to make sure this happens. This is all meant to avoid structure member + * padding which can cost a _lot_ of memory when you have hundreds of + * thousands of entries in your cache. + */ + +/* + * Timeout time should be around 1 minute or so. Using the + * the current simplistic backoff strategy, the sequence + * retrys after 4, 8, and 16 seconds. With 3 servers, this + * dies out in a little more than a minute. + * (sequence RETRYBASE, 2*RETRYBASE, 4*RETRYBASE... for MAXRETRY) + */ +#define MINROOTS 2 /* min number of root hints */ +#define NSMAX 16 /* max number of NS addrs to try ([0..255]) */ +#define RETRYBASE 4 /* base time between retries */ +#define MAXCLASS 255 /* XXX - may belong elsewhere */ +#define MAXRETRY 3 /* max number of retries per addr */ +#define MAXCNAMES 8 /* max # of CNAMES tried per addr */ +#define MAXQUERIES 20 /* max # of queries to be made */ +#define MAXQSERIAL 4 /* max # of outstanding QSERIAL's */ + /* (prevent "recursive" loops) */ +#define INIT_REFRESH 600 /* retry time for initial secondary */ + /* contact (10 minutes) */ +#define NADDRECS 20 /* max addt'l rr's per resp */ + +#define XFER_TIMER 120 /* named-xfer's connect timeout */ +#define MAX_XFER_TIME 60*60*2 /* max seconds for an xfer */ +#define XFER_TIME_FUDGE 10 /* MAX_XFER_TIME fudge */ +#define MAX_XFERS_RUNNING 10 /* default max value of xfers_running */ +#define MAX_XFERS_PERNS 2 /* max # of xfers per peer nameserver */ + +#define ALPHA 0.7 /* How much to preserve of old response time */ +#define BETA 1.2 /* How much to penalize response time on failure */ +#define GAMMA 0.98 /* How much to decay unused response times */ + + /* sequence-space arithmetic */ +#define SEQ_GT(a,b) ((int32_t)((a)-(b)) > 0) + +/* these fields are ordered to maintain word-alignment; + * be careful about changing them. + */ +struct zoneinfo { + char *z_origin; /* root domain name of zone */ + time_t z_time; /* time for next refresh */ + time_t z_lastupdate; /* time of last refresh */ + u_int32_t z_refresh; /* refresh interval */ + u_int32_t z_retry; /* refresh retry interval */ + u_int32_t z_expire; /* expiration time for cached info */ + u_int32_t z_minimum; /* minimum TTL value */ + u_int32_t z_serial; /* changes if zone modified */ + char *z_source; /* source location of data */ + time_t z_ftime; /* modification time of source file */ + struct in_addr z_xaddr; /* override server for next xfer */ + struct in_addr z_addr[NSMAX]; /* list of master servers for zone */ + u_char z_addrcnt; /* number of entries in z_addr[] */ + u_char z_type; /* type of zone; see below */ + u_int16_t z_flags; /* state bits; see below */ + pid_t z_xferpid; /* xfer child pid */ + int z_class; /* class of zone */ +#ifdef SECURE_ZONES + struct netinfo *secure_nets; /* list of secure networks for zone */ +#endif +}; + + /* zone types (z_type) */ +#define Z_NIL 0 /* zone slot not in use */ +#define Z_PRIMARY 1 +#define Z_SECONDARY 2 +#define Z_CACHE 3 +#define Z_STUB 4 + + /* zone state bits (16 bits) */ +#define Z_AUTH 0x0001 /* zone is authoritative */ +#define Z_NEED_XFER 0x0002 /* waiting to do xfer */ +#define Z_XFER_RUNNING 0x0004 /* asynch. xfer is running */ +#define Z_NEED_RELOAD 0x0008 /* waiting to do reload */ +#define Z_SYSLOGGED 0x0010 /* have logged timeout */ +#define Z_QSERIAL 0x0020 /* sysquery()'ing for serial number */ +#define Z_FOUND 0x0040 /* found in boot file when reloading */ +#define Z_INCLUDE 0x0080 /* set if include used in file */ +#define Z_DB_BAD 0x0100 /* errors when loading file */ +#define Z_TMP_FILE 0x0200 /* backup file for xfer is temporary */ +#ifdef ALLOW_UPDATES +#define Z_DYNAMIC 0x0400 /* allow dynamic updates */ +#define Z_DYNADDONLY 0x0800 /* dynamic mode: add new data only */ +#define Z_CHANGED 0x1000 /* zone has changed */ +#endif /* ALLOW_UPDATES */ + + /* named_xfer exit codes */ +#define XFER_UPTODATE 0 /* zone is up-to-date */ +#define XFER_SUCCESS 1 /* performed transfer successfully */ +#define XFER_TIMEOUT 2 /* no server reachable/xfer timeout */ +#define XFER_FAIL 3 /* other failure, has been logged */ + +#include <sys/time.h> + +/* XXX - "struct qserv" is deprecated in favor of "struct nameser" */ +struct qserv { + struct sockaddr_in + ns_addr; /* address of NS */ + struct databuf *ns; /* databuf for NS record */ + struct databuf *nsdata; /* databuf for server address */ + struct timeval stime; /* time first query started */ + int nretry; /* # of times addr retried */ +}; + +/* + * Structure for recording info on forwarded or generated queries. + */ +struct qinfo { + u_int16_t q_id; /* id of query */ + u_int16_t q_nsid; /* id of forwarded query */ + struct sockaddr_in + q_from; /* requestor's address */ + u_char *q_msg, /* the message */ + *q_cmsg; /* the cname message */ + int16_t q_msglen, /* len of message */ + q_cmsglen; /* len of cname message */ + int16_t q_dfd; /* UDP file descriptor */ + struct fwdinfo *q_fwd; /* last forwarder used */ + time_t q_time; /* time to retry */ + time_t q_expire; /* time to expire */ + struct qinfo *q_next; /* rexmit list (sorted by time) */ + struct qinfo *q_link; /* storage list (random order) */ + struct databuf *q_usedns[NSMAX]; /* databuf for NS that we've tried */ + struct qserv q_addr[NSMAX]; /* addresses of NS's */ +#ifdef notyet + struct nameser *q_ns[NSMAX]; /* name servers */ +#endif + u_char q_naddr; /* number of addr's in q_addr */ + u_char q_curaddr; /* last addr sent to */ + u_char q_nusedns; /* number of elements in q_usedns[] */ + u_int8_t q_flags; /* see below */ + int16_t q_cname; /* # of cnames found */ + int16_t q_nqueries; /* # of queries required */ + struct qstream *q_stream; /* TCP stream, null if UDP */ + struct zoneinfo *q_zquery; /* Zone query is about (Q_ZSERIAL) */ +#ifdef LAME_DELEGATION + char q_domain[MAXDNAME]; /* domain for servers we are querying */ +#endif /* LAME_DELEGATION */ +}; + + /* q_flags bits (8 bits) */ +#define Q_SYSTEM 0x01 /* is a system query */ +#define Q_PRIMING 0x02 /* generated during priming phase */ +#define Q_ZSERIAL 0x04 /* getting zone serial for xfer test */ + +#define Q_NEXTADDR(qp,n) \ + (((qp)->q_fwd == (struct fwdinfo *)0) ? \ + &(qp)->q_addr[n].ns_addr : &(qp)->q_fwd->fwdaddr) + +#define RETRY_TIMEOUT 45 +#define QINFO_NULL ((struct qinfo *)0) + +/* + * Return codes from ns_forw: + */ +#define FW_OK 0 +#define FW_DUP 1 +#define FW_NOSERVER 2 +#define FW_SERVFAIL 3 + +struct qstream { + int s_rfd; /* stream file descriptor */ + int s_size; /* expected amount of data to recive */ + int s_bufsize; /* amount of data recived in s_buf */ + u_char *s_buf; /* buffer of received data */ + u_char *s_bufp; /* pointer into s_buf of recived data*/ + struct qstream *s_next; /* next stream */ + struct sockaddr_in + s_from; /* address query came from */ + u_int32_t s_time; /* time stamp of last transaction */ + int s_refcnt; /* number of outstanding queries */ + u_int16_t s_tempsize; /* temporary for size from net */ +}; +#define QSTREAM_NULL ((struct qstream *)0) + +struct qdatagram { + int dq_dfd; /* datagram file descriptor */ + time_t dq_gen; /* generation number */ + struct qdatagram + *dq_next; /* next datagram */ + struct in_addr dq_addr; /* interface address */ +}; +#define QDATAGRAM_NULL ((struct qdatagram *)0) + +struct netinfo { + struct netinfo *next; + u_int32_t addr; + u_int32_t mask; + struct in_addr my_addr; +}; + +#define ALLOW_NETS 0x0001 +#define ALLOW_HOSTS 0x0002 +#define ALLOW_ALL (ALLOW_NETS | ALLOW_HOSTS) + +struct fwdinfo { + struct fwdinfo *next; + struct sockaddr_in + fwdaddr; +}; + +enum nameserStats { nssRcvdQ, /* sent us a query */ + nssRcvdR, /* sent us an answer */ + nssRcvdIQ, /* sent us an inverse query */ + nssRcvdNXD, /* sent us a negative response */ + nssRcvdFwdQ, /* sent us a query we had to fwd */ + nssRcvdFwdR, /* sent us a response we had to fwd */ + nssRcvdDupQ, /* sent us a retry */ + nssRcvdDupR, /* sent us an extra answer */ + nssRcvdFail, /* sent us a SERVFAIL */ + nssRcvdFErr, /* sent us a FORMERR */ + nssRcvdErr, /* sent us some other error */ + nssRcvdTCP, /* sent us a query using TCP */ + nssRcvdAXFR, /* sent us an AXFR */ + nssRcvdLDel, /* sent us a lame delegation */ + nssRcvdOpts, /* sent us some IP options */ + nssSentSysQ, /* sent them a sysquery */ + nssSentAns, /* sent them an answer */ + nssSentFwdQ, /* fwdd a query to them */ + nssSentFwdR, /* fwdd a response to them */ + nssSentDupQ, /* sent them a retry */ + nssSentFail, /* sent them a SERVFAIL */ + nssSentFErr, /* sent them a FORMERR */ + nssSendtoErr, /* error in sendto(2) */ + nssLast }; + +struct nameser { + struct in_addr addr; /* key */ + u_long stats[nssLast]; /* statistics */ +#ifdef notyet + u_int32_t rtt; /* round trip time */ + /* XXX - need to add more stuff from "struct qserv", and use our rtt */ + u_int16_t flags; /* see below */ +#endif + u_int8_t xfers; /* #/xfers running right now */ +}; + + +#ifdef NCACHE +#define NOERROR_NODATA 6 /* only used internally by the server, used for + * -ve $ing non-existence of records. 6 is not + * a code used as yet anyway. anant@isi.edu + */ +#define NTTL 600 /* ttl for negative data: 10 minutes? */ +#endif /*NCACHE*/ + +#define VQEXPIRY 900 /* a VQ entry expires in 15*60 = 900 seconds */ + +#ifdef VALIDATE + +#define INVALID 0 +#define VALID_NO_CACHE 1 +#define VALID_CACHE 2 +#define MAXNAMECACHE 100 +#define MAXVQ 100 /* Max number of elements in TO_Validate queue */ + +struct _nameaddr { + struct in_addr ns_addr; + char *nsname; +}; +typedef struct _nameaddr NAMEADDR; + +struct _to_validate { + int16_t class; /* Name Class */ + int16_t type; /* RR type */ + char *data; /* RR data */ + char *dname; /* Name */ + time_t time; /* time at which inserted in queue */ + struct _to_validate + *next, + *prev; +}; +typedef struct _to_validate TO_Validate; + +#endif /*VALIDATE*/ + + +#ifdef DEBUG +# define dprintf(lev, args) ((debug >= lev) && fprintf args) +#else +# define dprintf(lev, args) +#endif + + +#ifdef INIT + error "INIT already defined, check system include files" +#endif +#ifdef DECL + error "DECL already defined, check system include files" +#endif + +#ifdef MAIN_PROGRAM +#define INIT(x) = x +#define DECL +#else +#define INIT(x) +#define DECL extern +#endif diff --git a/usr.sbin/named/ns_forw.c b/usr.sbin/named/ns_forw.c new file mode 100644 index 000000000000..574596460ada --- /dev/null +++ b/usr.sbin/named/ns_forw.c @@ -0,0 +1,909 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_forw.c 4.32 (Berkeley) 3/3/91"; +static char rcsid[] = "$Id: ns_forw.c,v 4.9.1.17 1994/07/23 23:23:56 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986 + * - + * Copyright (c) 1986 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <syslog.h> +#include <resolv.h> +#include <stdio.h> +#include <errno.h> + +#include "named.h" + +/* + * Forward the query to get the answer since its not in the database. + * Returns FW_OK if a request struct is allocated and the query sent. + * Returns FW_DUP if this is a duplicate of a pending request. + * Returns FW_NOSERVER if there were no addresses for the nameservers. + * Returns FW_SERVFAIL on malloc error or if asked to do something + * dangerous, such as fwd to ourselves or fwd to the host that asked us. + * + * (no action is taken on errors and qpp is not filled in.) + */ +int +ns_forw(nsp, msg, msglen, fp, qsp, dfd, qpp, dname, np) + struct databuf *nsp[]; + u_char *msg; + int msglen; + struct sockaddr_in *fp; + struct qstream *qsp; + int dfd; + struct qinfo **qpp; + char *dname; + struct namebuf *np; +{ + register struct qinfo *qp; + struct sockaddr_in *nsa; + HEADER *hp; + u_int16_t id; + int n; + + dprintf(3, (ddt, "ns_forw()\n")); + + hp = (HEADER *) msg; + id = hp->id; + /* Look at them all */ + for (qp = qhead; qp!=QINFO_NULL; qp = qp->q_link) { + if (qp->q_id == id && + bcmp((char *)&qp->q_from, fp, sizeof(qp->q_from)) == 0 && + ((qp->q_cmsglen == 0 && qp->q_msglen == msglen && + bcmp((char *)qp->q_msg+2, msg+2, msglen-2) == 0) || + (qp->q_cmsglen == msglen && + bcmp((char *)qp->q_cmsg+2, msg+2, msglen-2) == 0))) { + dprintf(3, (ddt, + "forw: dropped DUP id=%d\n", ntohs(id))); + nameserIncr(fp->sin_addr, nssRcvdDupQ); + return (FW_DUP); + } + } + + qp = qnew(); +#ifdef LAME_DELEGATION + getname(np, qp->q_domain, sizeof qp->q_domain); +#endif + qp->q_from = *fp; /* nslookup wants to know this */ + if ((n = nslookup(nsp, qp, dname, "ns_forw")) < 0) { + dprintf(2, (ddt, "forw: nslookup reports danger\n")); + qfree(qp); + return (FW_SERVFAIL); + } else if (n == 0 && !(forward_only && fwdtab)) { + dprintf(2, (ddt, "forw: no nameservers found\n")); + qfree(qp); + return (FW_NOSERVER); + } + qp->q_stream = qsp; + qp->q_curaddr = 0; + qp->q_fwd = fwdtab; + qp->q_dfd = dfd; + qp->q_id = id; + qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; + hp->id = qp->q_nsid = htons((u_int16_t)++nsid); + hp->ancount = 0; + hp->nscount = 0; + hp->arcount = 0; + if ((qp->q_msg = (u_char *)malloc((unsigned)msglen)) == NULL) { + syslog(LOG_ERR, "forw: %m"); + qfree(qp); + return (FW_SERVFAIL); + } + bcopy(msg, qp->q_msg, qp->q_msglen = msglen); + if (!qp->q_fwd) { + hp->rd = 0; + qp->q_addr[0].stime = tt; + } + +#ifdef SLAVE_FORWARD + if (forward_only) + schedretry(qp, (time_t)slave_retry); + else +#endif /* SLAVE_FORWARD */ + schedretry(qp, qp->q_fwd ?(2*RETRYBASE) :retrytime(qp)); + + nsa = Q_NEXTADDR(qp, 0); + dprintf(1, (ddt, + "forw: forw -> [%s].%d ds=%d nsid=%d id=%d %dms retry %dsec\n", + inet_ntoa(nsa->sin_addr), + ntohs(nsa->sin_port), ds, + ntohs(qp->q_nsid), ntohs(qp->q_id), + (qp->q_addr[0].nsdata != NULL) + ? qp->q_addr[0].nsdata->d_nstime + : -1, + qp->q_time - tt.tv_sec)); +#ifdef DEBUG + if (debug >= 10) + fp_query(msg, ddt); +#endif + if (sendto(ds, msg, msglen, 0, (struct sockaddr *)nsa, + sizeof(struct sockaddr_in)) < 0) { + if (!haveComplained((char*)nsa->sin_addr.s_addr, sendtoStr)) + syslog(LOG_NOTICE, "ns_forw: sendto([%s].%d): %m", + inet_ntoa(nsa->sin_addr), ntohs(nsa->sin_port)); + nameserIncr(nsa->sin_addr, nssSendtoErr); + } + nameserIncr(fp->sin_addr, nssRcvdFwdQ); + nameserIncr(nsa->sin_addr, nssSentFwdQ); + if (qpp) + *qpp = qp; + hp->rd = 1; + return (0); +} + +/* struct qdatagram * + * aIsUs(addr) + * scan the datagramq (our list of interface addresses) for "addr" + * returns: + * pointer to qdatagram entry or NULL if no match is found + * notes: + * INADDR_ANY ([0.0.0.0]) is on the datagramq, so it's considered "us" + * author: + * Paul Vixie (DECWRL) April 1991 + */ +struct qdatagram * +aIsUs(addr) + struct in_addr addr; +{ + struct qdatagram *dqp; + + for (dqp = datagramq; dqp != QDATAGRAM_NULL; dqp = dqp->dq_next) { + if (addr.s_addr == dqp->dq_addr.s_addr) { + return dqp; + } + } + return NULL; +} + +/* haveComplained(tag1, tag2) + * check to see if we have complained about (tag1,tag2) recently + * (note that these are declared as pointers but are never deref'd) + * returns: + * boolean: have we complained recently? + * side-effects: + * outdated complaint records removed from our static list + * author: + * Paul Vixie (DECWRL) April 1991 + */ +int +haveComplained(tag1, tag2) + char *tag1, *tag2; +{ + struct complaint { + char *tag1, *tag2; + time_t expire; + struct complaint *next; + }; + static struct complaint *List = NULL; + struct complaint *cur, *next, *prev; + int r = 0; + + for (cur = List, prev = NULL; cur; prev = cur, cur = next) { + next = cur->next; + if (tt.tv_sec > cur->expire) { + if (prev) + prev->next = next; + else + List = next; + free((char*) cur); + cur = prev; + } else if ((tag1 == cur->tag1) && (tag2 == cur->tag2)) { + r++; + } + } + if (!r) { + cur = (struct complaint *)malloc(sizeof(struct complaint)); + cur->tag1 = tag1; + cur->tag2 = tag2; + cur->expire = tt.tv_sec + INIT_REFRESH; /* "10 minutes" */ + cur->next = NULL; + if (prev) + prev->next = cur; + else + List = cur; + } + return r; +} + +/* void + * nslookupComplain(sysloginfo, queryname, complaint, dname, a_rr) + * Issue a complaint about a dangerous situation found by nslookup(). + * params: + * sysloginfo is a string identifying the complainant. + * queryname is the domain name associated with the problem. + * complaint is a string describing what is wrong. + * dname and a_rr are the problematic other name server. + */ +void +nslookupComplain(sysloginfo, queryname, complaint, dname, a_rr) + char *sysloginfo, *queryname, *complaint, *dname; + register struct databuf *a_rr; +{ + dprintf(2, (ddt, "NS '%s' %s\n", dname, complaint)); + if (sysloginfo && queryname && !haveComplained(queryname, complaint)) + { + char buf[512]; + + /* syslog only takes 5 params */ + sprintf(buf, "%s: query(%s) %s (%s:%s)", + sysloginfo, queryname, + complaint, dname, inet_ntoa( + *(struct in_addr*)a_rr->d_data + )); + syslog(LOG_INFO, buf); + } +} + +/* + * nslookup(nsp, qp, syslogdname, sysloginfo) + * Lookup the address for each nameserver in `nsp' and add it to + * the list saved in the qinfo structure pointed to by `qp'. + * Omits information about nameservers that we shouldn't ask. + * Detects the following dangerous operations: + * One of the A records for one of the nameservers in nsp + * refers to the address of one of our own interfaces; + * One of the A records refers to the nameserver port on + * the host that asked us this question. + * returns: the number of addresses added, or -1 if a dangerous operation + * is detected. + * side effects: + * if a dangerous situation is detected and (syslogdname && sysloginfo), + * calls syslog. + */ +int +nslookup(nsp, qp, syslogdname, sysloginfo) + struct databuf *nsp[]; + register struct qinfo *qp; + char *syslogdname; + char *sysloginfo; +{ + register struct namebuf *np; + register struct databuf *dp, *nsdp; + register struct qserv *qs; + register int n; + register unsigned int i; + struct hashbuf *tmphtp; + char *dname, *fname; + int oldn, naddr, class, found_arr; + time_t curtime; + + dprintf(3, (ddt, "nslookup(nsp=0x%x, qp=0x%x, \"%s\")\n", + nsp, qp, syslogdname)); + + naddr = n = qp->q_naddr; + curtime = (u_long) tt.tv_sec; + while ((nsdp = *nsp++) != NULL) { + class = nsdp->d_class; + dname = (char *)nsdp->d_data; + dprintf(3, (ddt, "nslookup: NS \"%s\" c=%d t=%d (0x%x)\n", + dname, class, nsdp->d_type, nsdp->d_flags)); + + /* don't put in servers we have tried */ + for (i = 0; i < qp->q_nusedns; i++) { + if (qp->q_usedns[i] == nsdp) { + dprintf(2, (ddt, + "skipping used NS w/name %s\n", + nsdp->d_data)); + goto skipserver; + } + } + + tmphtp = ((nsdp->d_flags & DB_F_HINT) ?fcachetab :hashtab); + np = nlookup(dname, &tmphtp, &fname, 1); + if (np == NULL || fname != dname) { + dprintf(3, (ddt, "%s: not found %s %x\n", + dname, fname, np)); + continue; + } + found_arr = 0; + oldn = n; + + /* look for name server addresses */ + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + struct in_addr nsa; + + if (dp->d_type == T_CNAME && dp->d_class == class) + goto skipserver; + if (dp->d_type != T_A || dp->d_class != class) + continue; +#ifdef NCACHE + if (dp->d_rcode) + continue; +#endif + /* + * Don't use records that may become invalid to + * reference later when we do the rtt computation. + * Never delete our safety-belt information! + */ + if ((dp->d_zone == 0) && + (dp->d_ttl < (curtime+900)) && + !(dp->d_flags & DB_F_HINT) ) + { + dprintf(3, (ddt, + "nslookup: stale entry '%s'\n", + np->n_dname)); + /* Cache invalidate the NS RR's */ + if (dp->d_ttl < curtime) + delete_all(np, class, T_A); + n = oldn; + break; + } +#ifdef VALIDATE + /* anant@isi.edu validation procedure, maintains a + * table of server names-addresses used recently + */ + store_name_addr(dname, (struct in_addr *)dp->d_data, + syslogdname, sysloginfo); +#endif /*VALIDATE*/ + + found_arr++; + nsa = *(struct in_addr *)dp->d_data; + /* don't put in duplicates */ + qs = qp->q_addr; + for (i = 0; i < n; i++, qs++) + if (qs->ns_addr.sin_addr.s_addr == nsa.s_addr) + goto skipaddr; + qs->ns_addr.sin_family = AF_INET; + qs->ns_addr.sin_port = ns_port; + qs->ns_addr.sin_addr = nsa; + qs->ns = nsdp; + qs->nsdata = dp; + qs->nretry = 0; + /* + * if we are being asked to fwd a query whose + * nameserver list includes our own name/address(es), + * then we have detected a lame delegation and rather + * than melt down the network and hose down the other + * servers (who will hose us in return), we'll return + * -1 here which will cause SERVFAIL to be sent to + * the client's resolver which will hopefully then + * shut up. + * + * (originally done in nsContainsUs by vix@dec mar92; + * moved into nslookup by apb@und jan1993) + */ + if (aIsUs(nsa)) { + static char *complaint = "contains our address"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp); + return (-1); + } + /* + * If we want to forward to a host that asked us + * this question then either we or they are sick + * (unless they asked from some port other than + * their nameserver port). (apb@und jan1993) + */ + if (bcmp((char *)&qp->q_from, (char *)&qs->ns_addr, + sizeof(qp->q_from)) == 0) + { + static char *complaint = "forwarding loop"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp); + return (-1); + } + /* + * Don't forward queries to bogus servers. Note + * that this is unlike the previous tests, which + * are fatal to the query. Here we just skip the + * server, which is only fatal if it's the last + * server. Note also that we antialias here -- all + * A RR's of a server are considered the same server, + * and if any of them is bogus we skip the whole + * server. Those of you using multiple A RR's to + * load-balance your servers will (rightfully) lose + * here. But (unfortunately) only if they are bogus. + */ + if (addr_on_netlist(nsa, boglist)) + goto skipserver; + + n++; + if (n >= NSMAX) + goto out; + skipaddr: ; + } + dprintf(8, (ddt, "nslookup: %d ns addrs\n", n)); + if (found_arr == 0 && !(qp->q_flags & Q_SYSTEM)) + (void) sysquery(dname, class, T_A, NULL, 0); +skipserver: ; + } +out: + dprintf(3, (ddt, "nslookup: %d ns addrs total\n", n)); + qp->q_naddr = n; +#ifdef DATUMREFCNT + /* must be run before the sort */ + for (i = naddr ; i < n ; i++) { + qp->q_addr[i].nsdata->d_rcnt++; + qp->q_addr[i].ns->d_rcnt++; + } +#endif + if (n > 1) { + qsort((char *)qp->q_addr, n, sizeof(struct qserv), + (int (*)__P((const void *, const void *)))qcomp); + } + return (n - naddr); +} + +/* + * qcomp - compare two NS addresses, and return a negative, zero, or + * positive value depending on whether the first NS address is + * "better than", "equally good as", or "inferior to" the second + * NS address. + * + * How "goodness" is defined (for the purposes of this routine): + * - If the estimated round trip times differ by an amount deemed significant + * then the one with the smaller estimate is preferred; else + * - If we can determine which one is topologically closer then the + * closer one is preferred; else + * - The one with the smaller estimated round trip time is preferred + * (zero is returned if the two estimates are identical). + * + * How "topological closeness" is defined (for the purposes of this routine): + * Ideally, named could consult some magic map of the Internet and + * determine the length of the path to an arbitrary destination. Sadly, + * no such magic map exists. However, named does have a little bit of + * topological information in the form of the sortlist (which includes + * the directly connected subnet(s), the directly connected net(s), and + * any additional nets that the administrator has added using the "sortlist" + * directive in the bootfile. Thus, if only one of the addresses matches + * something in the sortlist then it is considered to be topologically + * closer. If both match, but match different entries in the sortlist, + * then the one that matches the entry closer to the beginning of the + * sorlist is considered to be topologically closer. In all other cases, + * topological closeness is ignored because it's either indeterminate or + * equal. + * + * How times are compared: + * Both times are rounded to the closest multiple of the NOISE constant + * defined below and then compared. If the rounded values are equal + * then the difference in the times is deemed insignificant. Rounding + * is used instead of merely taking the absolute value of the difference + * because doing the latter would make the ordering defined by this + * routine be incomplete in the mathematical sense (e.g. A > B and + * B > C would not imply A > C). The mathematics are important in + * practice to avoid core dumps in qsort(). + * + * XXX: this doesn't solve the European root nameserver problem very well. + * XXX: we should detect and mark as inferior nameservers that give bogus + * answers + * + * (this was originally vixie's stuff but almquist fixed fatal bugs in it + * and wrote the above documentation) + */ + +/* + * RTT delta deemed to be significant, in milliseconds. With the current + * definition of RTTROUND it must be a power of 2. + */ +#define NOISE 128 /* milliseconds; 0.128 seconds */ + +#define sign(x) (((x) < 0) ? -1 : ((x) > 0) ? 1 : 0) +#define RTTROUND(rtt) (((rtt) + (NOISE >> 1)) & ~(NOISE - 1)) + +int +qcomp(qs1, qs2) + struct qserv *qs1, *qs2; +{ + int pos1, pos2, pdiff; + u_long rtt1, rtt2; + long tdiff; + + if ((!qs1->nsdata) || (!qs2->nsdata)) + return 0; + rtt1 = qs1->nsdata->d_nstime; + rtt2 = qs2->nsdata->d_nstime; + + dprintf(10, (ddt, "qcomp(%s, %s) %lu (%lu) - %lu (%lu) = %lu", + inet_ntoa(qs1->ns_addr.sin_addr), + inet_ntoa(qs2->ns_addr.sin_addr), + rtt1, RTTROUND(rtt1), rtt2, RTTROUND(rtt2), + rtt1 - rtt2)); + if (RTTROUND(rtt1) == RTTROUND(rtt2)) { + pos1 = position_on_netlist(qs1->ns_addr.sin_addr, nettab); + pos2 = position_on_netlist(qs2->ns_addr.sin_addr, nettab); + pdiff = pos1 - pos2; + dprintf(10, (ddt, ", pos1=%d, pos2=%d\n", pos1, pos2)); + if (pdiff) + return (pdiff); + } else { + dprintf(10, (ddt, "\n")); + } + tdiff = rtt1 - rtt2; + return (sign(tdiff)); +} +#undef sign +#undef RTTROUND + +/* + * Arrange that forwarded query (qp) is retried after t seconds. + * Query list will be sorted after z_time is updated. + */ +void +schedretry(qp, t) + struct qinfo *qp; + time_t t; +{ + register struct qinfo *qp1, *qp2; + +#ifdef DEBUG + if (debug > 3) { + fprintf(ddt,"schedretry(0x%x, %ld sec)\n", qp, (long)t); + if (qp->q_time) + fprintf(ddt, + "WARNING: schedretry(%#x, %d) q_time already %d\n", + qp, t, qp->q_time); + } +#endif + t += (u_long) tt.tv_sec; + qp->q_time = t; + + if ((qp1 = retryqp) == NULL) { + retryqp = qp; + qp->q_next = NULL; + return; + } + if (t < qp1->q_time) { + qp->q_next = qp1; + retryqp = qp; + return; + } + while ((qp2 = qp1->q_next) != NULL && qp2->q_time < t) + qp1 = qp2; + qp1->q_next = qp; + qp->q_next = qp2; +} + +/* + * Unsched is called to remove a forwarded query entry. + */ +void +unsched(qp) + struct qinfo *qp; +{ + register struct qinfo *np; + + dprintf(3, (ddt, "unsched(%#x, %d )\n", qp, ntohs(qp->q_id))); + if (retryqp == qp) { + retryqp = qp->q_next; + } else { + for (np=retryqp; np->q_next != QINFO_NULL; np = np->q_next) { + if (np->q_next != qp) + continue; + np->q_next = qp->q_next; /* dequeue */ + break; + } + } + qp->q_next = QINFO_NULL; /* sanity check */ + qp->q_time = 0; +} + +/* + * Retry is called to retransmit query 'qp'. + */ +void +retry(qp) + register struct qinfo *qp; +{ + register int n; + register HEADER *hp; + struct sockaddr_in *nsa; + + dprintf(3, (ddt, "retry(x%x) id=%d\n", qp, ntohs(qp->q_id))); + + if (qp->q_msg == NULL) { /* XXX - why? */ + qremove(qp); + return; + } + + if (qp->q_expire && (qp->q_expire < tt.tv_sec)) { + dprintf(1, (ddt, + "retry(x%x): expired @ %d (%d secs before now (%d))\n", + qp, qp->q_expire, tt.tv_sec - qp->q_expire, + tt.tv_sec)); + if (qp->q_stream) /* return failure code on stream */ + goto fail; + qremove(qp); + return; + } + + /* try next address */ + n = qp->q_curaddr; + if (qp->q_fwd) { + qp->q_fwd = qp->q_fwd->next; + if (qp->q_fwd) + goto found; + /* out of forwarders, try direct queries */ + } else + ++qp->q_addr[n].nretry; + if (!forward_only) { + do { + if (++n >= (int)qp->q_naddr) + n = 0; + if (qp->q_addr[n].nretry < MAXRETRY) + goto found; + } while (n != qp->q_curaddr); + } +fail: + /* + * Give up. Can't reach destination. + */ + hp = (HEADER *)(qp->q_cmsg ? qp->q_cmsg : qp->q_msg); + if (qp->q_flags & Q_PRIMING) { + /* Can't give up priming */ + unsched(qp); + schedretry(qp, (time_t)60*60); /* 1 hour */ + hp->rcode = NOERROR; /* Lets be safe, reset the query */ + hp->qr = hp->aa = 0; + qp->q_fwd = fwdtab; + for (n = 0; n < (int)qp->q_naddr; n++) + qp->q_addr[n].nretry = 0; + return; + } + dprintf(5, (ddt, "give up\n")); + n = ((HEADER *)qp->q_cmsg ? qp->q_cmsglen : qp->q_msglen); + hp->id = qp->q_id; + hp->qr = 1; + hp->ra = 1; + hp->rd = 1; + hp->rcode = SERVFAIL; +#ifdef DEBUG + if (debug >= 10) + fp_query(qp->q_msg, ddt); +#endif + if (send_msg((u_char *)hp, n, qp)) { + dprintf(1, (ddt,"gave up retry(x%x) nsid=%d id=%d\n", + qp, ntohs(qp->q_nsid), ntohs(qp->q_id))); + } + nameserIncr(qp->q_from.sin_addr, nssSentFail); + qremove(qp); + return; + +found: + if (qp->q_fwd == 0 && qp->q_addr[n].nretry == 0) + qp->q_addr[n].stime = tt; + qp->q_curaddr = n; + hp = (HEADER *)qp->q_msg; + hp->rd = (qp->q_fwd ? 1 : 0); + nsa = Q_NEXTADDR(qp, n); + dprintf(1, (ddt, + "%s(addr=%d n=%d) -> [%s].%d ds=%d nsid=%d id=%d %dms\n", + (qp->q_fwd ? "reforw" : "resend"), + n, qp->q_addr[n].nretry, + inet_ntoa(nsa->sin_addr), + ntohs(nsa->sin_port), ds, + ntohs(qp->q_nsid), ntohs(qp->q_id), + (qp->q_addr[n].nsdata != 0) + ? qp->q_addr[n].nsdata->d_nstime + : (-1))); +#ifdef DEBUG + if (debug >= 10) + fp_query(qp->q_msg, ddt); +#endif + /* NOSTRICT */ + if (sendto(ds, qp->q_msg, qp->q_msglen, 0, + (struct sockaddr *)nsa, + sizeof(struct sockaddr_in)) < 0) { + dprintf(3, (ddt, "error resending msg errno=%d\n", errno)); + } + hp->rd = 1; /* leave set to 1 for dup detection */ + nameserIncr(nsa->sin_addr, nssSentDupQ); + unsched(qp); +#ifdef SLAVE_FORWARD + if(forward_only) + schedretry(qp, (time_t)slave_retry); + else +#endif /* SLAVE_FORWARD */ + schedretry(qp, qp->q_fwd ? (2*RETRYBASE) : retrytime(qp)); +} + +/* + * Compute retry time for the next server for a query. + * Use a minimum time of RETRYBASE (4 sec.) or twice the estimated + * service time; * back off exponentially on retries, but place a 45-sec. + * ceiling on retry times for now. (This is because we don't hold a reference + * on servers or their addresses, and we have to finish before they time out.) + */ +time_t +retrytime(qp) + struct qinfo *qp; +{ + time_t t, u, v; + struct qserv *ns = &qp->q_addr[qp->q_curaddr]; + + if (ns->nsdata != NULL) + t = (time_t) MAX(RETRYBASE, 2 * ns->nsdata->d_nstime / 1000); + else + t = (time_t) RETRYBASE; + u = t << ns->nretry; + v = MIN(u, RETRY_TIMEOUT); /* max. retry timeout for now */ + dprintf(3, (ddt, "retrytime: nstime%ldms t%ld nretry%ld u%ld : v%ld\n", + ns->nsdata ?(long)(ns->nsdata->d_nstime / 1000) :(long)-1, + (long)t, (long)ns->nretry, (long)u, (long)v)); + return (v); +} + +void +qflush() +{ + while (qhead) + qremove(qhead); + qhead = QINFO_NULL; +} + +void +qremove(qp) + register struct qinfo *qp; +{ + dprintf(3, (ddt, "qremove(x%x)\n", qp)); + + if (qp->q_flags & Q_ZSERIAL) + qserial_answer(qp, 0); + unsched(qp); + qfree(qp); +} + +#if defined(__STDC__) || defined(__GNUC__) +struct qinfo * +qfindid(u_int16_t id) +#else +struct qinfo * +qfindid(id) + register u_int16_t id; +#endif +{ + register struct qinfo *qp; + + dprintf(3, (ddt, "qfindid(%d)\n", ntohs(id))); + for (qp = qhead; qp!=QINFO_NULL; qp = qp->q_link) { + if (qp->q_nsid == id) + return(qp); + } + dprintf(5, (ddt, "qp not found\n")); + return (NULL); +} + +struct qinfo * +#ifdef DMALLOC +qnew_tagged(file, line) + char *file; + int line; +#else +qnew() +#endif +{ + register struct qinfo *qp; + + if ((qp = (struct qinfo *) +#ifdef DMALLOC + dcalloc(file, line, +#else + calloc( +#endif + 1, sizeof(struct qinfo))) == NULL) { + dprintf(5, (ddt, "qnew: calloc error\n")); + syslog(LOG_ERR, "forw: %m"); + exit(12); + } + dprintf(5, (ddt, "qnew(x%x)\n", qp)); + qp->q_link = qhead; + qhead = qp; + return (qp); +} + +void +qfree(qp) + struct qinfo *qp; +{ + register struct qinfo *np; + register struct databuf *dp; +#ifdef DATUMREFCNT + int i; +#endif + +#ifdef DEBUG + if(debug > 3) + fprintf(ddt,"Qfree( x%x )\n", qp); + if(debug && qp->q_next) + fprintf(ddt,"WARNING: qfree of linked ptr x%x\n", qp); +#endif + if (qp->q_msg) + free(qp->q_msg); + if (qp->q_cmsg) + free(qp->q_cmsg); +#ifdef DATUMREFCNT + for (i = 0 ; i < (int)qp->q_naddr ; i++) { + dp = qp->q_addr[i].ns; + if (dp) + if (--(dp->d_rcnt)) { + dprintf(3, (ddt, "qfree: ns %s rcnt %d\n", + dp->d_data, + dp->d_rcnt)); + } else { + dprintf(3, (ddt, "qfree: ns %s rcnt %d delayed\n", + dp->d_data, + dp->d_rcnt)); + free((char*)dp); + } + dp = qp->q_addr[i].nsdata; + if (dp) + if ((--(dp->d_rcnt))) { + dprintf(3, (ddt, "qfree: nsdata %08.8X rcnt %d\n", + *(int32_t *)(dp->d_data), + dp->d_rcnt)); + } else { + dprintf(3, (ddt, "qfree: nsdata %08.8X rcnt %d delayed\n", + *(int32_t *)(dp->d_data), + dp->d_rcnt)); + free((char*)dp); + } + } +#endif + if( qhead == qp ) { + qhead = qp->q_link; + } else { + for( np=qhead; np->q_link != QINFO_NULL; np = np->q_link ) { + if( np->q_link != qp ) continue; + np->q_link = qp->q_link; /* dequeue */ + break; + } + } + free((char *)qp); +} diff --git a/usr.sbin/named/ns_func.h b/usr.sbin/named/ns_func.h new file mode 100644 index 000000000000..eaff3bfe11fb --- /dev/null +++ b/usr.sbin/named/ns_func.h @@ -0,0 +1,140 @@ +/* ns_func.h - declarations for ns_*.c's externally visible functions + * + * $Id: ns_func.h,v 1.12 1994/07/22 08:42:39 vixie Exp $ + */ + +/* ++from ns_resp.c++ */ +extern void ns_resp __P((u_char *, int)), + prime_cache __P((void)), + delete_all __P((struct namebuf *, int, int)); +extern struct qinfo *sysquery __P((char *, int, int, + struct in_addr *, int)); +extern int + doupdate __P((u_char *, int, u_char *, int, + struct databuf **, int, u_int)), + send_msg __P((u_char *, int, struct qinfo *)), + findns __P((struct namebuf **, int, + struct databuf **, int *, int)), + finddata __P((struct namebuf *, int, int, HEADER *, + char **, int *, int *)), + wanted __P((struct databuf *, int, int)), + add_data __P((struct namebuf *, + struct databuf **, + u_char *, int)); +/* --from ns_resp.c-- */ + +/* ++from ns_req.c++ */ +extern void ns_req __P((u_char *, int, int, + struct qstream *, + struct sockaddr_in *, + int)); +extern int stale __P((struct databuf *)), + make_rr __P((char *, struct databuf *, + u_char *, int, int)), + doaddinfo __P((HEADER *, u_char *, int)), + doaddauth __P((HEADER *, u_char *, int, + struct namebuf *, + struct databuf *)); +/* --from ns_req.c-- */ + +/* ++from ns_forw.c++ */ +extern time_t retrytime __P((struct qinfo *)); +extern int ns_forw __P((struct databuf *nsp[], + u_char *msg, + int msglen, + struct sockaddr_in *fp, + struct qstream *qsp, + int dfd, + struct qinfo **qpp, + char *dname, + struct namebuf *np)), + haveComplained __P((char *, char *)), + nslookup __P((struct databuf *nsp[], + struct qinfo *qp, + char *syslogdname, + char *sysloginfo)), + qcomp __P((struct qserv *, struct qserv *)); +extern struct qdatagram *aIsUs __P((struct in_addr)); +extern void nslookupComplain __P((char *, char *, char *, char *, + struct databuf *)), + schedretry __P((struct qinfo *, time_t)), + unsched __P((struct qinfo *)), + retry __P((struct qinfo *)), + qflush __P((void)), + qremove __P((struct qinfo *)), + qfree __P((struct qinfo *)); +extern struct qinfo *qfindid __P((u_int16_t)), +#ifdef DMALLOC + *qnew_tagged __P((void)); +# define qnew() qnew_tagged(__FILE__, __LINE__) +#else + *qnew(); +#endif +/* --from ns_forw.c-- */ + +/* ++from ns_main.c++ */ +extern u_int32_t net_mask __P((struct in_addr)); +extern void sqrm __P((struct qstream *)), + sqflush __P((struct qstream *allbut)), + dqflush __P((time_t gen)), + sq_done __P((struct qstream *)), + ns_setproctitle __P((char *, int)), + getnetconf __P((void)); +extern struct netinfo *findnetinfo __P((struct in_addr)); +/* --from ns_main.c-- */ + +/* ++from ns_maint.c++ */ +extern void ns_maint __P((void)), + sched_maint __P((void)), +#ifdef CLEANCACHE + remove_zone __P((struct hashbuf *, int, int)), +#else + remove_zone __P((struct hashbuf *, int)), +#endif + loadxfer __P((void)), + qserial_answer __P((struct qinfo *, u_int32_t)); +extern SIG_FN endxfer __P((void)); +#ifdef DEBUG +extern void printzoneinfo __P((int)); +#endif +/* --from ns_maint.c-- */ + +/* ++from ns_sort.c++ */ +extern struct netinfo *local __P((struct sockaddr_in *)); +extern void sort_response __P((u_char *, int, + struct netinfo *, + u_char *)); +/* --from ns_sort.c-- */ + +/* ++from ns_init.c++ */ +extern void ns_init __P((char *)); +/* --from ns_init.c-- */ + +/* ++from ns_ncache.c++ */ +extern void cache_n_resp __P((u_char *, int)); +/* --from ns_ncache.c-- */ + +/* ++from ns_stats.c++ */ +extern void ns_stats __P((void)); +extern void qtypeIncr __P((int qtype)); +extern struct nameser *nameserFind __P((struct in_addr addr, int flags)); +#define NS_F_INSERT 0x0001 +extern void nameserIncr __P((struct in_addr addr, + enum nameserStats which)); +/* --from ns_stats.c-- */ + +/* ++from ns_validate.c++ */ +extern int +#ifdef NCACHE + validate __P((char *, struct sockaddr_in *, + int, int, char *, int, int)), +#else + validate __P((char *, struct sockaddr_in *, + int, int, char *, int)), +#endif + dovalidate __P((u_char *, int, u_char *, int, int, + struct sockaddr_in *, int *)), + update_msg __P((u_char *, int *, int Vlist[], int)); +extern void store_name_addr __P((char *, struct in_addr *, + char *, char *)); +/* --from ns_validate.c-- */ diff --git a/usr.sbin/named/ns_glob.h b/usr.sbin/named/ns_glob.h new file mode 100644 index 000000000000..993d14d01385 --- /dev/null +++ b/usr.sbin/named/ns_glob.h @@ -0,0 +1,245 @@ +/* + * from ns.h 4.33 (Berkeley) 8/23/90 + * $Id: ns_glob.h,v 1.7 1994/06/11 22:04:46 vixie Exp $ + */ + +/* + * ++Copyright++ 1986 + * - + * Copyright (c) 1986 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/* + * Global variables for the name server. + */ + +#ifdef DEBUG +DECL int debug INIT(0); +DECL FILE *ddt INIT(NULL); +#endif + + /* list of open streams */ +DECL struct qstream *streamq INIT(QSTREAM_NULL); + + /* list of datagram interfaces */ +DECL struct qdatagram *datagramq INIT(QDATAGRAM_NULL); + + /* often set to the current time */ +DECL struct timeval tt; + + /* head of allocated queries */ +DECL struct qinfo *qhead INIT(QINFO_NULL); + + /* list of forwarding hosts */ +DECL struct fwdinfo *fwdtab INIT(NULL); + + /* next forwarded query id */ +DECL int nsid INIT(0); + + /* datagram socket */ +DECL int ds INIT(-1); + + /* listening TCP socket */ +DECL int vs INIT(-1); + + /* received SIGHUP, need to reload db */ +DECL int needreload INIT(0); + + /* need to call ns_maint()*/ +DECL int needmaint INIT(0); + + /* how often does ns_maint() need to be called, in seconds? */ + /* (beware: this is also the upper bound on named_xfer real time) */ +DECL int maint_interval INIT(15*60); + + /* need to reload secondary zone(s) */ +DECL int needzoneload INIT(0); + + /* need to dump database */ +DECL int needToDoadump INIT(0); + + /* need to checkpoint cache */ +DECL int needToChkpt INIT(0); + + /* need to dump statistics */ +DECL int needStatsDump INIT(0); + +#ifdef ALLOW_UPDATES + /* need to exit (may need to doadump + * first, if database has changed since + * it was last dumped/booted). Gets + * set by shutdown signal handler + * (onintr) + */ +DECL int needToExit INIT(0); +#endif /* ALLOW_UPDATES */ + +#ifdef QRYLOG + /* is query logging turned on? */ +DECL int qrylog INIT(0); +#endif /*QRYLOG*/ + + /* is this a root server that should therefore not recurse? */ +DECL int NoRecurse INIT(0); + +/* + * We keep a list of favored networks headed by nettab. + * There are three (possibly empty) parts to this list, in this order: + * 1. directly attached (sub)nets. + * 2. logical networks for directly attached subnetted networks. + * 3. networks from the sort list. + * The value (*elocal) points at the first entry in the second part of the + * list, if any, while (*enettab) points at the first entry in the sort list. + */ +DECL struct netinfo *nettab INIT(NULL); +DECL struct netinfo **elocal INIT(&nettab); +DECL struct netinfo **enettab INIT(&nettab); + +#ifdef XFRNETS + /* list of nets we're willing to zone transfer to */ +DECL struct netinfo *xfrnets INIT(NULL); +#endif + +#ifdef BOGUSNS + /* list of bogus nameservers */ +DECL struct netinfo *boglist INIT(NULL); +#endif + + /* loopback net */ +DECL struct netinfo netloop; + + /* port to which we send queries */ +DECL u_int16_t ns_port; + + /* Source addr of last packet */ +DECL struct sockaddr_in from_addr; + + /* Source addr size of last packet */ +DECL int from_len; + + /* Used by ns_stats */ +DECL time_t boottime, + resettime; + + /* next query to retry */ +DECL struct qinfo *retryqp INIT(NULL); + + /* default boot file */ +#ifdef BOOTFILE +DECL char *bootfile INIT(BOOTFILE); +#else +DECL char *bootfile INIT(_PATH_BOOT); +#endif + + /* default debug output file */ +#ifdef DEBUGFILE +DECL char *debugfile INIT(DEBUGFILE); +#else +DECL char *debugfile INIT(_PATH_DEBUG); +#endif + +#ifdef WANT_PIDFILE + /* file to store current named PID */ +#ifdef PIDFILE +DECL char *PidFile INIT(PIDFILE); +#else +DECL char *PidFile INIT(_PATH_PIDFILE); +#endif +#endif /*WANT_PIDFILE*/ + + /* zone information */ +DECL struct zoneinfo *zones INIT(NULL); + + /* number of zones in use */ +DECL int nzones INIT(0); + + /* true on slave server */ +DECL int forward_only INIT(0); + + /* set if we need a priming */ +DECL int needs_prime_cache INIT(0); + + /* is cache being primed */ +DECL int priming INIT(0); + + /* ptrs to dnames in msg for dn_comp */ +DECL u_char *dnptrs[40]; + + /* number of names in addinfo */ +DECL int addcount; + + /* name of cache file */ +DECL char *cache_file; + +#ifdef LOCALDOM + /* our local domain (deprecated in favor of resolv.conf) */ +DECL char *localdomain; +#endif + +#ifdef SLAVE_FORWARD + /* retry time when a slave */ +DECL int slave_retry INIT(4); +#endif + +#ifdef STATSFILE +DECL char *statsfile INIT(STATSFILE); +#else +DECL char *statsfile INIT(_PATH_STATS); +#endif + +DECL char sendtoStr[] INIT("sendto"); + + /* defined in version.c, can't use DECL/INIT */ +extern char Version[]; + + /* max value of xfers_running */ +DECL int max_xfers_running INIT(MAX_XFERS_RUNNING); diff --git a/usr.sbin/named/ns_init.c b/usr.sbin/named/ns_init.c new file mode 100644 index 000000000000..82c2071ed2d6 --- /dev/null +++ b/usr.sbin/named/ns_init.c @@ -0,0 +1,808 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_init.c 4.38 (Berkeley) 3/21/91"; +static char rcsid[] = "$Id:"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1990 + * - + * Copyright (c) 1986, 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. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <syslog.h> +#include <signal.h> +#include <resolv.h> +#include <stdio.h> +#include <errno.h> +#include <ctype.h> + +#include "named.h" + +#undef nsaddr + +static void zoneinit __P((struct zoneinfo *)), + get_forwarders __P((FILE *)), + boot_read __P((char *)), +#ifdef DEBUG + content_zone __P((int)), +#endif + free_forwarders __P((void)); + +static struct zoneinfo *find_zone __P((char *, int, int)); + +/* + * Read boot file for configuration info. + */ +void +ns_init(bootfile) + char *bootfile; +{ + register struct zoneinfo *zp; + static int loads = 0; /* number of times loaded */ + + dprintf(1, (ddt, "\nns_init(%s)\n", bootfile)); + gettime(&tt); + + if (loads == 0) { + if ((zones = + (struct zoneinfo *)calloc(64, sizeof(struct zoneinfo))) + == NULL) { + syslog(LOG_ERR, + "Not enough memory to allocate initial zones array"); + exit(1); + } + nzones = 1; /* zone zero is cache data */ + /* allocate cache hash table, formerly the root hash table. */ + hashtab = savehash((struct hashbuf *)NULL); + + /* allocate root-hints/file-cache hash table */ + fcachetab = savehash((struct hashbuf *)NULL); + /* init zone data */ + zones[0].z_type = Z_CACHE; + } else { + /* Mark previous zones as not yet found in boot file. */ + for (zp = &zones[1]; zp < &zones[nzones]; zp++) + zp->z_flags &= ~Z_FOUND; +#ifdef LOCALDOM + if (localdomain) { + free(localdomain); + localdomain = NULL; + } +#endif + free_forwarders(); + free_netlist(enettab); +#ifdef XFRNETS + free_netlist(&xfrnets); +#endif +#ifdef BOGUSNS + free_netlist(&boglist); +#endif + forward_only = 0; + } + + dprintf(3, (ddt, "\n content of zones before loading \n")); +#ifdef DEBUG + if (debug >= 3) { + content_zone(nzones - 1); + } +#endif + boot_read(bootfile); + + /* erase all old zones that were not found */ + for (zp = &zones[1]; zp < &zones[nzones]; zp++) { + if (zp->z_type && (zp->z_flags & Z_FOUND) == 0) { +#ifdef CLEANCACHE + remove_zone(hashtab, zp - zones, 1); +#else + remove_zone(hashtab, zp - zones); +#endif +#ifdef SECURE_ZONES + free_netlist(&zp->secure_nets); +#endif + syslog(LOG_NOTICE, "Zone \"%s\" was removed", zp->z_origin); + free(zp->z_origin); + free(zp->z_source); + bzero((char *) zp, sizeof(*zp)); + } + } + dprintf(2, (ddt,"\n content of zones after loading\n")); + +#ifdef DEBUG + if (debug >= 2) { + content_zone(nzones-1); + } +#endif + + /* + * Schedule calls to ns_maint(). + */ + if (!needmaint) + sched_maint(); + dprintf(1, (ddt, "exit ns_init()%s\n", + needmaint ? ", need maintenance immediately" : "")); + loads++; +} + +/* + * Read the actual boot file. + * Set up to recurse. + */ +static void +boot_read(bootfile) + char *bootfile; +{ + register struct zoneinfo *zp; + char buf[BUFSIZ], obuf[BUFSIZ], *source; + FILE *fp; + int type; + int class; +#ifdef GEN_AXFR + char *class_p; +#endif + struct stat f_time; + static int tmpnum = 0; /* unique number for tmp zone files */ +#ifdef ALLOW_UPDATES + char *cp, *flag; +#endif + int slineno; /* Saved global line number. */ + int i; + + if ((fp = fopen(bootfile, "r")) == NULL) { + syslog(LOG_ERR, "%s: %m", bootfile); + exit(1); + } + + slineno = lineno; + lineno = 0; + + while (!feof(fp) && !ferror(fp)) { + if (!getword(buf, sizeof(buf), fp)) + continue; + /* read named.boot keyword and process args */ + if (strcasecmp(buf, "directory") == 0) { + (void) getword(buf, sizeof(buf), fp); + if (chdir(buf) < 0) { + syslog(LOG_CRIT, "directory %s: %m\n", + buf); + exit(1); + } + continue; + } else if (strcasecmp(buf, "sortlist") == 0) { + get_netlist(fp, enettab, ALLOW_NETS, buf); + continue; + } else if (strcasecmp(buf, "max-fetch") == 0) { + max_xfers_running = getnum(fp, bootfile, 0); + continue; + } else if (strcasecmp(buf, "forwarders") == 0) { + get_forwarders(fp); + continue; + } else if (strcasecmp(buf, "slave") == 0) { + forward_only++; + endline(fp); + continue; +#ifdef BOGUSNS + } else if (strcasecmp(buf, "bogusns") == 0) { + get_netlist(fp, &boglist, ALLOW_HOSTS, buf); + continue; +#endif +#ifdef XFRNETS + } else if ((strcasecmp(buf, "tcplist") == 0) || + (strcasecmp(buf, "xfrnets") == 0)) { + get_netlist(fp, &xfrnets, ALLOW_NETS, buf); + continue; +#endif +#ifdef LOCALDOM + } else if (strcasecmp(buf, "domain") == 0) { + if (getword(buf, sizeof(buf), fp)) + localdomain = savestr(buf); + endline(fp); + continue; +#endif + } else if (strcasecmp(buf, "include") == 0) { + if (getword(buf, sizeof(buf), fp)) + boot_read(buf); + endline(fp); + continue; + } else if (strncasecmp(buf, "cache", 5) == 0) { + type = Z_CACHE; + class = C_IN; +#ifdef GEN_AXFR + if (class_p = strchr(buf, '/')) + class = get_class(class_p+1); +#endif + } else if (strncasecmp(buf, "primary", 7) == 0) { + type = Z_PRIMARY; + class = C_IN; +#ifdef GEN_AXFR + if (class_p = strchr(buf, '/')) + class = get_class(class_p+1); +#endif + } else if (strncasecmp(buf, "secondary", 9) == 0) { + type = Z_SECONDARY; + class = C_IN; +#ifdef GEN_AXFR + if (class_p = strchr(buf, '/')) + class = get_class(class_p+1); +#endif +#ifdef STUBS + } else if (strncasecmp(buf, "stub", 4) == 0) { + type = Z_STUB; + class = C_IN; +#ifdef GEN_AXFR + if (class_p = strchr(buf, '/')) + class = get_class(class_p+1); +#endif +#endif + } else { + syslog(LOG_ERR, "%s: line %d: unknown field '%s'\n", + bootfile, lineno+1, buf); + endline(fp); + continue; + } + + /* + * read zone origin + */ + if (!getword(obuf, sizeof(obuf), fp)) { + syslog(LOG_ERR, "%s: line %d: missing origin\n", + bootfile, lineno); + continue; + } + i = strlen(obuf); + if ((obuf[i-1] == '.') && (i != 1)) + syslog(LOG_ERR, "%s: line %d: zone \"%s\" has trailing dot\n", + bootfile, lineno, obuf); + while ((--i >= 0) && (obuf[i] == '.')) + obuf[i] = '\0'; + dprintf(1, (ddt, "zone origin %s", obuf[0]?obuf:".")); + /* + * read source file or host address + */ + if (!getword(buf, sizeof(buf), fp)) { + syslog(LOG_ERR, "%s: line %d: missing %s\n", + bootfile, lineno, +#ifdef STUBS + (type == Z_SECONDARY || type == Z_STUB) +#else + (type == Z_SECONDARY) +#endif + ?"host address" + :"source file"); + continue; + } + + /* check for previous instance of this zone (reload) */ + if (!(zp = find_zone(obuf, type, class))) { + if (type == Z_CACHE) { + zp = &zones[0]; + zp->z_origin = ""; + goto gotcache; + } + for (zp = &zones[1]; zp < &zones[nzones]; zp++) + if (zp->z_type == Z_NIL) + goto gotzone; + /* + * this code assumes that nzones never decreases + */ + if (nzones % 64 == 0) { + dprintf(1, (ddt, + "Reallocating zones structure\n")); + /* + * Realloc() not used since it might damage zones + * if an error occurs + */ + zp = (struct zoneinfo *) + malloc((64 + nzones) + * sizeof(struct zoneinfo)); + if (zp == (struct zoneinfo *)0) { + syslog(LOG_ERR, + "no memory for more zones"); + dprintf(1, (ddt, + "Out of memory for new zones\n" + ) + ); + endline(fp); + continue; + } + bcopy((char *)zones, (char *)zp, + nzones * sizeof(struct zoneinfo)); + bzero((char *)&zp[nzones], + 64 * sizeof(struct zoneinfo)); + free(zones); + zones = zp; + } + zp = &zones[nzones++]; + gotzone: + zp->z_origin = savestr(obuf); + gotcache: + zp->z_type = type; + zp->z_class = class; + } + zp->z_addrcnt = 0; + + switch (type) { + case Z_CACHE: + source = savestr(buf); + dprintf(1, (ddt, ", source = %s\n", source)); + zp->z_refresh = 0; /* by default, no dumping */ + if (getword(buf, sizeof(buf), fp)) { +#ifdef notyet + zp->z_refresh = atoi(buf); + if (zp->z_refresh <= 0) { + syslog(LOG_ERR, + "%s: line %d: bad refresh time '%s', ignored\n", + bootfile, lineno, buf); + zp->z_refresh = 0; + } else if (cache_file == NULL) + cache_file = source; +#else + syslog(LOG_WARNING, + "%s: line %d: cache refresh ignored\n", + bootfile, lineno); +#endif + endline(fp); + } + /* + * If we've loaded this file, and the file has + * not been modified and contains no $include, + * then there's no need to reload. + */ + if (zp->z_source && + !strcmp(source, zp->z_source) && + !(zp->z_flags & Z_INCLUDE) && + stat(zp->z_source, &f_time) != -1 && + zp->z_ftime == f_time.st_mtime) { + dprintf(1, (ddt, "cache is up to date\n")); + if (source != cache_file) + free(source); + break; /* zone is already up to date */ + } + + /* file has changed, or hasn't been loaded yet */ + if (zp->z_source) { + free(zp->z_source); +#ifdef CLEANCACHE + remove_zone(fcachetab, 0, 1); +#else + remove_zone(fcachetab, 0); +#endif + } + zp->z_source = source; + dprintf(1, (ddt, "reloading zone\n")); + (void) db_load(zp->z_source, zp->z_origin, zp, 0); + break; + + case Z_PRIMARY: + source = savestr(buf); +#ifdef ALLOW_UPDATES + if (getword(buf, sizeof(buf), fp)) { + endline(fp); + flag = buf; + while (flag) { + cp = strchr(flag, ','); + if (cp) + *cp++ = 0; + if (strcasecmp(flag, "dynamic") == 0) + zp->z_flags |= Z_DYNAMIC; + else if (strcasecmp(flag, "addonly") == 0) + zp->z_flags |= Z_DYNADDONLY; + else { + syslog(LOG_ERR, + "%s: line %d: bad flag '%s'\n", + bootfile, lineno, flag); + } + flag = cp; + } + } +#else /*ALLOW_UPDATES*/ + endline(fp); +#endif + + dprintf(1, (ddt, ", source = %s\n", source)); + /* + * If we've loaded this file, and the file has + * not been modified and contains no $include, + * then there's no need to reload. + */ + if (zp->z_source && + !strcmp(source, zp->z_source) && + !(zp->z_flags & Z_INCLUDE) && + stat(zp->z_source, &f_time) != -1 && + zp->z_ftime == f_time.st_mtime) { + dprintf(1, (ddt, "zone is up to date\n")); + free(source); + break; /* zone is already up to date */ + } + if (zp->z_source) { + free(zp->z_source); +#ifdef CLEANCACHE + remove_zone(hashtab, zp - zones, 1); +#else + remove_zone(hashtab, zp - zones); +#endif + } + zp->z_source = source; + zp->z_flags &= ~Z_AUTH; + dprintf(1, (ddt, "reloading zone\n")); + if (db_load(zp->z_source, zp->z_origin, zp, 0) == 0) + zp->z_flags |= Z_AUTH; +#ifdef ALLOW_UPDATES + /* Guarantee calls to ns_maint() */ + zp->z_refresh = maint_interval; +#else + zp->z_refresh = 0; /* no maintenance needed */ + zp->z_time = 0; +#endif + break; + + case Z_SECONDARY: +#ifdef STUBS + case Z_STUB: +#endif + source = NULL; + dprintf(1, (ddt, "\n\taddrs: ")); + do { + if (!inet_aton(buf, + &zp->z_addr[zp->z_addrcnt]) + ) { + source = savestr(buf); + endline(fp); + break; + } + dprintf(1, (ddt, "%s, ", buf)); + if ((int)++zp->z_addrcnt > NSMAX - 1) { + zp->z_addrcnt = NSMAX - 1; + dprintf(1, (ddt, + "\nns.h NSMAX reached\n")); + } + } while (getword(buf, sizeof(buf), fp)); + dprintf(1, (ddt, "addrcnt = %d\n", zp->z_addrcnt)); + if (!source) { + /* + * We will always transfer this zone again + * after a reload. + */ + sprintf(buf, "/%s/NsTmp%d.%d", _PATH_TMPDIR, + getpid(), tmpnum++); + source = savestr(buf); + zp->z_flags |= Z_TMP_FILE; + } else + zp->z_flags &= ~Z_TMP_FILE; + /* + * If we had a backup file name, and it was changed, + * free old zone and start over. If we don't have + * current zone contents, try again now in case + * we have a new server on the list. + */ + if (zp->z_source && + (strcmp(source, zp->z_source) || + (stat(zp->z_source, &f_time) == -1 || + (zp->z_ftime != f_time.st_mtime)))) { + dprintf(1, (ddt, "backup file changed\n")); + free(zp->z_source); + zp->z_source = NULL; + zp->z_flags &= ~Z_AUTH; + zp->z_serial = 0; /* force xfer */ +#ifdef CLEANCACHE + remove_zone(hashtab, zp - zones, 1); +#else + remove_zone(hashtab, zp - zones); +#endif + } + if (zp->z_source) + free(source); + else + zp->z_source = source; + if (!(zp->z_flags & Z_AUTH)) + zoneinit(zp); +#ifdef FORCED_RELOAD + else { + /* + ** Force secondary to try transfer right away + ** after SIGHUP. + */ + if (reloading) { + zp->z_time = tt.tv_sec; + needmaint = 1; + } + } +#endif /* FORCED_RELOAD */ + break; + + } + zp->z_flags |= Z_FOUND; + dprintf(1, (ddt, "zone[%d] type %d: '%s'", + zp-zones, type, + *(zp->z_origin) == '\0' ? "." : zp->z_origin)); + if (zp->z_refresh && zp->z_time == 0) + zp->z_time = zp->z_refresh + tt.tv_sec; + if (zp->z_time <= tt.tv_sec) + needmaint = 1; + dprintf(1, (ddt, " z_time %d, z_refresh %d\n", + zp->z_time, zp->z_refresh)); + } + (void) my_fclose(fp); + lineno = slineno; +} + +static void +zoneinit(zp) + register struct zoneinfo *zp; +{ + struct stat sb; + + /* + * Try to load zone from backup file, + * if one was specified and it exists. + * If not, or if the data are out of date, + * we will refresh the zone from a primary + * immediately. + */ + if (!zp->z_source) + return; + if (stat(zp->z_source, &sb) == -1 || + db_load(zp->z_source, zp->z_origin, zp, 0) != 0) { + /* + * Set zone to be refreshed immediately. + */ + zp->z_refresh = INIT_REFRESH; + zp->z_retry = INIT_REFRESH; + zp->z_time = tt.tv_sec; + needmaint = 1; + } else { + zp->z_flags |= Z_AUTH; + } +} + +#ifdef ALLOW_UPDATES +/* + * Look for the authoritative zone with the longest matching RHS of dname + * and return its zone # or zero if not found. + */ +int +findzone(dname, class) + char *dname; + int class; +{ + char *dZoneName, *zoneName; + int dZoneNameLen, zoneNameLen; + int maxMatchLen = 0; + int maxMatchZoneNum = 0; + int zoneNum; + + dprintf(4, (ddt, "findzone(dname=%s, class=%d)\n", dname, class)); +#ifdef DEBUG + if (debug >= 5) { + fprintf(ddt, "zone dump:\n"); + for (zoneNum = 1; zoneNum < nzones; zoneNum++) + printzoneinfo(zoneNum); + } +#endif + + dZoneName = strchr(dname, '.'); + if (dZoneName == NULL) + dZoneName = ""; /* root */ + else + dZoneName++; /* There is a '.' in dname, so use remainder of + string as the zone name */ + dZoneNameLen = strlen(dZoneName); + for (zoneNum = 1; zoneNum < nzones; zoneNum++) { + zoneName = (zones[zoneNum]).z_origin; + zoneNameLen = strlen(zoneName); + /* The zone name may or may not end with a '.' */ + if (zoneName[zoneNameLen - 1] == '.') + zoneNameLen--; + if (dZoneNameLen != zoneNameLen) + continue; + dprintf(5, (ddt, "about to strncasecmp('%s', '%s', %d)\n", + dZoneName, zoneName, dZoneNameLen)); + if (strncasecmp(dZoneName, zoneName, dZoneNameLen) == 0) { + dprintf(5, (ddt, "match\n")); + /* + * See if this is as long a match as any so far. + * Check if "<=" instead of just "<" so that if + * root domain (whose name length is 0) matches, + * we use it's zone number instead of just 0 + */ + if (maxMatchLen <= zoneNameLen) { + maxMatchZoneNum = zoneNum; + maxMatchLen = zoneNameLen; + } + } else { + dprintf(5, (ddt, "no match\n")); + } + } + dprintf(4, (ddt, "findzone: returning %d\n", maxMatchZoneNum)); + return (maxMatchZoneNum); +} +#endif /* ALLOW_UPDATES */ + +static void +get_forwarders(fp) + FILE *fp; +{ + char buf[BUFSIZ]; + register struct fwdinfo *fip = NULL, *ftp = NULL; + +#ifdef SLAVE_FORWARD + int forward_count = 0; +#endif + + dprintf(1, (ddt, "forwarders ")); + + /* on mulitple forwarder lines, move to end of the list */ +#ifdef SLAVE_FORWARD + if (fwdtab != NULL){ + forward_count++; + for (fip = fwdtab; fip->next != NULL; fip = fip->next) + forward_count++; + } +#else + if (fwdtab != NULL) { + for (fip = fwdtab; fip->next != NULL; fip = fip->next) { + ; + } + } +#endif /* SLAVE_FORWARD */ + + while (getword(buf, sizeof(buf), fp)) { + if (strlen(buf) == 0) + break; + dprintf(1, (ddt," %s",buf)); + if (ftp == NULL) + ftp = (struct fwdinfo *)malloc(sizeof(struct fwdinfo)); + if (inet_aton(buf, &ftp->fwdaddr.sin_addr)) { + ftp->fwdaddr.sin_port = ns_port; + ftp->fwdaddr.sin_family = AF_INET; + } else { + syslog(LOG_ERR, "'%s' (ignored, NOT dotted quad)", + buf); + dprintf(1, (ddt, " (ignored, NOT dotted quad)")); + continue; + } +#ifdef FWD_LOOP + if (aIsUs(ftp->fwdaddr.sin_addr)) { + syslog(LOG_ERR, + "Forwarder '%s' ignored, my address", + buf); + dprintf(1, (ddt, " (ignored, my address)")); + continue; + } +#endif /* FWD_LOOP */ + ftp->next = NULL; + if (fwdtab == NULL) + fwdtab = ftp; /* First time only */ + else + fip->next = ftp; + fip = ftp; + ftp = NULL; +#ifdef SLAVE_FORWARD + forward_count++; +#endif /* SLAVE_FORWARD */ + } + if (ftp) + free((char *)ftp); + +#ifdef SLAVE_FORWARD + /* + ** Set the slave retry time to 60 seconds total divided + ** between each forwarder + */ + if (forward_count != 0) { + slave_retry = (int) (60 / forward_count); + if(slave_retry <= 0) + slave_retry = 1; + } +#endif + + dprintf(1, (ddt, "\n")); +#ifdef DEBUG + if (debug > 2) { + for (ftp = fwdtab; ftp != NULL; ftp = ftp->next) { + fprintf(ddt,"ftp x%x [%s] next x%x\n", + ftp, + inet_ntoa(ftp->fwdaddr.sin_addr), + ftp->next); + } + } +#endif +} + +static void +free_forwarders() +{ + register struct fwdinfo *ftp, *fnext; + + for (ftp = fwdtab; ftp != NULL; ftp = fnext) { + fnext = ftp->next; + free((char *)ftp); + } + fwdtab = NULL; +} + +static struct zoneinfo * +find_zone(name, type, class) + char *name; + int type, class; +{ + register struct zoneinfo *zp; + + for (zp = &zones[1]; zp < &zones[nzones]; zp++) { + if (zp->z_type == type && zp->z_class == class && + strcasecmp(name, zp->z_origin) == 0) { + dprintf(2, (ddt, ", old zone (%d)", zp - zones)); + return (zp); + } + } + dprintf(2, (ddt, ", new zone")); + return NULL; +} + +#ifdef DEBUG +/* prints out the content of zones */ +static void +content_zone(end) + int end; +{ + int i; + + for (i = 1; i <= end; i++) { + printzoneinfo(i); + } +} +#endif diff --git a/usr.sbin/named/ns_main.c b/usr.sbin/named/ns_main.c new file mode 100644 index 000000000000..8b04a49caa79 --- /dev/null +++ b/usr.sbin/named/ns_main.c @@ -0,0 +1,1581 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_main.c 4.55 (Berkeley) 7/1/91"; +static char rcsid[] = "$Id: ns_main.c,v 4.9.1.19 1994/07/23 23:23:56 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1989, 1990 + * - + * Copyright (c) 1986, 1989, 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. + * - + * --Copyright-- + */ + +#if !defined(lint) && !defined(SABER) +char copyright[] = +"@(#) Copyright (c) 1986, 1989, 1990 The Regents of the University of California.\n\ + portions Copyright (c) 1993 Digital Equipment Corporation\n\ + portions Copyright (c) 1993 Berkeley Network Software Consortium\n\ + All rights reserved.\n"; +#endif /* not lint */ + +/* + * Internet Name server (see rfc883 & others). + */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/stat.h> +#if !defined(SYSV) && defined(XXX) +#include <sys/wait.h> +#endif /* !SYSV */ +#include <sys/time.h> +#define TIME_H_INCLUDED +#include <sys/resource.h> +#if defined(__osf__) +#define _SOCKADDR_LEN /* XXX - should be in portability.h but that + * would need to be included before socket.h + */ +#endif +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <stdio.h> +#include <syslog.h> +#include <errno.h> +#include <signal.h> +#include <netdb.h> +#include <resolv.h> +#if defined(SVR4) +# include <sys/sockio.h> +#endif + +#define MAIN_PROGRAM +#include "named.h" +#undef MAIN_PROGRAM + +#undef nsaddr + + /* UDP receive, TCP send buffer size */ +static const int rbufsize = 8 * 1024; + +static struct sockaddr_in nsaddr; +static u_int16_t local_ns_port; /* our service port */ +static fd_set mask; /* open descriptors */ +static char **Argv = NULL; +static char *LastArg = NULL; /* end of argv */ + +static struct qstream *sqadd __P((void)); +static void sq_query __P((struct qstream *)), + opensocket __P((struct qdatagram *)), +#ifdef DEBUG + printnetinfo __P((struct netinfo *)), +#endif + setdebug __P((int)); +static int sq_here __P((struct qstream *)); + +static SIG_FN onintr __P(()), + maint_alarm __P(()), + setdumpflg __P(()), + onhup __P(()), +#if defined(QRYLOG) && defined(SIGWINCH) + setQrylogFlg __P(()), +#endif + setIncrDbgFlg __P(()), + setNoDbgFlg __P(()), +#ifdef SIGSYS + sigprof __P(()), +#endif /* SIGSYS */ + setchkptflg __P(()), + setstatsflg __P(()); + +static void +usage() +{ + fprintf(stderr, +"Usage: named [-d #] [-q] [-r] [-p port[/localport]] [[-b] bootfile]\n"); + exit(1); +} + +/*ARGSUSED*/ +void +main(argc, argv, envp) + int argc; + char *argv[], *envp[]; +{ + register int n, udpcnt; + register char *arg; + register struct qstream *sp; + register struct qdatagram *dqp; + struct qstream *nextsp; + int nfds; + const int on = 1; + int len; + int rfd, size; + time_t lasttime, maxctime; + u_char buf[BUFSIZ]; +#ifndef SYSV + struct sigvec vec; +#endif + fd_set tmpmask; + struct timeval t, *tp; + struct qstream *candidate = QSTREAM_NULL; + char **argp; +#ifdef PID_FIX + char oldpid[10]; +#endif +#ifdef WANT_PIDFILE + FILE *fp; /* file descriptor for pid file */ +#endif +#ifdef IP_OPTIONS + u_char ip_opts[50]; /* arbitrary size */ +#endif + + local_ns_port = ns_port = htons(NAMESERVER_PORT); + + /* + ** Save start and extent of argv for ns_setproctitle(). + */ + + Argv = argp = argv; + while (*argp) + argp++; + LastArg = argp[-1] + strlen(argp[-1]); + + (void) umask(022); + /* XXX - should use getopt here */ + while (--argc > 0) { + arg = *++argv; + if (*arg == '-') { + while (*++arg) + switch (*arg) { + case 'b': + if (--argc <= 0) + usage(); + bootfile = savestr(*++argv); + break; + + case 'd': + ++argv; + + if (*argv != 0) { + if (**argv == '-') { + argv--; + break; + } +#ifdef DEBUG + debug = atoi(*argv); +#endif + --argc; + } +#ifdef DEBUG + if (debug <= 0) + debug = 1; + setdebug(1); +#endif + break; + + case 'p': + /* use nonstandard port number. + * usage: -p remote/local + * remote is the port number to which + * we send queries. local is the port + * on which we listen for queries. + * local defaults to same as remote. + */ + if (--argc <= 0) + usage(); + ns_port = htons((u_int16_t) + atoi(*++argv)); + { + char *p = strchr(*argv, '/'); + if (p) { + local_ns_port = + htons((u_int16_t) + atoi(p+1)); + } else { + local_ns_port = ns_port; + } + } + break; + +#ifdef QRYLOG + case 'q': + qrylog = 1; + break; +#endif + + case 'r': + NoRecurse = 1; + break; + + default: + usage(); + } + } else + bootfile = savestr(*argv); + } + +#ifdef DEBUG + if (!debug) +#endif + for (n = getdtablesize() - 1; n > 2; n--) + (void) close(n); /* don't use my_close() here */ +#ifdef DEBUG + else { + fprintf(ddt, "Debug turned ON, Level %d\n",debug); + fprintf(ddt, "Version = %s\n", Version); + fprintf(ddt, "bootfile = %s\n", bootfile); + } +#endif + + n = 0; +#if defined(DEBUG) && defined(LOG_PERROR) + if (debug) + n = LOG_PERROR; +#endif +#ifdef LOG_DAEMON + openlog("named", LOG_PID|LOG_CONS|LOG_NDELAY|n, LOGFAC); +#else + openlog("named", LOG_PID); +#endif + +#ifdef WANT_PIDFILE + /* tuck my process id away */ +#ifdef PID_FIX + fp = fopen(PidFile, "r+"); + if (fp != NULL) { + (void) fgets(oldpid, sizeof(oldpid), fp); + (void) rewind(fp); + fprintf(fp, "%d\n", getpid()); + (void) my_fclose(fp); + } +#else /*PID_FIX*/ + fp = fopen(PidFile, "w"); + if (fp != NULL) { + fprintf(fp, "%d\n", getpid()); + (void) my_fclose(fp); + } +#endif /*PID_FIX*/ +#endif /*WANT_PIDFILE*/ + + syslog(LOG_NOTICE, "starting. %s", Version); + + _res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE); + + nsaddr.sin_family = AF_INET; + nsaddr.sin_addr.s_addr = INADDR_ANY; + nsaddr.sin_port = local_ns_port; + + /* + ** Open stream port. + */ + for (n = 0; ; n++) { + if ((vs = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + syslog(LOG_ERR, "socket(SOCK_STREAM): %m"); + exit(1); + } + if (setsockopt(vs, SOL_SOCKET, SO_REUSEADDR, (char *)&on, + sizeof(on)) != 0) + { + syslog(LOG_ERR, "setsockopt(vs, reuseaddr): %m"); + (void) my_close(vs); + continue; + } + if (bind(vs, (struct sockaddr *)&nsaddr, sizeof(nsaddr)) == 0) + break; + + if (errno != EADDRINUSE || n > 4) { + if (errno == EADDRINUSE) { + syslog(LOG_ERR, + "There may be a name server already running"); + syslog(LOG_ERR, "exiting"); + } else { + syslog(LOG_ERR, "bind(vs, [%s].%d): %m", + inet_ntoa(nsaddr.sin_addr), + ntohs(nsaddr.sin_port)); + } +#if defined(WANT_PIDFILE) && defined(PID_FIX) + /* put old pid back */ + if (atoi(oldpid) && (fp = fopen(PidFile, "w"))) { + fprintf(fp, "%s", oldpid); + (void) my_fclose(fp); + _exit(1); + } +#endif /*WANT_PIDFILE && PID_FIX*/ + exit(1); + } else { /* Retry opening the socket a few times */ + my_close(vs); + sleep(1); + } + } + if (listen(vs, 5) != 0) { + syslog(LOG_ERR, "listen(vs, 5): %m"); + exit(1); + } + + /* + * named would be terminated if one of these is sent and no handler + */ + (void) signal(SIGINT, setdumpflg); + (void) signal(SIGQUIT, setchkptflg); + (void) signal(SIGIOT, setstatsflg); +#if defined(SIGUSR1) && defined(SIGUSR2) + (void) signal(SIGUSR1, setIncrDbgFlg); + (void) signal(SIGUSR2, setNoDbgFlg); +#else /* SIGUSR1&&SIGUSR2 */ + (void) signal(SIGEMT, setIncrDbgFlg); + (void) signal(SIGFPE, setNoDbgFlg); +#endif /* SIGUSR1&&SIGUSR2 */ + +#if defined(SIGWINCH) && defined(QRYLOG) + (void) signal(SIGWINCH, setQrylogFlg); +#endif + + /* + * Get list of local addresses and set up datagram sockets. + */ + getnetconf(); + + /* + ** Initialize and load database. + */ + gettime(&tt); + buildservicelist(); + buildprotolist(); + ns_init(bootfile); +#ifdef DEBUG + if (debug) { + fprintf(ddt, "Network and sort list:\n"); + printnetinfo(nettab); + } +#endif + + time(&boottime); + resettime = boottime; + + (void) signal(SIGHUP, onhup); +#if defined(SIGXFSZ) + (void) signal(SIGXFSZ, onhup); /* wierd DEC Hesiodism, harmless */ +#endif +#if defined(SYSV) + (void) signal(SIGCLD, endxfer); + (void) signal(SIGALRM, maint_alarm); +#else + bzero((char *)&vec, sizeof(vec)); + vec.sv_handler = maint_alarm; + vec.sv_mask = sigmask(SIGCHLD); + (void) sigvec(SIGALRM, &vec, (struct sigvec *)NULL); + + vec.sv_handler = endxfer; + vec.sv_mask = sigmask(SIGALRM); + (void) sigvec(SIGCHLD, &vec, (struct sigvec *)NULL); +#endif /* SYSV */ + (void) signal(SIGPIPE, SIG_IGN); +#ifdef SIGSYS + (void) signal(SIGSYS, sigprof); +#endif /* SIGSYS */ + +#ifdef ALLOW_UPDATES + /* Catch SIGTERM so we can dump the database upon shutdown if it + has changed since it was last dumped/booted */ + (void) signal(SIGTERM, onintr); +#endif + + dprintf(1, (ddt, "database initialized\n")); + t.tv_usec = 0; + + /* + * Fork and go into background now that + * we've done any slow initialization + * and are ready to answer queries. + */ +#ifdef USE_SETSID + if ( +#ifdef DEBUG + !debug || +#endif + !isatty(0)) { + if (fork() > 0) + exit(0); + setsid(); +#ifdef DEBUG + if (!debug) +#endif + { + n = open(_PATH_DEVNULL, O_RDONLY); + (void) dup2(n, 0); + (void) dup2(n, 1); + (void) dup2(n, 2); + if (n > 2) + (void) my_close(n); + } + } +#else +#ifdef DEBUG + if (!debug) +#endif + { +#ifdef HAVE_DAEMON + daemon(1, 0); +#else + switch (fork()) { + case -1: + syslog(LOG_ERR, "fork: %m"); + exit(1); + /*FALLTHROUGH*/ + case 0: + /* child */ + break; + default: + /* parent */ + exit(0); + } + n = open(_PATH_DEVNULL, O_RDONLY); + (void) dup2(n, 0); + (void) dup2(n, 1); + (void) dup2(n, 2); + if (n > 2) + (void) my_close(n); +#ifdef SYSV + setpgrp(); +#else + { + struct itimerval ival; + + /* + * The open below may hang on pseudo ttys if the person + * who starts named logs out before this point. + * + * needmaint may get set inapropriately if the open + * hangs, but all that will happen is we will see that + * no maintenance is required. + */ + bzero((char *)&ival, sizeof(ival)); + ival.it_value.tv_sec = 120; + (void) setitimer(ITIMER_REAL, &ival, + (struct itimerval *)NULL); + n = open(_PATH_TTY, O_RDWR); + ival.it_value.tv_sec = 0; + (void) setitimer(ITIMER_REAL, &ival, + (struct itimerval *)NULL); + if (n > 0) { + (void) ioctl(n, TIOCNOTTY, (char *)NULL); + (void) my_close(n); + } + } +#endif /* SYSV */ +#endif /* HAVE_DAEMON */ + } +#endif /* USE_SETSID */ +#ifdef WANT_PIDFILE + /* tuck my process id away again */ + fp = fopen(PidFile, "w"); + if (fp != NULL) { + fprintf(fp, "%d\n", getpid()); + (void) my_fclose(fp); + } +#endif + + syslog(LOG_NOTICE, "Ready to answer queries.\n"); + prime_cache(); + nfds = getdtablesize(); /* get the number of file descriptors */ + if (nfds > FD_SETSIZE) { + nfds = FD_SETSIZE; /* Bulletproofing */ + syslog(LOG_ERR, "Return from getdtablesize() > FD_SETSIZE"); + } + FD_ZERO(&mask); + FD_SET(vs, &mask); + for (dqp = datagramq; dqp != QDATAGRAM_NULL; dqp = dqp->dq_next) + FD_SET(dqp->dq_dfd, &mask); + for (;;) { +#ifdef DEBUG + if (ddt && debug == 0) { + fprintf(ddt,"Debug turned OFF\n"); + (void) my_fclose(ddt); + ddt = 0; + } +#endif +#ifdef ALLOW_UPDATES + if (needToExit) { + struct zoneinfo *zp; + sigblock(~0); /* + * Block all blockable signals + * to ensure a consistant + * state during final dump + */ + dprintf(1, (ddt, "Received shutdown signal\n")); + for (zp = zones; zp < &zones[nzones]; zp++) { + if (zp->z_flags & Z_CHANGED) + zonedump(zp); + } + exit(0); + } +#endif /* ALLOW_UPDATES */ + if (needreload) { + needreload = 0; + db_reload(); + } + if (needStatsDump) { + needStatsDump = 0; + ns_stats(); + } + if (needzoneload) { + needzoneload = 0; + loadxfer(); + } + if (needmaint) { + needmaint = 0; + ns_maint(); + } + if(needToChkpt) { + needToChkpt = 0; + doachkpt(); + } + if(needToDoadump) { + needToDoadump = 0; + doadump(); + } + /* + ** Wait until a query arrives + */ + if (retryqp != NULL) { + gettime(&tt); + /* + ** The tv_sec field might be unsigned + ** and thus cannot be negative. + */ + if ((int32_t) retryqp->q_time <= tt.tv_sec) { + retry(retryqp); + continue; + } + t.tv_sec = (int32_t) retryqp->q_time - tt.tv_sec; + tp = &t; + } else + tp = NULL; + tmpmask = mask; + n = select(nfds, &tmpmask, (fd_set *)NULL, (fd_set *)NULL, tp); + if (n < 0) { + if (errno != EINTR) { + syslog(LOG_ERR, "select: %m"); + sleep(60); + } + } + if (n <= 0) + continue; + + for (dqp = datagramq; + dqp != QDATAGRAM_NULL; + dqp = dqp->dq_next) { + if (FD_ISSET(dqp->dq_dfd, &tmpmask)) + for (udpcnt = 0; udpcnt < 25; udpcnt++) { /*XXX*/ + from_len = sizeof(from_addr); + if ((n = recvfrom(dqp->dq_dfd, buf, sizeof(buf), 0, + (struct sockaddr *)&from_addr, &from_len)) < 0) + { + if ((n < 0) && (errno == PORT_WOULDBLK)) + break; + syslog(LOG_WARNING, "recvfrom: %m"); + break; + } + if (n == 0) + break; + gettime(&tt); + dprintf(1, (ddt, + "\ndatagram from [%s].%d, fd %d, len %d; now %s", + inet_ntoa(from_addr.sin_addr), + ntohs(from_addr.sin_port), + dqp->dq_dfd, n, + ctime(&tt.tv_sec))); +#ifdef DEBUG + if (debug >= 10) + fp_query(buf, ddt); +#endif + /* + * Consult database to get the answer. + */ + gettime(&tt); + ns_req(buf, n, PACKETSZ, QSTREAM_NULL, &from_addr, + dqp->dq_dfd); + } + } + /* + ** Process stream connection. + ** + ** Note that a "continue" in here takes us back to the select() + ** which, if our accept() failed, will bring us back here. + */ + if (FD_ISSET(vs, &tmpmask)) { + from_len = sizeof(from_addr); + rfd = accept(vs, + (struct sockaddr *)&from_addr, + &from_len); + if (rfd < 0 && errno == EINTR) + continue; + if (rfd < 0 && errno == EMFILE && streamq) { + maxctime = 0; + candidate = NULL; + for (sp = streamq; sp; sp = nextsp) { + nextsp = sp->s_next; + if (sp->s_refcnt) + continue; + gettime(&tt); + lasttime = tt.tv_sec - sp->s_time; + if (lasttime >= VQEXPIRY) + sqrm(sp); + else if (lasttime > maxctime) { + candidate = sp; + maxctime = lasttime; + } + } + if (candidate) + sqrm(candidate); + continue; + } + if (rfd < 0) { + syslog(LOG_WARNING, "accept: %m"); + continue; + } + if ((n = fcntl(rfd, F_GETFL, 0)) < 0) { + syslog(LOG_ERR, "fcntl(rfd, F_GETFL): %m"); + (void) my_close(rfd); + continue; + } + if (fcntl(rfd, F_SETFL, n|PORT_NONBLOCK) != 0) { + syslog(LOG_ERR, "fcntl(rfd, NONBLOCK): %m"); + (void) my_close(rfd); + continue; + } +#if defined(IP_OPTIONS) + len = sizeof ip_opts; + if (getsockopt(rfd, IPPROTO_IP, IP_OPTIONS, + (char *)ip_opts, &len) < 0) { + syslog(LOG_ERR, + "getsockopt(rfd, IP_OPTIONS): %m"); + (void) my_close(rfd); + continue; + } + if (len != 0) { + nameserIncr(from_addr.sin_addr, nssRcvdOpts); + if (!haveComplained((char*) + from_addr.sin_addr.s_addr, + "rcvd ip options")) { + syslog(LOG_NOTICE, + "rcvd IP_OPTIONS from [%s].%d (ignored)", + inet_ntoa(from_addr.sin_addr), + ntohs(from_addr.sin_port)); + } + if (setsockopt(rfd, IPPROTO_IP, IP_OPTIONS, + NULL, 0) < 0) { + syslog(LOG_ERR, + "setsockopt(!IP_OPTIONS): %m"); + (void) my_close(rfd); + continue; + } + } +#endif + if (setsockopt(rfd, SOL_SOCKET, SO_KEEPALIVE, + (char *)&on, sizeof(on)) < 0) { + syslog(LOG_ERR, + "setsockopt(rfd, KEEPALIVE): %m"); + (void) my_close(rfd); + continue; + } + if ((sp = sqadd()) == QSTREAM_NULL) { + (void) my_close(rfd); + continue; + } + sp->s_rfd = rfd; /* stream file descriptor */ + sp->s_size = -1; /* amount of data to receive */ + gettime(&tt); + sp->s_time = tt.tv_sec; /* last transaction time */ + sp->s_from = from_addr; /* address to respond to */ + sp->s_bufp = (u_char *)&sp->s_tempsize; + FD_SET(rfd, &mask); + FD_SET(rfd, &tmpmask); + dprintf(1, (ddt, + "\nTCP connection from [%s].%d (fd %d)\n", + inet_ntoa(sp->s_from.sin_addr), + ntohs(sp->s_from.sin_port), rfd)); + } + if (streamq) { + dprintf(3, (ddt, "streamq = 0x%x\n",streamq)); + } + for (sp = streamq; sp != QSTREAM_NULL; sp = nextsp) { + nextsp = sp->s_next; + if (!FD_ISSET(sp->s_rfd, &tmpmask)) + continue; + dprintf(5, (ddt, + "sp x%x rfd %d size %d time %d next x%x\n", + sp, sp->s_rfd, sp->s_size, + sp->s_time, sp->s_next)); + dprintf(5, (ddt, + "\tbufsize %d buf x%x bufp x%x\n", + sp->s_bufsize, sp->s_buf, sp->s_bufp)); + if (sp->s_size < 0) { + size = INT16SZ + - (sp->s_bufp - (u_char *)&sp->s_tempsize); + while (size > 0 && + (n = read(sp->s_rfd, sp->s_bufp, size)) > 0 + ) { + sp->s_bufp += n; + size -= n; + } + if ((n < 0) && (errno == PORT_WOULDBLK)) + continue; + if (n <= 0) { + sqrm(sp); + continue; + } + if ((sp->s_bufp - (u_char *)&sp->s_tempsize) == + INT16SZ) { + sp->s_size = htons(sp->s_tempsize); + if (sp->s_bufsize == 0) { + if ( (sp->s_buf = (u_char *) + malloc(rbufsize)) + == NULL) { + sp->s_buf = buf; + sp->s_size = sizeof(buf); + } else { + sp->s_bufsize = rbufsize; + } + } + if (sp->s_size > sp->s_bufsize && + sp->s_bufsize != 0 + ) { + sp->s_buf = (u_char *) + realloc((char *)sp->s_buf, + (unsigned)sp->s_size); + if (sp->s_buf == NULL) { + sp->s_buf = buf; + sp->s_bufsize = 0; + sp->s_size = sizeof(buf); + } else { + sp->s_bufsize = sp->s_size; + } + } + sp->s_bufp = sp->s_buf; + } + } + gettime(&tt); + sp->s_time = tt.tv_sec; + while (sp->s_size > 0 && + (n = read(sp->s_rfd, + sp->s_bufp, + sp->s_size) + ) > 0 + ) { + sp->s_bufp += n; + sp->s_size -= n; + } + /* + * we don't have enough memory for the query. + * if we have a query id, then we will send an + * error back to the user. + */ + if (sp->s_bufsize == 0 && + (sp->s_bufp - sp->s_buf > INT16SZ)) { + HEADER *hp; + + hp = (HEADER *)sp->s_buf; + hp->qr = 1; + hp->ra = 1; + hp->ancount = 0; + hp->qdcount = 0; + hp->nscount = 0; + hp->arcount = 0; + hp->rcode = SERVFAIL; + (void) writemsg(sp->s_rfd, sp->s_buf, + HFIXEDSZ); + continue; + } + if ((n == -1) && (errno == PORT_WOULDBLK)) + continue; + if (n <= 0) { + sqrm(sp); + continue; + } + /* + * Consult database to get the answer. + */ + if (sp->s_size == 0) { + nameserIncr(sp->s_from.sin_addr, nssRcvdTCP); + sq_query(sp); + ns_req(sp->s_buf, + sp->s_bufp - sp->s_buf, + sp->s_bufsize, sp, + &sp->s_from, -1); + /* ns_req() can call sqrm() - check for it */ + if (sq_here(sp)) { + sp->s_bufp = (u_char *)&sp->s_tempsize; + sp->s_size = -1; + } + continue; + } + } + } + /* NOTREACHED */ +} + +void +getnetconf() +{ + register struct netinfo *ntp; + struct netinfo *ontp; + struct ifconf ifc; + struct ifreq ifreq, *ifr; + struct qdatagram *dqp; + static int first = 1; + char buf[BUFSIZ], *cp, *cplim; + u_int32_t nm; + time_t my_generation = time(NULL); + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0) { + syslog(LOG_ERR, "get interface configuration: %m - exiting"); + exit(1); + } + ntp = NULL; +#if defined(AF_LINK) && !defined(RISCOS_BSD) +#define my_max(a, b) (a > b ? a : b) +#define my_size(p) my_max((p).sa_len, sizeof(p)) +#else +#define my_size(p) (sizeof (p)) +#endif + cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */ + for (cp = buf; + cp < cplim; + cp += sizeof (ifr->ifr_name) + my_size(ifr->ifr_addr)) { +#undef my_size + ifr = (struct ifreq *)cp; + if (ifr->ifr_addr.sa_family != AF_INET || + ((struct sockaddr_in *) + &ifr->ifr_addr)->sin_addr.s_addr == 0) { + continue; + } + ifreq = *ifr; + /* + * Don't test IFF_UP, packets may still be received at this + * address if any other interface is up. + */ +#if !defined(BSD) || (BSD < 199103) + if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "get interface addr: %m"); + continue; + } +#endif + dprintf(1, (ddt, "considering [%s]\n", + inet_ntoa(((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr))); + /* build datagram queue */ + /* + * look for an already existing source interface address. + * This happens mostly when reinitializing. Also, if + * the machine has multiple point to point interfaces, then + * the local address may appear more than once. + */ + if (dqp = aIsUs(((struct sockaddr_in *)&ifreq.ifr_addr) + ->sin_addr)) { + dprintf(1, (ddt, + "dup interface address %s on %s\n", + inet_ntoa(((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr), + ifreq.ifr_name)); + dqp->dq_gen = my_generation; + continue; + } + + /* + * Skip over address 0.0.0.0 since this will conflict + * with binding to wildcard address later. Interfaces + * which are not completely configured can have this addr. + */ + if (((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr + == 0x00000000) { /* XXX */ + dprintf(1, (ddt, "skipping address 0.0.0.0 on %s\n", + ifreq.ifr_name)); + continue; + } + if ((dqp = (struct qdatagram *) + calloc(1, sizeof(struct qdatagram)) + ) == NULL) { + syslog(LOG_ERR, "getnetconf: malloc: %m"); + exit(12); + } + dqp->dq_next = datagramq; + datagramq = dqp; + dqp->dq_addr = ((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr; + dqp->dq_gen = my_generation; + opensocket(dqp); + dprintf(1, (ddt, "listening [%s]\n", + inet_ntoa(((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr))); + + /* + * Add interface to list of directly-attached (sub)nets + * for use in sorting addresses. + */ + if (ntp == NULL) { + ntp = (struct netinfo *)malloc(sizeof(struct netinfo)); + } + ntp->my_addr = ((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr; +#ifdef SIOCGIFNETMASK + if (ioctl(vs, SIOCGIFNETMASK, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "get netmask: %m"); + ntp->mask = net_mask(ntp->my_addr); + } else + ntp->mask = ((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr.s_addr; +#else + /* 4.2 does not support subnets */ + ntp->mask = net_mask(ntp->my_addr); +#endif + if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "get interface flags: %m"); + continue; + } +#ifdef IFF_LOOPBACK + if (ifreq.ifr_flags & IFF_LOOPBACK) +#else + /* test against 127.0.0.1 (yuck!!) */ + if (ntp->my_addr.s_addr == inet_addr("127.0.0.1")) /* XXX */ +#endif + { + if (netloop.my_addr.s_addr == 0) { + netloop.my_addr = ntp->my_addr; + netloop.mask = 0xffffffff; + netloop.addr = ntp->my_addr.s_addr; + dprintf(1, (ddt, "loopback address: x%lx\n", + netloop.my_addr.s_addr)); + } + continue; + } else if ((ifreq.ifr_flags & IFF_POINTOPOINT)) { + if (ioctl(vs, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "get dst addr: %m"); + continue; + } + ntp->mask = 0xffffffff; + ntp->addr = ((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr.s_addr; + } else { + ntp->addr = ntp->mask & ntp->my_addr.s_addr; + } + /* + * Place on end of list of locally-attached (sub)nets, + * but before logical nets for subnetted nets. + */ + ntp->next = *elocal; + *elocal = ntp; + if (elocal == enettab) + enettab = &ntp->next; + elocal = &ntp->next; + ntp = NULL; + } + if (ntp) + free((char *)ntp); + + /* + * now go through the datagramq and delete anything that + * does not have the current generation number. this is + * how we catch interfaces that go away or change their + * addresses. note that 0.0.0.0 is the wildcard element + * and should never be deleted by this code. + * + * XXX - need to update enettab/elocal as well. + */ + dqflush(my_generation); + + /* + * Create separate qdatagram structure for socket + * wildcard address. + */ + if (first) { + if (!(dqp = (struct qdatagram *)calloc(1, sizeof(*dqp)))) { + syslog(LOG_ERR, "getnetconf: malloc: %m"); + exit(12); + } + dqp->dq_next = datagramq; + datagramq = dqp; + dqp->dq_addr.s_addr = INADDR_ANY; + opensocket(dqp); + ds = dqp->dq_dfd; + } + + /* + * Compute logical networks to which we're connected + * based on attached subnets; + * used for sorting based on network configuration. + */ + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + nm = net_mask(ntp->my_addr); + if (nm != ntp->mask) { + if (findnetinfo(ntp->my_addr)) + continue; + ontp = (struct netinfo *) + malloc(sizeof(struct netinfo)); + if (ontp == NULL) { + syslog(LOG_ERR, "getnetconf: malloc: %m"); + exit(12); + } + ontp->my_addr = ntp->my_addr; + ontp->mask = nm; + ontp->addr = ontp->my_addr.s_addr & nm; + ontp->next = *enettab; + *enettab = ontp; + enettab = &ontp->next; + } + } + first = 0; +} + +/* + * Find netinfo structure for logical network implied by address "addr", + * if it's on list of local/favored networks. + */ +struct netinfo * +findnetinfo(addr) + struct in_addr addr; +{ + register struct netinfo *ntp; + u_int32_t net, mask; + + mask = net_mask(addr); + net = addr.s_addr & mask; + for (ntp = nettab; ntp != NULL; ntp = ntp->next) + if (ntp->addr == net && ntp->mask == mask) + return (ntp); + return ((struct netinfo *) NULL); +} + +#ifdef DEBUG +static void +printnetinfo(ntp) + register struct netinfo *ntp; +{ + for ( ; ntp != NULL; ntp = ntp->next) { + fprintf(ddt,"addr x%lx mask x%lx", ntp->addr, ntp->mask); + fprintf(ddt," my_addr x%lx", ntp->my_addr.s_addr); + fprintf(ddt," %s\n", inet_ntoa(ntp->my_addr)); + } +} +#endif + +static void +opensocket(dqp) + register struct qdatagram *dqp; +{ + int n, m; + int on = 1; + + /* + * Open datagram sockets bound to interface address. + */ + if ((dqp->dq_dfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "socket(SOCK_DGRAM): %m - exiting"); + exit(1); + } + dprintf(1, (ddt, "dqp->dq_addr %s d_dfd %d\n", + inet_ntoa(dqp->dq_addr), dqp->dq_dfd)); + if (setsockopt(dqp->dq_dfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)) != 0) + { + syslog(LOG_ERR, "setsockopt(dqp->dq_dfd, reuseaddr): %m"); + /* XXX press on regardless, this is not too serious. */ + } +#ifdef SO_RCVBUF + m = sizeof(n); + if ((getsockopt(dqp->dq_dfd, SOL_SOCKET, SO_RCVBUF, &n, &m) >= 0) + && (m == sizeof(n)) + && (n < rbufsize)) { + (void) setsockopt(dqp->dq_dfd, SOL_SOCKET, SO_RCVBUF, + (char *)&rbufsize, sizeof(rbufsize)); + } +#endif /* SO_RCVBUF */ + if ((n = fcntl(dqp->dq_dfd, F_GETFL, 0)) < 0) { + syslog(LOG_ERR, "fcntl(dfd, F_GETFL): %m"); + /* XXX press on regardless, but this really is a problem. */ + } else if (fcntl(dqp->dq_dfd, F_SETFL, n|PORT_NONBLOCK) != 0) { + syslog(LOG_ERR, "fcntl(dqp->dq_dfd, non-blocking): %m"); + /* XXX press on regardless, but this really is a problem. */ + } + /* + * NOTE: Some versions of SunOS have problems with the following + * call to bind. Bind still seems to function on these systems + * if you comment out the exit inside the if. This may cause + * Suns with multiple interfaces to reply strangely. + */ + nsaddr.sin_addr = dqp->dq_addr; + if (bind(dqp->dq_dfd, (struct sockaddr *)&nsaddr, sizeof(nsaddr))) { + syslog(LOG_ERR, "bind(dfd=%d, [%s].%d): %m - exiting", + dqp->dq_dfd, inet_ntoa(nsaddr.sin_addr), + ntohs(nsaddr.sin_port)); +#if !defined(sun) + exit(1); +#endif + } +} + +/* +** Set flag saying to reload database upon receiving SIGHUP. +** Must make sure that someone isn't walking through a data +** structure at the time. +*/ + +static SIG_FN +onhup() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGHUP, onhup); +#endif /* SYSV */ + needreload = 1; + errno = save_errno; +} + +/* +** Set flag saying to call ns_maint() +** Must make sure that someone isn't walking through a data +** structure at the time. +*/ + +static SIG_FN +maint_alarm() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGALRM, maint_alarm); +#endif /* SYSV */ + needmaint = 1; + errno = save_errno; +} + + +#ifdef ALLOW_UPDATES +/* + * Signal handler to schedule shutdown. Just set flag, to ensure a consistent + * state during dump. + */ +static SIG_FN +onintr() +{ + needToExit = 1; +} +#endif /* ALLOW_UPDATES */ + +/* + * Signal handler to schedule a data base dump. Do this instead of dumping the + * data base immediately, to avoid seeing it in a possibly inconsistent state + * (due to updates), and to avoid long disk I/O delays at signal-handler + * level + */ +static SIG_FN +setdumpflg() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGINT, setdumpflg); +#endif /* SYSV */ + needToDoadump = 1; + errno = save_errno; +} + +/* +** Turn on or off debuging by open or closeing the debug file +*/ + +static void +setdebug(code) + int code; +{ +#if defined(lint) && !defined(DEBUG) + code = code; +#endif +#ifdef DEBUG + + if (code) { + int n; + + ddt = freopen(debugfile, "w+", stderr); + if ( ddt == NULL) { + syslog(LOG_WARNING, "can't open debug file %s: %m", + debugfile); + debug = 0; + } else { +#if defined(SYSV) + setvbuf(ddt, NULL, _IOLBF, BUFSIZ); +#else + setlinebuf(ddt); +#endif + if ((n = fcntl(fileno(ddt), F_GETFL, 0)) < 0) { + syslog(LOG_WARNING, + "fcntl(ddt, F_GETFL): %m"); + } else { + (void) fcntl(fileno(ddt), F_SETFL, n|O_APPEND); + } + } + } else + debug = 0; + /* delay closing ddt, we might interrupt someone */ +#endif +} + +/* +** Catch a special signal and set debug level. +** +** If debuging is off then turn on debuging else increment the level. +** +** Handy for looking in on long running name servers. +*/ + +static SIG_FN +setIncrDbgFlg() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGUSR1, setIncrDbgFlg); +#endif /* SYSV */ +#ifdef DEBUG + if (debug == 0) { + debug++; + setdebug(1); + } else { + debug++; + } + if (debug) + fprintf(ddt, "Debug turned ON, Level %d\n", debug); +#endif + errno = save_errno; +} + +/* +** Catch a special signal to turn off debugging +*/ + +static SIG_FN +setNoDbgFlg() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGUSR2, setNoDbgFlg); +#endif /* SYSV */ + setdebug(0); + errno = save_errno; +} + +#if defined(QRYLOG) && defined(SIGWINCH) +/* +** Set flag for query logging +*/ +static SIG_FN +setQrylogFlg() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGWINCH, setQrylogFlg); +#endif /* SYSV */ + qrylog = !qrylog; + syslog(LOG_NOTICE, "query log %s\n", qrylog ?"on" :"off"); + errno = save_errno; +} +#endif /*QRYLOG && SIGWINCH*/ + +/* +** Set flag for statistics dump +*/ +static SIG_FN +setstatsflg() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGIOT, setstatsflg); +#endif /* SYSV */ + needStatsDump = 1; + errno = save_errno; +} + +static SIG_FN +setchkptflg() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGQUIT, setchkptflg); +#endif /* SYSV */ + needToChkpt = 1; + errno = save_errno; +} + +/* +** Catch a special signal SIGSYS +** +** this is setup to fork and exit to drop to /usr/tmp/gmon.out +** and keep the server running +*/ + +#ifdef SIGSYS +static SIG_FN +sigprof() +{ + int save_errno = errno; +#if defined(SYSV) + (void)signal(SIGSYS, sigprof); +#endif /* SYSV */ + dprintf(1, (ddt, "sigprof()\n")); + if (fork() == 0) + { + (void) chdir(_PATH_TMPDIR); + exit(1); + } + errno = save_errno; +} +#endif /* SIGSYS */ + +/* +** Routines for managing stream queue +*/ + +static struct qstream * +sqadd() +{ + register struct qstream *sqp; + + if (!(sqp = (struct qstream *)calloc(1, sizeof(struct qstream)))) { + syslog(LOG_ERR, "sqadd: calloc: %m"); + return (QSTREAM_NULL); + } + dprintf(3, (ddt, "sqadd(x%x)\n", sqp)); + + sqp->s_next = streamq; + streamq = sqp; + return (sqp); +} + +/* sqrm(qp) + * remove stream queue structure `qp'. + * no current queries may refer to this stream when it is removed. + * side effects: + * memory is deallocated. sockets are closed. lists are relinked. + */ +void +sqrm(qp) + register struct qstream *qp; +{ + register struct qstream *qsp; + + dprintf(2, (ddt, "sqrm(%#x, %d ) rfcnt=%d\n", + qp, qp->s_rfd, qp->s_refcnt)); + + if (qp->s_bufsize != 0) + free(qp->s_buf); + FD_CLR(qp->s_rfd, &mask); + (void) my_close(qp->s_rfd); + if (qp == streamq) { + streamq = qp->s_next; + } else { + for (qsp = streamq; + qsp && (qsp->s_next != qp); + qsp = qsp->s_next) + ; + if (qsp) { + qsp->s_next = qp->s_next; + } + } + free((char *)qp); +} + +/* void + * sqflush(allbut) + * call sqrm() on all open streams except `allbut' + * side effects: + * global list `streamq' modified + * idiocy: + * is N^2 due to the scan inside of sqrm() + */ +void +sqflush(allbut) + register struct qstream *allbut; +{ + register struct qstream *sp, *spnext; + + for (sp = streamq; sp != NULL; sp = spnext) { + spnext = sp->s_next; + if (sp != allbut) + sqrm(sp); + } +} + +/* void + * dqflush(gen) + * close/deallocate all the udp sockets, unless `gen' != (time_t)0 + * in which case all those not matching this generation will + * be deleted except the 0.0.0.0 element, and syslog() will + * be called whenever something is deleted. + * side effects: + * global list `datagramq' is modified + */ +void +dqflush(gen) + register time_t gen; +{ + register struct qdatagram *dqp, *pqp, *nqp; + + for (pqp = NULL, dqp = datagramq; + dqp != NULL; + pqp = dqp, dqp = nqp) { + nqp = dqp->dq_next; + if (gen != (time_t)0) { + if (dqp->dq_addr.s_addr == INADDR_ANY || + dqp->dq_gen == gen) + continue; + syslog(LOG_CRIT, "interface [%s] missing; deleting", + inet_ntoa(dqp->dq_addr)); + } + if (pqp != NULL) + pqp->dq_next = dqp->dq_next; + else + datagramq = dqp->dq_next; + free(dqp); + } +} + +/* int + * sq_here(sp) + * determine whether stream 'sp' is still on the streamq + * return: + * boolean: is it here? + */ +static int +sq_here(sp) + register struct qstream *sp; +{ + register struct qstream *t; + + for (t = streamq; t != NULL; t = t->s_next) + if (t == sp) + return (1); + return (0); +} + +/* + * Initiate query on stream; + * mark as referenced and stop selecting for input. + */ +static void +sq_query(sp) + register struct qstream *sp; +{ + sp->s_refcnt++; + FD_CLR(sp->s_rfd, &mask); +} + +/* + * Note that the current request on a stream has completed, + * and that we should continue looking for requests on the stream. + */ +void +sq_done(sp) + register struct qstream *sp; +{ + + sp->s_refcnt = 0; + sp->s_time = tt.tv_sec; + FD_SET(sp->s_rfd, &mask); +} + +void +ns_setproctitle(a, s) + char *a; + int s; +{ + int size; + register char *cp; + struct sockaddr_in sin; + char buf[80]; + + cp = Argv[0]; + size = sizeof(sin); + if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) + (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); + else { + syslog(LOG_DEBUG, "getpeername: %m"); + (void) sprintf(buf, "-%s", a); + } + (void) strncpy(cp, buf, LastArg - cp); + cp += strlen(cp); + while (cp < LastArg) + *cp++ = ' '; +} + +u_int32_t +net_mask(in) + struct in_addr in; +{ + register u_int32_t i = ntohl(in.s_addr); + + if (IN_CLASSA(i)) + return (htonl(IN_CLASSA_NET)); + else if (IN_CLASSB(i)) + return (htonl(IN_CLASSB_NET)); + else + return (htonl(IN_CLASSC_NET)); +} + +#if defined(BSD43_BSD43_NFS) +/* junk needed for old Sun NFS licensees */ +#undef dn_skipname +extern char *dn_skipname(); +char *(*hack_skipname)() = dn_skipname; +#endif diff --git a/usr.sbin/named/ns_maint.c b/usr.sbin/named/ns_maint.c new file mode 100644 index 000000000000..927fde3fde58 --- /dev/null +++ b/usr.sbin/named/ns_maint.c @@ -0,0 +1,784 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_maint.c 4.39 (Berkeley) 3/2/91"; +static char rcsid[] = "$Id: ns_maint.c,v 4.9.1.15 1994/06/11 22:04:46 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1988 + * - + * Copyright (c) 1986, 1988 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <sys/wait.h> +#include <stdio.h> +#include <syslog.h> +#include <signal.h> +#include <errno.h> +#include <sys/stat.h> + +#include "named.h" + +#ifdef USE_UTIME +# include <utime.h> +#endif + +static int xfers_running, /* # of xfers running */ + xfers_deferred, /* # of needed xfers not run yet */ + qserials_running, + alarm_pending, /* flag */ + nxfers __P((struct zoneinfo *, int)); + +static void startxfer __P((struct zoneinfo *)), + abortxfer __P((struct zoneinfo *)), + addxfer __P((struct zoneinfo *)), + qserial_query __P((struct zoneinfo *)), + tryxfer __P((void)); + +#define qserial_qfull() (qserials_running == MAXQSERIAL) + +#ifdef CLEANCACHE +static time_t cache_time; +#endif +/* + * Invoked at regular intervals by signal interrupt; refresh all secondary + * zones from primary name server and remove old cache entries. Also, + * ifdef'd ALLOW_UPDATES, dump database if it has changed since last + * dump/bootup. + */ +void +ns_maint() +{ + register struct zoneinfo *zp; + int zonenum; + + gettime(&tt); + + dprintf(1, (ddt, "\nns_maint(); now %s", ctime(&tt.tv_sec))); + + alarm_pending = 0; + for (zp = zones, zonenum = 0; zp < &zones[nzones]; zp++, zonenum++) { +#ifdef DEBUG + if (debug >= 2) + printzoneinfo(zonenum); +#endif + if (tt.tv_sec >= zp->z_time && zp->z_refresh > 0) { + switch (zp->z_type) { + + case Z_CACHE: + doachkpt(); + zp->z_time = tt.tv_sec + zp->z_refresh; + break; + + case Z_SECONDARY: +#ifdef STUBS + case Z_STUB: +#endif + if (zp->z_flags & + (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) { + zp->z_time = tt.tv_sec + zp->z_refresh; + break; + } + if (zp->z_flags & Z_XFER_RUNNING) { + abortxfer(zp); + zp->z_time = tt.tv_sec + zp->z_retry; + break; + } + qserial_query(zp); + break; +#ifdef ALLOW_UPDATES + case Z_PRIMARY: + /* + * Checkpoint the zone if it has changed + * since we last checkpointed + */ + if (zp->z_flags & Z_CHANGED) { + zonedump(zp); + zp->z_time = tt.tv_sec + zp->z_refresh; + } + break; +#endif /* ALLOW_UPDATES */ + } + gettime(&tt); + } + } +#ifdef CLEANCACHE + remove_zone(hashtab, 0, 0); +#endif + if (!needmaint) + sched_maint(); + dprintf(1, (ddt, "exit ns_maint()\n")); +} + +/* + * Find when the next refresh needs to be and set + * interrupt time accordingly. + */ +void +sched_maint() +{ + register struct zoneinfo *zp; + struct itimerval ival; +#ifdef CLEANCACHE + time_t next_refresh = cache_time + 3600; +#else + time_t next_refresh = 0; +#endif + static time_t next_alarm; + + for (zp = zones; zp < &zones[nzones]; zp++) + if (zp->z_time != 0 && + (next_refresh == 0 || next_refresh > zp->z_time)) + next_refresh = zp->z_time; + /* + * Schedule the next call to ns_maint. + * Don't visit any sooner than maint_interval. + */ + bzero((char *)&ival, sizeof (ival)); + if (next_refresh != 0) { + if (next_refresh == next_alarm && alarm_pending) { + dprintf(1, (ddt, "sched_maint: no schedule change\n")); + return; + } + /* + * tv_sec can be an unsigned long, so we can't let + * it go negative. + */ + if (next_refresh < tt.tv_sec) + next_refresh = tt.tv_sec; + ival.it_value.tv_sec = next_refresh - tt.tv_sec; + if ((long) ival.it_value.tv_sec < maint_interval) + ival.it_value.tv_sec = maint_interval; + next_alarm = next_refresh; + alarm_pending = 1; + } + (void) setitimer(ITIMER_REAL, &ival, (struct itimerval *)NULL); + dprintf(1, (ddt, "sched_maint: Next interrupt in %d sec\n", + ival.it_value.tv_sec)); +} + +/* + * Mark a zone "up to date" after named-xfer tells us this or we + * discover it through the qserial_*() logic. + */ +static void +markUpToDate(zp) + struct zoneinfo *zp; +{ + struct stat f_time; + + zp->z_flags &= ~Z_SYSLOGGED; + zp->z_lastupdate = tt.tv_sec; + zp->z_time = tt.tv_sec + zp->z_refresh; + /* + * Restore Z_AUTH in case expired, + * but only if there were no errors + * in the zone file. + */ + if ((zp->z_flags & Z_DB_BAD) == 0) + zp->z_flags |= Z_AUTH; + if (zp->z_source) { +#if defined(USE_UTIME) + struct utimbuf t; + + t.actime = tt.tv_sec; + t.modtime = tt.tv_sec; + (void) utime(zp->z_source, &t); +#else + struct timeval t[2]; + + t[0] = tt; + t[1] = tt; + (void) utimes(zp->z_source, t); +#endif /* USE_UTIME */ + } + /* we use "stat" to set zp->z_ftime instead of just + setting it to tt.tv_sec in order to avoid any + possible rounding problems in utimes(). */ + if (stat(zp->z_source, &f_time) != -1) + zp->z_ftime = f_time.st_mtime; + /* XXX log if stat fails? XXX */ +} + +/* + * Query for the serial number of a zone, so that + * we can check to see if we need to transfer it. + */ +static void +qserial_query(zp) + struct zoneinfo *zp; +{ + struct qinfo *qp; + + dprintf(1, (ddt, "qserial_query(%s)\n", zp->z_origin)); + + if (qserial_qfull()) + return; + + qp = sysquery(zp->z_origin, zp->z_class, T_SOA, + zp->z_addr, zp->z_addrcnt); + if (!qp) { + dprintf(1, (ddt, "qserial_query(%s) FAILED\n", zp->z_origin)); + return; /* XXX - this is bad, we should do something */ + } + qp->q_flags |= Q_ZSERIAL; + qp->q_zquery = zp; + zp->z_flags |= Z_QSERIAL; + zp->z_time = tt.tv_sec + zp->z_refresh; + qserials_running++; + dprintf(1, (ddt, "qserial_query(%s) QUEUED\n", zp->z_origin)); +} + +void +qserial_answer(qp, serial) + struct qinfo *qp; + u_int32_t serial; +{ + struct zoneinfo *zp = qp->q_zquery; + int was_qfull = qserial_qfull(); + + dprintf(1, (ddt, "qserial_answer(%s, %lu)\n", zp->z_origin, serial)); + zp->z_flags &= ~Z_QSERIAL; + qp->q_flags &= ~Q_ZSERIAL; /* keeps us from being called twice */ + qserials_running--; + if (serial == 0) { + /* an error occurred, or the query timed out. + */ +#ifdef GETSER_LOGGING + syslog(GETSER_LOGGING, "Err/TO getting serial# for \"%s\"", + zp->z_origin); +#endif /* GETSER_LOGGING */ + addxfer(zp); + } else if (SEQ_GT(serial, zp->z_serial) || !zp->z_serial) { + dprintf(1, (ddt, "qserial_answer: zone is out of date\n")); + zp->z_xaddr = from_addr.sin_addr; /* don't use qp->q_from */ + addxfer(zp); + } else if (SEQ_GT(zp->z_serial, serial)) { + if (!haveComplained((char*)zp, "went backward")) { + syslog(LOG_NOTICE, + "Zone \"%s\" (class %d) SOA serial# (%lu) rcvd from [%s] is < ours (%lu)\n", + zp->z_origin, zp->z_class, serial, + inet_ntoa(from_addr.sin_addr), + zp->z_serial); + } + } else { + dprintf(1, (ddt, "qserial_answer: zone serial is still OK\n")); + markUpToDate(zp); + } + done: + if (was_qfull) + needmaint = 1; +} + +/* + * Start an asynchronous zone transfer for a zone. + * Depends on current time being in tt. + * The caller must call sched_maint after startxfer. + */ +static void +startxfer(zp) + struct zoneinfo *zp; +{ + static char *argv[NSMAX + 20], argv_ns[NSMAX][MAXDNAME]; + int argc = 0, argc_ns = 0, pid, omask; + unsigned int cnt; + char debug_str[10]; + char serial_str[10]; + char port_str[10]; +#ifdef GEN_AXFR + char class_str[10]; +#endif + + dprintf(1, (ddt, "startxfer() %s\n", zp->z_origin)); + + argv[argc++] = "named-xfer"; + argv[argc++] = "-z"; + argv[argc++] = zp->z_origin; + argv[argc++] = "-f"; + argv[argc++] = zp->z_source; + argv[argc++] = "-s"; + sprintf(serial_str, "%lu", zp->z_serial); + argv[argc++] = serial_str; +#ifdef GEN_AXFR + argv[argc++] = "-C"; + sprintf(class_str, "%d", zp->z_class); + argv[argc++] = class_str; +#endif + if (zp->z_flags & Z_SYSLOGGED) + argv[argc++] = "-q"; + argv[argc++] = "-P"; + sprintf(port_str, "%d", ns_port); + argv[argc++] = port_str; +#ifdef STUBS + if (zp->z_type == Z_STUB) + argv[argc++] = "-S"; +#endif +#ifdef DEBUG + if (debug) { + argv[argc++] = "-d"; + sprintf(debug_str, "%d", debug); + argv[argc++] = debug_str; + argv[argc++] = "-l"; + argv[argc++] = _PATH_XFERDDT; + if (debug > 5) { + argv[argc++] = "-t"; + argv[argc++] = _PATH_XFERTRACE; + } + } +#endif + + if (zp->z_xaddr.s_addr != 0) { + /* address was specified by the qserial logic, use it */ + argv[argc++] = strcpy(argv_ns[argc_ns++], + inet_ntoa(zp->z_xaddr)); + } else { + /* + * Copy the server ip addresses into argv, after converting + * to ascii and saving the static inet_ntoa result + */ + for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { + struct in_addr a; + + a = zp->z_addr[cnt]; + if (aIsUs(a) + && !haveComplained(zp->z_origin, + (char*)startxfer)) { + syslog(LOG_ERR, + "attempted to fetch zone %s from self (%s)", + zp->z_origin, inet_ntoa(a)); + continue; + } + argv[argc++] = strcpy(argv_ns[argc_ns++], + inet_ntoa(a)); + } + } + + argv[argc] = 0; + +#ifdef DEBUG +#ifdef ECHOARGS + if (debug) { + int i; + for (i = 0; i < argc; i++) + fprintf(ddt, "Arg %d=%s\n", i, argv[i]); + } +#endif /* ECHOARGS */ +#endif /* DEBUG */ + +#ifdef SYSV +#define vfork fork +#else + gettime(&tt); + omask = sigblock(sigmask(SIGCHLD)); +#endif + if ((pid = vfork()) == -1) { + syslog(LOG_ERR, "xfer vfork: %m"); +#ifndef SYSV + (void) sigsetmask(omask); +#endif + zp->z_time = tt.tv_sec + 10; + return; + } + + if (pid == 0) { + /* child */ + execv(_PATH_XFER, argv); + syslog(LOG_ERR, "can't exec %s: %m", _PATH_XFER); + _exit(XFER_FAIL); /* avoid duplicate buffer flushes */ + } + /* parent */ + dprintf(1, (ddt, "started xfer child %d\n", pid)); + zp->z_flags &= ~Z_NEED_XFER; + zp->z_flags |= Z_XFER_RUNNING; + zp->z_xferpid = pid; + xfers_running++; + zp->z_time = tt.tv_sec + MAX_XFER_TIME; +#ifndef SYSV + (void) sigsetmask(omask); +#endif +} + +#ifdef DEBUG +void +printzoneinfo(zonenum) +int zonenum; +{ + struct timeval tt; + struct zoneinfo *zp = &zones[zonenum]; + char *ZoneType; + + if (!debug) + return; + + fprintf(ddt, "printzoneinfo(%d):\n", zonenum); + + gettime(&tt); + switch (zp->z_type) { + case Z_PRIMARY: + ZoneType = "Primary"; + break; + case Z_SECONDARY: + ZoneType = "Secondary"; + break; +#ifdef STUBS + case Z_STUB: + ZoneType = "Stub"; + break; +#endif + case Z_CACHE: + ZoneType = "Cache"; + break; + default: + ZoneType = "Unknown"; + break; + } + if (zp->z_origin != NULL && (zp->z_origin[0] == '\0')) + fprintf(ddt, "origin ='.'"); + else + fprintf(ddt, "origin ='%s'", zp->z_origin); +#ifdef GEN_AXFR + fprintf(ddt, ", class = %d", zp->z_class); +#endif + fprintf(ddt, ", type = %s", ZoneType); + if (zp->z_source) + fprintf(ddt,", source = %s\n", zp->z_source); + fprintf(ddt, "z_refresh = %ld", zp->z_refresh); + fprintf(ddt, ", retry = %ld", zp->z_retry); + fprintf(ddt, ", expire = %ld", zp->z_expire); + fprintf(ddt, ", minimum = %ld", zp->z_minimum); + fprintf(ddt, ", serial = %lu\n", zp->z_serial); + fprintf(ddt, "z_time = %d", zp->z_time); + if (zp->z_time) { + fprintf(ddt, ", now time : %d sec", tt.tv_sec); + fprintf(ddt, ", time left: %d sec", zp->z_time - tt.tv_sec); + } + fprintf(ddt, "; flags %x\n", zp->z_flags); +} +#endif /* DEBUG */ + +/* + * remove_zone (htp, zone) -- + * Delete all RR's in the zone "zone" under specified hash table. + */ +void +#ifdef CLEANCACHE +remove_zone(htp, zone, all) +#else +remove_zone(htp, zone) +#endif + register struct hashbuf *htp; + register int zone; +#ifdef CLEANCACHE + register int all; +#endif +{ + register struct databuf *dp, *pdp; + register struct namebuf *np, *pnp, *npn; + struct namebuf **npp, **nppend; + + nppend = htp->h_tab + htp->h_size; + for (npp = htp->h_tab; npp < nppend; npp++) + for (pnp = NULL, np = *npp; np != NULL; np = npn) { + for (pdp = NULL, dp = np->n_data; dp != NULL; ) { +#ifdef CLEANCACHE + if (dp->d_zone == zone && (all || stale(dp))) +#else + if (dp->d_zone == zone) +#endif + dp = rm_datum(dp, np, pdp); + else { + pdp = dp; + dp = dp->d_next; + } + } + + if (np->n_hash) { + /* call recursively to remove subdomains. */ +#ifdef CLEANCACHE + remove_zone(np->n_hash, zone, all); +#else + remove_zone(np->n_hash, zone); +#endif + + /* if now empty, free it */ + if (np->n_hash->h_cnt == 0) { + free((char*)np->n_hash); + np->n_hash = NULL; + } + } + + if ((np->n_hash == NULL) && (np->n_data == NULL)) { + npn = rm_name(np, npp, pnp); + htp->h_cnt--; + } else { + npn = np->n_next; + pnp = np; + } + } +} + +/* + * Handle XFER limit for a nameserver. + */ +static int +nxfers(zp, delta) + struct zoneinfo *zp; + int delta; +{ + struct in_addr nsa; + struct nameser *nsp; + int ret; + + if (zp->z_xaddr.s_addr) + nsa = zp->z_xaddr; /* qserial overrode address */ + else if (!zp->z_addrcnt) + return (-1); + else + nsa = zp->z_addr[0]; /* first ns holds zone's xfer limit */ + + if (!(nsp = nameserFind(nsa, NS_F_INSERT))) + return (-1); /* probably ENOMEM */ + + ret = nsp->xfers; + if (delta < 0 && -delta > ret) + return (-1); /* taking more than we have */ + + nsp->xfers += delta; + return (ret); +} + +/* + * Abort an xfer that has taken too long. + */ +static void +abortxfer(zp) + struct zoneinfo *zp; +{ + kill(zp->z_xferpid, SIGKILL); + syslog(LOG_NOTICE, "zone transfer timeout for \"%s\"; pid %lu killed", + zp->z_origin, (u_long)zp->z_xferpid); + zp->z_time = tt.tv_sec + zp->z_retry; + (void) nxfers(zp, -1); + xfers_running--; +} + +/* + * SIGCHLD signal handler: process exit of xfer's. + * (Note: also called when outgoing transfer completes.) + */ +SIG_FN +endxfer() +{ + register struct zoneinfo *zp; + int exitstatus, pid, xfers, save_errno; +#if defined(sequent) + union wait status; +#else + int status; +#endif /* sequent */ + + save_errno = errno; + xfers = 0; + gettime(&tt); +#if defined(USE_WAITPID) + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { +#else /* USE_WAITPID */ + { + pid = wait(&status); +#endif /* USE_WAITPID */ + exitstatus = WIFEXITED(status) ?WEXITSTATUS(status) :0; + + for (zp = zones; zp < &zones[nzones]; zp++) { + if (zp->z_xferpid != pid) + continue; + xfers++; + xfers_running--; + (void) nxfers(zp, -1); + zp->z_xferpid = 0; + zp->z_flags &= ~Z_XFER_RUNNING; + dprintf(1, (ddt, + "\nendxfer: child %d zone %s returned status=%d termsig=%d\n", + pid, zp->z_origin, exitstatus, + WIFSIGNALED(status) ?WTERMSIG(status) :-1 + ) + ); + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) != SIGKILL) { + syslog(LOG_NOTICE, + "named-xfer exited with signal %d\n", + WTERMSIG(status)); + } + zp->z_time = tt.tv_sec + zp->z_retry; + } else { + switch (exitstatus) { + case XFER_UPTODATE: + markUpToDate(zp); + break; + + case XFER_SUCCESS: + zp->z_flags |= Z_NEED_RELOAD; + zp->z_flags &= ~Z_SYSLOGGED; + needzoneload++; + break; + + case XFER_TIMEOUT: + dprintf(1, (ddt, + "zoneref: Masters for secondary zone %s unreachable\n", + zp->z_origin)); + if (!(zp->z_flags & Z_SYSLOGGED)) { + zp->z_flags |= Z_SYSLOGGED; + syslog(LOG_NOTICE, + "zoneref: Masters for secondary zone %s unreachable", + zp->z_origin); + } + zp->z_time = tt.tv_sec + zp->z_retry; + break; + + default: + if (!(zp->z_flags & Z_SYSLOGGED)) { + zp->z_flags |= Z_SYSLOGGED; + syslog(LOG_NOTICE, + "named-xfer for %s exited %d", + zp->z_origin, exitstatus); + } + /* FALLTHROUGH */ + case XFER_FAIL: + zp->z_flags |= Z_SYSLOGGED; + zp->z_time = tt.tv_sec + zp->z_retry; + break; + } /*switch*/ + break; + } /*if/else*/ + } /*for*/ + } /*while*/ + tryxfer(); +#if defined(SYSV) + (void)signal(SIGCLD, endxfer); +#endif + errno = save_errno; +} + +/* + * Try to start some xfers + */ +static void +tryxfer() { + struct zoneinfo *zp; + + for (zp = zones; zp < &zones[nzones]; zp++) { + int xfers; + + if (!xfers_deferred || xfers_running >= max_xfers_running) + break; + + if ((xfers = nxfers(zp, 0)) != -1 && + xfers < MAX_XFERS_PERNS && + (zp->z_flags & Z_NEED_XFER)) { + nxfers(zp, 1); + xfers_deferred--; + startxfer(zp); + } + } + if (!needmaint) + sched_maint(); +} + +/* + * Reload zones whose transfers have completed. + */ +void +loadxfer() +{ + register struct zoneinfo *zp; + + gettime(&tt); + for (zp = zones; zp < &zones[nzones]; zp++) { + if (zp->z_flags & Z_NEED_RELOAD) { + dprintf(1, (ddt, "loadxfer() \"%s\"\n", + zp->z_origin[0] ? zp->z_origin : ".")); + zp->z_flags &= ~(Z_NEED_RELOAD|Z_AUTH); +#ifdef CLEANCACHE + remove_zone(hashtab, zp - zones, 1); +#else + remove_zone(hashtab, zp - zones); +#endif + if (db_load(zp->z_source, zp->z_origin, zp, 0) == 0) + zp->z_flags |= Z_AUTH; + if (zp->z_flags & Z_TMP_FILE) + (void) unlink(zp->z_source); + syslog(LOG_INFO, + "Zone \"%s\" (class %d) xfer'd and loaded (serial %lu)", + zp->z_origin, zp->z_class, zp->z_serial); + } + } + if (!needmaint) + sched_maint(); +} + +/* + * Add this zone to the set of those needing transfers. + */ +static void +addxfer(zp) + struct zoneinfo *zp; +{ + if (!(zp->z_flags & Z_NEED_XFER)) { + zp->z_flags |= Z_NEED_XFER; + xfers_deferred++; + tryxfer(); + } +} diff --git a/usr.sbin/named/ns_ncache.c b/usr.sbin/named/ns_ncache.c new file mode 100644 index 000000000000..54083428f979 --- /dev/null +++ b/usr.sbin/named/ns_ncache.c @@ -0,0 +1,152 @@ +/************************************************************************** + * ns_ncache.c + * author: anant kumar + * last modification: March 17, 1993 + * + * implements negative caching + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <syslog.h> +#include <errno.h> +#include <stdio.h> +#include <resolv.h> + +#include "named.h" + +#ifdef NCACHE + +void +cache_n_resp(msg, msglen) + u_char *msg; + int msglen; +{ + register struct databuf *dp; + HEADER *hp; + u_char *cp; + char dname[MAXDNAME]; + int n; + int type, class; + int Vcode; + int flags; + + nameserIncr(from_addr.sin_addr, nssRcvdNXD); + + hp = (HEADER *)msg; + cp = msg+HFIXEDSZ; + + n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); + if (n < 0) { + dprintf(1, (ddt, "Query expand name failed:cache_n_resp\n")); + hp->rcode = FORMERR; + return; + } + cp += n; + GETSHORT(type, cp); + GETSHORT(class, cp); + dprintf(1, (ddt, + "ncache: dname %s, type %d, class %d\n", + dname, type, class)); + +#ifdef VALIDATE + Vcode = validate(dname, &from_addr, type, class, NULL, 0, + hp->rcode == NXDOMAIN ? NXDOMAIN : NOERROR_NODATA); + if (Vcode == INVALID || Vcode == VALID_NO_CACHE) { + /*Valid_no_cache should never occur but doesn't hurt to check*/ + return; + } +#endif +#ifdef RETURNSOA + if (hp->rcode==NXDOMAIN) { + u_int32_t ttl; + u_int16_t atype; + u_char * tp = cp; + u_char * cp1; + u_char data[BUFSIZ+MAXDNAME]; + int len = sizeof(data); + + /* store ther SOA record */ + if (!hp->nscount) { + dprintf(3, (ddt, "ncache: nscount == 0\n")); + return; + } + n = dn_skipname(tp, msg + msglen); + if (n < 0) { + dprintf(3, (ddt, "ncache: form error\n")); + return; + } + tp += n; + GETSHORT(atype,tp); /* type */ + if (atype != T_SOA) { + dprintf(3, (ddt, "ncache: type (%d) != T_SOA\n",atype)); + return; + } + tp += sizeof(u_int16_t); /* class */ + GETLONG(ttl,tp); /* ttl */ + tp += sizeof(u_int16_t); /* dlen */ + + if ((n = dn_expand(msg, msg + msglen, tp, data, len)) + < 0 ) { + dprintf(3, (ddt, "ncache: form error 2\n")); + return; + } /* origin */ + tp += n; + cp1 = data + (n = strlen(data) + 1); + len -= n; + if ((n = dn_expand(msg, msg + msglen, tp, cp1, len)) < 0 ) { + dprintf(3, (ddt, "ncache: form error 2\n")); + return; + } /* mail */ + tp += n; + n = strlen(cp1) + 1; + cp1 += n; + len -= n; + bcopy(tp, cp1, n = 5 * sizeof(u_int32_t)); + /* serial, refresh, retry, expire, min */ + cp1 += n; + len -= n; + /* store the zone of the soa record */ + if ((n = dn_expand(msg, msg + msglen, cp, cp1, len)) < 0 ) { + dprintf(3, (ddt, "ncache: form error 2\n")); + return; + } + n = strlen(cp1) + 1; + cp1 += n; + + dp = savedata(class, T_SOA, MIN(ttl,NTTL)+tt.tv_sec, data, + cp1 - data); + } else { +#endif + dp = savedata(class, type, NTTL+tt.tv_sec, NULL, 0); +#ifdef RETURNSOA + } +#endif + dp->d_zone = DB_Z_CACHE; + dp->d_cred = hp->aa ? DB_C_AUTH : DB_C_ANSWER; + dp->d_clev = 0; + if(hp->rcode == NXDOMAIN) { + dp->d_rcode = NXDOMAIN; + flags = DB_NODATA|DB_NOTAUTH|DB_NOHINTS; + } else { + dp->d_rcode = NOERROR_NODATA; + flags = DB_NOTAUTH|DB_NOHINTS; + } + + if ((n = db_update(dname,dp,dp,flags,hashtab)) != OK) { + dprintf(1, (ddt, + "db_update failed return value:%d, cache_n_resp()\n", + n)); + free((char *)dp); + return; + } + dprintf(4, (ddt, + "ncache succeeded: d:%s, t:%d, c:%d rcode:%d ttl:%d\n", + dname,type,class,dp->d_rcode, dp->d_ttl-tt.tv_sec)); + return; +} + +#endif /*NCACHE*/ diff --git a/usr.sbin/named/ns_req.c b/usr.sbin/named/ns_req.c new file mode 100644 index 000000000000..afc3fc5af271 --- /dev/null +++ b/usr.sbin/named/ns_req.c @@ -0,0 +1,1793 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_req.c 4.47 (Berkeley) 7/1/91"; +static char rcsid[] = "$Id: ns_req.c,v 4.9.1.22 1994/07/23 23:23:56 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1988, 1990 + * - + * Copyright (c) 1986, 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. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <syslog.h> +#include <errno.h> +#include <stdio.h> +#include <resolv.h> + +#include "named.h" + +struct addinfo { + char *a_dname; /* domain name */ + u_int a_class; /* class for address */ +}; + +enum req_action { Finish, Refuse, Return }; + +static enum req_action req_query __P((HEADER *hp, u_char **cpp, u_char *eom, + struct qstream *qsp, + int *buflenp, int *msglenp, + u_char *msg, int dfd, + struct sockaddr_in *from)); + +#ifdef INVQ +static enum req_action req_iquery __P((HEADER *hp, u_char **cpp, u_char *eom, + int *buflenp, u_char *msg, + struct sockaddr_in *from)); +#endif + +static void fwritemsg __P((FILE *, u_char *, int)), + doaxfr __P((struct namebuf *, FILE *, + struct namebuf *, int)), + startxfr __P((struct qstream *, struct namebuf *, + u_char *, int, int, const char *)), + printSOAdata __P((struct databuf)); + +#ifdef ALLOW_UPDATES +static int InitDynUpdate __P((register HEADER *hp, + char *msg, + int msglen, + u_char *startcp, + struct sockaddr_in *from, + struct qstream *qsp, + int dfd)); +#endif + +static struct addinfo addinfo[NADDRECS]; +static void addname __P((char *, u_int16_t)); + +/* + * Process request using database; assemble and send response. + */ +void +ns_req(msg, msglen, buflen, qsp, from, dfd) + u_char *msg; + int msglen, buflen; + struct qstream *qsp; + struct sockaddr_in *from; + int dfd; +{ + register HEADER *hp = (HEADER *) msg; + u_char *cp, *eom; + enum req_action action; + +#ifdef DEBUG + if (debug > 3) { + fprintf(ddt, "ns_req(from=[%s].%d)\n", + inet_ntoa(from->sin_addr), ntohs(from->sin_port)); + fp_query(msg, ddt); + } +#endif + + /* + * XXX - this decision should be made by our caller, not by us. + */ + if (hp->qr) { + ns_resp(msg, msglen); + + /* Now is a safe time for housekeeping */ + if (needs_prime_cache) + prime_cache(); + + return; + } + + /* its a query and these bits have no business + * being set. will later simplify work if we can + * safely assume these are always 0 when a query + * comes in + */ + hp->aa = hp->ra = 0; + + hp->rcode = NOERROR; + cp = msg + HFIXEDSZ; + eom = msg + msglen; + + dnptrs[0] = msg; + dnptrs[1] = NULL; + + free_addinfo(); /* sets addcount to zero */ + dnptrs[0] = NULL; + + switch (hp->opcode) { + case QUERY: + action = req_query(hp, &cp, eom, qsp, + &buflen, &msglen, + msg, dfd, from); + break; + +#ifdef INVQ + case IQUERY: + action = req_iquery(hp, &cp, eom, &buflen, msg, from); + break; +#endif + +#ifdef ALLOW_UPDATES +#define FORWARDED 1000 +/* + * In a sense the following constant should be defined in <arpa/nameser.h>, + * since it is returned here in place of a response code if the update was + * forwarded, and the response codes are defined in nameser.h. On the other + * hand, though, this constant is only seen in this file. The assumption + * here is that none of the other return codes equals this one (a good + * assumption, since they only occupy 4 bits over-the-wire) + */ + /* Call InitDynUpdate for all dynamic update requests */ + case UPDATEM: + case UPDATEMA: + case UPDATED: + case UPDATEDA: + case UPDATEA: + n = InitDynUpdate(hp, msg, msglen, cp, from, qsp, dfd); + if (n == FORWARDED) { + /* Return directly because InitDynUpdate + * forwarded the query to the primary, so we + * will send response later + */ + action = Return; + } else { + /* Either sucessful primary update or failure; + * return response code to client + */ + action = Finish; + } +#endif /* ALLOW_UPDATES */ + + case ZONEREF: + dprintf(1, (ddt, "Refresh Zone\n")); + /*FALLTHROUGH*/ + + default: + dprintf(1, (ddt, "ns_req: Opcode %d not implemented\n", + hp->opcode)); + /* XXX - should syslog, limited by haveComplained */ + hp->qdcount = 0; + hp->ancount = 0; + hp->nscount = 0; + hp->arcount = 0; + hp->rcode = NOTIMP; + action = Finish; + } + + /* + * vector via internal opcode. (yes, it was even uglier before.) + */ + switch (action) { + case Return: + return; + case Refuse: + hp->rcode = REFUSED; + /*FALLTHROUGH*/ + case Finish: + /* rest of the function handles this case */ + break; + default: + syslog(LOG_CRIT, "bad action variable in ns_req() -- %d", + (int) action); + return; /* XXX - should really exit here */ + } + + /* + * apply final polish + */ + hp->qr = 1; /* set Response flag */ + if (NoRecurse) + hp->ra = 0; /* No recursion; maybe we're a root server */ + else + hp->ra = 1; /* Recursion is Available */ + hp->ancount = htons(hp->ancount); + if (addcount) { + int n = doaddinfo(hp, cp, buflen); + cp += n; + buflen -= n; + } + + dprintf(1, (ddt, "ns_req: answer -> [%s].%d fd=%d id=%d %s\n", + inet_ntoa(from->sin_addr), + ntohs(from->sin_port), + (qsp == QSTREAM_NULL) ?dfd :qsp->s_rfd, + ntohs(hp->id), local(from) == NULL ? "Remote" : "Local")); +#ifdef DEBUG + if (debug >= 10) + fp_query(msg, ddt); +#endif + if (qsp == QSTREAM_NULL) { + if (sendto(dfd, msg, cp - msg, 0, + (struct sockaddr *)from, + sizeof(*from)) < 0) { + if (!haveComplained((char*)from->sin_addr.s_addr, + sendtoStr)) + syslog(LOG_NOTICE, + "ns_req: sendto([%s].%d): %m", + inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + nameserIncr(from->sin_addr, nssSendtoErr); + } + nameserIncr(from->sin_addr, nssSentAns); + } else { + (void) writemsg(qsp->s_rfd, msg, cp - msg); + sq_done(qsp); + } + + if (needs_prime_cache) { + prime_cache(); /* Now is a safe time */ + } +} + +static enum req_action +req_query(hp, cpp, eom, qsp, buflenp, msglenp, msg, dfd, from) + HEADER *hp; + u_char **cpp; + u_char *eom; + struct qstream *qsp; + u_char *msg; + int *buflenp, *msglenp, dfd; + struct sockaddr_in *from; +{ + int n, class, type, count, foundname, founddata, omsglen, cname; + u_int16_t id; + u_char **dpp, *omsg, *answers; + char dnbuf[MAXDNAME], *dname, *fname; + struct hashbuf *htp; + struct databuf *nsp[NSMAX]; + struct namebuf *np; + struct qinfo *qp; + struct netinfo *lp; + + nameserIncr(from->sin_addr, nssRcvdQ); +#ifdef DATUMREFCNT + nsp[0] = NULL; +#endif + + dpp = dnptrs; + *dpp++ = msg; + *dpp = NULL; + + /* valid queries have one question and zero answers */ + if ((ntohs(hp->qdcount) != 1) + || hp->ancount + || hp->nscount + || hp->arcount) { + dprintf(1, (ddt, "FORMERR Query header counts wrong\n")); + hp->qdcount = 0; + hp->ancount = 0; + hp->nscount = 0; + hp->arcount = 0; + hp->rcode = FORMERR; + return (Finish); + } + + /* + * Get domain name, class, and type. + */ + if ((**cpp & INDIR_MASK) == 0) { + *dpp++ = *cpp; /* remember name for compression */ + } + *dpp = NULL; + n = dn_expand(msg, eom, *cpp, dnbuf, sizeof dnbuf); + if (n < 0) { + dprintf(1, (ddt, "FORMERR Query expand name failed\n")); + hp->rcode = FORMERR; + return (Finish); + } + *cpp += n; + GETSHORT(type, *cpp); + GETSHORT(class, *cpp); + if (*cpp > eom) { + dprintf(1, (ddt, "FORMERR Query message length short\n")); + hp->rcode = FORMERR; + return (Finish); + } + if (*cpp < eom) { + dprintf(6, (ddt,"message length > received message\n")); + *msglenp = *cpp - msg; + } + + qtypeIncr(type); + + /* + * Process query. + */ + if (type == T_AXFR) { + /* refuse request if not a TCP connection */ + if (qsp == QSTREAM_NULL) { + syslog(LOG_INFO, + "rejected UDP AXFR from [%s].%u for \"%s\"", + inet_ntoa(from->sin_addr), + ntohs(from->sin_port), + *dnbuf ? dnbuf : "."); + return (Refuse); + } + /* the position of this is subtle. */ + nameserIncr(from->sin_addr, nssRcvdAXFR); +#ifdef XFRNETS + if (xfrnets) { + /* if xfrnets was specified, peer address + * must be on it. should probably allow + * for negation some day. + */ + if (!addr_on_netlist(from->sin_addr, xfrnets)) { + syslog(LOG_INFO, + "unapproved AXFR from [%s].%u for %s", + inet_ntoa(from->sin_addr), + ntohs(from->sin_port), + *dnbuf ? dnbuf : "."); + return (Refuse); + } + } +#endif /*XFRNETS*/ + dnptrs[0] = NULL; /* don't compress names */ + hp->rd = 0; /* recursion not possible */ + syslog(LOG_INFO, "approved AXFR from [%s].%d for \"%s\"", + inet_ntoa(from->sin_addr), + ntohs(from->sin_port), + *dnbuf ? dnbuf : "."); + } + *buflenp -= *msglenp; + count = 0; + foundname = 0; + founddata = 0; + dname = dnbuf; + cname = 0; + +#ifdef QRYLOG + if (qrylog) { + syslog(LOG_INFO, "XX /%s/%s/%s", + inet_ntoa(from->sin_addr), + (dname[0] == '\0') ?"." :dname, + p_type(type)); + } +#endif /*QRYLOG*/ + +try_again: + dprintf(1, (ddt, "req: nlookup(%s) id %d type=%d\n", + dname, hp->id, type)); + htp = hashtab; /* lookup relative to root */ + if ((np = nlookup(dname, &htp, &fname, 0)) == NULL) + fname = ""; + dprintf(1, (ddt, "req: %s '%s' as '%s' (cname=%d)\n", + np == NULL ? "missed" : "found", + dname, fname, cname)); + +#ifdef LOCALDOM + /* + * if nlookup failed to find the name then + * see if there are any '.''s in the name + * if not then add local domain name to the + * name and try again. + */ + if (!np && localdomain && !strchr(dname, '.')) { + (void) strcat(dname, "."); + (void) strcat(dname, localdomain); + dprintf(1, (ddt,"req: nlookup(%s) type=%d\n", dname, type)); + htp = hashtab; + np = nlookup(dname, &htp, &fname, 0); + } +#endif /*LOCALDOM*/ + +#ifdef YPKLUDGE + /* Some braindamaged resolver software will not + recognize internet addresses in dot notation and + send out address queries for "names" such as + 128.93.8.1. This kludge will prevent those + from flooding higher level servers. + We simply claim to be authoritative and that + the domain doesn't exist. + Note that we could return the address but we + don't do that in order to encourage that broken + software is fixed. + */ + + if (!np && type == T_A && class == C_IN && dname) { + struct in_addr ina; + + if (inet_aton(dname, &ina)) { + hp->rcode = NXDOMAIN; + hp->aa = 1; + dprintf(3, (ddt, "ypkludge: hit as '%s'\n", dname)); + return (Finish); + } + } +#endif /*YPKLUDGE*/ + + if ((!np) || (fname != dname)) + goto fetchns; + +#ifdef SECURE_ZONES + if (np->n_data) { + struct zoneinfo *zp; + + zp = &zones[np->n_data->d_zone]; + if (zp->secure_nets + && !addr_on_netlist(from->sin_addr, zp->secure_nets)) { + dprintf(1, (ddt, + "REFUSED Unauthorized request from %s\n", + inet_ntoa(from->sin_addr))); + syslog(LOG_INFO, "Unauthorized request %s from %s", + dname, inet_ntoa(from->sin_addr)); + return (Refuse); + } + } +#endif + foundname++; + answers = *cpp; + count = *cpp - msg; + +#ifdef NCACHE + /* if this is a NXDOMAIN, will have only one databuf + * whose d_rcode field will be NXDOMAIN. So we can go home + * right here. -ve $ing: anant@isi.edu + */ + if (np->n_data != NULL && !stale(np->n_data)) { + if (np->n_data->d_rcode == NXDOMAIN) { +#ifdef RETURNSOA + n = finddata(np, class, T_SOA, hp, &dname, + buflenp, &count); + if (n != 0 ) { + if (hp->rcode == NOERROR_NODATA) { + /* this should not occur */ + hp->rcode = NOERROR; + return (Finish); + } + *cpp += n; + *buflenp -= n; + *msglenp += n; + hp->rcode = NXDOMAIN; + hp->nscount = htons((u_int16_t)count); + hp->aa = 1; + return (Finish); + } + else + goto fetchns; +#else + hp->rcode = NXDOMAIN; + hp->aa = 1; + return (Finish); +#endif + } + } + + /* if not NXDOMAIN, the NOERROR_NODATA record might be + * anywhere in the chain. have to go through the grind. + */ +#endif /*NCACHE*/ + + n = finddata(np, class, type, hp, &dname, buflenp, &count); + if (n == 0) { + /* NO data available. Refuse AXFR requests, or + * look for better servers for other requests. + */ + if (type == T_AXFR) { + dprintf(1, (ddt, "T_AXFR refused: no data\n")); + return (Refuse); + } else { + goto fetchns; + } + } + +#ifdef NCACHE + if (hp->rcode == NOERROR_NODATA) { + hp->rcode = NOERROR; + founddata = 1; + return (Finish); + } +#endif + + *cpp += n; + *buflenp -= n; + *msglenp += n; + hp->ancount += count; + if (fname != dname && type != T_CNAME && type != T_ANY) { + if (cname++ >= MAXCNAMES) { + dprintf(3, (ddt, + "resp: leaving, MAXCNAMES exceeded\n")); + hp->rcode = SERVFAIL; + return (Finish); + } + goto try_again; + } + founddata = 1; + dprintf(3, (ddt, + "req: foundname=%d, count=%d, founddata=%d, cname=%d\n", + foundname, count, founddata, cname)); + + if ((lp = local(from)) != NULL) + sort_response(answers, count, lp, *cpp); + if (type == T_AXFR) { + hp->ancount = htons(hp->ancount); + startxfr(qsp, np, msg, *cpp - msg, class, dname); + sqrm(qsp); + return (Return); + } + +#ifdef notdef + /* + * If we found an authoritative answer, + * we're done. + */ + if (hp->aa) + return (Finish); +#endif + +fetchns: + /* + * Look for name servers to refer to and fill in the authority + * section or record the address for forwarding the query + * (recursion desired). + */ +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + nsp[0] = NULL; + count = 0; + switch (findns(&np, class, nsp, &count, 0)) { + case NXDOMAIN: + if (!foundname) { + hp->rcode = NXDOMAIN; + } + dprintf(3, (ddt, "req: leaving (%s, rcode %d)\n", + dname, hp->rcode)); + if (class != C_ANY) { + hp->aa = 1; + /* XXX: should return SOA if founddata == 0, + * but old named's are confused by an SOA + * in the auth. section if there's no error. + */ + if (foundname == 0 && np) { + n = doaddauth(hp, *cpp, *buflenp, np, nsp[0]); + *cpp += n; + *buflenp -= n; +#ifdef ADDAUTH + } else if (hp->ancount) { + /* don't add NS records for NOERROR NODATA + as some severs can get confused */ +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + switch (findns(&np, class, nsp, &count, 1)) { + case NXDOMAIN: + case SERVFAIL: + break; + default: + if (np) { + n = add_data(np, nsp, *cpp, + *buflenp); + if (n < 0) { + hp->tc = 1; + n = (-n); + } + *cpp += n; + *buflenp -= n; + hp->nscount = + htons((u_int16_t) + count); + } + } +#endif /*ADDAUTH*/ + } + } +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (Finish); + + case SERVFAIL: + if (!founddata && !(forward_only && fwdtab)) { + hp->rcode = SERVFAIL; +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (Finish); + } + } + + /* + * If we successfully found the answer in the cache, + * or this is not a recursive query, or we are purposely + * never recursing, then add the nameserver references + * ("authority section") here and we're done. + */ + if (founddata || (!hp->rd) || NoRecurse) { + n = add_data(np, nsp, *cpp, *buflenp); + if (n < 0) { + hp->tc = 1; + n = (-n); + } + *cpp += n; + *buflenp -= n; + hp->nscount = htons((u_int16_t)count); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (Finish); + } + + /* + * At this point, we don't have the answer, but we do + * have some NS's to try. If the user would like us + * to recurse, create the initial query. If a cname + * is involved, we need to build a new query and save + * the old one in cmsg/cmsglen. + */ + if (cname) { + omsg = (u_char *)malloc((unsigned) *msglenp); + if (omsg == (u_char *)NULL) { + dprintf(1, (ddt, "ns_req: malloc fail\n")); + syslog(LOG_ERR, "ns_req: Out Of Memory"); + hp->rcode = SERVFAIL; +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (Finish); + } + id = hp->id; + hp->ancount = htons(hp->ancount); + omsglen = *msglenp; + bcopy(msg, omsg, omsglen); + *msglenp = res_mkquery(QUERY, dname, class, type, + NULL, 0, NULL, msg, + *msglenp + *buflenp); + } + n = ns_forw(nsp, msg, *msglenp, from, qsp, dfd, &qp, dname, np); + if (n != FW_OK && cname) + free(omsg); + switch (n) { + case FW_OK: + if (cname) { + qp->q_cname = cname; + qp->q_cmsg = omsg; + qp->q_cmsglen = omsglen; + qp->q_id = id; + } + break; + case FW_DUP: + break; /* Duplicate request dropped */ + case FW_NOSERVER: + /* + ** Don't go into an infinite loop if + ** the admin gave root NS records in the cache + ** file without giving address records + ** for the root servers. + */ + if (np) { + if (np->n_dname[0] == '\0') { + dprintf(1, (ddt, + "ns_req: no address for root NS\n" + )); + syslog(LOG_ERR, + "ns_req: no address for root server"); + hp->rcode = SERVFAIL; +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (Finish); + } +#ifdef VALIDATE + /* + * we need to kill all the NS records here as + * validate will fail as we are talking to the parent + * server + */ + delete_all(np, class, T_NS); +#endif + np = np->n_parent; + } + goto fetchns; /* Try again. */ + case FW_SERVFAIL: + hp->rcode = SERVFAIL; +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (Finish); + } +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (Return); +} + +#ifdef INVQ +static enum req_action +req_iquery(hp, cpp, eom, buflenp, msg, from) + HEADER *hp; + u_char **cpp, *eom; + int *buflenp; + u_char *msg; + struct sockaddr_in *from; +{ + register struct invbuf *ip; + int dlen, alen, i, n, type, class, count; + char dnbuf[MAXDNAME], anbuf[PACKETSZ], *data, *fname; + struct namebuf *np; + struct qinfo *qp; + struct databuf *dp; + + nameserIncr(from->sin_addr, nssRcvdIQ); + + hp->ancount = htons(hp->ancount); + if ((hp->ancount != 1) + || hp->qdcount + || hp->nscount + || hp->arcount) { + dprintf(1, (ddt, "FORMERR IQuery header counts wrong\n")); + hp->qdcount = 0; + hp->ancount = 0; + hp->nscount = 0; + hp->arcount = 0; + hp->rcode = FORMERR; + return (Finish); + } + + /* + * Skip domain name, get class, and type. + */ + if ((n = dn_skipname(*cpp, eom)) < 0) { + dprintf(1, (ddt, "FORMERR IQuery packet name problem\n")); + hp->rcode = FORMERR; + return (Finish); + } + *cpp += n; + GETSHORT(type, *cpp); + GETSHORT(class, *cpp); + *cpp += INT32SZ; /* ttl */ + GETSHORT(dlen, *cpp); + *cpp += dlen; + if (*cpp != eom) { + dprintf(1, (ddt, "FORMERR IQuery message length off\n")); + hp->rcode = FORMERR; + return (Finish); + } + + /* + * not all inverse queries are handled. + */ + switch (type) { + case T_A: + case T_UID: + case T_GID: + break; + default: + return (Refuse); + } + dprintf(1, (ddt, "req: IQuery class %d type %d\n", class, type)); + + fname = (char *)msg + HFIXEDSZ; + bcopy(fname, anbuf, alen = (char *)*cpp - fname); + data = anbuf + alen - dlen; + *cpp = (u_char *)fname; + *buflenp -= HFIXEDSZ; + count = 0; + for (ip = invtab[dhash((u_char *)data, dlen)]; + ip != NULL; + ip = ip->i_next) { + for (i = 0; i < INVBLKSZ; i++) { + if ((np = ip->i_dname[i]) == NULL) + break; + dprintf(5, (ddt, "dname = %d\n", np->n_dname)); + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!match(dp, class, type)) + continue; + if (dp->d_size != dlen || + bcmp(dp->d_data, data, dlen)) + continue; + getname(np, dnbuf, sizeof(dnbuf)); + dprintf(2, (ddt, "req: IQuery found %s\n", + dnbuf)); + *buflenp -= QFIXEDSZ; + n = dn_comp(dnbuf, *cpp, *buflenp, NULL, NULL); + if (n < 0) { + hp->tc = 1; + return (Finish); + } + *cpp += n; + PUTSHORT((u_int16_t)dp->d_type, *cpp); + PUTSHORT((u_int16_t)dp->d_class, *cpp); + *buflenp -= n; + count++; + } + } + } + dprintf(1, (ddt, "req: IQuery %d records\n", count)); + hp->qdcount = htons((u_int16_t)count); + if (alen > *buflenp) { + hp->tc = 1; + return (Finish); + } + bcopy(anbuf, *cpp, alen); + *cpp += alen; + return (Finish); +} +#endif + +static void +fwritemsg(rfp, msg, msglen) + FILE *rfp; + u_char *msg; + int msglen; +{ + u_char len[INT16SZ]; + + __putshort(msglen, len); + if (fwrite((char *)len, INT16SZ, 1, rfp) != 1 || + fwrite((char *)msg, msglen, 1, rfp) != 1) { + dprintf(1, (ddt, "fwrite failed %d\n", errno)); + } +} + +/* + * Test a datum for validity and return non-zero if it is out of date. + */ +int +stale(dp) + register struct databuf *dp; +{ + register struct zoneinfo *zp = &zones[dp->d_zone]; + + switch (zp->z_type) { + + case Z_PRIMARY: + return (0); + + case Z_SECONDARY: +#ifdef STUBS + case Z_STUB: +#endif + /* + * Check to see whether a secondary zone + * has expired; if so clear authority flag + * for zone and return true. If lastupdate + * is in the future, assume zone is up-to-date. + */ + if ((int32_t)(tt.tv_sec - zp->z_lastupdate) + > (int32_t)zp->z_expire) { + dprintf(1, (ddt, + "stale: secondary zone %s expired\n", + zp->z_origin)); + if (!haveComplained(zp->z_origin, (char*)stale)) { + syslog(LOG_ERR, + "secondary zone \"%s\" expired", + zp->z_origin); + } + zp->z_flags &= ~Z_AUTH; + return (1); + } + return (0); + + case Z_CACHE: + if (dp->d_flags & DB_F_HINT || dp->d_ttl >= tt.tv_sec) + return (0); + dprintf(3, (ddt, "stale: ttl %d %d (x%x)\n", + dp->d_ttl, dp->d_ttl - tt.tv_sec, dp->d_flags)); + return (1); + + default: + /* FALLTHROUGH */ ; + + } + abort(); + /* NOTREACHED */ +} + +/* + * Copy databuf into a resource record for replies. + * Return size of RR if OK, -1 if buffer is full. + */ +int +make_rr(name, dp, buf, buflen, doadd) + char *name; + register struct databuf *dp; + u_char *buf; + int buflen, doadd; +{ + register u_char *cp; + u_char *cp1, *sp; + struct zoneinfo *zp; + register int32_t n; + register int32_t ttl; + u_char **edp = dnptrs + sizeof dnptrs / sizeof dnptrs[0]; + + dprintf(5, (ddt, "make_rr(%s, %x, %x, %d, %d) %d zone %d ttl %d\n", + name, dp, buf, + buflen, doadd, dp->d_size, dp->d_zone, dp->d_ttl)); + +#ifdef NCACHE + if (dp->d_rcode +#ifdef RETURNSOA + && dp->d_rcode != NXDOMAIN +#endif + ) { + syslog(LOG_CRIT, "make_rr d_rcode %d", dp->d_rcode); +#ifdef DEBUG + if (debug) abort(); +#endif + return (-1); /* XXX We should exit here */ + } +#endif + zp = &zones[dp->d_zone]; + /* check for outdated RR before updating dnptrs by dn_comp() (???) */ + if (zp->z_type == Z_CACHE) { + ttl = dp->d_ttl - (u_int32_t) tt.tv_sec; + if ((dp->d_flags & DB_F_HINT) || (ttl < 0)) { + dprintf(3, (ddt, + "make_rr: %d=>0, x%x\n", + ttl, dp->d_flags)); /* XXX */ + ttl = 0; + } + } else { + if (dp->d_ttl) + ttl = dp->d_ttl; + else + ttl = zp->z_minimum; /* really default */ +#ifdef notdef /* don't decrease ttl based on time since verification */ + if (zp->z_type == Z_SECONDARY) { + /* + * Set ttl to value received from primary, + * less time since we verified it (but never + * less than a small positive value). + */ + ttl -= tt.tv_sec - zp->z_lastupdate; + if (ttl <= 0) + ttl = 120; + } +#endif + } + + buflen -= RRFIXEDSZ; +#if defined(RETURNSOA) && defined(NCACHE) + if (dp->d_rcode == NXDOMAIN) { + name = (char *)dp->d_data; + name += strlen(name) +1; + name += strlen(name) +1; + name += 5 * INT32SZ; + } +#endif + if ((n = dn_comp(name, buf, buflen, dnptrs, edp)) < 0) + return (-1); + cp = buf + n; + buflen -= n; + PUTSHORT((u_int16_t)dp->d_type, cp); + PUTSHORT((u_int16_t)dp->d_class, cp); + PUTLONG(ttl, cp); + sp = cp; + cp += INT16SZ; + switch (dp->d_type) { + case T_CNAME: + case T_MG: + case T_MR: + case T_PTR: + n = dn_comp((char *)dp->d_data, cp, buflen, dnptrs, edp); + if (n < 0) + return (-1); + PUTSHORT((u_int16_t)n, sp); + cp += n; + break; + + case T_MB: + case T_NS: + /* Store domain name in answer */ + n = dn_comp((char *)dp->d_data, cp, buflen, dnptrs, edp); + if (n < 0) + return (-1); + PUTSHORT((u_int16_t)n, sp); + cp += n; + if (doadd) + addname((char*)dp->d_data, dp->d_class); + break; + + case T_SOA: + case T_MINFO: + case T_RP: + cp1 = dp->d_data; + n = dn_comp((char *)cp1, cp, buflen, dnptrs, edp); + if (n < 0) + return (-1); + cp += n; + buflen -= dp->d_type == T_SOA ? n + 5 * INT32SZ : n; + cp1 += strlen((char *)cp1) + 1; + n = dn_comp((char *)cp1, cp, buflen, dnptrs, edp); + if (n < 0) + return (-1); + cp += n; + if (dp->d_type == T_SOA) { + cp1 += strlen((char *)cp1) + 1; + bcopy(cp1, cp, (n = 5 * INT32SZ)); + cp += n; + } + n = (u_int16_t)((cp - sp) - INT16SZ); + PUTSHORT((u_int16_t)n, sp); + break; + + case T_MX: + case T_AFSDB: + case T_RT: + /* cp1 == our data/ cp == data of RR */ + cp1 = dp->d_data; + + /* copy preference */ + bcopy(cp1, cp, INT16SZ); + cp += INT16SZ; + cp1 += INT16SZ; + buflen -= INT16SZ; + + n = dn_comp((char *)cp1, cp, buflen, dnptrs, edp); + if (n < 0) + return (-1); + cp += n; + + /* save data length */ + n = (u_int16_t)((cp - sp) - INT16SZ); + PUTSHORT((u_int16_t)n, sp); + if (doadd) + addname((char*)cp1, dp->d_class); + break; + + default: + if (dp->d_size > buflen) + return (-1); + bcopy(dp->d_data, cp, dp->d_size); + PUTSHORT((u_int16_t)dp->d_size, sp); + cp += dp->d_size; + } + return (cp - buf); +} + +#if defined(__STDC__) || defined(__GNUC__) +static void +addname(register char *name, + u_int16_t class) +#else +static void +addname(name, class) + register char *name; + u_int16_t class; +#endif +{ + register struct addinfo *ap; + register int n; + + for (ap = addinfo, n = addcount; --n >= 0; ap++) + if (strcasecmp(ap->a_dname, name) == 0) + return; + + + /* add domain name to additional section */ + if (addcount < NADDRECS) { + addcount++; + ap->a_dname = (char *)malloc(strlen(name)+1); + strcpy(ap->a_dname,name); + ap->a_class = class; + } +} + +/* + * Lookup addresses for names in addinfo and put into the message's + * additional section. + */ +int +doaddinfo(hp, msg, msglen) + HEADER *hp; + u_char *msg; + int msglen; +{ + register struct namebuf *np; + register struct databuf *dp; + register struct addinfo *ap; + register u_char *cp; + struct hashbuf *htp; + char *fname; + int n, count; + + dprintf(3, (ddt, "doaddinfo() addcount = %d\n", addcount)); + + if (hp->tc) { + dprintf(4, (ddt, "doaddinfo(): tc already set, bailing\n")); + return (0); + } + + count = 0; + cp = msg; + for (ap = addinfo; --addcount >= 0; ap++) { + int foundstale = 0, + foundany = 0, + save_count = count, + save_msglen = msglen; + u_char *save_cp = cp; + + dprintf(3, (ddt, "do additional '%s'\n", ap->a_dname)); + htp = hashtab; /* because "nlookup" stomps on arg. */ + np = nlookup(ap->a_dname, &htp, &fname, 0); + if (np == NULL || fname != ap->a_dname) + goto next_rr; + dprintf(3, (ddt, "found it\n")); + /* look for the data */ + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if ( (!match(dp, (int)ap->a_class, T_A)) + && (!match(dp, C_IN, T_A)) + ) { + continue; + } + foundany++; + if (stale(dp)) { + foundstale++; + dprintf(1, (ddt, + "doaddinfo: stale entry '%s'%s\n", + np->n_dname, + (dp->d_flags&DB_F_HINT) + ? " hint" + : "" + )); + continue; + } +#ifdef NCACHE + if (dp->d_rcode) + continue; +#endif + /* + * Should be smart and eliminate duplicate + * data here. XXX + */ + if ((n = make_rr(ap->a_dname, dp, cp, msglen, 0)) < 0){ + /* truncation in the additional-data section + * is not all that serious. we do not set TC, + * since the answer and authority sections are + * OK; however, since we're not setting TC we + * have to make sure that none of the RR's for + * this name go out (!TC implies that all + * {name,type} appearances are complete -- and + * since we only do A RR's here, the name is + * the key). vixie, 23apr93 + */ + cp = save_cp; + msglen = save_msglen; + count = save_count; + break; + } + dprintf(5, (ddt, + "addinfo: adding address data n = %d\n", + n)); + cp += n; + msglen -= n; + count++; + } +next_rr: if (foundstale) { + /* Cache invalidate the address RR's */ + delete_all(np, (int)ap->a_class, T_A); + } + if (foundstale || !foundany) { + /* ask a real server for this info */ + (void) sysquery(ap->a_dname, (int)ap->a_class, T_A, + NULL, 0); + } + free(ap->a_dname); + } + hp->arcount = htons((u_int16_t)count); + return (cp - msg); +} + +int +doaddauth(hp, cp, buflen, np, dp) + register HEADER *hp; + u_char *cp; + int buflen; + struct namebuf *np; + struct databuf *dp; +{ + char dnbuf[MAXDNAME]; + int n; + + getname(np, dnbuf, sizeof(dnbuf)); + if (stale(dp)) { + dprintf(1, (ddt, + "doaddauth: can't add stale '%s' (%d)\n", + dnbuf, buflen)); + return (0); + } + n = make_rr(dnbuf, dp, cp, buflen, 1); + if (n <= 0) { + dprintf(1, (ddt, + "doaddauth: can't add oversize '%s' (%d) (n=%d)\n", + dnbuf, buflen, n)); + if (n < 0) { + hp->tc = 1; + } + return (0); + } + hp->nscount = htons((u_int16_t)1); + return (n); +} + +/* + * Do a zone transfer (or a recursive part of a zone transfer). + * SOA record already sent. + * + * top always refers to the domain at the top of the zone being transferred. + * np refers to a domain inside the zone being transferred, + * which will be equal to top if this is the first call, + * or will be a subdomain below top if this is a recursive call, + * rfp is a stdio file to which output is sent. + */ +static void +doaxfr(np, rfp, top, class) + register struct namebuf *np; + FILE *rfp; + struct namebuf *top; + int class; /* Class to transfer */ +{ + register struct databuf *dp; + register int n; + struct hashbuf *htp; + struct databuf *gdp; /* glue databuf */ + struct namebuf *gnp; /* glue namebuf */ + struct namebuf *tnp; /* top namebuf */ + struct databuf *tdp; /* top databuf */ + struct namebuf **npp, **nppend; + u_char msg[PACKETSZ]; + u_char *cp; + char *fname; + char dname[MAXDNAME]; + HEADER *hp = (HEADER *) msg; + int fndns; + + if (np == top) + dprintf(1, (ddt, "doaxfr()\n")); + fndns = 0; + hp->id = 0; + hp->opcode = QUERY; + hp->aa = hp->tc = hp->ra = hp->pr = hp->rd = 0; + hp->qr = 1; + hp->rcode = NOERROR; + hp->qdcount = 0; + hp->ancount = htons(1); + hp->nscount = 0; + hp->arcount = 0; + cp = (u_char *) (msg + HFIXEDSZ); + getname(np, dname, sizeof(dname)); + + /* first do the NS records (del@harris) */ + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { +#ifdef GEN_AXFR + if (dp->d_class != class && class != C_ANY) + continue; +#endif +#ifdef NCACHE + if (dp->d_rcode) + continue; +#endif + if (dp->d_type == T_NS) { + fndns = 1; + n = make_rr(dname, dp, cp, sizeof(msg)-HFIXEDSZ, 0); + if (n < 0) + continue; + fwritemsg(rfp, msg, n + HFIXEDSZ); +#ifdef NO_GLUE + if ((np != top) || (top->n_dname[0] == '\0')) { +#endif /*NO_GLUE*/ + /* Glue the sub domains together by sending + * the address records for the sub domain + * name servers along if necessary. + * Glue is necessary if the server is in any zone + * delegated from the current (top) zone. Such + * a delegated zone might or might not be that + * referred to by the NS record now being handled. + */ + htp = hashtab; + cp = (u_char *) (msg + HFIXEDSZ); + gnp = nlookup((char *)dp->d_data, &htp, &fname, 0); + if (gnp == NULL || fname != (char *)dp->d_data) + continue; +#ifdef NO_GLUE + for (tnp = gnp; tnp != NULL; tnp = tnp->n_parent) + if ( tnp == top ) + break; + if ( (tnp == NULL) && (top->n_dname[0] != '\0') ) + continue; /* name server is not below top domain */ + for (tnp = gnp; tnp != top; tnp = tnp->n_parent) { + for (tdp = tnp->n_data; + tdp != NULL; + tdp = tdp->d_next) { +#ifdef GEN_AXFR + if (tdp->d_class != class && class != C_ANY) + continue; +#endif + if (tdp->d_type == T_NS) + break; + } + if (tdp != NULL) + break; /* found a zone cut */ + } + if (tnp == top) + continue; /* name server is not in a delegated zone */ + /* now we know glue records are needed. send them. */ +#endif /*NO_GLUE*/ + for (gdp=gnp->n_data; gdp != NULL; gdp=gdp->d_next) { +#ifdef GEN_AXFR + if (gdp->d_class != class && class != C_ANY) + continue; +#endif + if (gdp->d_type != T_A || stale(gdp)) + continue; +#ifdef NCACHE + if (gdp->d_rcode) + continue; +#endif + n = make_rr(fname, gdp, cp, sizeof(msg)-HFIXEDSZ, 0); + if (n < 0) + continue; + fwritemsg(rfp, msg, n + HFIXEDSZ); + } +#ifdef NO_GLUE + } +#endif /*NO_GLUE*/ + } + } + /* no need to send anything else if a delegation appeared */ + if ((np != top) && fndns) + return; + + /* do the rest of the data records */ + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { +#ifdef GEN_AXFR + if (dp->d_class != class && class != C_ANY) + continue; +#endif + /* + * Skip the top SOA record (marks end of data); + * don't send SOA for subdomains, as we're not sending them; + * skip the NS records because we did them first. + */ + if (dp->d_type == T_SOA || dp->d_type == T_NS) + continue; + if (dp->d_zone == 0 || stale(dp)) + continue; +#ifdef NCACHE + if (dp->d_rcode) + continue; +#endif + if ((n = make_rr(dname, dp, cp, sizeof(msg)-HFIXEDSZ, 0)) < 0) + continue; + fwritemsg(rfp, msg, n + HFIXEDSZ); + } + + /* Finally do non-delegated subdomains. Delegated subdomains + * have already been handled. + */ + /* + * We find the subdomains by looking in the hash table for this + * domain, but the root domain needs special treatment, because + * of the following wart in the database design: + * + * The top level hash table (pointed to by the global `hashtab' + * variable) contains pointers to the namebuf's for the root as + * well as for the top-level domains below the root, in contrast + * to the usual situation where a hash table contains entries + * for domains at the same level. The n_hash member of the + * namebuf for the root domain is NULL instead of pointing to a + * hashbuf for the top-level domains. The n_parent members of + * the namebufs for the top-level domains are NULL instead of + * pointing to the namebuf for the root. + * + * We work around the wart as follows: + * + * If we are not dealing with the root zone then we just set + * htp = np->n_hash, pointing to the hash table for the current + * domain, and we walk through the hash table as usual, + * processing the namebufs for all the subdomains. + * + * If we are dealing with the root zone, then we set + * htp = hashtab, pointing to the global hash table (because + * there is no hash table associated with the root domain's + * namebuf. While we walk this hash table, we take care not to + * recursively process the entry for the root namebuf. + * + * (apb@und nov1990) + */ + htp = ((dname[0] == '\0') ? hashtab : np->n_hash); + if (htp == NULL) { + return; /* no subdomains */ + } + npp = htp->h_tab; + nppend = npp + htp->h_size; + while (npp < nppend) { + for (np = *npp++; np != NULL; np = np->n_next) { + if (np->n_dname[0] != '\0') { /* don't redo root domain */ + doaxfr(np, rfp, top, class); + } + } + } + if (np == top) + dprintf(1, (ddt, "exit doaxfr()\n")); +} + +#ifdef ALLOW_UPDATES +/* + * Called by UPDATE{A,D,DA,M,MA} to initiate a dynamic update. If this is the + * primary server for the zone being updated, we update the zone's serial + * number and then call doupdate directly. If this is a secondary, we just + * forward the update; this way, if the primary update fails (e.g., if the + * primary is unavailable), we don't update the secondary; if the primary + * update suceeds, ns_resp will get called with the response (when it comes + * in), and then update the secondary's copy. + */ +static int +InitDynUpdate(hp, msg, msglen, startcp, from, qsp, dfd) + register HEADER *hp; + char *msg; + int msglen; + u_char *startcp; + struct sockaddr_in *from; + struct qstream *qsp; + int dfd; +{ + struct databuf *nsp[NSMAX]; + struct zoneinfo *zp; + char dnbuf[MAXDNAME]; + struct hashbuf *htp = hashtab; /* lookup relative to root */ + struct namebuf *np; + struct databuf *olddp, *newdp, *dp; + struct databuf **nspp; + char *fname; + register u_char *cp = startcp; + u_int16_t class, type; + int n, size, zonenum; + char ZoneName[MAXDNAME], *znp; + +#ifdef DATUMREFCNT + nsp[0] = NULL; +#endif + if ((n = dn_expand(msg, msg + msglen, cp, dnbuf, sizeof(dnbuf))) < 0) { + dprintf(1, (ddt,"FORMERR InitDynUpdate expand name failed\n")); + hp->rcode = FORMERR; + return (FORMERR); + } + cp += n; + GETSHORT(type, cp); + if (type == T_SOA) { /* T_SOA updates not allowed */ + hp->rcode = REFUSED; + dprintf(1, (ddt, "InitDynUpdate: REFUSED - SOA update\n")); + return (REFUSED); + } + GETSHORT(class, cp); + cp += INT32SZ; + GETSHORT(size, cp); +/****XXX - need bounds checking here ****/ + cp += size; + + if ((zonenum = findzone(dnbuf, class)) == 0) { /* zone not found */ + hp->rcode = NXDOMAIN; + return (NXDOMAIN); + } + zp = &zones[zonenum]; + + /* Disallow updates for which we aren't authoratative. Note: the + following test doesn't work right: If it's for a non-local zone, + we will think it's a primary but be unable to lookup the namebuf, + thus returning 'NXDOMAIN' */ + if (zp->z_type != Z_PRIMARY && zp->z_type != Z_SECONDARY) { + hp->rcode = REFUSED; + dprintf(1, (ddt, + "InitDynUpdate: REFUSED - non-{primary,secondary} update\n")); + return (REFUSED); + } + if (!(zp->z_flags & Z_DYNAMIC)) { + hp->rcode = REFUSED; + dprintf(1, (ddt, + "InitDynUpdate: REFUSED - dynamic flag not set for zone\n")); + return (REFUSED); + } + + /* + * Lookup the zone namebuf. Lookup "xyz" not "xyz.", since + * otherwise the lookup fails, because '.' may have a nil n_hash + * associated with it. + */ + strcpy(ZoneName, zp->z_origin); + znp = &ZoneName[strlen(ZoneName) - 1]; + if (*znp == '.') + *znp = NULL; + np = nlookup(ZoneName, &htp, &fname, 0); + if ((np == NULL) || (fname != ZoneName)) { + dprintf(1, (ddt, "InitDynUpdate: lookup failed on zone (%s)\n", + ZoneName)); + syslog(LOG_ERR, "InitDynUpdate: lookup failed on zone (%s)\n", + ZoneName); + hp->rcode = NXDOMAIN; + return (NXDOMAIN); + } + + /* + * If this is the primary copy increment the serial number. Don't + * increment the serial number if this is a secondary; this way, if 2 + * different secondaries both update the primary, they will both have + * lower serial numbers than the primary has, and hence eventually + * refresh and get all updates and become consistent. + * + * Note that the serial number must be incremented in both the zone + * data structure and the zone's namebuf. + */ + switch (zp->z_type) { + case Z_SECONDARY: /* forward update to primary */ + nspp = nsp; + dp = np->n_data; + while (dp != NULL) { + if (match(dp, class, T_NS)) { + if (nspp < &nsp[NSMAX-1]) { + *nspp++ = dp; +#ifdef DATUMREFCNT + dp->d_rcnt++; +#endif + } else + break; + } + dp = dp->d_next; + } + *nspp = NULL; /* Delimiter */ + if (ns_forw(nsp, msg, msglen, from, qsp, dfd, NULL, dnbuf, np) + < + 0) { + hp->rcode = SERVFAIL; +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (SERVFAIL); + } +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (FORWARDED); + + case Z_PRIMARY: + zp->z_serial++; + /* Find the SOA record */ + for (olddp = np->n_data; olddp != NULL; olddp = olddp->d_next) + if (match(olddp, class, T_SOA)) + break; + if (olddp == NULL) { + dprintf(1, (ddt, + "InitDynUpdate: Couldn't find SOA record for '%s'\n", + ZoneName)); + syslog(LOG_ERR, + "InitDynUpdate: Couldn't find SOA record for '%s'\n" +, + ZoneName); + hp->rcode = NXDOMAIN; +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (NXDOMAIN); + } + newdp = savedata(olddp->d_class, olddp->d_type, olddp->d_ttl, + olddp->d_data, olddp->d_size); + newdp->d_zone = olddp->d_zone; + newdp->d_cred = DB_C_AUTH; /* XXX - it may not be so */ + newdp->d_clev = db_getclev(zp->z_origin); + cp = (u_char *)newdp->d_data; + cp += strlen(cp) + 1; /* skip origin string */ + cp += strlen(cp) + 1; /* skip in-charge string */ + putlong((u_int32_t)(zp->z_serial), cp); + dprintf(4, (ddt, "after stuffing data into newdp:\n")); +#ifdef DEBUG + if (debug >= 4) + printSOAdata(newdp); +#endif + + if ((n = db_update(ZoneName, olddp, newdp, DB_DELETE, + hashtab)) != NOERROR) { /* XXX */ + dprintf(1, (ddt, + "InitDynUpdate: SOA update failed\n")); + hp->rcode = NOCHANGE; + free((char*) dp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (NOCHANGE); + } + + /* Now update the RR itself */ + /* XXX - DB_C_AUTH may be wrong */ + if (doupdate(msg, msglen, msg + HFIXEDSZ, zonenum, + (struct databuf *)0, DB_NODATA, DB_C_AUTH) < 0) { + dprintf(1, (ddt, "InitDynUpdate: doupdate failed\n")); + /* doupdate fills in rcode */ +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (hp->rcode); + } + zp->z_flags |= Z_CHANGED; +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (NOERROR); + } +} + +#ifdef DEBUG +/* + * Print the contents of the data in databuf pointed to by dp for an SOA record + */ +static void +printSOAdata(dp) + struct databuf *dp; +{ + register u_char *cp; + + if (!debug) + return; /* Otherwise fprintf to ddt will bomb */ + cp = (u_char *)dp->d_data; + fprintf(ddt, "printSOAdata(%x): origin(%x)='%s'\n", dp, cp, cp); + cp += strlen(cp) + 1; /* skip origin string */ + fprintf(ddt, "printSOAdata: in-charge(%x)='%s'\n", cp, cp); + cp += strlen(cp) + 1; /* skip in-charge string */ + fprintf(ddt, "printSOAdata: serial(%x)=%d\n", cp, _getlong(cp)); +} +#endif +#endif + +static void +startxfr(qsp, np, soa, soalen, class, dname) + struct qstream *qsp; + struct namebuf *np; + u_char *soa; + int soalen; + int class; + const char *dname; +{ + FILE *rfp; + int fdstat; + pid_t pid; + + dprintf(5, (ddt, "startxfr()\n")); + + /* + * child does the work while + * the parent continues + */ + switch (pid = fork()) { + case -1: + syslog(LOG_ERR, "startxfr(%s -> [%s]) failing; fork: %m", + dname, inet_ntoa(qsp->s_from.sin_addr)); + return; + case 0: + /* child */ + break; + default: + /* parent */ + syslog(LOG_DEBUG, "zone transfer of \"%s\" to [%s] (pid %lu)", + dname, inet_ntoa(qsp->s_from.sin_addr), pid); + return; + } + + /* + * Child. + * + * XXX: this should be a vfork/exec since on non-copy-on-write + * systems with huge nameserver images, this is very expensive. + */ + close(vs); + sqflush(/*allbut*/ qsp); + dqflush((time_t)0); + +#ifdef RENICE + nice(-40); nice(20); nice(0); /* back to "normal" */ +#endif + dprintf(5, (ddt, "startxfr: child pid %lu\n", (u_long)pid)); + + if (!(rfp = fdopen(qsp->s_rfd, "w"))) { + syslog(LOG_ERR, "fdopen: %m"); + _exit(1); + } + ns_setproctitle("zone XFR to", qsp->s_rfd); + if (-1 == (fdstat = fcntl(qsp->s_rfd, F_GETFL, 0))) { + syslog(LOG_ERR, "fcntl(F_GETFL): %m"); + _exit(1); + } + (void) fcntl(qsp->s_rfd, F_SETFL, fdstat & ~PORT_NONBLOCK); + fwritemsg(rfp, soa, soalen); + doaxfr(np, rfp, np, class); + fwritemsg(rfp, soa, soalen); + (void) fflush(rfp); + _exit(0); +} + +free_addinfo() { + struct addinfo *ap; + + for (ap = addinfo; --addcount >= 0; ap++) { + free(ap->a_dname); + } + addcount = 0; +} + +#ifdef DATUMREFCNT +free_nsp(nsp) +struct databuf **nsp; +{ + while (*nsp) { + if (--((*nsp)->d_rcnt)) { + dprintf(3, (ddt, "free_nsp: %s rcnt %d\n", + (*nsp)->d_data, (*nsp)->d_rcnt)); + } else { + dprintf(3, (ddt, "free_nsp: %s rcnt %d delayed\n", + (*nsp)->d_data, (*nsp)->d_rcnt)); + free(*nsp); /* delayed free */ + } + *nsp++ = NULL; + } +} +#endif diff --git a/usr.sbin/named/ns_resp.c b/usr.sbin/named/ns_resp.c new file mode 100644 index 000000000000..f8086e316dbb --- /dev/null +++ b/usr.sbin/named/ns_resp.c @@ -0,0 +1,2190 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_resp.c 4.65 (Berkeley) 3/3/91"; +static char rcsid[] = "$Id: ns_resp.c,v 4.9.1.24 1994/07/23 23:23:56 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1988, 1990 + * - + * Copyright (c) 1986, 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. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <syslog.h> +#include <errno.h> +#include <stdio.h> +#include <resolv.h> + +#include "named.h" + +static void check_root __P((void)), + check_ns __P((void)); + +static u_int8_t norootlogged[MAXCLASS]; /* XXX- should be a bitmap */ + +static char skipnameFailedAnswer[] = "skipname failed in answer", + skipnameFailedQuery[] = "skipname failed in query", + outofDataQuery[] = "ran out of data in query", + outofDataAnswer[] = "ran out of data in answer", +#ifdef LAME_DELEGATION + expandFailedQuery[] = "dn_expand failed in query", + expandFailedAuth[] = "dn_expand failed in authority", + outofDataAuth[] = "ran out of data in authority", +#endif /* LAME_DELEGATION */ + dlenOverrunAnswer[] = "dlen overrun in answer", + dlenUnderrunAnswer[] = "dlen underrun in answer", + outofDataFinal[] = "out of data in final pass", + outofDataAFinal[] = "out of data after final pass"; + +void +ns_resp(msg, msglen) + u_char *msg; + int msglen; +{ + register struct qinfo *qp; + register HEADER *hp; + register struct qserv *qs; + register struct databuf *ns, *ns2; + register u_char *cp; +#ifdef VALIDATE + register u_char *tempcp; + struct sockaddr_in *server = &from_addr; + int *validatelist; + int lesscount; +#endif + struct sockaddr_in *nsa; + struct databuf *nsp[NSMAX], **nspp; + int i, c, n, ancount, aucount, nscount, arcount; + int old_ancount; + int type, class, dbflags; + int cname = 0; /* flag for processing cname response */ + int count, founddata, foundname; + int buflen; + int newmsglen; + char name[MAXDNAME], *dname; + char *fname; + char *formerrmsg = "brain damage"; + u_char newmsg[BUFSIZ]; + u_char **dpp, *tp; + time_t rtrip; + struct hashbuf *htp; + struct namebuf *np; + struct netinfo *lp; + struct fwdinfo *fwd; + + nameserIncr(from_addr.sin_addr, nssRcvdR); +#ifdef DATUMREFCNT + nsp[0] = NULL; +#endif + hp = (HEADER *) msg; + if ((qp = qfindid(hp->id)) == NULL ) { + dprintf(1, (ddt, "DUP? dropped (id %d)\n", ntohs(hp->id))); + nameserIncr(from_addr.sin_addr, nssRcvdDupR); + return; + } + + dprintf(2, (ddt, "Response (%s %s %s) nsid=%d id=%d\n", + (qp->q_flags & Q_SYSTEM) ?"SYSTEM" :"USER", + (qp->q_flags & Q_PRIMING) ?"PRIMING" :"NORMAL", + (qp->q_flags & Q_ZSERIAL) ?"ZSERIAL" :"-", + ntohs(qp->q_nsid), ntohs(qp->q_id))); + + /* + * Here we handle bad responses from servers. + * Several possibilities come to mind: + * The server is sick and returns SERVFAIL + * The server returns some garbage opcode (its sick) + * The server can't understand our query and return FORMERR + * In all these cases, we simply drop the packet and force + * a retry. This will make him look bad due to unresponsiveness. + */ + if ((hp->rcode != NOERROR && hp->rcode != NXDOMAIN) +#ifndef NCACHE + || (hp->rcode == NXDOMAIN && !hp->aa) /* must accept this one if + * we allow negative caching + */ +#endif /*NCACHE*/ + || hp->opcode != QUERY) { + dprintf(2, (ddt, "resp: error (ret %d, op %d), dropped\n", + hp->rcode, hp->opcode)); + switch (hp->rcode) { + case SERVFAIL: + nameserIncr(from_addr.sin_addr, nssRcvdFail); + break; + case FORMERR: + nameserIncr(from_addr.sin_addr, nssRcvdFErr); + break; + default: + nameserIncr(from_addr.sin_addr, nssRcvdErr); + break; + } + return; + } +#ifdef LAME_DELEGATION + /* + * Non-authoritative, no answer, no error + */ + if (hp->rcode == NOERROR && !hp->aa && ntohs(hp->ancount) == 0 && + ntohs(hp->nscount) > 0) { + +#ifdef LAME_LOGGING + char qname[MAXDNAME]; +#endif /* LAME_LOGGING */ + +#ifdef DEBUG + if (debug > 0) + fp_query(msg, ddt); +#endif + + cp = msg + HFIXEDSZ; + dpp = dnptrs; + *dpp++ = msg; + if ((*cp & INDIR_MASK) == 0) + *dpp++ = cp; + *dpp = NULL; + if (hp->qdcount) { +#ifdef LAME_LOGGING + n = dn_expand(msg, msg + msglen, cp, qname, + sizeof(qname)); + if (n <= 0) { + formerrmsg = expandFailedQuery; + goto formerr; + } +#else /* LAME_LOGGING */ + n = dn_skipname(cp, msg + msglen); + if (n <= 0) { + formerrmsg = skipnameFailedQuery; + goto formerr; + } +#endif /* LAME_LOGGING */ + cp += n; + GETSHORT(type, cp); + GETSHORT(class, cp); + if (cp - msg > msglen) { + formerrmsg = outofDataQuery; + goto formerr; + } +#ifdef LAME_LOGGING + } else { + strcpy(qname, "[No query name!]"); +#endif /* LAME_LOGGING */ + } + n = dn_expand(msg, msg + msglen, cp, name, sizeof name); + if (n < 0) { + formerrmsg = expandFailedAuth; + goto formerr; + } + cp += n; + GETSHORT(type, cp); + if (cp - msg > msglen) { + formerrmsg = outofDataAuth; + goto formerr; + } + + /* + * If the answer delegates us either to the same level in + * the hierarchy or closer to the root, we consider this + * server lame. + */ + + if (type == T_NS && samedomain(qp->q_domain, name)) { + nameserIncr(from_addr.sin_addr, nssRcvdLDel); +#ifdef LAME_LOGGING + if (!haveComplained((char*)dhash((u_char*)name, + strlen(name)), + (char*)dhash((u_char*)qp->q_domain, + strlen(qp->q_domain) + ) + ) + ) { + syslog(LAME_LOGGING, +"Lame delegation to '%s' from [%s] (server for '%s'?) on query on name '%s'\n", + name, inet_ntoa(from_addr.sin_addr), + qp->q_domain, qname); + } +#endif /* LAME_LOGGING */ + return; + } + } +#endif /* LAME_DELEGATION */ + + +#ifdef ALLOW_UPDATES + if ( (hp->rcode == NOERROR) && + (hp->opcode == UPDATEA || hp->opcode == UPDATED || + hp->opcode == UPDATEDA || hp->opcode == UPDATEM || + hp->opcode == UPDATEMA) ) { + /* + * Update the secondary's copy, now that the primary + * successfully completed the update. Zone doesn't matter + * for dyn. update -- doupdate calls findzone to find it + */ + /* XXX - DB_C_AUTH may be wrong */ + (void) doupdate(qp->q_msg, qp->q_msglen, qp->q_msg + HFIXEDSZ, + 0, (struct databuf *)0, 0, DB_C_AUTH); + dprintf(3, (ddt, "resp: leaving, UPDATE*\n")); + /* return code filled in by doupdate */ + goto return_msg; + } +#endif /* ALLOW_UPDATES */ + + /* + * Determine if the response came from a forwarder. Packets from + * anyplace not listed as a forwarder or as a server to whom we + * might have forwarded the query will be dropped. + */ + for (fwd = fwdtab; fwd != (struct fwdinfo *)NULL; fwd = fwd->next) { + if (fwd->fwdaddr.sin_addr.s_addr == + from_addr.sin_addr.s_addr) { + /* XXX - should put this in STATS somewhere */ + break; + } + } + /* XXX: note bad ambiguity here. if one of our forwarders is also + * a delegated server for some domain, then we will not update + * the RTT information on any replies we get from those servers. + */ + /* + * If we were using nameservers, find the qinfo pointer and update + * the rtt and fact that we have called on this server before. + */ + if (fwd == (struct fwdinfo *)NULL) { + struct timeval *stp; + + for (n = 0, qs = qp->q_addr; n < qp->q_naddr; n++, qs++) + if (qs->ns_addr.sin_addr.s_addr == + from_addr.sin_addr.s_addr) + break; + if (n >= qp->q_naddr) { + if (!haveComplained((char*)from_addr.sin_addr.s_addr, + "unexpected source")) { + syslog(LOG_NOTICE, + "Response from unexpected source [%s].%d", + inet_ntoa(from_addr.sin_addr), + ntohs(from_addr.sin_port)); + } + /* + * We don't know who this response came from so it + * gets dropped on the floor. + */ + return; + } + stp = &qs->stime; + + /* Handle response from different (untried) interface */ + if ((qs->ns != NULL) && (stp->tv_sec == 0)) { + ns = qs->ns; + while (qs > qp->q_addr && + (qs->stime.tv_sec == 0 || qs->ns != ns)) + qs--; + *stp = qs->stime; + /* XXX - sometimes stp still ends up pointing to + * a zero timeval, in spite of the above attempt. + * Why? What should we do about it? + */ + dprintf(1, (ddt, + "Response from unused address %s, assuming %s\n", + inet_ntoa(from_addr.sin_addr), + inet_ntoa(qs->ns_addr.sin_addr))); + /* XXX - catch aliases here */ + } + + /* compute query round trip time */ + /* XXX - avoid integer overflow, which is quite likely if stp + * points to a zero timeval (see above). + * rtrip is of type time_t, which we assume is at least + * as big as an int. + */ + if ((tt.tv_sec - stp->tv_sec) > (INT_MAX-999)/1000) { + rtrip = INT_MAX; + } else { + rtrip = ((tt.tv_sec - stp->tv_sec) * 1000 + + (tt.tv_usec - stp->tv_usec) / 1000); + } + + dprintf(3, (ddt, "stime %d/%d now %d/%d rtt %d\n", + stp->tv_sec, stp->tv_usec, + tt.tv_sec, tt.tv_usec, rtrip)); + + /* prevent floating point overflow, limit to 1000 sec */ + if (rtrip > 1000000) { + rtrip = 1000000; + } + ns = qs->nsdata; + /* + * Don't update nstime if this doesn't look + * like an address databuf now. XXX + */ + if (ns && (ns->d_type==T_A) && (ns->d_class==qs->ns->d_class)){ + if (ns->d_nstime == 0) + ns->d_nstime = (u_int32_t)rtrip; + else + ns->d_nstime = (u_int32_t) + (ns->d_nstime * ALPHA + + + (1-ALPHA) * (u_int32_t)rtrip); + /* prevent floating point overflow, + * limit to 1000 sec + */ + if (ns->d_nstime > 1000000) + ns->d_nstime = 1000000; + } + + /* + * Record the source so that we do not use this NS again. + */ + if (ns && qs->ns && (qp->q_nusedns < NSMAX)) { + qp->q_usedns[qp->q_nusedns++] = qs->ns; + dprintf(2, (ddt, "NS #%d addr [%s] used, rtt %d\n", + n, inet_ntoa(qs->ns_addr.sin_addr), + ns->d_nstime)); + } + + /* + * Penalize those who had earlier chances but failed + * by multiplying round-trip times by BETA (>1). + * Improve nstime for unused addresses by applying GAMMA. + * The GAMMA factor makes unused entries slowly + * improve, so they eventually get tried again. + * GAMMA should be slightly less than 1. + * Watch out for records that may have timed out + * and are no longer the correct type. XXX + */ + + for (n = 0, qs = qp->q_addr; n < qp->q_naddr; n++, qs++) { + ns2 = qs->nsdata; + if ((!ns2) || (ns2 == ns)) + continue; + if (ns2->d_type != T_A || + ns2->d_class != qs->ns->d_class) /* XXX */ + continue; + if (qs->stime.tv_sec) { + if (ns2->d_nstime == 0) + ns2->d_nstime = (u_int32_t)(rtrip * BETA); + else + ns2->d_nstime = (u_int32_t)( + ns2->d_nstime * BETA + (1-ALPHA) * rtrip + ); + if (ns2->d_nstime > 1000000) + ns2->d_nstime = 1000000; + } else + ns2->d_nstime = (u_int32_t)(ns2->d_nstime * GAMMA); + dprintf(2, (ddt, "NS #%d [%s] rtt now %d\n", n, + inet_ntoa(qs->ns_addr.sin_addr), + ns2->d_nstime)); + } + } + + /*************************************************************/ + + /* + * Skip query section + */ + free_addinfo(); /* sets addcount to zero */ + cp = msg + HFIXEDSZ; + dpp = dnptrs; + *dpp++ = msg; + if ((*cp & INDIR_MASK) == 0) + *dpp++ = cp; + *dpp = NULL; + type = class = 0; + if (hp->qdcount) { + n = dn_skipname(cp, msg + msglen); + if (n <= 0) { + formerrmsg = skipnameFailedQuery; + goto formerr; + } + cp += n; + GETSHORT(type, cp); + GETSHORT(class, cp); + if (cp - msg > msglen) { + formerrmsg = outofDataQuery; + goto formerr; + } + } + + /* + * Save answers, authority, and additional records for future use. + */ + ancount = ntohs(hp->ancount); + aucount = ntohs(hp->nscount); + arcount = ntohs(hp->arcount); + nscount = 0; + tp = cp; + dprintf(3, (ddt, "resp: ancount %d, aucount %d, arcount %d\n", + ancount, aucount, arcount)); + + /* + * If there's an answer, check if it's a CNAME response; + * if no answer but aucount > 0, see if there is an NS + * or just an SOA. (NOTE: ancount might be 1 with a CNAME, + * and NS records may still be in the authority section; + * we don't bother counting them, as we only use nscount + * if ancount == 0.) + */ + if (ancount == 1 || (ancount == 0 && aucount > 0)) { + c = aucount; + do { + if (tp - msg >= msglen) { + formerrmsg = outofDataAnswer; + goto formerr; + } + n = dn_skipname(tp, msg + msglen); + if (n <= 0) { + formerrmsg = skipnameFailedAnswer; + goto formerr; + } + tp += n; /* name */ + GETSHORT(i, tp); /* type */ + tp += INT16SZ; /* class */ + tp += INT32SZ; /* ttl */ + GETSHORT(count, tp); /* dlen */ + if (tp - msg > msglen - count) { + formerrmsg = dlenOverrunAnswer; + goto formerr; + } + tp += count; + if (ancount && i == T_CNAME) { + cname++; + dprintf(1, + (ddt, + "CNAME - needs more processing\n" + ) + ); + if (!qp->q_cmsglen) { + qp->q_cmsg = qp->q_msg; + qp->q_cmsglen = qp->q_msglen; + qp->q_msg = NULL; + qp->q_msglen = 0; + } + } + /* + * See if authority record is a nameserver. + */ + if (ancount == 0 && i == T_NS) + nscount++; + } while (--c > 0); + tp = cp; + } + + if (qp->q_flags & Q_ZSERIAL) { + if ((hp->aa) + && (ancount != 0) + && (hp->rcode == NOERROR) + && (type == T_SOA) + && ((class == C_IN) || (class == C_HS)) + ) { /* XXX - should check name, too */ + int n; + u_int16_t dlen; + u_int32_t serial; + u_char *tp = cp; + + if (0 >= (n = dn_skipname(tp, msg + msglen))) { + formerrmsg = skipnameFailedAnswer; + goto formerr; + } + tp += n /* name */ + + INT16SZ /* type */ + + INT16SZ /* class */ + + INT32SZ; /* ttl */ + GETSHORT(dlen, tp); /* dlen */ + + if (dlen < (5 * INT32SZ)) { + formerrmsg = dlenUnderrunAnswer; + goto formerr; + } + + if (0 >= (n = dn_skipname(tp, msg + msglen))) { + formerrmsg = skipnameFailedAnswer; + goto formerr; + } + tp += n; /* mname */ + if (0 >= (n = dn_skipname(tp, msg + msglen))) { + formerrmsg = skipnameFailedAnswer; + goto formerr; + } + tp += n; /* rname */ + GETLONG(serial, tp); + + qserial_answer(qp, serial); + } + qremove(qp); + return; + } + + /* + * Add the info received in the response to the data base. + */ + c = ancount + aucount + arcount; +#ifdef NCACHE + /* -ve $ing non-existence of record, must handle non-authoritative + * NOERRORs with c == 0. + */ + if (!hp->aa && hp->rcode == NOERROR && c == 0) { + goto return_msg; + } /*should ideally validate message before returning it*/ +#endif /*NCACHE*/ +#ifdef notdef + /* + * If the request was for a CNAME that doesn't exist, + * but the name is valid, fetch any other data for the name. + * DON'T do this now, as it will requery if data are already + * in the cache (maybe later with negative caching). + */ + if (hp->qdcount && type == T_CNAME && c == 0 && hp->rcode == NOERROR + && !(qp->q_flags & Q_SYSTEM)) { + dprintf(4, (ddt, "resp: leaving, no CNAME\n")); + + /* Cause us to put it in the cache later */ + prime(class, T_ANY, qp); + + /* Nothing to store, just give user the answer */ + goto return_msg; + } +#endif /* notdef */ + + nspp = nsp; + if (qp->q_flags & Q_SYSTEM) + dbflags = DB_NOTAUTH | DB_NODATA; + else + dbflags = DB_NOTAUTH | DB_NODATA | DB_NOHINTS; + count = c; + if (hp->tc) { + count -= arcount; /* truncation had to affect this */ + if (!arcount) { + count -= aucount; /* guess it got this too */ + } + if (!(arcount || aucount)) { + count -= ancount; /* things are pretty grim */ + } + /* XXX - should retry this query with TCP */ + } +#ifdef VALIDATE + tempcp = cp; + validatelist = (int *)malloc(count * sizeof(int)); + lesscount = 0; /*initialize*/ + old_ancount = ancount; + for (i = 0; i < count; i++) { + int VCode; + if (tempcp >= msg + msglen) { + free((char *)validatelist); + formerrmsg = outofDataFinal; + goto formerr; + } + if ((n = dovalidate(msg, msglen, tempcp, 0, + dbflags, server, &VCode)) < 0) { + dprintf(1, (ddt, + "resp: leaving, dovalidate failed\n")); + free((char *)validatelist); + + /* return code filled in by dovalidate */ + goto return_msg; + } + validatelist[i] = VCode; + if (VCode == INVALID) lesscount++; + tempcp += n; + } + + /* need to delete INVALID records from the message + * and change fields appropriately + */ + n = update_msg(msg, &msglen, validatelist, count); + free((char *)validatelist); + if (n < 0) + goto formerr; + count -= lesscount; + + if (old_ancount && !hp->ancount) { + /* We lost all the answers */ + dprintf(1, (ddt, "validate count -> 0")); + return; + } + ancount = ntohs(hp->ancount); +#endif + + for (i = 0; i < count; i++) { + struct databuf *ns3; + u_char cred; + + if (cp >= msg + msglen) { + formerrmsg = outofDataFinal; + goto formerr; + } + if (i < ancount) { + cred = hp->aa ? DB_C_AUTH : DB_C_ANSWER; + } else { + cred = DB_C_ADDITIONAL; + } + ns3 = 0; + n = doupdate(msg, msglen, cp, 0, &ns3, dbflags, cred); + if (n < 0) { + dprintf(1, (ddt, "resp: leaving, doupdate failed\n")); + + /* return code filled in by doupdate */ + goto return_msg; + } + /* + * Remember nameservers from the authority section + * for referrals. + * (This is usually overwritten by findns below(?). XXX + */ + if (ns3 && i >= ancount && i < ancount + aucount && + nspp < &nsp[NSMAX-1]) { + *nspp++ = ns3; +#ifdef DATUMREFCNT + ns3->d_rcnt++; + *nspp = NULL; +#endif + } + cp += n; + } + + if ((qp->q_flags & Q_SYSTEM) && ancount) { + if (qp->q_flags & Q_PRIMING) + check_root(); + dprintf(3, (ddt, "resp: leaving, SYSQUERY ancount %d\n", + ancount)); + qremove(qp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return; + } + + if (cp > msg + msglen) { + formerrmsg = outofDataAFinal; + goto formerr; + } + + /* + * If there are addresses and this is a local query, + * sort them appropriately for the local context. + */ + if (ancount > 1 && (lp = local(&qp->q_from)) != NULL) + sort_response(tp, ancount, lp, msg + msglen); + + /* + * An answer to a T_ANY query or a successful answer to a + * regular query with no indirection, then just return answer. + */ + if ((hp->qdcount && type == T_ANY && ancount) || + (!cname && !qp->q_cmsglen && ancount)) { + dprintf(3, (ddt, "resp: got as much answer as there is\n")); + goto return_msg; + } + + /* + * We might want to cache this negative answer. + */ + if (!ancount && + (!nscount || hp->rcode == NXDOMAIN) && + (hp->aa || fwd || class == C_ANY)) { + /* we have an authoritative NO */ + dprintf(3, (ddt, "resp: leaving auth NO\n")); + if (qp->q_cmsglen) { + msg = qp->q_cmsg; + msglen = qp->q_cmsglen; + hp = (HEADER *)msg; + } +#ifdef NCACHE + /* answer was NO */ + if (hp->aa && + ((hp->rcode == NXDOMAIN) || (hp->rcode == NOERROR))) { + cache_n_resp(msg, msglen); + } +#endif /*NCACHE*/ + goto return_msg; + } + + /* + * All messages in here need further processing. i.e. they + * are either CNAMEs or we got referred again. + */ + count = 0; + founddata = 0; + foundname = 0; + dname = name; + if (!cname && qp->q_cmsglen && ancount) { + dprintf(1, (ddt, "Cname second pass\n")); + newmsglen = qp->q_cmsglen; + bcopy(qp->q_cmsg, newmsg, newmsglen); + } else { + newmsglen = msglen; + bcopy(msg, newmsg, newmsglen); + } + hp = (HEADER *) newmsg; + hp->ancount = 0; + hp->nscount = 0; + hp->arcount = 0; + dnptrs[0] = newmsg; + dnptrs[1] = NULL; + cp = newmsg + HFIXEDSZ; + if (cname) + cp += dn_skipname(cp, newmsg + newmsglen) + QFIXEDSZ; + n = dn_expand(newmsg, newmsg + newmsglen, cp, dname, sizeof name); + if (n < 0) { + dprintf(1, (ddt, "dn_expand failed\n")); + goto servfail; + } + if (!cname) + cp += n + QFIXEDSZ; + buflen = sizeof(newmsg) - (cp - newmsg); + +try_again: + dprintf(1, (ddt, "resp: nlookup(%s) type=%d\n", dname, type)); + fname = ""; + htp = hashtab; /* lookup relative to root */ + np = nlookup(dname, &htp, &fname, 0); + dprintf(1, (ddt, "resp: %s '%s' as '%s' (cname=%d)\n", + np == NULL ? "missed" : "found", dname, fname, cname)); + if (np == NULL || fname != dname) + goto fetch_ns; + + foundname++; + count = cp - newmsg; + n = finddata(np, class, type, hp, &dname, &buflen, &count); + if (n == 0) + goto fetch_ns; /* NO data available */ + cp += n; + buflen -= n; + hp->ancount += count; + if (fname != dname && type != T_CNAME && type != T_ANY) { + cname++; + goto try_again; + } + founddata = 1; + + dprintf(3, (ddt, + "resp: foundname=%d, count=%d, founddata=%d, cname=%d\n", + foundname, count, founddata, cname)); + +fetch_ns: + hp->ancount = htons(hp->ancount); + /* + * Look for name servers to refer to and fill in the authority + * section or record the address for forwarding the query + * (recursion desired). + */ +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + switch (findns(&np, class, nsp, &count, 0)) { + case NXDOMAIN: /* shouldn't happen */ + dprintf(3, (ddt, "req: leaving (%s, rcode %d)\n", + dname, hp->rcode)); + if (!foundname) + hp->rcode = NXDOMAIN; + if (class != C_ANY) { + hp->aa = 1; + /* XXX: should return SOA if founddata == 0, + * but old named's are confused by an SOA + * in the auth. section if there's no error. + */ + if (foundname == 0 && np) { + n = doaddauth(hp, cp, buflen, np, nsp[0]); + cp += n; + buflen -= n; + } + } + goto return_newmsg; + + case SERVFAIL: + goto servfail; + } + + if (founddata) { + hp = (HEADER *)newmsg; + n = add_data(np, nsp, cp, buflen); + if (n < 0) { + hp->tc = 1; + n = (-n); + } + cp += n; + buflen -= n; + hp->nscount = htons((u_int16_t)count); + goto return_newmsg; + } + + /* + * If we get here, we don't have the answer yet and are about + * to iterate to try and get it. First, infinite loop avoidance. + */ + if (qp->q_nqueries++ > MAXQUERIES) { + dprintf(1, + (ddt, + "resp: MAXQUERIES exceeded (%s, class %d, type %d)\n", + dname, class, type + ) + ); + syslog(LOG_NOTICE, + "MAXQUERIES exceeded, possible data loop in resolving (%s)", + dname); + goto servfail; + } + + /* Reset the query control structure */ +#ifdef DATUMREFCNT + for (i = 0 ; i < qp->q_naddr ; i++) { + if ((--(qp->q_addr[i].ns->d_rcnt))) { + dprintf(1 ,(ddt, "ns_resp: ns %s rcnt %d\n", + qp->q_addr[i].ns->d_data, + qp->q_addr[i].ns->d_rcnt)); + } else { + dprintf(1 ,(ddt, "ns_resp: ns %s rcnt %d delayed\n", + qp->q_addr[i].ns->d_data, + qp->q_addr[i].ns->d_rcnt)); + free((char*)qp->q_addr[i].ns); + } + if ((--(qp->q_addr[i].nsdata->d_rcnt))) { + dprintf(1 ,(ddt, "ns_resp: nsdata %08.8X rcnt %d\n", + *(int32_t *)(qp->q_addr[i].nsdata->d_data), + qp->q_addr[i].nsdata->d_rcnt)); + } else { + dprintf(1 ,(ddt, "ns_resp: nsdata %08.8X rcnt %d delayed\n", + *(int32_t *)(qp->q_addr[i].nsdata->d_data), + qp->q_addr[i].nsdata->d_rcnt)); + free((char*)qp->q_addr[i].nsdata); + } + } +#endif + qp->q_naddr = 0; + qp->q_curaddr = 0; + qp->q_fwd = fwdtab; +#ifdef LAME_DELEGATION + getname(np, qp->q_domain, sizeof(qp->q_domain)); +#endif /* LAME_DELEGATION */ + if ((n = nslookup(nsp, qp, dname, "ns_resp")) <= 0) { + if (n < 0) { + dprintf(3, (ddt, "resp: nslookup reports danger\n")); + } else { + dprintf(3, (ddt, "resp: no addrs found for NS's\n")); + } + if (cname) /* a remote CNAME that does not have data */ + goto return_newmsg; + goto servfail; + } + for (n = 0; n < qp->q_naddr; n++) + qp->q_addr[n].stime.tv_sec = 0; + if (!qp->q_fwd) + qp->q_addr[0].stime = tt; + if (cname) { + if (qp->q_cname++ == MAXCNAMES) { + dprintf(3, (ddt, + "resp: leaving, MAXCNAMES exceeded\n")); + goto servfail; + } + dprintf(1, (ddt, "q_cname = %d\n",qp->q_cname)); + dprintf(3, (ddt, + "resp: building recursive query; nslookup\n")); + if (qp->q_msg) + (void) free(qp->q_msg); + if ((qp->q_msg = (u_char *)malloc(BUFSIZ)) == NULL) { + dprintf(1, (ddt, "resp: malloc error\n")); + goto servfail; + } + qp->q_msglen = res_mkquery(QUERY, dname, class, + type, NULL, 0, NULL, + qp->q_msg, BUFSIZ); + hp = (HEADER *) qp->q_msg; + hp->rd = 0; + } else + hp = (HEADER *)qp->q_msg; + hp->id = qp->q_nsid = htons((u_int16_t)++nsid); + if (qp->q_fwd) + hp->rd = 1; + unsched(qp); + schedretry(qp, retrytime(qp)); + nsa = Q_NEXTADDR(qp, 0); + dprintf(1, (ddt, "resp: forw -> [%s].%d ds=%d nsid=%d id=%d %dms\n", + inet_ntoa(nsa->sin_addr), + ntohs(nsa->sin_port), ds, + ntohs(qp->q_nsid), ntohs(qp->q_id), + (qp->q_addr[0].nsdata != NULL) + ? qp->q_addr[0].nsdata->d_nstime + : (-1))); +#ifdef DEBUG + if (debug >= 10) + fp_query(msg, ddt); +#endif + if (sendto(ds, qp->q_msg, qp->q_msglen, 0, + (struct sockaddr *)nsa, + sizeof(struct sockaddr_in)) < 0) { + if (!haveComplained((char*)nsa->sin_addr.s_addr, sendtoStr)) + syslog(LOG_NOTICE, "ns_resp: sendto([%s].%d): %m", + inet_ntoa(nsa->sin_addr), ntohs(nsa->sin_port)); + nameserIncr(nsa->sin_addr, nssSendtoErr); + } + hp->rd = 0; /* leave set to 0 for dup detection */ + nameserIncr(nsa->sin_addr, nssSentFwdR); + nameserIncr(qp->q_from.sin_addr, nssRcvdFwdR); + dprintf(3, (ddt, "resp: Query sent.\n")); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return; + +formerr: + dprintf(3, (ddt, + "FORMERR resp() from [%s].%d size err %d, msglen %d\n", + inet_ntoa(from_addr.sin_addr), + ntohs(from_addr.sin_port), + cp - msg, msglen)); + if (!haveComplained((char*)from_addr.sin_addr.s_addr, + (char*)dhash((u_char *)formerrmsg, + strlen(formerrmsg) + ) + ) + ) { + syslog(LOG_INFO, "Malformed response from [%s].%d (%s)\n", + inet_ntoa(from_addr.sin_addr), + ntohs(from_addr.sin_port), + formerrmsg); + } + nameserIncr(from_addr.sin_addr, nssSentFErr); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return; + +return_msg: + nameserIncr(from_addr.sin_addr, nssRcvdFwdR); + nameserIncr(qp->q_from.sin_addr, nssSentFwdR); + /* The "standard" return code */ + hp->qr = 1; + hp->id = qp->q_id; + hp->rd = 1; + hp->ra = 1; + (void) send_msg(msg, msglen, qp); + qremove(qp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return; + +return_newmsg: + nameserIncr(qp->q_from.sin_addr, nssSentAns); + if (addcount) { + n = doaddinfo(hp, cp, buflen); + cp += n; + buflen -= n; + } + hp->qr = 1; + hp->id = qp->q_id; + hp->rd = 1; + hp->ra = 1; + (void) send_msg(newmsg, cp - newmsg, qp); + qremove(qp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return; + +servfail: + nameserIncr(qp->q_from.sin_addr, nssSentFail); + hp = (HEADER *)(cname ? qp->q_cmsg : qp->q_msg); + hp->rcode = SERVFAIL; + hp->id = qp->q_id; + hp->rd = 1; + hp->ra = 1; + hp->qr = 1; + (void) send_msg((u_char *)hp, (cname ? qp->q_cmsglen : qp->q_msglen), + qp); + qremove(qp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return; +} + +/* + * Decode the resource record 'rrp' and update the database. + * If savens is non-nil, record pointer for forwarding queries a second time. + */ +int +doupdate(msg, msglen, rrp, zone, savens, flags, cred) + u_char *msg, *rrp; + struct databuf **savens; + int msglen, zone, flags; + u_int cred; +{ + register u_char *cp; + register int n; + int class, type, dlen, n1; + u_int32_t ttl; + struct databuf *dp; + char dname[MAXDNAME]; + u_char *cp1; + u_char data[BUFSIZ]; + register HEADER *hp = (HEADER *)msg; +#ifdef ALLOW_UPDATES + int zonenum; +#endif + + dprintf(3, (ddt, "doupdate(zone %d, savens %x, flags %x)\n", + zone, savens, flags)); + + cp = rrp; + if ((n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname)) < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + dprintf(3, (ddt, "doupdate: dname %s type %d class %d ttl %d\n", + dname, type, class, ttl)); + /* + * Convert the resource record data into the internal + * database format. + */ + switch (type) { + case T_A: + if (dlen != INT32SZ) { + hp->rcode = FORMERR; + return (-1); + } + /*FALLTHROUGH*/ + case T_WKS: + case T_HINFO: + case T_UINFO: + case T_UID: + case T_GID: + case T_TXT: + case T_X25: + case T_ISDN: + case T_NSAP: +#ifdef ALLOW_T_UNSPEC + case T_UNSPEC: +#endif + 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, + (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = data; + n = strlen((char *)data) + 1; + break; + + case T_MINFO: + case T_SOA: + case T_RP: + n = dn_expand(msg, msg + msglen, cp, + (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = data + (n = strlen((char *)data) + 1); + n1 = sizeof(data) - n; + if (type == T_SOA) + n1 -= 5 * 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) { + bcopy(cp, cp1, n = 5 * INT32SZ); + cp += n; + cp1 += n; + } + n = cp1 - data; + cp1 = data; + break; + + case T_MX: + case T_AFSDB: + case T_RT: + /* grab preference */ + bcopy(cp, data, INT16SZ); + cp1 = data + INT16SZ; + cp += INT16SZ; + + /* get name */ + n = dn_expand(msg, msg + msglen, cp, (char *)cp1, + sizeof data - INT16SZ); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + + /* compute end of data */ + cp1 += strlen((char *)cp1) + 1; + /* compute size of data */ + n = cp1 - data; + cp1 = data; + break; + + default: + dprintf(3, (ddt, "unknown type %d\n", type)); + return ((cp - rrp) + dlen); + } + if (n > MAXDATA) { + dprintf(1, (ddt, + "update type %d: %d bytes is too much data\n", + type, n)); + hp->rcode = NOCHANGE; /* XXX - FORMERR ??? */ + return (-1); + } + +#ifdef ALLOW_UPDATES + /* + * If this is a dynamic update request, process it specially; else, + * execute normal update code. + */ + switch(hp->opcode) { + + /* For UPDATEM and UPDATEMA, do UPDATED/UPDATEDA followed by UPDATEA */ + case UPDATEM: + case UPDATEMA: + + /* + * The named code for UPDATED and UPDATEDA is the same except that for + * UPDATEDA we we ignore any data that was passed: we just delete all + * RRs whose name, type, and class matches + */ + case UPDATED: + case UPDATEDA: + if (type == T_SOA) { /* Not allowed */ + dprintf(1, (ddt, "UDPATE: REFUSED - SOA delete\n")); + hp->rcode = REFUSED; + return (-1); + } + /* + * Don't check message length if doing UPDATEM/UPDATEMA, + * since the whole message wont have been demarshalled until + * we reach the code for UPDATEA + */ + if ( (hp->opcode == UPDATED) || (hp->opcode == UPDATEDA) ) { + if (cp != (u_char *)(msg + msglen)) { + dprintf(1, + (ddt, + "FORMERR UPDATE message length off\n" + ) + ); + hp->rcode = FORMERR; + return (-1); + } + } + if ((zonenum = findzone(dname, class)) == 0) { + hp->rcode = NXDOMAIN; + return (-1); + } + if (zones[zonenum].z_flags & Z_DYNADDONLY) { + hp->rcode = NXDOMAIN; + return (-1); + } + if ( (hp->opcode == UPDATED) || (hp->opcode == UPDATEM) ) { + /* Make a dp for use in db_update, as old dp */ + dp = savedata(class, type, 0, cp1, n); + dp->d_zone = zonenum; + dp->d_cred = cred; + dp->d_clev = db_getclev(zones[zonenum].z_origin); + n = db_update(dname, dp, NULL, DB_MEXIST | DB_DELETE, + hashtab); + if (n != OK) { + dprintf(1, (ddt, + "UPDATE: db_update failed\n")); + free((char*) dp); + hp->rcode = NOCHANGE; + return (-1); + } + } else { /* UPDATEDA or UPDATEMA */ + int DeletedOne = 0; + /* Make a dp for use in db_update, as old dp */ + dp = savedata(class, type, 0, NULL, 0); + dp->d_zone = zonenum; + dp->d_cred = cred; + dp->d_clev = db_getclev(zones[zonenum].z_origin); + do { /* Loop and delete all matching RR(s) */ + n = db_update(dname, dp, NULL, DB_DELETE, + hashtab); + if (n != OK) + break; + DeletedOne++; + } while (1); + free((char*) dp); + /* Ok for UPDATEMA not to have deleted any RRs */ + if (!DeletedOne && hp->opcode == UPDATEDA) { + dprintf(1, (ddt, + "UPDATE: db_update failed\n")); + hp->rcode = NOCHANGE; + return (-1); + } + } + if ( (hp->opcode == UPDATED) || (hp->opcode == UPDATEDA) ) + return (cp - rrp);; + /* + * Else unmarshal the RR to be added and continue on to + * UPDATEA code for UPDATEM/UPDATEMA + */ + if ((n = + dn_expand(msg, msg+msglen, cp, dname, sizeof(dname))) < 0) { + dprintf(1, (ddt, + "FORMERR UPDATE expand name failed\n")); + hp->rcode = FORMERR; + return (-1); + } + cp += n; + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(n, cp); + cp1 = cp; +/**** XXX - need bounds checking here ****/ + cp += n; + + case UPDATEA: + if (n > MAXDATA) { + dprintf(1, (ddt, "UPDATE: too much data\n")); + hp->rcode = NOCHANGE; + return (-1); + } + if (cp != (u_char *)(msg + msglen)) { + dprintf(1, (ddt, + "FORMERR UPDATE message length off\n")); + hp->rcode = FORMERR; + return (-1); + } + if ((zonenum = findzone(dname, class)) == 0) { + hp->rcode = NXDOMAIN; + return (-1); + } + if (zones[zonenum].z_flags & Z_DYNADDONLY) { + struct hashbuf *htp = hashtab; + char *fname; + if (nlookup(dname, &htp, &fname, 0) && + !strcasecmp(dname, fname)) { + dprintf(1, (ddt, + "refusing add of existing name\n" + )); + hp->rcode = REFUSED; + return (-1); + } + } + dp = savedata(class, type, ttl, cp1, n); + dp->d_zone = zonenum; + dp->d_cred = cred; + dp->d_clev = db_getclev(zones[zonenum].z_origin); + if ((n = db_update(dname, NULL, dp, DB_NODATA, + hashtab)) != OK) { + dprintf(1, (ddt, "UPDATE: db_update failed\n")); + hp->rcode = NOCHANGE; + free((char*) dp); + return (-1); + } + else + return (cp - rrp); + } +#endif /* ALLOW_UPDATES */ + + if (zone == 0) + ttl += tt.tv_sec; +#if defined(TRACEROOT) || defined(BOGUSNS) + if ((type == T_NS) && (savens != NULL)) { + char qname[MAXDNAME], *temp; + register int bogus = 0; + int bogusns = 0; +#ifdef BOGUSNS + if (addr_on_netlist(from_addr.sin_addr, boglist)) { + bogusns++; + bogus++; + } +#endif + if (!bogus && + ((temp = strrchr((char *)data, '.')) != NULL) && + !strcasecmp(temp, ".arpa") + ) + bogus++; + qname[0] = qname[1] = '\0'; + if (dn_expand(msg, msg + msglen, msg + HFIXEDSZ, + qname, sizeof(qname)) < 0) + qname[0] = '?'; + else if (qname[0] == '\0') + qname[0] = '.'; + if (bogus && ((dname[0] == '\0') && (zone == 0))) { + if (!haveComplained((char*)from_addr.sin_addr.s_addr, + "bogus root NS")) + syslog(LOG_NOTICE, + "bogus root NS %s rcvd from [%s] on query for \"%s\"", + data, inet_ntoa(from_addr.sin_addr), + qname); + return (cp - rrp); + } +#ifdef BOGUSNS + if (bogusns) { + if (!haveComplained((char*)from_addr.sin_addr.s_addr, + "bogus nonroot NS")) + syslog(LOG_NOTICE, + "bogus nonroot NS %s rcvd from [%s] on query for \"%s\"", + data, inet_ntoa(from_addr.sin_addr), + qname); + return (cp - rrp); + } +#endif + } +#endif /*TRACEROOT || BOGUSNS*/ + + dp = savedata(class, type, ttl, cp1, n); + dp->d_zone = zone; + dp->d_cred = cred; + dp->d_clev = 0; /* We trust what is on disk more, except root srvrs */ + if ((n = db_update(dname, dp, dp, flags, hashtab)) != OK) { +#ifdef DEBUG + if (debug && (n != DATAEXISTS)) + fprintf(ddt,"update failed (%d)\n", n); + else if (debug >= 3) + fprintf(ddt,"update failed (DATAEXISTS)\n"); +#endif + free((char *)dp); + } else if (type == T_NS && savens != NULL) + *savens = dp; + return (cp - rrp); +} + +int +send_msg(msg, msglen, qp) + u_char *msg; + int msglen; + struct qinfo *qp; +{ + if (qp->q_flags & Q_SYSTEM) + return (1); +#ifdef DEBUG + if (debug) { + fprintf(ddt,"send_msg -> [%s] (%s %d %d) id=%d\n", + inet_ntoa(qp->q_from.sin_addr), + qp->q_stream == QSTREAM_NULL ? "UDP" : "TCP", + qp->q_stream == QSTREAM_NULL ? qp->q_dfd + : qp->q_stream->s_rfd, + ntohs(qp->q_from.sin_port), + ntohs(qp->q_id)); + } + if (debug>4) { + struct qinfo *tqp; + + for (tqp = qhead; tqp!=QINFO_NULL; tqp = tqp->q_link) { + fprintf(ddt, "qp %x q_id: %d q_nsid: %d q_msglen: %d ", + tqp, tqp->q_id,tqp->q_nsid,tqp->q_msglen); + fprintf(ddt,"q_naddr: %d q_curaddr: %d\n", tqp->q_naddr, + tqp->q_curaddr); + fprintf(ddt,"q_next: %x q_link: %x\n", qp->q_next, + qp->q_link); + } + } + if (debug >= 10) + fp_query(msg, ddt); +#endif /* DEBUG */ + if (qp->q_stream == QSTREAM_NULL) { + if (sendto(qp->q_dfd, msg, msglen, 0, + (struct sockaddr *)&qp->q_from, + sizeof(qp->q_from)) < 0) { + if (!haveComplained((char*)qp->q_from.sin_addr.s_addr, + sendtoStr)) + syslog(LOG_NOTICE, + "send_msg: sendto([%s].%d): %m", + inet_ntoa(qp->q_from.sin_addr), + ntohs(qp->q_from.sin_port)); + nameserIncr(qp->q_from.sin_addr, nssSendtoErr); + return (1); + } + } else { + (void) writemsg(qp->q_stream->s_rfd, (u_char*)msg, msglen); + sq_done(qp->q_stream); + } + return (0); +} + +#ifdef notdef +/* i don't quite understand this but the only ref to it is notdef'd --vix */ +prime(class, type, oqp) + int class, type; + register struct qinfo *oqp; +{ + char dname[BUFSIZ]; + + if (oqp->q_msg == NULL) + return; + if (dn_expand((u_char *)oqp->q_msg, + (u_char *)oqp->q_msg + oqp->q_msglen, + (u_char *)oqp->q_msg + HFIXEDSZ, (u_char *)dname, + sizeof(dname)) < 0) + return; + dprintf(2, (ddt, "prime: %s\n", dname)); + (void) sysquery(dname, class, type, NULL, 0); +} +#endif + +void +prime_cache() +{ + register struct qinfo *qp; + + dprintf(1, (ddt, "prime_cache: priming = %d\n", priming)); + if (!priming && fcachetab->h_tab[0] != NULL && !forward_only) { + priming++; + if ((qp = sysquery("", C_IN, T_NS, NULL, 0)) == NULL) + priming = 0; + else + qp->q_flags |= (Q_SYSTEM | Q_PRIMING); + } + needs_prime_cache = 0; + return; +} + +struct qinfo * +sysquery(dname, class, type, nss, nsc) + char *dname; + int class, type; + struct in_addr *nss; + int nsc; +{ + register struct qinfo *qp, *oqp; + register HEADER *hp; + struct namebuf *np; + struct databuf *nsp[NSMAX]; + struct hashbuf *htp; + struct sockaddr_in *nsa; + char *fname; + int count; + +#ifdef DATUMREFCNT + nsp[0] = NULL; +#endif + dprintf(3, (ddt, "sysquery(%s, %d, %d, 0x%x, %d)\n", + dname, class, type, nss, nsc)); + qp = qnew(); + + if (nss && nsc) { + np = NULL; + } else { + htp = hashtab; + if (priming && dname[0] == '\0') { + np = NULL; + } else if ((np = nlookup(dname, &htp, &fname, 1)) == NULL) { + dprintf(1, (ddt, + "sysquery: nlookup error on %s?\n", + dname)); + qfree(qp); + return (0); + } + + switch (findns(&np, class, nsp, &count, 0)) { + case NXDOMAIN: + case SERVFAIL: + dprintf(1, (ddt, + "sysquery: findns error on %s?\n", dname)); + qfree(qp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (0); + } + } + + /* build new qinfo struct */ + qp->q_cmsg = qp->q_msg = NULL; + qp->q_dfd = ds; + if (nss && nsc) + qp->q_fwd = NULL; + else + qp->q_fwd = fwdtab; + qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; + qp->q_flags |= Q_SYSTEM; +#ifdef LAME_DELEGATION + getname(np, qp->q_domain, sizeof(qp->q_domain)); +#endif /* LAME_DELEGATION */ + + if ((qp->q_msg = (u_char *)malloc(BUFSIZ)) == NULL) { + qfree(qp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (0); + } + qp->q_msglen = res_mkquery(QUERY, dname, class, + type, NULL, 0, NULL, + qp->q_msg, BUFSIZ); + hp = (HEADER *) qp->q_msg; + hp->id = qp->q_nsid = htons((u_int16_t)++nsid); + hp->rd = (qp->q_fwd ? 1 : 0); + + /* First check for an already pending query for this data */ + for (oqp = qhead; oqp != QINFO_NULL; oqp = oqp->q_link) { + if ((oqp != qp) + && (oqp->q_msglen == qp->q_msglen) + && bcmp((char *)oqp->q_msg+2, + qp->q_msg+2, + qp->q_msglen-2) == 0 + ) { + dprintf(3, (ddt, "sysquery: duplicate\n")); + qfree(qp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (0); + } + } + + if (nss && nsc) { + int i; + struct qserv *qs; + + for (i = 0, qs = qp->q_addr; + i < nsc; + i++, qs++) { + qs->ns_addr.sin_family = AF_INET; + qs->ns_addr.sin_addr = nss[i]; + qs->ns_addr.sin_port = ns_port; + qs->ns = NULL; + qs->nsdata = NULL; + qs->stime = tt; + qs->nretry = 0; + } + qp->q_naddr = nsc; + } else { + if ((count = nslookup(nsp, qp, dname, "sysquery")) <= 0) { + if (count < 0) { + dprintf(1, (ddt, + "sysquery: nslookup reports danger\n")); + } else { + dprintf(1, (ddt, + "sysquery: no addrs found for NS's\n")); + } + qfree(qp); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (0); + } + } + + schedretry(qp, retrytime(qp)); + if (qp->q_fwd == NULL) + qp->q_addr[0].stime = tt; /* XXX - why not every? */ + nsa = Q_NEXTADDR(qp, 0); + + dprintf(1, (ddt, + "sysquery: send -> [%s].%d dfd=%d nsid=%d id=%d retry=%ld\n", + inet_ntoa(nsa->sin_addr), + ntohs(nsa->sin_port), qp->q_dfd, + ntohs(qp->q_nsid), ntohs(qp->q_id), + qp->q_time)); +#ifdef DEBUG + if (debug >= 10) + fp_query(qp->q_msg, ddt); +#endif + if (sendto(qp->q_dfd, qp->q_msg, qp->q_msglen, 0, + (struct sockaddr *)nsa, + sizeof(struct sockaddr_in)) < 0) { + if (!haveComplained((char*)nsa->sin_addr.s_addr, sendtoStr)) + syslog(LOG_NOTICE, "sysquery: sendto([%s].%d): %m", + inet_ntoa(nsa->sin_addr), ntohs(nsa->sin_port)); + nameserIncr(nsa->sin_addr, nssSendtoErr); + } + nameserIncr(nsa->sin_addr, nssSentSysQ); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return (qp); +} + +/* + * Check the list of root servers after receiving a response + * to a query for the root servers. + */ +static void +check_root() +{ + register struct databuf *dp, *pdp; + register struct namebuf *np; + int count = 0; + + priming = 0; + for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) + if (np->n_dname[0] == '\0') + break; + if (np == NULL) { + syslog(LOG_ERR, "check_root: Can't find root!\n"); + return; + } + for (dp = np->n_data; dp != NULL; dp = dp->d_next) + if (dp->d_type == T_NS) + count++; + dprintf(1, (ddt, "%d root servers\n", count)); + if (count < MINROOTS) { + syslog(LOG_WARNING, + "check_root: %d root servers after query to root server < min", + count); + return; + } + pdp = NULL; + dp = np->n_data; + while (dp != NULL) { + if (dp->d_type == T_NS && dp->d_zone == 0 && + dp->d_ttl < tt.tv_sec) { + dprintf(1, (ddt, "deleting old root server '%s'\n", + dp->d_data)); + dp = rm_datum(dp, np, pdp); + /* SHOULD DELETE FROM HINTS ALSO */ + continue; + } + pdp = dp; + dp = dp->d_next; + } + check_ns(); +} + +/* + * Check the root to make sure that for each NS record we have a A RR + */ +static void +check_ns() +{ + register struct databuf *dp, *tdp; + register struct namebuf *np, *tnp; + struct hashbuf *htp; + char *dname; + int found_arr; + char *fname; + time_t curtime; + + dprintf(2, (ddt, "check_ns()\n")); + + curtime = (u_int32_t) tt.tv_sec; + for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) { + if (np->n_dname[0] != 0) + continue; + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (dp->d_type != T_NS) + continue; + + /* look for A records */ + dname = (caddr_t) dp->d_data; + htp = hashtab; + tnp = nlookup(dname, &htp, &fname, 0); + if (tnp == NULL || fname != dname) { + dprintf(3, (ddt, + "check_ns: %s: not found %s %x\n", + dname, fname, tnp)); + sysquery(dname, dp->d_class, T_A, NULL, 0); + continue; + } + /* look for name server addresses */ + found_arr = 0; + for (tdp=tnp->n_data; tdp!=NULL; tdp=tdp->d_next) { + if (tdp->d_type != T_A || + tdp->d_class != dp->d_class) + continue; + if ((tdp->d_zone == 0) && + (tdp->d_ttl < curtime)) { + dprintf(3, (ddt, + "check_ns: stale entry '%s'\n", + tnp->n_dname)); + /* Cache invalidate the address RR's */ + delete_all(tnp, dp->d_class, T_A); + found_arr = 0; + break; + } + found_arr++; + } + if (!found_arr) + (void) sysquery(dname, dp->d_class, T_A, NULL, 0); + } + } +} + +/* int findns(npp, class, nsp, countp, flag) + * Find NS' or an SOA + * npp, class: + * dname whose least-superior NS is wanted + * nsp, countp: + * result array and count; array will also be NULL terminated + * flag: + * boolean: we're being called from ADDAUTH, bypass authority checks + * return value: + * NXDOMAIN: we are authoritative for this {dname,class} + * SERVFAIL: we are auth but zone isn't loaded; or, no root servers found + * OK: success (this is the only case where *countp and nsp[] are valid) + */ +int +findns(npp, class, nsp, countp, flag) + register struct namebuf **npp; + int class; + struct databuf **nsp; + int *countp; + int flag; +{ + register struct namebuf *np = *npp; + register struct databuf *dp; + register struct databuf **nspp; + struct hashbuf *htp; + +#ifdef DATUMREFCNT + nsp[0] = NULL; +#endif + + if (priming && (np == NULL || np->n_dname[0] == '\0')) + htp = fcachetab; + else + htp = hashtab; + +try_again: + if (htp == fcachetab) + needs_prime_cache = 1; + while (np == NULL && htp != NULL) { + dprintf(3, (ddt, "findns: using %s\n", + htp == hashtab ? "cache" : "hints")); + for (np = htp->h_tab[0]; np != NULL; np = np->n_next) + if (np->n_dname[0] == '\0') + break; + htp = (htp == hashtab ? fcachetab : NULL); /* Fallback */ + } + while (np != NULL) { + dprintf(5, (ddt, "findns: np 0x%x '%s'\n", np, np->n_dname)); + /* Look first for SOA records. */ +#ifdef ADDAUTH + if (!flag) +#endif + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (dp->d_zone != 0 && match(dp, class, T_SOA)) { + dprintf(3, (ddt, "findns: SOA found\n")); + if (zones[dp->d_zone].z_flags & Z_AUTH) { + *npp = np; + nsp[0] = dp; +#ifdef DATUMREFCNT + nsp[1] = NULL; + dp->d_rcnt++; +#endif + return (NXDOMAIN); + } else { + /* XXX: zone isn't loaded but we're + * primary or secondary for it. + * should we fwd this? + */ + return (SERVFAIL); + } + } + } + + /* If no SOA records, look for NS records. */ + nspp = &nsp[0]; + *nspp = NULL; + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!match(dp, class, T_NS)) + continue; + /* + * Don't use records that may become invalid to + * reference later when we do the rtt computation. + * Never delete our safety-belt information! + * + * XXX: this is horribly bogus. + */ + if ((dp->d_zone == 0) && + (dp->d_ttl < (tt.tv_sec+900)) && + !(dp->d_flags & DB_F_HINT)) { + dprintf(1, (ddt, "findns: stale entry '%s'\n", + np->n_dname)); + /* Cache invalidate the NS RR's. */ + if (dp->d_ttl < tt.tv_sec) + delete_all(np, class, T_NS); + goto try_parent; + } + if (nspp < &nsp[NSMAX-1]) { + *nspp++ = dp; +#ifdef DATUMREFCNT + dp->d_rcnt++; +#endif + } + } + + *countp = nspp - nsp; + if (*countp > 0) { + dprintf(3, (ddt, "findns: %d NS's added for '%s'\n", + *countp, np->n_dname)); + *nspp = NULL; + *npp = np; + return (OK); /* Success, got some NS's */ + } +try_parent: + np = np->n_parent; + } + if (htp) + goto try_again; + dprintf(1, (ddt, "findns: No root nameservers for class %s?\n", + p_class(class))); + if ((unsigned)class < MAXCLASS && norootlogged[class] == 0) { + norootlogged[class] = 1; + syslog(LOG_ERR, "No root nameservers for class %s\n", + p_class(class)); + } + return (SERVFAIL); +} + +/* + * Extract RR's from the given node that match class and type. + * Return number of bytes added to response. + * If no matching data is found, then 0 is returned. + */ +int +finddata(np, class, type, hp, dnamep, lenp, countp) + struct namebuf *np; + int class, type; + register HEADER *hp; + char **dnamep; + int *lenp, *countp; +{ + register struct databuf *dp; + register char *cp; + int buflen, n, count = 0, foundstale = 0; + +#ifdef ROUND_ROBIN + if (type != T_ANY && type != T_PTR) { + /* cycle order of RRs, for a load balancing effect... */ + + register struct databuf **dpp; + + for (dpp = &np->n_data; dp = *dpp; dpp = &dp->d_next) { + if (dp->d_next && wanted(dp, class, type)) { + register struct databuf *lp; + + *dpp = lp = dp->d_next; + dp->d_next = NULL; + + for (dpp = &lp->d_next; + *dpp; + dpp = &lp->d_next) + lp = *dpp; + *dpp = dp; + break; + } + } + } +#endif /*ROUND_ROBIN*/ + + buflen = *lenp; + cp = ((char *)hp) + *countp; + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!wanted(dp, class, type)) { +#ifndef NCACHE /*if no negative caching then cname => nothing else*/ + if (type == T_CNAME && class == dp->d_class) { + /* any data means no CNAME exists */ + *countp = 0; + return 0; + } +#endif /*NCACHE*/ + continue; + } + if (stale(dp)) { + /* + * Don't use stale data. + * Would like to call delete_all here + * and continue, but the data chain would get + * munged; can't restart, as make_rr has side + * effects (leaving pointers in dnptr). + * Just skip this entry for now + * and call delete_all at the end. + */ + dprintf(3, (ddt, + "finddata: stale entry '%s'\n", + np->n_dname)); + if (dp->d_zone == 0) + foundstale++; + continue; + } + if (dp->d_cred == DB_C_ADDITIONAL) { + /* we want to expire additional data very + * quickly. current strategy is to cut 5% + * off each time it is accessed. this makes + * stale(dp) true faster when this datum is + * used often. + */ + dp->d_ttl = tt.tv_sec + + + 0.95 * (int) (dp->d_ttl - tt.tv_sec); + } +#ifdef NCACHE + /* -ve $ing stuff, anant@isi.edu + * if we have a -ve $ed record, change the rcode on the + * header to reflect that + */ + if (dp->d_rcode == NOERROR_NODATA) { + if (count != 0) { + /* + * This should not happen, yet it does... + */ + syslog(LOG_WARNING, + "NODATA & data for \"%s\" type %d class %d", + *dnamep, type, class); + continue; + } + if (type != T_ANY) { + hp->rcode = NOERROR_NODATA; + *countp = 0; + return 1; /* XXX - we have to report success */ + } + /* don't satisfy T_ANY queries from -$ info */ + continue; + } +#ifndef RETURNSOA + if (dp->d_rcode == NXDOMAIN) { + if (count != 0) { + /* + * This should not happen, yet it might... + */ + syslog(LOG_WARNING, + "NXDOMAIN & data for \"%s\" type %d class %d", + *dnamep, type, class); + continue; + } + if (type != T_ANY) { + hp->rcode = NXDOMAIN; + *countp = 0; + return 1; /* XXX - we have to report success */ + } + /* don't satisfy T_ANY queries from -$ info */ + continue; + } +#endif +#endif /*NCACHE*/ + + if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1)) < 0) { + hp->tc = 1; + *countp = count; + return (*lenp - buflen); + } + + cp += n; + buflen -= n; + count++; +#ifdef notdef + /* this isn't right for glue records, aa is set in ns_req */ + if (dp->d_zone && + (zones[dp->d_zone].z_flags & Z_AUTH) && + class != C_ANY) + hp->aa = 1; /* XXX */ +#endif + if (dp->d_type == T_CNAME) { + if (type != T_ANY) { /* or T_NS? */ + *dnamep = (caddr_t) dp->d_data; + if (dp->d_zone && + (zones[dp->d_zone].z_flags & Z_AUTH) && + class != C_ANY) /* XXX */ + hp->aa = 1; /* XXX */ + } + break; + } + } + /* + * Cache invalidate the other RR's of same type + * if some have timed out + */ + if (foundstale) { + delete_all(np, class, type); + /* XXX this isn't right if 'type' is something special + * such as T_AXFR or T_MAILB, since the matching done + * by match() in delete_all() is different from that + * done by wanted() above. + */ + } + dprintf(3, (ddt, "finddata: added %d class %d type %d RRs\n", + count, class, type)); + *countp = count; + return (*lenp - buflen); +} + +/* + * Do we want this data record based on the class and type? + */ +int +wanted(dp, class, type) + struct databuf *dp; + int class, type; +{ + + dprintf(3, (ddt, "wanted(%x, %d, %d) %d, %d\n", dp, class, type, + dp->d_class, dp->d_type)); + + if (dp->d_class != class && class != C_ANY) + return (0); + if (type == dp->d_type) + return (1); +#ifdef NCACHE + /*-ve $ing stuff, for a T_ANY query, we do not want to return + * -ve $ed RRs. + */ + if (type == T_ANY && dp->d_rcode == NOERROR_NODATA) + return (0); +#endif + + switch (dp->d_type) { + case T_ANY: + return (1); + case T_CNAME: +#ifdef NCACHE + if (dp->d_rcode != NOERROR_NODATA) +#endif + return (1); +#ifdef NCACHE + else + break; +#endif + } + switch (type) { + case T_ANY: + return (1); + + case T_MAILB: + switch (dp->d_type) { + case T_MR: + case T_MB: + case T_MG: + case T_MINFO: + return (1); + } + break; + + case T_AXFR: + /* T_AXFR needs an authoritative SOA */ + if (dp->d_type == T_SOA && dp->d_zone != 0 + && (zones[dp->d_zone].z_flags & Z_AUTH)) + return (1); + break; + } + return (0); +} + +/* + * Add RR entries from dpp array to a query/response. + * Return the number of bytes added or negative the amount + * added if truncation was required. Typically you are + * adding NS records to a response. + */ +int +add_data(np, dpp, cp, buflen) + struct namebuf *np; + struct databuf **dpp; + register u_char *cp; + int buflen; +{ + register struct databuf *dp; + char dname[MAXDNAME]; + register int n, count = 0; + + getname(np, dname, sizeof(dname)); + for(dp = *dpp++; dp != NULL; dp = *dpp++) { + if (stale(dp)) + continue; /* ignore old cache entry */ +#ifdef NCACHE + if (dp->d_rcode) + continue; +#endif + if ((n = make_rr(dname, dp, cp, buflen, 1)) < 0) + return (-count); /* Truncation */ + cp += n; + buflen -= n; + count += n; + } + return (count); +} + +/* + * This is best thought of as a "cache invalidate" function. + * It is called whenever a piece of data is determined to have + * timed out. It is better to have no information, than to + * have partial information you pass off as complete. + */ +void +delete_all(np, class, type) + register struct namebuf *np; + int class, type; +{ + register struct databuf *dp, *pdp; + + dprintf(3, (ddt, "delete_all: '%s' 0x%x class %d type %d\n", + np->n_dname, np, class, type)); + pdp = NULL; + dp = np->n_data; + while (dp != NULL) { + if ((dp->d_zone == 0) && !(dp->d_flags & DB_F_HINT) + && match(dp, class, type)) { + dp = rm_datum(dp, np, pdp); + continue; + } + pdp = dp; + dp = dp->d_next; + } +} diff --git a/usr.sbin/named/ns_sort.c b/usr.sbin/named/ns_sort.c new file mode 100644 index 000000000000..c35b58f16ae4 --- /dev/null +++ b/usr.sbin/named/ns_sort.c @@ -0,0 +1,169 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_sort.c 4.10 (Berkeley) 3/3/91"; +static char rcsid[] = "$Id: ns_sort.c,v 4.9.1.6 1994/07/23 23:23:56 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986, 1990 + * - + * Copyright (c) 1986, 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. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <syslog.h> +#include <resolv.h> + +#include "named.h" + +static int sort_rr __P((u_char *cp, int count, struct netinfo *ntp, u_char *eom)); + +struct netinfo * +local(from) + struct sockaddr_in *from; +{ + struct netinfo *ntp; + + if (from->sin_addr.s_addr == netloop.my_addr.s_addr) + return (&netloop); + for (ntp = nettab; ntp != *enettab; ntp = ntp->next) { + if (ntp->addr == (from->sin_addr.s_addr & ntp->mask)) + return (ntp); + } + return (NULL); +} + +void +sort_response(cp, ancount, lp, eom) + register u_char *cp; + register int ancount; + struct netinfo *lp; + u_char *eom; +{ + register struct netinfo *ntp; + + dprintf(3, (ddt, "sort_response(%d)\n", ancount)); + if (ancount > 1) { + if (sort_rr(cp, ancount, lp, eom)) + return; + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if ((ntp->addr == lp->addr) && (ntp->mask == lp->mask)) + continue; + if (sort_rr(cp, ancount, ntp, eom)) + break; + } + } +} + +static int +sort_rr(cp, count, ntp, eom) + register u_char *cp; + int count; + register struct netinfo *ntp; + u_char *eom; +{ + int type, class, dlen, n, c; + struct in_addr inaddr; + u_char *rr1; + +#ifdef DEBUG + if (debug > 2) { + inaddr.s_addr = ntp->addr; + fprintf(ddt, "sort_rr(x%x, %d, [%s])\n", + cp, count, inet_ntoa(inaddr)); + } +#endif + rr1 = NULL; + for (c = count; c > 0; --c) { + n = dn_skipname(cp, eom); + if (n < 0) + return (1); /* bogus, stop processing */ + cp += n; + if (cp + QFIXEDSZ > eom) + return (1); + GETSHORT(type, cp); + GETSHORT(class, cp); + cp += INT32SZ; + GETSHORT(dlen, cp); + if (dlen > eom - cp) + return (1); /* bogus, stop processing */ + switch (type) { + case T_A: + switch (class) { + case C_IN: + case C_HS: + bcopy(cp, (char *)&inaddr, INADDRSZ); + if (rr1 == NULL) + rr1 = cp; + if ((ntp->mask & inaddr.s_addr) == ntp->addr) { + dprintf(2, (ddt,"net [%s] best choice\n", + inet_ntoa(inaddr))); + if (rr1 != cp) { + bcopy(rr1, cp, INADDRSZ); + bcopy((char *)&inaddr, rr1, INADDRSZ); + } + return (1); + } + break; + } + break; + } + cp += dlen; + } + return (0); +} diff --git a/usr.sbin/named/ns_stats.c b/usr.sbin/named/ns_stats.c new file mode 100644 index 000000000000..d6d6225b6210 --- /dev/null +++ b/usr.sbin/named/ns_stats.c @@ -0,0 +1,325 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_stats.c 4.10 (Berkeley) 6/27/90"; +static char rcsid[] = "$Id: ns_stats.c,v 4.9.1.7 1994/06/06 09:08:15 vixie Exp $"; +#endif /* not lint */ + +/* + * ++Copyright++ 1986 + * - + * Copyright (c) 1986 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/**************************************************************************/ +/* simple monitoring of named behavior */ +/* dumps a bunch of values into a well-known file */ +/**************************************************************************/ + +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <stdio.h> +#include <syslog.h> +#include <errno.h> + +#include "named.h" +#include "tree.h" + +static u_long typestats[T_ANY+1]; +static const char *typenames[T_ANY+1] = { + /* 5 types per line */ + "Unknown", "A", "NS", "invalid(MD)", "invalid(MF)", + "CNAME", "SOA", "MB", "MG", "MR", + "NULL", "WKS", "PTR", "HINFO", "MINFO", + "MX", "TXT", "RP", "AFSDB", "X25", + "ISDN", "RT", "NSAP", 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 20 per line */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 100 */ + "UINFO", "UID", "GID", "UNSPEC", 0, 0, 0, 0, 0, 0, + /* 110 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 120 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 200 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 240 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 250 */ + 0, 0, "AXFR", "MAILB", "MAILA", "ANY" +}; + +static void nameserStats __P((FILE *)); + +void +ns_stats() +{ + time_t timenow; + register FILE *f; + register int i; + + if (!(f = fopen(statsfile, "a"))) { + dprintf(1, (ddt, "can't open stat file, \"%s\"\n", statsfile)); + syslog(LOG_ERR, "cannot open stat file, \"%s\"\n", statsfile); + return; + } + + time(&timenow); + fprintf(f, "+++ Statistics Dump +++ (%ld) %s", + (long)timenow, ctime(&timenow)); + fprintf(f, "%d\ttime since boot (secs)\n", timenow - boottime); + fprintf(f, "%d\ttime since reset (secs)\n", timenow - resettime); + +#ifdef DMALLOC + /* malloc statistics */ + dmallocstats(f); +#endif + + /* query type statistics */ + fprintf(f, "%d\tUnknown query types\n", typestats[0]); + for(i=1; i < T_ANY+1; i++) + if (typestats[i]) + if (typenames[i]) + fprintf(f, "%lu\t%s queries\n", typestats[i], + typenames[i]); + else + fprintf(f, "%lu\ttype %d queries\n", + typestats[i], i); + + /* name server statistics */ + nameserStats(f); + + fprintf(f, "--- Statistics Dump --- (%ld) %s", + (long)timenow, ctime(&timenow)); + (void) my_fclose(f); +} + +void +qtypeIncr(qtype) + int qtype; +{ + if (qtype < T_A || qtype > T_ANY) + qtype = 0; /* bad type */ + typestats[qtype]++; +} + +static tree *nameserTree; +static int nameserInit; + +#ifdef STATS +static FILE *nameserStatsFile; +static u_long globalStats[nssLast]; +static const char *statNames[nssLast] = { + "RQ", /* sent us a query */ + "RR", /* sent us an answer */ + "RIQ", /* sent us an inverse query */ + "RNXD", /* sent us a negative response */ + "RFwdQ", /* sent us a query we had to fwd */ + "RFwdR", /* sent us a response we had to fwd */ + "RDupQ", /* sent us a retry */ + "RDupR", /* sent us an extra answer */ + "RFail", /* sent us a SERVFAIL */ + "RFErr", /* sent us a FORMERR */ + "RErr", /* sent us some other error */ + "RTCP", /* sent us a query using TCP */ + "RAXFR", /* sent us an AXFR */ + "RLame", /* sent us a lame delegation */ + "ROpts", /* sent us some IP options */ + "SSysQ", /* sent them a sysquery */ + "SAns", /* sent them an answer */ + "SFwdQ", /* fwdd a query to them */ + "SFwdR", /* fwdd a response to them */ + "SDupQ", /* sent them a retry */ + "SFail", /* sent them a SERVFAIL */ + "SFErr", /* sent them a FORMERR */ + }; +#endif /*STATS*/ + +static int +nameserCompar(t1, t2) + const tree_t t1, t2; +{ + u_int32_t a1 = ntohl(((struct nameser *)t1)->addr.s_addr), + a2 = ntohl(((struct nameser *)t2)->addr.s_addr); + + if (a1 < a2) + return (-1); + else if (a1 > a2) + return (1); + else + return (0); +} + +struct nameser * +nameserFind(addr, flags) + struct in_addr addr; + int flags; +{ + struct nameser dummy; + struct nameser *ns; + + if (!nameserInit) { + tree_init(&nameserTree); + nameserInit++; + } + + dummy.addr = addr; + ns = (struct nameser *)tree_srch(&nameserTree, nameserCompar, + (tree_t)&dummy); + if (!ns && (flags & NS_F_INSERT)) { + ns = (struct nameser *)malloc(sizeof(struct nameser)); + if (!ns) { + nomem: if (!haveComplained("nameserFind complaint", "")) + syslog(LOG_NOTICE, + "nameserFind: malloc failed; %m"); + return (NULL); + } + memset(ns, 0, sizeof(struct nameser)); + ns->addr = addr; + if (!tree_add(&nameserTree, nameserCompar, (tree_t)ns, NULL)) { + int save = errno; + free(ns); + errno = save; + goto nomem; + } + } + return (ns); +} + + +void +nameserIncr(addr, which) + struct in_addr addr; + enum nameserStats which; +{ +#ifdef STATS + struct nameser *ns = nameserFind(addr, NS_F_INSERT); + + if ((int)which < (int)nssLast) { + if (ns) + ns->stats[(int)which]++; + globalStats[(int)which]++; + } else { + syslog(LOG_DEBUG, "nameserIncr([%d], %d): bad 'which'", + inet_ntoa(addr), (int)which); + } +#endif /*STATS*/ +} + +#ifdef STATS +static void +nameserStatsOut(f, stats) + FILE *f; + u_long stats[]; +{ + int i; + char *pre = "\t"; + + for (i = 0; i < (int)nssLast; i++) { + fprintf(f, "%s%u", pre, stats[i]); + pre = ((i+1) % 5) ? " " : " "; + } + fputc('\n', f); +} + +static void +nameserStatsHdr(f) + FILE *f; +{ + int i; + char *pre = "\t"; + + fprintf(f, "(Legend)\n"); + for (i = 0; i < (int)nssLast; i++) { + fprintf(f, "%s%s", pre, + statNames[i] ? statNames[i] : ""); + pre = ((i+1) % 5) ? "\t" : "\n\t"; + } + fputc('\n', f); +} + +static int +nameserStatsTravUAR(t) + tree_t t; +{ + struct nameser *ns = (struct nameser *)t; + + fprintf(nameserStatsFile, "[%s]\n", /* : rtt %u */ + inet_ntoa(ns->addr) /*, ns->rtt*/ ); + nameserStatsOut(nameserStatsFile, ns->stats); + return (1); +} +#endif /*STATS*/ + +static void +nameserStats(f) + FILE *f; +{ +#ifndef STATS + fprintf(f, "<<No nameserver statistics in this server>>\n"); +#else + nameserStatsFile = f; + fprintf(f, "++ Name Server Statistics ++\n"); + nameserStatsHdr(f); + fprintf(f, "(Global)\n"); + nameserStatsOut(f, globalStats); + tree_trav(&nameserTree, nameserStatsTravUAR); + fprintf(f, "-- Name Server Statistics --\n"); + nameserStatsFile = NULL; +#endif /*STATS*/ +} diff --git a/usr.sbin/named/ns_validate.c b/usr.sbin/named/ns_validate.c new file mode 100644 index 000000000000..f1236c542e10 --- /dev/null +++ b/usr.sbin/named/ns_validate.c @@ -0,0 +1,1161 @@ +/************************************************************************** + * ns_validate.c (was security.c in original ISI contribution) + * author: anant kumar + * contributed: March 17, 1993 + * + * implements validation procedure for RR's received from a server as a + * response to a query. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <syslog.h> +#include <errno.h> +#include <stdio.h> +#include <resolv.h> + +#include "named.h" + +#ifdef VALIDATE + +static int isvalid __P((struct namebuf *, int, int, char *, int)), + check_addr_ns __P((struct databuf **, + struct sockaddr_in *, + char *)), + check_in_tables __P((struct databuf **, + struct sockaddr_in *, + char *)); +#if 0 +static void stick_in_queue __P((char *, int, int, char *)); +#endif + +static NAMEADDR nameaddrlist[MAXNAMECACHE]; +static int firstNA = 0, + lastNA = 0; + +static TO_Validate *validateQ, *currentVQ; +static int VQcount; + +/***************************************************************** + * validate() is called from dovalidate(). it takes as parameters, + * the domain name sought, the class, type etc. of record, the server + * that gave us the answer and the data it gave us + * + * it returns VALID if it is able to validate the record, INVALID if it cannot. + * furtehr VALID is split into VALID_CACHE if we need to cache this record + * since the domainname is not something we are authoritative for and + * VALID_NO_CACHE if the name is something we are authoritative for. + * + * pseudocode for function validate is as follows: + * validate(domain, server, type, class, data, dlen, rcode) { + * + * if(dname or a higher level name not found in cache) + * return INVALID; + * if (NS records for "domain" found in cache){ + * + * if(we are authoritative) /findns() returned NXDOMAIN;/ + * if(we did not have an exact match on names) + * =>the name does not exist in our database + * => data is bad: return INVALID + * if(data agrees with what we have) + * return VALID_NO_CACHE; + * else return INVALID; + * + * if(we are not authoritative) /findns() returned OK;/ + * if (address records for NS's found in cache){ + * if("server" = one of the addresses){ + * return VALID_CACHE; + * }else{ + * stick in queue of "to_validate" data; + * return (INVALID); + * } + * else return INVALID; + * + * This performs the validation procedure described above. Checks + * for the longest component of the dname that has a NS record + * associated with it. At any stage, if no data is found, it implies + * that the name is bad (has an unknown domain identifier) thus, we + * return INVALID. + * If address of one of these servers matches the address of the server + * that returned us this data, we are happy! + * + * since findns will set needs_prime_cache if np = NULL is passed, we always + * reset it. will let ns_req do it when we are searching for ns records to + * query someone. hence in all the three cases of switch(findns()) + * we have needs_prime_cache = 0; + *****************************************************************************/ +int +validate(dname, server, type, class, data, dlen +#ifdef NCACHE + ,rcode +#endif + ) + char *dname; + struct sockaddr_in *server; + int type; + int class; + char *data; + int dlen; +#ifdef NCACHE + int rcode; +#endif +{ + struct namebuf *np, *dnamep; + struct hashbuf *htp; + struct databuf *nsp[NSMAX]; + int count; + char *fname; + int exactmatch = 0; + struct fwdinfo *fwd; + +#ifdef DATUMREFCNT + nsp[0] = NULL; +#endif + dprintf(3, (ddt, + "validate(), d:%s, s:[%s], t:%d, c:%d\n", + dname, inet_ntoa(server->sin_addr), type, class)); + + /* everything from forwarders is the GOSPEL */ + for (fwd = fwdtab; fwd != NULL; fwd = fwd->next) { + if (server->sin_addr.s_addr == fwd->fwdaddr.sin_addr.s_addr) + return(VALID_CACHE); + } + + htp = hashtab; + if (priming && (dname[0] == '\0')) + np = NULL; + else + np = nlookup(dname, &htp, &fname, 0); + + /* we were able to locate namebufs for this domain, or a parent domain, + * or ??? */ + + if (np == NULL) { + fname = (char *)malloc(1); + fname[0] = '\0'; + } + dprintf(5, (ddt, + "validate:namebuf found np:0x%x, d:\"%s\", f:\"%s\"\n", + np, dname, fname)); + /* save the namebuf if we were able to locate the exact dname */ + if (!strcasecmp(dname, fname)) { + dnamep = np; + exactmatch = 1; + } + if (np == NULL && fname != NULL) { + free((char *)fname); + } + switch (findns(&np, class, nsp, &count, 0)) { + case NXDOMAIN: + /** we are authoritative for this domain, lookup name + * in our zone data, if it matches, return valid. + * in either case, do not cache + **/ + dprintf(5, (ddt, "validate: auth data found\n")); +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + if (needs_prime_cache) + needs_prime_cache = 0; + +#ifdef NCACHE + if(rcode == NXDOMAIN) { + /* If we had an exactmatch on the name, we found the + * name in our authority database, so this couldn't + * have been a bad name. INVALID data, say so + */ + if (exactmatch) + return INVALID; + else + /* we did not have an exactmatch, the data is + * good, we do not NCACHE stuff we are + * authoritative for, though. + */ + return VALID_NO_CACHE; + } +#endif + if (!strcasecmp(dname, np->n_dname)) { + + /* if the name we seek is the same as that we have ns + * records for, compare the data we have to see if it + * matches. if it does, return valid_no_cache, if it + * doesn't, invalid. + */ + if (isvalid(np, type, class, data, dlen)) + return VALID_NO_CACHE; + else + return INVALID; + } + + /* we found ns records in a higher level, if we were unable to + * locate the exact name earlier, it means we are + * authoritative for this domain but do not have records for + * this name. this name is obviously invalid + */ + if (!exactmatch) + return INVALID; + + /* we found the exact name earlier and we are obviously + * authoritative so check for data records and see if any + * match. + */ + if (isvalid(dnamep, type, class, data, dlen)) + return VALID_NO_CACHE; + else + return INVALID; + + case SERVFAIL:/* could not find name server records*/ + /* stick_in_queue(dname, type, class, data); */ + if (needs_prime_cache) + needs_prime_cache = 0; +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return INVALID; + + case OK: /*proceed */ + dprintf(5, + (ddt, + "validate:found ns records:calling check_addr_ns\n")); + if (needs_prime_cache) + needs_prime_cache = 0; + if (check_addr_ns(nsp, server, dname)) { +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return VALID_CACHE; + } + /* server is not one of those we know of */ + /* stick_in_queue(dname, type, class, data); */ +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return INVALID; + default: +#ifdef DATUMREFCNT + free_nsp(nsp); +#endif + return INVALID; + } /*switch*/ + +} /*validate*/ + +/*********************************************************************** + * validate rr returned by somebody against your own database, if you are + * authoritative for the information. if you have a record that matches, + * return 1, else return 0. validate() above will use this and determine + * if the record should be returned/discarded. + ***********************************************************************/ +static int +isvalid(np, type, class, data, dlen) + struct namebuf *np; + int type, class; + char *data; + int dlen; +{ + register struct databuf *dp; + + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!wanted(dp, class, type)) { + if ((type == T_CNAME) && (class == dp->d_class)) { + /* if a cname exists, any other will not */ + return(0); + /* we come here only for zone info, + * so -ve $ed info can't be + */ + } + continue; + } + /* type and class match, if i get here + * let's now compare the data section, per RR type + */ + + /* unless, of course, the data was negative, in which case + * we should return FAILURE since we should not have found + * data here. + */ + if ((data == NULL) || (dlen == 0)) { + return 0; + } + + /* XXX: why aren't we just calling db_cmp() ? + */ + + switch (type) { + char *td; + u_char *tdp; + int x; + + case T_A: + case T_WKS: + case T_HINFO: + case T_UINFO: + case T_UID: + case T_GID: + case T_TXT: + case T_X25: + case T_ISDN: +#ifdef ALLOW_T_UNSPEC + case T_UNSPEC: +#endif + x = memcmp(dp->d_data, data, dlen); + dprintf(3, (ddt, "type = %d, GOOD = %d\n", + type, x)); + if (x == 0) + return (1); + else + break; + + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + x = strncasecmp((char *)dp->d_data, data, dlen); + dprintf(3, (ddt, "type = %d, GOOD = %d\n", + type, x)); + if (x == 0) + return (1); + else + break; + + case T_MINFO: + case T_SOA: + case T_RP: + /* compare first string */ + x = strncasecmp((char *)dp->d_data, data, + strlen(data) + 1); + if (x != 0) + break; + + /* move to second string */ + td = data + (strlen(data) + 1); + tdp = dp->d_data + + (strlen((char *)dp->d_data)+1); + + /* compare second string */ + x = strncasecmp(td, (char *)tdp, + strlen((char *)td+1)); + if (x != 0) + break; + + /* move beyond second string, to + * set of words in SOA. + * RP and MINFO stuff really + * ends here + */ + + td = td + strlen((char *)td) + 1; + tdp = tdp + strlen((char *)tdp) + 1; + if (type == T_SOA) { + x = memcmp(td, (char *)tdp, + 5*INT32SZ); + if (x != 0) + break; + } + + /* everything was equal, wow! + * so return a success + */ + return (1); + + case T_MX: + case T_AFSDB: + case T_RT: + x = memcmp(dp->d_data, data, + INT16SZ); + if (x != 0) + break; + td = data + INT16SZ; + tdp = dp->d_data + INT16SZ; + x = strncasecmp(td, (char *)tdp, + strlen((char *)td) + 1); + if (x != 0) + break; + return (1); + + default: + dprintf(3, (ddt, "unknown type %d\n", type)); + return (0); + } + /* continue in loop if record did not match */ + } + /* saw no record of interest in whole chain + * If the data we were trying to validate was negative, we succeeded! + * else we failed + */ + if ((data == NULL) || (dlen == 0)) { + /* negative data, report success */ + return 1; + } + /* positive data, no such RR, validation failed */ + return (0); +} + +/****************************************************************** + * get a list of databufs that have ns addresses for the closest domain + * you know about, get their addresses and confirm that server indeed + * is one of them. if yes return 1 else 0. + * first checks the cache that we build in nslookup() earlier + * when we ns_forw(). if unableto find it there, it checks the entire + * hash table to do address translations. + *******************************************************************/ +static int +check_addr_ns(nsp, server, dname) + struct databuf **nsp; + struct sockaddr_in *server; + char *dname; +{ + int i, found=0; + char sname[MAXDNAME]; + struct in_addr *saddr = &(server->sin_addr); + struct databuf **nsdp; + + dprintf(5, (ddt, + "check_addr_ns: s:[%s], db:0x%x, d:\"%s\"\n", + inet_ntoa(*saddr), nsp, dname)); + + for(i = lastNA; i != firstNA; i = (i+1) % MAXNAMECACHE) { + if (!bcmp((char *)saddr, + (char *)&(nameaddrlist[i].ns_addr), + INADDRSZ)) { + strcpy(sname, nameaddrlist[i].nsname); + found = 1; + break; + } + } + if (found) { + dprintf(3, (ddt, + "check_addr_ns: found address:[%s]\n", + inet_ntoa(*saddr))); + for (nsdp = nsp; *nsdp != NULL;nsdp++) { + dprintf(5, (ddt, + "check_addr_ns:names are:%s, %s\n", + sname,(*nsdp)->d_data)); + if (!strcasecmp(sname,(char *)((*nsdp)->d_data))) { + return (1); + } + } + } + /* could not find name in my cache of servers, must go through the + * whole grind + */ + + dprintf(2, (ddt, "check_addr_ns:calling check_in_tables()\n")); + return (check_in_tables(nsp, server, dname)); +} + +/************************************************************************* + * checks in hash tables for the address of servers whose name is in the + * data section of nsp records. borrows code from nslookup()/ns_forw.c + * largely. + *************************************************************************/ +static int +check_in_tables(nsp, server, syslogdname) + struct databuf *nsp[]; + struct sockaddr_in *server; + char *syslogdname; +{ + register struct namebuf *np; + register struct databuf *dp, *nsdp; + struct hashbuf *tmphtp; + char *dname, *fname; + int class; + int qcomp(); + + dprintf(3, (ddt, "check_in_tables(nsp=x%x,qp=x%x,'%s')\n", + nsp, server, syslogdname)); + + while ((nsdp = *nsp++) != NULL) { + class = nsdp->d_class; + dname = (char *)nsdp->d_data; + dprintf(3, (ddt, "check_in_tables: NS %s c%d t%d (x%x)\n", + dname, class, nsdp->d_type, nsdp->d_flags)); + tmphtp = ((nsdp->d_flags & DB_F_HINT) ? fcachetab : hashtab); + np = nlookup(dname, &tmphtp, &fname, 1); + if (np == NULL || fname != dname) { + dprintf(3, (ddt, "%s: not found %s %x\n", + dname, fname, np)); + continue; + } + /* look for name server addresses */ + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (stale(dp)) + continue; + if (dp->d_type != T_A || dp->d_class != class) + continue; +#ifdef NCACHE + if (dp->d_rcode) + continue; +#endif + if (!bcmp((char *)dp->d_data, + (char *)&(server->sin_addr), + INADDRSZ)) { + return (1); + } + } + } + return (0); /* haven't been able to locate the right address */ +} + +/************************************************************************ + * is called in nslookup() and stores the name vs address of a name server + * --& check_in_tables above-- + * we contact, in a list of a maximum MAXNAMECACHE entries. we later refer + * -- NAMEADDR nameaddrlist[MAXNAMECACHE]; -- + * to this list when we are trying to resolve the name in check_addr_ns(). + *************************************************************************/ +void +store_name_addr(servername, serveraddr, syslogdname, sysloginfo) + char *servername; + struct in_addr *serveraddr; + char *syslogdname; + char *sysloginfo; +{ + int i; + + dprintf(3, (ddt, + "store_name_addr:s:%s, a:[%s]\n", + servername, inet_ntoa(*serveraddr))); + + /* if we already have the name address pair in cache, return */ + for (i = lastNA; i != firstNA; i = (i+1) % MAXNAMECACHE) { + if (strcasecmp(servername, nameaddrlist[i].nsname) == 0) { + if (serveraddr->s_addr + == + nameaddrlist[i].ns_addr.s_addr) { + dprintf(5, (ddt, + "store_name_addr:found n and a [%s] [%s] in our $\n", + inet_ntoa(nameaddrlist[i].ns_addr), + inet_ntoa(*serveraddr))); + return; + } /* if */ + } else if (serveraddr->s_addr + == + nameaddrlist[i].ns_addr.s_addr) { +#ifdef BAD_IDEA + /* + * log this as it needs to be fixed. + * replace old name by new, next query likely to have + * NS record matching new + */ + if (!haveComplained( + (char*)dhash((u_char*)nameaddrlist[i].nsname, + strlen(nameaddrlist[i].nsname)), + (char*)dhash((u_char*)servername, + strlen(servername)) + ) + ) { + syslog(LOG_INFO, + "%s: server name mismatch for [%s]: (%s != %s) (server for %s)", + sysloginfo, + inet_ntoa(*serveraddr), + nameaddrlist[i].nsname, servername, + syslogdname); + } +#endif + free(nameaddrlist[i].nsname); + nameaddrlist[i].nsname = + (char *)malloc((unsigned)strlen(servername)+1); + strcpy(nameaddrlist[i].nsname, servername); + return; + } + } + /* we have to add this one to our cache */ + + nameaddrlist[firstNA].nsname = + (char *)malloc((unsigned)strlen(servername)+1); + strcpy(nameaddrlist[firstNA].nsname, servername); + bcopy((char *)serveraddr, + (char *)&(nameaddrlist[firstNA].ns_addr), + INADDRSZ); + + dprintf(2, (ddt, "store_name_addr:added entry #:%d n:%s a:[%s]\n", + firstNA, nameaddrlist[firstNA].nsname, + inet_ntoa(nameaddrlist[firstNA].ns_addr))); + + firstNA = (firstNA+1) % MAXNAMECACHE; + if (firstNA == lastNA) { + free(nameaddrlist[firstNA].nsname); + nameaddrlist[firstNA].nsname = 0; + lastNA = (lastNA+1) % MAXNAMECACHE; + } + return; +} + +/* + * Decode the resource record 'rrp' and validate the RR. + * Borrows code almost entirely from doupdate(). is a rather + * non-invasive routine since it just goes thru the same motions + * as doupdate but just marks the array validatelist entry as + * the return code from validate(). This is later used in doupdate + * to cache/not cache the entry. also used in update_msg() to + * delete/keep the record from the outgoing message. + */ +int +dovalidate(msg, msglen, rrp, zone, flags, server, VCode) + u_char *msg, *rrp; + int msglen, zone, flags; + struct sockaddr_in *server; + int *VCode; +{ + register u_char *cp; + register int n; + int class, type, dlen, n1; + u_int32_t ttl; + char dname[MAXDNAME]; + u_char *cp1; + u_char data[BUFSIZ]; + register HEADER *hp = (HEADER *) msg; + + dprintf(2, (ddt, "dovalidate(zone %d, flags %x)\n", + zone, flags)); +#ifdef DEBUG + if (debug >= 10) + fp_query(msg, ddt); +#endif + + cp = rrp; + n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + dprintf(2, (ddt, "dovalidate: dname %s type %d class %d ttl %d\n", + dname, type, class, ttl)); + /* + * Convert the resource record data into the internal + * database format. + */ + switch (type) { + case T_A: + case T_WKS: + case T_HINFO: + case T_UINFO: + case T_UID: + case T_GID: + case T_TXT: + case T_X25: + case T_ISDN: +#ifdef ALLOW_T_UNSPEC + case T_UNSPEC: +#endif + 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, + (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = data; + n = strlen((char *)data) + 1; + break; + + case T_MINFO: + case T_SOA: + case T_RP: + n = dn_expand(msg, msg + msglen, cp, + (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = data + (n = strlen((char *)data) + 1); + n1 = sizeof(data) - n; + if (type == T_SOA) + n1 -= 5 * 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) { + bcopy((char *)cp, (char *)cp1, n = 5 * INT32SZ); + cp += n; + cp1 += n; + } + n = cp1 - data; + cp1 = data; + break; + + case T_MX: + case T_AFSDB: + case T_RT: + /* grab preference */ + bcopy((char *)cp, data, INT16SZ); + cp1 = data + INT16SZ; + cp += INT16SZ; + + /* get name */ + n = dn_expand(msg, msg + msglen, cp, + (char *)cp1, sizeof(data) - INT16SZ); + if (n < 0) { + hp->rcode = FORMERR; + return(-1); + } + cp += n; + + /* compute end of data */ + cp1 += strlen((char *)cp1) + 1; + /* compute size of data */ + n = cp1 - data; + cp1 = data; + break; + + default: + dprintf(3, (ddt, "unknown type %d\n", type)); + return ((cp - rrp) + dlen); + } + if (n > MAXDATA) { + dprintf(2, (ddt, + "update type %d: %d bytes is too much data\n", + type, n)); + hp->rcode = NOCHANGE; /* XXX - FORMERR ??? */ + return(-1); + } + + *VCode = validate(dname, server, type, class,(char *)cp1, n +#ifdef NCACHE + ,NOERROR +#endif + ); + if (*VCode == INVALID) { + dprintf(2, (ddt, + "validation failed d:%s, t:%d, c:%d\n", + dname, type, class)); + } else { + dprintf(2, (ddt, + "validation succeeded d:%s, t:%d, c:%d\n", + dname, type, class)); + } + return(cp -rrp); +} + +#if 0 +/****************************************************************** + * This manages a data structure that stores all RRs that we were + * unable to validate. Am not sure exactly what purpose this might + * serve but until such time as we are sure it will not help, let + * me do it anyway. + *****************************************************************/ +static void +stick_in_queue(dname, type, class, data) + char *dname; + int type; + int class; + char *data; +{ + struct timeval tp; + struct _TIMEZONE tzp; + TO_Validate *tempVQ; + u_long leasttime; + + if (validateQ == NULL) { + validateQ = (TO_Validate *)malloc(sizeof(TO_Validate)); + validateQ->type = type; + validateQ->class = class; + validateQ->dname = malloc((unsigned)strlen(dname)+1); + strcpy(validateQ->dname, dname); + validateQ->data = malloc((unsigned)strlen(data)+1); + strcpy(validateQ->data, data); + gettimeofday(&tp, &tzp); + validateQ->time = tp.tv_sec; + VQcount = 1; + validateQ->next = validateQ->prev = NULL; + currentVQ = validateQ; + return; + } + if (VQcount < MAXVQ) { + tempVQ =(TO_Validate *)malloc(sizeof(TO_Validate)); + tempVQ->type = type; + tempVQ->class = class; + tempVQ->dname = malloc((unsigned)strlen(dname)+1); + strcpy(tempVQ->dname, dname); + tempVQ->data = malloc((unsigned)strlen(data)+1); + strcpy(tempVQ->data, data); + gettimeofday(&tp,&tzp); + tempVQ->time = tp.tv_sec; + tempVQ->next = currentVQ->next; + tempVQ->prev = currentVQ; + if (currentVQ->next != NULL) + currentVQ->next->prev = tempVQ; + currentVQ->next = tempVQ; + currentVQ = tempVQ; + VQcount++; + return; + } + gettimeofday(&tp, &tzp); + leasttime = validateQ->time; + currentVQ = validateQ; + for (tempVQ = validateQ; tempVQ != NULL; tempVQ = tempVQ->next) { + if (tp.tv_sec >= tempVQ->time +VQEXPIRY) { + tempVQ->type = type; + tempVQ->class = class; + strcpy(tempVQ->dname, dname); + strcpy(tempVQ->data, data); + tempVQ->time = tp.tv_sec; + currentVQ = tempVQ; + return; + } + if (tempVQ->time < leasttime) { + leasttime = tempVQ->time; + currentVQ = tempVQ; + } + } + currentVQ->type = type; + currentVQ->class = class; + strcpy(currentVQ->dname, dname); + strcpy(currentVQ->data, data); + currentVQ->time = tp.tv_sec; + return; +} +#endif + +/* removes any INVALID RR's from the msg being returned, updates msglen to + * reflect the new message length. + */ +int +update_msg(msg, msglen, Vlist, c) + u_char *msg; + int *msglen; + int Vlist[]; + int c; +{ + register HEADER *hp; + register u_char *cp; + int i; + int n = 0; + u_char *tempcp, *newcp; + int *RRlen; + int qlen; /* the length of the query section*/ + u_int16_t rdlength; + u_int16_t ancount, nscount; + u_int16_t new_ancount, new_nscount, new_arcount; + char dname[MAXDNAME], qname[MAXDNAME]; + u_char data[MAXDNAME]; + u_char **dpp; + u_char *dnptrs[40]; + u_char **edp = dnptrs + sizeof(dnptrs)/sizeof(dnptrs[0]); + u_char *eom = msg + *msglen; + int n_new; + int rembuflen, newlen; + u_char *newmsg; + u_int16_t type, class, dlen; + u_int32_t ttl; + int inv = 0; + +#ifdef DEBUG + if (debug) { + fprintf(ddt, "update_msg: msglen:%d, c:%d\n", *msglen, c); + if (debug >= 10) + fp_query(msg, ddt); + } +#endif + /* just making sure we do not do all the work for nothing */ + for (i=0; i<c; i++) { + if (Vlist[i] == INVALID) { + inv = 1; + break; + } + } + if (inv != 1) { + /* no invalid records, go about your job */ + return (0); + } + + dprintf(2, (ddt, "update_msg: NEEDS updating:\n")); + + RRlen = (int *)malloc((unsigned)c*sizeof(int)); + hp = (HEADER *)msg; + new_ancount = ancount = ntohs(hp->ancount); + new_nscount = nscount = ntohs(hp->nscount); + new_arcount = ntohs(hp->arcount); + + cp = msg + HFIXEDSZ; + newlen = HFIXEDSZ; + /* skip the query section */ + qlen = dn_expand(msg, eom, cp, qname, sizeof qname); + if (qlen <= 0) { + dprintf(2, (ddt, "dn_skipname() failed, bad record\n")); + goto badend; + } + cp +=qlen; + GETSHORT(type,cp); + GETSHORT(class,cp); + qlen += 2 * INT16SZ; + newlen += qlen; + + for (i = 0; i < c; i++) { + if (Vlist[i] == INVALID) { + if (i < ancount) + new_ancount--; + else if (i < ancount+nscount) + new_nscount--; + else + new_arcount--; + } + + RRlen[i] = dn_skipname(cp, msg + *msglen); + if (RRlen[i] <= 0) { + dprintf(2, (ddt, + "dn_skipname() failed, bad record\n")); + goto badend; + } + RRlen[i] += 2 * INT16SZ + INT32SZ; + /*type+class+TTL*/ + cp += RRlen[i]; + GETSHORT(rdlength, cp); + RRlen[i] += INT16SZ; /*rdlength*/ + RRlen[i] += rdlength; /*rdata field*/ + dprintf(3, (ddt, "RRlen[%d]=%d\n", i, RRlen[i])); + if (Vlist[i] != INVALID) + newlen += RRlen[i]; + cp += rdlength; /*increment pointer to next RR*/ + } + hp->ancount = htons(new_ancount); + hp->nscount = htons(new_nscount); + hp->arcount = htons(new_arcount); + /* get new buffer */ + dprintf(3, (ddt, + "newlen:%d, if no RR is INVALID == msglen\n", newlen)); + newmsg = (u_char *)calloc(1,newlen + MAXDNAME); + if(newmsg == NULL) + goto badend; + dpp = dnptrs; + *dpp++ = newmsg; + *dpp = NULL; + /* bcopy the header, with all the length fields correctly put in */ + bcopy((char *)msg, (char*)newmsg, HFIXEDSZ); /*header copied */ + newcp = newmsg +HFIXEDSZ; /*need a pointer in the new buffer */ + rembuflen = newlen +MAXDNAME - HFIXEDSZ; /*buflen we can workin*/ + newlen = HFIXEDSZ; /* this will now contain the length of msg */ + n_new = dn_comp(qname, newcp, rembuflen, dnptrs, edp); + if (n_new < 0) + goto badend; + newcp += n_new; + PUTSHORT(type, newcp); + PUTSHORT(class, newcp); /*query section complete*/ + newlen += (n_new+2*INT16SZ); + rembuflen -= (n_new+2*INT16SZ); + /* have to decode and copy every Valid RR from here */ + + cp = msg +HFIXEDSZ +qlen; /*skip header and query section*/ + for (i = 0; i < c; i++) { + if (Vlist[i] == INVALID) { + /* go to next RR if this one is not INVALID */ + cp += RRlen[i]; + continue; + } + /* we have a valid record, must put it in the newmsg */ + n = dn_expand(msg, eom, cp, dname, sizeof dname); + if (n < 0) { + hp->rcode = FORMERR; + goto badend; + } + n_new = dn_comp(dname, newcp, rembuflen, dnptrs, edp); + if (n_new < 0) + goto badend; + cp += n; + newcp += n_new; + dprintf(5, (ddt, + "cp:0x%x newcp:0x%x after getting name\n", + cp, newcp)); + GETSHORT(type, cp); + PUTSHORT(type, newcp); + dprintf(5, (ddt, + "cp:0x%x newcp:0x%x after getting type\n", + cp, newcp)); + GETSHORT(class, cp); + PUTSHORT(class, newcp); + dprintf(5, (ddt, + "cp:0x%x newcp:0x%x after getting class\n", + cp, newcp)); + GETLONG(ttl, cp); + PUTLONG(ttl, newcp); + dprintf(5, (ddt, + "cp:0x%x newcp:0x%x after getting ttl\n", + cp, newcp)); + /* this will probably be modified for newmsg, + * will put this in later, after compression + */ + GETSHORT(dlen, cp); + newlen += (n_new+3*INT16SZ + INT32SZ); + rembuflen -= (n_new+3*INT16SZ+ INT32SZ); + tempcp = newcp; + newcp += INT16SZ; /*advance to rdata field*/ + dprintf(5, (ddt, "tempcp:0x%x newcp:0x%x\n", + tempcp, newcp)); + dprintf(3, (ddt, + "update_msg: dname %s type %d class %d ttl %d\n", + dname, type, class, ttl)); + /* read off the data section */ + switch (type) { + case T_A: + case T_WKS: + case T_HINFO: + case T_UINFO: + case T_UID: + case T_GID: + case T_TXT: + case T_X25: + case T_ISDN: +#ifdef ALLOW_T_UNSPEC + case T_UNSPEC: +#endif + n = dlen; + PUTSHORT(n, tempcp); /*time to put in the dlen*/ + bcopy(cp, newcp,n); /*done here*/ + cp +=n; + newcp +=n; + newlen += n; + rembuflen -= n; + dprintf(3, (ddt, "\tcp:0x%x newcp:0x%x dlen:%d\n", + cp, newcp, dlen)); + break; + + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + /*read off name from data section */ + n = dn_expand(msg, eom, cp, + (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + goto badend; + } + cp += n; /*advance pointer*/ + /* fill in new packet */ + n_new = dn_comp((char *)data, newcp, rembuflen, + dnptrs, edp); + if (n_new < 0) + goto badend; + PUTSHORT(n_new,tempcp); /*put in dlen field*/ + newcp += n_new; /*advance new pointer*/ + newlen += n_new; + rembuflen -= n_new; + break; + + case T_MINFO: + case T_SOA: + case T_RP: + n = dn_expand(msg, eom, cp, (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + goto badend; + } + cp += n; + n_new = dn_comp((char *)data, newcp, rembuflen, + dnptrs, edp); + if (n_new < 0) + goto badend; + newcp += n_new; + newlen += n_new; + rembuflen -= n_new; + dlen = n_new; + n = dn_expand(msg, eom, cp, (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + goto badend; + } + cp += n; + n_new = dn_comp((char *)data, newcp, rembuflen, + dnptrs, edp); + if (n_new < 0) + goto badend; + newcp += n_new; + newlen += n_new; + rembuflen -= n_new; + dlen += n_new; + if (type == T_SOA) { + bcopy(cp, newcp, n = 5*INT32SZ); + cp += n; + newcp += n; + newlen +=n; + rembuflen -= n; + dlen +=n; + } + PUTSHORT(dlen, tempcp); + break; + + case T_MX: + case T_AFSDB: + case T_RT: + /* grab preference */ + bcopy(cp,newcp,INT16SZ); + cp += INT16SZ; + newcp += INT16SZ; + + /* get name */ + n = dn_expand(msg, eom, cp, (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + goto badend; + } + cp += n; + n_new = dn_comp((char *)data, newcp, rembuflen, + dnptrs, edp); + if (n_new < 0) + goto badend; + PUTSHORT(n_new+INT16SZ, tempcp); + newcp += n_new; + newlen += n_new+INT16SZ; + rembuflen -= n_new+INT16SZ; + break; + + default: + dprintf(3, (ddt, "unknown type %d\n", type)); + goto badend; + } + dprintf(2, (ddt, + "newlen:%d, i:%d newcp:0x%x cp:0x%x\n\n", + newlen, i, newcp, cp)); + } + bcopy(newmsg, msg, newlen); + n = *msglen - newlen; + if (n < 0) { + dprintf(2, (ddt, + "update_msg():newmsg longer than old: n:%d o:%d ???\n", + newlen, *msglen)); + } + *msglen = newlen; + free((char *)newmsg); + +#ifdef DEBUG + if (debug >= 10) + fp_query(msg, ddt); +#endif + free((char *)RRlen); + return(n); +badend: + dprintf(2, (ddt, "encountered problems: UPDATE_MSG\n")); + free((char *)RRlen); + return(-1); +} + +#endif /*VALIDATE*/ diff --git a/usr.sbin/named/pathnames.h b/usr.sbin/named/pathnames.h new file mode 100644 index 000000000000..bcbe642cb4a3 --- /dev/null +++ b/usr.sbin/named/pathnames.h @@ -0,0 +1,122 @@ +/* + * @(#)pathnames.h 5.4 (Berkeley) 6/1/90 + * $Id: pathnames.h,v 4.9.1.5 1994/04/09 03:43:17 vixie Exp $ + */ + +/* + * ++Copyright++ 1989 + * - + * Copyright (c) 1989 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#ifdef _PATH_XFER +# define _PATH_XFER_PREDEFINED /* probably from Makefile */ +#endif + +#if defined (__sgi) && !defined(_SYSTYPE_SVR4) && !defined(__SYSTYPE_SVR4) +#define _PATH_BOOT "/usr/etc/named.d/named.boot" +#else +#define _PATH_BOOT "/etc/named.boot" +#endif + +#if defined(BSD) && BSD >= 198810 + +#include <paths.h> +#ifndef _PATH_XFER +# define _PATH_XFER "/usr/libexec/named-xfer" +#endif +#define _PATH_DEBUG "/var/tmp/named.run" +#define _PATH_DUMPFILE "/var/tmp/named_dump.db" +#ifndef _PATH_PIDFILE +# define _PATH_PIDFILE "/var/run/named.pid" +#endif +#define _PATH_STATS "/var/tmp/named.stats" +#define _PATH_XFERTRACE "/var/tmp/xfer.trace" +#define _PATH_XFERDDT "/var/tmp/xfer.ddt" +#define _PATH_TMPXFER "/var/tmp/xfer.ddt.XXXXXX" +#define _PATH_TMPDIR "/var/tmp" + +#else /* BSD */ + +#define _PATH_DEVNULL "/dev/null" +#define _PATH_TTY "/dev/tty" +#ifndef _PATH_XFER +# define _PATH_XFER "/etc/named-xfer" +#endif +#define _PATH_DEBUG "/usr/tmp/named.run" +#define _PATH_DUMPFILE "/usr/tmp/named_dump.db" +#ifndef _PATH_PIDFILE +# define _PATH_PIDFILE "/etc/named.pid" +#endif +#define _PATH_STATS "/usr/tmp/named.stats" +#define _PATH_XFERTRACE "/usr/tmp/xfer.trace" +#define _PATH_XFERDDT "/usr/tmp/xfer.ddt" +#define _PATH_TMPXFER "/usr/tmp/xfer.ddt.XXXXXX" +#define _PATH_TMPDIR "/usr/tmp" +#endif /* BSD */ + +#ifndef _PATH_XFER_PREDEFINED +# if defined(__sgi) || defined(NeXT) || defined(__ultrix) +# undef _PATH_XFER +# define _PATH_XFER "/usr/etc/named-xfer" +# endif +# if defined(__osf__) +# undef _PATH_XFER +# define _PATH_XFER "/usr/sbin/named-xfer" +# endif +# ifdef sun +# undef _PATH_XFER +# define _PATH_XFER "/usr/etc/in.named-xfer" +# endif +#else +# undef _PATH_XFER_PREDEFINED +#endif /*_PATH_XFER_PREDEFINED*/ diff --git a/usr.sbin/named/storage.c b/usr.sbin/named/storage.c new file mode 100644 index 000000000000..98a71b3f294b --- /dev/null +++ b/usr.sbin/named/storage.c @@ -0,0 +1,207 @@ +/* + * ++Copyright++ 1985, 1989 + * - + * Copyright (c) 1985, 1989 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#include <sys/param.h> +#include <syslog.h> +#include "../conf/portability.h" +#include "../conf/options.h" + +#ifdef DSTORAGE +/* + * S T O R A G E . C + * + * Ray Tracing program, storage manager. + * + * Functions - + * rt_malloc Allocate storage, with visibility & checking + * rt_free Similarly, free storage + * rt_prmem When debugging, print memory map + * calloc, cfree Which call rt_malloc, rt_free + * + * Author - + * Michael John Muuss + * + * Source - + * SECAD/VLD Computing Consortium, Bldg 394 + * The U. S. Army Ballistic Research Laboratory + * Aberdeen Proving Ground, Maryland 21005-5066 + * + * Copyright Notice - + * This software is Copyright (C) 1987 by the United States Army. + * All rights reserved. + */ +#ifndef lint +static char RCSid[] = "$Id: storage.c,v 4.9.1.2 1993/09/08 00:01:17 vixie Exp $"; +#endif + +#undef malloc +#undef free + +#define MDB_SIZE 20000 +#define MDB_MAGIC 0x12348969 +struct memdebug { + char *mdb_addr; + char *mdb_str; + int mdb_len; +} rt_mdb[MDB_SIZE]; + +/* + * R T _ M A L L O C + */ +char * +rt_malloc(cnt) +unsigned int cnt; +{ + register char *ptr; + + cnt = (cnt+2*sizeof(int)-1)&(~(sizeof(int)-1)); + ptr = malloc(cnt); + + if( ptr==(char *)0 ) { + syslog(LOG_ERR, "rt_malloc: malloc failure"); + abort(); + } else { + register struct memdebug *mp = rt_mdb; + for( ; mp < &rt_mdb[MDB_SIZE]; mp++ ) { + if( mp->mdb_len > 0 ) continue; + mp->mdb_addr = ptr; + mp->mdb_len = cnt; + mp->mdb_str = "???"; + goto ok; + } + syslog(LOG_ERR, "rt_malloc: memdebug overflow\n"); + } +ok: ; + { + register int *ip = (int *)(ptr+cnt-sizeof(int)); + *ip = MDB_MAGIC; + } + return(ptr); +} + +/* + * R T _ F R E E + */ +void +rt_free(ptr) +char *ptr; +{ + register struct memdebug *mp = rt_mdb; + for( ; mp < &rt_mdb[MDB_SIZE]; mp++ ) { + if( mp->mdb_len <= 0 ) continue; + if( mp->mdb_addr != ptr ) continue; + { + register int *ip = (int *)(ptr+mp->mdb_len-sizeof(int)); + if( *ip != MDB_MAGIC ) { + syslog(LOG_ERR, "ERROR rt_free(x%x, %s) corrupted! x%x!=x%x\n", ptr, "???", *ip, MDB_MAGIC); + abort(); + } + } + mp->mdb_len = 0; /* successful free */ + goto ok; + } + syslog(LOG_ERR, "ERROR rt_free(x%x, %s) bad pointer!\n", ptr, "???"); + abort(); +ok: ; + + *((int *)ptr) = -1; /* zappo! */ + free(ptr); +} + +/* + * R T _ P R M E M + * + * Print map of memory currently in use. + */ +void +rt_prmem(str) +char *str; +{ + register struct memdebug *mp = rt_mdb; + register int *ip; + + printf("\nRT memory use\t\t%s\n", str); + for( ; mp < &rt_mdb[MDB_SIZE]; mp++ ) { + if( mp->mdb_len <= 0 ) continue; + ip = (int *)(mp->mdb_addr+mp->mdb_len-sizeof(int)); + printf("%7x %5x %s %s\n", + mp->mdb_addr, mp->mdb_len, mp->mdb_str, + *ip!=MDB_MAGIC ? "-BAD-" : "" ); + if( *ip != MDB_MAGIC ) + printf("\t%x\t%x\n", *ip, MDB_MAGIC); + } +} + +char * +calloc(num, size) + register unsigned num, size; +{ + register char *p; + + size *= num; + if (p = rt_malloc(size)) + bzero(p, size); + return (p); +} + +cfree(p, num, size) + char *p; + unsigned num; + unsigned size; +{ + rt_free(p); +} + +#endif /*DSTORAGE*/ diff --git a/usr.sbin/named/tools/named.reload/named.reload.sh b/usr.sbin/named/tools/named.reload/named.reload.sh new file mode 100644 index 000000000000..a25de1b87f61 --- /dev/null +++ b/usr.sbin/named/tools/named.reload/named.reload.sh @@ -0,0 +1,6 @@ +#!/bin/sh - +# +# from named.reload 5.2 (Berkeley) 6/27/89 +# $Id: named.reload.sh,v 4.9.1.2 1993/09/08 00:01:17 vixie Exp $ +# +kill -HUP `cat %PIDDIR%/named.pid` diff --git a/usr.sbin/named/tools/named.restart/named.restart.sh b/usr.sbin/named/tools/named.restart/named.restart.sh new file mode 100644 index 000000000000..8a61ccca4df4 --- /dev/null +++ b/usr.sbin/named/tools/named.restart/named.restart.sh @@ -0,0 +1,12 @@ +#!/bin/sh - +# +# from named.restart 5.4 (Berkeley) 6/27/89 +# $Id: named.restart.sh,v 4.9.1.5 1994/04/09 03:43:17 vixie Exp $ +# + +PATH=%DESTSBIN%:/bin:/usr/bin + +pid=`cat %PIDDIR%/named.pid` +kill $pid +sleep 5 +exec %INDOT%named diff --git a/usr.sbin/named/tools/nsquery/nsquery.c b/usr.sbin/named/tools/nsquery/nsquery.c new file mode 100644 index 000000000000..c7ff6ebe4d72 --- /dev/null +++ b/usr.sbin/named/tools/nsquery/nsquery.c @@ -0,0 +1,140 @@ +/* + * ++Copyright++ 1986 + * - + * Copyright (c) 1986 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1986 Regents of the University of California.\n\ + portions Copyright (c) 1993 Digital Equipment Corporation\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)nsquery.c 4.8 (Berkeley) 6/1/90"; +static char rcsid[] = "$Id: nsquery.c,v 4.9.1.4 1994/06/11 22:05:07 vixie Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <netdb.h> +#include <resolv.h> +#include "../conf/portability.h" + +main(argc, argv) + int argc; + char **argv; +{ + extern struct __res_state _res; + register struct hostent *hp; + register char *s; + + res_init(); + + if (argc >= 2 && strcmp(argv[1], "-d") == 0) { + _res.options |= RES_DEBUG; + argc--; + argv++; + } + if (argc < 2) { + fprintf(stderr, "usage: nsquery [-d] host [server]\n"); + exit(1); + } + if (argc == 3) { + hp = gethostbyname(argv[2]); + if (hp == NULL) { + fprintf(stderr, "nsquery:"); + herror(argv[2]); + exit(1); + } + printf("\nServer:\n"); + printanswer(hp); + _res.nsaddr.sin_addr = *(struct in_addr *)hp->h_addr; +#ifdef nsaddr /* struct __res_state includes nscount and nsaddr_list[] */ + _res.nscount = 1; +#endif + } + + hp = gethostbyname(argv[1]); + if (hp == NULL) { + fprintf(stderr, "nsquery: %s: ", argv[1]); + herror((char *)NULL); + exit(1); + } + printanswer(hp); + exit(0); +} + +printanswer(hp) + register struct hostent *hp; +{ + register char **cp; + + printf("Name: %s\n", hp->h_name); +#if BSD >= 43 || defined(h_addr) + printf("Addresses:"); + for (cp = hp->h_addr_list; cp && *cp; cp++) + printf(" %s", inet_ntoa(*(struct in_addr *)(*cp))); + printf("\n"); +#else + printf("Address: %s\n", inet_ntoa(*(struct in_addr *)hp->h_addr)); +#endif + printf("Aliases:"); + for (cp = hp->h_aliases; cp && *cp && **cp; cp++) + printf(" %s", *cp); + printf("\n\n"); +} diff --git a/usr.sbin/named/tools/nstest/nstest.c b/usr.sbin/named/tools/nstest/nstest.c new file mode 100644 index 000000000000..f20388d8696e --- /dev/null +++ b/usr.sbin/named/tools/nstest/nstest.c @@ -0,0 +1,424 @@ +/* + * ++Copyright++ 1986 + * - + * Copyright (c) 1986 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1986 Regents of the University of California.\n\ + portions Copyright (c) 1993 Digital Equipment Corporation\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)nstest.c 4.15 (Berkeley) 3/21/91"; +static char rcsid[] = "$Id: nstest.c,v 4.9.1.6 1994/06/01 21:10:11 vixie Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <stdio.h> +#include <resolv.h> +#include "../conf/portability.h" + +char *progname; +FILE *log; +#define MAXDATA 256 /* really should get this from named/db.h */ +main(argc, argv) + char **argv; +{ + register char *cp; + register u_char *ucp; + struct hostent *hp; + u_short port = htons(NAMESERVER_PORT); + char buf[BUFSIZ]; + u_char packet[PACKETSZ], answer[8*1024], OldRRData[MAXDATA]; + struct rrec NewRR; + u_int32_t l; + int n, dump_packet; + + NewRR.r_data = (char *) malloc(MAXDATA); + NewRR.r_data = (char *) malloc(MAXDATA); + progname = argv[0]; + dump_packet = 0; + _res.options |= RES_DEBUG|RES_RECURSE; + (void) res_init(); + while (argc > 1 && argv[1][0] == '-') { + argc--; + cp = *++argv; + while (*++cp) + switch (*cp) { + case 'p': + if (--argc <= 0) + usage(); + port = htons(atoi(*++argv)); + break; + + case 'i': + _res.options |= RES_IGNTC; + break; + + case 'v': + _res.options |= RES_USEVC|RES_STAYOPEN; + break; + + case 'r': + _res.options &= ~RES_RECURSE; + break; + + case 'd': + dump_packet++; + break; + + default: + usage(); + } + } + _res.nsaddr.sin_family = AF_INET; + _res.nsaddr.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1); + _res.nsaddr.sin_port = port; + if (argc > 1) { + if (!inet_aton(argv[1], + (struct in_addr *)&_res.nsaddr.sin_addr)) + usage(); + } + if (argc > 2) { + log = fopen(argv[2],"w"); + if (log == NULL) perror(argv[2]); + } + for (;;) { + printf("> "); + fflush(stdout); + if ((cp = fgets(buf, sizeof buf, stdin)) == NULL) + break; + switch (*cp++) { + case 'a': + n = res_mkquery(QUERY, cp, C_IN, T_A, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'A': + n = ntohl(inet_addr(cp)); + putlong((u_int32_t)n, (u_char*)cp); + n = res_mkquery(IQUERY, "", C_IN, T_A, (u_char *)cp, + INT32SZ, NULL, + packet, sizeof(packet)); + break; + + case 'f': + n = res_mkquery(QUERY, cp, C_ANY, T_UINFO, NULL, + 0, NULL, packet, sizeof packet); + break; + + case 'F': + n = res_mkquery(QUERY, cp, C_IN, T_AFSDB, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'g': + n = res_mkquery(QUERY, cp, C_ANY, T_GID, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'G': + *(int *)cp = htonl(atoi(cp)); + n = res_mkquery(IQUERY, "", C_ANY, T_GID, (u_char *)cp, + sizeof(int), NULL, packet, sizeof packet); + break; + + case 'c': + n = res_mkquery(QUERY, cp, C_IN, T_CNAME, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'h': + n = res_mkquery(QUERY, cp, C_IN, T_HINFO, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'm': + n = res_mkquery(QUERY, cp, C_IN, T_MX, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'M': + n = res_mkquery(QUERY, cp, C_IN, T_MAILB, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'n': + n = res_mkquery(QUERY, cp, C_IN, T_NS, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'p': + n = res_mkquery(QUERY, cp, C_IN, T_PTR, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 's': + n = res_mkquery(QUERY, cp, C_IN, T_SOA, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'T': + n = res_mkquery(QUERY, cp, C_IN, T_TXT, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'u': + n = res_mkquery(QUERY, cp, C_ANY, T_UID, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'U': + *(int *)cp = htonl(atoi(cp)); + n = res_mkquery(IQUERY, "", C_ANY, T_UID, (u_char *)cp, + sizeof(int), NULL, + packet, sizeof packet); + break; + + case 'x': + n = res_mkquery(QUERY, cp, C_IN, T_AXFR, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'w': + n = res_mkquery(QUERY, cp, C_IN, T_WKS, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'b': + n = res_mkquery(QUERY, cp, C_IN, T_MB, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'B': + n = res_mkquery(QUERY, cp, C_IN, T_MG, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'i': + n = res_mkquery(QUERY, cp, C_IN, T_MINFO, NULL, 0, + NULL, packet, sizeof packet); + break; + + case 'r': + n = res_mkquery(QUERY, cp, C_IN, T_MR, NULL, 0, + NULL, packet, sizeof packet); + break; + + case '*': + n = res_mkquery(QUERY, cp, C_IN, T_ANY, NULL, 0, + NULL, packet, sizeof packet); + break; + +#ifdef ALLOW_UPDATES + case '^': + { + char IType[10], TempStr[50]; + int Type, oldnbytes, nbytes, i; +#ifdef ALLOW_T_UNSPEC + printf("Data type (a = T_A, u = T_UNSPEC): "); + gets(IType); + if (IType[0] == 'u') { + Type = T_UNSPEC; + printf("How many data bytes? "); + gets(TempStr); /* Throw away CR */ + sscanf(TempStr, "%d", &nbytes); + for (i = 0; i < nbytes; i++) { + (NewRR.r_data)[i] = (char) i; + } + } else { +#endif /* ALLOW_T_UNSPEC */ + Type = T_A; + nbytes = INT32SZ; + printf( + "Inet addr for new dname (e.g., 192.4.3.2): " + ); + gets(TempStr); + putlong(ntohl(inet_addr(TempStr)), + NewRR.r_data); +#ifdef ALLOW_T_UNSPEC + } +#endif + NewRR.r_class = C_IN; + NewRR.r_type = Type; + NewRR.r_size = nbytes; + NewRR.r_ttl = 99999999; + printf("Add, modify, or modify all (a/m/M)? "); + gets(TempStr); + if (TempStr[0] == 'a') { + n = res_mkquery(UPDATEA, cp, C_IN, Type, + OldRRData, nbytes, + &NewRR, packet, + sizeof packet); + } else { + if (TempStr[0] == 'm') { + printf("How many data bytes in old RR? "); + gets(TempStr); /* Throw away CR */ + sscanf(TempStr, "%d", &oldnbytes); + for (i = 0; i < oldnbytes; i++) { + OldRRData[i] = (char) i; + } + n = res_mkquery(UPDATEM, cp, + C_IN, Type, + OldRRData, oldnbytes, + &NewRR, packet, + sizeof packet); + } else { /* Modify all */ + n = res_mkquery(UPDATEMA, cp, + C_IN, Type, NULL, 0, + &NewRR, packet, + sizeof packet); + + } + } + } + break; + +#ifdef ALLOW_T_UNSPEC + case 'D': + n = res_mkquery(UPDATEDA, cp, C_IN, T_UNSPEC, + (char *)0, 0, NULL, + packet, sizeof packet); + break; + + case 'd': + { + char TempStr[100]; + int nbytes, i; + printf("How many data bytes in oldrr data? "); + gets(TempStr); /* Throw away CR */ + sscanf(TempStr, "%d", &nbytes); + for (i = 0; i < nbytes; i++) { + OldRRData[i] = (char) i; + } + n = res_mkquery(UPDATED, cp, C_IN, T_UNSPEC, + OldRRData, nbytes, NULL, + packet, sizeof packet); + } + break; +#endif /* ALLOW_T_UNSPEC */ +#endif /* ALLOW_UPDATES */ + + default: + printf("a{host} - query T_A\n"); + printf("A{addr} - iquery T_A\n"); + printf("b{user} - query T_MB\n"); + printf("B{user} - query T_MG\n"); + printf("f{host} - query T_UINFO\n"); + printf("g{host} - query T_GID\n"); + printf("G{gid} - iquery T_GID\n"); + printf("h{host} - query T_HINFO\n"); + printf("i{host} - query T_MINFO\n"); + printf("p{host} - query T_PTR\n"); + printf("m{host} - query T_MX\n"); + printf("M{host} - query T_MAILB\n"); + printf("n{host} - query T_NS\n"); + printf("r{host} - query T_MR\n"); + printf("s{host} - query T_SOA\n"); + printf("T{host} - query T_TXT\n"); + printf("u{host} - query T_UID\n"); + printf("U{uid} - iquery T_UID\n"); + printf("x{host} - query T_AXFR\n"); + printf("w{host} - query T_WKS\n"); + printf("F{host} - query T_AFSDB\n"); + printf("c{host} - query T_CNAME\n"); + printf("*{host} - query T_ANY\n"); +#ifdef ALLOW_UPDATES + printf("^{host} - add/mod/moda (T_A/T_UNSPEC)\n"); +#ifdef ALLOW_T_UNSPEC + printf("D{host} - deletea T_UNSPEC\n"); + printf("d{host} - delete T_UNSPEC\n"); +#endif /* ALLOW_T_UNSPEC */ +#endif /* ALLOW_UPDATES */ + continue; + } + if (n < 0) { + printf("res_mkquery: buffer too small\n"); + continue; + } + if (log) { + fprintf(log,"SEND QUERY\n"); + fp_query(packet, log); + } + n = res_send(packet, n, answer, sizeof(answer)); + if (n < 0) { + printf("res_send: send error\n"); + if (log) fprintf(log, "res_send: send error\n"); + } + else { + if (dump_packet) { + int f; + f = creat("ns_packet.dump", 0644); + write(f, answer, n); + (void) close(f); + } + if (log) { + fprintf(log, "GOT ANSWER\n"); + fp_query(answer, log); + } + } + } +} + +usage() +{ + fprintf(stderr, "Usage: %s [-v] [-i] [-r] [-d] [-p port] hostaddr\n", + progname); + exit(1); +} diff --git a/usr.sbin/named/tree.c b/usr.sbin/named/tree.c new file mode 100644 index 000000000000..58607ea0bd48 --- /dev/null +++ b/usr.sbin/named/tree.c @@ -0,0 +1,570 @@ +/* tree - balanced binary tree library + * + * vix 05apr94 [removed vixie.h dependencies; cleaned up formatting, names] + * vix 22jan93 [revisited; uses RCS, ANSI, POSIX; has bug fixes] + * vix 23jun86 [added delete uar to add for replaced nodes] + * vix 20jun86 [added tree_delete per wirth a+ds (mod2 v.) p. 224] + * vix 06feb86 [added tree_mung()] + * vix 02feb86 [added tree balancing from wirth "a+ds=p" p. 220-221] + * vix 14dec85 [written] + */ + + +/* This program text was created by Paul Vixie using examples from the book: + * "Algorithms & Data Structures," Niklaus Wirth, Prentice-Hall, 1986, ISBN + * 0-13-022005-1. Any errors in the conversion from Modula-2 to C are Paul + * Vixie's. + * + * This code and associated documentation is hereby placed in the public + * domain, with the wish that my name and Prof. Wirth's not be removed + * from the source or documentation. + */ + + +#ifndef LINT +static char RCSid[] = "$Id:"; +#endif + + +/*#define DEBUG "tree"*/ + + +#include <stdio.h> +#ifndef _PATH_XFER +# include <stdlib.h> +#else +# include "../conf/portability.h" +#endif +#include "tree.h" + + +#ifdef DEBUG +static int debugDepth = 0; +static char *debugFuncs[256]; +# define ENTER(proc) { \ + debugFuncs[debugDepth] = proc; \ + fprintf(stderr, "ENTER(%d:%s.%s)\n", \ + debugDepth, DEBUG, + debugFuncs[debugDepth]); \ + debugDepth++; \ + } +# define RET(value) { \ + debugDepth--; \ + fprintf(stderr, "RET(%d:%s.%s)\n", \ + debugDepth, DEBUG, \ + debugFuncs[debugDepth]); \ + return (value); \ + } +# define RETV { \ + debugDepth--; \ + fprintf(stderr, "RETV(%d:%s.%s)\n", \ + debugDepth, DEBUG, \ + debugFuncs[debugDepth]); \ + return; \ + } +# define MSG(msg) fprintf(stderr, "MSG(%s)\n", msg); +#else +# define ENTER(proc) ; +# define RET(value) return (value); +# define RETV return; +# define MSG(msg) ; +#endif + + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + + +static tree * sprout __P( (tree **, tree_t, int *, int (*)(), void (*)()) ); +static int delete __P( (tree **, int (*)(), tree_t, void (*)(), + int *, int *) ); +static void del __P( (tree **, int *, tree **, void (*)(), int *) ); +static void bal_L __P( (tree **, int *) ); +static void bal_R __P( (tree **, int *) ); + + +void +tree_init(ppr_tree) + tree **ppr_tree; +{ + ENTER("tree_init") + *ppr_tree = NULL; + RETV +} + + +tree_t +tree_srch(ppr_tree, pfi_compare, p_user) + tree **ppr_tree; + int (*pfi_compare)(); + tree_t p_user; +{ + register int i_comp; + + ENTER("tree_srch") + + if (*ppr_tree) { + i_comp = (*pfi_compare)(p_user, (**ppr_tree).data); + + if (i_comp > 0) + RET(tree_srch(&(**ppr_tree).right, + pfi_compare, + p_user)) + + if (i_comp < 0) + RET(tree_srch(&(**ppr_tree).left, + pfi_compare, + p_user)) + + /* not higher, not lower... this must be the one. + */ + RET((**ppr_tree).data) + } + + /* grounded. NOT found. + */ + RET(NULL) +} + + +tree_t +tree_add(ppr_tree, pfi_compare, p_user, pfv_uar) + tree **ppr_tree; + int (*pfi_compare)(); + tree_t p_user; + void (*pfv_uar)(); +{ + int i_balance = FALSE; + + ENTER("tree_add") + if (!sprout(ppr_tree, p_user, &i_balance, pfi_compare, pfv_uar)) + RET(NULL) + RET(p_user) +} + + +int +tree_delete(ppr_p, pfi_compare, p_user, pfv_uar) + tree **ppr_p; + int (*pfi_compare)(); + tree_t p_user; + void (*pfv_uar)(); +{ + int i_balance = FALSE, + i_uar_called = FALSE; + + ENTER("tree_delete"); + RET(delete(ppr_p, pfi_compare, p_user, pfv_uar, + &i_balance, &i_uar_called)) +} + + +int +tree_trav(ppr_tree, pfi_uar) + tree **ppr_tree; + int (*pfi_uar)(); +{ + ENTER("tree_trav") + + if (!*ppr_tree) + RET(TRUE) + + if (!tree_trav(&(**ppr_tree).left, pfi_uar)) + RET(FALSE) + if (!(*pfi_uar)((**ppr_tree).data)) + RET(FALSE) + if (!tree_trav(&(**ppr_tree).right, pfi_uar)) + RET(FALSE) + RET(TRUE) +} + + +void +tree_mung(ppr_tree, pfv_uar) + tree **ppr_tree; + void (*pfv_uar)(); +{ + ENTER("tree_mung") + if (*ppr_tree) { + tree_mung(&(**ppr_tree).left, pfv_uar); + tree_mung(&(**ppr_tree).right, pfv_uar); + if (pfv_uar) + (*pfv_uar)((**ppr_tree).data); + free(*ppr_tree); + *ppr_tree = NULL; + } + RETV +} + + +static tree * +sprout(ppr, p_data, pi_balance, pfi_compare, pfv_delete) + tree **ppr; + tree_t p_data; + int *pi_balance; + int (*pfi_compare)(); + void (*pfv_delete)(); +{ + tree *p1, *p2, *sub; + int cmp; + + ENTER("sprout") + + /* are we grounded? if so, add the node "here" and set the rebalance + * flag, then exit. + */ + if (!*ppr) { + MSG("grounded. adding new node, setting h=true") + *ppr = (tree *) malloc(sizeof(tree)); + if (*ppr) { + (*ppr)->left = NULL; + (*ppr)->right = NULL; + (*ppr)->bal = 0; + (*ppr)->data = p_data; + *pi_balance = TRUE; + } + RET(*ppr); + } + + /* compare the data using routine passed by caller. + */ + cmp = (*pfi_compare)(p_data, (*ppr)->data); + + /* if LESS, prepare to move to the left. + */ + if (cmp < 0) { + MSG("LESS. sprouting left.") + sub = sprout(&(*ppr)->left, p_data, pi_balance, + pfi_compare, pfv_delete); + if (sub && *pi_balance) { /* left branch has grown */ + MSG("LESS: left branch has grown") + switch ((*ppr)->bal) { + case 1: /* right branch WAS longer; bal is ok now */ + MSG("LESS: case 1.. bal restored implicitly") + (*ppr)->bal = 0; + *pi_balance = FALSE; + break; + case 0: /* balance WAS okay; now left branch longer */ + MSG("LESS: case 0.. balnce bad but still ok") + (*ppr)->bal = -1; + break; + case -1: /* left branch was already too long. rebal */ + MSG("LESS: case -1: rebalancing") + p1 = (*ppr)->left; + if (p1->bal == -1) { /* LL */ + MSG("LESS: single LL") + (*ppr)->left = p1->right; + p1->right = *ppr; + (*ppr)->bal = 0; + *ppr = p1; + } else { /* double LR */ + MSG("LESS: double LR") + + p2 = p1->right; + p1->right = p2->left; + p2->left = p1; + + (*ppr)->left = p2->right; + p2->right = *ppr; + + if (p2->bal == -1) + (*ppr)->bal = 1; + else + (*ppr)->bal = 0; + + if (p2->bal == 1) + p1->bal = -1; + else + p1->bal = 0; + *ppr = p2; + } /*else*/ + (*ppr)->bal = 0; + *pi_balance = FALSE; + } /*switch*/ + } /*if*/ + RET(sub) + } /*if*/ + + /* if MORE, prepare to move to the right. + */ + if (cmp > 0) { + MSG("MORE: sprouting to the right") + sub = sprout(&(*ppr)->right, p_data, pi_balance, + pfi_compare, pfv_delete); + if (sub && *pi_balance) { + MSG("MORE: right branch has grown") + + switch ((*ppr)->bal) { + case -1: + MSG("MORE: balance was off, fixed implicitly") + (*ppr)->bal = 0; + *pi_balance = FALSE; + break; + case 0: + MSG("MORE: balance was okay, now off but ok") + (*ppr)->bal = 1; + break; + case 1: + MSG("MORE: balance was off, need to rebalance") + p1 = (*ppr)->right; + if (p1->bal == 1) { /* RR */ + MSG("MORE: single RR") + (*ppr)->right = p1->left; + p1->left = *ppr; + (*ppr)->bal = 0; + *ppr = p1; + } else { /* double RL */ + MSG("MORE: double RL") + + p2 = p1->left; + p1->left = p2->right; + p2->right = p1; + + (*ppr)->right = p2->left; + p2->left = *ppr; + + if (p2->bal == 1) + (*ppr)->bal = -1; + else + (*ppr)->bal = 0; + + if (p2->bal == -1) + p1->bal = 1; + else + p1->bal = 0; + + *ppr = p2; + } /*else*/ + (*ppr)->bal = 0; + *pi_balance = FALSE; + } /*switch*/ + } /*if*/ + RET(sub) + } /*if*/ + + /* not less, not more: this is the same key! replace... + */ + MSG("FOUND: Replacing data value") + *pi_balance = FALSE; + if (pfv_delete) + (*pfv_delete)((*ppr)->data); + (*ppr)->data = p_data; + RET(*ppr) +} + + +static int +delete(ppr_p, pfi_compare, p_user, pfv_uar, pi_balance, pi_uar_called) + tree **ppr_p; + int (*pfi_compare)(); + tree_t p_user; + void (*pfv_uar)(); + int *pi_balance; + int *pi_uar_called; +{ + tree *pr_q; + int i_comp, i_ret; + + ENTER("delete") + + if (*ppr_p == NULL) { + MSG("key not in tree") + RET(FALSE) + } + + i_comp = (*pfi_compare)((*ppr_p)->data, p_user); + if (i_comp > 0) { + MSG("too high - scan left") + i_ret = delete(&(*ppr_p)->left, pfi_compare, p_user, pfv_uar, + pi_balance, pi_uar_called); + if (*pi_balance) + bal_L(ppr_p, pi_balance); + } else if (i_comp < 0) { + MSG("too low - scan right") + i_ret = delete(&(*ppr_p)->right, pfi_compare, p_user, pfv_uar, + pi_balance, pi_uar_called); + if (*pi_balance) + bal_R(ppr_p, pi_balance); + } else { + MSG("equal") + pr_q = *ppr_p; + if (pr_q->right == NULL) { + MSG("right subtree null") + *ppr_p = pr_q->left; + *pi_balance = TRUE; + } else if (pr_q->left == NULL) { + MSG("right subtree non-null, left subtree null") + *ppr_p = pr_q->right; + *pi_balance = TRUE; + } else { + MSG("neither subtree null") + del(&pr_q->left, pi_balance, &pr_q, + pfv_uar, pi_uar_called); + if (*pi_balance) + bal_L(ppr_p, pi_balance); + } + if (!*pi_uar_called && pfv_uar) + (*pfv_uar)(pr_q->data); + free(pr_q); /* thanks to wuth@castrov.cuc.ab.ca */ + i_ret = TRUE; + } + RET(i_ret) +} + + +static void +del(ppr_r, pi_balance, ppr_q, pfv_uar, pi_uar_called) + tree **ppr_r; + int *pi_balance; + tree **ppr_q; + void (*pfv_uar)(); + int *pi_uar_called; +{ + ENTER("del") + + if ((*ppr_r)->right != NULL) { + del(&(*ppr_r)->right, pi_balance, ppr_q, + pfv_uar, pi_uar_called); + if (*pi_balance) + bal_R(ppr_r, pi_balance); + } else { + if (pfv_uar) + (*pfv_uar)((*ppr_q)->data); + *pi_uar_called = TRUE; + (*ppr_q)->data = (*ppr_r)->data; + *ppr_q = *ppr_r; + *ppr_r = (*ppr_r)->left; + *pi_balance = TRUE; + } + + RETV +} + + +static void +bal_L(ppr_p, pi_balance) + tree **ppr_p; + int *pi_balance; +{ + tree *p1, *p2; + int b1, b2; + + ENTER("bal_L") + MSG("left branch has shrunk") + + switch ((*ppr_p)->bal) { + case -1: + MSG("was imbalanced, fixed implicitly") + (*ppr_p)->bal = 0; + break; + case 0: + MSG("was okay, is now one off") + (*ppr_p)->bal = 1; + *pi_balance = FALSE; + break; + case 1: + MSG("was already off, this is too much") + p1 = (*ppr_p)->right; + b1 = p1->bal; + if (b1 >= 0) { + MSG("single RR") + (*ppr_p)->right = p1->left; + p1->left = *ppr_p; + if (b1 == 0) { + MSG("b1 == 0") + (*ppr_p)->bal = 1; + p1->bal = -1; + *pi_balance = FALSE; + } else { + MSG("b1 != 0") + (*ppr_p)->bal = 0; + p1->bal = 0; + } + *ppr_p = p1; + } else { + MSG("double RL") + p2 = p1->left; + b2 = p2->bal; + p1->left = p2->right; + p2->right = p1; + (*ppr_p)->right = p2->left; + p2->left = *ppr_p; + if (b2 == 1) + (*ppr_p)->bal = -1; + else + (*ppr_p)->bal = 0; + if (b2 == -1) + p1->bal = 1; + else + p1->bal = 0; + *ppr_p = p2; + p2->bal = 0; + } + } + RETV +} + + +static void +bal_R(ppr_p, pi_balance) + tree **ppr_p; + int *pi_balance; +{ + tree *p1, *p2; + int b1, b2; + + ENTER("bal_R") + MSG("right branch has shrunk") + switch ((*ppr_p)->bal) { + case 1: + MSG("was imbalanced, fixed implicitly") + (*ppr_p)->bal = 0; + break; + case 0: + MSG("was okay, is now one off") + (*ppr_p)->bal = -1; + *pi_balance = FALSE; + break; + case -1: + MSG("was already off, this is too much") + p1 = (*ppr_p)->left; + b1 = p1->bal; + if (b1 <= 0) { + MSG("single LL") + (*ppr_p)->left = p1->right; + p1->right = *ppr_p; + if (b1 == 0) { + MSG("b1 == 0") + (*ppr_p)->bal = -1; + p1->bal = 1; + *pi_balance = FALSE; + } else { + MSG("b1 != 0") + (*ppr_p)->bal = 0; + p1->bal = 0; + } + *ppr_p = p1; + } else { + MSG("double LR") + p2 = p1->right; + b2 = p2->bal; + p1->right = p2->left; + p2->left = p1; + (*ppr_p)->left = p2->right; + p2->right = *ppr_p; + if (b2 == -1) + (*ppr_p)->bal = 1; + else + (*ppr_p)->bal = 0; + if (b2 == 1) + p1->bal = -1; + else + p1->bal = 0; + *ppr_p = p2; + p2->bal = 0; + } + } + RETV +} diff --git a/usr.sbin/named/tree.h b/usr.sbin/named/tree.h new file mode 100644 index 000000000000..2cad7c1233ae --- /dev/null +++ b/usr.sbin/named/tree.h @@ -0,0 +1,48 @@ +/* tree.h - declare structures used by tree library + * + * vix 22jan93 [revisited; uses RCS, ANSI, POSIX; has bug fixes] + * vix 27jun86 [broken out of tree.c] + * + * $Id: tree.h,v 1.1 1994/04/09 04:05:46 vixie Exp $ + */ + + +#ifndef _TREE_H_INCLUDED +#define _TREE_H_INCLUDED + + +#ifndef __P +# if defined(__STDC__) || defined(__GNUC__) +# define __P(x) x +# else +# define __P(x) () +# endif +#endif + +/* + * tree_t is our package-specific anonymous pointer. + */ +#if defined(__STDC__) || defined(__GNUC__) +typedef void *tree_t; +#else +typedef char *tree_t; +#endif + + +typedef struct tree_s { + tree_t data; + struct tree_s *left, *right; + short bal; + } + tree; + + +void tree_init __P((tree **)); +tree_t tree_srch __P((tree **, int (*)(), tree_t)); +tree_t tree_add __P((tree **, int (*)(), tree_t, void (*)())); +int tree_delete __P((tree **, int (*)(), tree_t, void (*)())); +int tree_trav __P((tree **, int (*)())); +void tree_mung __P((tree **, void (*)())); + + +#endif /* _TREE_H_INCLUDED */ diff --git a/usr.sbin/named/tree.man3 b/usr.sbin/named/tree.man3 new file mode 100644 index 000000000000..5be48783e2b6 --- /dev/null +++ b/usr.sbin/named/tree.man3 @@ -0,0 +1,154 @@ +.TH TREE 3 "5 April 1994" +.\" from .TH TREE 3 "22 Jan 1993" +.\" from .TH TREE 2 "23 June 1986" +.UC 4 +.SH NAME +tree_init, tree_mung, tree_srch, tree_add, tree_delete, tree_trav +\- balanced binary tree routines +.SH SYNOPSIS +.nf +.B void +.B tree_init(tree) +.B void **tree; +.PP +.B void * +.B tree_srch(tree, compare, data) +.B void **tree; +.B int (*compare)(); +.B void *data; +.PP +.B void +.B tree_add(tree, compare, data, del_uar) +.B void **tree; +.B int (*compare)(); +.B void *data; +.B void (*del_uar)(); +.PP +.B int +.B tree_delete(tree, compare, data, del_uar) +.B void **tree; +.B int (*compare)(); +.B void *data; +.B void (*del_uar)(); +.PP +.B int +.B tree_trav(tree, trav_uar) +.B void **tree; +.B int (*trav_uar)(); +.PP +.B void +.B tree_mung(tree, del_uar) +.B void **tree; +.B void (*del_uar)(); +.fi +.SH DESCRIPTION +These functions create and manipulate a balanced binary (AVL) tree. Each node +of the tree contains the expected left & right subtree pointers, a short int +balance indicator, and a pointer to the user data. On a 32 bit system, this +means an overhead of 4+4+2+4 bytes per node (or, on a RISC or otherwise +alignment constrained system with implied padding, 4+4+4+4 bytes per node). +There is no key data type enforced by this package; a caller supplied +compare routine is used to compare user data blocks. +.PP +Balanced binary trees are very fast on searches and replacements, but have a +moderately high cost for additions and deletions. If your application does a +lot more searches and replacements than it does additions and deletions, the +balanced (AVL) binary tree is a good choice for a data structure. +.PP +.I Tree_init +creates an empty tree and binds it to +.I tree +(which for this and all other routines in this package should be declared as +a pointer to void or int, and passed by reference), which can then be used by +other routines in this package. Note that more than one +.I tree +variable can exist at once; thus multiple trees can be manipulated +simultaneously. +.PP +.I Tree_srch +searches a tree for a specific node and returns either +.I NULL +if no node was found, or the value of the user data pointer if the node +was found. +.I compare +is the address of a function to compare two user data blocks. This routine +should work much the way +.IR strcmp (3) +does; in fact, +.I strcmp +could be used if the user data was a \s-2NUL\s+2 terminated string. +.I data +is the address of a user data block to be used by +.I compare +as the search criteria. The tree is searched for a node where +.I compare +returns 0. +.PP +.I Tree_add +inserts or replaces a node in the specified tree. The tree specified by +.I tree +is searched as in +.I tree_srch, +and if a node is found to match +.I data, +then the +.I del_uar +function, if non\-\s-2NULL\s+2, is called with the address of the user data +block for the node (this routine should deallocate any dynamic memory which +is referenced exclusively by the node); the user data pointer for the node +is then replaced by the value of +.I data. +If no node is found to match, a new node is added (which may or may not +cause a transparent rebalance operation), with a user data pointer equal to +.I data. +A rebalance may or may not occur, depending on where the node is added +and what the rest of the tree looks like. +.I Tree_add +will return the +.I data +pointer unless catastrophe occurs in which case it will return \s-2NULL\s+2. +.PP +.I Tree_delete +deletes a node from +.I tree. +A rebalance may or may not occur, depending on where the node is removed from +and what the rest of the tree looks like. +.I Tree_delete +returns TRUE if a node was deleted, FALSE otherwise. +.PP +.I Tree_trav +traverses all of +.I tree, +calling +.I trav_uar +with the address of each user data block. If +.I trav_uar +returns FALSE at any time, +.I tree_trav +will immediately return FALSE to its caller. Otherwise all nodes will be +reached and +.I tree_trav +will return TRUE. +.PP +.I Tree_mung +deletes every node in +.I tree, +calling +.I del_uar +(if it is not \s-2NULL\s+2) with the user data address from each node (see +.I tree_add +and +.I tree_delete +above). The tree is left in the same state that +.I tree_init +leaves it in \- i.e., empty. +.SH BUGS +Should have a way for the caller to specify application specific +.I malloc +and +.I free +functions to be used internally when allocating meta data. +.SH AUTHOR +Paul Vixie, converted and augumented from Modula\-2 examples in +.I Algorithms & Data Structures, +Niklaus Wirth, Prentice\-Hall, ISBN 0\-13\-022005\-1. diff --git a/usr.sbin/named/xfer/named-xfer.c b/usr.sbin/named/xfer/named-xfer.c new file mode 100644 index 000000000000..5d8a9cdd39de --- /dev/null +++ b/usr.sbin/named/xfer/named-xfer.c @@ -0,0 +1,1481 @@ +/* + * 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++ 1988, 1990 + * - + * 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. + * - + * --Copyright-- + */ + +#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\ + 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 4.9.1.23 1994/07/22 08:42:39 vixie Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <net/if.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv.h> +#include <stdio.h> +#include <syslog.h> +#include <math.h> +#include <signal.h> + +#define MAIN_PROGRAM +#include "named.h" +#undef MAIN_PROGRAM + +#ifndef LOG_PERROR +# define LOG_PERROR 0 +#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, + domain_len; /* strlen(domain) */ + +static FILE *fp = NULL, + *dbfp = NULL; + +static void usage __P((const char *)); +static int getzone __P((struct zoneinfo *, u_int32_t, int)), + soa_zinfo __P((struct zoneinfo *, u_char *, u_char *)), + print_output __P((u_char *, int, u_char *)), + netread __P((int, char *, int, int)); +static SIG_FN read_alarm __P((void)); +static char *ProgName; + +extern char *optarg; +extern int optind, getopt(); + +void +main(argc, argv) + int argc; + char *argv[]; +{ + register struct zoneinfo *zp; + register struct hostent *hp; + char *dbfile = NULL, *tracefile = NULL, *tm = NULL; + int dbfd, ddtd, result, c, fd, closed = 0; + u_int32_t serial_no = 0; + u_int16_t port = htons(NAMESERVER_PORT); + struct stat statbuf; +#ifdef STUBS + int stub_only = 0; +#endif +#ifdef GEN_AXFR + int class = C_IN; +#endif + + if (ProgName = strrchr(argv[0], '/')) + ProgName++; + else + ProgName = argv[0]; + + (void) umask(022); + + /* this is a hack; closing everything in the parent is hard. */ + for (fd = getdtablesize()-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_DAEMON + openlog(ProgName, LOG_PID|LOG_CONS|LOG_PERROR, LOGFAC); +#else + openlog(ProgName, LOG_PID); +#endif +#ifdef STUBS + while ((c = getopt(argc, argv, "C:d:l:s:t:z:f:p:P:qS")) != EOF) +#else + while ((c = getopt(argc, argv, "C:d:l:s:t:z:f:p:P:q")) != EOF) +#endif + switch (c) { +#ifdef GEN_AXFR + case 'C': + class = get_class(optarg); + break; +#endif + case 'd': +#ifdef DEBUG + debug = atoi(optarg); +#endif + break; + case 'l': + ddtfile = (char *)malloc(strlen(optarg) + + sizeof(".XXXXXX") + 1); +#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); +#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 '?': + 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); + } +#if HAVE_FCHMOD + 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; + } +#if 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 { +#if defined(SYSV) + setvbuf(ddt, NULL, _IOLBF, BUFSIZ); +#else + setlinebuf(ddt); +#endif + } + } +#endif + /* + * Ignore many types of signals that named (assumed to be our parent) + * considers important- if not, the user controlling named with + * signals usually kills us. + */ + (void) signal(SIGHUP, SIG_IGN); +#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(SIGIOT, SIG_IGN); + +#if defined(SIGUSR1) && defined(SIGUSR2) + (void) signal(SIGUSR1, SIG_IGN); + (void) signal(SIGUSR2, SIG_IGN); +#else /* SIGUSR1&&SIGUSR2 */ + (void) signal(SIGEMT, SIG_IGN); + (void) signal(SIGFPE, SIG_IGN); +#endif /* SIGUSR1&&SIGUSR2 */ + + dprintf(1, (ddt, + "domain `%s'; file `%s'; serial %lu; 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; +#ifdef GEN_AXFR + zp->z_class = class; +#endif + zp->z_origin = domain; + zp->z_source = dbfile; + zp->z_addrcnt = 0; + dprintf(1, (ddt, "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++, zp->z_addrcnt++) { + 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); + zp->z_addrcnt--; /* hack */ + continue; + } + bcopy(hp->h_addr, + (char *)&zp->z_addr[zp->z_addrcnt], + INADDRSZ); + dprintf(1, (ddt, "Arg: \"%s\"\n", tm)); + } + if (zp->z_addr[zp->z_addrcnt].s_addr == 0) { + syslog(LOG_NOTICE, + "SOA query to localhost (%s) for %s", + tm, zp->z_origin); + zp->z_addrcnt--; /* hack */ + continue; + } + if (zp->z_addrcnt >= NSMAX) { + zp->z_addrcnt = NSMAX; + dprintf(1, (ddt, "NSMAX reached\n")); + break; + } + } + dprintf(1, (ddt, "addrcnt = %d\n", zp->z_addrcnt)); + + _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); + + case XFER_TIMEOUT: +#ifdef DEBUG + if (!debug) +#endif + (void) unlink(tmpname); + exit(XFER_TIMEOUT); /* servers not reachable exit */ + + case XFER_FAIL: + default: +#ifdef DEBUG + if (!debug) +#endif + (void) unlink(tmpname); + exit(XFER_FAIL); /* yuck exit */ + } + /*NOTREACHED*/ +} + +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 +#ifdef GEN_AXFR + "\t[-C class]\n", +#endif + "\tservers...\n", + NULL +}; + +static void +usage(msg) + 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" */ +int minimum_ttl = 0, got_soa = 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(zp, serial_no, port) + struct zoneinfo *zp; + u_int32_t serial_no; + int port; +{ + HEADER *hp; + u_int16_t len; + u_int32_t serial; + int s, n, l, cnt, nscnt, soacnt, error = 0; + u_char *cp, *nmp, *eom, *tmp ; + u_char *buf = NULL; + int bufsize; + char name[MAXDNAME], name2[MAXDNAME]; + struct sockaddr_in sin; + struct zoneinfo zp_start, zp_finish; +#ifdef POSIX_SIGNALS + struct sigaction sv, osv; +#else + struct sigvec sv, osv; +#endif + int ancount, aucount; + int Class; +#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 + sv.sa_handler = 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); +#else + sv.sv_handler = read_alarm; + sv.sv_onstack = 0; + sv.sv_mask = ~0; + (void) sigvec(SIGALRM, &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++) { +#ifdef GEN_AXFR + Class = zp->z_class; +#else + Class = C_IN; +#endif + error = 0; + if (buf == NULL) { + if ((buf = (u_char *)malloc(2 * PACKETSZ)) == NULL) { + syslog(LOG_ERR, "malloc(%u) failed", + 2 * PACKETSZ); + error++; + break; + } + bufsize = 2 * PACKETSZ; + } + bzero((char *)&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = (u_int16_t)port; + sin.sin_addr = zp->z_addr[cnt]; + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + syslog(LOG_ERR, "socket: %m"); + error++; + break; + } + dprintf(2, (ddt, "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) { + syslog(LOG_ERR, "connect(%s) failed: %m", + inet_ntoa(sin.sin_addr)); + error++; + (void) my_close(s); + continue; + } +tryagain: + n = res_mkquery(QUERY, zp->z_origin, Class, + T_SOA, NULL, 0, NULL, buf, bufsize); + if (n < 0) { + if (!quiet) + syslog(LOG_NOTICE, + "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_ERR, "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 = _getshort(buf)) == 0) { + (void) my_close(s); + continue; + } + if (len > bufsize) { + if ((buf = (u_char *)realloc(buf, len)) == NULL) { + syslog(LOG_ERR, + "malloc(%u) failed for SOA from server [%s], zone %s\n", + len, + inet_ntoa(sin.sin_addr), + zp->z_origin); + (void) 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_query(buf, ddt); + } +#endif + hp = (HEADER *) buf; + ancount = ntohs(hp->ancount); + aucount = ntohs(hp->nscount); + + /* + * close socket if: + * 1) rcode != NOERROR + * 2) not an authority response + * 3) both the number of answers and authority count < 1) + */ + if (hp->rcode != NOERROR || !(hp->aa) || + (ancount < 1 && aucount < 1)) { +#ifndef GEN_AXFR + if (Class == C_IN) { + dprintf(1, (ddt, "SOA failed, trying C_HS\n")); + Class = C_HS; + goto tryagain; + } +#endif + syslog(LOG_NOTICE, + "%s from [%s], zone %s: rcode %d, aa %d, ancount %d, aucount %d\n", + "bad response to SOA query", + inet_ntoa(sin.sin_addr), zp->z_origin, + hp->rcode, hp->aa, ancount, aucount); + error++; + (void) my_close(s); + continue; + } + zp_start = *zp; + if (len < HFIXEDSZ + QFIXEDSZ) { + badsoa: + syslog(LOG_NOTICE, + "malformed SOA from [%s], zone %s: too short\n", + inet_ntoa(sin.sin_addr), zp->z_origin); + error++; + (void) my_close(s); + continue; + } + tmp = buf + HFIXEDSZ; + eom = buf + len; + if ((n = dn_skipname(tmp, eom)) == -1) + goto badsoa; + tmp += n + QFIXEDSZ; + if ((n = dn_skipname(tmp, eom)) == -1) + goto badsoa; + tmp += n; + if (soa_zinfo(&zp_start, tmp, eom) == -1) + goto badsoa; + if (SEQ_GT(zp_start.z_serial, serial_no) || !serial_no) { + dprintf(1, (ddt, "need update, serial %d\n", + zp_start.z_serial)); + hp = (HEADER *) buf; + soacnt = 0; + nscnt = 0; + gettime (&tt); + (void) fprintf (dbfp, "; zone '%s' last serial %lu\n", + domain, serial_no); + (void) fprintf (dbfp, "; from %s at %s", + inet_ntoa (sin.sin_addr), + ctime (&tt.tv_sec)); + for (;;) { + if ((soacnt == 0) || (zp->z_type == Z_STUB)) { + int type; +#ifdef STUBS + if (zp->z_type == Z_STUB) { + if (!soacnt) + type = T_SOA; + else if (!nscnt) + type = T_NS; + else + type = T_SOA; + } else +#endif + type = T_AXFR; + n = res_mkquery(QUERY, zp->z_origin, + Class, type, NULL, 0, + NULL, buf, bufsize); + if (n < 0) { + if (!quiet) { +#ifdef STUBS + if (zp->z_type == Z_STUB) + syslog(LOG_NOTICE, + (type == T_SOA) + ? "zone %s: res_mkquery T_SOA failed" + : "zone %s: res_mkquery T_NS failed", + zp->z_origin); + else +#endif + syslog(LOG_NOTICE, + "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_ERR,"writemsg: %m"); + error++; + (void) my_close(s); + break; + } + } + /* + * Receive length & response + */ + if (netread(s, (char *)buf, INT16SZ, + (soacnt == 0) ?300 :XFER_TIMER) + < 0) { + error++; + break; + } + if ((len = _getshort(buf)) == 0) + break; + 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_query(buf, ddt); + } + if (fp) + fp_query(buf, fp); +#endif + if (len < HFIXEDSZ) { + badrec: + error++; + syslog(LOG_NOTICE, + "record too short from [%s], zone %s\n", + 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); + for (cnt = 0 ; cnt < ancount ; cnt++) { + + n = print_output(buf, bufsize, cp); + cp += n; + } + if (hp->nscount) { + /* we should not get here */ + ancount = ntohs(hp->nscount); + for (cnt = 0 ; cnt < ancount ; cnt++) { + n = print_output(buf, bufsize, cp); + cp += n; + } + } + ancount = ntohs(hp->arcount); + for (cnt = 0 ; cnt < ancount ; cnt ++) { + n = print_output(buf, bufsize, cp); + cp += n; + } + if (cp != eom) { + syslog(LOG_ERR, + "print_output: short answer (%d, %d), zone %s", + cp - buf, n, zp->z_origin); + error++; + break; + } + + } else { +#endif /*STUBS*/ + n = print_output(buf, bufsize, cp); + if (cp + n != eom) { + syslog(LOG_ERR, + "print_output: short answer (%d, %d), zone %s", + cp - buf, n, zp->z_origin); + error++; + break; + } +#ifdef STUBS + } +#endif + GETSHORT(n, tmp); + if (n == T_SOA) { + if (soacnt == 0) { + soacnt++; + if (dn_expand(buf, buf + 512, nmp, + name, sizeof name) == -1) + goto badsoa; + if (eom - tmp + <= 2 * INT16SZ + + INT32SZ) { + goto badsoa; + } + tmp += 2 * INT16SZ + + INT32SZ; + if ((n = dn_skipname(tmp, eom)) == -1) + goto badsoa; + tmp += n; + if ((n = dn_skipname(tmp, eom)) == -1) + goto badsoa; + tmp += n; + if (eom - tmp <= INT32SZ) + goto badsoa; + GETLONG(serial, tmp); + dprintf(3, (ddt, + "first SOA for %s, serial %d\n", + name, serial)); + continue; + } + if (dn_expand(buf, buf + 512, nmp, + name2, sizeof name2) == -1) + goto badsoa; + if (strcasecmp((char *)name, + (char *)name2) != 0) { + dprintf(2, (ddt, + "extraneous SOA for %s\n", + name2)); + continue; + } + tmp -= INT16SZ; + if (soa_zinfo(&zp_finish, tmp, eom) == -1) + goto badsoa; + dprintf(2, (ddt, + "SOA, serial %d\n", + zp_finish.z_serial)); + if (serial != zp_finish.z_serial) { + soacnt = 0; + got_soa = 0; + minimum_ttl = 0; + strcpy(prev_origin, zp->z_origin); + prev_dname[0] = DEF_DNAME; + dprintf(1, (ddt, + "serial changed, restart\n" + )); + /* + * Flush buffer, truncate file + * and seek to beginning to restart. + */ + fflush(dbfp); + if (ftruncate(fileno(dbfp), 0) != 0) { + if (!quiet) + syslog(LOG_ERR, + "ftruncate %s: %m\n", + tmpname); + return (XFER_FAIL); + } + fseek(dbfp, 0L, 0); + } else + break; +#ifdef STUBS + } else if (zp->z_type == Z_STUB && n == T_NS) { + nscnt++; + } else if (zp->z_type == Z_STUB) { + break; +#endif + } + } + (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, (ddt, "error receiving zone transfer\n")); + } else if (zp_start.z_serial == serial_no) { + (void) my_close(s); + dprintf(1, (ddt, + "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); +} + +/* + * Set flag saying to read was interrupted + * used for a read timer + */ +static SIG_FN +read_alarm() +{ + read_interrupted = 1; +} + +static int +netread(fd, buf, len, timeout) + int fd; + register char *buf; + register int len; + int timeout; +{ + static const char setitimerStr[] = "setitimer: %m"; + struct itimerval ival, zeroival; + register int n; +#if defined(sun) + 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_ERR, setitimerStr); + return (-1); + } + errno = 0; + n = recv(fd, buf, len, 0); + if (n <= 0) { + if (errno == 0) { +#if defined(sun) + if (++retries < 42) /* doug adams */ + continue; +#endif + syslog(LOG_ERR, + "recv(len=%d): n=%d && !errno", + len, n); + return (-1); + } + if (errno == EINTR && !read_interrupted) { + /* Some other signal returned; ignore it. */ + continue; + } + syslog(LOG_ERR, "recv(len=%d): %m", len); + return (-1); + } + buf += n; + len -= n; + } + if (setitimer(ITIMER_REAL, &zeroival, NULL) < 0) { + syslog(LOG_ERR, setitimerStr); + return (-1); + } + return (0); +} + +static int +soa_zinfo(zp, cp, eom) + register struct zoneinfo *zp; + register u_char *cp; + u_char *eom; +{ + register int n; + + if (eom - cp < 3 * INT16SZ + INT32SZ) + return (-1); + cp += 3 * INT16SZ + INT32SZ; + if ((n = dn_skipname(cp, eom)) == -1) + return (-1); + cp += n; + if ((n = dn_skipname(cp, eom)) == -1) + return (-1); + cp += n; + if (eom - cp < 5 * INT32SZ) + return (-1); + GETLONG(zp->z_serial, cp); + GETLONG(zp->z_refresh, cp); + GETLONG(zp->z_retry, cp); + GETLONG(zp->z_expire, cp); + GETLONG(zp->z_minimum, cp); + return (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. + */ +static int +print_output(msg, msglen, rrp) + u_char *msg; + int msglen; + u_char *rrp; +{ + register u_char *cp; + register HEADER *hp = (HEADER *) msg; + u_int32_t addr, ttl; + int i, j, tab, result, class, type, dlen, n1, n; + char data[BUFSIZ]; + u_char *cp1, *cp2, *temp_ptr; + char *cdata, *origin, *proto, dname[MAXDNAME]; + char *ignore = ""; +#ifdef NO_GLUE + int lend, lenn; +#endif /*NO_GLUE*/ + + cp = rrp; + n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + + origin = strchr(dname, '.'); + if (origin == NULL) + origin = ""; + else + origin++; /* move past the '.' */ + dprintf(3, (ddt, + "print_output: dname %s type %d class %d ttl %d\n", + dname, type, class, ttl)); + /* + * Convert the resource record data into the internal database format. + */ + switch (type) { + case T_A: + case T_WKS: + case T_HINFO: + case T_UINFO: + case T_TXT: + case T_X25: + case T_ISDN: + case T_UID: + case T_GID: + cp1 = cp; + n = dlen; + cp += n; + break; + + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + 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) { + temp_ptr = cp + 4 * INT32SZ; + GETLONG(minimum_ttl, temp_ptr); + n = 5 * INT32SZ; + bcopy((char *) cp, (char *) cp1, n); + cp += n; + cp1 += n; + } + n = cp1 - (u_char *)data; + cp1 = (u_char *)data; + break; + + case T_MX: + case T_AFSDB: + case T_RT: + /* grab preference */ + bcopy((char *)cp, data, INT16SZ); + cp1 = (u_char *)data + INT16SZ; + cp += INT16SZ; + + /* get name */ + n = dn_expand(msg, msg + msglen, cp, + (char *)cp1, sizeof data - INT16SZ); + 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; + + default: + dprintf(3, (ddt, "unknown type %d\n", type)); + return ((cp - rrp) + dlen); + } + if (n > MAXDATA) { + dprintf(1, (ddt, + "update type %d: %d bytes is too much data\n", + type, n)); + hp->rcode = NOCHANGE; /* XXX - FORMERR ??? */ + return (-1); + } + cdata = (char *) cp1; + result = cp - rrp; + + /* + * Only print one SOA per db file + */ + if (type == T_SOA) { + if (got_soa) + return (result); + else + got_soa++; + } + +#ifdef NO_GLUE + /* + * 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 vs 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 = "; "; + } +#endif /*NO_GLUE*/ + + /* + * 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 = strchr(dname, '.'); + + (void) strcpy(prev_dname, dname); + if (cutp) + *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 + (void) fprintf(dbfp, "%s%s\t", ignore, dname); + if (strlen(dname) < 8) + tab = 1; + } else { + (void) fprintf(dbfp, "%s\t", ignore); + tab = 1; + } + + if (ttl != 0 && ttl != minimum_ttl) + (void) fprintf(dbfp, "%d\t", (int) ttl); + else if (tab) + (void) putc('\t', dbfp); + + (void) fprintf(dbfp, "%s\t%s\t", p_class(class), p_type(type)); + cp = (u_char *) cdata; + + /* + * Print type specific data + */ + switch (type) { + + case T_A: + switch (class) { + case C_IN: + case C_HS: + GETLONG(n, cp); + n = htonl(n); + (void) fprintf(dbfp, "%s", + inet_ntoa(*(struct in_addr *) & n)); + break; + } + (void) fprintf(dbfp, "\n"); + break; + + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_PTR: + if (cp[0] == '\0') + (void) fprintf(dbfp, ".\n"); + else + (void) fprintf(dbfp, "%s.\n", cp); + break; + + case T_NS: + cp = (u_char *) cdata; + if (cp[0] == '\0') + (void) fprintf(dbfp, ".\t"); + else + (void) fprintf(dbfp, "%s.", cp); + (void) fprintf(dbfp, "\n"); + break; + + case T_HINFO: + case T_ISDN: + cp2 = cp + n; + n = *cp++; + cp1 = cp + n; + if (cp1 > cp2) + cp1 = cp2; + (void) putc('"', dbfp); + while (cp < cp1) { + if (*cp == '\0') { + cp = cp1; + break; + } + if ((*cp == '\n') || (*cp == '"')) { + (void) putc('\\', dbfp); + } + (void) putc(*cp++, dbfp); + } + (void) fputs("\" \"", dbfp); + n = *cp++; + cp1 = cp + n; + if (cp1 > cp2) + cp1 = cp2; + while (cp < cp1) { + if (*cp == '\0') { + cp = cp1; + break; + } + if ((*cp == '\n') || (*cp == '"')) { + (void) putc('\\', dbfp); + } + (void) putc(*cp++, dbfp); + } + (void) fputs("\"\n", dbfp); + break; + + case T_SOA: + (void) fprintf(dbfp, "%s.", cp); + cp += strlen((char *) cp) + 1; + (void) fprintf(dbfp, " %s. (\n", cp); + cp += strlen((char *) cp) + 1; + GETLONG(n, cp); + (void) fprintf(dbfp, "%s\t\t%lu", ignore, n); + GETLONG(n, cp); + (void) fprintf(dbfp, " %lu", n); + GETLONG(n, cp); + (void) fprintf(dbfp, " %lu", n); + GETLONG(n, cp); + (void) fprintf(dbfp, " %lu", n); + GETLONG(n, cp); + (void) fprintf(dbfp, " %lu )\n", n); + break; + + case T_MX: + case T_AFSDB: + case T_RT: + GETSHORT(n, cp); + (void) fprintf(dbfp, "%lu", n); + (void) fprintf(dbfp, " %s.\n", cp); + break; + + case T_TXT: + case T_X25: + cp1 = cp + n; + (void) putc('"', dbfp); + while (cp < cp1) { + if (i = *cp++) { + for (j = i ; j > 0 && cp < cp1 ; j--) { + if ((*cp == '\n') || (*cp == '"')) { + (void) putc('\\', dbfp); + } + (void) putc(*cp++, dbfp); + } + } + } + (void) fputs("\"\n", dbfp); + break; + + case T_NSAP: + fprintf(dbfp, "%s\n", inet_nsap_ntoa(n, cp, NULL)); + break; + + case T_UINFO: + (void) fprintf(dbfp, "\"%s\"\n", cp); + break; + + case T_UID: + case T_GID: + if (n == INT32SZ) { + GETLONG(n, cp); + (void) fprintf(dbfp, "%lu\n", n); + } + break; + + case T_WKS: + GETLONG(addr, cp); + addr = htonl(addr); + (void) fprintf(dbfp, "%s ", + inet_ntoa(*(struct in_addr *) & addr)); + proto = protocolname(*cp); + cp += sizeof(char); + (void) fprintf(dbfp, "%s ", proto); + i = 0; + while (cp < (u_char *) cdata + n) { + j = *cp++; + do { + if (j & 0200) + (void) fprintf(dbfp, " %s", + servicename(i, proto)); + j <<= 1; + } while (++i & 07); + } + (void) fprintf(dbfp, "\n"); + break; + + case T_MINFO: + case T_RP: + (void) fprintf(dbfp, "%s.", cp); + cp += strlen((char *) cp) + 1; + (void) fprintf(dbfp, " %s.\n", cp); + break; + + default: + (void) fprintf(dbfp, "???\n"); + } + if (ferror(dbfp)) { + syslog(LOG_ERR, "%s: %m", tmpname); + exit(XFER_FAIL); + } + return (result); +} + +#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(ddtfile, optarg) +char *ddtfile, *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 */ |