diff options
author | peter <peter@FreeBSD.org> | 1998-05-03 04:11:49 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1998-05-03 04:11:49 +0000 |
commit | 0666320b4eda500556d2c671c9527c9000057492 (patch) | |
tree | 759849259eae9f7cb0d3ddbd7a131081c6688068 /contrib/bind/bin/named | |
parent | 58ca52f41726d17758909ddafba7b6b6766c789c (diff) | |
download | FreeBSD-src-0666320b4eda500556d2c671c9527c9000057492.zip FreeBSD-src-0666320b4eda500556d2c671c9527c9000057492.tar.gz |
Import (trimmed) ISC bind-8.1.2-t3b. This will be updated to 8.1.2 on
final release.
Obtained from: ftp.isc.org
Diffstat (limited to 'contrib/bind/bin/named')
44 files changed, 29841 insertions, 0 deletions
diff --git a/contrib/bind/bin/named/Makefile b/contrib/bind/bin/named/Makefile new file mode 100644 index 0000000..7e30cd0 --- /dev/null +++ b/contrib/bind/bin/named/Makefile @@ -0,0 +1,126 @@ +## Copyright (c) 1996, 1997 by Internet Software Consortium +## +## Permission to use, copy, modify, and distribute this software for any +## purpose with or without fee is hereby granted, provided that the above +## copyright notice and this permission notice appear in all copies. +## +## THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +## ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +## OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +## CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +## DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +## PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +## ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +## SOFTWARE. + +# $Id: Makefile,v 8.31 1998/03/20 00:49:46 halley Exp $ + +DESTDIR= +CC= cc +SHELL= /bin/sh + +CDEBUG= -g + +#(net2 and its descendents) +SYSTYPE = bsdos +TOP = ../.. +INCL = ${TOP}/include +PORTINCL = ${TOP}/port/${SYSTYPE}/include +LIBBIND = ${TOP}/lib/libbind.a +A=a +O=o +LEX = lex -I +YACC = yacc -d +SYSLIBS = -ll -lutil +PIDDIR = /var/run +DESTBIN = /usr/local/bin +DESTSBIN = /usr/local/sbin +DESTEXEC = /usr/local/libexec +DESTMAN = /usr/share/man +DESTHELP= /usr/share/misc +AR= ar cruv +INSTALL= install +STRIP=-s + +PS=ps +LDFLAGS= +CFLAGS= ${CDEBUG} -I${PORTINCL} -I${INCL} ${DEFS} + +VER= LOCAL-`date +%y%m%d.%H%M%S` +HOSTNAMECMD= hostname || uname -n + +PROG= named +HDRS= db_defs.h db_glob.h ns_defs.h ns_glob.h named.h pathnames.h +SRCS= db_dump.c db_load.c db_lookup.c db_save.c db_update.c \ + db_glue.c \ + ns_parser.c ns_lexer.c ns_parseutil.c \ + ns_forw.c ns_init.c ns_main.c ns_maint.c ns_req.c \ + ns_resp.c ns_stats.c ns_ncache.c ns_xfr.c ns_glue.c \ + ns_udp.c ns_config.c ns_update.c +OBJS= db_dump.${O} db_load.${O} db_lookup.${O} db_save.${O} db_update.${O} \ + db_glue.${O} \ + ns_parser.${O} ns_lexer.${O} ns_parseutil.${O} \ + ns_forw.${O} ns_init.${O} ns_main.${O} ns_maint.${O} ns_req.${O} \ + ns_resp.${O} ns_stats.${O} ns_ncache.${O} ns_xfr.${O} ns_glue.${O} \ + ns_udp.${O} ns_config.${O} ns_update.${O} + +all: ${PROG} pathnames + +${PROG}: pathnames.h ${OBJS} ${LIBBIND} Makefile tmp_version.${O} + ${CC} ${CDEBUG} ${LDFLAGS} -o ${PROG} ${OBJS} tmp_version.${O} \ + ${LIBBIND} ${SYSLIBS} + +ns_parser.c ns_parser.h: ns_parser.y + ${YACC} ns_parser.y + mv y.tab.c ns_parser.c + mv y.tab.h ns_parser.h + +tmp_version.${O}: tmp_version.c + +tmp_version.c: version.c Makefile ../Makefile ${SRCS} ${HDRS} + (u=$${USER-root} d=`pwd` h=`${HOSTNAMECMD}` t=`date`; \ + sed -e "s|%WHEN%|$${t}|" -e "s|%VERSION%|"${VER}"|" \ + -e "s|%WHOANDWHERE%|$${u}@$${h}:$${d}|" \ + < version.c > tmp_version.c) + +pathnames.h: ${TOP}/.settings Makefile + rm -f pathnames.h + sed -e "s|%DESTSBIN%|${DESTSBIN}|" \ + -e "s|%DESTEXEC%|${DESTEXEC}|" \ + -e "s|%DESTETC%|${DESTETC}|" \ + -e "s|%DESTRUN%|${DESTRUN}|" \ + < pathtemplate.h > pathnames.h + +pathnames: pathnames.${O} pathnames.h ${LIBBIND} Makefile + ${CC} ${CDEBUG} ${LDFLAGS} -o $@ pathnames.${O} \ + ${LIBBIND} ${SYSLIBS} + +distclean: clean + rm -f ns_parser.c ns_parser.h + +clean: FRC + rm -f ${PROG} ${OBJS} core .depend + rm -f *.BAK *.CKP *~ *.orig + rm -f tmp_version.c tmp_version.${O} + rm -f pathnames pathnames.${O} tmp_pathnames.h pathnames.h + rm -f y.tab.h y.tab.c + +depend: ${SRCS} pathnames.h + mkdep ${CPPFLAGS} -I${INCL} -I${PORTINCL} ${DEFS} ${SRCS} + +${DESTDIR}${DESTSBIN}: + mkdir -p ${DESTDIR}${DESTSBIN} + +install: ${DESTDIR}${DESTSBIN} ${PROG} + ${INSTALL} ${STRIP} -c -m 755 ${PROG} ${DESTDIR}${DESTSBIN}/${PROG} + +links: FRC + @ln -s SRC/*.[chy] SRC/test .; rm -f ns_parser.[ch] + +tags: FRC + ctags ${SRCS} *.h + +FRC: + +# DO NOT DELETE THIS LINE -- mkdep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. diff --git a/contrib/bind/bin/named/db_defs.h b/contrib/bind/bin/named/db_defs.h new file mode 100644 index 0000000..ab93480 --- /dev/null +++ b/contrib/bind/bin/named/db_defs.h @@ -0,0 +1,233 @@ +/* + * from db.h 4.16 (Berkeley) 6/1/90 + * $Id: db_defs.h,v 8.17 1998/02/17 17:17:43 vixie Exp $ + */ + +/* 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. + */ + +/* Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * 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 (2*MAXDNAME + 5*INT32SZ) + +#define DB_ROOT_TIMBUF 3600 +#define TIMBUF 300 + +#define DICT_INDEXBITS 24 +#define DICT_MAXLENGTH 127 +#define DICT_INSERT_P 0x0001 + +/* + * Hash table structures. + */ +struct databuf { + struct databuf *d_next; /* linked list */ + struct nameser *d_ns; /* NS from whence this came */ + 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; + u_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_size; /* size of data area */ + u_int32_t d_rcnt; + unsigned d_rcode :4; /* rcode for negative caching */ + unsigned d_mark :12; /* place to mark data */ + u_int16_t d_nstime; /* NS response time, milliseconds */ + u_char d_data[sizeof(void*)]; /* dynamic (padded) */ +}; +#define DATASIZE(n) (sizeof(struct databuf) - sizeof(void*) + n) + +#ifdef BIND_UPDATE +/* + * d_mark definitions + */ +#define D_MARK_DELETED 0x01 +#define D_MARK_ADDED 0x02 +#define D_MARK_FOUND 0x04 +#endif + +/* + * d_flags definitions + */ +#define DB_F_HINT 0x01 /* databuf belongs to fcachetab */ +#define DB_F_ACTIVE 0x02 /* databuf is linked into a cache */ +#define DB_F_FREE 0x04 /* databuf has been freed */ + +/* + * 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 { + u_int n_hashval; /* hash value of _n_name */ + 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 */ + char _n_name[sizeof(void*)]; /* Counted str (dynamic). */ +}; +#define NAMESIZE(n) (sizeof(struct namebuf) - sizeof(void*) + 1 + n + 1) +#define NAMELEN(nb) (((u_char *)((nb)._n_name))[0]) +#define NAME(nb) ((nb)._n_name + 1) + +struct hashbuf { + int h_size; /* size of hash table */ + int h_cnt; /* number of entries */ + struct namebuf *h_tab[1]; /* allocated as needed */ +}; +#define HASHSIZE(s) (sizeof(struct hashbuf) + (s-1) * sizeof(struct namebuf *)) + +#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_PRIMING 0x20 /* is this update the result of priming? */ + +#define DB_Z_CACHE 0 /* cache-zone-only db_dump() */ +#define DB_Z_ALL 65535 /* normal db_dump() */ +#define DB_Z_SPECIAL(z) ((z) == DB_Z_CACHE || (z) == DB_Z_ALL) + +/* + * 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) +#ifdef BIND_UPDATE +#define SERIAL (-11) +#endif + +/* + * getnum() options + */ +#define GETNUM_NONE 0x00 /* placeholder */ +#define GETNUM_SERIAL 0x01 /* treat as serial number */ +#define GETNUM_SCALED 0x02 /* permit "k", "m" suffixes, scale result */ + +/* + * Database access abstractions. + */ +#define foreach_rr(dp, np, ty, cl, zn) \ + for ((dp) = (np)->n_data; (dp) != NULL; (dp) = (dp)->d_next) \ + if ((cl) != C_ANY && (cl) != (dp)->d_class) \ + continue; \ + else if ((ty) != T_ANY && (ty) != (dp)->d_type) \ + continue; \ + else if (((zn) == DB_Z_CACHE) \ + ? stale(dp) \ + : (zn) != (dp)->d_zone) \ + continue; \ + else if ((dp)->d_rcode) \ + continue; \ + else \ + /* Caller code follows in sequence. */ + +#define DRCNTINC(x) \ + do { \ + if (++((x)->d_rcnt) == 0) \ + ns_panic(ns_log_db, 1, "++d_rcnt == 0"); \ + } while (0) + +#define DRCNTDEC(x) \ + do { \ + if (((x)->d_rcnt)-- == 0) \ + ns_panic(ns_log_db, 1, "d_rcnt-- == 0"); \ + } while (0) diff --git a/contrib/bind/bin/named/db_dict.c b/contrib/bind/bin/named/db_dict.c new file mode 100644 index 0000000..a0b8921 --- /dev/null +++ b/contrib/bind/bin/named/db_dict.c @@ -0,0 +1,111 @@ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: db_dict.c,v 8.1 1997/09/26 17:55:40 halley Exp $"; +#endif /* not lint */ + +/* + * Portions Copyright (c) 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +#define DICT_BLOCKBITS 8 +#define DICT_BLOCKSHIFT 16 +#define DICT_BLOCKMAX (1 << DICT_BLOCKBITS) +#define DICT_OFFSETBITS 16 +#define DICT_OFFSETSHIFT 0 +#define DICT_OFFSETMAX (1 << DICT_OFFSETBITS) + +#define DICT_CONSUMED(Length) ((Length) + 1) +#define DICT_INDEX(Block,Offset) (((Block) << DICT_BLOCKSHIFT) | \ + ((Offset) << DICT_OFFSETSHIFT)) + +static int dict_new(const char *, int); + +static char * blocks[DICT_BLOCKMAX]; +static int offsets[DICT_BLOCKMAX]; +static int cur_block = 0; +static int cur_offset = -1; + +int +dict_lookup(const char *text, int length, int flags) { + int block, offset, ret; + + /* XXX this is a proof of concept, some kind of hash is needed. */ + for (block = 0; block <= cur_block; block++) { + const char *cur = &blocks[block][0]; + const char *end = &blocks[block][offsets[block]]; + + while (cur < end) { + int xlength = *cur; + + if (xlength == length && + memcmp(cur+1, text, length) == 0) + return (DICT_INDEX(block, offset)); + cur += DICT_CONSUMED(length); + } + } + if ((flags & DICT_INSERT_P) != 0) + return (dict_new(text, length)); + return (-ENOENT); +} + +static int +dict_new(const char *text, int length) { + int ret; + + if (length < 0 || length > DICT_MAXLENGTH) + return (-E2BIG); + if (cur_offset + DICT_CONSUMED(length) >= DICT_OFFSETMAX) { + if (cur_block + 1 == DICT_BLOCKMAX) + return (-ENOSPC); + cur_block++; + blocks[cur_block] = memget(DICT_OFFSETMAX); + if (blocks[cur_block] == NULL) + return (-ENOMEM); + cur_offset = 0; + } + assert(cur_offset >= 0); + assert(cur_offset + DICT_CONSUMED(length) < DICT_OFFSETMAX); + ret = DICT_INDEX(cur_block, cur_offset); + blocks[cur_block][cur_offset] = length; + memcpy(&blocks[cur_block][cur_offset+1], text, length); + cur_offset += DICT_CONSUMED(length); + offsets[cur_block] = cur_offset; + return (ret); +} diff --git a/contrib/bind/bin/named/db_dump.c b/contrib/bind/bin/named/db_dump.c new file mode 100644 index 0000000..9c15af3 --- /dev/null +++ b/contrib/bind/bin/named/db_dump.c @@ -0,0 +1,635 @@ +#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 8.24 1998/02/13 19:49:09 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.h" + +#include "named.h" + +static const char *MkCredStr(int); + +/* + * Dump current data base in a format similar to RFC 883. + */ + +void +doadump() +{ + FILE *fp; + + ns_notice(ns_log_db, "dumping nameserver data"); + + if ((fp = write_open(server_options->dump_filename)) == NULL) + return; + gettime(&tt); + fprintf(fp, "; Dumped at %s", ctimel(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); + ns_notice(ns_log_db, "finished dumping nameserver data"); +} + +int +zt_dump(FILE *fp) { + struct zoneinfo *zp; + + fprintf(fp, ";; ++zone table++\n"); + for (zp = &zones[1]; zp < &zones[nzones]; zp++) { + char *pre, buf[64]; + u_int cnt; + + if (!zp->z_origin) + continue; + + fprintf(fp, "; %s (type %d, class %d, source %s)\n", + zp->z_origin + ? (*zp->z_origin ? zp->z_origin : ".") + : "Nil", + zp->z_type, zp->z_class, + zp->z_source ? zp->z_source : "Nil"); + fprintf(fp, ";\ttime=%lu, lastupdate=%lu, serial=%u,\n", + (u_long)zp->z_time, (u_long)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=%lu, xaddr=[%s], state=%04x, pid=%d\n", + (u_long)zp->z_ftime, inet_ntoa(zp->z_xaddr), + zp->z_flags, (int)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); + if (zp->z_axfr_src.s_addr != 0) + fprintf(fp, "; update source [%s]\n", + inet_ntoa(zp->z_axfr_src)); + } + fprintf(fp, ";; --zone table--\n"); + return (0); +} + +int +db_dump(struct hashbuf *htp, FILE *fp, int zone, char *origin) { + struct databuf *dp = NULL; + struct namebuf *np; + struct namebuf **npp, **nppend; + char dname[MAXDNAME]; + u_int32_t n; + u_int32_t addr; + int j, i, found_data, tab, printed_origin; + u_char *cp, *end; + const char *proto, *sep; + int16_t type; + u_int16_t keyflags; + u_char *sigdata; + u_char *savecp; + char temp_base64[NS_MD5RSA_MAX_BASE64]; + + found_data = 0; + 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; + /* XXX why are we not calling stale() here? */ + if (dp->d_zone == DB_Z_CACHE && + dp->d_ttl <= (u_int32_t)tt.tv_sec && + (dp->d_flags & DB_F_HINT) == 0) + continue; + if (!printed_origin) { + fprintf(fp, "$ORIGIN %s.\n", origin); + printed_origin++; + } + tab = 0; + if (dp->d_rcode == NXDOMAIN || + dp->d_rcode == NOERROR_NODATA) { + fputc(';', fp); + } else if (found_data == 0 || found_data == 1) { + found_data = 2; + } + if (found_data == 0 || found_data == 2) { + if (NAME(*np)[0] == '\0') { + if (origin[0] == '\0') + fprintf(fp, ".\t"); + else + fprintf(fp, ".%s.\t", origin); /* ??? */ + } else + fprintf(fp, "%s\t", NAME(*np)); + if (NAMELEN(*np) < (unsigned)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 != USE_MINIMUM && + 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;"; + type = dp->d_type; + if (dp->d_rcode == NXDOMAIN || + dp->d_rcode == NOERROR_NODATA) { +#ifdef RETURNSOA + if (dp->d_size == 0) { +#endif + + fprintf(fp, "%s%s-$", + (dp->d_rcode == NXDOMAIN) + ?"NXDOMAIN" :"NODATA", + sep); + goto eoln; +#ifdef RETURNSOA + } else { + type = T_SOA; + } +#endif + } + /* + * Print type specific data + */ + /* XXX why are we not using ns_sprintrr() here? */ + switch (type) { + case T_A: + switch (dp->d_class) { + case C_IN: + case C_HS: + fputs(inet_ntoa(ina_get(cp)), fp); + 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: { + char buf[256]; + + if ((n = *cp++) != '\0') { + memcpy(buf, cp, n); buf[n] = '\0'; + fprintf(fp, "\"%.*s\"", (int)n, buf); + cp += n; + } else + fprintf(fp, "\"\""); + if ((n = *cp++) != '\0') { + memcpy(buf, cp, n); buf[n] = '\0'; + fprintf(fp, " \"%.*s\"", (int)n, buf); + } else + fprintf(fp, " \"\""); + break; + } + + case T_SOA: + fprintf(fp, "%s.", cp); + cp += strlen((char *)cp) + 1; + fprintf(fp, " %s. (\n", cp); +#if defined(RETURNSOA) + if (dp->d_rcode) + fputs(";", fp); +#endif + cp += strlen((char *)cp) + 1; + NS_GET32(n, cp); + fprintf(fp, "\t\t%u", n); + NS_GET32(n, cp); + fprintf(fp, " %u", n); + NS_GET32(n, cp); + fprintf(fp, " %u", n); + NS_GET32(n, cp); + fprintf(fp, " %u", n); + NS_GET32(n, cp); + fprintf(fp, " %u )", n); +#if defined(RETURNSOA) + if (dp->d_rcode) { + fprintf(fp,";%s.;%s%s-$",cp, + (dp->d_rcode == NXDOMAIN) ? + "NXDOMAIN" : "NODATA", + sep); + } +#endif + break; + + case T_MX: + case T_AFSDB: + case T_RT: + NS_GET16(n, cp); + fprintf(fp, "%u", n); + fprintf(fp, " %s.", cp); + break; + + case T_PX: + NS_GET16(n, cp); + fprintf(fp, "%u", n); + fprintf(fp, " %s.", cp); + cp += strlen((char *)cp) + 1; + fprintf(fp, " %s.", cp); + break; + + case T_X25: + if ((n = *cp++) != '\0') + fprintf(fp, " \"%.*s\"", (int)n, cp); + else + fprintf(fp, " \"\""); + break; + + case T_TXT: + end = (u_char *)dp->d_data + dp->d_size; + while (cp < end) { + (void) putc('"', fp); + if ((n = *cp++) != '\0') { + for (j = n ; j > 0 && cp < end ; j--) { + if (*cp == '\n' || *cp == '"' || *cp == '\\') + (void) putc('\\', fp); + (void) putc(*cp++, fp); + } + } + (void) putc('"', fp); + if (cp < end) + (void) putc(' ', fp); + } + break; + + case T_NSAP: + (void) fputs(inet_nsap_ntoa(dp->d_size, + dp->d_data, NULL), + fp); + break; + + case T_AAAA: { + char t[sizeof + "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255" + ]; + + (void) fputs(inet_ntop(AF_INET6, dp->d_data, + t, sizeof t), + fp); + break; + } + + case T_LOC: { + char t[256]; + + (void) fputs(loc_ntoa(dp->d_data, t), fp); + break; + } + + case T_NAPTR: { + u_int32_t order, preference; + + NS_GET16(order, cp); + fprintf(fp, "%u", order); + + NS_GET16(preference, cp); + fprintf(fp, "%u", preference); + + if ((n = *cp++) != 0) { + fprintf(fp, "\"%.*s\"", (int)n, cp); + cp += n; + } + if ((n = *cp++) != 0) { + fprintf(fp, "\"%.*s\"", (int)n, cp); + cp += n; + } + if ((n = *cp++) != 0) { + fprintf(fp, " \"%.*s\"", (int)n, cp); + cp += n; + } + fprintf(fp, " %s.", cp); + + break; + } + + case T_SRV: { + u_int priority, weight, port; + + NS_GET16(priority, cp); + NS_GET16(weight, cp); + NS_GET16(port, cp); + fprintf(fp, "\t%u %u %u %s.", + priority, weight, port, cp); + break; + } + + case T_WKS: + fputs(inet_ntoa(ina_get(cp)), fp); + cp += INADDRSZ; + 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; + + case T_KEY: + savecp = cp; /* save the beginning */ + /*>>> Flags (unsigned_16) */ + NS_GET16(keyflags,cp); + fprintf(fp, "0x%04x ", keyflags); + /*>>> Protocol (8-bit decimal) */ + fprintf(fp, "%3u ", *cp++); + /*>>> Algorithm id (8-bit decimal) */ + fprintf(fp, "%3u ", *cp++); + + /*>>> Public-Key Data (multidigit BASE64) */ + /* containing ExponentLen, Exponent, and Modulus */ + i = b64_ntop(cp, dp->d_size - (cp - savecp), + temp_base64, + sizeof temp_base64); + if (i < 0) + fprintf(fp, "; BAD BASE64"); + else + fprintf(fp, "%s", temp_base64); + break; + + case T_SIG: + sigdata = cp; + /* RRtype (char *) */ + NS_GET16(n,cp); + fprintf(fp, "%s ", p_type(n)); + /* Algorithm id (8-bit decimal) */ + fprintf(fp, "%d ", *cp++); + /* Labels (8-bit decimal) (not saved in file) */ + /* FIXME -- check value and print err if bad */ + cp++; + /* OTTL (u_long) */ + NS_GET32(n, cp); + fprintf(fp, "%u ", n); + /* Texp (u_long) */ + NS_GET32(n, cp); + fprintf(fp, "%s ", p_secstodate (n)); + /* Tsig (u_long) */ + NS_GET32(n, cp); + fprintf(fp, "%s ", p_secstodate (n)); + /* Kfootprint (unsigned_16) */ + NS_GET16(n, cp); + fprintf(fp, "%u ", n); + /* Signer's Name (char *) */ + fprintf(fp, "%s ", cp); + cp += strlen((char *)cp) + 1; + /* Signature (base64 of any length) */ + i = b64_ntop(cp, dp->d_size - (cp - sigdata), + temp_base64, + sizeof temp_base64); + if (i < 0) + fprintf(fp, "; BAD BASE64"); + else + fprintf(fp, "%s", temp_base64); + break; + + case T_NXT: + fprintf(fp, "%s.", cp); + n = strlen ((char *)cp) + 1; + cp += n; + i = 8 * (dp->d_size - n); /* How many bits? */ + for (n = 0; n < (u_int32_t)i; n++) { + if (NS_NXT_BIT_ISSET(n, cp)) + fprintf(fp," %s",__p_type(n)); + } + break; + + 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: + if (dp->d_ns != NULL){ + fprintf(fp, "%s[%s]", + sep, inet_ntoa(dp->d_ns->addr)); + sep = " "; + } + 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(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); +} diff --git a/contrib/bind/bin/named/db_func.h b/contrib/bind/bin/named/db_func.h new file mode 100644 index 0000000..2d4c05b --- /dev/null +++ b/contrib/bind/bin/named/db_func.h @@ -0,0 +1,151 @@ +/* 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. + */ + +/* Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* db_proc.h - prototypes for functions in db_*.c + * + * $Id: db_func.h,v 8.22 1997/12/04 06:47:00 halley Exp $ + */ + +/* ++from db_update.c++ */ +extern int db_update(const char *name, + struct databuf *odp, + struct databuf *newdp, + struct databuf **savedpp, + int flags, + struct hashbuf *htp, + struct sockaddr_in from), + db_cmp(const struct databuf *, const struct databuf *), + findMyZone(struct namebuf *np, int class); +void fixttl(struct databuf *dp); +/* --from db_update.c-- */ + +/* ++from db_save.c++ */ +extern struct namebuf *savename(const char *, int); +extern struct databuf *savedata(int, int, u_int32_t, u_char *, int); +extern struct hashbuf *savehash(struct hashbuf *); +/* --from db_save.c-- */ + +/* ++from db_dump.c++ */ +extern int db_dump(struct hashbuf *, FILE *, int, char *), + zt_dump(FILE *); +extern void doadump(void); +/* --from db_dump.c-- */ + +/* ++from db_load.c++ */ +extern void endline(FILE *); +extern int getword(char *, size_t, FILE *, int), + getnum(FILE *, const char *, int), + db_load(const char *, const char *, + struct zoneinfo *, const char *); +extern int getnonblank(FILE *, const char *), + getservices(int, char *, FILE *, const char *); +extern char getprotocol(FILE *, const char *); +extern int makename(char *, const char *, int); +#ifdef BIND_NOTIFY +extern void notify_after_load(evContext, void *, const void *), + db_cancel_pending_notifies(void); +#endif +/* --from db_load.c-- */ + +/* ++from db_glue.c++ */ +extern void buildservicelist(void), + destroyservicelist(void), + buildprotolist(void), + destroyprotolist(void), + getname(struct namebuf *, char *, int); +extern int servicenumber(const char *), + protocolnumber(const char *), + get_class(const char *), + samedomain(const char *, const char *); +extern u_int dhash(const u_char *, int), + nhash(const char *); +extern const char *protocolname(int), + *servicename(u_int16_t, const char *); +#ifndef BSD +extern int getdtablesize(void); +#endif +extern struct databuf *rm_datum(struct databuf *, + struct namebuf *, + struct databuf *, + struct databuf **); +extern struct namebuf *rm_name(struct namebuf *, + struct namebuf **, + struct namebuf *); +extern void rm_hash(struct hashbuf *); +extern void db_freedata(struct databuf *); +/* --from db_glue.c-- */ + +/* ++from db_lookup.c++ */ +extern struct namebuf *nlookup(const char *, struct hashbuf **, + const char **, int); +extern struct namebuf *np_parent __P((struct namebuf *)); +extern int match(struct databuf *, int, int); +/* --from db_lookup.c-- */ + +/* ++from db_dict.c++ */ +int dict_lookup(const char *, int, int); +/* --from db_dict.c-- */ diff --git a/contrib/bind/bin/named/db_glob.h b/contrib/bind/bin/named/db_glob.h new file mode 100644 index 0000000..79d915d --- /dev/null +++ b/contrib/bind/bin/named/db_glob.h @@ -0,0 +1,94 @@ +/* + * from db.h 4.16 (Berkeley) 6/1/90 + * $Id: db_glob.h,v 8.8 1997/06/09 17:46:51 halley Exp $ + */ + +/* 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. + */ + +/* Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Global variables for data base routines. + */ + + /* ONE_WEEK maximum ttl */ +DECL u_int max_cache_ttl INIT(7*24*60*60); + + /* no minimum ttl */ +DECL u_int min_cache_ttl INIT(0); + + /* current line number */ +DECL int lineno; + + /* root hash table */ +DECL struct hashbuf *hashtab INIT(NULL); + + /* hash table of cache read from file */ +DECL struct hashbuf *fcachetab INIT(NULL); + +#ifdef FORCED_RELOAD +DECL int reloading INIT(0); +#endif /* FORCED_RELOAD */ diff --git a/contrib/bind/bin/named/db_glue.c b/contrib/bind/bin/named/db_glue.c new file mode 100644 index 0000000..bc6aed4 --- /dev/null +++ b/contrib/bind/bin/named/db_glue.c @@ -0,0 +1,616 @@ +#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 8.27 1998/02/14 00:41:39 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +struct valuelist { + struct valuelist * next; + struct valuelist * prev; + char * name; + char * proto; + int port; +}; +static struct valuelist *servicelist, *protolist; + +void +buildservicelist() { + struct servent *sp; + struct valuelist *slp; + +#ifdef MAYBE_HESIOD + setservent(0); +#else + setservent(1); +#endif + while ((sp = getservent()) != NULL) { + slp = (struct valuelist *)memget(sizeof(struct valuelist)); + if (!slp) + panic("memget(servent)", NULL); + slp->name = savestr(sp->s_name, 1); + slp->proto = savestr(sp->s_proto, 1); + slp->port = ntohs((u_int16_t)sp->s_port); /* host byt order */ + slp->next = servicelist; + slp->prev = NULL; + if (servicelist) + servicelist->prev = slp; + servicelist = slp; + } + endservent(); +} + +void +destroyservicelist() { + struct valuelist *slp, *slp_next; + + for (slp = servicelist; slp != NULL; slp = slp_next) { + slp_next = slp->next; + freestr(slp->name); + freestr(slp->proto); + memput(slp, sizeof *slp); + } +} + +void +buildprotolist() { + struct protoent *pp; + struct valuelist *slp; + +#ifdef MAYBE_HESIOD + setprotoent(0); +#else + setprotoent(1); +#endif + while ((pp = getprotoent()) != NULL) { + slp = (struct valuelist *)memget(sizeof(struct valuelist)); + if (!slp) + panic("memget(protoent)", NULL); + slp->name = savestr(pp->p_name, 1); + slp->port = pp->p_proto; /* host byte order */ + slp->next = protolist; + slp->prev = NULL; + if (protolist) + protolist->prev = slp; + protolist = slp; + } + endprotoent(); +} + +void +destroyprotolist() { + struct valuelist *plp, *plp_next; + + for (plp = protolist; plp != NULL; plp = plp_next) { + plp_next = plp->next; + freestr(plp->name); + memput(plp, sizeof *plp); + } +} + +static int +findservice(const char *s, struct valuelist **list) { + 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); /* host byte order */ + } + if (sscanf(s, "%d", &n) != 1 || n <= 0) + n = -1; + return (n); +} + +/* + * Convert service name or (ascii) number to int. + */ +int +servicenumber(const char *p) { + return (findservice(p, &servicelist)); +} + +/* + * Convert protocol name or (ascii) number to int. + */ +int +protocolnumber(const char *p) { + return (findservice(p, &protolist)); +} + +static struct servent * +cgetservbyport(u_int16_t port, const char *proto) { /* Host byte order. */ + struct valuelist **list = &servicelist; + struct valuelist *lp = *list; + static struct servent serv; + + port = ntohs(port); + for (; lp != NULL; lp = lp->next) { + if (port != (u_int16_t)lp->port) /* Host byte order. */ + 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(int proto) { /* Host byte order. */ + struct valuelist **list = &protolist; + struct valuelist *lp = *list; + static struct protoent prot; + + for (; lp != NULL; lp = lp->next) + if (lp->port == proto) { /* Host byte order. */ + 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; /* Host byte order. */ + return (&prot); + } + return (0); +} + +const char * +protocolname(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); +} + +const char * +servicename(u_int16_t port, const char *proto) { /* Host byte order. */ + 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); +} + +static struct map map_class[] = { + { "in", C_IN }, + { "chaos", C_CHAOS }, + { "hs", C_HS }, + { NULL, 0 } +}; + +int +get_class(const char *class) { + const 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); +} + +/* rm_datum(dp, np, pdp, savedpp) + * remove datum 'dp' from name 'np'. pdp is previous data pointer. + * if savedpp is not NULL, and compiled with BIND_UPDATE, save + * datum dp there rather than freeing the memory (caller will take + * care of freeing it) + * return value: + * "next" field from removed datum, suitable for relinking + */ +struct databuf * +rm_datum(struct databuf *dp, struct namebuf *np, struct databuf *pdp, + struct databuf **savedpp) { + struct databuf *ndp = dp->d_next; + + ns_debug(ns_log_db, 3, "rm_datum(%lx, %lx, %lx, %lx) -> %lx", + (u_long)dp, (u_long)np->n_data, (u_long)pdp, + (u_long)savedpp, (u_long)ndp); + if ((dp->d_flags & DB_F_ACTIVE) == 0) + panic("rm_datum: DB_F_ACTIVE not set", NULL); + if (pdp == NULL) + np->n_data = ndp; + else + pdp->d_next = ndp; +#ifdef BIND_UPDATE + if (savedpp != NULL) { + /* mark deleted or pending deletion */ + dp->d_mark |= D_MARK_DELETED; + dp->d_next = *savedpp; + *savedpp = dp; + } else + dp->d_next = NULL; +#else + dp->d_next = NULL; +#endif + dp->d_flags &= ~DB_F_ACTIVE; + DRCNTDEC(dp); + if (dp->d_rcnt) { +#ifdef DEBUG + int32_t ii; +#endif + + switch(dp->d_type) { + case T_NS: + ns_debug(ns_log_db, 3, "rm_datum: %s rcnt = %d", + dp->d_data, dp->d_rcnt); + break; +#ifdef DEBUG + case T_A: + memcpy(&ii, dp->d_data, sizeof ii); + ns_debug(ns_log_db, 3, + "rm_datum: %08.8X rcnt = %d", + ii, dp->d_rcnt); + break; +#endif + default: + ns_debug(ns_log_db, 3, + "rm_datum: rcnt = %d", dp->d_rcnt); + } + } else +#ifdef BIND_UPDATE + if (savedpp == NULL) +#endif + db_freedata(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(struct namebuf *np, struct namebuf **pp, struct namebuf *pnp) { + struct namebuf *nnp = np->n_next; + const char *msg; + + /* verify */ + if ( (np->n_data && (msg = "data")) + || (np->n_hash && (msg = "hash")) + ) { + ns_panic(ns_log_db, 1, "rm_name(%#x(%s)): non-nil %s pointer", + np, NAME(*np), msg); + } + + /* unlink */ + if (pnp) + pnp->n_next = nnp; + else + *pp = nnp; + + /* deallocate */ + memput(np, NAMESIZE(NAMELEN(*np))); + + /* done */ + return (nnp); +} + +void +rm_hash(struct hashbuf *htp) { + REQUIRE(htp != NULL); + REQUIRE(htp->h_cnt == 0); + + memput(htp, HASHSIZE(htp->h_size)); +} + +/* + * Get the domain name of 'np' and put in 'buf'. Bounds checking is done. + */ +void +getname(struct namebuf *np, char *buf, int buflen) { + char *cp; + int i; + + cp = buf; + while (np != NULL) { + i = (int) NAMELEN(*np); + if (i + 1 >= buflen) { + *cp = '\0'; + ns_info(ns_log_db, + "domain name too long: %s...", buf); + strcpy(buf, "Name_Too_Long"); + return; + } + if (cp != buf) + *cp++ = '.'; + memcpy(cp, NAME(*np), i); + cp += i; + buflen -= i + 1; + np = np->n_parent; + } + *cp = '\0'; +} + +/* + * Compute hash value from data. + */ +u_int +dhash(const u_char *dp, int dlen) { + u_char *cp; + u_int hval; + int n; + + n = dlen; + if (n > 8) + n = 8; + hval = 0; + while (--n >= 0) { + hval <<= 1; + hval += *dp++; + } + return (hval % INVHASHSZ); +} + +/* u_int + * nhash(name) + * compute hash for this name and return it; ignore case differences + */ +u_int +nhash(const char *name) { + u_char ch; + u_int hval; + + hval = 0; + while ((ch = (u_char)*name++) != (u_char)'\0') { + if (isascii(ch) && isupper(ch)) + ch = tolower(ch); + hval <<= 1; + hval += ch; + } + 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" +*/ + +int +samedomain(const char *a, const char *b) { + size_t la, lb; + int diff, i, escaped; + const char *cp; + + la = strlen(a); + lb = strlen(b); + + /* ignore a trailing label separator (i.e. an unescaped dot) in 'a' */ + if (la && a[la-1] == '.') { + escaped = 0; + /* note this loop doesn't get executed if la==1 */ + for (i = la - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else { + break; + } + if (!escaped) + la--; + } + /* ignore a trailing label separator (i.e. an unescaped dot) in 'b' */ + if (lb && b[lb-1] == '.') { + escaped = 0; + /* note this loop doesn't get executed if lb==1 */ + for (i = lb - 2; i >= 0; i--) + if (b[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else { + break; + } + if (!escaped) + 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 a trailing dot. */ + if (lb == la) + return (strncasecmp(a, b, lb) == 0); + + /* Ok, we know la > lb. */ + + diff = la - lb; + + /* If 'a' is only 1 character longer than 'b', then it can't be + a subdomain of 'b' (because of the need for the '.' label + separator). */ + if (diff < 2) + return (0); + + /* If the character before the last 'lb' characters of 'b' + isn't '.', then it can't be a match (this lets us avoid + having "foobar.com" match "bar.com"). */ + if (a[diff-1] != '.') + return (0); + + /* We're not sure about that '.', however. It could be escaped + and thus not a really a label separator. */ + escaped=0; + for (i = diff-2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } + else + break; + if (escaped) + return (0); + + /* We use strncasecmp because we might be trying to + * ignore trailing dots. */ + cp = a + diff; + return (strncasecmp(cp, b, lb) == 0); +} + +void +db_freedata(struct databuf *dp) { + int bytes = (dp->d_type == T_NS) ? + DATASIZE(dp->d_size)+INT32SZ : DATASIZE(dp->d_size); + + if (dp->d_rcnt != 0) + panic("db_freedata: d_rcnt != 0", NULL); + if ((dp->d_flags & (DB_F_ACTIVE|DB_F_FREE)) != 0) + panic("db_freedata: %s set", + (dp->d_flags & DB_F_FREE) != 0 ? "DB_F_FREE" : + "DB_F_ACTIVE"); + if (dp->d_next != NULL) + panic("db_free: d_next != NULL", NULL); + dp->d_flags |= DB_F_FREE; + memput(dp, bytes); +} diff --git a/contrib/bind/bin/named/db_load.c b/contrib/bind/bin/named/db_load.c new file mode 100644 index 0000000..d05a969 --- /dev/null +++ b/contrib/bind/bin/named/db_load.c @@ -0,0 +1,2218 @@ +#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 8.41 1998/02/13 20:02:28 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Load zone from ASCII file on local host. Format similar to RFC 883. + */ + +/* Import. */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +/* Forward. */ + +static int gettoken(FILE *, const char *); +static int getttl(FILE *, const char *, int, u_int32_t *, int *); +static int getcharstring(char *, char *, int, int, int, FILE *, + const char *); +static int makename_ok(char *name, const char *origin, int class, + struct zoneinfo *zp, + enum transport transport, + enum context context, + const char *owner, const char *filename, + int lineno, int size); +static int getmlword(char *, size_t, FILE *, int); +static int getallwords(char *, size_t, FILE *, int); +static u_int32_t wordtouint32(char *); +static int datepart(const char *, int, int, int, int *); +static u_int32_t datetosecs(const char *, int *); +static int get_nxt_types(u_char *, FILE *, const char *); +static void fixup_soa(const char *fn, struct zoneinfo *zp); +#ifdef BIND_NOTIFY +static void notify_after_delay(evContext ctx, void *uap, + struct timespec due, + struct timespec inter); +#endif +static int wordtouint32_error = 0; +static int empty_token = 0; +static int getmlword_nesting = 0; + +/* Global. */ + +static int clev; /* a zone deeper in a hierarchy has more credibility */ + +#ifdef BIND_NOTIFY +static notify_info_list pending_notifies; +#endif + +/* + * Parser token values + */ +#define CURRENT 1 +#define DOT 2 +#define AT 3 +#define DNAME 4 +#define INCLUDE 5 +#define ORIGIN 6 +#define ERROR 7 + +#define MAKENAME_OK(N) \ + do { \ + if (!makename_ok(N, origin, class, zp, \ + transport, context, \ + domain, filename, lineno, \ + sizeof(data) - ((u_char*)N - data))) { \ + errs++; \ + sprintf(buf, "bad name \"%s\"", N); \ + goto err; \ + } \ + } while (0) + +/* Public. */ + +/* int + * db_load(filename, in_origin, zp, def_domain) + * load a database from `filename' into zone `zp'. append `in_origin' + * to all nonterminal domain names in the file. `def_domain' is the + * default domain for include files or NULL for zone base files. + * returns: + * -1 = can't open file + * 0 = success + * >0 = number of errors encountered + */ +int +db_load(const char *filename, const char *in_origin, + struct zoneinfo *zp, const char *def_domain) +{ + static int read_soa, read_ns, rrcount; + + const char *errtype = "Database"; + char *cp; + char domain[MAXDNAME], origin[MAXDNAME], tmporigin[MAXDNAME]; + char buf[MAXDATA]; + u_char data[MAXDATA]; + const char *op; + int c, someclass, class, type, dbflags, dataflags, multiline; + int slineno, i, errs, didinclude, escape, success, dateerror; + u_int32_t ttl, n, serial; + u_long tmplong; + struct databuf *dp; + FILE *fp; + struct stat sb; + struct in_addr ina; + enum transport transport; + enum context context; + u_int32_t sig_type; + u_int32_t keyflags; + struct sockaddr_in empty_from; + + empty_from.sin_family = AF_INET; + empty_from.sin_addr.s_addr = htonl(INADDR_ANY); + empty_from.sin_port = htons(0); + +/* + * We use an 'if' inside of the 'do' below because otherwise the Solaris + * compiler detects that the 'while' is never executed because of the 'goto' + * and complains. + */ +#define ERRTO(msg) do { if (1) { errtype = msg; goto err; } } while (0) + + switch (zp->z_type) { + case Z_PRIMARY: + case Z_CACHE: + transport = primary_trans; + break; + case Z_SECONDARY: + case Z_STUB: + transport = secondary_trans; + break; + default: + transport = response_trans; /*guessing*/ + break; + } + errs = 0; + didinclude = 0; + if (!def_domain) { + /* This is not the result of a $INCLUDE. */ + rrcount = 0; + read_soa = 0; + read_ns = 0; + clev = nlabels(in_origin); + } + + ns_debug(ns_log_load, 1, "db_load(%s, %s, %d, %s)", + filename, in_origin, zp - zones, + def_domain ? def_domain : "Nil"); + + strcpy(origin, in_origin); + if ((fp = fopen(filename, "r")) == NULL) { + ns_warning(ns_log_load, "%s: %s", filename, strerror(errno)); + return (-1); + } + if (zp->z_type == Z_CACHE) { + dbflags = DB_NODATA | DB_NOHINTS; + dataflags = DB_F_HINT; +#ifdef STUBS + } else if (zp->z_type == Z_STUB && clev == 0) { + dbflags = DB_NODATA | DB_NOHINTS; + dataflags = DB_F_HINT; +#endif + } else { + dbflags = DB_NODATA; + dataflags = 0; + } + gettime(&tt); + if (fstat(fileno(fp), &sb) < 0) { + ns_warning(ns_log_load, "%s: %s", filename, strerror(errno)); + sb.st_mtime = (int)tt.tv_sec; + } + slineno = lineno; + lineno = 1; + if (def_domain) + strcpy(domain, def_domain); + else + 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(buf, sizeof buf, fp, 0)) + /* file name*/ + break; + if (!getword(tmporigin, sizeof(tmporigin), fp, 1)) + strcpy(tmporigin, origin); + else { + if (makename(tmporigin, origin, + sizeof(tmporigin)) == -1) + ERRTO("$INCLUDE makename failed"); + endline(fp); + } + didinclude = 1; + errs += db_load(buf, tmporigin, zp, domain); + continue; + + case ORIGIN: + (void) strcpy(buf, origin); + if (!getword(origin, sizeof(origin), fp, 1)) + break; + ns_debug(ns_log_load, 3, "db_load: origin %s, buf %s", + origin, buf); + if (makename(origin, buf, sizeof(origin)) == -1) + ERRTO("$ORIGIN makename failed"); + ns_debug(ns_log_load, 3, "db_load: origin now %s", + origin); + continue; + + case DNAME: + if (!getword(domain, sizeof(domain), fp, 1)) + break; + if (makename(domain, origin, sizeof(domain)) == -1) + ERRTO("ownername makename failed"); + goto gotdomain; + + case AT: + (void) strcpy(domain, origin); + goto gotdomain; + + case DOT: + domain[0] = '\0'; + /* FALLTHROUGH */ + case CURRENT: + gotdomain: + if (!getword(buf, sizeof buf, fp, 0)) { + if (c == CURRENT) + continue; + break; + } + if (ns_parse_ttl(buf, &tmplong) < 0) + ttl = USE_MINIMUM; + else { + ttl = (u_int32_t)tmplong; + if (ttl > MAXIMUM_TTL) { + ns_info(ns_log_load, + "%s: Line %d: TTL > %u; converted to 0", + filename, lineno, MAXIMUM_TTL); + ttl = 0; + } + if (zp->z_type == Z_CACHE) { + /* + * This allows the cache entry to age + * while sitting on disk (powered off). + */ + if (ttl > max_cache_ttl) + ttl = max_cache_ttl; + ttl += sb.st_mtime; + } + if (!getword(buf, sizeof buf, fp, 0)) + break; + } + + /* Parse class (IN, etc) */ + someclass = sym_ston(__p_class_syms, buf, &success); + if (success && someclass != C_ANY) { + class = someclass; + (void) getword(buf, sizeof buf, fp, 0); + } + + /* Parse RR type (A, MX, etc) */ + type = sym_ston(__p_type_syms, buf, &success); + if (success == 0 || type == ns_t_any) { + ns_info(ns_log_load, + "%s: Line %d: Unknown type: %s.", + filename, lineno, buf); + errs++; + break; + } + + context = ns_ownercontext(type, transport); + if (!ns_nameok(domain, class, zp, transport, context, + domain, inaddr_any)) { + errs++; + ns_notice(ns_log_load, + "%s:%d: owner name error", + filename, lineno); + break; + } + context = domain_ctx; + switch (type) { + case ns_t_key: + case ns_t_sig: + case ns_t_nxt: + /* + * Don't do anything here for these types -- + * they read their own input separately later. + */ + goto dont_get_word; + + case ns_t_soa: + case ns_t_minfo: + case ns_t_rp: + case ns_t_ns: + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ptr: + escape = 1; + break; + default: + escape = 0; + } + if (!getword(buf, sizeof buf, fp, escape)) + break; + ns_debug(ns_log_load, 3, + "d='%s', c=%d, t=%d, ttl=%u, data='%s'", + domain, class, type, ttl, buf); + /* + * Convert the ascii data 'buf' to the proper format + * based on the type and pack into 'data'. + */ + dont_get_word: + switch (type) { + case ns_t_a: + if (!inet_aton(buf, &ina)) + ERRTO("IP Address"); + (void) ina_put(ina, data); + n = NS_INT32SZ; + break; + + case ns_t_soa: + context = hostname_ctx; + goto soa_rp_minfo; + case ns_t_rp: + case ns_t_minfo: + context = mailname_ctx; + /* FALLTHROUGH */ + soa_rp_minfo: + (void) strcpy((char *)data, buf); + + MAKENAME_OK((char *)data); + cp = (char *)(data + strlen((char *)data) + 1); + if (!getword(cp, + (sizeof data) - + (cp - (char*)data), + fp, 1)) + ERRTO("Domain Name"); + if (type == ns_t_rp) + context = domain_ctx; + else + context = mailname_ctx; + MAKENAME_OK(cp); + cp += strlen((char *)cp) + 1; + if (type != ns_t_soa) { + n = cp - (char *)data; + break; + } + if (class != zp->z_class) { + errs++; + ns_info(ns_log_load, "%s:%d: %s", + filename, lineno, + "SOA class not same as zone's"); + } + if (strcasecmp(zp->z_origin, domain) != 0) { + errs++; + ns_error(ns_log_load, + "%s:%d: SOA for \"%s\" not at zone top \"%s\"", + filename, lineno, domain, + zp->z_origin); + } + c = getnonblank(fp, filename); + if (c == '(') { + multiline = 1; + } else { + multiline = 0; + ungetc(c, fp); + } + serial = zp->z_serial; + zp->z_serial = getnum(fp, filename, + GETNUM_SERIAL); + if (getnum_error) + errs++; + n = (u_int32_t) zp->z_serial; + PUTLONG(n, cp); + if (serial != 0 && + SEQ_GT(serial, zp->z_serial)) { + ns_notice(ns_log_load, + "%s:%d: WARNING: new serial number < old (%lu < %lu)", + filename , lineno, + zp->z_serial, serial); + } + if (getttl(fp, filename, lineno, &n, + &multiline) <= 0) { + errs++; + n = INIT_REFRESH; + } + PUTLONG(n, cp); + zp->z_refresh = MAX(n, MIN_REFRESH); + if (zp->z_type == Z_SECONDARY +#if defined(STUBS) + || zp->z_type == Z_STUB +#endif + ) { + ns_refreshtime(zp, MIN(sb.st_mtime, + tt.tv_sec)); + sched_zone_maint(zp); + } +#ifdef BIND_UPDATE + if ((zp->z_type == Z_PRIMARY) && + (zp->z_flags & Z_DYNAMIC)) + if ((u_int32_t)zp->z_soaincrintvl > + zp->z_refresh/3) { + ns_info(ns_log_load, + "zone soa update time truncated to 1/3rd of refresh time"); + zp->z_soaincrintvl = + zp->z_refresh / 3; + } +#endif + + if (getttl(fp, filename, lineno, &n, + &multiline) <= 0) { + errs++; + n = INIT_REFRESH; + } + PUTLONG(n, cp); + zp->z_retry = MAX(n, MIN_RETRY); + if (getttl(fp, filename, lineno, + &zp->z_expire, &multiline) <= 0) { + errs++; + zp->z_expire = INIT_REFRESH; + } + n = zp->z_expire; + PUTLONG(n, cp); + if (getttl(fp, filename, lineno, &n, + &multiline) <= 0) { + errs++; + n = 120; + } + PUTLONG(n, cp); + if (n > MAXIMUM_TTL) { + ns_info(ns_log_load, + "%s: Line %d: SOA minimum TTL > %u; converted to 0", + filename, lineno, MAXIMUM_TTL); + zp->z_minimum = 0; + } else + zp->z_minimum = n; + n = cp - (char *)data; + if (multiline) { + buf[0] = getnonblank(fp, filename); + buf[1] = '\0'; + if (buf[0] != ')') + ERRTO("SOA \")\""); + endline(fp); + } + read_soa++; + if (zp->z_type == Z_PRIMARY) + fixup_soa(filename, zp); + break; + + case ns_t_wks: + /* Address */ + if (!inet_aton(buf, &ina)) + ERRTO("WKS IP Address"); + (void) ina_put(ina, data); + /* Protocol */ + data[INADDRSZ] = getprotocol(fp, filename); + /* Services */ + n = getservices(NS_INT32SZ + sizeof(char), + (char *)data, fp, filename); + break; + + case ns_t_ns: + if (strcasecmp(zp->z_origin, domain) == 0) + read_ns++; + context = hostname_ctx; + goto cname_etc; + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + context = domain_ctx; + goto cname_etc; + case ns_t_ptr: + context = ns_ptrcontext(domain); + cname_etc: + (void) strcpy((char *)data, buf); + MAKENAME_OK((char *)data); + n = strlen((char *)data) + 1; + break; + + case ns_t_naptr: + /* Order Preference Flags Service Replacement Regexp */ + n = 0; + cp = buf; + /* Order */ + while (isdigit(*cp)) + n = n * 10 + (*cp++ - '0'); + /* catch bad values */ + if (cp == buf || n > 65535) + ERRTO("NAPTR Order"); + cp = (char *)data; + PUTSHORT((u_int16_t)n, cp); + + /* Preference */ + n = getnum(fp, filename, GETNUM_NONE); + if (getnum_error || n > 65536) + ERRTO("NAPTR Preference"); + PUTSHORT((u_int16_t)n, cp); + + /* Flags */ + if (!getword(buf, sizeof buf, fp, 0)) + ERRTO("NAPTR Flags"); + n = strlen(buf); + *cp++ = n; + memcpy(cp, buf, (int)n); + cp += n; + + /* Service Classes */ + if (!getword(buf, sizeof buf, fp, 0)) + ERRTO("NAPTR Service Classes"); + n = strlen(buf); + *cp++ = n; + memcpy(cp, buf, (int)n); + cp += n; + + /* Pattern */ + if (!getword(buf, sizeof buf, fp, 0)) + ERRTO("NAPTR Pattern"); + n = strlen(buf); + *cp++ = n; + memcpy(cp, buf, (int)n); + cp += n; + + /* Replacement */ + if (!getword(buf, sizeof buf, fp, 1)) + ERRTO("NAPTR Replacement"); + (void) strcpy((char *)cp, buf); + context = domain_ctx; + MAKENAME_OK(cp); + /* advance pointer to end of data */ + cp += strlen((char *)cp) +1; + + /* now save length */ + n = (cp - (char *)data); + break; + + + case ns_t_mx: + case ns_t_afsdb: + case ns_t_rt: + case ns_t_srv: + n = 0; + cp = buf; + while (isdigit(*cp)) + n = n * 10 + (*cp++ - '0'); + /* catch bad values */ + if ((cp == buf) || (n > 65535)) + ERRTO("Priority"); + cp = (char *)data; + PUTSHORT((u_int16_t)n, cp); + + if (type == ns_t_srv) { + n = getnum(fp, filename, GETNUM_NONE); + if (getnum_error || n > 65536) + ERRTO("SRV RR"); + PUTSHORT((u_int16_t)n, cp); + + n = getnum(fp, filename, GETNUM_NONE); + if (getnum_error || n > 65536) + ERRTO("SRV RR"); + PUTSHORT((u_int16_t)n, cp); + } + + if (!getword(buf, sizeof buf, fp, 1)) + ERRTO("Domain Name"); + (void) strcpy((char *)cp, buf); + context = hostname_ctx; + MAKENAME_OK(cp); + /* advance pointer to end of data */ + cp += strlen((char *)cp) +1; + + /* now save length */ + n = (cp - (char *)data); + break; + + case ns_t_px: + context = domain_ctx; + n = 0; + data[0] = '\0'; + cp = buf; + while (isdigit(*cp)) + n = n * 10 + (*cp++ - '0'); + /* catch bad values */ + if ((cp == buf) || (n > 65535)) + ERRTO("PX Priority"); + cp = (char *)data; + PUTSHORT((u_int16_t)n, cp); + + if (!getword(buf, sizeof buf, fp, 0)) + ERRTO("PX Domain1"); + (void) strcpy((char *)cp, buf); + MAKENAME_OK(cp); + /* advance pointer to next field */ + cp += strlen((char *)cp) + 1; + if (!getword(buf, sizeof buf, fp, 0)) + ERRTO("PX Domain2"); + (void) strcpy((char *)cp, buf); + MAKENAME_OK(cp); + /* advance pointer to end of data */ + cp += strlen((char *)cp) + 1; + + /* now save length */ + n = (cp - (char *)data); + break; + + case ns_t_hinfo: + n = getcharstring(buf, (char *)data, type, + 2, 2, fp, filename); + if (n == 0) + ERRTO("HINFO RR"); + break; + + case ns_t_isdn: + n = getcharstring(buf, (char *)data, type, + 1, 2, fp, filename); + if (n == 0) + ERRTO("ISDN RR"); + break; + + case ns_t_txt: + n = getcharstring(buf, (char *)data, type, + 1, 0, fp, filename); + if (n == 0) + ERRTO("TXT RR"); + break; + + + case ns_t_x25: + n = getcharstring(buf, (char *)data, type, + 1, 1, fp, filename); + if (n == 0) + ERRTO("X25 RR"); + break; + + case ns_t_nsap: + n = inet_nsap_addr(buf, (u_char *)data, + sizeof data); + if (n == 0) + ERRTO("NSAP RR"); + endline(fp); + break; + + case ns_t_aaaa: + if (inet_pton(AF_INET6, buf, data) <= 0) + ERRTO("IPv4 Address"); + n = NS_IN6ADDRSZ; + endline(fp); + break; + + case ns_t_key: { + /* The KEY record looks like this in the db file: + * Name Cl KEY Flags Proto Algid PublicKeyData + * where: + * Name,Cl per usual + * KEY RR type + * Flags 4 digit hex value (unsigned_16) + * Proto 8 bit u_int + * Algid 8 bit u_int + * PublicKeyData + * a string of base64 digits, + * skipping any embedded whitespace. + */ + u_int32_t al, pr; + int nk, klen; + char *expstart; + u_int expbytes, modbytes; + + i = 0; + data[i] = '\0'; + cp = (char *)data; + getmlword_nesting = 0; /* KLUDGE err recov. */ + /*>>> Flags (unsigned_16) */ + if (!getmlword((char*)buf, sizeof buf, fp, 0)) + ERRTO("KEY Flags Field"); + keyflags = wordtouint32(buf); + if (wordtouint32_error || 0xFFFF < keyflags) + goto err; + if (keyflags & NS_KEY_RESERVED_BITMASK) + ERRTO("KEY Reserved Flag Bit"); + PUTSHORT(keyflags, cp); + + /*>>> Protocol (8-bit decimal) */ + if (!getmlword((char*)buf, sizeof buf, fp, 0)) + ERRTO("KEY Protocol Field"); + pr = wordtouint32(buf); + if (wordtouint32_error || 255 < pr) + ERRTO("KEY Protocol Field"); + *cp++ = (u_char) pr; + + /*>>> Algorithm id (8-bit decimal) */ + if (!getmlword((char*)buf, sizeof buf, fp, 0)) + ERRTO("KEY Algorithm ID"); + al = wordtouint32(buf); + if (wordtouint32_error || + 0 == al || 255 == al || 255 < al) + ERRTO("KEY Algorithm ID"); + *cp++ = (u_char) al; + + /*>>> Public Key data is in BASE64. + * We don't care what algorithm it uses or what + * the internal structure of the BASE64 data is. + */ + if (!getallwords(buf, MAXDATA, fp, 0)) + klen = 0; + else { + /* Convert from BASE64 to binary. */ + klen = b64_pton(buf, (u_char*)cp, + sizeof data - + (cp - (char *)data)); + if (klen < 0) + ERRTO("KEY Public Key"); + } + + /* set total length */ + n = cp + klen - (char *)data; + + /* + * Now check for valid key flags & algs & etc, + * from the RFC. + */ + + if (keyflags & (NS_KEY_ZONEKEY | NS_KEY_IPSEC + | NS_KEY_EMAIL)) + pr |= 1; /* A nonzero proto. */ + if (NS_KEY_TYPE_NO_KEY == + (keyflags & NS_KEY_TYPEMASK)) + nk = 1; /* No-key */ + else + nk = 0; /* have a key */ + + if ((keyflags & NS_KEY_ZONEKEY) && + (NS_KEY_TYPE_CONF_ONLY == + (keyflags & NS_KEY_TYPEMASK))) + /* Zone key must have Auth bit set. */ + ERRTO("KEY Zone Key Auth. bit"); + + if (al == 0 && nk == 0) + ERRTO("KEY Algorithm"); + if (al != 0 && pr == 0) + ERRTO("KEY Protocols"); + + if (nk == 1 && klen != 0) + ERRTO("KEY No-Key Flags Set"); + + if (nk == 0 && klen == 0) + ERRTO("KEY Type Spec'd"); + + /* Check algorithm-ID and key structure, for + the algorithm-ID's that we know about. */ + switch (al) { + case NS_ALG_MD5RSA: + if (klen == 0) + break; + expstart = cp; + expbytes = *expstart++; + if (expbytes == 0) + GETSHORT(expbytes, expstart); + + if (expbytes < 1) + ERRTO("Exponent too short"); + if (expbytes > + (NS_MD5RSA_MAX_BITS + 7) / 8 + ) + ERRTO("Exponent too long"); + if (*expstart == 0) + ERRTO("Exponent w/ 0"); + + modbytes = klen - + (expbytes + (expstart - cp)); + if (modbytes < + (NS_MD5RSA_MIN_BITS + 7) / 8 + ) + ERRTO("Modulus too short"); + if (modbytes > + (NS_MD5RSA_MAX_BITS + 7) / 8 + ) + ERRTO("Modulus too long"); + if (*(expstart+expbytes) == 0) + ERRTO("Modulus starts w/ 0"); + break; + + case NS_ALG_EXPIRE_ONLY: + if (klen != 0) + ERRTO( + "Key provided for expire-only algorithm" + ); + break; + case NS_ALG_PRIVATE_OID: + if (klen == 0) + ERRTO("No ObjectID in key"); + break; + } + + endline(fp); /* flush the rest of the line */ + break; + } /*T_KEY*/ + + case ns_t_sig: + { + /* The SIG record looks like this in the db file: + Name Cl SIG RRtype Algid [OTTL] Texp Tsig Kfoot Signer Sig + + where: Name and Cl are as usual + SIG is a keyword + RRtype is a char string + ALGid is 8 bit u_int + OTTL is 32 bit u_int (optionally present) + Texp is YYYYMMDDHHMMSS + Tsig is YYYYMMDDHHMMSS + Kfoot is 16-bit unsigned decimal integer + Signer is a char string + Sig is 64 to 319 base-64 digits + A missing OTTL is detected by the magnitude of the Texp value + that follows it, which is larger than any u_int. + The Labels field in the binary RR does not appear in the + text RR. + + It's too crazy to run these pages of SIG code at the right + margin. I'm exdenting them for readability. + */ + int siglen; + u_int32_t al; + u_int32_t signtime, exptime, timetilexp; + u_int32_t origTTL; + time_t now; + + /* The TTL gets checked against the Original TTL, + and bounded by the signature expiration time, which + are both under the signature. We can't let TTL drift + based on the SOA record. If defaulted, fix it now. + (It's not clear to me why USE_MINIMUM isn't eliminated + before putting ALL RR's into the database. -gnu@toad.com) */ + if (ttl == USE_MINIMUM) + ttl = zp->z_minimum; + + i = 0; + data[i] = '\0'; + getmlword_nesting = 0; /* KLUDGE err recovery */ + + /* RRtype (char *) */ + if (!getmlword((char*)buf, sizeof buf, fp, 0)) + ERRTO("SIG record doesn't specify type"); + sig_type = sym_ston(__p_type_syms, buf, &success); + if (!success || sig_type == ns_t_any) { + /* + * We'll also accept a numeric RR type, + * for signing RR types that this version + * of named doesn't yet understand. + * In the ns_t_any case, we rely on wordtouint32 + * to fail when scanning the string "ANY". + */ + sig_type = wordtouint32 (buf); + if (wordtouint32_error || sig_type > 0xFFFF) + ERRTO("Unknown RR type in SIG record"); + } + cp = (char *)&data[i]; + PUTSHORT((u_int16_t)sig_type, cp); + i += 2; + + /* Algorithm id (8-bit decimal) */ + if (!getmlword(buf, sizeof buf, fp, 0)) + ERRTO("Missing algorithm ID"); + al = wordtouint32(buf); + if (0 == al || wordtouint32_error || 255 <= al) + goto err; + data[i] = (u_char) al; + i++; + + /* + * Labels (8-bit decimal) + * Not given in the file. Must compute. + */ + n = dn_count_labels(domain); + if (0 >= n || 255 < n) + ERRTO("SIG label count invalid"); + data[i] = (u_char) n; + i++; + + /* + * OTTL (optional u_int32_t) and + * Texp (u_int32_t date) + */ + if (!getmlword(buf, sizeof buf, fp, 0)) + ERRTO("OTTL and expiration time missing"); + /* + * See if OTTL is missing and this is a date. + * This relies on good, silent error checking + * in datetosecs. + */ + exptime = datetosecs(buf, &dateerror); + if (!dateerror) { + /* Output TTL as OTTL */ + origTTL = ttl; + cp = (char *)&data[i]; + PUTLONG (origTTL, cp); + i += 4; + } else { + /* Parse and output OTTL; scan TEXP */ + origTTL = wordtouint32(buf); + if (0 >= origTTL || wordtouint32_error || + (origTTL > 0x7fffffff)) + goto err; + cp = (char *)&data[i]; + PUTLONG(origTTL, cp); + i += 4; + if (!getmlword(buf, sizeof buf, fp, 0)) + ERRTO("Expiration time missing"); + exptime = datetosecs(buf, &dateerror); + } + if (dateerror || exptime > 0x7fffffff || exptime <= 0) + ERRTO("Invalid expiration time"); + cp = (char *)&data[i]; + PUTLONG(exptime, cp); + i += 4; + + /* Tsig (u_int32_t) */ + if (!getmlword(buf, sizeof buf, fp, 0)) + ERRTO("Missing signature time"); + signtime = datetosecs(buf, &dateerror); + if (0 == signtime || dateerror) + ERRTO("Invalid signature time"); + cp = (char *)&data[i]; + PUTLONG(signtime, cp); + i += 4; + + /* Kfootprint (unsigned_16) */ + if (!getmlword(buf, sizeof buf, fp, 0)) + ERRTO("Missing key footprint"); + n = wordtouint32(buf); + if (wordtouint32_error || n >= 0x0ffff) + ERRTO("Invalid key footprint"); + cp = (char *)&data[i]; + PUTSHORT((u_int16_t)n, cp); + i += 2; + + /* Signer's Name */ + if (!getmlword((char*)buf, sizeof buf, fp, 0)) + ERRTO("Missing signer's name"); + cp = (char *)&data[i]; + strcpy(cp,buf); + context = domain_ctx; + MAKENAME_OK(cp); + i += strlen(cp) + 1; + + /* + * Signature (base64 of any length) + * We don't care what algorithm it uses or what + * the internal structure of the BASE64 data is. + */ + if (!getallwords(buf, sizeof buf, fp, 0)) { + siglen = 0; + } else { + cp = (char *)&data[i]; + siglen = b64_pton(buf, (u_char*)cp, sizeof data - i); + if (siglen < 0) + goto err; + } + + /* set total length and we're done! */ + n = i + siglen; + + /* + * Check signature time, expiration, and adjust TTL. Note + * that all time values are in GMT (UTC), *not* local time. + */ + + now = time (0); + + /* Don't let bogus name servers increase the signed TTL */ + if (ttl > origTTL) + ERRTO("TTL is greater than signed original TTL"); + + /* Don't let bogus signers "sign" in the future. */ + if (signtime > (u_int32_t)now) + ERRTO("signature time is in the future"); + + /* Ignore received SIG RR's that are already expired. */ + if (exptime <= (u_int32_t)now) + ERRTO("expiration time is in the past"); + + /* Lop off the TTL at the expiration time. */ + timetilexp = exptime - now; + if (timetilexp < ttl) { + ns_debug(ns_log_load, 1, + "shrinking expiring %s SIG TTL from %d to %d", + p_secstodate(exptime), ttl, timetilexp); + ttl = timetilexp; + } + + /* + * Check algorithm-ID and key structure, for + * the algorithm-ID's that we know about. + */ + switch (al) { + case NS_ALG_MD5RSA: + if (siglen == 0) + ERRTO("No key for RSA algorithm"); + if (siglen < 1) + ERRTO("Signature too short"); + if (siglen > (NS_MD5RSA_MAX_BITS + 7) / 8) + ERRTO("Signature too long"); + /* We rely on cp from parse */ + if (*cp == 0) + ERRTO("Signature starts with zeroes"); + break; + + case NS_ALG_EXPIRE_ONLY: + if (siglen != 0) + ERRTO( + "Signature supplied to expire-only algorithm"); + break; + case NS_ALG_PRIVATE_OID: + if (siglen == 0) + ERRTO("No ObjectID in key"); + break; + } + + /* Should we complain about algorithm-ID's that we + don't understand? It may help debug some obscure + cases, but in general we should accept any RR whether + we could cryptographically process it or not; it + may be being published for some newer DNS clients + to validate themselves. */ + + endline(fp); /* flush the rest of the line */ + + break; /* Accept this RR. */ + } + + case ns_t_nxt: + /* The NXT record looks like: + Name Cl NXT nextname RRT1 RRT2 MX A SOA ... + + where: Name and Cl are as usual + NXT is a keyword + nextname is the next valid name in + the zone after "Name". All + names between the two are + known to be nonexistent. + RRT's... are a series of RR type + names, which indicate that + RR's of these types are + published for "Name", and + that no RR's of any other + types are published for + "Name". + + When a NXT record is cryptographically + signed, it proves the nonexistence of an + RR (actually a whole set of RR's). */ + + getmlword_nesting = 0; /* KLUDGE err recov. */ + if (!getmlword(buf, sizeof buf, fp, 1)) + goto err; + (void) strcpy((char *)data, buf); + MAKENAME_OK((char *)data); + n = strlen((char *)data) + 1; + cp = n + (char *)data; + n += get_nxt_types((u_char *)cp, fp, filename); + break; + + case ns_t_loc: + cp = buf + (n = strlen(buf)); + *cp = ' '; + cp++; + 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 process the whole line */ + n = loc_aton(buf, (u_char *)data); + if (n == 0) + goto err; + endline(fp); + break; + + default: + goto err; + } + /* + * Ignore data outside the zone. + */ + if (zp->z_type != Z_CACHE && + !samedomain(domain, zp->z_origin)) + { + ns_info(ns_log_load, + "%s:%d: data \"%s\" outside zone \"%s\" (ignored)", + filename, lineno, domain, + zp->z_origin); + continue; + } + 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, NULL, dbflags, + (dataflags & DB_F_HINT) + ? fcachetab + : hashtab, empty_from)) + != OK) { + if (c != DATAEXISTS) + ns_debug(ns_log_load, 1, + "update failed %s %d", + domain, type); + db_freedata(dp); + } else { + rrcount++; + } + continue; + + case ERROR: + break; + } + err: + errs++; + ns_notice(ns_log_load, "%s:%d: %s error (%s)", + filename, empty_token ? (lineno - 1) : lineno, + errtype, buf); + if (!empty_token) + endline(fp); + } + (void) my_fclose(fp); + lineno = slineno; + if (!def_domain) { + 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) { + const char *msg = NULL; + + if (read_soa == 0) + msg = "no SOA RR found"; + else if (read_soa != 1) + msg = "multiple SOA RRs found"; + else if (read_ns == 0) + msg = "no NS RRs found at zone top"; + else if (!rrcount) + msg = "no relevant RRs found"; + if (msg != NULL) { + errs++; + ns_warning(ns_log_load, + "Zone \"%s\" (file %s): %s", + zp->z_origin, filename, msg); + } + } + } + if (!def_domain) { + if (errs) + ns_warning(ns_log_load, + "%s zone \"%s\" (%s) rejected due to errors (serial %u)", + zoneTypeString(zp), zp->z_origin, + p_class(zp->z_class), zp->z_serial); + else + ns_info(ns_log_load, + "%s zone \"%s\" (%s) loaded (serial %u)", + zoneTypeString(zp), zp->z_origin, + p_class(zp->z_class), zp->z_serial); + } + if (errs) { + zp->z_flags |= Z_DB_BAD; + zp->z_ftime = 0; + } +#ifdef BIND_NOTIFY + if (!errs && !def_domain && + (zp->z_type == z_master || zp->z_type == z_slave)) { + static const char no_room[] = + "%s failed, cannot notify for zone %s"; + notify_info ni; + + ni = memget(sizeof *ni); + if (ni == NULL) + ns_info(ns_log_load, no_room, "memget", zp->z_origin); + else { + ni->name = savestr(zp->z_origin, 0); + if (ni->name == NULL) { + memput(ni, sizeof *ni); + ns_info(ns_log_load, no_room, + "memget", zp->z_origin); + } else { + ni->class = zp->z_class; + ni->state = notify_info_waitfor; + if (evWaitFor(ev, + (const void *)notify_after_load, + notify_after_load, ni, + &ni->wait_id) < 0) { + ns_error(ns_log_load, + "evWaitFor() failed: %s", + strerror(errno)); + freestr(ni->name); + memput(ni, sizeof *ni); + } else { + APPEND(pending_notifies, ni, link); + ns_need(MAIN_NEED_NOTIFY); + } + } + } + } +#endif + return (errs); +} + +static int +gettoken(FILE *fp, const char *src) { + int c; + char op[32]; + + for (;;) { + c = getc(fp); + top: + switch (c) { + case EOF: + return (EOF); + + case '$': + if (getword(op, sizeof op, fp, 0)) { + if (!strcasecmp("include", op)) + return (INCLUDE); + if (!strcasecmp("origin", op)) + return (ORIGIN); + } + ns_notice(ns_log_db, + "%s:%d: Unknown $ option: $%s", + 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, preserve) + * get next word, skipping blanks & comments. + * '\' '\n' outside of "quotes" is considered a blank. + * parameters: + * buf - destination + * size - of destination + * fp - file to read from + * preserve - should we preserve \ before \\ and \.? + * return value: + * 0 = no word; perhaps EOL or EOF; lineno was incremented. + * 1 = word was read + */ +int +getword(char *buf, size_t size, FILE *fp, int preserve) { + char *cp = buf; + int c, spaceok; + + empty_token = 0; /* XXX global side effect. */ + while ((c = getc(fp)) != EOF) { + if (c == ';') { + /* Comment. Skip to end of line. */ + while ((c = getc(fp)) != EOF && c != '\n') + (void)NULL; + c = '\n'; + } + if (c == '\n') { + /* + * Unescaped newline. It's a terminator unless we're + * already midway into a token. + */ + if (cp != buf) + ungetc(c, fp); + else + lineno++; + break; + } + if (c == '"') { + /* "Quoted string." Gather the whole string here. */ + while ((c = getc(fp)) != EOF && c!='"' && c!='\n') { + if (c == '\\') { + if ((c = getc(fp)) == EOF) + c = '\\'; + if (preserve) + switch (c) { + case '\\': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (cp >= buf+size-1) + break; + *cp++ = '\\'; + } + if (c == '\n') + lineno++; + } + if (cp >= buf+size-1) + break; + *cp++ = c; + } + /* + * Newline string terminators are + * not token terminators. + */ + if (c == '\n') { + lineno++; + break; + } + /* Sample following character, check for terminator. */ + if ((c = getc(fp)) != EOF) + ungetc(c, fp); + if (c == EOF || isspace(c)) { + *cp = '\0'; + return (1); + } + continue; + } + spaceok = 0; + if (c == '\\') { + /* Do escape processing. */ + if ((c = getc(fp)) == EOF) + c = '\\'; + if (preserve) + switch (c) { + case '\\': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (cp >= buf+size-1) + break; + *cp++ = '\\'; + } + if (c == ' ' || c == '\t') + spaceok++; + } + if (isspace(c) && !spaceok) { + /* Blank of some kind. Skip run. */ + while (isspace(c = getc(fp)) && c != '\n') + (void)NULL; + ungetc(c, fp); + /* Blank means terminator if the token is nonempty. */ + if (cp != buf) /* Trailing whitespace */ + break; + continue; /* Leading whitespace */ + } + if (cp >= buf + size - 1) + break; + *cp++ = (char)c; + } + *cp = '\0'; + if (cp == buf) + empty_token = 1; + return (cp != buf); +} + +/* + * int + * getttl(fp, fn, ln, ttl, multiline) + * read a word from the file and parse it as a TTL. + * return: + * 1 ttl found + * 0 word not read (EOF or EOL?) + * -1 word read but it wasn't a ttl + * side effects: + * *ttl is written if the return value is to be 1. + */ +static int +getttl(FILE *fp, const char *fn, int lineno, u_int32_t *ttl, int *multiline) { + char buf[MAXDATA]; + u_long tmp; + int ch; + int len; + + while (!feof(fp) && !getword(buf, sizeof buf, fp, 0) && *multiline) + (void)NULL; + len = strlen(buf); + if (*multiline && len && buf[len-1] == ')') { + buf[len-1] = '\0'; + *multiline = 0; + } + if (ns_parse_ttl(buf, &tmp) < 0) { + ns_notice(ns_log_db, "%s:%d: expected a TTL, got \"%s\"", + fn, lineno, buf); + return (-1); + } + if (*multiline) { + ch = getnonblank(fp, fn); + if (ch == EOF) + return (-1); + if (ch == ';') + endline(fp); + else + ungetc(ch, fp); + } + *ttl = (u_int32_t)tmp; + return (1); +} + +/* Get multiline words. Same parameters as getword. Handles any + number of leading ('s or )'s in the words it sees. + FIXME: We kludge recognition of ( and ) for multiline input. + Each paren must appear at the start of a (blank-separated) word, + which is particularly counter-intuitive for ). Good enough for now, + until Paul rewrites the parser. (gnu@toad.com, oct96) +*/ +static int +getmlword(char *buf, size_t size, FILE *fp, int preserve) { + char *p; + + do { + while (!getword (buf, size, fp, preserve)) { + /* No more words on this line. See if doing the + multiline thing. */ + if (!getmlword_nesting) { /* Nope... */ + ungetc('\n', fp); /* Push back newline */ + lineno--; /* Unbump the lineno */ + empty_token = 0; /* Undo this botch */ + return 0; + } + if (feof(fp) || ferror(fp)) + return 0; /* Error, no terminating ')' */ + /* Continue reading til we get a word... */ + } + while ('(' == *buf) { + /* Word starts with paren. Multiline mode. + Move the rest of the word down over the paren. */ + getmlword_nesting++; + p = buf; + while (0 != (p[0]=p[1])) p++; + } + while (')' == *buf) { + getmlword_nesting--; + p = buf; + while (0 != (p[0]=p[1])) p++; + } + } while (buf[0] == 0); /* loop til we get a non-( non-) word */ + + return 1; /* Got a word... */ +} + +/* Get all the remaining words on a line, concatenated into one big + long (not too long!) string, with the whitespace squeezed out. + This routine, like getword(), does not swallow the newline if words seen. + This routine, unlike getword(), never swallows the newline if no words. + Parameters are the same as getword(). Result is: + 0 got no words at all + 1 got one or more words + -1 got too many words, they don't all fit; or missing close paren +*/ +static int +getallwords(char *buf, size_t size, FILE *fp, int preserve) { + char *runningbuf = buf; + int runningsize = size; + int len; + + while (runningsize > 0) { + if (!getmlword (runningbuf, runningsize, fp, preserve)) { + return runningbuf!=buf; /* 1 or 0 */ + } + len = strlen(runningbuf); + runningbuf += len; + runningsize -= len; + } + return -1; /* Error, String too long */ +} + +int +getnum(FILE *fp, const char *src, int opt) { + int c, n; + int seendigit = 0; + int seendecimal = 0; + int m = 0; + int allow_dots = 0; + + getnum_error = 0; +#ifdef DOTTED_SERIAL + if (opt & GETNUM_SERIAL) + allow_dots++; +#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 (getnum_error) + continue; + if (!isdigit(c)) { + if (c == ')' && seendigit) { + (void) ungetc(c, fp); + break; + } + if (seendigit && (opt & GETNUM_SCALED) && + strchr("KkMmGg", c) != NULL) { + switch (c) { + case 'K': case 'k': + n *= 1024; + break; + case 'M': case 'm': + n *= (1024 * 1024); + break; + case 'G': case 'g': + n *= (1024 * 1024 * 1024); + break; + } + break; + } + if (seendecimal || c != '.' || !allow_dots) { + ns_notice(ns_log_db, + "%s:%d: expected a number", + src, lineno); + getnum_error = 1; + } else { + if (!seendigit) + n = 1; +#ifdef SENSIBLE_DOTS + n *= 10000; +#else + 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 (getnum_error) + return (0); + if (m > 9999) { + ns_info(ns_log_db, + "%s:%d: number after the decimal point exceeds 9999", + src, lineno); + getnum_error = 1; + return (0); + } + if (seendecimal) { + ns_info(ns_log_db, + "%s:%d: decimal serial number interpreted as %d", + src, lineno, n+m); + } + return (n + m); +} + +#ifndef BIND_UPDATE +static +#endif +int +getnonblank(FILE *fp, const char *src) { + 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); + } + ns_info(ns_log_db, "%s:%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. + */ +int +makename(char *name, const char *origin, int size) { + int n; + u_char domain[MAXCDNAME]; + + switch (ns_name_pton(name, domain, sizeof(domain))) { + case -1: + return (-1); + case 1: /* FULLY QUALIFIED */ + break; + case 0: /* UNQUALIFIED */ + if (strcmp(name, "@") == 0) /* must test raw name */ + domain[0] = 0; + if ((n = dn_skipname(domain, domain+sizeof(domain))) == -1) + return (-1); + /* step back over root, append origin */ + switch (ns_name_pton(origin, domain+n-1, sizeof(domain)-n+1)) { + case -1: + return (-1); + case 0: + case 1: + break; + } + break; + } + if (ns_name_ntop(domain, name, size) == -1) + return (-1); + if (name[0] == '.') /* root */ + name[0] = '\0'; + return (0); +} + +static int +makename_ok(char *name, const char *origin, int class, struct zoneinfo *zp, + enum transport transport, enum context context, + const char *owner, const char *filename, int lineno, int size) +{ + int ret = 1; + + if (makename(name, origin, size) == -1) { + ns_info(ns_log_db, "%s:%d: makename failed", + filename, lineno); + return (0); + } + if (!ns_nameok(name, class, zp, transport, context, owner, + inaddr_any)) { + ns_info(ns_log_db, "%s:%d: database naming error", + filename, lineno); + ret = 0; + } + return (ret); +} + +void +endline(FILE *fp) { + int c; + + while ((c = getc(fp)) != '\0') { + if (c == '\n') { + (void) ungetc(c,fp); + break; + } else if (c == EOF) { + break; + } + } +} + +#define MAXPORT 1024 +#define MAXLEN 24 + +#ifndef BIND_UPDATE +static +#endif +char +getprotocol(FILE *fp, const char *src) { + int k; + char b[MAXLEN]; + + (void) getword(b, sizeof(b), fp, 0); + + k = protocolnumber(b); + if (k == -1) + ns_info(ns_log_db, "%s:%d: unknown protocol: %s.", + src, lineno, b); + return ((char) k); +} + +#ifndef BIND_UPDATE +static +#endif +int +getservices(int offset, char *data, FILE *fp, const char *src) { + int j, ch, k, maxl, bracket; + char bm[MAXPORT/8]; + char b[MAXLEN]; + + for (j = 0; j < MAXPORT/8; j++) + bm[j] = 0; + maxl = 0; + bracket = 0; + while (getword(b, sizeof(b), fp, 0) || 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') + (void)NULL; + if (ch == '\n') + lineno++; + break; + } + k = servicenumber(b); + if (k == -1) { + ns_info(ns_log_db, + "%s:%d: Unknown service '%s'", + src, lineno, b); + continue; + } + if ((k < MAXPORT) && (k)) { + bm[k/8] |= (0x80>>(k%8)); + if (k > maxl) + maxl = k; + } else { + ns_info(ns_log_db, + "%s:%d: port no. (%d) too big", + src, lineno, k); + } + } + if (bracket) + ns_info(ns_log_db, "%s:%d: missing close paren", + src, lineno); + maxl = maxl/8+1; + memcpy(data+offset, bm, maxl); + return (maxl+offset); +} + +/* + * Converts a word to a u_int32_t. Error if any non-numeric + * characters in the word, except leading or trailing white space. + */ +static u_int32_t +wordtouint32(buf) + char *buf; +{ + u_long result; + u_int32_t res2; + char *bufend; + + wordtouint32_error = 0; + result = strtoul(buf, &bufend, 0); + if (bufend == buf) + wordtouint32_error = 1; + else + while ('\0' != *bufend) { + if (isspace(*bufend)) + bufend++; + else { + wordtouint32_error = 1; + break; + } + } + /* Check for truncation between u_long and u_int32_t */ + res2 = result; + if (res2 != result) + wordtouint32_error = 1; + return (res2); +} + + +/* + * Parse part of a date. Set error flag if any error. + * Don't reset the flag if there is no error. + */ +static int +datepart(const char *buf, int size, int min, int max, int *errp) { + int result = 0; + int i; + + for (i = 0; i < size; i++) { + if (!isdigit(buf[i])) + *errp = 1; + result = (result * 10) + buf[i] - '0'; + } + if (result < min) + *errp = 1; + if (result > max) + *errp = 1; + return (result); +} + + +/* Convert a date in ASCII into the number of seconds since + 1 January 1970 (GMT assumed). Format is yyyymmddhhmmss, all + digits required, no spaces allowed. */ + +static u_int32_t +datetosecs(const char *cp, int *errp) { + struct tm time; + u_int32_t result; + int mdays, i; + static const int days_per_month[12] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if (strlen(cp) != 14) { + *errp = 1; + return 0; + } + *errp = 0; + + memset(&time, 0, sizeof time); + time.tm_year = datepart(cp + 0, 4, 1990, 9999, errp) - 1900; + time.tm_mon = datepart(cp + 4, 2, 01, 12, errp) - 1; + time.tm_mday = datepart(cp + 6, 2, 01, 31, errp); + time.tm_hour = datepart(cp + 8, 2, 00, 23, errp); + time.tm_min = datepart(cp + 10, 2, 00, 59, errp); + time.tm_sec = datepart(cp + 12, 2, 00, 59, errp); + if (*errp) /* Any parse errors? */ + return (0); + + /* + * OK, now because timegm() is not available in all environments, + * we will do it by hand. Roll up sleeves, curse the gods, begin! + */ + +#define SECS_PER_DAY ((u_int32_t)24*60*60) +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + + result = time.tm_sec; /* Seconds */ + result += time.tm_min * 60; /* Minutes */ + result += time.tm_hour * (60*60); /* Hours */ + result += (time.tm_mday - 1) * SECS_PER_DAY; /* Days */ + + /* Months are trickier. Look without leaping, then leap */ + mdays = 0; + for (i = 0; i < time.tm_mon; i++) + mdays += days_per_month[i]; + result += mdays * SECS_PER_DAY; /* Months */ + if (time.tm_mon > 1 && isleap (1900+time.tm_year)) + result += SECS_PER_DAY; /* Add leapday for this year */ + + /* First figure years without leapdays, then add them in. */ + /* The loop is slow, FIXME, but simple and accurate. */ + result += (time.tm_year - 70) * (SECS_PER_DAY*365); /* Years */ + for (i = 70; i < time.tm_year; i++) + if (isleap (1900+i)) + result += SECS_PER_DAY; /* Add leapday for prev year */ + + return (result); +} + + +#define MAXCHARSTRING 255 + +static int +getcharstring(char *buf, char *data, int type, + int minfields, int maxfields, + FILE *fp, const char *src) +{ + int nfield = 0, done = 0, n = 0, i; + char *b = buf; + + do { + nfield++; + i = strlen(buf); +#ifdef ALLOW_LONG_TXT_RDATA + b = buf; + if (type == ns_t_txt || type == ns_t_x25) { + while (i > MAXCHARSTRING + && n + MAXCHARSTRING + 1 < MAXDATA) { + data[n] = MAXCHARSTRING; + memmove(data + n + 1, b, MAXCHARSTRING); + n += MAXCHARSTRING + 1; + b += MAXCHARSTRING; + i -= MAXCHARSTRING; + } + } +#endif /* ALLOW_LONG_TXT_RDATA */ + if (i > MAXCHARSTRING) { + ns_info(ns_log_db, + "%s:%d: RDATA field %d too long", + src, lineno, nfield); + return (0); + } + if (n + i + 1 > MAXDATA) { + ns_info(ns_log_db, + "%s:%d: total RDATA too long", + src, lineno); + return (0); + } + data[n] = i; + memmove(data + n + 1, b, (int)i); + n += i + 1; + done = (maxfields && nfield >= maxfields); + } while (!done && getword(buf, MAXDATA, fp, 0)); + + if (nfield < minfields) { + ns_info(ns_log_db, + "%s:%d: expected %d RDATA fields, only saw %d", + src, lineno, minfields, nfield); + return (0); + } + + if (done) + endline(fp); + + return (n); +} + + +/* + * get_nxt_types(): Read the list of types in the NXT record. + * + * Data is the array where the bit flags are stored; it must + * contain at least ns_t_any/NS_NXT_BITS bytes. + * FP is the input FILE *. + * Filename is the sourcefile + * + * The result is how many bytes are significant in the result. + * ogud@tis.com 1995 + */ +static int +get_nxt_types(u_char *data, FILE *fp, const char *filename) { + char b[MAXLABEL]; /* Not quite the right size, but good enough */ + int maxtype=0; + int success; + int type; + int errs = 0; + + memset(data, 0, ns_t_any/NS_NXT_BITS+1); + + while (getmlword(b, sizeof(b), fp, 0)) { + if (feof(fp) || ferror(fp)) + break; + if (strlen(b) == 0 || b[0] == '\n') + continue; + + /* Parse RR type (A, MX, etc) */ + type = sym_ston(__p_type_syms, (char *)b, &success); + if ((!success) || type == ns_t_any) { + errs++; + ns_info(ns_log_db, + "%s: Line %d: Unknown type: %s in NXT record.", + filename, lineno, b); + continue; + } + NS_NXT_BIT_SET(type, data); + if (type > maxtype) + maxtype = type; + } + if (errs) + return (0); + else + return (maxtype/NS_NXT_BITS+1); +} + +/* sanity checks PRIMARY ONLY */ +static void +fixup_soa(const char *fn, struct zoneinfo *zp) { + /* Sanity: give enough time for the zone to transfer (retry). */ + if (zp->z_expire < (zp->z_refresh + zp->z_retry)) + ns_notice(ns_log_db, + "%s: WARNING SOA expire value is less than SOA refresh+retry (%u < %u+%u)", + fn, zp->z_expire, zp->z_refresh, zp->z_retry); + + /* Sanity. */ + if (zp->z_expire < (zp->z_refresh + 10 * zp->z_retry)) + ns_warning(ns_log_db, +"%s: WARNING SOA expire value is less than refresh + 10 * retry \ +(%u < (%u + 10 * %u))", + fn, zp->z_expire, zp->z_refresh, zp->z_retry); + + /* + * Sanity: most hardware/telco faults are detected and fixed within + * a week, secondaries should continue to operate for this time. + * (minimum of 4 days for long weekends) + */ + if (zp->z_expire < (7 * 24 * 3600)) + ns_warning(ns_log_db, + "%s: WARNING SOA expire value is less than 7 days (%u)", + fn, zp->z_expire); + + /* + * Sanity: maximum down time if we havn't talked for six months + * war must have broken out. + */ + if (zp->z_expire > ( 183 * 24 * 3600)) + ns_warning(ns_log_db, + "%s: WARNING SOA expire value is greater than 6 months (%u)", + fn, zp->z_expire); + + /* Sanity. */ + if (zp->z_refresh < (zp->z_retry * 2)) + ns_warning(ns_log_db, + "%s: WARNING SOA refresh value is less than 2 * retry (%u < %u * 2)", + fn, zp->z_refresh, zp->z_retry); +} + +#ifdef BIND_NOTIFY +static void +free_notify_info(notify_info ni) { + if (ni->state == notify_info_waitfor) + evUnwait(ev, ni->wait_id); + else if (ni->state == notify_info_delay) + evClearTimer(ev, ni->timer_id); + freestr(ni->name); + memput(ni, sizeof *ni); +} + +void +notify_after_load(evContext ctx, void *uap, const void *tag) { + int delay, max_delay; + notify_info ni = uap; + + INSIST(tag == (const void *)notify_after_load); + + /* delay notification for from five seconds up to fifteen minutes */ + max_delay = MIN(nzones/5, 895); + max_delay = MAX(max_delay, 25); + delay = 5 + (rand() % max_delay); + ns_debug(ns_log_notify, 3, "notify_after_load: uap %p tag %p delay %d", + uap, tag, delay); + if (evSetTimer(ctx, notify_after_delay, ni, + evAddTime(evNowTime(), evConsTime(delay, 0)), + evConsTime(0, 0), &ni->timer_id) < 0) { + ns_error(ns_log_notify, "evSetTimer() failed: %s", + strerror(errno)); + UNLINK(pending_notifies, ni, link); + ni->state = notify_info_error; + free_notify_info(ni); + } + ni->state = notify_info_delay; +} + +static void +notify_after_delay(evContext ctx, void *uap, + struct timespec due, + struct timespec inter) +{ + notify_info ni = uap; + + UNLINK(pending_notifies, ni, link); + ni->state = notify_info_done; + sysnotify(ni->name, ni->class, ns_t_soa); + free_notify_info(ni); +} + +void +db_cancel_pending_notifies(void) { + notify_info ni, ni_next; + for (ni = HEAD(pending_notifies); ni != NULL; ni = ni_next) { + ni_next = NEXT(ni, link); + free_notify_info(ni); + } + INIT_LIST(pending_notifies); +} +#endif diff --git a/contrib/bind/bin/named/db_lookup.c b/contrib/bind/bin/named/db_lookup.c new file mode 100644 index 0000000..ddf17ad --- /dev/null +++ b/contrib/bind/bin/named/db_lookup.c @@ -0,0 +1,269 @@ +#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 8.13 1998/02/13 19:52:54 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Table lookup routines. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.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(const char *name, struct hashbuf **htpp, + const char **fname, int insert) +{ + struct namebuf *np; + const char *cp; + int c; + u_int hval; + struct hashbuf *htp; + struct namebuf *parent = NULL; + int escaped = 0; + + htp = *htpp; + hval = 0; + *fname = "???"; + for (cp = name; (c = *cp++) != 0; (void)NULL) { + if (!escaped && (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 (ns_wildcard(NAME(*np))) + *fname = name; + return (np); + } + htp = savehash((struct hashbuf *)NULL); + np->n_hash = htp; + } + *htpp = htp; + break; + } + + /* rotate left HASHSHIFT */ + hval = (hval << HASHSHIFT) | + (hval>>((sizeof(hval)*8)-HASHSHIFT)); + hval += ((isascii(c) && isupper(c)) ? tolower(c) : c) + & HASHMASK; + if (escaped) + escaped = 0; + else if (c == '\\') + escaped = 1; + } + cp--; + /* + * 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 && + ((size_t)NAMELEN(*np) == (size_t)(cp - name)) && + (strncasecmp(name, NAME(*np), cp - name) == 0)) { + *fname = name; + 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 (ns_wildcard(NAME(*np)) && + np->n_data && np->n_data->d_zone != 0) { + *fname = name; + return (np); + } + } + return (parent); + } + np = savename(name, cp - 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; + return (np); +} + +/* struct namebuf * + * np_parent(struct namebuf *np) + * Find the "parent" namebuf of np. + * This is tricky since the parent of "com" is "" and both are stored + * in the same hashbuf. + * See also: + * the AXFR wart description in ns_req.c + */ +struct namebuf * +np_parent(np) + struct namebuf *np; +{ + struct hashbuf *htp; + struct namebuf *np2; + + if (np->n_parent != NULL || NAME(*np)[0] == '\0') + return (np->n_parent); + + /* Try to figure out if np is pointing into the cache or hints. */ + /* Try the cache first. */ + htp = hashtab; + try_again: + /* Search the hash chain that np should be part of. */ + for (np2 = htp->h_tab[np->n_hashval % htp->h_size]; + np2 != NULL; + np2 = np2->n_next) { + + if (np == np2) { /* found it! */ + /* "" hashes into the first bucket */ + for (np = htp->h_tab[0]; np ; np=np->n_next) { + if (NAME(*np)[0] == '\0') + /* found the root namebuf */ + return (np); + } + ns_debug(ns_log_db, 1, + "np_parent(0x%lx) couldn't find root entry", + (u_long) np); + return (NULL); /* XXX shouldn't happen */ + } + } + /* Try the hints. */ + if (htp == hashtab) { + htp = fcachetab; + goto try_again; + } + ns_debug(ns_log_db, 1, "np_parent(0x%lx) couldn't namebuf", + (u_long)np); + return (NULL); /* XXX shouldn't happen */ +} + +/* int + * match(dp, class, type) + * Does data record `dp' match the class and type? + * return value: + * boolean + */ +int +match(struct databuf *dp, int class, int 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/contrib/bind/bin/named/db_save.c b/contrib/bind/bin/named/db_save.c new file mode 100644 index 0000000..a05c40f --- /dev/null +++ b/contrib/bind/bin/named/db_save.c @@ -0,0 +1,221 @@ +#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 8.15 1998/01/26 22:40:08 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Buffer allocation and deallocation routines. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +/* + * Allocate a name buffer & save name. + */ +struct namebuf * +savename(const char *name, int len) { + struct namebuf *np; + + /* + * Note that MAXLABEL * 4 < 256, so a single length byte is enough. + * Also, we use MAXLABEL * 4 because each label character can + * expand into up to four characters when rendered in canonical + * form. + */ + INSIST(len >= 0 && len <= (MAXLABEL * 4)); + np = (struct namebuf *) memget(NAMESIZE(len)); + if (np == NULL) + panic("savename: memget", NULL); + memset(np, 0, NAMESIZE(len)); + NAMELEN(*np) = (unsigned)len; + memcpy(NAME(*np), name, len); + NAME(*np)[len] = '\0'; + return (np); +} + +/* + * Allocate a data buffer & save data. + */ +struct databuf * +savedata(class, type, ttl, data, size) + int class, type; + u_int32_t ttl; + u_char *data; + int size; +{ + struct databuf *dp; + int bytes = (type == T_NS) ? DATASIZE(size)+INT32SZ : DATASIZE(size); + + dp = (struct databuf *)memget(bytes); + if (dp == NULL) + panic("savedata: memget", NULL); + memset(dp, 0, bytes); + 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; + dp->d_rcode = NOERROR; + dp->d_ns = NULL; + dp->d_nstime = 0; + memcpy(dp->d_data, data, dp->d_size); + return (dp); +} + +int hashsizes[] = { /* hashtable sizes */ + 2, + 11, + 113, + 337, + 977, + 2053, + 4073, + 8011, + 16001, + 99887, + 0 +}; + +/* + * Allocate a data buffer & save data. + */ +struct hashbuf * +savehash(oldhtp) + struct hashbuf *oldhtp; +{ + struct hashbuf *htp; + struct namebuf *np, *nnp, **hp; + int n, newsize; + + if (oldhtp == NULL) + newsize = hashsizes[0]; + else { + for (n = 0; (newsize = hashsizes[n++]) != 0; (void)NULL) + if (oldhtp->h_size == newsize) { + newsize = hashsizes[n]; + break; + } + if (newsize == 0) + newsize = oldhtp->h_size * 2 + 1; + } + ns_debug(ns_log_db, 4, "savehash GROWING to %d", newsize); + htp = (struct hashbuf *) memget(HASHSIZE(newsize)); + if (htp == NULL) + ns_panic(ns_log_db, 0, "savehash: %s", strerror(errno)); + htp->h_size = newsize; + memset(htp->h_tab, 0, newsize * sizeof(struct namebuf *)); + if (oldhtp == NULL) { + htp->h_cnt = 0; + return (htp); + } + ns_debug(ns_log_db, 4, "savehash(%#lx) cnt=%d, sz=%d, newsz=%d", + (u_long)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; + } + } + oldhtp->h_cnt = 0; /* Keep rm_hash() happy. */ + rm_hash(oldhtp); + return (htp); +} diff --git a/contrib/bind/bin/named/db_update.c b/contrib/bind/bin/named/db_update.c new file mode 100644 index 0000000..ab70663 --- /dev/null +++ b/contrib/bind/bin/named/db_update.c @@ -0,0 +1,932 @@ +#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 8.23 1998/02/13 20:01:38 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <resolv.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.h" + +#include "named.h" + +/* 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(const char *name, struct hashbuf *htp) { + struct namebuf *np; + 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_class == C_HS) && + dp->d_type == T_NS && + !dp->d_rcode && + !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, int class) + * 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(struct namebuf *np, int class) { + for ((void)NULL; np; np = np_parent(np)) { + 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); +} + + +#define ISVALIDGLUE(xdp) ((xdp)->d_type == T_NS || (xdp)->d_type == T_A \ + || (xdp)->d_type == T_AAAA) + +/* int + * db_update(name, odp, newdp, savedpp, flags, htp, from) + * 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 clean_cache() and made + * general enough for this use, and for clean_cache()'s continued use. + * vix, 21jul94 + */ +int +db_update(const char *name, + struct databuf *odp, struct databuf *newdp, + struct databuf **savedpp, + int flags, struct hashbuf *htp, struct sockaddr_in from) +{ + struct databuf *dp, *pdp; + struct namebuf *np; + int zn, isHintNS; + int check_ttl = 0; + const char *fname; +#ifdef BIND_UPDATE + int i, found_other_ns = 0; + struct databuf *tmpdp; + u_char *cp1, *cp2; + u_int32_t dp_serial, newdp_serial; +#endif + + ns_debug(ns_log_db, 3, "db_update(%s, %#x, %#x, %#x, 0%o, %#x)%s", + name, odp, newdp, savedpp, flags, htp, + (odp && (odp->d_flags&DB_F_HINT)) ? " hint" : ""); + np = nlookup(name, &htp, &fname, newdp != NULL); + if (np == NULL || fname != name) + return (NONAME); + + if (newdp && zones[newdp->d_zone].z_type == Z_PRIMARY) + check_ttl = 1; + + /* don't let nonauthoritative updates write in authority zones */ + if (newdp && ((zn = findMyZone(np, newdp->d_class)) != DB_Z_CACHE) && +#ifdef STUBS + (zones[zn].z_type != Z_STUB) && +#endif + (flags & DB_NOTAUTH)) { + int foundRR = 0; + + /* + * Don't generate the warning 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) + ns_debug(ns_log_db, 5, + "[%s].%d update? to auth zone \"%s\" (%s)", + inet_ntoa(from.sin_addr), + ntohs(from.sin_port), + zones[zn].z_origin, + name); + return (AUTH); + } + + if (newdp && zn && !(flags & DB_NOTAUTH)) { + if (nlabels(zones[zn].z_origin) > newdp->d_clev) { + ns_debug(ns_log_db, 5, + "attempted update child zone %s, %s", + zones[zn].z_origin, name); + return (AUTH); + } + } + + /* some special checks for root NS' A RR's */ + isHintNS = isRefByNS(name, fcachetab); +#ifdef DEPRECATED + if (newdp && isHintNS && newdp->d_type == T_A) { + /* upgrade credibility of additional data for rootsrv addrs */ + if (newdp->d_cred == DB_C_ADDITIONAL) { + ns_debug(ns_log_db, 3, + "upgrading credibility for A RR (%s)", + 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; + } + } +#endif + + /* Reflect certain updates in hint cache also... */ + /* Don't stick data we are authoritative for in hints. */ + if (!(flags & DB_NOHINTS) && + (flags & DB_PRIMING) && + (odp != NULL) && + (htp != fcachetab) && + (DB_Z_SPECIAL(odp->d_zone)) && + !(odp->d_flags & DB_F_HINT) && + (!newdp || !newdp->d_rcode) && + ((name[0] == '\0' && odp->d_type == T_NS) || + (odp->d_type == T_A && isHintNS) + ) + ) + { + ns_debug(ns_log_db, 3, "db_update: hint '%s' %u", + 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, NULL, + (flags|DB_NOHINTS), + fcachetab, from) + != OK) { + ns_debug(ns_log_db, 3, + "db_update: hint %#x freed", dp); + db_freedata(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. + */ + /* Check that CNAMEs are only accompanied by + * Secure DNS RR's (KEY, SIG, and NXT). + */ + if (((dp->d_type == T_CNAME && + odp->d_type != T_KEY && + odp->d_type != T_SIG && + odp->d_type != T_NXT) || + (odp->d_type == T_CNAME && + dp->d_type != T_KEY && + dp->d_type != T_SIG && + dp->d_type != T_NXT)) && + odp->d_class == dp->d_class && + /* XXXRTH d_mark removed in 4.9.5, + but still here for dynamic + update */ + odp->d_mark == dp->d_mark && + !dp->d_rcode && + !odp->d_rcode && +#ifdef BIND_UPDATE + /* updating a CNAME with another CNAME is permitted */ + (dp->d_type != T_CNAME || + odp->d_type != T_CNAME) && +#endif + zones[odp->d_zone].z_type != Z_CACHE) { + ns_info(ns_log_db, + "%s has CNAME and other data (invalid)", + name); + goto skip; + } + if (!newdp || newdp->d_class != dp->d_class) + goto skip; + + /* 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 0 /* caught by findMyZone() now. */ + /* 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); +#endif + + /* if the new data is authoritative but + * but isn't as credible, reject it. + */ + if (newdp->d_cred == DB_C_ZONE && + dp->d_cred == DB_C_ZONE) { + /* Both records are from a zone file. + * If their credibility levels differ, + * we're dealing with a zone cut. The + * record with lower clev is from the + * upper zone's file and is therefore + * glue. + */ + if (newdp->d_clev < dp->d_clev) { + if (!ISVALIDGLUE(newdp)) { + ns_info(ns_log_db, + "domain %s %s record in zone %s should be in zone %s, ignored", + name, p_type(newdp->d_type), + zones[newdp->d_zone].z_origin, + zones[dp->d_zone].z_origin); + } + return (AUTH); + } + if (newdp->d_clev > dp->d_clev) { + if (!ISVALIDGLUE(dp)) { + ns_info(ns_log_db, + "domain %s %s record in zone %s should be in zone %s, deleted", + name, p_type(dp->d_type), + zones[dp->d_zone].z_origin, + zones[newdp->d_zone].z_origin); + } + goto delete; + } + } + + /* 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 */ + + goto skip; + } /*if {class,type} did not match*/ + + /* + * {type,class} did match. This is the replace case. + */ + ns_debug(ns_log_db, 5, + "db_update: flags = %#x, sizes = %d, %d (cmp %d)", + flags, odp->d_size, dp->d_size, + db_cmp(dp, odp)); + if (newdp) { + ns_debug(ns_log_db, 4, + "credibility for %s is %d(%d) from [%s].%d, is %d(%d) in cache", + *name ? name : ".", + newdp->d_cred, + newdp->d_clev, + inet_ntoa(from.sin_addr), + ntohs(from.sin_port), + dp->d_cred, + dp->d_clev); + if (newdp->d_cred > dp->d_cred) { + /* better credibility. + * 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 && + dp->d_cred == DB_C_ZONE ) { + /* Both records are from a zone file. + * If their credibility levels differ, + * we're dealing with a zone cut. The + * record with lower clev is from the + * upper zone's file and is therefore + * glue. + */ + + /* XXX - Tricky situation here is you + * have 2 zones a.b.c and sub.a.b.c + * being served by the same server. + * named will send NS records for + * sub.a.b.c during zone transfer of + * a.b.c zone. If we're secondary for + * both zones, and we reload zone + * a.b.c, we'll get the NS records + * (and possibly A records to go with + * them?) for sub.a.b.c as part of the + * a.b.c zone transfer. But we've + * already got a more credible record + * from the sub.a.b.c zone. So we want + * to ignore the new record, but we + * shouldn't syslog because there's + * nothing the user can do to prevent + * the situation. Perhaps we should + * only complain when we are primary? + */ + + if (newdp->d_clev < dp->d_clev) { + if (!ISVALIDGLUE(newdp)) { + ns_info(ns_log_db, + "domain %s %s record in zone %s should be in zone %s, ignored", + name, p_type(newdp->d_type), + zones[newdp->d_zone].z_origin, + zones[dp->d_zone].z_origin); + } + return (AUTH); + } + if (newdp->d_clev > dp->d_clev) { + if (!ISVALIDGLUE(dp)) { + ns_info(ns_log_db, + "domain %s %s record in zone %s should be in zone %s, deleted", + name, p_type(dp->d_type), + zones[dp->d_zone].z_origin, + zones[newdp->d_zone].z_origin); + } + goto delete; + } + } + + /* credibility is the same. + * let it aggregate in the normal way. + */ + + /* + * 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); + } + + /* + * Some RR types should not be aggregated. + */ + if (dp->d_type == T_SOA) { +#ifdef BIND_UPDATE + u_int32_t dp_ser, ndp_ser; + u_char *dp_cp, *ndp_cp; + + dp_cp = findsoaserial(dp->d_data); + ndp_cp = findsoaserial(newdp->d_data); + GETLONG(dp_ser, dp_cp); + GETLONG(ndp_ser, ndp_cp); + + if (SEQ_GT(ndp_ser, dp_ser)) + goto delete; + else + return (SERIAL); +#else + goto delete; +#endif /*BIND_UPDATE*/ + } + if (dp->d_type == T_WKS && + !memcmp(dp->d_data, newdp->d_data, + INT32SZ + sizeof(u_char))) + goto delete; + if (dp->d_type == T_CNAME && + !NS_OPTION_P(OPTION_MULTIPLE_CNAMES)) + goto delete; +#ifdef BIND_UPDATE + if (dp->d_type == T_SIG) + /* + * Type covered has already been + * checked. + */ + goto delete; +#endif + if (check_ttl) { + if (newdp->d_ttl != dp->d_ttl) + ns_warning(ns_log_db, + "%s %s %s differing ttls: corrected", + name[0]?name:".", + p_class(dp->d_class), + p_type(dp->d_type)); + if (newdp->d_ttl > dp->d_ttl) { + newdp->d_ttl = dp->d_ttl; + } else { + dp->d_ttl = newdp->d_ttl; + } + } + } + if ((flags & DB_NODATA) && !db_cmp(dp, odp)) { + /* Refresh ttl if cache entry. */ + if (dp->d_zone == DB_Z_CACHE) { + if (odp->d_zone != DB_Z_CACHE) { + /* Changing cache->auth. */ + dp->d_zone = odp->d_zone; + dp->d_ttl = odp->d_ttl; + ns_debug(ns_log_db, 4, + "db_update: cache entry now in auth zone"); + return (DATAEXISTS); + } + fixttl(odp); + if (odp->d_ttl > dp->d_ttl) + dp->d_ttl = odp->d_ttl; + ns_debug(ns_log_db, 3, + "db_update: new ttl %u +%lu", + dp->d_ttl, + (u_long)(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. + */ + if (odp->d_size > 0) + if (db_cmp(dp, odp)) + goto skip; + if (odp->d_clev < dp->d_clev) + goto skip; + if (odp->d_cred < dp->d_cred) + goto skip; +#ifdef BIND_UPDATE + if (!strcasecmp(name, zones[dp->d_zone].z_origin) && + !newdp) { + /* do not delete SOA or NS records as a set */ + /* XXXRTH isn't testing d_size unnecessary? */ + if ((odp->d_size == 0) && + (odp->d_class == C_ANY) && + (odp->d_type == T_ANY || + odp->d_type == T_SOA || + odp->d_type == T_NS) && + (dp->d_type == T_SOA || + dp->d_type == T_NS)) + goto skip; + /* XXXRTH I added this to prevent SOA deletion + I'm using the same style of comparison as + the other code in this section. Do we + really need to look at dp->d_type here? + We're in the "match" section... */ + if ((odp->d_type == T_SOA) && + (dp->d_type == T_SOA)) + goto skip; + /* do not delete the last NS record + for the zone */ + if ((odp->d_type == T_NS) && + (dp->d_type == T_NS)) { + found_other_ns = 0; + for (tmpdp = np->n_data; + tmpdp && !found_other_ns; + tmpdp = tmpdp->d_next) + if ((tmpdp->d_type == T_NS) && + (tmpdp != dp)) + found_other_ns = 1; + if (!found_other_ns) { + ns_debug(ns_log_db, 3, + "cannot delete last remaining NS record for zone %s", + name); + goto skip; + } + } + } +#endif + + foundRR = 1; + if (flags & DB_DELETE) { + delete: +#ifdef BIND_UPDATE + /* + * XXX assume here that savedpp!=NULL iff. db_update + * has been called by the dyanmic update code. + * Maybe a new flag is more appropriate? + */ + if (savedpp != NULL) + foundRR = 1; +#endif + dp = rm_datum(dp, np, pdp, savedpp); + } else { + skip: pdp = dp; + dp = dp->d_next; + } + } + if (!foundRR) { + if (flags & DB_DELETE) + return (NODATA); + if (flags & DB_MEXIST) + return (NODATA); + } + } + /* XXX: delete a terminal namebuf also if all databuf's + * underneath of it have been deleted) */ + if (newdp == NULL) + return (OK); + /* XXX: empty nodes bypass credibility checks above; should check + * response source address here if flags&NOTAUTH. + */ + fixttl(newdp); + ns_debug(ns_log_db, 3, "db_update: adding%s %#x", + (newdp->d_flags&DB_F_HINT) ? " hint":"", newdp); + + if (NS_OPTION_P(OPTION_HOSTSTATS) && + newdp->d_zone == DB_Z_CACHE && + (newdp->d_flags & DB_F_HINT) == 0) + newdp->d_ns = nameserFind(from.sin_addr, NS_F_INSERT); + + /* Add to end of list, generally preserving order */ + newdp->d_next = NULL; + if ((dp = np->n_data) == NULL) { + DRCNTINC(newdp); + if (newdp->d_flags & DB_F_ACTIVE) + panic("db_update: DB_F_ACTIVE set", NULL); + newdp->d_flags |= DB_F_ACTIVE; + 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); + DRCNTINC(newdp); + if (newdp->d_flags & DB_F_ACTIVE) + panic("db_update: DB_F_ACTIVE set", NULL); + newdp->d_flags |= DB_F_ACTIVE; + dp->d_next = newdp; + return (OK); +} + +void +fixttl(struct databuf *dp) { + if (dp->d_zone == DB_Z_CACHE && (dp->d_flags & DB_F_HINT) == 0) { + if (dp->d_ttl <= (u_int32_t)tt.tv_sec) + return; + else if (dp->d_ttl < (u_int32_t)tt.tv_sec+min_cache_ttl) + dp->d_ttl = (u_int32_t)tt.tv_sec+min_cache_ttl; + else if (dp->d_ttl > (u_int32_t)tt.tv_sec+max_cache_ttl) + dp->d_ttl = (u_int32_t)tt.tv_sec+max_cache_ttl; + } +} + +/* + * Compare type, class and data from databufs for equivalence. + * All domain names in RR's must be compared case-insensitively. + * Return 0 if equivalent, nonzero otherwise. + */ +int +db_cmp(const struct databuf *dp1, const struct databuf *dp2) { + const u_char *cp1, *cp2; + int len, len2; + + /* XXXDYNUP- should be changed to + if (!match(dp1, dp2->d_type, dp2->d_class) */ + if (dp1->d_type != dp2->d_type || dp1->d_class != dp2->d_class) + return (1); + /* XXXDYNUP - should be changed to (dp1->d_size != dp2->d_size && + dp1->d_size != 0 && dp2->d_size != 0) */ + if (dp1->d_size != dp2->d_size) + return (1); + /* d_mark is only used for dynamic updates currently */ +#ifndef BIND_UPDATE + if (dp1->d_mark != dp2->d_mark) + return (1); /* old and new RR's are distinct */ +#endif + 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); + + switch (dp1->d_type) { + + case T_A: + case T_WKS: + case T_NULL: + case T_NSAP: + case T_AAAA: + case T_LOC: + case T_KEY: + /* Only binary data */ + return (memcmp(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: + /* Only a domain name */ + return (strcasecmp((char *)dp1->d_data, (char *)dp2->d_data)); + + case T_SIG: + /* Binary data, a domain name, more binary data */ + if (dp1->d_size < NS_SIG_SIGNER) + return (1); + if (memcmp(dp1->d_data, dp2->d_data, NS_SIG_SIGNER)) + return (1); + len = NS_SIG_SIGNER + + strlen((char *)dp1->d_data + NS_SIG_SIGNER); + if (strcasecmp((char *)dp1->d_data + NS_SIG_SIGNER, + (char *)dp2->d_data + NS_SIG_SIGNER)) + return (1); + return (memcmp(dp1->d_data + len, + dp2->d_data + len, + dp1->d_size - len)); + + case T_NXT: + /* First a domain name, then binary data */ + if (strcasecmp((char *)dp1->d_data, (char *)dp2->d_data)) + return (1); + len = strlen((char *)dp1->d_data)+1; + return (memcmp(dp1->d_data + len, + dp2->d_data + len, + dp1->d_size - len)); + + case T_HINFO: + case T_ISDN: + cp1 = dp1->d_data; + cp2 = dp2->d_data; + len = *cp1; + len2 = *cp2; + if (len != len2) + return (1); + if (strncasecmp((char *)++cp1, (char *)++cp2, len)) + return (1); + cp1 += len; + cp2 += len; + len = *cp1; + len2 = *cp2; + if (len != len2) + return (1); + 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 (memcmp(cp1, cp2, INT32SZ * 5)); + + case T_NAPTR: { + int t1,t2; + + if (dp1->d_size != dp2->d_size) + return (1); + cp1 = dp1->d_data; + cp2 = dp2->d_data; + + /* Order */ + if (*cp1++ != *cp2++ || *cp1++ != *cp2++) + return (1); + + /* Preference */ + if (*cp1++ != *cp2++ || *cp1++ != *cp2++) + return (1); + + /* Flags */ + t1 = *cp1++; t2 = *cp2++; + if (t1 != t2 || memcmp(cp1, cp2, t1)) + return (1); + cp1 += t1; cp2 += t2; + + /* Services */ + t1 = *cp1++; t2 = *cp2++; + if (t1 != t2 || memcmp(cp1, cp2, t1)) + return (1); + cp1 += t1; cp2 += t2; + + /* Regexp */ + t1 = *cp1++; t2 = *cp2++; + if (t1 != t2 || memcmp(cp1, cp2, t1)) + return (1); + cp1 += t1; cp2 += t2; + + /* Replacement */ + t1 = strlen((char *)cp1); t2 = strlen((char *)cp2); + if (t1 != t2 || memcmp(cp1, cp2, t1)) + return (1); + cp1 += t1 + 1; cp2 += t2 + 1; + + /* they all checked out! */ + return (0); + } + + case T_MX: + case T_AFSDB: + case T_RT: + case T_SRV: + cp1 = dp1->d_data; + cp2 = dp2->d_data; + if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* cmp prio */ + return (1); + if (dp1->d_type == T_SRV) { + if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* weight */ + return (1); + if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* port */ + return (1); + } + return (strcasecmp((char *)cp1, (char *)cp2)); + + case T_PX: + cp1 = dp1->d_data; + cp2 = dp2->d_data; + if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* cmp prio */ + return (1); + if (strcasecmp((char *)cp1, (char *)cp2)) + return (1); + cp1 += strlen((char *)cp1) + 1; + cp2 += strlen((char *)cp2) + 1; + return (strcasecmp((char *)cp1, (char *)cp2)); + + case T_TXT: + case T_X25: + if (dp1->d_size != dp2->d_size) + return (1); + return (memcmp(dp1->d_data, dp2->d_data, dp1->d_size)); + + default: + return (1); + } +} diff --git a/contrib/bind/bin/named/named-bootconf.pl b/contrib/bind/bin/named/named-bootconf.pl new file mode 100755 index 0000000..ce474c4 --- /dev/null +++ b/contrib/bind/bin/named/named-bootconf.pl @@ -0,0 +1,324 @@ +#!/usr/bin/perl + +## Copyright (c) 1996, 1997 by Internet Software Consortium +## +## Permission to use, copy, modify, and distribute this software for any +## purpose with or without fee is hereby granted, provided that the above +## copyright notice and this permission notice appear in all copies. +## +## THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +## ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +## OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +## CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +## DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +## PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +## ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +## SOFTWARE. + +## $Id: named-bootconf.pl,v 8.16 1998/02/13 19:48:25 halley Exp $ + +# This is a filter. Input is a named.boot. Output is a named.conf. + +$new_config = ""; + +$have_options = 0; +%options = (); +%options_comments = (); +@topology = (); +@topology_comments = (); +@bogus = (); +@bogus_comments = (); +@transfer_acl = (); +@transfer_comments = (); +$logging = ""; + +while(<>) { + next if /^$/; + + # skip comment-only lines + if (/^\s*;+\s*(.*)$/) { + $new_config .= "// $1\n"; + next; + } + + # handle continued lines + while (/\\$/) { + s/\\$/ /; + $_ .= <>; + } + + chop; + + # deal with lines ending in a coment + if (s/\s*;+\s*(.*)$//) { + $comment = "// $1"; + } else { + $comment = ""; + } + + ($directive, @rest) = split; + + $class = ""; + if ($directive =~ /^(.*)\/(.*)$/) { + $directive = $1; + $class = $2; + } + + if ($directive eq "primary") { + $zname = shift(@rest); + &maybe_print_comment("","\n"); + $new_config .= "zone \"$zname\" "; + if ($class ne "") { + $new_config .= "$class "; + } + $new_config .= "{\n"; + $new_config .= "\ttype master;\n"; + $filename = shift(@rest); + $new_config .= "\tfile \"$filename\";\n"; + $new_config .= "};\n\n"; + } elsif ($directive eq "secondary" || $directive eq "stub") { + if ($directive eq "secondary") { + $type = "slave"; + } else { + $type = "stub"; + } + $zname = shift(@rest); + &maybe_print_comment("","\n"); + $new_config .= "zone \"$zname\" "; + if ($class ne "") { + $new_config .= "$class "; + } + $new_config .= "{\n"; + $new_config .= "\ttype $type;\n"; + $filename = pop(@rest); + if ($filename =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) { + push(@rest, $filename); + $filename = ""; + } else { + $new_config .= "\tfile \"$filename\";\n"; + } + $new_config .= "\tmasters {\n"; + foreach $master (@rest) { + $new_config .= "\t\t$master;\n"; + } + $new_config .= "\t};\n"; + $new_config .= "};\n\n"; + } elsif ($directive eq "cache") { + $zname = shift(@rest); + &maybe_print_comment("","\n"); + $new_config .= "zone \"$zname\" {\n"; + $new_config .= "\ttype hint;\n"; + $filename = shift(@rest); + $new_config .= "\tfile \"$filename\";\n"; + $new_config .= "};\n\n"; + } elsif ($directive eq "directory") { + $options{"directory"} = "\"$rest[0]\""; + $options_comments{"directory"} = $comment; + $have_options = 1; + } elsif ($directive eq "check-names") { + $type = shift(@rest); + if ($type eq "primary") { + $type = "master"; + } elsif ($type eq "secondary") { + $type = "slave"; + } + $action = shift(@rest); + $options{"check-names $type"} = $action; + $options_comments{"check-names $type"} = $comment; + $have_options = 1; + } elsif ($directive eq "forwarders") { + $options{"forwarders"}="{\n"; + foreach $forwarder (@rest) { + $options{"forwarders"} .= "\t\t$forwarder;\n"; + } + $options{"forwarders"} .= "\t}"; + $options_comments{"forwarders"} = $comment; + $have_options = 1; + } elsif ($directive eq "slave") { + &handle_options("forward-only"); + } elsif ($directive eq "options") { + &handle_options(@rest); + } elsif ($directive eq "limit") { + &handle_limit(@rest); + } elsif ($directive eq "include") { + $new_config .= + "// make sure your include is still in the right place\n"; + $comment = "\t" . $comment; + $new_config .= "include \"$rest[0]\";$comment\n\n"; + } elsif ($directive eq "xfrnets" || $directive eq "tcplist") { + if ($comment ne "") { + $comment = "\t$comment"; + } + foreach $elt (@rest) { + push(@transfer_acl, $elt); + push(@transfer_comments, $comment); + } + $have_options = 1; + } elsif ($directive eq "sortlist") { + if ($comment ne "") { + $comment = "\t$comment"; + } + foreach $elt (@rest) { + push(@topology, $elt); + push(@topology_comments, $comment); + } + } elsif ($directive eq "bogusns") { + if ($comment ne "") { + $comment = "\t$comment"; + } + foreach $elt (@rest) { + push(@bogus, $elt); + push(@bogus_comments, $comment); + } + } elsif ($directive eq "max-fetch") { + $options{"transfers-in"}=$rest[0]; + $options_comments{"transfers-in"}=$comment; + $have_options = 1; + } else { + $new_config .= "// NOTE: unconverted directive '$directive @rest'\n\n"; + } +} + +print "// generated by named-bootconf.pl\n\n"; +if ($have_options) { + print "options {\n"; + foreach $option (sort(keys(%options))) { + print "\t$option $options{$option};"; + if ($options_comments{$option} ne "") { + print "\t$options_comments{$option}"; + } + print "\n"; + } + if (@transfer_acl > 0) { + print "\tallow-transfer {\n"; + for ($i = 0; $i <= $#transfer_acl; $i++) { + &print_maybe_masked("\t\t", $transfer_acl[$i], + $transfer_comments[$i]); + } + print "\t};\n"; + } + print "\t/* +\t * If there is a firewall between you and nameservers you want +\t * to talk to, you might need to uncomment the query-source +\t * directive below. Previous versions of BIND always asked +\t * questions using port 53, but BIND 8.1 uses an unprivileged +\t * port by default. +\t */ +\t// query-source address * port 53; +"; + + print "};\n\n"; +} +if ($logging ne "") { + print "logging {\n$logging};\n\n"; +} +if (@topology > 0) { + print "// Note: the following will be supported in a future release.\n"; + print "/*\n"; + print "host { any; } {\n\ttopology {\n"; + for ($i = 0; $i <= $#topology; $i++) { + &print_maybe_masked("\t\t", $topology[$i], + $topology_comments[$i]); + } + print "\t};\n};\n"; + print "*/\n"; + print "\n"; +} +if (@bogus > 0) { + for ($i = 0; $i <= $#bogus; $i++) { + print "server $bogus[$i] { bogus yes; };$bogus_comments[$i]\n"; + } + print "\n"; +} +print $new_config; + +exit 0; + +sub maybe_print_comment { + $prefix = shift; + $suffix = shift; + if ($comment ne "") { + $new_config .= sprintf("%s%s%s", $prefix, $comment, $suffix); + } +} + +sub handle_options { + foreach $option (@_) { + if ($option eq "forward-only") { + $options{"forward"}="only"; + $options_comments{"forward"}=$comment; + $have_options = 1; + } elsif ($option eq "no-recursion") { + $options{"recursion"}="no"; + $options_comments{"recursion"}=$comment; + $have_options = 1; + } elsif ($option eq "no-fetch-glue") { + $options{"fetch-glue"}="no"; + $options_comments{"fetch-glue"}=$comment; + $have_options = 1; + } elsif ($option eq "fake-iquery") { + $options{"fake-iquery"}="yes"; + $options_comments{"fake-iquery"}=$comment; + $have_options = 1; + } elsif ($option eq "query-log") { + if ($comment ne "") { + $logging .= "\t$comment\n"; + } + $logging .= "\tcategory queries { default_syslog; };\n"; + } else { + $options{"// NOTE: unconverted option '$option'"}=""; + $options_comments{"// NOTE: unconverted option '$option'"}= + $comment; + $have_options = 1; + } + } +} + +sub handle_limit { + $limit = shift; + if ($limit eq "datasize" || $limit eq "transfers-in" + || $limit eq "transfers-per-ns" || $limit eq "files") { + $options{$limit}=$_[0]; + $options_comments{$limit}=$comment; + $have_options = 1; + } else { + $options{"// NOTE: unconverted limit '$limit @_'"}=""; + $options_comments{"// NOTE: unconverted limit '$limit @_'"}=$comment; + $have_options = 1; + } +} + +sub print_maybe_masked { + # this assumes a contiguous netmask starting at the MSB + $prefix = shift; + $elt = shift; + $elt_comment = shift; + if ($elt =~ /^(.*)&(.*)$/) { + $address = $1; + $mask = $2; + ($m1,$m2,$m3,$m4) = split(/\./, $mask); + $mask_val = ($m1 << 24) + ($m2 << 16) +($m3 << 8) + $m4; + $zero_bits = 0; + while (($mask_val % 2) == 0) { + $mask_val /= 2; + $zero_bits++; + } + $mask_bits = 32 - $zero_bits; + } else { + $address = $elt; + ($a1,$a2,$a3,$a4) = split(/\./, $address); + if ($a1 < 128) { + $mask_bits = 8; + } elsif ($a1 < 192) { + $mask_bits = 16; + } else { + $mask_bits = 24; + } + } + + print "$prefix$address"; + if ($mask_bits != 32) { + print "/$mask_bits"; + } + print ";$elt_comment\n"; +} diff --git a/contrib/bind/bin/named/named.conf b/contrib/bind/bin/named/named.conf new file mode 100644 index 0000000..8df9bc1 --- /dev/null +++ b/contrib/bind/bin/named/named.conf @@ -0,0 +1,426 @@ +/* + * This is a worthless, nonrunnable example of a named.conf file that has + * every conceivable syntax element in use. We use it to test the parser. + * It could also be used as a conceptual template for users of new features. + */ + +/* + * C-style comments are OK + */ + +// So are C++-style comments + +# So are shell-style comments + +// watch out for ";" -- it's important! + +options { + directory "."; // use current directory + named-xfer "/usr/libexec/named-xfer"; // _PATH_XFER + dump-file "named_dump.db"; // _PATH_DUMPFILE + pid-file "/var/run/named.pid"; // _PATH_PIDFILE + statistics-file "named.stats"; // _PATH_STATS + memstatistics-file "named.memstats"; // _PATH_MEMSTATS + check-names master fail; + check-names slave warn; + check-names response ignore; + host-statistics yes; + deallocate-on-exit no; // Painstakingly deallocate all + // objects when exiting instead of + // letting the OS clean up for us. + // Useful a memory leak is suspected. + // Final statistics are written to the + // memstatistics-file. + datasize default; + stacksize default; + coresize default; + files unlimited; + recursion yes; + fetch-glue yes; + fake-iquery no; + notify yes; // send NOTIFY messages. You can set + // notify on a zone-by-zone + // basis in the "zone" statement + // see (below) + auth-nxdomain yes; // always set AA on NXDOMAIN. + // don't set this to 'no' unless + // you know what you're doing -- older + // servers won't like it. + multiple-cnames no; // if yes, then a name my have more + // than one CNAME RR. This use + // is non-standard and is not + // recommended, but it is available + // because previous releases supported + // it and it was used by large sites + // for load balancing. + allow-query { any; }; + allow-transfer { any; }; + transfers-in 10; // DEFAULT_XFERS_RUNNING, cannot be + // set > than MAX_XFERS_RUNNING (20) + transfers-per-ns 2; // DEFAULT_XFERS_PER_NS + transfers-out 0; // not implemented + max-transfer-time-in 120; // MAX_XFER_TIME; the default number + // of minutes an inbound zone transfer + // may run. May be set on a per-zone + // basis. + /* + * The "transfer-format" option specifies the way outbound zone + * transfers (i.e. from us to them) are formatted. Two values are + * allowed: + * + * one-answer Each RR gets its own DNS message. + * This format is not very efficient, + * but is widely understood. All + * versions of BIND prior to 8.1 generate + * this format for outbound zone + * and require it on inbound transfers. + * + * many-answers As many RRs as will fit are put into + * each DNS message. This format is + * the most efficient, but is only known + * to work with BIND 8. Patches to + * BIND 4.9.5 named-xfer that enable it + * to understand 'many-answers' will be + * available. + * + * If you are going to be doing zone transfers to older servers, you + * shouldn't use 'many-answers'. 'transfer-format' may also be set + * on a host-by-host basis using the 'server' statement (see below). + */ + transfer-format one-answer; + query-source address * port *; + /* + * The "forward" option is only meaningful if you've defined + * forwarders. "first" gives the normal BIND + * forwarding behavior, i.e. ask the forwarders first, and if that + * doesn't work then do the full lookup. You can also say + * "forward only;" which is what used to be specified with + * "slave" or "options forward-only". "only" will never attempt + * a full lookup; only the forwarders will be used. + */ + forward first; + forwarders { }; // default is no forwarders + /* + * Here's a forwarders example that isn't trivial + */ + /* + forwarders { + 1.2.3.4; + 5.6.7.8; + }; + */ + topology { localhost; localnets; }; // prefer local nameservers + /* + * Here's a more complicated topology example; it's commented out + * because only one topology block is allowed. + * + topology { + 10/8; // prefer network 10.0.0.0 + // netmask 255.0.0.0 most + !1.2.3/24; // don't like 1.2.3.0 netmask + // 255.255.255.0 at all + { 1.2/16; 3/8; }; // like 1.2.0.0 netmask 255.255.0.0 + // and 3.0.0.0 netmask 255.0.0.0 + // equally well, but less than 10/8 + }; + */ + + listen-on port 53 { any; }; // listen for queries on port 53 on + // any interface on the system + // (i.e. all interfaces). The + // "port 53" is optional; if you + // don't specify a port, port 53 + // is assumed. + /* + * Multiple listen-on statements are allowed. Here's a more + * complicated example: + */ + /* + listen-on { 5.6.7.8; }; // listen on port 53 on interface + // 5.6.7.8 + listen-on port 1234 { // listen on port 1234 on any + !1.2.3.4; // interface on network 1.2.3 + 1.2.3/24; // netmask 255.255.255.0, except for + }; // interface 1.2.3.4. + */ + + /* + * Interval Timers + */ + cleaning-interval 60; // clean the cache of expired RRs + // every 'cleaning-interval' minutes + interface-interval 60; // scan for new or deleted interfaces + // every 'interface-interval' minutes + statistics-interval 60; // log statistics every + // 'statistics-interval' minutes +}; + +zone "master.demo.zone" { + type master; // what used to be called "primary" + file "master.demo.zone"; + check-names fail; + allow-update { none; }; + allow-transfer { any; }; + allow-query { any; }; + // notify yes; // send NOTIFY messages for this + // zone? The global option is used + // if "notify" is not specified + // here. + also-notify { }; // don't notify any nameservers other + // than those on the NS list for this + // zone +}; + +zone "slave.demo.zone" { + type slave; // what used to be called "secondary" + file "slave.demo.zone"; + masters { + 1.2.3.4; // where to zone transfer from + 5.6.7.8; + }; + transfer-source 10.0.0.53; // fixes multihoming problems + check-names warn; + allow-update { none; }; + allow-transfer { any; }; + allow-query { any; }; + max-transfer-time-in 120; // if not set, global option is used. + also-notify { }; // don't notify any nameservers other + // than those on the NS list for this + // zone +}; + +zone "stub.demo.zone" { + type stub; // stub zones are like slave zones, + // except that only the NS records + // are transferred. + file "stub.demo.zone"; + masters { + 1.2.3.4; // where to zone transfer from + 5.6.7.8; + }; + check-names warn; + allow-update { none; }; + allow-transfer { any; }; + allow-query { any; }; + max-transfer-time-in 120; // if not set, global option is used. +}; + +zone "." { + type hint; // used to be specified w/ "cache" + file "cache.db"; +}; + +acl can_query { !1.2.3/24; any; }; // network 1.2.3.0 mask 255.255.255.0 + // is disallowed; rest are OK +acl can_axfr { 1.2.3.4; can_query; }; // host 1.2.3.4 and any host allowed + // by can_query are OK + +zone "non-default-acl.demo.zone" { + type master; + file "foo"; + allow-query { can_query; }; + allow-transfer { can_axfr; }; + allow-update { + 1.2.3.4; + 5.6.7.8; + }; +}; + +key sample_key { // for TSIG; supported by parser + algorithm hmac-md5; // but not yet implemented in the + secret "your secret here"; // rest of the server +}; + +key key2 { + algorithm hmac-md5; + secret "ereh terces rouy"; +}; + +server 1.2.3.4 { + bogus no; // if yes, we won't query or listen + // to this server + transfer-format one-answer; // set transfer format for this + // server (see the description of + // 'transfer-format' above) + // if not specified, the global option + // will be used + transfers 0; // not implemented + keys { sample_key; key2; }; // for TSIG; supported by the parser + // but not yet implemented in the + // rest of the server +}; + +logging { + /* + * All log output goes to one or more "channels"; you can make as + * many of them as you want. + */ + + channel syslog_errors { // this channel will send errors or + syslog user; // or worse to syslog (user facility) + severity error; + }; + + /* + * Channels have a severity level. Messages at severity levels + * greater than or equal to the channel's level will be logged on + * the channel. In order of decreasing severity, the levels are: + * + * critical a fatal error + * error + * warning + * notice a normal, but significant event + * info an informational message + * debug 1 the least detailed debugging info + * ... + * debug 99 the most detailed debugging info + */ + + /* + * Here are the built-in channels: + * + * channel default_syslog { + * syslog daemon; + * severity info; + * }; + * + * channel default_debug { + * file "named.run"; + * severity dynamic; // this means log debugging + * // at whatever debugging level + * // the server is at, and don't + * // log anything if not + * // debugging + * }; + * + * channel null { // this is the bit bucket; + * file "/dev/null"; // any logging to this channel + * // is discarded. + * }; + * + * channel default_stderr { // writes to stderr + * file "<stderr>"; // this is illustrative only; + * // there's currently no way + * // of saying "stderr" in the + * // configuration language. + * // i.e. don't try this at home. + * severity info; + * }; + * + * default_stderr only works before the server daemonizes (i.e. + * during initial startup) or when it is running in foreground + * mode (-f command line option). + */ + + /* + * There are many categories, so you can send the logs + * you want to see wherever you want, without seeing logs you + * don't want. Right now the categories are + * + * default the catch-all. many things still + * aren't classified into categories, and + * they all end up here. also, if you + * don't specify any channels for a + * category, the default category is used + * instead. + * config high-level configuration file + * processing + * parser low-level configuration file processing + * queries what used to be called "query logging" + * lame-servers messages like "Lame server on ..." + * statistics + * panic if the server has to shut itself + * down due to an internal problem, it + * logs the problem here (as well as + * in the problem's native category) + * update dynamic update + * ncache negative caching + * xfer-in zone transfers we're receiving + * xfer-out zone transfers we're sending + * db all database operations + * eventlib debugging info from the event system + * (see below) + * packet dumps of packets received and sent + * (see below) + * notify the NOTIFY protocol + * cname messages like "XX points to a CNAME" + * security approved/unapproved requests + * os operating system problems + * insist consistency check failures + * maintenance periodic maintenance + * load zone loading + * response-checks messages like + * "Malformed response ..." + * "wrong ans. name ..." + * "unrelated additional info ..." + * "invalid RR type ..." + * "bad referral ..." + */ + + category parser { + syslog_errors; // you can log to as many channels + default_syslog; // as you want + }; + + category lame-servers { null; }; // don't log these at all + + channel moderate_debug { + severity debug 3; // level 3 debugging to file + file "foo"; // foo + print-time yes; // timestamp log entries + print-category yes; // print category name + print-severity yes; // print severity level + /* + * Note that debugging must have been turned on either + * on the command line or with a signal to get debugging + * output (non-debugging output will still be written to + * this channel). + */ + }; + + /* + * If you don't want to see "zone XXXX loaded" messages but do + * want to see any problems, you could do the following. + */ + channel no_info_messages { + syslog; + severity notice; + }; + + category load { no_info_messages; }; + + /* + * You can also define category "default"; it gets used when no + * "category" statement has been given for a category. + */ + category default { + default_syslog; + moderate_debug; + }; + + /* + * If you don't define category default yourself, the default + * default category will be used. It is + * + * category default { default_syslog; default_debug; }; + */ + + /* + * If you don't define category panic yourself, the default + * panic category will be used. It is + * + * category panic { default_syslog; default_stderr; }; + */ + + /* + * Two categories, 'packet' and 'eventlib', are special. Only one + * channel may be assigned to each of them, and it must be a + * file channel. If you don't define them yourself, they default to + * + * category eventlib { default_debug; }; + * + * category packet { default_debug; }; + */ +}; + +include "filename"; // can't do within a statement diff --git a/contrib/bind/bin/named/named.h b/contrib/bind/bin/named/named.h new file mode 100644 index 0000000..57e787b --- /dev/null +++ b/contrib/bind/bin/named/named.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * $Id: named.h,v 8.12 1997/12/04 06:52:27 halley Exp $ + */ + +/* Options. Leave these on. */ +#define DEBUG +#define ADDAUTH +#define STUBS +#define RETURNSOA +#define BOGUSNS +#define TRACEROOT +#define XFRNETS +#define QRYLOG +#define YPKLUDGE +#define RENICE +#define FORCED_RELOAD +#define SLAVE_FORWARD +#define BIND_UPDATE +#define BIND_NOTIFY +#define WANT_PIDFILE +#define FWD_LOOP +#define DOTTED_SERIAL +#define SENSIBLE_DOTS +#define ROUND_ROBIN +#define SORT_RESPONSE +#define DNS_SECURITY +#undef RSAREF +#undef BSAFE +#define ALLOW_LONG_TXT_RDATA + +#if 0 +#define strdup PLEASE_USE_SAVESTR +#define malloc PLEASE_USE_DB_MEMGET +#define calloc PLEASE_USE_DB_MEMGET +#define realloc PLEASE_USE_DB_MEMGET +#define free PLEASE_USE_DB_MEMPUT +#endif + +#include <isc/assertions.h> +#include <isc/list.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/contrib/bind/bin/named/ns_config.c b/contrib/bind/bin/named/ns_config.c new file mode 100644 index 0000000..ff26156 --- /dev/null +++ b/contrib/bind/bin/named/ns_config.c @@ -0,0 +1,2366 @@ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_config.c,v 8.34 1998/03/27 00:19:47 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#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 <ctype.h> +#include <errno.h> +#include <limits.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> +#include <fcntl.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#ifdef HAVE_GETRUSAGE /* XXX */ +#include <sys/resource.h> +#endif + +#include "named.h" +#include "ns_parseutil.h" + +static int tmpnum = 0; +static int config_initialized = 0; + +static int need_logging_free = 0; +static int default_logging_installed; + +static int options_installed = 0; +static int logging_installed = 0; +static int default_options_installed; +static int initial_configuration = 1; + +static char **logging_categories; +static char *current_pid_filename = NULL; + +#define ZONE_SYM_TABLE_SIZE 4973 +static symbol_table zone_symbol_table; + +/* Zones */ + +void +free_zone_timerinfo(struct zoneinfo *zp) { + if (zp->z_timerinfo != NULL) { + freestr(zp->z_timerinfo->name); + memput(zp->z_timerinfo, sizeof *zp->z_timerinfo); + zp->z_timerinfo = NULL; + } else + ns_error(ns_log_config, "timer for zone '%s' had no timerinfo", + zp->z_origin); +} + +void +free_zone_contents(struct zoneinfo *zp, int undefine_sym) { + INSIST(zp != NULL); + + if (undefine_sym) + undefine_symbol(zone_symbol_table, zp->z_origin, + (zp->z_type << 16) | zp->z_class); + if (zp->z_flags & Z_TIMER_SET) { + free_zone_timerinfo(zp); + if (evClearTimer(ev, zp->z_timer) < 0) + ns_error(ns_log_config, + "evClearTimer for zone '%s' failed in ns_init: %s", + zp->z_origin, + strerror(errno)); + } + if (zp->z_origin != NULL) + freestr(zp->z_origin); + if (zp->z_source != NULL) + freestr(zp->z_source); + if (zp->z_update_acl != NULL) + free_ip_match_list(zp->z_update_acl); + if (zp->z_query_acl != NULL) + free_ip_match_list(zp->z_query_acl); + if (zp->z_transfer_acl != NULL) + free_ip_match_list(zp->z_transfer_acl); +#ifdef BIND_UPDATE + if (zp->z_updatelog != NULL) + freestr(zp->z_updatelog); +#endif /* BIND_UPDATE */ +} + +static void +free_zone(struct zoneinfo *zp) { + INSIST(zp != NULL); + + free_zone_contents(zp, 0); + memput(zp, sizeof *zp); +} + +struct zoneinfo * +find_zone(const char *name, int type, int class) { + struct zoneinfo *zp; + symbol_value value; + + ns_debug(ns_log_config, 3, "find_zone(%s,%d,%d)", name, type, class); + if (lookup_symbol(zone_symbol_table, name, + (type<<16) | class, &value)) { + INSIST(value.integer >= 0 && value.integer < nzones); + ns_debug(ns_log_config, 3, + "find_zone: existing zone %d", value.integer); + zp = &zones[value.integer]; + return (zp); + } + ns_debug(ns_log_config, 3, "find_zone: unknown zone"); + return (NULL); +} + +static struct zoneinfo * +new_zone(int class, int type) { + struct zoneinfo *zp; + + if (zones != NULL) { + if (type == z_hint) { + zp = &zones[0]; + return (zp); + } + + for (zp = &zones[1]; zp < &zones[nzones]; zp++) + if (zp->z_type == z_nil) + return (zp); + } + + /* + * This code assumes that nzones never decreases. + */ + if (nzones % 64 == 0) { + ns_debug(ns_log_config, 1, "Reallocating zones structure"); + zp = (struct zoneinfo *) + memget((64 + nzones) * sizeof(struct zoneinfo)); + if (zp == NULL) + panic("no memory for more zones", NULL); + memcpy(zp, zones, nzones * sizeof(struct zoneinfo)); + memset(&zp[nzones], 0, 64 * sizeof(struct zoneinfo)); + memput(zones, nzones * sizeof(struct zoneinfo)); + zones = zp; + } + zp = &zones[nzones++]; + + return (zp); +} + +/* + * Check out a zoneinfo structure and return non-zero if it's OK. + */ +static int +validate_zone(struct zoneinfo *zp) { + int warnings = 0; + char filename[MAXPATHLEN+1]; + + /* Check name */ + if (!res_dnok(zp->z_origin)) { + ns_error(ns_log_config, "invalid zone name '%s'", + zp->z_origin); + return (0); + } + + /* Check class */ + if (zp->z_class == C_ANY || zp->z_class == C_NONE) { + ns_error(ns_log_config, "invalid class %d for zone '%s'", + zp->z_class, zp->z_origin); + return (0); + } + + /* Check type. */ + if (zp->z_type == 0) { + ns_error(ns_log_config, "no type specified for zone '%s'", + zp->z_origin); + return (0); + } + if (zp->z_type == z_hint && strcasecmp(zp->z_origin, "") != 0) { + ns_error(ns_log_config, + "only the root zone may be a hint zone (zone '%s')", + zp->z_origin); + return (0); + } + + /* Check filename. */ + if (zp->z_type == z_master && zp->z_source == NULL) { + ns_error(ns_log_config, + "'file' statement missing for master zone %s", + zp->z_origin); + return (0); + } + /* + * XXX We should run filename through an OS-specific + * validator here. + */ + if (zp->z_source != NULL && + strlen(zp->z_source) > MAXPATHLEN) { + ns_error(ns_log_config, "filename too long for zone '%s'", + zp->z_origin); + return (0); + } + + /* Check masters */ + if (zp->z_addrcnt != 0) { + if (zp->z_type != z_slave && zp->z_type != z_stub) { + ns_error(ns_log_config, + "'masters' statement present for %s zone '%s'", + (zp->z_type == z_master) ? "master" : "hint", + zp->z_origin); + return (0); + } + } else { + if (zp->z_type == z_slave || zp->z_type == z_stub) { + ns_error(ns_log_config, + "no 'masters' statement for non-master zone '%s'", + zp->z_origin); + return (0); + } + } + + /* Check also-notify */ + if (zp->z_notify_count != 0) { + if (zp->z_type != z_master && zp->z_type != z_slave) { + ns_error(ns_log_config, + "'also-notify' given for non-master, non-slave zone '%s'", + zp->z_origin); + return (0); + } + } + +#ifdef BIND_UPDATE + /* XXX need more checking here */ + if (zp->z_type == z_master) { + if (!zp->z_soaincrintvl) + zp->z_soaincrintvl = SOAINCRINTVL; + if (!zp->z_dumpintvl) + zp->z_dumpintvl = DUMPINTVL; + if (!zp->z_deferupdcnt) + zp->z_deferupdcnt = DEFERUPDCNT; + if (!zp->z_updatelog) { + /* XXX OS-specific filename validation here */ + if ((strlen(zp->z_source) + (sizeof ".log" - 1)) > + MAXPATHLEN) { + ns_error(ns_log_config, + "filename too long for dynamic zone '%s'", + zp->z_origin); + return (0); + } + /* this sprintf() is now safe */ + sprintf(filename, "%s.log", zp->z_source); + zp->z_updatelog = savestr(filename, 1); + } + } +#endif /* BIND_UPDATE */ + + return (1); +} + +/* + * Start building a new zoneinfo structure. Returns an opaque + * zone_config suitable for use by the parser. + */ +zone_config +begin_zone(char *name, int class) { + zone_config zh; + struct zoneinfo *zp; + + /* + * require: name is canonical, class is a valid class + */ + + ns_debug(ns_log_config, 3, "begin_zone('%s', %d)", + (*name == '\0') ? "." : name, class); + + zp = (struct zoneinfo *)memget(sizeof (struct zoneinfo)); + if (zp == NULL) + panic("memget failed in begin_zone", NULL); + memset(zp, 0, sizeof (struct zoneinfo)); + zp->z_origin = name; + zp->z_class = class; + zp->z_checknames = not_set; + zh.opaque = zp; + return (zh); +} + +/* + * Merge new configuration information into an existing zone. The + * new zoneinfo must be valid. + */ +static void +update_zone_info(struct zoneinfo *zp, struct zoneinfo *new_zp) { + struct stat f_time; + char buf[MAXPATHLEN+1]; + int i; + + INSIST(zp != NULL); + INSIST(new_zp != NULL); + + ns_debug(ns_log_config, 1, "update_zone_info('%s', %d)", + (*new_zp->z_origin == '\0') ? "." : new_zp->z_origin, + new_zp->z_type); + +#ifdef BIND_UPDATE + /* + * A dynamic master zone that's becoming non-dynamic may need to be + * dumped before we start the update. + */ + if ((zp->z_flags & Z_DYNAMIC) && !(new_zp->z_flags & Z_DYNAMIC) && + ((zp->z_flags & Z_NEED_SOAUPDATE) || + (zp->z_flags & Z_NEED_DUMP))) + (void)zonedump(zp); +#endif + + /* + * First do the simple stuff, making sure to free + * any data that was dynamically allocated. + */ + if (zp->z_origin != NULL) + freestr(zp->z_origin); + zp->z_origin = new_zp->z_origin; + new_zp->z_origin = NULL; + zp->z_class = new_zp->z_class; + zp->z_type = new_zp->z_type; + zp->z_checknames = new_zp->z_checknames; + for (i = 0; i < new_zp->z_addrcnt; i++) + zp->z_addr[i] = new_zp->z_addr[i]; + zp->z_addrcnt = new_zp->z_addrcnt; + if (zp->z_update_acl) + free_ip_match_list(zp->z_update_acl); + zp->z_update_acl = new_zp->z_update_acl; + new_zp->z_update_acl = NULL; + if (zp->z_query_acl) + free_ip_match_list(zp->z_query_acl); + zp->z_query_acl = new_zp->z_query_acl; + new_zp->z_query_acl = NULL; + zp->z_axfr_src = new_zp->z_axfr_src; + if (zp->z_transfer_acl) + free_ip_match_list(zp->z_transfer_acl); + zp->z_transfer_acl = new_zp->z_transfer_acl; + new_zp->z_transfer_acl = NULL; + zp->z_max_transfer_time_in = new_zp->z_max_transfer_time_in; + + zp->z_notify = new_zp->z_notify; + for (i = 0; i < new_zp->z_notify_count; i++) + zp->z_also_notify[i] = new_zp->z_also_notify[i]; + zp->z_notify_count = new_zp->z_notify_count; + +#ifdef BIND_UPDATE + if (new_zp->z_flags & Z_DYNAMIC) + zp->z_flags |= Z_DYNAMIC; + else + zp->z_flags &= ~Z_DYNAMIC; + zp->z_soaincrintvl = new_zp->z_soaincrintvl; + zp->z_dumpintvl = new_zp->z_dumpintvl; + zp->z_deferupdcnt = new_zp->z_deferupdcnt; + if (zp->z_updatelog) + freestr(zp->z_updatelog); + zp->z_updatelog = new_zp->z_updatelog; + new_zp->z_updatelog = NULL; +#endif /* BIND_UPDATE */ + + /* + * now deal with files + */ + switch (zp->z_type) { + case z_hint: + ns_debug(ns_log_config, 1, "source = %s", new_zp->z_source); + zp->z_refresh = 0; /* No dumping. */ + /* + * 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(new_zp->z_source, zp->z_source) && + !(zp->z_flags & Z_INCLUDE) && + stat(zp->z_source, &f_time) != -1 && + zp->z_ftime == f_time.st_mtime) { + ns_debug(ns_log_config, 1, "cache is up to date"); + break; + } + + /* File has changed, or hasn't been loaded yet. */ + if (zp->z_source) { + freestr(zp->z_source); + clean_cache(fcachetab, 1); + } + zp->z_source = new_zp->z_source; + new_zp->z_source = NULL; + ns_debug(ns_log_config, 1, "reloading zone"); + (void) db_load(zp->z_source, zp->z_origin, zp, NULL); + break; + + case z_master: + ns_debug(ns_log_config, 1, "source = %s", new_zp->z_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(new_zp->z_source, zp->z_source) && + !(zp->z_flags & Z_INCLUDE) && + stat(zp->z_source, &f_time) != -1 && + zp->z_ftime == f_time.st_mtime) { + ns_debug(ns_log_config, 1, "zone is up to date"); + break; /* zone is already up to date */ + } +#ifdef BIND_UPDATE + if (zp->z_source && (zp->z_flags & Z_DYNAMIC)) + ns_warning(ns_log_config, + "source file of dynamic zone '%s' has changed", + zp->z_origin); + + primary_reload: +#endif /* BIND_UPDATE */ + if (zp->z_source != NULL) + freestr(zp->z_source); + zp->z_source = new_zp->z_source; + new_zp->z_source = NULL; + zp->z_flags &= ~Z_AUTH; + ns_stopxfrs(zp); + purge_zone(zp->z_origin, hashtab, zp->z_class); + ns_debug(ns_log_config, 1, "reloading zone"); +#ifdef BIND_UPDATE + if (zp->z_flags & Z_DYNAMIC) { + struct stat sb; + + if (stat(zp->z_source, &sb) < 0) + ns_error(ns_log_config, "stat(%s) failed: %s", + zp->z_source, strerror(errno)); + else { + if (sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) + ns_warning(ns_log_config, + "dynamic zone file '%s' is writable", + zp->z_source); + } + } +#endif + if (!db_load(zp->z_source, zp->z_origin, zp, NULL)) + zp->z_flags |= Z_AUTH; + zp->z_refresh = 0; /* no maintenance needed */ + zp->z_time = 0; +#ifdef BIND_UPDATE + zp->z_lastupdate = 0; + if (zp->z_flags & Z_DYNAMIC) + /* + * Note that going to primary_reload + * unconditionally reloads the zone. + */ + if (merge_logs(zp) == 1) { + new_zp->z_source = savestr(zp->z_source, 1); + goto primary_reload; + } +#endif + break; + + case z_slave: +#ifdef STUBS + case z_stub: +#endif + ns_debug(ns_log_config, 1, "addrcnt = %d", zp->z_addrcnt); + if (!new_zp->z_source) { + /* + * We will always transfer this zone again + * after a reload. + */ + sprintf(buf, "NsTmp%ld.%d", (long)getpid(), tmpnum++); + new_zp->z_source = savestr(buf, 1); + 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(new_zp->z_source, zp->z_source) || + (stat(zp->z_source, &f_time) == -1 || + (zp->z_ftime != f_time.st_mtime)))) { + ns_debug(ns_log_config, 1, + "backup file changed or missing"); + freestr(zp->z_source); + zp->z_source = NULL; + zp->z_serial = 0; /* force xfer */ + ns_stopxfrs(zp); + /* + * We only need to do_reload if we have + * successfully transferred the zone. + */ + if (zp->z_flags & Z_AUTH) { + zp->z_flags &= ~Z_AUTH; + /* + * Purge old data and reload parent so that + * NS records are present during the zone + * transfer. + */ + do_reload(zp->z_origin, zp->z_type, + zp->z_class); + } + } + if (zp->z_source == NULL) { + zp->z_source = new_zp->z_source; + new_zp->z_source = NULL; + } + if (!(zp->z_flags & Z_AUTH)) + zoneinit(zp); +#ifdef FORCED_RELOAD + else { + /* + ** Force secondary to try transfer soon + ** after SIGHUP. + */ + if (!(zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) + && reloading) { + qserial_retrytime(zp, tt.tv_sec); + sched_zone_maint(zp); + } + } +#endif /* FORCED_RELOAD */ + break; + } + if ((zp->z_flags & Z_FOUND) && /* already found? */ + (zp - zones) != DB_Z_CACHE) /* cache never sets Z_FOUND */ + ns_error(ns_log_config, "Zone \"%s\" declared more than once", + zp->z_origin); + zp->z_flags |= Z_FOUND; + ns_debug(ns_log_config, 1, + "zone[%d] type %d: '%s' z_time %lu, z_refresh %u", + zp-zones, zp->z_type, + *(zp->z_origin) == '\0' ? "." : zp->z_origin, + (u_long)zp->z_time, zp->z_refresh); +} + +/* + * Finish constructing a new zone. If valid, the constructed zone is + * merged into the zone database. The zone_config used is invalid after + * end_zone() completes. + */ +void +end_zone(zone_config zh, int should_install) { + struct zoneinfo *zp, *new_zp; + char *zname; + symbol_value value; + + new_zp = zh.opaque; + INSIST(new_zp != NULL); + + zname = (new_zp->z_origin[0] == '\0') ? "." : new_zp->z_origin; + ns_debug(ns_log_config, 3, "end_zone('%s', %d)", zname, + should_install); + + if (!should_install) { + free_zone(new_zp); + return; + } + if (!validate_zone(new_zp)) { + ns_error(ns_log_config, + "zone '%s' did not validate, skipping", zname); + free_zone(new_zp); + return; + } + zp = find_zone(new_zp->z_origin, new_zp->z_type, new_zp->z_class); + if (zp == NULL) { + zp = new_zone(new_zp->z_class, new_zp->z_type); + INSIST(zp != NULL); + value.integer = (zp - zones); + define_symbol(zone_symbol_table, savestr(new_zp->z_origin, 1), + (new_zp->z_type << 16) | new_zp->z_class, + value, SYMBOL_FREE_KEY); + } + ns_debug(ns_log_config, 5, "zone '%s', type = %d, class = %d", zname, + new_zp->z_type, new_zp->z_class); + if (new_zp->z_source != NULL) + ns_debug(ns_log_config, 5, " file = %s", new_zp->z_source); + ns_debug(ns_log_config, 5, " checknames = %d", new_zp->z_checknames); + if (new_zp->z_addrcnt) { + int i; + + ns_debug(ns_log_config, 5, " masters:"); + for (i=0; i<new_zp->z_addrcnt; i++) + ns_debug(ns_log_config, 5, " %s", + inet_ntoa(new_zp->z_addr[i])); + } + + update_zone_info(zp, new_zp); + free_zone(new_zp); + zh.opaque = NULL; +} + +int +set_zone_type(zone_config zh, int type) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + /* Fail if type already set for this zone */ + if (zp->z_type != 0) + return (0); + zp->z_type = type; + return (1); +} + +int +set_zone_filename(zone_config zh, char *filename) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + /* Fail if filename already set for this zone */ + if (zp->z_source != NULL) + return (0); + zp->z_source = filename; + return (1); +} + +int +set_zone_checknames(zone_config zh, enum severity s) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + /* Fail if checknames already set for this zone */ + if (zp->z_checknames != not_set) + return (0); + zp->z_checknames = s; + return (1); +} + +int +set_zone_notify(zone_config zh, int value) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + if (value) + zp->z_notify = znotify_yes; + else + zp->z_notify = znotify_no; + + return (1); +} + +int +set_zone_update_acl(zone_config zh, ip_match_list iml) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + /* Fail if checknames already set for this zone */ + if (zp->z_update_acl != NULL) + return (0); + zp->z_update_acl = iml; +#ifdef BIND_UPDATE + if (!ip_match_is_none(iml)) + zp->z_flags |= Z_DYNAMIC; + else + ns_debug(ns_log_config, 3, "update acl is none for '%s'", + zp->z_origin); +#endif + return (1); +} + +int +set_zone_query_acl(zone_config zh, ip_match_list iml) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + /* Fail if checknames already set for this zone */ + if (zp->z_query_acl != NULL) + return (0); + zp->z_query_acl = iml; + return (1); +} + +int +set_zone_transfer_source(zone_config zh, struct in_addr ina) { + struct zoneinfo *zp = zh.opaque; + + zp->z_axfr_src = ina; + return (1); +} + +int +set_zone_transfer_acl(zone_config zh, ip_match_list iml) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + /* Fail if checknames already set for this zone */ + if (zp->z_transfer_acl != NULL) + return (0); + zp->z_transfer_acl = iml; + return (1); +} + +int +set_zone_transfer_time_in(zone_config zh, long max_time) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + /* Fail if checknames already set for this zone */ + if (zp->z_max_transfer_time_in) + return (0); + zp->z_max_transfer_time_in = max_time; + return (1); +} + +int +add_zone_master(zone_config zh, struct in_addr address) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + zp->z_addr[zp->z_addrcnt] = address; + zp->z_addrcnt++; + if (zp->z_addrcnt >= NSMAX) { + ns_warning(ns_log_config, "NSMAX reached for zone '%s'", + zp->z_origin); + zp->z_addrcnt = NSMAX - 1; + } + return (1); +} + +int +add_zone_notify(zone_config zh, struct in_addr address) { + struct zoneinfo *zp; + + zp = zh.opaque; + INSIST(zp != NULL); + + zp->z_also_notify[zp->z_notify_count] = address; + zp->z_notify_count++; + if (zp->z_notify_count >= NSMAX) { + ns_warning(ns_log_config, "also-notify set full for zone '%s'", + zp->z_origin); + zp->z_notify_count = NSMAX - 1; + } + return (1); +} + +/* Options */ + +options +new_options() { + options op; + ip_match_list iml; + ip_match_element ime; + + op = (options)memget(sizeof (struct options)); + if (op == NULL) + panic("memget failed in new_options()", NULL); + + op->directory = savestr(".", 1); + op->pid_filename = savestr(_PATH_PIDFILE, 1); + op->named_xfer = savestr(_PATH_XFER, 1); + op->dump_filename = savestr(_PATH_DUMPFILE, 1); + op->stats_filename = savestr(_PATH_STATS, 1); + op->memstats_filename = savestr(_PATH_MEMSTATS, 1); + op->flags = DEFAULT_OPTION_FLAGS; + op->transfers_in = DEFAULT_XFERS_RUNNING; + op->transfers_per_ns = DEFAULT_XFERS_PER_NS; + op->transfers_out = 0; + op->transfer_format = axfr_one_answer; + op->max_transfer_time_in = MAX_XFER_TIME; + memset(&op->query_source, 0, sizeof op->query_source); + op->query_source.sin_family = AF_INET; + op->query_source.sin_addr.s_addr = htonl(INADDR_ANY); + op->query_source.sin_port = htons(0); /* INPORT_ANY */ + op->query_acl = NULL; + op->transfer_acl = NULL; + /* default topology is { localhost; localnets; } */ + iml = new_ip_match_list(); + ime = new_ip_match_localhost(); + add_to_ip_match_list(iml, ime); + ime = new_ip_match_localnets(); + add_to_ip_match_list(iml, ime); + op->topology = iml; + op->data_size = 0UL; /* use system default */ + op->stack_size = 0UL; /* use system default */ + op->core_size = 0UL; /* use system default */ + op->files = ULONG_MAX; /* unlimited */ + op->check_names[primary_trans] = fail; + op->check_names[secondary_trans] = warn; + op->check_names[response_trans] = ignore; + op->listen_list = NULL; + op->fwdtab = NULL; + /* XXX init forwarding */ + op->clean_interval = 3600; + op->interface_interval = 3600; + op->stats_interval = 3600; + return (op); +} + +void +free_options(options op) { + INSIST(op != NULL); + + if (op->directory) + freestr(op->directory); + if (op->pid_filename) + freestr(op->pid_filename); + if (op->named_xfer) + freestr(op->named_xfer); + if (op->dump_filename) + freestr(op->dump_filename); + if (op->stats_filename) + freestr(op->stats_filename); + if (op->memstats_filename) + freestr(op->memstats_filename); + if (op->query_acl) + free_ip_match_list(op->query_acl); + if (op->transfer_acl) + free_ip_match_list(op->transfer_acl); + if (op->topology) + free_ip_match_list(op->topology); + if (op->listen_list) + free_listen_info_list(op->listen_list); + if (op->fwdtab) + free_forwarders(op->fwdtab); + memput(op, sizeof *op); +} + +void +set_boolean_option(options op, int bool_opt, int value) { + INSIST(op != NULL); + + switch (bool_opt) { + case OPTION_NORECURSE: + case OPTION_NOFETCHGLUE: + case OPTION_FORWARD_ONLY: + case OPTION_FAKE_IQUERY: + case OPTION_NONOTIFY: + case OPTION_NONAUTH_NXDOMAIN: + case OPTION_MULTIPLE_CNAMES: + case OPTION_HOSTSTATS: + case OPTION_DEALLOC_ON_EXIT: + if (value) + op->flags |= bool_opt; + else + op->flags &= ~bool_opt; + break; + default: + panic("unexpected option in set_boolean_option", NULL); + } +} + +#ifdef HAVE_GETRUSAGE +enum limit { Datasize, Stacksize, Coresize, Files }; + +static struct rlimit initial_data_size; +static struct rlimit initial_stack_size; +static struct rlimit initial_core_size; +static struct rlimit initial_num_files; + +static void +get_initial_limits() { +# ifdef RLIMIT_DATA + if (getrlimit(RLIMIT_DATA, &initial_data_size) < 0) + ns_warning(ns_log_config, "getrlimit(DATA): %s", + strerror(errno)); +# endif +# ifdef RLIMIT_STACK + if (getrlimit(RLIMIT_STACK, &initial_stack_size) < 0) + ns_warning(ns_log_config, "getrlimit(STACK): %s", + strerror(errno)); +# endif +# ifdef RLIMIT_CORE + if (getrlimit(RLIMIT_CORE, &initial_core_size) < 0) + ns_warning(ns_log_config, "getrlimit(CORE): %s", + strerror(errno)); +# endif +# ifdef RLIMIT_NOFILE + if (getrlimit(RLIMIT_NOFILE, &initial_num_files) < 0) + ns_warning(ns_log_config, "getrlimit(NOFILE): %s", + strerror(errno)); +# endif +} + +static void +ns_rlimit(enum limit limit, u_long limit_value) { + struct rlimit limits, old_limits; + int rlimit = -1; + char *name; + rlimit_type value; + + if (limit_value == ULONG_MAX) { +#ifndef RLIMIT_FILE_INFINITY + if (limit == Files) + value = MAX((rlimit_type)sysconf(_SC_OPEN_MAX), + initial_num_files.rlim_max); + else +#endif + value = (rlimit_type)RLIM_INFINITY; + } else + value = (rlimit_type)limit_value; + + limits.rlim_cur = limits.rlim_max = value; + switch (limit) { + case Datasize: +#ifdef RLIMIT_DATA + rlimit = RLIMIT_DATA; +#endif + name = "max data size"; + if (value == 0) + limits = initial_data_size; + break; + case Stacksize: +#ifdef RLIMIT_STACK + rlimit = RLIMIT_STACK; +#endif + name = "max stack size"; + if (value == 0) + limits = initial_stack_size; + break; + case Coresize: +#ifdef RLIMIT_CORE + rlimit = RLIMIT_CORE; +#endif + name = "max core size"; + if (value == 0) + limits = initial_core_size; + break; + case Files: +#ifdef RLIMIT_NOFILE + rlimit = RLIMIT_NOFILE; +#endif + name = "max number of open files"; + if (value == 0) + limits = initial_num_files; + /* XXX check < FD_SETSIZE? */ + break; + default: + name = NULL; /* Make gcc happy. */ + panic("impossible condition in ns_rlimit()", NULL); + } + if (rlimit == -1) { + ns_warning(ns_log_config, + "limit \"%s\" not supported on this system - ignored", + name); + return; + } + if (getrlimit(rlimit, &old_limits) < 0) { + ns_warning(ns_log_config, "getrlimit(%s): %s", name, + strerror(errno)); + } + if (user_id != 0 && limits.rlim_max == RLIM_INFINITY) + limits.rlim_cur = limits.rlim_max = old_limits.rlim_max; + if (setrlimit(rlimit, &limits) < 0) { + ns_warning(ns_log_config, "setrlimit(%s): %s", name, + strerror(errno)); + return; + } else { + if (value == 0) + ns_debug(ns_log_config, 3, "%s is default", + name); + else if (value == RLIM_INFINITY) + ns_debug(ns_log_config, 3, "%s is unlimited", name); + else +#ifdef RLIMIT_LONGLONG + ns_debug(ns_log_config, 3, "%s is %llu", name, + (unsigned long long)value); +#else + ns_debug(ns_log_config, 3, "%s is %lu", name, value); +#endif + } +} +#endif /* HAVE_GETRUSAGE */ + +listen_info_list +new_listen_info_list() { + listen_info_list ll; + + ll = (listen_info_list)memget(sizeof (struct listen_info_list)); + if (ll == NULL) + panic("memget failed in new_listen_info_list()", NULL); + ll->first = NULL; + ll->last = NULL; + return (ll); +} + +void +free_listen_info_list(listen_info_list ll) { + listen_info li, next_li; + + INSIST(ll != NULL); + for (li = ll->first; li != NULL; li = next_li) { + next_li = li->next; + free_ip_match_list(li->list); + memput(li, sizeof *li); + } + memput(ll, sizeof *ll); +} + +void +add_listen_on(options op, u_short port, ip_match_list iml) { + listen_info_list ll; + listen_info ni; + + INSIST(op != NULL); + + if (op->listen_list == NULL) + op->listen_list = new_listen_info_list(); + ll = op->listen_list; + ni = (listen_info)memget(sizeof (struct listen_info)); + if (ni == NULL) + panic("memget failed in add_listen_on", NULL); + ni->port = port; + ni->list = iml; + ni->next = NULL; + if (ll->last != NULL) + ll->last->next = ni; + ll->last = ni; + if (ll->first == NULL) + ll->first = ni; +} + +FILE * +write_open(char *filename) { + FILE *stream; + int fd; + struct stat sb; + int regular; + + if (stat(filename, &sb) < 0) { + if (errno != ENOENT) { + ns_error(ns_log_os, + "write_open: stat of %s failed: %s", + filename, strerror(errno)); + return (NULL); + } + regular = 1; + } else + regular = (sb.st_mode & S_IFREG); + + if (!regular) { + ns_error(ns_log_os, "write_open: %s isn't a regular file", + filename); + return (NULL); + } + + (void)unlink(filename); + fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + if (fd < 0) + return (NULL); + stream = fdopen(fd, "w"); + if (stream == NULL) + (void)close(fd); + return (stream); +} + +void +update_pid_file() { + FILE *fp; + + REQUIRE(server_options != NULL); + REQUIRE(server_options->pid_filename != NULL); + + /* XXX */ ns_debug(ns_log_default, 1, "update_pid_file()"); + if (current_pid_filename != NULL) { + (void)unlink(current_pid_filename); + freestr(current_pid_filename); + current_pid_filename = NULL; + } + current_pid_filename = savestr(server_options->pid_filename, 0); + if (current_pid_filename == NULL) { + ns_error(ns_log_config, + "savestr() failed in update_pid_file()"); + return; + } + fp = write_open(current_pid_filename); + if (fp != NULL) { + (void) fprintf(fp, "%ld\n", (long)getpid()); + (void) fclose(fp); + } else + ns_error(ns_log_config, "couldn't create pid file '%s'", + server_options->pid_filename); +} + +/* + * XXX This function will eventually be public and will be relocated to + * the UNIX OS support library. + */ + +static int +os_change_directory(const char *name) { + struct stat sb; + + if (name == NULL || + *name == '\0') { + errno = EINVAL; + return (0); + } + + if (chdir(name) < 0) + return (0); + + if (stat(name, &sb) < 0) { + ns_error(ns_log_os, "stat(%s) failed: %s", name, + strerror(errno)); + return (1); + } + if (sb.st_mode & S_IWOTH) + ns_warning(ns_log_os, "directory %s is world-writable", name); + + return (1); +} + +static void +periodic_getnetconf(evContext ctx, void *uap, struct timespec due, + struct timespec inter) +{ + getnetconf(1); +} + +static void +set_interval_timer(int which_timer, int interval) { + evTimerID *tid = NULL; + evTimerFunc func = NULL; + + switch (which_timer) { + case CLEAN_TIMER: + tid = &clean_timer; + func = ns_cleancache; + break; + case INTERFACE_TIMER: + tid = &interface_timer; + func = periodic_getnetconf; + break; + case STATS_TIMER: + tid = &stats_timer; + func = ns_logstats; + break; + default: + ns_panic(ns_log_config, 1, + "set_interval_timer: unknown timer %d", which_timer); + } + if ((active_timers & which_timer) != 0) { + if (interval > 0) { + if (evResetTimer(ev, *tid, func, NULL, + evAddTime(evNowTime(), + evConsTime(interval, 0)), + evConsTime(interval, 0)) < 0) + ns_error(ns_log_config, + "evResetTimer %d interval %d failed: %s", + which_timer, interval, + strerror(errno)); + } else { + if (evClearTimer(ev, *tid) < 0) + ns_error(ns_log_config, + "evClearTimer %d failed: %s", + which_timer, strerror(errno)); + else + active_timers &= ~which_timer; + } + } else if (interval > 0) { + if (evSetTimer(ev, func, NULL, + evAddTime(evNowTime(), + evConsTime(interval, 0)), + evConsTime(interval, 0), tid) < 0) + ns_error(ns_log_config, + "evSetTimer %d interval %d failed: %s", + which_timer, interval, strerror(errno)); + else + active_timers |= which_timer; + } +} + +/* + * Set all named global options based on the global options structure + * generated by the parser. + */ +void +set_options(options op, int is_default) { + listen_info li; + INSIST(op != NULL); + + if (op->listen_list == NULL) { + ip_match_list iml; + ip_match_element ime; + struct in_addr address; + + op->listen_list = new_listen_info_list(); + + address.s_addr = htonl(INADDR_ANY); + iml = new_ip_match_list(); + ime = new_ip_match_pattern(address, 0); + add_to_ip_match_list(iml, ime); + add_listen_on(op, htons(NS_DEFAULTPORT), iml); + } + if (server_options != NULL) + free_options(server_options); + server_options = op; + + /* XXX should validate pid filename */ + INSIST(op->pid_filename != NULL); + + if (op->directory && !os_change_directory(op->directory)) + ns_panic(ns_log_config, 0, "can't change directory to %s: %s", + op->directory, strerror(errno)); + + /* XXX currently a value of 0 means "use default"; it would be + better if the options block had a "attributes updated" vector + (like the way X deals with GC updates) */ + + if (!op->transfers_in) + op->transfers_in = DEFAULT_XFERS_RUNNING; + else if (op->transfers_in > MAX_XFERS_RUNNING) { + ns_warning(ns_log_config, + "the maximum number of concurrent inbound transfers is %d", + MAX_XFERS_RUNNING); + op->transfers_in = MAX_XFERS_RUNNING; + } + + if (!op->transfers_per_ns) + op->transfers_per_ns = DEFAULT_XFERS_PER_NS; + + if (!op->max_transfer_time_in) + op->max_transfer_time_in = MAX_XFER_TIME; + + /* XXX currently transfers_out is not used */ + + /* + * Limits + */ + +#ifdef HAVE_GETRUSAGE + ns_rlimit(Datasize, op->data_size); + ns_rlimit(Stacksize, op->stack_size); + ns_rlimit(Coresize, op->core_size); + ns_rlimit(Files, op->files); +#else + ns_info(ns_log_config, "cannot set resource limits on this system"); +#endif + + + /* + * Timers + */ + set_interval_timer(CLEAN_TIMER, server_options->clean_interval); + set_interval_timer(INTERFACE_TIMER, + server_options->interface_interval); + set_interval_timer(STATS_TIMER, server_options->stats_interval); + + options_installed = 1; + default_options_installed = is_default; +} + +void +use_default_options() { + set_options(new_options(), 1); +} + +/* + * IP Matching Lists + */ + +ip_match_list +new_ip_match_list() { + ip_match_list iml; + + iml = (ip_match_list)memget(sizeof (struct ip_match_list)); + if (iml == NULL) + panic("memget failed in new_ip_match_list", NULL); + iml->first = NULL; + iml->last = NULL; + return (iml); +} + +void +free_ip_match_list(ip_match_list iml) { + ip_match_element ime, next_element; + + for (ime = iml->first; ime != NULL; ime = next_element) { + next_element = ime->next; + memput(ime, sizeof *ime); + } + memput(iml, sizeof *iml); +} + +ip_match_element +new_ip_match_pattern(struct in_addr address, u_int mask_bits) { + ip_match_element ime; + u_int32_t mask; + + ime = (ip_match_element)memget(sizeof (struct ip_match_element)); + if (ime == NULL) + panic("memget failed in new_ip_match_pattern", NULL); + ime->type = ip_match_pattern; + ime->flags = 0; + ime->u.direct.address = address; + if (mask_bits == 0) + /* can't shift >= the size of a type in bits, so + we deal with an empty mask here */ + mask = 0; + else { + /* set the 'mask_bits' most significant bits */ + mask = 0xffffffffU; + mask >>= (32 - mask_bits); + mask <<= (32 - mask_bits); + } + mask = ntohl(mask); + ime->u.direct.mask.s_addr = mask; + ime->next = NULL; + if (!ina_onnet(ime->u.direct.address, ime->u.direct.address, + ime->u.direct.mask)) { + memput(ime, sizeof *ime); + ime = NULL; + } + return (ime); +} + +ip_match_element +new_ip_match_mask(struct in_addr address, struct in_addr mask) { + ip_match_element ime; + + ime = (ip_match_element)memget(sizeof (struct ip_match_element)); + if (ime == NULL) + panic("memget failed in new_ip_match_pattern", NULL); + ime->type = ip_match_pattern; + ime->flags = 0; + ime->u.direct.address = address; + ime->u.direct.mask = mask; + ime->next = NULL; + if (!ina_onnet(ime->u.direct.address, ime->u.direct.address, + ime->u.direct.mask)) { + memput(ime, sizeof *ime); + ime = NULL; + } + return (ime); +} + +ip_match_element +new_ip_match_indirect(ip_match_list iml) { + ip_match_element ime; + + INSIST(iml != NULL); + + ime = (ip_match_element)memget(sizeof (struct ip_match_element)); + if (ime == NULL) + panic("memget failed in new_ip_match_indirect", NULL); + ime->type = ip_match_indirect; + ime->flags = 0; + ime->u.indirect.list = iml; + ime->next = NULL; + return (ime); +} + +ip_match_element +new_ip_match_localhost() { + ip_match_element ime; + + ime = (ip_match_element)memget(sizeof (struct ip_match_element)); + if (ime == NULL) + panic("memget failed in new_ip_match_localhost", NULL); + ime->type = ip_match_localhost; + ime->flags = 0; + ime->u.indirect.list = NULL; + ime->next = NULL; + return (ime); +} + +ip_match_element +new_ip_match_localnets() { + ip_match_element ime; + + ime = (ip_match_element)memget(sizeof (struct ip_match_element)); + if (ime == NULL) + panic("memget failed in new_ip_match_localnets", NULL); + ime->type = ip_match_localnets; + ime->flags = 0; + ime->u.indirect.list = NULL; + ime->next = NULL; + return (ime); +} + +void +ip_match_negate(ip_match_element ime) { + if (ime->flags & IP_MATCH_NEGATE) + ime->flags &= ~IP_MATCH_NEGATE; + else + ime->flags |= IP_MATCH_NEGATE; +} + +void +add_to_ip_match_list(ip_match_list iml, ip_match_element ime) { + INSIST(iml != NULL); + INSIST(ime != NULL); + + if (iml->last != NULL) + iml->last->next = ime; + ime->next = NULL; + iml->last = ime; + if (iml->first == NULL) + iml->first = ime; +} + +void +dprint_ip_match_list(int category, ip_match_list iml, int indent, + char *allow, char *deny) { + ip_match_element ime; + char spaces[40+1]; + char addr_text[sizeof "255.255.255.255"]; + char mask_text[sizeof "255.255.255.255"]; + char *tmp; + + INSIST(iml != NULL); + + if (indent > 40) + indent = 40; + if (indent) + memset(spaces, ' ', indent); + spaces[indent] = '\0'; + + for (ime = iml->first; ime != NULL; ime = ime->next) { + switch (ime->type) { + case ip_match_pattern: + memset(addr_text, 0, sizeof addr_text); + strncpy(addr_text, inet_ntoa(ime->u.direct.address), + ((sizeof addr_text) - 1)); + memset(mask_text, 0, sizeof mask_text); + strncpy(mask_text, inet_ntoa(ime->u.direct.mask), + ((sizeof mask_text) - 1)); + ns_debug(category, 7, "%s%saddr: %s, mask: %s", + spaces, + (ime->flags & IP_MATCH_NEGATE) ? deny : allow, + addr_text, mask_text); + break; + case ip_match_localhost: + ns_debug(category, 7, "%s%slocalhost", spaces, + (ime->flags & IP_MATCH_NEGATE) ? + deny : allow); + break; + case ip_match_localnets: + ns_debug(category, 7, "%s%slocalnets", spaces, + (ime->flags & IP_MATCH_NEGATE) ? + deny : allow); + break; + case ip_match_indirect: + ns_debug(category, 7, "%s%sindirect list %p", spaces, + (ime->flags & IP_MATCH_NEGATE) ? deny : allow, + ime->u.indirect.list); + if (ime->u.indirect.list != NULL) + dprint_ip_match_list(category, + ime->u.indirect.list, + indent+2, allow, deny); + break; + default: + panic("unexpected ime type in dprint_ip_match_list()", + NULL); + } + } +} + +int +ip_match_address(ip_match_list iml, struct in_addr address) { + ip_match_element ime; + int ret; + int indirect; + + INSIST(iml != NULL); + for (ime = iml->first; ime != NULL; ime = ime->next) { + switch (ime->type) { + case ip_match_pattern: + indirect = 0; + break; + case ip_match_indirect: + indirect = 1; + break; + case ip_match_localhost: + ime->u.indirect.list = local_addresses; + indirect = 1; + break; + case ip_match_localnets: + ime->u.indirect.list = local_networks; + indirect = 1; + break; + default: + indirect = 0; /* Make gcc happy. */ + panic("unexpected ime type in ip_match_address()", + NULL); + } + if (indirect) { + ret = ip_match_address(ime->u.indirect.list, address); + if (ret >= 0) { + if (ime->flags & IP_MATCH_NEGATE) + ret = (ret) ? 0 : 1; + return (ret); + } + } else { + if (ina_onnet(address, ime->u.direct.address, + ime->u.direct.mask)) { + if (ime->flags & IP_MATCH_NEGATE) + return (0); + else + return (1); + } + } + } + return (-1); +} + +int +ip_address_allowed(ip_match_list iml, struct in_addr address) { + int ret; + + if (iml == NULL) + return (0); + ret = ip_match_address(iml, address); + if (ret < 0) + ret = 0; + return (ret); +} + +int +ip_match_network(ip_match_list iml, struct in_addr address, + struct in_addr mask) { + ip_match_element ime; + int ret; + int indirect; + + INSIST(iml != NULL); + for (ime = iml->first; ime != NULL; ime = ime->next) { + switch (ime->type) { + case ip_match_pattern: + indirect = 0; + break; + case ip_match_indirect: + indirect = 1; + break; + case ip_match_localhost: + ime->u.indirect.list = local_addresses; + indirect = 1; + break; + case ip_match_localnets: + ime->u.indirect.list = local_networks; + indirect = 1; + break; + default: + indirect = 0; /* Make gcc happy. */ + panic("unexpected ime type in ip_match_network()", + NULL); + } + if (indirect) { + ret = ip_match_network(ime->u.indirect.list, + address, mask); + if (ret >= 0) { + if (ime->flags & IP_MATCH_NEGATE) + ret = (ret) ? 0 : 1; + return (ret); + } + } else { + if (address.s_addr == ime->u.direct.address.s_addr && + mask.s_addr == ime->u.direct.mask.s_addr) { + if (ime->flags & IP_MATCH_NEGATE) + return (0); + else + return (1); + } + } + } + return (-1); +} + +int +distance_of_address(ip_match_list iml, struct in_addr address) { + ip_match_element ime; + int ret; + int indirect; + int distance; + + INSIST(iml != NULL); + for (distance = 1, ime = iml->first; + ime != NULL; ime = ime->next, distance++) { + switch (ime->type) { + case ip_match_pattern: + indirect = 0; + break; + case ip_match_indirect: + indirect = 1; + break; + case ip_match_localhost: + ime->u.indirect.list = local_addresses; + indirect = 1; + break; + case ip_match_localnets: + ime->u.indirect.list = local_networks; + indirect = 1; + break; + default: + indirect = 0; /* Make gcc happy. */ + panic("unexpected ime type in distance_of_address()", + NULL); + } + if (indirect) { + ret = ip_match_address(ime->u.indirect.list, address); + if (ret >= 0) { + if (ime->flags & IP_MATCH_NEGATE) + ret = (ret) ? 0 : 1; + if (distance > MAX_TOPOLOGY_DISTANCE) + distance = MAX_TOPOLOGY_DISTANCE; + if (ret) + return (distance); + else + return (MAX_TOPOLOGY_DISTANCE); + } + } else { + if (ina_onnet(address, ime->u.direct.address, + ime->u.direct.mask)) { + if (distance > MAX_TOPOLOGY_DISTANCE) + distance = MAX_TOPOLOGY_DISTANCE; + if (ime->flags & IP_MATCH_NEGATE) + return (MAX_TOPOLOGY_DISTANCE); + else + return (distance); + } + } + } + return (UNKNOWN_TOPOLOGY_DISTANCE); +} + +int +ip_match_is_none(ip_match_list iml) { + ip_match_element ime; + + if (iml == NULL) + return (1); + ime = iml->first; + if (ime->type == ip_match_indirect) { + if (ime->flags & IP_MATCH_NEGATE) + return (0); + iml = ime->u.indirect.list; + if (iml == NULL) + return (0); + ime = iml->first; + } + if (ime->type == ip_match_pattern) { + if ((ime->flags & IP_MATCH_NEGATE) && + ime->u.direct.address.s_addr == 0 && + ime->u.direct.mask.s_addr == 0) + return (1); + } + return (0); +} + + +/* + * Forwarder glue + * + * XXX This will go away when the rest of bind understands + * forward zones. + */ + +void +add_forwarder(options op, struct in_addr address) { + struct fwdinfo *fip = NULL, *ftp = NULL; + +#ifdef SLAVE_FORWARD + int forward_count = 0; +#endif + + INSIST(op != NULL); + + /* On multiple forwarder lines, move to end of the list. */ +#ifdef SLAVE_FORWARD + if (op->fwdtab != NULL) { + forward_count++; + for (fip = op->fwdtab; fip->next != NULL; fip = fip->next) + forward_count++; + } +#else + if (op->fwdtab != NULL) { + for (fip = op->fwdtab; fip->next != NULL; fip = fip->next) { + ; + } + } +#endif /* SLAVE_FORWARD */ + + ftp = (struct fwdinfo *)memget(sizeof(struct fwdinfo)); + if (!ftp) + panic("memget failed in add_forwarder", NULL); + ftp->fwdaddr.sin_family = AF_INET; + ftp->fwdaddr.sin_addr = address; + ftp->fwdaddr.sin_port = ns_port; +#ifdef FWD_LOOP + if (aIsUs(ftp->fwdaddr.sin_addr)) { + ns_error(ns_log_config, "forwarder '%s' ignored, my address", + inet_ntoa(address)); + memput(ftp, sizeof *ftp); + return; + } +#endif /* FWD_LOOP */ + ns_debug(ns_log_config, 2, "added forwarder %s", inet_ntoa(address)); + ftp->next = NULL; + if (op->fwdtab == NULL) + op->fwdtab = ftp; /* First time only */ + else + fip->next = ftp; +#ifdef SLAVE_FORWARD + forward_count++; + + /* + ** 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 +} + +void +free_forwarders(struct fwdinfo *fwdtab) { + struct fwdinfo *ftp, *fnext; + + for (ftp = fwdtab; ftp != NULL; ftp = fnext) { + fnext = ftp->next; + memput(ftp, sizeof *ftp); + } +} + +/* + * Servers + */ + + +static server_info +new_server(struct in_addr address) { + server_info si; + + si = (server_info)memget(sizeof (struct server_info)); + if (si == NULL) + panic("memget failed in new_server()", NULL); + si->address = address; + si->flags = 0U; + si->transfers = 0; + si->transfer_format = axfr_use_default; + si->key_list = NULL; + si->next = NULL; + return si; +} + +static void +free_server(server_info si) { + /* Don't free key; it'll be done when the auth table is freed. */ + memput(si, sizeof *si); +} + +server_info +find_server(struct in_addr address) { + server_info si; + + for (si = nameserver_info; si != NULL; si = si->next) + if (si->address.s_addr == address.s_addr) + break; + return (si); +} + +static void +add_server(server_info si) { + ip_match_element ime; + + si->next = nameserver_info; + nameserver_info = si; + + /* + * To ease transition, we'll add bogus nameservers to an + * ip matching list. This will probably be redone when the + * merging of nameserver data structures occurs. + */ + if (si->flags & SERVER_INFO_BOGUS) { + ime = new_ip_match_pattern(si->address, 32); + INSIST(ime != NULL); + add_to_ip_match_list(bogus_nameservers, ime); + } + ns_debug(ns_log_config, 3, "server %s: flags %08x transfers %d", + inet_ntoa(si->address), si->flags, si->transfers); + if (si->key_list != NULL) + dprint_key_info_list(si->key_list); +} + +static void +free_nameserver_info() { + server_info si_next, si; + + for (si = nameserver_info; si != NULL; si = si_next) { + si_next = si->next; + free_server(si); + } + nameserver_info = NULL; + if (bogus_nameservers != NULL) { + free_ip_match_list(bogus_nameservers); + bogus_nameservers = NULL; + } +} + +server_config +begin_server(struct in_addr address) { + server_config sc; + + sc.opaque = new_server(address); + return (sc); +} + +void +end_server(server_config sc, int should_install) { + server_info si; + + si = sc.opaque; + + INSIST(si != NULL); + + if (should_install) + add_server(si); + else + free_server(si); + sc.opaque = NULL; +} + +void +set_server_option(server_config sc, int bool_opt, int value) { + server_info si; + + si = sc.opaque; + + INSIST(si != NULL); + + switch (bool_opt) { + case SERVER_INFO_BOGUS: + if (value) + si->flags |= bool_opt; + else + si->flags &= ~bool_opt; + break; + default: + panic("unexpected option in set_server_option", NULL); + } +} + +void +set_server_transfers(server_config sc, int transfers) { + server_info si; + + si = sc.opaque; + + INSIST(si != NULL); + + if (transfers < 0) + transfers = 0; + si->transfers = transfers; +} + +void +set_server_transfer_format(server_config sc, + enum axfr_format transfer_format) { + server_info si; + + si = sc.opaque; + + INSIST(si != NULL); + + si->transfer_format = transfer_format; +} + +void +add_server_key_info(server_config sc, key_info ki) { + server_info si; + + si = sc.opaque; + + INSIST(si != NULL); + + if (si->key_list == NULL) + si->key_list = new_key_info_list(); + add_to_key_info_list(si->key_list, ki); +} + +/* + * Keys + */ + +key_info +new_key_info(char *name, char *algorithm, char *secret) { + key_info ki; + + INSIST(name != NULL); + INSIST(algorithm != NULL); + INSIST(secret != NULL); + ki = (key_info)memget(sizeof (struct key_info)); + if (ki == NULL) + panic("memget failed in new_key_info", NULL); + ki->name = name; + ki->algorithm = algorithm; + ki->secret = secret; + return (ki); +} + +void +free_key_info(key_info ki) { + INSIST(ki != NULL); + freestr(ki->name); + freestr(ki->algorithm); + freestr(ki->secret); + memput(ki, sizeof *ki); +} + +void +dprint_key_info(key_info ki) { + INSIST(ki != NULL); + ns_debug(ns_log_config, 7, "key %s", ki->name); + ns_debug(ns_log_config, 7, " algorithm %s", ki->algorithm); + ns_debug(ns_log_config, 7, " secret %s", ki->secret); +} + +key_info_list +new_key_info_list() { + key_info_list kil; + + kil = (key_info_list)memget(sizeof (struct key_info_list)); + if (kil == NULL) + panic("memget failed in new_key_info_list()", NULL); + kil->first = NULL; + kil->last = NULL; + return (kil); +} + +void +free_key_info_list(key_info_list kil) { + key_list_element kle, kle_next; + + INSIST(kil != NULL); + for (kle = kil->first; kle != NULL; kle = kle_next) { + kle_next = kle->next; + /* note we do NOT free kle->info */ + memput(kle, sizeof *kle); + } + memput(kil, sizeof *kil); +} + +void +add_to_key_info_list(key_info_list kil, key_info ki) { + key_list_element kle; + + INSIST(kil != NULL); + INSIST(ki != NULL); + + kle = (key_list_element)memget(sizeof (struct key_list_element)); + if (kle == NULL) + panic("memget failed in add_to_key_info_list()", NULL); + kle->info = ki; + if (kil->last != NULL) + kil->last->next = kle; + kle->next = NULL; + kil->last = kle; + if (kil->first == NULL) + kil->first = kle; +} + +void +dprint_key_info_list(key_info_list kil) { + key_list_element kle; + + INSIST(kil != NULL); + + for (kle = kil->first; kle != NULL; kle = kle->next) + dprint_key_info(kle->info); +} + +/* + * Logging. + */ + +log_config +begin_logging() { + log_config log_cfg; + log_context lc; + + log_cfg = (log_config)memget(sizeof (struct log_config)); + if (log_cfg == NULL) + ns_panic(ns_log_config, 0, + "memget failed creating log_config"); + if (log_new_context(ns_log_max_category, logging_categories, &lc) < 0) + ns_panic(ns_log_config, 0, + "log_new_context() failed: %s", strerror(errno)); + log_cfg->log_ctx = lc; + log_cfg->eventlib_channel = NULL; + log_cfg->packet_channel = NULL; + log_cfg->default_debug_active = 0; + return (log_cfg); +} + +void +add_log_channel(log_config log_cfg, int category, log_channel chan) { + log_channel_type type; + + INSIST(log_cfg != NULL); + + type = log_get_channel_type(chan); + if (category == ns_log_eventlib) { + if (type != log_file && type != log_null) { + ns_error(ns_log_config, + "must specify a file or null channel for the eventlib category"); + return; + } + if (log_cfg->eventlib_channel != NULL) { + ns_error(ns_log_config, + "only one channel allowed for the eventlib category"); + return; + } + log_cfg->eventlib_channel = chan; + } + if (category == ns_log_packet) { + if (type != log_file && type != log_null) { + ns_error(ns_log_config, + "must specify a file or null channel for the packet category"); + return; + } + if (log_cfg->packet_channel != NULL) { + ns_error(ns_log_config, + "only one channel allowed for the packet category"); + return; + } + log_cfg->packet_channel = chan; + } + + if (log_add_channel(log_cfg->log_ctx, category, chan) < 0) { + ns_error(ns_log_config, "log_add_channel() failed"); + return; + } + + if (chan == debug_channel) + log_cfg->default_debug_active = 1; +} + +void +open_special_channels() { + int using_null = 0; + + if (log_open_stream(eventlib_channel) == NULL) { + eventlib_channel = null_channel; + using_null = 1; + } + if (log_open_stream(packet_channel) == NULL) { + packet_channel = null_channel; + using_null = 1; + } + + if (using_null && + log_open_stream(null_channel) == NULL) + ns_panic(ns_log_config, 1, "couldn't open null channel"); +} + +void +set_logging(log_config log_cfg, int is_default) { + log_context lc; + int skip_debug = 0; + + INSIST(log_cfg != NULL); + lc = log_cfg->log_ctx; + + /* + * Add the default category if it's not in the context already. + */ + if (!log_category_is_active(lc, ns_log_default)) { + add_log_channel(log_cfg, ns_log_default, debug_channel); + add_log_channel(log_cfg, ns_log_default, syslog_channel); + } + + /* + * Add the panic category if it's not in the context already. + */ + if (!log_category_is_active(lc, ns_log_panic)) { + add_log_channel(log_cfg, ns_log_panic, stderr_channel); + add_log_channel(log_cfg, ns_log_panic, syslog_channel); + } + + /* + * Add the eventlib category if it's not in the context already. + */ + if (!log_category_is_active(lc, ns_log_eventlib)) + add_log_channel(log_cfg, ns_log_eventlib, debug_channel); + + /* + * Add the packet category if it's not in the context already. + */ + if (!log_category_is_active(lc, ns_log_packet)) + add_log_channel(log_cfg, ns_log_packet, debug_channel); + +#ifdef DEBUG + /* + * Preserve debugging state. + */ + log_option(lc, LOG_OPTION_DEBUG, debug); + log_option(lc, LOG_OPTION_LEVEL, debug); +#endif + + /* + * Special case for query-log, so we can co-exist with the command + * line option and SIGWINCH. + */ + if (log_category_is_active(lc, ns_log_queries)) + qrylog = 1; + + /* + * Cleanup the old context. + */ + if (need_logging_free) + log_free_context(log_ctx); + + /* + * The default file channels will never have their reference counts + * drop to zero, and so they will not be closed by the logging system + * when log_free_context() is called. We don't want to keep files + * open unnecessarily, and we want them to behave like user-created + * channels, so we close them here. + */ + if (log_get_stream(debug_channel) != stderr) + (void)log_close_stream(debug_channel); + (void)log_close_stream(null_channel); + + /* + * Install the new context. + */ + log_ctx = lc; + eventlib_channel = log_cfg->eventlib_channel; + packet_channel = log_cfg->packet_channel; + +#ifdef DEBUG + if (debug) { + open_special_channels(); + evSetDebug(ev, debug, log_get_stream(eventlib_channel)); + } +#endif + + log_ctx_valid = 1; + need_logging_free = 1; + logging_installed = 1; + default_logging_installed = is_default; +} + +void +end_logging(log_config log_cfg, int should_install) { + if (should_install) + set_logging(log_cfg, 0); + else + log_free_context(log_cfg->log_ctx); + memput(log_cfg, sizeof (struct log_config)); +} + +void +use_default_logging() { + log_config log_cfg; + + log_cfg = begin_logging(); + set_logging(log_cfg, 1); + memput(log_cfg, sizeof (struct log_config)); +} + +static void +init_default_log_channels() { + FILE *null_stream; + u_int flags; + char *name; + FILE *stream; + + syslog_channel = log_new_syslog_channel(0, log_info, LOG_DAEMON); + if (syslog_channel == NULL || log_inc_references(syslog_channel) < 0) + ns_panic(ns_log_config, 0, "couldn't create syslog_channel"); + + flags = LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; + if (foreground) { + name = NULL; + stream = stderr; + } else { + name = _PATH_DEBUG; + stream = NULL; + } + debug_channel = log_new_file_channel(flags, log_info, name, stream, + 0, ULONG_MAX); + if (debug_channel == NULL || log_inc_references(debug_channel) < 0) + ns_panic(ns_log_config, 0, "couldn't create debug_channel"); + + stderr_channel = log_new_file_channel(0, log_info, NULL, stderr, + 0, ULONG_MAX); + if (stderr_channel == NULL || log_inc_references(stderr_channel) < 0) + ns_panic(ns_log_config, 0, "couldn't create stderr_channel"); + + null_channel = log_new_file_channel(LOG_CHANNEL_OFF, log_info, + _PATH_DEVNULL, NULL, 0, ULONG_MAX); + if (null_channel == NULL || log_inc_references(null_channel) < 0) + ns_panic(ns_log_config, 0, "couldn't create null_channel"); +} + +static void +shutdown_default_log_channels() { + log_free_channel(syslog_channel); + log_free_channel(debug_channel); + log_free_channel(stderr_channel); + log_free_channel(null_channel); +} + +void +init_logging() { + int i; + int size; + const struct ns_sym *s; + char category_name[256]; + + size = ns_log_max_category * (sizeof (char *)); + + logging_categories = (char **)memget(size); + if (logging_categories == NULL) + ns_panic(ns_log_config, 0, "memget failed in init_logging"); + memset(logging_categories, 0, size); + for (s = category_constants; s != NULL && s->name != NULL; s++) { + sprintf(category_name, "%s: ", s->name); + logging_categories[s->number] = savestr(category_name, 1); + } + + init_default_log_channels(); + use_default_logging(); +} + +void +shutdown_logging() { + int size; + const struct ns_sym *s; + + evSetDebug(ev, 0, NULL); + shutdown_default_log_channels(); + log_free_context(log_ctx); + + for (s = category_constants; s != NULL && s->name != NULL; s++) + freestr(logging_categories[s->number]); + size = ns_log_max_category * (sizeof (char *)); + memput(logging_categories, size); +} + +/* + * Main Loader + */ + +void +init_configuration() { + /* + * Remember initial limits for use if "default" is specified in + * a config file. + */ +#ifdef HAVE_GETRUSAGE + get_initial_limits(); +#endif + zone_symbol_table = new_symbol_table(ZONE_SYM_TABLE_SIZE, NULL); + use_default_options(); + parser_initialize(); + config_initialized = 1; +} + +void +shutdown_configuration() { + REQUIRE(config_initialized); + + if (server_options != NULL) { + free_options(server_options); + server_options = NULL; + } + if (current_pid_filename != NULL) + freestr(current_pid_filename); + free_nameserver_info(); + free_symbol_table(zone_symbol_table); + parser_shutdown(); + config_initialized = 0; +} + +void +load_configuration(const char *filename) { + REQUIRE(config_initialized); + + ns_debug(ns_log_config, 3, "load configuration %s", filename); + + /* + * Clean up any previous configuration and initialize + * global data structures we'll be updating. + */ + free_nameserver_info(); + bogus_nameservers = new_ip_match_list(); + + options_installed = 0; + logging_installed = 0; + + parse_configuration(filename); + + /* + * If the user didn't specify logging or options, but they previously + * had specified one or both of them, then we need to + * re-establish the default environment. We have to be careful + * about when we install default options because the parser + * must respect limits (e.g. data-size, number of open files) + * specified in the options file. In the ordinary case where the + * options section isn't changing on a zone reload, it would be bad + * to lower these limits temporarily, because we might not survive + * to the point where they get raised back again. The logging case + * has similar motivation -- we don't want to override the existing + * logging scheme (perhaps causing log messages to go somewhere + * unexpected) when the user hasn't expressed a desire for a new + * scheme. + */ + if (!logging_installed) + use_default_logging(); + if (!options_installed && !default_options_installed) { + use_default_options(); + ns_warning(ns_log_config, "re-establishing default options"); + } + + update_pid_file(); + + /* Init or reinit the interface/port list and associated sockets. */ + getnetconf(0); + opensocket_f(); + + initial_configuration = 0; +} diff --git a/contrib/bind/bin/named/ns_defs.h b/contrib/bind/bin/named/ns_defs.h new file mode 100644 index 0000000..f81a383 --- /dev/null +++ b/contrib/bind/bin/named/ns_defs.h @@ -0,0 +1,756 @@ +/* + * from ns.h 4.33 (Berkeley) 8/23/90 + * $Id: ns_defs.h,v 8.38 1998/03/17 03:22:27 halley Exp $ + */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * 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 MIN_REFRESH 2 /* never refresh more frequently than once */ + /* every MIN_REFRESH seconds */ +#define MIN_RETRY 1 /* never retry more frequently than once */ + /* every MIN_RETRY seconds */ +#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 /* default max seconds for an xfer */ +#define XFER_TIME_FUDGE 10 /* MAX_XFER_TIME fudge */ +#define MAX_XFERS_RUNNING 20 /* max value of transfers_in */ +#define DEFAULT_XFERS_RUNNING 10 /* default value of transfers_in */ +#define DEFAULT_XFERS_PER_NS 2 /* default # of xfers per peer nameserver */ +#define XFER_BUFSIZE (16*1024) /* arbitrary but bigger than most MTU's */ + +#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 */ + + /* What maintainance operations need to be performed sometime soon? */ +#define MAIN_NEED_RELOAD 0x0001 /* db_reload() needed. */ +#define MAIN_NEED_MAINT 0x0002 /* ns_maint() needed. */ +#define MAIN_NEED_ENDXFER 0x0004 /* endxfer() needed. */ +#define MAIN_NEED_ZONELOAD 0x0008 /* loadxfer() needed. */ +#define MAIN_NEED_DUMP 0x0010 /* doadump() needed. */ +#define MAIN_NEED_STATSDUMP 0x0020 /* ns_stats() needed. */ +#define MAIN_NEED_EXIT 0x0040 /* exit() needed. */ +#define MAIN_NEED_QRYLOG 0x0080 /* toggle_qrylog() needed. */ +#define MAIN_NEED_DEBUG 0x0100 /* use_desired_debug() needed. */ +#define MAIN_NEED_NOTIFY 0x0200 /* do_notify_after_load() needed */ + + /* What global options are set? */ +#define OPTION_NORECURSE 0x0001 /* Don't recurse even if asked. */ +#define OPTION_NOFETCHGLUE 0x0002 /* Don't fetch missing glue. */ +#define OPTION_FORWARD_ONLY 0x0004 /* Don't use NS RR's, just forward. */ +#define OPTION_FAKE_IQUERY 0x0008 /* Fake up bogus response to IQUERY. */ +#define OPTION_NONOTIFY 0x0010 /* Turn off notify */ +#define OPTION_NONAUTH_NXDOMAIN 0x0020 /* Generate non-auth NXDOMAINs? */ +#define OPTION_MULTIPLE_CNAMES 0x0040 /* Allow a name to have multiple + * CNAME RRs */ +#define OPTION_HOSTSTATS 0x0080 /* Maintain per-host statistics? */ +#define OPTION_DEALLOC_ON_EXIT 0x0100 /* Deallocate everything on exit? */ + +#define DEFAULT_OPTION_FLAGS (OPTION_HOSTSTATS) + +#ifdef BIND_UPDATE +#define SOAINCRINTVL 300 /* default value for the time after which + * the zone serial number must be incremented + * after a successful update has occurred */ +#define DUMPINTVL 3600 /* default interval at which to dump changed zones + * randomized, not exact */ +#define DEFERUPDCNT 100 /* default number of updates that can happen + * before the zone serial number will be + * incremented */ +#define UPDATE_TIMER XFER_TIMER +#endif /* BIND_UPDATE */ + +#define USE_MINIMUM 0xffffffff +#define MAXIMUM_TTL 0x7fffffff + +#define CLEAN_TIMER 0x01 +#define INTERFACE_TIMER 0x02 +#define STATS_TIMER 0x04 + + /* IP address accessor, network byte order. */ +#define ina_ulong(ina) (ina.s_addr) + + /* IP address accessor, host byte order, read only. */ +#define ina_hlong(ina) ntohl(ina.s_addr) + + /* IP address equality. */ + /* XXX: assumes that network byte order won't affect equality. */ +#define ina_equal(a, b) (ina_ulong(a) == ina_ulong(b)) + + /* IP address equality with a mask. */ +#define ina_onnet(h, n, m) ((ina_ulong(h) & ina_ulong(m)) == ina_ulong(n)) + + /* Sequence space arithmetic. */ +#define SEQ_GT(a,b) ((int32_t)((a)-(b)) > 0) + +#define NS_OPTION_P(option) ((server_options == NULL) ? \ + (panic(panic_msg_no_options, NULL), 0) : \ + ((server_options->flags & option) != 0)) + +#define NS_INCRSTAT(addr, which) \ + do { \ + if ((int)which >= (int)nssLast) \ + ns_panic(ns_log_insist, 1, panic_msg_bad_which, \ + __FILE__, __LINE__, #which); \ + else { \ + if (NS_OPTION_P(OPTION_HOSTSTATS)) { \ + struct nameser *ns = \ + nameserFind(addr, NS_F_INSERT); \ + if (ns != NULL) \ + ns->stats[(int)which]++; \ + } \ + globalStats[(int)which]++; \ + } \ + } while (0) + +enum severity { ignore, warn, fail, not_set }; + +enum znotify { znotify_use_default=0, znotify_yes, znotify_no }; + +enum axfr_format { axfr_use_default=0, axfr_one_answer, axfr_many_answers }; + +struct ip_match_direct { + struct in_addr address; + struct in_addr mask; +}; + +struct ip_match_indirect { + struct ip_match_list *list; +}; + +typedef enum { ip_match_pattern, ip_match_indirect, ip_match_localhost, + ip_match_localnets } ip_match_type; + +typedef struct ip_match_element { + ip_match_type type; + u_int flags; + union { + struct ip_match_direct direct; + struct ip_match_indirect indirect; + } u; + struct ip_match_element *next; +} *ip_match_element; + +/* Flags for ip_match_element */ +#define IP_MATCH_NEGATE 0x01 /* match means deny access */ + +typedef struct ip_match_list { + ip_match_element first; + ip_match_element last; +} *ip_match_list; + +typedef struct ztimer_info { + char *name; + int class; + int type; +} *ztimer_info; + +/* 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 soa serial increment */ + 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_axfr_src; /* bind() the axfr socket to this */ + 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 */ + int z_numxfrs; /* Ref count of concurrent xfrs. */ + enum severity z_checknames; /* How to handle non-RFC-compliant names */ +#ifdef BIND_UPDATE + time_t z_dumptime; /* randomized time for next zone dump + * if Z_NEED_DUMP is set */ + u_int32_t z_dumpintvl; /* time interval between zone dumps */ + time_t z_soaincrintvl; /* interval for updating soa serial */ + time_t z_soaincrtime; /* time for soa increment */ + u_int32_t z_deferupdcnt; /* max number of updates before SOA + * serial number incremented */ + u_int32_t z_updatecnt; /* number of update requests processed + * since the last SOA serial update */ + char *z_updatelog; /* log file for updates */ +#endif + ip_match_list z_update_acl; /* list of who can issue dynamic + updates */ + ip_match_list z_query_acl; /* sites we'll answer questions for */ + ip_match_list z_transfer_acl; /* sites that may get a zone transfer + from us */ + long z_max_transfer_time_in; /* max num seconds for AXFR */ + enum znotify z_notify; /* Notify mode */ + struct in_addr z_also_notify[NSMAX]; /* More nameservers to notify */ + int z_notify_count; + evTimerID z_timer; /* maintenance timer */ + ztimer_info z_timerinfo; /* UAP associated with timer */ + time_t z_nextmaint; /* time of next maintenance */ +}; + + /* zone types (z_type) */ +enum zonetype { z_nil, z_master, z_slave, z_hint, z_stub, z_any }; +#define Z_NIL z_nil /* XXX */ +#define Z_MASTER z_master /* XXX */ +#define Z_PRIMARY z_master /* XXX */ +#define Z_SLAVE z_slave /* XXX */ +#define Z_SECONDARY z_slave /* XXX */ +#define Z_HINT z_hint /* XXX */ +#define Z_CACHE z_hint /* XXX */ +#define Z_STUB z_stub /* XXX */ +#define Z_ANY z_any /* XXX*2 */ + + /* 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 BIND_UPDATE +#define Z_DYNAMIC 0x0400 /* allow dynamic updates */ +#define Z_NEED_DUMP 0x0800 /* zone has changed, needs a dump */ +#define Z_NEED_SOAUPDATE 0x1000 /* soa serial number needs increment */ +#endif /* BIND_UPDATE */ +#define Z_XFER_ABORTED 0x2000 /* zone transfer has been aborted */ +#define Z_XFER_GONE 0x4000 /* zone transfer process is gone */ +#define Z_TIMER_SET 0x8000 /* z_timer contains a valid id */ + + /* 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 */ + +/* 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_msgsize, /* allocated size of message */ + q_cmsglen, /* len of cname message */ + q_cmsgsize; /* allocated size 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) */ + char *q_domain; /* domain of most enclosing zone cut */ + char *q_name; /* domain of query */ + u_int16_t q_class; /* class of query */ + u_int16_t q_type; /* type of query */ +#ifdef BIND_NOTIFY + int q_notifyzone; /* zone which needs a sysnotify() + * when the reply to this comes in. + */ +#endif +}; + + /* 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_USEVC 0x08 /* forward using tcp not udp */ + +#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 + +/* + * Return codes from ns_forw: + */ +#define FW_OK 0 +#define FW_DUP 1 +#define FW_NOSERVER 2 +#define FW_SERVFAIL 3 + +typedef void (*sq_closure)(struct qstream *qs); + +#ifdef BIND_UPDATE +struct fdlist { + int fd; + struct fdlist *next; +}; +#endif + +typedef struct _interface { + int dfd, /* Datagram file descriptor */ + sfd; /* Stream file descriptor. */ + time_t gen; /* Generation number. */ + struct in_addr addr; /* Interface address. */ + u_int16_t port; /* Interface port. */ + u_int16_t flags; /* Valid bits for evXXXXID. */ + evFileID evID_d; /* Datagram read-event. */ + evConnID evID_s; /* Stream listen-event. */ + LINK(struct _interface) link; +} interface; + +#define INTERFACE_FILE_VALID 0x01 +#define INTERFACE_CONN_VALID 0x02 +#define INTERFACE_FORWARDING 0x04 + +struct qstream { + int s_rfd; /* stream file descriptor */ + int s_size; /* expected amount of data to rcv */ + int s_bufsize; /* amount of data received in s_buf */ + u_char *s_buf; /* buffer of received data */ + u_char *s_wbuf; /* send buffer */ + u_char *s_wbuf_send; /* next sendable byte of send buffer */ + u_char *s_wbuf_free; /* next free byte of send buffer */ + u_char *s_wbuf_end; /* byte after end of send buffer */ + sq_closure s_wbuf_closure; /* callback for writable descriptor */ + struct qstream *s_next; /* next stream */ + struct sockaddr_in + s_from; /* address query came from */ + interface *s_ifp; /* interface query came from */ + time_t s_time; /* time stamp of last transaction */ + int s_refcnt; /* number of outstanding queries */ + u_char s_temp[HFIXEDSZ]; +#ifdef BIND_UPDATE + int s_opcode; /* type of request */ + int s_linkcnt; /* number of client connections using + * this connection to forward updates + * to the primary */ + struct fdlist *s_fds; /* linked list of connections to the + * primaries that have been used by + * the server to forward this client's + * update requests */ +#endif + evStreamID evID_r; /* read event. */ + evFileID evID_w; /* writable event handle. */ + evConnID evID_c; /* connect event handle */ + u_int flags; /* see below */ + struct qstream_xfr { + enum { s_x_base, s_x_firstsoa, s_x_zone, + s_x_lastsoa, s_x_done } + state; /* state of transfer. */ + u_char *msg, /* current assembly message. */ + *cp, /* where are we in msg? */ + *eom, /* end of msg. */ + *ptrs[128]; /* ptrs for dn_comp(). */ + int class, /* class of an XFR. */ + id, /* id of an XFR. */ + opcode; /* opcode of an XFR. */ + u_int zone; /* zone being XFR'd. */ + struct namebuf *top; /* top np of an XFR. */ + struct qs_x_lev { /* decompose the recursion. */ + enum {sxl_ns, sxl_all, sxl_sub} + state; /* what's this level doing? */ + int flags; /* see below (SXL_*). */ + char dname[MAXDNAME]; + struct namebuf *np, /* this node. */ + *nnp, /* next node to process. */ + **npp, /* subs. */ + **npe; /* end of subs. */ + struct databuf *dp; /* current rr. */ + struct qs_x_lev *next; /* link. */ + } *lev; /* LIFO. */ + enum axfr_format transfer_format; + } xfr; +}; +#define SXL_GLUING 0x01 +#define SXL_ZONECUT 0x02 + + /* flags */ +#define STREAM_MALLOC 0x01 +#define STREAM_WRITE_EV 0x02 +#define STREAM_READ_EV 0x04 +#define STREAM_CONNECT_EV 0x08 +#define STREAM_DONE_CLOSE 0x10 +#define STREAM_AXFR 0x20 + +#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 { nssRcvdR, /* sent us an answer */ + nssRcvdNXD, /* sent us a negative response */ + nssRcvdFwdR, /* sent us a response we had to fwd */ + nssRcvdDupR, /* sent us an extra answer */ + nssRcvdFail, /* sent us a SERVFAIL */ + nssRcvdFErr, /* sent us a FORMERR */ + nssRcvdErr, /* sent us some other error */ + 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 */ + nssSentDupQ, /* sent them a retry */ + nssSendtoErr, /* error in sendto */ + nssRcvdQ, /* sent us a query */ + nssRcvdIQ, /* sent us an inverse query */ + nssRcvdFwdQ, /* sent us a query we had to fwd */ + nssRcvdDupQ, /* sent us a retry */ + nssRcvdTCP, /* sent us a query using TCP */ + nssSentFwdR, /* fwdd a response to them */ + nssSentFail, /* sent them a SERVFAIL */ + nssSentFErr, /* sent them a FORMERR */ + nssSentNaAns, /* sent them a non autoritative answer */ + nssSentNXD, /* sent them a negative response */ + 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 */ +}; + +enum transport { primary_trans, secondary_trans, response_trans, num_trans }; + +/* types used by the parser or config routines */ + +typedef struct zone_config { + void *opaque; +} zone_config; + +typedef struct listen_info { + u_short port; + ip_match_list list; + struct listen_info *next; +} *listen_info; + +typedef struct listen_info_list { + listen_info first; + listen_info last; +} *listen_info_list; + +#ifndef RLIMIT_TYPE +#define RLIMIT_TYPE u_long +#endif +typedef RLIMIT_TYPE rlimit_type; + +typedef struct options { + u_int flags; + char *directory; + char *dump_filename; + char *pid_filename; + char *stats_filename; + char *memstats_filename; + char *named_xfer; + int transfers_in; + int transfers_per_ns; + int transfers_out; + enum axfr_format transfer_format; + long max_transfer_time_in; + struct sockaddr_in query_source; + ip_match_list query_acl; + ip_match_list transfer_acl; + ip_match_list topology; + enum severity check_names[num_trans]; + u_long data_size; + u_long stack_size; + u_long core_size; + u_long files; + listen_info_list listen_list; + struct fwdinfo *fwdtab; + /* XXX need to add forward option */ + int clean_interval; + int interface_interval; + int stats_interval; +} *options; + +typedef struct key_info { + char *name; + char *algorithm; + char *secret; /* XXX should be u_char? */ +} *key_info; + +typedef struct key_list_element { + key_info info; + struct key_list_element *next; +} *key_list_element; + +typedef struct key_info_list { + key_list_element first; + key_list_element last; +} *key_info_list; + +typedef struct topology_config { + void *opaque; +} topology_config; + +#define UNKNOWN_TOPOLOGY_DISTANCE 9998 +#define MAX_TOPOLOGY_DISTANCE 9999 + +typedef struct topology_distance { + ip_match_list patterns; + struct topology_distance *next; +} *topology_distance; + +typedef struct topology_context { + topology_distance first; + topology_distance last; +} *topology_context; + +typedef struct acl_table_entry { + char *name; + ip_match_list list; + struct acl_table_entry *next; +} *acl_table_entry; + +typedef struct server_config { + void *opaque; +} server_config; + +#define SERVER_INFO_BOGUS 0x01 + +typedef struct server_info { + struct in_addr address; + u_int flags; + int transfers; + enum axfr_format transfer_format; + key_info_list key_list; + /* could move statistics to here, too */ + struct server_info *next; +} *server_info; + +/* + * enum <--> name translation + */ + +struct ns_sym { + int number; /* Identifying number, like ns_log_default */ + char * name; /* Its symbolic name, like "default" */ +}; + +/* + * Logging options + */ + +typedef enum ns_logging_categories { + ns_log_default = 0, + ns_log_config, + ns_log_parser, + ns_log_queries, + ns_log_lame_servers, + ns_log_statistics, + ns_log_panic, + ns_log_update, + ns_log_ncache, + ns_log_xfer_in, + ns_log_xfer_out, + ns_log_db, + ns_log_eventlib, + ns_log_packet, + ns_log_notify, + ns_log_cname, + ns_log_security, + ns_log_os, + ns_log_insist, + ns_log_maint, + ns_log_load, + ns_log_resp_checks, + ns_log_max_category +} ns_logging_categories; + +typedef struct log_config { + log_context log_ctx; + log_channel eventlib_channel; + log_channel packet_channel; + int default_debug_active; +} *log_config; + +struct map { + char * token; + int val; +}; + +#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? */ + +#define VQEXPIRY 900 /* a VQ entry expires in 15*60 = 900 seconds */ + +#ifdef BIND_UPDATE +enum req_action { Finish, Refuse, Return }; +#endif + +#ifdef BIND_NOTIFY +typedef enum { + notify_info_waitfor, notify_info_delay, notify_info_error, + notify_info_done +} notify_info_state; + +typedef struct notify_info { + char *name; + int class; + notify_info_state state; + evWaitID wait_id; + evTimerID timer_id; + LINK(struct notify_info) link; +} *notify_info; + +typedef LIST(struct notify_info) notify_info_list; +#endif /* BIND_NOTIFY */ + +#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/contrib/bind/bin/named/ns_forw.c b/contrib/bind/bin/named/ns_forw.c new file mode 100644 index 0000000..9c72825 --- /dev/null +++ b/contrib/bind/bin/named/ns_forw.c @@ -0,0 +1,1132 @@ +#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 8.34 1998/02/24 01:02:40 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +struct complaint { + u_long tag1, tag2; + time_t expire; + struct complaint *next; +}; + +static struct complaint *complaints = NULL; +static int retry_timer_set = 0; + +/* + * 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 memory allocation 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(struct databuf *nsp[], u_char *msg, int msglen, + struct sockaddr_in from, struct qstream *qsp, int dfd, + struct qinfo **qpp, const char *dname, int class, int type, + struct namebuf *np, int use_tcp) +{ + struct qinfo *qp; + char tmpdomain[MAXDNAME]; + struct sockaddr_in *nsa; + HEADER *hp; + u_int16_t id; + int n; + + ns_debug(ns_log_default, 3, "ns_forw()"); + + hp = (HEADER *) msg; + id = hp->id; + /* Look at them all */ + for (qp = nsqhead; qp != NULL; qp = qp->q_link) { + if (qp->q_id == id && + memcmp(&qp->q_from, &from, sizeof qp->q_from) == 0 && + ((qp->q_cmsglen == 0 && qp->q_msglen == msglen && + memcmp(qp->q_msg + 2, msg + 2, msglen - 2) == 0) || + (qp->q_cmsglen == msglen && + memcmp(qp->q_cmsg + 2, msg + 2, msglen - 2) == 0) + )) { + ns_debug(ns_log_default, 3, "forw: dropped DUP id=%d", + ntohs(id)); + nameserIncr(from.sin_addr, nssRcvdDupQ); + return (FW_DUP); + } + } + + qp = qnew(dname, class, type); + getname(np, tmpdomain, sizeof tmpdomain); + qp->q_domain = savestr(tmpdomain, 1); + qp->q_from = from; /* nslookup wants to know this */ + n = nslookup(nsp, qp, dname, "ns_forw"); + if (n < 0) { + ns_debug(ns_log_default, 2, "forw: nslookup reports danger"); + ns_freeqry(qp); + return (FW_SERVFAIL); + } + if (n == 0 && !server_options->fwdtab) { + ns_debug(ns_log_default, 2, "forw: no nameservers found"); + ns_freeqry(qp); + return (FW_NOSERVER); + } + qp->q_stream = qsp; + qp->q_curaddr = 0; + qp->q_fwd = server_options->fwdtab; + qp->q_dfd = dfd; + qp->q_id = id; + qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; + if (use_tcp) + qp->q_flags |= Q_USEVC; + hp->id = qp->q_nsid = htons(nsid_next()); + hp->ancount = htons(0); + hp->nscount = htons(0); + hp->arcount = htons(0); + if ((qp->q_msg = (u_char *)memget((unsigned)msglen)) == NULL) { + ns_notice(ns_log_default, "forw: memget: %s", + strerror(errno)); + ns_freeqry(qp); + return (FW_SERVFAIL); + } + qp->q_msgsize = msglen; + memcpy(qp->q_msg, msg, qp->q_msglen = msglen); + if (!qp->q_fwd) { + hp->rd = 0; + qp->q_addr[0].stime = tt; + } + +#ifdef SLAVE_FORWARD + if (NS_OPTION_P(OPTION_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); + ns_debug(ns_log_default, 1, + "forw: forw -> [%s].%d ds=%d nsid=%d id=%d %dms retry %dsec", + 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, + (int)(qp->q_time - tt.tv_sec)); +#ifdef DEBUG + if (debug >= 10) + fp_nquery(msg, msglen, log_get_stream(packet_channel)); +#endif + if (qp->q_flags & Q_USEVC) { + if (tcp_send(qp) != NOERROR) { + if (!haveComplained(ina_ulong(nsa->sin_addr), + (u_long)tcpsendStr)) + ns_info(ns_log_default, + "ns_forw: tcp_send(%s) failed: %s", + sin_ntoa(*nsa), strerror(errno)); + } + } else if (sendto(ds, (char *)msg, msglen, 0, (struct sockaddr *)nsa, + sizeof(struct sockaddr_in)) < 0) { + if (!haveComplained(ina_ulong(nsa->sin_addr), + (u_long)sendtoStr)) + ns_info(ns_log_default, "ns_forw: sendto(%s): %s", + sin_ntoa(*nsa), strerror(errno)); + nameserIncr(nsa->sin_addr, nssSendtoErr); + } + if (NS_OPTION_P(OPTION_HOSTSTATS)) + nameserIncr(from.sin_addr, nssRcvdFwdQ); + nameserIncr(nsa->sin_addr, nssSentFwdQ); + if (qpp) + *qpp = qp; + hp->rd = 1; + return (0); +} + +/* haveComplained(tag1, tag2) + * check to see if we have complained about (tag1,tag2) recently + * returns: + * boolean: have we complained recently? + * side-effects: + * outdated complaint records removed from our static list + * author: + * Paul Vixie (DECWRL) April 1991 + */ +int +haveComplained(u_long tag1, u_long tag2) { + struct complaint *cur, *next, *prev; + int r = 0; + + for (cur = complaints, prev = NULL; + cur != NULL; + prev = cur, cur = next) { + next = cur->next; + if (tt.tv_sec > cur->expire) { + if (prev) + prev->next = next; + else + complaints = next; + memput(cur, sizeof *cur); + cur = prev; + } else if (tag1 == cur->tag1 && tag2 == cur->tag2) + r++; + } + if (!r) { + cur = (struct complaint *)memget(sizeof(struct complaint)); + if (cur) { + cur->tag1 = tag1; + cur->tag2 = tag2; + cur->expire = tt.tv_sec + INIT_REFRESH; /* "10:00" */ + cur->next = NULL; + if (prev) + prev->next = cur; + else + complaints = cur; + } + } + return (r); +} + +void +freeComplaints(void) { + struct complaint *cur, *next; + + for (cur = complaints; cur != NULL; cur = next) { + next = cur->next; + memput(cur, sizeof *cur); + } + complaints = NULL; +} + +/* 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. + */ +static void +nslookupComplain(const char *sysloginfo, const char *queryname, + const char *complaint, const char *dname, + const struct databuf *a_rr, const struct databuf *nsdp) +{ + char *a, *ns; + const char *a_type; + int print_a; + + ns_debug(ns_log_default, 2, "NS '%s' %s", dname, complaint); + if (sysloginfo && queryname && !haveComplained((u_long)queryname, + (u_long)complaint)) { + char buf[999]; + + a = ns = (char *)NULL; + print_a = (a_rr->d_type == T_A); + a_type = p_type(a_rr->d_type); + if (a_rr->d_rcode) { + print_a = 0; + switch(a_rr->d_rcode) { + case NXDOMAIN: + a_type = "NXDOMAIN"; + break; + case NOERROR_NODATA: + a_type = "NODATA"; + break; + } + } + if (NS_OPTION_P(OPTION_HOSTSTATS)) { + char nsbuf[20], abuf[20]; + + if (nsdp != NULL) { + if (nsdp->d_ns != NULL) { + strcpy(nsbuf, + inet_ntoa(nsdp->d_ns->addr)); + ns = nsbuf; + } else { + ns = zones[nsdp->d_zone].z_origin; + } + } + if (a_rr->d_ns != NULL) { + strcpy(abuf, inet_ntoa(a_rr->d_ns->addr)); + a = abuf; + } else { + a = zones[a_rr->d_zone].z_origin; + } + } + if (a != NULL || ns != NULL) + ns_info(ns_log_default, + "%s: query(%s) %s (%s:%s) learnt (%s=%s:NS=%s)", + sysloginfo, queryname, + complaint, dname, + print_a ? + inet_ntoa(ina_get(a_rr->d_data)) : "", + a_type, + a ? a : "<Not Available>", + ns ? ns : "<Not Available>" ); + else + ns_info(ns_log_default, "%s: query(%s) %s (%s:%s)", + sysloginfo, queryname, + complaint, dname, + print_a ? + inet_ntoa(ina_get(a_rr->d_data)) : ""); + } +} + +/* + * 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: + * logs if a dangerous situation is detected and + * (syslogdname && sysloginfo) + */ +int +nslookup(struct databuf *nsp[], struct qinfo *qp, + const char *syslogdname, const char *sysloginfo) +{ + struct namebuf *np; + struct databuf *dp, *nsdp; + struct qserv *qs; + int n; + u_int i; + struct hashbuf *tmphtp; + char *dname; + const char *fname; + int oldn, naddr, class, found_arr, potential_ns; + time_t curtime; + + ns_debug(ns_log_default, 3, "nslookup(nsp=%#x, qp=%#x, \"%s\")", + nsp, qp, syslogdname); + + potential_ns = 0; + naddr = n = qp->q_naddr; + curtime = (u_long) tt.tv_sec; + while ((nsdp = *nsp++) != NULL) { + class = nsdp->d_class; + dname = (char *)nsdp->d_data; + ns_debug(ns_log_default, 3, + "nslookup: NS \"%s\" c=%d t=%d (flags 0x%lu)", + dname, class, nsdp->d_type, (u_long)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) { + ns_debug(ns_log_default, 2, + "skipping used NS w/name %s", + nsdp->d_data); + goto skipserver; + } + } + + tmphtp = ((nsdp->d_flags & DB_F_HINT) ?fcachetab :hashtab); + np = nlookup(dname, &tmphtp, &fname, 1); + if (np == NULL) { + ns_debug(ns_log_default, 3, "%s: not found %s %#x", + dname, fname, np); + found_arr = 0; + goto need_sysquery; + } + if (fname != dname) { + if (findMyZone(np, class) == DB_Z_CACHE) { + /* + * lifted from findMyZone() + * We really need to know if the NS + * is the bottom of one of our zones + * to see if we've got missing glue + */ + for (; np; np = np_parent(np)) + for (dp = np->n_data; dp; dp = dp->d_next) + if (match(dp, class, T_NS)) { + if (dp->d_rcode) + break; + if (dp->d_zone) { + static char *complaint = + "Glue A RR missing"; + nslookupComplain(sysloginfo, + syslogdname, + complaint, + dname, dp, + nsdp); + goto skipserver; + } else { + found_arr = 0; + goto need_sysquery; + } + } + /* shouldn't happen, but ... */ + found_arr = 0; + goto need_sysquery; + } else { + /* Authoritative A RR missing. */ + continue; + } + } + found_arr = 0; + oldn = n; + + /* look for name server addresses */ + delete_stale(np); + 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) { + static const char *complaint = + "NS points to CNAME"; + if (dp->d_rcode) + continue; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp, nsdp); + goto skipserver; + } + if (dp->d_type != T_A || dp->d_class != class) + continue; + if (dp->d_rcode) { + static const char *complaint = + "A RR negative cache entry"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp, nsdp); + goto skipserver; + } + if (ina_hlong(ina_get(dp->d_data)) == INADDR_ANY) { + static const char *complaint = + "Bogus (0.0.0.0) A RR"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp, nsdp); + continue; + } +#ifdef INADDR_LOOPBACK + if (ina_hlong(ina_get(dp->d_data))==INADDR_LOOPBACK) { + static const char *complaint = + "Bogus LOOPBACK A RR"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp, nsdp); + continue; + } +#endif +#ifdef INADDR_BROADCAST + if (ina_hlong(ina_get(dp->d_data))==INADDR_BROADCAST){ + static const char *complaint = + "Bogus BROADCAST A RR"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp, nsdp); + continue; + } +#endif +#ifdef IN_MULTICAST + if (IN_MULTICAST(ina_hlong(ina_get(dp->d_data)))) { + static const char *complaint = + "Bogus MULTICAST A RR"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp, nsdp); + 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 == DB_Z_CACHE) && + (dp->d_ttl < (u_int32_t)curtime) && + !(dp->d_flags & DB_F_HINT) ) + { + ns_debug(ns_log_default, 1, + "nslookup: stale '%s'", + NAME(*np)); + n = oldn; + found_arr = 0; + goto need_sysquery; + } + + found_arr++; + nsa = ina_get(dp->d_data); + /* don't put in duplicates */ + qs = qp->q_addr; + for (i = 0; i < (u_int)n; i++, qs++) + if (ina_equal(qs->ns_addr.sin_addr, nsa)) + 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 this A RR has no RTT, initialize its RTT to a + * small random value. + */ + if (dp->d_nstime == 0) + dp->d_nstime = 1 + + (int)(25.0*rand()/(RAND_MAX + 1.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) + * + * try to limp along instead of denying service + * gdonl mar96 + */ + if (aIsUs(nsa)) { + static char *complaint = "contains our address"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp, nsdp); + continue; + } + /* + * 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) + * + * try to limp along instead of denying service + * gdonl mar96 + */ + if (memcmp(&qp->q_from, &qs->ns_addr, + sizeof(qp->q_from)) == 0) + { + static char *complaint = "forwarding loop"; + nslookupComplain(sysloginfo, syslogdname, + complaint, dname, dp, nsdp); + continue; + } +#ifdef BOGUSNS + /* + * 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 (ip_match_address(bogus_nameservers, nsa) > 0) + goto skipserver; +#endif + + n++; + if (n >= NSMAX) + goto out; + skipaddr: + (void)NULL; + } + ns_debug(ns_log_default, 8, "nslookup: %d ns addrs", n); + need_sysquery: + if (found_arr == 0) { + potential_ns++; + if (!(qp->q_flags & Q_SYSTEM)) + (void) sysquery(dname, class, T_A, NULL, 0, + QUERY); + } + skipserver: + (void)NULL; + } + out: + ns_debug(ns_log_default, 3, "nslookup: %d ns addrs total", n); + qp->q_naddr = n; + if (n == 0 && potential_ns == 0 && !server_options->fwdtab) { + static char *complaint = "No possible A RRs"; + if (sysloginfo && syslogdname && + !haveComplained((u_long)syslogdname, (u_long)complaint)) + { + ns_info(ns_log_default, "%s: query(%s) %s", + sysloginfo, syslogdname, complaint); + } + return(-1); + } + /* Update the refcounts before the sort. */ + for (i = naddr; i < (u_int)n; i++) { + DRCNTINC(qp->q_addr[i].nsdata); + DRCNTINC(qp->q_addr[i].ns); + } + if (n > 1) { + qsort((char *)qp->q_addr, n, sizeof(struct qserv), + (int (*)(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(struct qserv *qs1, struct qserv *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; + +#ifdef DEBUG + if (debug >= 10) { + char a1[sizeof "255.255.255.255"], + a2[sizeof "255.255.255.255"]; + + strcpy(a1, inet_ntoa(qs1->ns_addr.sin_addr)); + strcpy(a2, inet_ntoa(qs2->ns_addr.sin_addr)); + ns_debug(ns_log_default, 10, + "qcomp(%s, %s) %lu (%lu) - %lu (%lu) = %lu", + a1, a2, + rtt1, RTTROUND(rtt1), + rtt2, RTTROUND(rtt2), + rtt1 - rtt2); + } +#endif + if (RTTROUND(rtt1) == RTTROUND(rtt2)) { + pos1 = distance_of_address(server_options->topology, + qs1->ns_addr.sin_addr); + pos2 = distance_of_address(server_options->topology, + qs2->ns_addr.sin_addr); + pdiff = pos1 - pos2; + ns_debug(ns_log_default, 10, "\tpos1=%d, pos2=%d", pos1, pos2); + if (pdiff) + return (pdiff); + } + 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(struct qinfo *qp, time_t t) { + struct qinfo *qp1, *qp2; + + ns_debug(ns_log_default, 4, "schedretry(%#x, %ld sec)", qp, (long)t); + if (qp->q_time) + ns_debug(ns_log_default, 4, + "WARNING: schedretry(%#lx, %ld) q_time already %ld", + (u_long)qp, (long)t, (long)qp->q_time); + gettime(&tt); + t += (u_long) tt.tv_sec; + qp->q_time = t; + + if ((qp1 = retryqp) == NULL) { + retryqp = qp; + qp->q_next = NULL; + goto done; + } + if (t < qp1->q_time) { + qp->q_next = qp1; + retryqp = qp; + goto done; + } + while ((qp2 = qp1->q_next) != NULL && qp2->q_time < t) + qp1 = qp2; + qp1->q_next = qp; + qp->q_next = qp2; + done: + reset_retrytimer(); +} + +/* + * Unsched is called to remove a forwarded query entry. + */ +void +unsched(struct qinfo *qp) { + struct qinfo *np; + + ns_debug(ns_log_default, 3, "unsched(%#lx, %d)", + (u_long)qp, ntohs(qp->q_id)); + if (retryqp == qp) { + retryqp = qp->q_next; + } else { + for (np = retryqp; np->q_next != NULL; np = np->q_next) { + if (np->q_next != qp) + continue; + np->q_next = qp->q_next; /* dequeue */ + break; + } + } + qp->q_next = NULL; /* sanity check */ + qp->q_time = 0; + reset_retrytimer(); +} + +void +reset_retrytimer() { + static evTimerID id; + + if (retry_timer_set) { + (void) evClearTimer(ev, id); + retry_timer_set = 0; + } + + if (retryqp) { + evSetTimer(ev, retrytimer, NULL, + evConsTime(retryqp->q_time, 0), + evConsTime(0, 0), &id); + retry_timer_set = 1; + } else + memset(&id, 0, sizeof id); +} + +void +retrytimer(evContext ctx, void *uap, struct timespec due, + struct timespec ival) { + retry_timer_set = 0; + retry(retryqp); +} + +/* + * Retry is called to retransmit query 'qp'. + */ +void +retry(struct qinfo *qp) { + int n; + HEADER *hp; + struct sockaddr_in *nsa; + + ns_debug(ns_log_default, 3, "retry(%#lx) id=%d", (u_long)qp, + ntohs(qp->q_id)); + + if (qp->q_msg == NULL) { /* XXX - why? */ + qremove(qp); + return; + } + + if (qp->q_expire < tt.tv_sec) { + ns_debug(ns_log_default, 1, + "retry(%#lx): expired @ %lu (%d secs before now (%lu))", + (u_long)qp, (u_long)qp->q_expire, + (int)(tt.tv_sec - qp->q_expire), + (u_long)tt.tv_sec); + if (qp->q_stream || (qp->q_flags & Q_PRIMING)) + goto fail; + qremove(qp); + return; + } + + /* try next address */ + n = qp->q_curaddr; + if (qp->q_fwd != NULL) { + qp->q_fwd = qp->q_fwd->next; + if (qp->q_fwd != NULL) + goto found; + /* Out of forwarders, try direct queries. */ + } + if (qp->q_naddr > 0) { + ++qp->q_addr[n].nretry; + if (!NS_OPTION_P(OPTION_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 */ + if (qp->q_expire < tt.tv_sec) { + /* + * The query has expired. Reset it and retry from + * the beginning. + */ + hp->rcode = NOERROR; + hp->qr = hp->aa = 0; + qp->q_fwd = server_options->fwdtab; + for (n = 0; n < (int)qp->q_naddr; n++) + qp->q_addr[n].nretry = 0; + n = 0; + qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; + goto found; + } + /* + * The query hasn't expired yet; it probably ran out + * of servers or forwarders. Wait up to 60 seconds + * past the expire time. + */ + unsched(qp); + schedretry(qp, (time_t)(qp->q_expire - tt.tv_sec + 60)); + return; + } + ns_debug(ns_log_default, 5, "give up"); + n = ((HEADER *)qp->q_cmsg ? qp->q_cmsglen : qp->q_msglen); + hp->id = qp->q_id; + hp->qr = 1; + hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); + hp->rd = 1; + hp->rcode = SERVFAIL; +#ifdef DEBUG + if (debug >= 10) + fp_nquery(qp->q_msg, n, log_get_stream(packet_channel)); +#endif + if (send_msg((u_char *)hp, n, qp)) { + ns_debug(ns_log_default, 1, + "gave up retry(%#lx) nsid=%d id=%d", + (u_long)qp, ntohs(qp->q_nsid), ntohs(qp->q_id)); + } + if (NS_OPTION_P(OPTION_HOSTSTATS)) + 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); + ns_debug(ns_log_default, 1, + "%s(addr=%d n=%d) -> [%s].%d ds=%d nsid=%d id=%d %dms", + (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_nquery(qp->q_msg, qp->q_msglen, + log_get_stream(packet_channel)); +#endif + if (qp->q_flags & Q_USEVC) { + if (tcp_send(qp) != NOERROR) + ns_debug(ns_log_default, 3, + "error resending tcp msg: %s", + strerror(errno)); + } else if (sendto(ds, (char*)qp->q_msg, qp->q_msglen, 0, + (struct sockaddr *)nsa, + sizeof(struct sockaddr_in)) < 0) + { + ns_debug(ns_log_default, 3, "error resending msg: %s", + strerror(errno)); + } + hp->rd = 1; /* leave set to 1 for dup detection */ + nameserIncr(nsa->sin_addr, nssSentDupQ); + unsched(qp); +#ifdef SLAVE_FORWARD + if (NS_OPTION_P(OPTION_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(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 */ + ns_debug(ns_log_default, 3, + "retrytime: nstime%ldms t%ld nretry%ld u%ld : v%ld", + ns->nsdata ? (long)(ns->nsdata->d_nstime / 1000) : (long)-1, + (long)t, (long)ns->nretry, (long)u, (long)v); + return (v); +} + +void +qflush() { + while (nsqhead) + qremove(nsqhead); + nsqhead = NULL; + priming = 0; +} + +void +qremove(struct qinfo *qp) { + struct sockaddr_in empty_from; + + empty_from.sin_family = AF_INET; + empty_from.sin_addr.s_addr = htonl(INADDR_ANY); + empty_from.sin_port = htons(0); + + ns_debug(ns_log_default, 3, "qremove(%#lx)", (u_long)qp); + + if (qp->q_flags & Q_ZSERIAL) + qserial_answer(qp, 0, empty_from); + unsched(qp); + ns_freeqry(qp); +} + +struct qinfo * +qfindid(u_int16_t id) { + struct qinfo *qp; + + for (qp = nsqhead; qp != NULL; qp = qp->q_link) + if (qp->q_nsid == id) + break; + ns_debug(ns_log_default, 3, "qfindid(%d) -> %#lx", ntohs(id), + (u_long)qp); + return (qp); +} + +struct qinfo * +qnew(const char *name, int class, int type) { + struct qinfo *qp; + + qp = (struct qinfo *)memget(sizeof *qp); + if (qp == NULL) + panic("qnew: memget failed", NULL); + memset(qp, 0, sizeof *qp); + ns_debug(ns_log_default, 5, "qnew(%#lx)", (u_long)qp); +#ifdef BIND_NOTIFY + qp->q_notifyzone = DB_Z_CACHE; +#endif + qp->q_link = nsqhead; + nsqhead = qp; + qp->q_name = savestr(name, 1); + qp->q_class = (u_int16_t)class; + qp->q_type = (u_int16_t)type; + qp->q_flags = 0; + return (qp); +} + +void +ns_freeqns(struct qinfo *qp, char *where) { + static const char freed[] = "freed", busy[] = "busy"; + const char *result; + struct databuf *dp; + int i; + + for (i = 0 ; i < (int)qp->q_naddr ; i++) { + dp = qp->q_addr[i].ns; + if (dp) { + DRCNTDEC(dp); + result = (dp->d_rcnt) ? busy : freed; + ns_debug(ns_log_default, 3, "%s: ns %s rcnt %d (%s)", + where, dp->d_data, dp->d_rcnt, result); + if (result == freed) + db_freedata(dp); + } + dp = qp->q_addr[i].nsdata; + if (dp) { + DRCNTDEC(dp); + result = (dp->d_rcnt) ? busy : freed; + ns_debug(ns_log_default, 3, + "%s: nsdata %s rcnt %d (%s)", + where, inet_ntoa(ina_get(dp->d_data)), + dp->d_rcnt, result); + if (result == freed) + db_freedata(dp); + } + } +} + +void +ns_freeqry(struct qinfo *qp) { + struct qinfo *np; + struct databuf *dp; + + ns_debug(ns_log_default, 3, "ns_freeqry(%#lx)", (u_long)qp); + if (qp->q_next) + ns_debug(ns_log_default, 1, + "WARNING: ns_freeqry of linked ptr %#lx", (u_long)qp); + if (qp->q_msg != NULL) + memput(qp->q_msg, qp->q_msgsize); + if (qp->q_cmsg != NULL) + memput(qp->q_cmsg, qp->q_cmsgsize); + if (qp->q_domain != NULL) + freestr(qp->q_domain); + if (qp->q_name != NULL) + freestr(qp->q_name); + ns_freeqns(qp, "ns_freeqry"); + if (nsqhead == qp) + nsqhead = qp->q_link; + else { + for(np = nsqhead; + np->q_link != NULL; + np = np->q_link) { + if (np->q_link != qp) + continue; + np->q_link = qp->q_link; /* dequeue */ + break; + } + } + memput(qp, sizeof *qp); +} diff --git a/contrib/bind/bin/named/ns_func.h b/contrib/bind/bin/named/ns_func.h new file mode 100644 index 0000000..bf58528 --- /dev/null +++ b/contrib/bind/bin/named/ns_func.h @@ -0,0 +1,380 @@ +/* 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. + */ + +/* Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* ns_func.h - declarations for ns_*.c's externally visible functions + * + * $Id: ns_func.h,v 8.43 1998/03/20 00:53:44 halley Exp $ + */ + +/* ++from ns_glue.c++ */ +extern struct in_addr ina_get(const u_char *data); +extern const char *sin_ntoa(struct sockaddr_in); +extern void ns_debug(int, int, const char *, ...), + ns_info(int, const char *, ...), + ns_notice(int, const char *, ...), + ns_warning(int, const char *, ...), + ns_error(int, const char *, ...), + ns_panic(int, int, const char *, ...), + ns_assertion_failed(char *file, int line, + assertion_type type, char *cond, + int print_errno); +extern void panic(const char *, const void *), + gettime(struct timeval *); +extern int nlabels(const char *), + my_close(int), + my_fclose(FILE *); +extern void __freestr(char *); +extern char *__newstr(size_t, int), + *__savestr(const char *, int), + *checked_ctime(const time_t *t), + *ctimel(long); +extern u_char *ina_put(struct in_addr ina, u_char *data), + *savebuf(const u_char *, size_t, int); +extern void dprintf(int level, const char *format, ...); +#ifdef DEBUG_STRINGS +extern char *debug_newstr(size_t, int, const char *, int), + *debug_savestr(const char *, int, const char *, int); +extern void debug_freestr(char *, const char *, int); +#define newstr(l, n) debug_newstr((l), (n), __FILE__, __LINE__) +#define savestr(s, n) debug_savestr((s), (n), __FILE__, __LINE__) +#define freestr(s) debug_freestr((s), __FILE__, __LINE__) +#else +#define newstr(l, n) __newstr((l), (n)) +#define savestr(s, n) __savestr((s), (n)) +#define freestr(s) __freestr((s)) +#endif /* DEBUG_STRINGS */ +/* --from ns_glue.c-- */ + +/* ++from ns_resp.c++ */ +extern void ns_resp(u_char *, int, struct sockaddr_in, + struct qstream *), + prime_cache(void), + delete_all(struct namebuf *, int, int), + delete_stale(struct namebuf *); +extern struct qinfo *sysquery(const char *, int, int, + struct in_addr *, int, int); +extern void sysnotify(const char *, int, int); +extern int doupdate(u_char *, u_char *, struct databuf **, + int, int, int, u_int, struct sockaddr_in), + send_msg(u_char *, int, struct qinfo *), + findns(struct namebuf **, int, + struct databuf **, int *, int), + finddata(struct namebuf *, int, int, HEADER *, + char **, int *, int *), + wanted(const struct databuf *, int, int), + add_data(struct namebuf *, + struct databuf **, + u_char *, int, int *), + trunc_adjust(u_char *, int, int); +/* --from ns_resp.c-- */ + +/* ++from ns_req.c++ */ +extern void ns_req(u_char *, int, int, + struct qstream *, + struct sockaddr_in, + int), + free_addinfo(void), + free_nsp(struct databuf **); +extern int stale(struct databuf *), + make_rr(const char *, struct databuf *, + u_char *, int, int, + u_char **, u_char **), + doaddinfo(HEADER *, u_char *, int), + doaddauth(HEADER *, u_char *, int, + struct namebuf *, + struct databuf *); +#ifdef BIND_NOTIFY +extern int findZonePri(const struct zoneinfo *, + const struct sockaddr_in); +#endif +/* --from ns_req.c-- */ + +/* ++from ns_xfr.c++ */ +extern void ns_xfr(struct qstream *qsp, struct namebuf *znp, + int zone, int class, int type, + int id, int opcode), + ns_stopxfrs(struct zoneinfo *), + ns_freexfr(struct qstream *); +/* --from ns_xfr.c-- */ + +/* ++from ns_forw.c++ */ +extern time_t retrytime(struct qinfo *); +extern int ns_forw(struct databuf *nsp[], + u_char *msg, + int msglen, + struct sockaddr_in from, + struct qstream *qsp, + int dfd, + struct qinfo **qpp, + const char *dname, + int class, + int type, + struct namebuf *np, + int use_tcp), + haveComplained(u_long, u_long), + nslookup(struct databuf *nsp[], + struct qinfo *qp, + const char *syslogdname, + const char *sysloginfo), + qcomp(struct qserv *, struct qserv *); +extern void schedretry(struct qinfo *, time_t), + unsched(struct qinfo *), + reset_retrytimer(void), + retrytimer(evContext ctx, void *uap, + struct timespec due, struct timespec ival), + retry(struct qinfo *), + qflush(void), + qremove(struct qinfo *), + ns_freeqns(struct qinfo *, char *), + ns_freeqry(struct qinfo *), + freeComplaints(void); +extern struct qinfo *qfindid(u_int16_t), + *qnew(const char *, int, int); +/* --from ns_forw.c-- */ + +/* ++from ns_main.c++ */ +extern struct in_addr net_mask(struct in_addr); +extern void sq_remove(struct qstream *), + sq_flushw(struct qstream *), + sq_flush(struct qstream *allbut), + dq_remove_gen(time_t gen), + dq_remove_all(), + sq_done(struct qstream *), + ns_setproctitle(char *, int), + getnetconf(int), + nsid_init(void), + ns_setoption(int option), + writestream(struct qstream *, const u_char *, int), + ns_need(int need), + opensocket_f(void); +extern u_int16_t nsid_next(void); +extern int sq_openw(struct qstream *, int), + sq_writeh(struct qstream *, sq_closure), + sq_write(struct qstream *, const u_char *, int), + ns_need_p(int option), + tcp_send(struct qinfo *), + aIsUs(struct in_addr); +/* --from ns_main.c-- */ + +/* ++from ns_maint.c++ */ +extern void ns_maint(void), + zone_maint(struct zoneinfo *), + sched_zone_maint(struct zoneinfo *), + ns_cleancache(evContext ctx, void *uap, + struct timespec due, + struct timespec inter), + purge_zone(const char *, struct hashbuf *, int), + loadxfer(void), + qserial_retrytime(struct zoneinfo *, time_t), + qserial_query(struct zoneinfo *), + qserial_answer(struct qinfo *, u_int32_t, + struct sockaddr_in), +#ifdef DEBUG + printzoneinfo(int, int, int), +#endif + endxfer(void), + ns_reload(void); +extern int clean_cache(struct hashbuf *, int); +extern void reapchild(evContext, void *, int); +extern const char * zoneTypeString(const struct zoneinfo *); +/* --from ns_maint.c-- */ + +/* ++from ns_init.c++ */ +extern void ns_refreshtime(struct zoneinfo *, time_t), + ns_retrytime(struct zoneinfo *, time_t), + ns_init(const char *); +extern enum context ns_ptrcontext(const char *owner); +extern enum context ns_ownercontext(int type, enum transport); +extern int ns_nameok(const char *name, int class, + struct zoneinfo *zp, + enum transport, enum context, + const char *owner, + struct in_addr source); +extern int ns_wildcard(const char *name); +extern void zoneinit(struct zoneinfo *), + do_reload(const char *, int, int), + ns_shutdown(void); +/* --from ns_init.c-- */ + +/* ++from ns_ncache.c++ */ +extern void cache_n_resp(u_char *, int, struct sockaddr_in); +/* --from ns_ncache.c-- */ + +/* ++from ns_udp.c++ */ +extern void ns_udp(void); +/* --from ns_udp.c-- */ + +/* ++from ns_stats.c++ */ +extern void ns_stats(void), + ns_freestats(void); +extern void ns_logstats(evContext ctx, void *uap, + struct timespec, struct timespec); +extern void qtypeIncr(int qtype); +extern struct nameser *nameserFind(struct in_addr addr, int flags); +#define NS_F_INSERT 0x0001 +#define nameserIncr(a,w) NS_INCRSTAT(a,w) /* XXX should change name. */ +/* --from ns_stats.c-- */ + +/* ++from ns_update.c++ */ +u_char *findsoaserial(u_char *data); +u_int32_t get_serial_unchecked(struct zoneinfo *zp); +u_int32_t get_serial(struct zoneinfo *zp); +void set_serial(struct zoneinfo *zp, u_int32_t serial); +int schedule_soa_update(struct zoneinfo *, int); +int schedule_dump(struct zoneinfo *); +int incr_serial(struct zoneinfo *zp); +int merge_logs(struct zoneinfo *zp); +int zonedump(struct zoneinfo *zp); +void dynamic_about_to_exit(void); +enum req_action req_update(HEADER *hp, u_char *cp, u_char *eom, + u_char *msg, struct qstream *qsp, + int dfd, struct sockaddr_in from); +void rdata_dump(struct databuf *dp, FILE *fp); +/* --from ns_update.c-- */ + +/* ++from ns_config.c++ */ +void free_zone_timerinfo(struct zoneinfo *); +void free_zone_contents(struct zoneinfo *, int); +struct zoneinfo *find_zone(const char *, int, int); +zone_config begin_zone(char *, int); +void end_zone(zone_config, int); +int set_zone_type(zone_config, int); +int set_zone_filename(zone_config, char *); +int set_zone_checknames(zone_config, enum severity); +int set_zone_notify(zone_config, int value); +int set_zone_update_acl(zone_config, ip_match_list); +int set_zone_query_acl(zone_config, ip_match_list); +int set_zone_transfer_acl(zone_config, ip_match_list); +int set_zone_transfer_source(zone_config, struct in_addr); +int set_zone_transfer_time_in(zone_config, long); +int add_zone_master(zone_config, struct in_addr); +int add_zone_notify(zone_config, struct in_addr); +options new_options(void); +void free_options(options); +void set_boolean_option(options, int, int); +listen_info_list new_listen_info_list(void); +void free_listen_info_list(listen_info_list); +void add_listen_on(options, u_int16_t, ip_match_list); +FILE * write_open(char *filename); +void update_pid_file(void); +void set_options(options, int); +void use_default_options(void); +ip_match_list new_ip_match_list(void); +void free_ip_match_list(ip_match_list); +ip_match_element new_ip_match_pattern(struct in_addr, u_int); +ip_match_element new_ip_match_mask(struct in_addr, struct in_addr); +ip_match_element new_ip_match_indirect(ip_match_list); +ip_match_element new_ip_match_localhost(void); +ip_match_element new_ip_match_localnets(void); +void ip_match_negate(ip_match_element); +void add_to_ip_match_list(ip_match_list, ip_match_element); +void dprint_ip_match_list(int, ip_match_list, int, char *, + char *); +int ip_match_address(ip_match_list, struct in_addr); +int ip_address_allowed(ip_match_list, struct in_addr); +int ip_match_network(ip_match_list, struct in_addr, + struct in_addr); +int distance_of_address(ip_match_list, struct in_addr); +int ip_match_is_none(ip_match_list); +void add_forwarder(options, struct in_addr); +void free_forwarders(struct fwdinfo *); +server_info find_server(struct in_addr); +server_config begin_server(struct in_addr); +void end_server(server_config, int); +void set_server_option(server_config, int, int); +void set_server_transfers(server_config, int); +void set_server_transfer_format(server_config, + enum axfr_format); +void add_server_key_info(server_config, key_info); +key_info new_key_info(char *, char *, char *); +void free_key_info(key_info); +void dprint_key_info(key_info); +key_info_list new_key_info_list(void); +void free_key_info_list(key_info_list); +void add_to_key_info_list(key_info_list, key_info); +void dprint_key_info_list(key_info_list); +log_config begin_logging(void); +void add_log_channel(log_config, int, log_channel); +void open_special_channels(void); +void set_logging(log_config, int); +void end_logging(log_config, int); +void use_default_logging(void); +void init_logging(void); +void shutdown_logging(void); +void init_configuration(void); +void shutdown_configuration(void); +void load_configuration(const char *); +/* --from ns_config.c-- */ +/* ++from parser.y++ */ +ip_match_list lookup_acl(char *); +void define_acl(char *, ip_match_list); +key_info lookup_key(char *); +void define_key(char *, key_info); +void parse_configuration(const char *); +void parser_initialize(void); +void parser_shutdown(void); +/* --from parser.y-- */ diff --git a/contrib/bind/bin/named/ns_glob.h b/contrib/bind/bin/named/ns_glob.h new file mode 100644 index 0000000..35f3041 --- /dev/null +++ b/contrib/bind/bin/named/ns_glob.h @@ -0,0 +1,317 @@ +/* + * from ns.h 4.33 (Berkeley) 8/23/90 + * $Id: ns_glob.h,v 8.34 1998/03/20 01:18:07 halley Exp $ + */ + +/* 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. + */ + +/* Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Global variables for the name server. + */ + +#ifdef DEBUG +DECL int debug INIT(0); +DECL int desired_debug INIT(0); +#endif + + /* global event context */ +DECL evContext ev; + + /* list of open streams */ +DECL struct qstream *streamq; + + /* often set to the current time */ +DECL struct timeval tt; + + /* head of allocated queries */ +DECL struct qinfo *nsqhead; + + /* datagram socket for sysquery() and ns_forw(). */ +DECL int ds INIT(-1); + + /* event ID for reads of "ds". */ +DECL evFileID ds_evID; + +#ifdef QRYLOG + /* is query logging turned on? */ +DECL int qrylog; +#endif /*QRYLOG*/ + + /* port to which we send queries */ +DECL u_int16_t ns_port; + + /* Source addr of our internal resolver. */ +DECL struct sockaddr_in source_addr; /* INITs to <INADDR_ANY, 0>. */ + + /* Used by ns_stats */ +DECL time_t boottime; + +DECL time_t resettime; + + /* next query to retry */ +DECL struct qinfo *retryqp; + + /* default configuration file */ +DECL char *conffile INIT(NULL); + + /* default debug output file */ +DECL const char *debugfile INIT(_PATH_DEBUG); + + /* zone information */ +DECL struct zoneinfo *zones; + + /* number of zones in use */ +DECL int nzones; + + /* set if we need a priming */ +DECL int needs_prime_cache; + + /* is cache being primed */ +DECL int priming; + + /* ptrs to dnames in msg for dn_comp */ +DECL u_char *dnptrs[40]; + + /* end pointer for dnptrs */ +DECL u_char **dnptrs_end + INIT(dnptrs + sizeof dnptrs / sizeof(u_char*)); + + /* number of names in addinfo */ +DECL int addcount; + + /* name of cache file */ +DECL const char *cache_file; + +#ifdef SLAVE_FORWARD + /* retry time when a slave */ +DECL int slave_retry INIT(4); +#endif + +#ifdef BIND_UPDATE +DECL const char * LogSignature INIT(";BIND LOG V8\n"); +DECL const char * DumpSignature INIT(";BIND DUMP V8\n"); +DECL const char * DumpSuffix INIT(".dumptmp"); +#endif + +DECL const char sendtoStr[] INIT("sendto"); +DECL const char tcpsendStr[] INIT("tcp_send"); + + /* defined in version.c, can't use DECL/INIT */ +extern char Version[]; +extern char ShortVersion[]; + + /* If getnum() has an error, here will be the result. */ +DECL int getnum_error INIT(0); + +enum context { domain_ctx, owner_ctx, mailname_ctx, hostname_ctx }; +DECL const char *context_strings[] +#ifdef MAIN_PROGRAM + = { "domain", "owner", "mail", "host", NULL } +#endif +; + +DECL const char *transport_strings[] +#ifdef MAIN_PROGRAM + = { "primary", "secondary", "response", NULL } +#endif +; + +DECL const char *severity_strings[] +#ifdef MAIN_PROGRAM + = { "ignore", "warn", "fail", "not_set", NULL } +#endif +; + +DECL struct in_addr inaddr_any; /* Inits to 0.0.0.0 */ + +DECL options server_options INIT(NULL); + +DECL server_info nameserver_info INIT(NULL); + + /* These will disappear some day in favour of "struct nameser". */ +DECL ip_match_list bogus_nameservers INIT(NULL); + +DECL log_context log_ctx; +DECL int log_ctx_valid INIT(0); + +DECL log_channel syslog_channel INIT(NULL); +DECL log_channel debug_channel INIT(NULL); +DECL log_channel stderr_channel INIT(NULL); +DECL log_channel eventlib_channel INIT(NULL); +DECL log_channel packet_channel INIT(NULL); +DECL log_channel null_channel INIT(NULL); + +DECL ip_match_list local_addresses INIT(NULL); +DECL ip_match_list local_networks INIT(NULL); + + /* are we running in no-fork mode? */ + +DECL int foreground INIT(0); + +DECL const struct ns_sym logging_constants[] +#ifdef MAIN_PROGRAM += { + { log_info, "info" }, + { log_notice, "notice" }, + { log_warning, "warning" }, + { log_error, "error" }, + { log_critical, "critical" }, + { 0, NULL } +} +#endif +; + +DECL const struct ns_sym syslog_constants[] +#ifdef MAIN_PROGRAM += { + { LOG_KERN, "kern" }, + { LOG_USER, "user" }, + { LOG_MAIL, "mail" }, + { LOG_DAEMON, "daemon" }, + { LOG_AUTH, "auth" }, + { LOG_SYSLOG, "syslog" }, + { LOG_LPR, "lpr" }, +#ifdef LOG_NEWS + { LOG_NEWS, "news" }, +#endif +#ifdef LOG_UUCP + { LOG_UUCP, "uucp" }, +#endif +#ifdef LOG_CRON + { LOG_CRON, "cron" }, +#endif +#ifdef LOG_AUTHPRIV + { LOG_AUTHPRIV, "authpriv" }, +#endif +#ifdef LOG_FTP + { LOG_FTP, "ftp" }, +#endif + { LOG_LOCAL0, "local0"}, + { LOG_LOCAL1, "local1"}, + { LOG_LOCAL2, "local2"}, + { LOG_LOCAL3, "local3"}, + { LOG_LOCAL4, "local4"}, + { LOG_LOCAL5, "local5"}, + { LOG_LOCAL6, "local6"}, + { LOG_LOCAL7, "local7"}, + { 0, NULL } +} +#endif +; + +DECL const struct ns_sym category_constants[] +#ifdef MAIN_PROGRAM += { + { ns_log_default, "default" }, + { ns_log_config, "config" }, + { ns_log_parser, "parser" }, + { ns_log_queries, "queries" }, + { ns_log_lame_servers, "lame-servers" }, + { ns_log_statistics, "statistics" }, + { ns_log_panic, "panic" }, + { ns_log_update, "update" }, + { ns_log_ncache, "ncache" }, + { ns_log_xfer_in, "xfer-in" }, + { ns_log_xfer_out, "xfer-out" }, + { ns_log_db, "db" }, + { ns_log_eventlib, "eventlib" }, + { ns_log_packet, "packet" }, + { ns_log_notify, "notify" }, + { ns_log_cname, "cname" }, + { ns_log_security, "security" }, + { ns_log_os, "os" }, + { ns_log_insist, "insist" }, + { ns_log_maint, "maintenance" }, + { ns_log_load, "load" }, + { ns_log_resp_checks, "response-checks" }, + { 0, NULL } +} +#endif +; + +DECL const char panic_msg_no_options[] + INIT("no server_options in NS_OPTION_P"); + +DECL const char panic_msg_insist_failed[] + INIT("%s:%d: insist '%s' failed: %s"); + +DECL const char panic_msg_bad_which[] + INIT("%s:%d: INCRSTATS(%s): bad \"which\""); + +DECL u_long globalStats[nssLast]; + +DECL evTimerID clean_timer; +DECL evTimerID interface_timer; +DECL evTimerID stats_timer; +DECL int active_timers INIT(0); + +DECL uid_t user_id; +DECL char * user_name INIT(NULL); +DECL gid_t group_id; +DECL char * group_name INIT(NULL); +DECL char * chroot_dir INIT(NULL); diff --git a/contrib/bind/bin/named/ns_glue.c b/contrib/bind/bin/named/ns_glue.c new file mode 100644 index 0000000..460b64d --- /dev/null +++ b/contrib/bind/bin/named/ns_glue.c @@ -0,0 +1,416 @@ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_glue.c,v 8.7 1998/02/13 19:51:45 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <resolv.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <stdarg.h> +#include <unistd.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +/* + * IP address from unaligned octets. + */ +struct in_addr +ina_get(const u_char *data) { + struct in_addr ret; + u_int32_t i; + + GETLONG(i, data); + ina_ulong(ret) = htonl(i); + return (ret); +} + +/* + * IP address to unaligned octets. + */ +u_char * +ina_put(struct in_addr ina, u_char *data) { + PUTLONG(ntohl(ina_ulong(ina)), data); + return (data); +} + +/* + * XXX: sin_ntoa() should probably be in libc. + */ +const char * +sin_ntoa(struct sockaddr_in sin) { + static char ret[sizeof "[111.222.333.444].55555"]; + + sprintf(ret, "[%s].%u", + inet_ntoa(sin.sin_addr), + ntohs(sin.sin_port)); + return (ret); +} + +/* + * Logging Support + */ + +void +ns_debug(int category, int level, const char *format, ...) { + va_list args; + + if (!log_ctx_valid) + return; + va_start(args, format); + log_vwrite(log_ctx, category, log_debug(level), format, args); + va_end(args); +} + +void +ns_info(int category, const char *format, ...) { + va_list args; + + if (!log_ctx_valid) + return; + va_start(args, format); + log_vwrite(log_ctx, category, log_info, format, args); + va_end(args); +} + +void +ns_notice(int category, const char *format, ...) { + va_list args; + + if (!log_ctx_valid) + return; + va_start(args, format); + log_vwrite(log_ctx, category, log_notice, format, args); + va_end(args); +} + +void +ns_warning(int category, const char *format, ...) { + va_list args; + + if (!log_ctx_valid) + return; + va_start(args, format); + log_vwrite(log_ctx, category, log_warning, format, args); + va_end(args); +} + +void +ns_error(int category, const char *format, ...) { + va_list args; + + if (!log_ctx_valid) + return; + va_start(args, format); + log_vwrite(log_ctx, category, log_error, format, args); + va_end(args); +} + +void +ns_panic(int category, int dump_core, const char *format, ...) { + va_list args; + + if (!log_ctx_valid) + return; + va_start(args, format); + log_vwrite(log_ctx, category, log_critical, format, args); + va_end(args); + va_start(args, format); + log_vwrite(log_ctx, ns_log_panic, log_critical, format, args); + va_end(args); + if (dump_core) + abort(); + else + exit(1); +} + +void +ns_assertion_failed(char *file, int line, assertion_type type, char *cond, + int print_errno) +{ + ns_panic(ns_log_insist, 1, "%s:%d: %s(%s)%s%s failed.", + file, line, assertion_type_to_text(type), cond, + (print_errno) ? ": " : "", + (print_errno) ? strerror(errno) : ""); +} + +/* + * XXX This is for compatibility and will eventually be removed. + */ +void +panic(const char *msg, const void *arg) { + ns_panic(ns_log_default, 1, msg, arg); +} + +/* + * How many labels in this name? + * Note: the root label is not included in the count. + */ +int +nlabels (const char *dname) { + int count, i, found, escaped; + const char *tmpdname, *end_tmpdname; + int tmpdnamelen, c; + + INSIST(dname != NULL); + + count = 0; + tmpdname = dname; + tmpdnamelen = strlen(tmpdname); + /* + * Ignore a trailing label separator (i.e. an unescaped dot) + * in 'tmpdname'. + */ + if (tmpdnamelen && tmpdname[tmpdnamelen-1] == '.') { + escaped = 0; + /* note this loop doesn't get executed if tmpdnamelen==1 */ + for (i = tmpdnamelen - 2; i >= 0; i--) + if (tmpdname[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else { + break; + } + if (!escaped) + tmpdnamelen--; + } + + end_tmpdname = tmpdname + tmpdnamelen; + + while(tmpdname != end_tmpdname) { + count++; + /* + * Strip off the first label if we're not already at + * the root label. + */ + for (escaped = found = 0; + (tmpdname != end_tmpdname) && !found; + tmpdname++) { + c = *tmpdname; + if (!escaped && (c == '.')) + found = 1; + + if (escaped) + escaped = 0; + else if (c == '\\') + escaped = 1; + } + } + + ns_debug(ns_log_default, 12, "nlabels of \"%s\" -> %d", dname, + count); + return (count); +} + +/* + * Get current system time and put it in a global. + */ +void +gettime(struct timeval *ttp) { + if (gettimeofday(ttp, NULL) < 0) + ns_error(ns_log_default, "gettimeofday: %s", strerror(errno)); +} + +/* + * This is useful for tracking down lost file descriptors. + */ +int +my_close(int fd) { + int s; + + do { + errno = 0; + s = close(fd); + } while (s < 0 && errno == EINTR); + + if (s < 0 && errno != EBADF) + ns_info(ns_log_default, "close(%d) failed: %s", fd, + strerror(errno)); + else + ns_debug(ns_log_default, 3, "close(%d) succeeded", fd); + return (s); +} + +/* + * This is useful for tracking down lost file descriptors. + */ +int +my_fclose(FILE *fp) { + int fd = fileno(fp), + s = fclose(fp); + + if (s < 0) + ns_info(ns_log_default, "fclose(%d) failed: %m", fd, + strerror(errno)); + else + ns_debug(ns_log_default, 3, "fclose(%d) succeeded", fd); + return (s); +} + +/* + * Save a counted buffer and return a pointer to it. + */ +u_char * +savebuf(const u_char *buf, size_t len, int needpanic) { + u_char *bp = (u_char *)memget(len); + + if (bp == NULL) { + if (needpanic) + panic("savebuf: memget failed (%s)", strerror(errno)); + else + return (NULL); + } + memcpy(bp, buf, len); + return (bp); +} + +#ifdef DEBUG_STRINGS +char * +debug_newstr(size_t len, int needpanic, const char *file, int line) { + size_t size; + + size = len + 3; /* 2 length bytes + NUL. */ + printf("%s:%d: newstr %d\n", file, line, size); + return (__newstr(len, needpanic)); +} + +char * +debug_savestr(const char *str, int needpanic, const char *file, int line) { + size_t len; + + len = strlen(str); + len += 3; /* 2 length bytes + NUL. */ + printf("%s:%d: savestr %d %s\n", file, line, len, str); + return (__savestr(str, needpanic)); +} + +void +debug_freestr(char *str, const char *file, int line) { + u_char *buf, *bp; + size_t len; + + buf = (u_char *)str - 2/*Len*/; + bp = buf; + NS_GET16(len, bp); + len += 3; /* 2 length bytes + NUL. */ + printf("%s:%d: freestr %d %s\n", file, line, len, str); + __freestr(str); + return; +} +#endif /* DEBUG_STRINGS */ + +/* + * Return a counted string buffer big enough for a string of length 'len'. + */ +char * +__newstr(size_t len, int needpanic) { + u_char *buf, *bp; + + REQUIRE(len <= 65536); + + buf = (u_char *)memget(2/*Len*/ + len + 1/*Nul*/); + if (buf == NULL) { + if (needpanic) + panic("savestr: memget failed (%s)", strerror(errno)); + else + return (NULL); + } + bp = buf; + NS_PUT16(len, bp); + return ((char *)bp); +} + +/* + * Save a NUL terminated string and return a pointer to it. + */ +char * +__savestr(const char *str, int needpanic) { + char *buf; + size_t len; + + len = strlen(str); + if (len > 65536) { + if (needpanic) + ns_panic(ns_log_default, 1, + "savestr: string too long"); + else + return (NULL); + } + buf = __newstr(len, needpanic); + memcpy(buf, str, len + 1); + return (buf); +} + +void +__freestr(char *str) { + u_char *buf, *bp; + size_t len; + + buf = (u_char *)str - 2/*Len*/; + bp = buf; + NS_GET16(len, bp); + memput(buf, 2/*Len*/ + len + 1/*Nul*/); +} + +char * +checked_ctime(const time_t *t) { + char *ctime_result; + + ctime_result = ctime(t); + if (ctime_result == NULL) { + ns_error(ns_log_default, "ctime() returned NULL!"); + ctime_result = "<unknown time>\n"; + } + + return (ctime_result); +} + +/* + * Since the fields in a "struct timeval" are longs, and the argument to ctime + * is a pointer to a time_t (which might not be a long), here's a bridge. + */ +char * +ctimel(long l) { + time_t t = (time_t)l; + + return (checked_ctime(&t)); +} diff --git a/contrib/bind/bin/named/ns_init.c b/contrib/bind/bin/named/ns_init.c new file mode 100644 index 0000000..920bfeb --- /dev/null +++ b/contrib/bind/bin/named/ns_init.c @@ -0,0 +1,512 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_init.c 4.38 (Berkeley) 3/21/91"; +static char rcsid[] = "$Id: ns_init.c,v 8.40 1998/04/07 18:11:58 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#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 <ctype.h> +#include <errno.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +#ifdef DEBUG +static void content_zone(int, int); +#endif + +/* + * Set new refresh time for zone. Use a random number in the last half of + * the refresh limit; we want it to be substantially correct while still + * preventing slave synchronization. + */ +void +ns_refreshtime(struct zoneinfo *zp, time_t timebase) { + u_long refresh = (zp->z_refresh > 0) ? zp->z_refresh : INIT_REFRESH; + time_t half = (refresh + 1) / 2; + + zp->z_time = timebase + half + (rand() % half); +} + +/* + * Set new retry time for zone. + */ +void +ns_retrytime(struct zoneinfo *zp, time_t timebase) { + zp->z_time = timebase + zp->z_retry; +} + +/* + * Read configuration file and save it as internal state. + */ +void +ns_init(const char *conffile) { + struct zoneinfo *zp; + static int loads = 0; /* number of times loaded */ + + ns_debug(ns_log_config, 1, "ns_init(%s)", conffile); + gettime(&tt); + + if (loads == 0) { + zones = (struct zoneinfo *)memget(64 * sizeof *zones); + if (zones == NULL) + ns_panic(ns_log_config, 0, + "Not enough memory to allocate initial zones array"); + memset(zones, 0, 64 * sizeof *zones); + 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; + zones[0].z_origin = savestr("", 1); + init_configuration(); + } 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 DEBUG + if (debug >= 3) { + ns_debug(ns_log_config, 3, "content of zones before loading"); + content_zone(nzones - 1, 3); + } +#endif + + load_configuration(conffile); + + /* 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 BIND_UPDATE + /* + * A dynamic zone might have changed, so we + * need to dump it before removing it. + */ + if ((zp->z_flags & Z_DYNAMIC) && + ((zp->z_flags & Z_NEED_SOAUPDATE) || + (zp->z_flags & Z_NEED_DUMP))) + (void)zonedump(zp); +#endif + ns_stopxfrs(zp); + do_reload(zp->z_origin, zp->z_type, zp->z_class); + ns_notice(ns_log_config, + "%s zone \"%s\" (%s) removed", + zoneTypeString(zp), zp->z_origin, + p_class(zp->z_class)); + free_zone_contents(zp, 1); + memset(zp, 0, sizeof(*zp)); + } + } + +#ifdef DEBUG + if (debug >= 2) { + ns_debug(ns_log_config, 2, "content of zones after loading"); + content_zone(nzones-1, 2); + } +#endif + + ns_debug(ns_log_config, 1, "exit ns_init()"); + loads++; +} + +void +zoneinit(struct zoneinfo *zp) { + struct stat sb; + int result; + + /* + * 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; + result = stat(zp->z_source, &sb); + if (result != -1) { + ns_stopxfrs(zp); + purge_zone(zp->z_origin, hashtab, zp->z_class); + } + if (result == -1 || db_load(zp->z_source, zp->z_origin, zp, NULL)) { + /* + * Set zone to be refreshed immediately. + */ + zp->z_refresh = INIT_REFRESH; + zp->z_retry = INIT_REFRESH; + if (!(zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING))) { + zp->z_time = tt.tv_sec; + sched_zone_maint(zp); + } + } else { + zp->z_flags |= Z_AUTH; + zp->z_flags &= ~Z_NEED_RELOAD; + ns_refreshtime(zp, tt.tv_sec); + sched_zone_maint(zp); + } +} + +void +do_reload(const char *domain, int type, int class) { + struct zoneinfo *zp; + + ns_debug(ns_log_config, 1, "do_reload: %s %d %d", + *domain ? domain : ".", type, class); + + /* + * Check if the zone has changed type. If so, we might not need to + * do any purging or parent reloading. + * + * If the new zone is a master zone, then it will have purged the + * old data and loaded, so we don't need to do anything. + * + * If the new zone is a slave or stub zone and has successfully loaded, + * then we don't need to do anything either. + * + * NOTE: we take care not to match ourselves. + */ + if ((type != z_master && + find_zone(domain, z_master, class) != NULL) || + (type != z_slave && + (zp = find_zone(domain, z_slave, class)) != NULL && + zp->z_serial != 0) || + (type != z_stub && + (zp = find_zone(domain, z_stub, class)) != NULL && + zp->z_serial != 0) + ) + return; + + /* + * Clean up any leftover data. + */ + purge_zone(domain, hashtab, class); + + /* + * Reload + */ + while (*domain) { + const char *s; + int escaped; + + /* + * XXX this is presentation level hair and belongs elsewhere. + */ + escaped = 0; + for (s = domain; *s != '\0'; s++) { + if (!escaped) { + if (*s == '.') + break; + else if (*s == '\\') + escaped = 1; + } else + escaped = 0; + } + + if (*s != '\0') + domain = s + 1; /* skip label and its separator */ + else + domain = ""; /* root zone */ + + if ((zp = find_zone(domain, Z_STUB, class)) || + (zp = find_zone(domain, Z_CACHE, class)) || + (zp = find_zone(domain, Z_PRIMARY, class)) || + (zp = find_zone(domain, Z_SECONDARY, class))) { + + ns_debug(ns_log_config, 1, "do_reload: matched %s", + *domain ? domain : "."); + + if (zp->z_type == Z_CACHE) + purge_zone(zp->z_origin, fcachetab, + zp->z_class); + else + purge_zone(zp->z_origin, hashtab, zp->z_class); + + zp->z_flags &= ~Z_AUTH; + + switch (zp->z_type) { + case Z_SECONDARY: + case Z_STUB: + zoneinit(zp); + break; + case Z_PRIMARY: + if (db_load(zp->z_source, zp->z_origin, zp, 0) + == 0) + zp->z_flags |= Z_AUTH; + break; + case Z_CACHE: + (void)db_load(zp->z_source, zp->z_origin, zp, + 0); + break; + } + break; + } + } +} + +#ifdef DEBUG +/* prints out the content of zones */ +static void +content_zone(int end, int level) { + int i; + + for (i = 1; i <= end; i++) { + printzoneinfo(i, ns_log_config, level); + } +} +#endif + +enum context +ns_ptrcontext(owner) + const char *owner; +{ + if (samedomain(owner, "in-addr.arpa") || samedomain(owner, "ip6.int")) + return (hostname_ctx); + return (domain_ctx); +} + +enum context +ns_ownercontext(type, transport) + int type; + enum transport transport; +{ + enum context context = domain_ctx; + + switch (type) { + case T_A: + case T_WKS: + case T_MX: + switch (transport) { + case primary_trans: + case secondary_trans: + context = owner_ctx; + break; + case response_trans: + context = hostname_ctx; + break; + default: + panic("impossible condition in ns_ownercontext()", + NULL); + } + break; + case T_MB: + case T_MG: + context = mailname_ctx; + break; + default: + /* Nothing to do. */ + break; + } + return (context); +} + +int +ns_nameok(const char *name, int class, struct zoneinfo *zp, + enum transport transport, + enum context context, + const char *owner, + struct in_addr source) +{ + enum severity severity = not_set; + int ok = 1; + + if (zp != NULL) + severity = zp->z_checknames; + if (severity == not_set) + severity = server_options->check_names[transport]; + + if (severity == ignore) + return (1); + switch (context) { + case domain_ctx: + ok = (class != C_IN) || res_dnok(name); + break; + case owner_ctx: + ok = (class != C_IN) || res_ownok(name); + break; + case mailname_ctx: + ok = res_mailok(name); + break; + case hostname_ctx: + ok = res_hnok(name); + break; + default: + ns_panic(ns_log_default, 1, + "unexpected context %d in ns_nameok", (int)context); + } + if (!ok) { + char *s, *o; + + if (source.s_addr == INADDR_ANY) + s = savestr(transport_strings[transport], 0); + else { + s = newstr(strlen(transport_strings[transport]) + + sizeof " from [000.000.000.000]", 0); + if (s) + sprintf(s, "%s from [%s]", + transport_strings[transport], + inet_ntoa(source)); + } + if (strcasecmp(owner, name) == 0) + o = savestr("", 0); + else { + const char *t = (*owner == '\0') ? "." : owner; + + o = newstr(strlen(t) + sizeof " (owner \"\")", 0); + if (o) + sprintf(o, " (owner \"%s\")", t); + } + /* + * We use log_write directly here to avoid duplicating + * the message formatting and arguments. + */ + log_write(log_ctx, ns_log_default, + (transport == response_trans) ? + log_info : log_notice, + "%s name \"%s\"%s %s (%s) is invalid - %s", + context_strings[context], + name, o != NULL ? o : "[memget failed]", + p_class(class), + s != NULL ? s : "[memget failed]", + (severity == fail) ? + "rejecting" : "proceeding anyway"); + if (severity == warn) + ok = 1; + if (s != NULL) + freestr(s); + if (o != NULL) + freestr(o); + } + return (ok); +} + +int +ns_wildcard(const char *name) { + if (*name != '*') + return (0); + return (*++name == '\0'); +} + +void +ns_shutdown() { + struct zoneinfo *zp; + + /* Erase zones. */ + for (zp = &zones[0]; zp < &zones[nzones]; zp++) { + if (zp->z_type) { + if (zp->z_type != z_hint) { + ns_stopxfrs(zp); + purge_zone(zp->z_origin, hashtab, zp->z_class); + } + free_zone_contents(zp, 1); + } + } + memput(zones, ((nzones / 64) + 1) * 64 * sizeof *zones); + + /* Erase the cache. */ + clean_cache(hashtab, 1); + hashtab->h_cnt = 0; /* ??? */ + rm_hash(hashtab); + clean_cache(fcachetab, 1); + fcachetab->h_cnt = 0; /* ??? */ + rm_hash(fcachetab); + +#ifdef BIND_NOTIFY + db_cancel_pending_notifies(); +#endif + freeComplaints(); + shutdown_configuration(); +} diff --git a/contrib/bind/bin/named/ns_lexer.c b/contrib/bind/bin/named/ns_lexer.c new file mode 100644 index 0000000..fe319fa --- /dev/null +++ b/contrib/bind/bin/named/ns_lexer.c @@ -0,0 +1,750 @@ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_lexer.c,v 8.12 1997/12/04 08:11:52 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include <stdarg.h> +#include <syslog.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" +#include "ns_parser.h" +#include "ns_parseutil.h" +#include "ns_lexer.h" + +typedef enum lexer_state { + scan, number, identifier, ipv4, quoted_string +} LexerState; + +#define LEX_EOF 0x01 + +typedef struct lexer_file_context { + const char * name; + FILE * stream; + int line_number; + LexerState state; + u_int flags; + int warnings; + int errors; + struct lexer_file_context * + next; +} *LexerFileContext; + +LexerFileContext current_file = NULL; + +#define LEX_LAST_WAS_DOT 0x01 +#define LEX_CONSECUTIVE_DOTS 0x02 + +typedef struct lexer_identifier { + char buffer[LEX_MAX_IDENT_SIZE+1]; + int index; + int num_dots; + unsigned int flags; +} *LexerIdentifier; + +static LexerIdentifier id; + +static char special_chars[256]; + +#define whitespace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') +#define domain_char(c) (isalnum((c)) || (c) == '.' || (c) == '-') +#define special_char(c) (special_chars[(c)] == 1) +#define identifier_char(c) (!whitespace(c) && !special_char(c)) + +static int last_token; +static YYSTYPE last_yylval; + +static int lexer_initialized = 0; + +/* + * Problem Reporting + */ + +static char * +token_to_text(int token, YYSTYPE lval) { + static char buffer[LEX_MAX_IDENT_SIZE+50]; + + if (token < 128) { + if (token == 0) + strcpy(buffer, "<end of file>"); + else + sprintf(buffer, "'%c'", token); + } else { + switch (token) { + case L_EOS: + strcpy(buffer, ";"); + break; + case L_STRING: + sprintf(buffer, "'%s'", lval.cp); + break; + case L_QSTRING: + sprintf(buffer, "\"%s\"", lval.cp); + break; + case L_IPADDR: + sprintf(buffer, "%s", inet_ntoa(lval.ip_addr)); + break; + case L_NUMBER: + sprintf(buffer, "%ld", lval.num); + break; + case L_END_INCLUDE: + sprintf(buffer, "<end of include>"); + break; + default: + sprintf(buffer, "%s", lval.cp); + } + } + + return (buffer); +} + +static char where[MAXPATHLEN + 100]; +static char message[20480]; + +static void +parser_complain(int is_warning, int print_last_token, const char *format, + va_list args) +{ + LexerFileContext lf; + int severity; + + if (is_warning) { + severity = log_warning; + } else { + severity = log_error; + } + + INSIST(current_file != NULL); + if (current_file->next != NULL) { + for (lf = current_file; lf != NULL; lf = lf->next) { + log_write(log_ctx, ns_log_parser, severity, + "%s '%s' line %d", + (lf == current_file) ? + "In" : "included from", + lf->name, lf->line_number); + } + } + sprintf(where, "%s:%d: ", current_file->name, + current_file->line_number); + vsprintf(message, format, args); + if (print_last_token) + log_write(log_ctx, ns_log_parser, severity, "%s%s near %s", + where, message, + token_to_text(last_token, last_yylval)); + else + log_write(log_ctx, ns_log_parser, severity, + "%s%s", where, message); +} + +int +parser_warning(int print_last_token, const char *format, ...) { + va_list args; + + va_start(args, format); + parser_complain(1, print_last_token, format, args); + va_end(args); + current_file->warnings++; + return (1); +} + +int +parser_error(int print_last_token, const char *format, ...) { + va_list args; + + va_start(args, format); + parser_complain(0, print_last_token, format, args); + va_end(args); + current_file->errors++; + return (1); +} + +void +yyerror(const char *message) { + parser_error(1, message); +} + +/* + * Keywords + */ + +struct keyword { + char *name; + int token; +}; + +/* + * "keywords" is an array of the keywords which are the fixed syntactic + * elements of the configuration file. Each keyword has a string version + * of the keyword and a token id, which should be an identifier which + * matches that in a %token statement inside the parser.y file. + */ +static struct keyword keywords[] = { + {"acl", T_ACL}, + {"address", T_ADDRESS}, + {"algorithm", T_ALGID}, + {"allow-query", T_ALLOW_QUERY}, + {"allow-transfer", T_ALLOW_TRANSFER}, + {"allow-update", T_ALLOW_UPDATE}, + {"also-notify", T_ALSO_NOTIFY}, + {"auth-nxdomain", T_AUTH_NXDOMAIN}, + {"bogus", T_BOGUS}, + {"category", T_CATEGORY}, + {"channel", T_CHANNEL}, + {"check-names", T_CHECK_NAMES}, + {"cleaning-interval", T_CLEAN_INTERVAL}, + {"coresize", T_CORESIZE}, + {"datasize", T_DATASIZE}, + {"deallocate-on-exit", T_DEALLOC_ON_EXIT}, + {"debug", T_DEBUG}, + {"default", T_DEFAULT}, + {"directory", T_DIRECTORY}, + {"dump-file", T_DUMP_FILE}, + {"dynamic", T_DYNAMIC}, + {"fail", T_FAIL}, + {"fake-iquery", T_FAKE_IQUERY}, + {"false", T_FALSE}, + {"fetch-glue", T_FETCH_GLUE}, + {"file", T_FILE}, + {"files", T_FILES}, + {"first", T_FIRST}, + {"forward", T_FORWARD}, + {"forwarders", T_FORWARDERS}, + {"hint", T_HINT}, + {"host-statistics", T_HOSTSTATS}, + {"if-no-answer", T_IF_NO_ANSWER}, + {"if-no-domain", T_IF_NO_DOMAIN}, + {"ignore", T_IGNORE}, + {"include", T_INCLUDE}, + {"interface-interval", T_INTERFACE_INTERVAL}, + {"key", T_SEC_KEY}, + {"keys", T_KEYS}, + {"listen-on", T_LISTEN_ON}, + {"logging", T_LOGGING}, + {"many-answers", T_MANY_ANSWERS}, + {"master", T_MASTER}, + {"masters", T_MASTERS}, + {"max-transfer-time-in", T_MAX_TRANSFER_TIME_IN}, + {"memstatistics-file", T_MEMSTATS_FILE}, + {"multiple-cnames", T_MULTIPLE_CNAMES}, + {"named-xfer", T_NAMED_XFER}, + {"no", T_NO}, + {"notify", T_NOTIFY}, + {"null", T_NULL_OUTPUT}, + {"one-answer", T_ONE_ANSWER}, + {"only", T_ONLY}, + {"options", T_OPTIONS}, + {"pid-file", T_PIDFILE}, + {"port", T_PORT}, + {"print-category", T_PRINT_CATEGORY}, + {"print-severity", T_PRINT_SEVERITY}, + {"print-time", T_PRINT_TIME}, + {"query-source", T_QUERY_SOURCE}, + {"recursion", T_RECURSION}, + {"response", T_RESPONSE}, + {"secret", T_SECRET}, + {"server", T_SERVER}, + {"severity", T_SEVERITY}, + {"size", T_SIZE}, + {"slave", T_SLAVE}, + {"stacksize", T_STACKSIZE}, + {"statistics-file", T_STATS_FILE}, + {"statistics-interval", T_STATS_INTERVAL}, + {"stub", T_STUB}, + {"syslog", T_SYSLOG}, + {"topology", T_TOPOLOGY}, + {"transfer-format", T_TRANSFER_FORMAT}, + {"transfer-source", T_TRANSFER_SOURCE}, + {"transfers", T_TRANSFERS}, + {"transfers-in", T_TRANSFERS_IN}, + {"transfers-out", T_TRANSFERS_OUT}, + {"transfers-per-ns", T_TRANSFERS_PER_NS}, + {"true", T_TRUE}, + {"type", T_TYPE}, + {"unlimited", T_UNLIMITED}, + {"versions", T_VERSIONS}, + {"warn", T_WARN}, + {"yes", T_YES}, + {"zone", T_ZONE}, + {(char *) NULL, 0}, +}; + +/* + * The table size should be a prime chosen to minimize collisions. + */ +#define KEYWORD_TABLE_SIZE 461 + +static symbol_table keyword_table = NULL; + +static void +init_keywords() { + struct keyword *k; + symbol_value value; + + if (keyword_table != NULL) + free_symbol_table(keyword_table); + keyword_table = new_symbol_table(KEYWORD_TABLE_SIZE, NULL); + for (k = keywords; k->name != NULL; k++) { + value.integer = k->token; + define_symbol(keyword_table, k->name, 0, value, 0); + } + dprint_symbol_table(99, keyword_table); +} + +/* + * File Contexts + */ + +void +lexer_begin_file(const char *filename, FILE *stream) { + LexerFileContext lf; + + if (stream == NULL) { + stream = fopen(filename, "r"); + if (stream == NULL) { + parser_error(0, "couldn't open include file '%s'", + filename); + return; + } + } + lf = (LexerFileContext)memget(sizeof (struct lexer_file_context)); + if (lf == NULL) + panic("memget failed in lexer_begin_file", NULL); + INSIST(stream != NULL); + lf->stream = stream; + lf->name = filename; /* note copy by reference */ + lf->line_number = 1; + lf->state = scan; + lf->flags = 0; + lf->warnings = 0; + lf->errors = 0; + lf->next = current_file; + current_file = lf; +} + +void +lexer_end_file(void) { + LexerFileContext lf; + + INSIST(current_file != NULL); + lf = current_file; + current_file = lf->next; + fclose(lf->stream); + memput(lf, sizeof *lf); +} + +/* + * Character Input + */ + +static void +scan_to_comment_end(int c_plus_plus_style) { + int c, nc; + int done = 0; + int prev_was_star = 0; + + while (!done) { + c = getc(current_file->stream); + switch (c) { + case EOF: + if (!c_plus_plus_style) + parser_error(0, "EOF in comment"); + current_file->flags |= LEX_EOF; + done = 1; + break; + case '*': + prev_was_star = 1; + break; + case '/': + if (prev_was_star && !c_plus_plus_style) + done = 1; + prev_was_star = 0; + break; + case '\n': + if (c_plus_plus_style) { + /* don't consume the newline because + we want it to be a delimiter for + anything before the comment + started */ + ungetc(c, current_file->stream); + done = 1; + } else { + current_file->line_number++; + } + prev_was_star = 0; + break; + default: + prev_was_star = 0; + } + } +} + +int +get_next_char(int comment_ok) { + int c, nc; + + if (current_file->flags & LEX_EOF) + return (EOF); + + c = getc(current_file->stream); + + if (comment_ok) { + while (c == '/' || c == '#') { + if (c == '#') { + scan_to_comment_end(1); + if (current_file->flags & LEX_EOF) + return (EOF); + c = getc(current_file->stream); + } else { + nc = getc(current_file->stream); + switch (nc) { + case EOF: + current_file->flags |= LEX_EOF; + return ('/'); + case '*': + case '/': + scan_to_comment_end((nc == '/')); + if (current_file->flags & LEX_EOF) + return (EOF); + c = getc(current_file->stream); + break; + default: + ungetc((nc), current_file->stream); + return ('/'); + } + } + } + } + + if (c == EOF) + current_file->flags |= LEX_EOF; + else if (c == '\n') + current_file->line_number++; + return (c); +} + +void +put_back_char(int c) { + if (c == EOF) + current_file->flags |= LEX_EOF; + else { + ungetc((c), current_file->stream); + if (c == '\n') + current_file->line_number--; + } +} + + +/* + * Identifiers + */ + +static void +clear_identifier(LexerIdentifier id) { + INSIST(id != NULL); + id->index = 0; + id->num_dots = 0; + id->flags = 0; +} + +static char * +dup_identifier(LexerIdentifier id) { + char *duplicate; + + INSIST(id != NULL); + duplicate = savestr(id->buffer, 1); + return (duplicate); +} + +static void +finish_identifier(LexerIdentifier id) { + INSIST(id != NULL && id->index < LEX_MAX_IDENT_SIZE); + id->buffer[id->index] = '\0'; +} + +static void +add_to_identifier(LexerIdentifier id, int c) { + INSIST(id != NULL); + id->buffer[id->index] = c; + id->index++; + if (id->index >= LEX_MAX_IDENT_SIZE) { + parser_error(0, "identifier too long"); + current_file->state = scan; + /* discard chars until we hit a non-identifier char */ + while (identifier_char(c)) { + c = get_next_char(1); + } + put_back_char(c); + clear_identifier(id); + } else { + if (c == '.') { + if (id->flags & LEX_LAST_WAS_DOT) + id->flags |= LEX_CONSECUTIVE_DOTS; + id->flags |= LEX_LAST_WAS_DOT; + id->num_dots++; + } else { + id->flags &= ~LEX_LAST_WAS_DOT; + } + } +} + +/* + * yylex() -- return the next token from the current input stream + */ +int +yylex() { + int c, i; + int comment_ok = 1; + int token = -1; + symbol_value value; + + while (token < 0) { + c = get_next_char(comment_ok); + switch(current_file->state) { + case scan: + if (c == EOF) { + if (current_file->next == NULL) + /* + * We don't want to call + * lexer_end_file() here because we + * want to keep the toplevel file + * context to log errors against. + */ + token = 0; + else { + lexer_end_file(); + token = L_END_INCLUDE; + } + break; + } + if (whitespace(c)) + break; + if (identifier_char(c)) { + if (isdigit(c)) + current_file->state = number; + else + current_file->state = identifier; + clear_identifier(id); + add_to_identifier(id, c); + } else + if (special_char(c)) { + if (c == ';') { + token = L_EOS; + break; + } + if (c == '"') { + clear_identifier(id); + current_file->state = + quoted_string; + comment_ok = 0; + break; + } + token = c; + } else { + parser_error(0, + "invalid character '%c'", + c); + } + break; + + case number: + if (identifier_char(c)) { + if (!isdigit(c)) + current_file->state = + (c == '.') ? ipv4 : identifier; + add_to_identifier(id, c); + } else { + put_back_char(c); + current_file->state = scan; + finish_identifier(id); + yylval.num = atoi(id->buffer); + token = L_NUMBER; + } + break; + + case identifier: + if (identifier_char(c)) { + add_to_identifier(id, c); + } else { + put_back_char(c); + current_file->state = scan; + finish_identifier(id); + /* is it a keyword? */ + if (lookup_symbol(keyword_table, id->buffer, + 0, &value)) { + yylval.cp = id->buffer; + token = value.integer; + } else { + yylval.cp = dup_identifier(id); + token = L_STRING; + } + } + break; + + case ipv4: + if (identifier_char(c)) { + if (!isdigit(c)) { + if (c != '.' || + (id->flags & LEX_CONSECUTIVE_DOTS)) + current_file->state = + identifier; + } + add_to_identifier(id, c); + } else { + put_back_char(c); + if (id->num_dots > 3 || + (id->flags & LEX_LAST_WAS_DOT)) + current_file->state = identifier; + else { + if (id->num_dots == 1) { + add_to_identifier(id, '.'); + add_to_identifier(id, '0'); + add_to_identifier(id, '.'); + add_to_identifier(id, '0'); + } else if (id->num_dots == 2) { + add_to_identifier(id, '.'); + add_to_identifier(id, '0'); + } + current_file->state = scan; + finish_identifier(id); + token = L_IPADDR; + if (inet_aton(id->buffer, + &(yylval.ip_addr))==0) { + yylval.cp = dup_identifier(id); + token = L_STRING; + } + } + } + break; + + case quoted_string: + if (c == EOF) { + parser_error(0, "EOF in quoted string"); + return 0; + } else { + if (c == '"') { + comment_ok = 1; + current_file->state = scan; + finish_identifier(id); + yylval.cp = dup_identifier(id); + token = L_QSTRING; + } else { + /* XXX add backslash escapes here */ + add_to_identifier(id, c); + } + } + break; + + default: + panic("unhandled state in yylex", NULL); + } + } + + last_token = token; + last_yylval = yylval; + return (token); +} + +/* + * Initialization + */ + +symbol_table constants; + +static void +import_constants(const struct ns_sym *s, int type) { + symbol_value value; + for ((void)NULL; s != NULL && s->name != NULL; s++) { + value.integer = s->number; + define_symbol(constants, s->name, type, value, 0); + } +} + +static void +import_res_constants(const struct res_sym *r, int type) { + symbol_value value; + for ((void)NULL; r != NULL && r->name != NULL; r++) { + value.integer = r->number; + define_symbol(constants, r->name, type, value, 0); + } +} + +#define CONSTANTS_TABLE_SIZE 397 /* should be prime */ + +static void +import_all_constants() { + constants = new_symbol_table(CONSTANTS_TABLE_SIZE, NULL); + import_res_constants(__p_class_syms, SYM_CLASS); + import_constants(category_constants, SYM_CATEGORY); + import_constants(logging_constants, SYM_LOGGING); + import_constants(syslog_constants, SYM_SYSLOG); +} + +void +lexer_initialize() { + memset(special_chars, 0, sizeof special_chars); + special_chars[';'] = 1; + special_chars['{'] = 1; + special_chars['}'] = 1; + special_chars['!'] = 1; + special_chars['/'] = 1; + special_chars['"'] = 1; + special_chars['*'] = 1; + id = (LexerIdentifier)memget(sizeof (struct lexer_identifier)); + if (id == NULL) + panic("memget failed in init_once", NULL); + init_keywords(); + import_all_constants(); + lexer_initialized = 1; +} + +void +lexer_setup(void) { + REQUIRE(lexer_initialized); + + current_file = NULL; /* XXX should we INSIST(current_file==NULL)? */ + INSIST(id != NULL); +} + +void +lexer_shutdown(void) { + REQUIRE(lexer_initialized); + + free_symbol_table(keyword_table); + free_symbol_table(constants); + memput(id, sizeof (struct lexer_identifier)); + lexer_initialized = 0; +} diff --git a/contrib/bind/bin/named/ns_lexer.h b/contrib/bind/bin/named/ns_lexer.h new file mode 100644 index 0000000..3491df3 --- /dev/null +++ b/contrib/bind/bin/named/ns_lexer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef NS_LEXER_H +#define NS_LEXER_H + +/* + * Note: <stdio.h> and "ns_parseutil.h" must be included + * before this file is included. + */ + +#define LEX_MAX_IDENT_SIZE 1024 + +#define SYM_CLASS 0x01 +#define SYM_CATEGORY 0x02 +#define SYM_LOGGING 0x04 +#define SYM_SYSLOG 0x08 + +int parser_warning(int, const char *, ...); +int parser_error(int, const char *, ...); +void yyerror(const char *); +void lexer_begin_file(const char *, FILE *); +void lexer_end_file(void); +int yylex(void); +void lexer_initialize(void); +void lexer_setup(void); +void lexer_shutdown(void); + +extern symbol_table constants; + +#endif /* NS_LEXER_H */ diff --git a/contrib/bind/bin/named/ns_main.c b/contrib/bind/bin/named/ns_main.c new file mode 100644 index 0000000..a4b4360 --- /dev/null +++ b/contrib/bind/bin/named/ns_main.c @@ -0,0 +1,2200 @@ +#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 8.65 1998/04/06 23:45:32 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#if !defined(lint) && !defined(SABER) +char copyright[] = +"@(#) Copyright (c) 1986, 1989, 1990 The Regents of the University of California.\n\ + portions Copyright (c) 1993 Digital Equipment Corporation\n\ + portions Copyright (c) 1995, 1996, 1997 Internet Software Consortium\n\ + All rights reserved.\n"; +#endif /* not lint */ + +/* + * Internet Name server (see RCF1035 & others). + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#ifdef SVR4 /* XXX */ +# include <sys/sockio.h> +#else +# include <sys/mbuf.h> +#endif + +#include <netinet/in.h> +#include <net/route.h> +#include <net/if.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <netdb.h> +#include <pwd.h> +#include <resolv.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> +#include <isc/list.h> + +#include "port_after.h" + +#ifdef HAVE_GETRUSAGE /* XXX */ +#include <sys/resource.h> +#endif + +#define MAIN_PROGRAM +#include "named.h" +#undef MAIN_PROGRAM + + /* list of interfaces */ +static LIST(struct _interface) iflist; +static int iflist_initialized = 0; + + /* UDP receive, TCP send buffer size */ +static const int rbufsize = 8 * 1024, + /* TCP send window size */ + sbufsize = 16 * 1024; + +static u_int16_t nsid_state; +static int needs; + +static struct qstream *sq_add(void); +static int opensocket_d(interface *), + opensocket_s(interface *); +static void sq_query(struct qstream *), + dq_remove(interface *), + ns_handle_needs(void); +static int sq_dowrite(struct qstream *); +static void use_desired_debug(void); +static void stream_write(evContext, void *, int, int); + +static interface * if_find(struct in_addr, u_int16_t port); + +static int sq_here(struct qstream *); + +static void stream_accept(evContext, void *, int, + const void *, int, + const void *, int), + stream_getlen(evContext, void *, int, int), + stream_getmsg(evContext, void *, int, int), + datagram_read(evContext, void *, int, int), + dispatch_message(u_char *, int, int, + struct qstream *, + struct sockaddr_in, int, + interface *); +static void stream_send(evContext, void *, int, + const void *, int, + const void *, int); +static void init_signals(void); +static void set_signal_handler(int, SIG_FN (*)()); +static int only_digits(const char *); + +static void +usage() { + fprintf(stderr, +"Usage: named [-d #] [-q] [-r] [-f] [-p port] [[-b|-c] configfile]\n"); +#ifdef CAN_CHANGE_ID + fprintf(stderr, +" [-u (username|uid)] [-g (groupname|gid)]\n"); +#endif +#ifdef HAVE_CHROOT + fprintf(stderr, +" [-t directory]\n"); +#endif + exit(1); +} + +static char bad_p_option[] = +"-p remote/local obsolete; use 'listen-on' in config file to specify local"; + +static char bad_directory[] = "chdir failed for directory '%s': %s"; + +/*ARGSUSED*/ +int +main(int argc, char *argv[], char *envp[]) { + int n, udpcnt; + char *arg; + struct qstream *sp; + interface *ifp; + const int on = 1; + int rfd, size, len, debug_option; + char **argp, *p; + int ch; + FILE *fp; /* file descriptor for pid file */ + struct passwd *pw; + struct group *gr; +#ifdef HAVE_GETRUSAGE + struct rlimit rl; +#endif + + user_id = getuid(); + group_id = getgid(); + + ns_port = htons(NAMESERVER_PORT); + desired_debug = debug; + + /* BSD has a better random number generator but it's not clear + * that we need it here. + */ + gettime(&tt); + srand(((unsigned)getpid()) + (unsigned)tt.tv_usec); + + (void) umask(022); + + while ((ch = getopt(argc, argv, "b:c:d:g:p:t:u:w:qrf")) != EOF) { + switch (ch) { + case 'b': + case 'c': + if (conffile != NULL) + freestr(conffile); + conffile = savestr(optarg, 1); + break; + + case 'd': + desired_debug = atoi(optarg); + if (desired_debug <= 0) + desired_debug = 1; + 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. + */ + ns_port = htons((u_int16_t) atoi(optarg)); + p = strchr(optarg, '/'); + if (p) { + syslog(LOG_WARNING, bad_p_option); + fprintf(stderr, bad_p_option); + fputc('\n', stderr); + } + break; + + case 'w': + if (chdir(optarg) < 0) { + syslog(LOG_CRIT, bad_directory, optarg, + strerror(errno)); + fprintf(stderr, bad_directory, optarg, + strerror(errno)); + fputc('\n', stderr); + exit(1); + } + break; +#ifdef QRYLOG + case 'q': + qrylog = 1; + break; +#endif + + case 'r': + ns_setoption(OPTION_NORECURSE); + break; + + case 'f': + foreground = 1; + break; + + case 't': + chroot_dir = savestr(optarg, 1); + break; + +#ifdef CAN_CHANGE_ID + case 'u': + user_name = savestr(optarg, 1); + if (only_digits(user_name)) + user_id = atoi(user_name); + else { + pw = getpwnam(user_name); + if (pw == NULL) { + fprintf(stderr, + "user \"%s\" unknown\n", + user_name); + exit(1); + } + user_id = pw->pw_uid; + if (group_name == NULL) { + char name[256]; + + sprintf(name, "%lu", + (u_long)pw->pw_gid); + group_name = savestr(name, 1); + group_id = pw->pw_gid; + } + } + break; + + case 'g': + if (group_name != NULL) + freestr(group_name); + group_name = savestr(optarg, 1); + if (only_digits(group_name)) + group_id = atoi(group_name); + else { + gr = getgrnam(group_name); + if (gr == NULL) { + fprintf(stderr, + "group \"%s\" unknown\n", + group_name); + exit(1); + } + group_id = gr->gr_gid; + } + break; +#endif /* CAN_CHANGE_ID */ + + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc) { + if (conffile != NULL) + freestr(conffile); + conffile = savestr(*argv, 1); + argc--, argv++; + } + if (argc) + usage(); + + if (conffile == NULL) + conffile = savestr(_PATH_CONF, 1); + + /* + * Make sure we don't inherit any open descriptors + * other than those that daemon() can deal with. + */ + for (n = sysconf(_SC_OPEN_MAX) - 1; n >= 0; n--) + if (n != STDIN_FILENO && + n != STDOUT_FILENO && + n != STDERR_FILENO) + (void) close(n); + + /* + * Chroot if desired. + */ + if (chroot_dir != NULL) { +#ifdef HAVE_CHROOT + if (chroot(chroot_dir) < 0) { + fprintf(stderr, "chroot %s failed: %s\n", chroot_dir, + strerror(errno)); + exit(1); + } + if (chdir("/") < 0) { + fprintf(stderr, "chdir(\"/\") failed: %s\n", + strerror(errno)); + exit(1); + } +#else + fprintf(stderr, "warning: chroot() not available\n"); + freestr(chroot_dir); + chroot_dir = NULL; +#endif + } + + /* Establish global event context. */ + evCreate(&ev); + + /* + * Set up logging. + */ + n = LOG_PID; +#ifdef LOG_NOWAIT + n |= LOG_NOWAIT; +#endif +#ifdef LOG_NDELAY + n |= LOG_NDELAY; +#endif +#if defined(LOG_CONS) && defined(USE_LOG_CONS) + n |= LOG_CONS; +#endif +#ifdef SYSLOG_42BSD + openlog("named", n); +#else + openlog("named", n, LOG_DAEMON); +#endif + + init_logging(); + set_assertion_failure_callback(ns_assertion_failed); + +#ifdef DEBUG + use_desired_debug(); +#endif + + init_signals(); + + ns_notice(ns_log_default, "starting. %s", Version); + + _res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE); + + nsid_init(); + + /* + * Initialize and load database. + */ + gettime(&tt); + buildservicelist(); + buildprotolist(); + ns_init(conffile); + time(&boottime); + resettime = boottime; + + /* + * Fork and go into background now that + * we've done any slow initialization + * and are ready to answer queries. + */ + + if (foreground == 0) { + if (daemon(1, 0)) + ns_panic(ns_log_default, 1, "daemon: %s", + strerror(errno)); + update_pid_file(); + } + + /* Check that udp checksums are on. */ + ns_udp(); + + /* + * We waited until now to log this because we wanted logging to + * be set up the way the user prefers. + */ + if (chroot_dir != NULL) + ns_info(ns_log_security, "chrooted to %s", chroot_dir); + +#ifdef CAN_CHANGE_ID + /* + * Set user and group if desired. + */ + if (group_name != NULL) { + if (setgid(group_id) < 0) + ns_panic(ns_log_security, 1, "setgid(%s): %s", + group_name, strerror(errno)); + ns_info(ns_log_security, "group = %s", group_name); + } + if (user_name != NULL) { + if (getuid() == 0 && initgroups(user_name, group_id) < 0) + ns_panic(ns_log_security, 1, "initgroups(%s, %d): %s", + user_name, (int)group_id, strerror(errno)); + endgrent(); + endpwent(); + if (setuid(user_id) < 0) + ns_panic(ns_log_security, 1, "setuid(%s): %s", + user_name, strerror(errno)); + ns_info(ns_log_security, "user = %s", user_name); + } +#endif /* CAN_CHANGE_ID */ + + ns_notice(ns_log_default, "Ready to answer queries."); + gettime(&tt); + prime_cache(); + for (;;) { + evEvent event; + + if (needs) + ns_handle_needs(); + INSIST_ERR(evGetNext(ev, &event, EV_WAIT) != -1); + INSIST_ERR(evDispatch(ev, event) != -1); + } + /* NOTREACHED */ + return (0); +} + +#ifndef IP_OPT_BUF_SIZE +/* arbitrary size */ +#define IP_OPT_BUF_SIZE 50 +#endif + +static void +stream_accept(evContext lev, void *uap, int rfd, + const void *lav, int lalen, + const void *rav, int ralen) +{ + interface *ifp = uap; + struct qstream *sp; + struct iovec iov; + int n, len; + const int on = 1; +#ifdef IP_OPTIONS /* XXX */ + u_char ip_opts[IP_OPT_BUF_SIZE]; +#endif + const struct sockaddr_in *la, *ra; + + la = (const struct sockaddr_in *)lav; + ra = (const struct sockaddr_in *)rav; + + INSIST(ifp != NULL); + + if (rfd < 0) { + switch (errno) { + case EINTR: + case EAGAIN: +#if (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + case ECONNABORTED: +#ifdef EPROTO + case EPROTO: +#endif + case EHOSTUNREACH: + case EHOSTDOWN: + case ENETUNREACH: + case ENETDOWN: + case ECONNREFUSED: +#ifdef ENONET + case ENONET: +#endif + /* + * These errors are expected and harmless, so + * we ignore them. + */ + return; + case EBADF: + case ENOTSOCK: + case EFAULT: + /* + * If one these happens, we're broken. + */ + ns_panic(ns_log_default, 1, "accept: %s", + strerror(errno)); + case EMFILE: + /* + * If we're out of file descriptors, find the least + * busy fd and close it. Then we'll return to the + * eventlib which will call us right back. + */ + if (streamq) { + struct qstream *nextsp; + struct qstream *candidate = NULL; + time_t lasttime, maxctime = 0; + + 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) + sq_remove(sp); + else if (lasttime > maxctime) { + candidate = sp; + maxctime = lasttime; + } + } + if (candidate) + sq_remove(candidate); + return; + } + /* fall through */ + default: + /* + * Either we got an error we didn't expect, or we + * got EMFILE and didn't have anything left to close. + * Log it and press on. + */ + ns_info(ns_log_default, "accept: %s", strerror(errno)); + return; + } + } + + /* Condition the socket. */ + +/* XXX clean up */ +#if 0 + if ((n = fcntl(rfd, F_GETFL, 0)) == -1) { + ns_info(ns_log_default, "fcntl(rfd, F_GETFL): %s", + strerror(errno)); + (void) close(rfd); + return; + } + if (fcntl(rfd, F_SETFL, n|PORT_NONBLOCK) == -1) { + ns_info(ns_log_default, "fcntl(rfd, NONBLOCK): %s", + strerror(errno)); + (void) close(rfd); + return; + } +#endif +#ifndef CANNOT_SET_SNDBUF + if (setsockopt(rfd, SOL_SOCKET, SO_SNDBUF, + (char*)&sbufsize, sizeof sbufsize) < 0) { + ns_info(ns_log_default, "setsockopt(rfd, SO_SNDBUF, %d): %s", + sbufsize, strerror(errno)); + (void) close(rfd); + return; + } +#endif + if (setsockopt(rfd, SOL_SOCKET, SO_KEEPALIVE, + (char *)&on, sizeof on) < 0) { + ns_info(ns_log_default, "setsockopt(rfd, KEEPALIVE): %s", + strerror(errno)); + (void) close(rfd); + return; + } + + /* + * We don't like IP options. Turn them off if the connection came in + * with any. log this event since it usually indicates a security + * problem. + */ +#if defined(IP_OPTIONS) /* XXX */ + len = sizeof ip_opts; + if (getsockopt(rfd, IPPROTO_IP, IP_OPTIONS, + (char *)ip_opts, &len) < 0) { + ns_info(ns_log_default, "getsockopt(rfd, IP_OPTIONS): %s", + strerror(errno)); + (void) close(rfd); + return; + } + if (len != 0) { + nameserIncr(ra->sin_addr, nssRcvdOpts); + if (!haveComplained(ina_ulong(ra->sin_addr), + (u_long)"rcvd ip options")) { + ns_info(ns_log_default, + "rcvd IP_OPTIONS from %s (ignored)", + sin_ntoa(*ra)); + } + if (setsockopt(rfd, IPPROTO_IP, IP_OPTIONS, NULL, 0) < 0) { + ns_info(ns_log_default, "setsockopt(!IP_OPTIONS): %s", + strerror(errno)); + (void) close(rfd); + } + } +#endif + + /* Create and populate a qsp for this socket. */ + if ((sp = sq_add()) == NULL) { + (void) close(rfd); + return; + } + sp->s_rfd = rfd; /* stream file descriptor */ + gettime(&tt); + sp->s_time = tt.tv_sec; /* last transaction time */ + sp->s_from = *ra; /* address to respond to */ + sp->s_ifp = ifp; + INSIST(sizeof sp->s_temp >= INT16SZ); + iov = evConsIovec(sp->s_temp, INT16SZ); + INSIST_ERR(evRead(lev, rfd, &iov, 1, stream_getlen, sp, &sp->evID_r) + != -1); + sp->flags |= STREAM_READ_EV; +#ifdef DEBUG + if (debug) + ns_info(ns_log_default, "IP/TCP connection from %s (fd %d)", + sin_ntoa(sp->s_from), rfd); +#endif +} + +int +tcp_send(struct qinfo *qp) { + struct qstream *sp; + int on = 1; + + ns_debug(ns_log_default, 1, "tcp_send"); + if ((sp = sq_add()) == NULL) { + return(SERVFAIL); + } + if ((sp->s_rfd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) == -1) { + sq_remove(sp); + return(SERVFAIL); + } + + if (sq_openw(sp, qp->q_msglen + INT16SZ) == -1) { + sq_remove(sp); + return(SERVFAIL); + } + if (sq_write(sp, qp->q_msg, qp->q_msglen) == -1) { + sq_remove(sp); + return(SERVFAIL); + } + + if (setsockopt(sp->s_rfd, SOL_SOCKET, SO_KEEPALIVE, + (char*)&on, sizeof(on)) < 0) + ns_info(ns_log_default, + "tcp_send: setsockopt(rfd, SO_KEEPALIVE): %s", + strerror(errno)); + gettime(&tt); + sp->s_size = -1; + sp->s_time = tt.tv_sec; /* last transaction time */ + sp->s_refcnt = 1; + sp->flags |= STREAM_DONE_CLOSE; + if (qp->q_fwd) + sp->s_from = qp->q_fwd->fwdaddr; + else + sp->s_from = qp->q_addr[qp->q_curaddr].ns_addr; + if (evConnect(ev, sp->s_rfd, &sp->s_from, sizeof(sp->s_from), + stream_send, sp, &sp->evID_c) == -1) { + sq_remove(sp); + return (SERVFAIL); + } + sp->flags |= STREAM_CONNECT_EV; + return(NOERROR); +} + +static void +stream_send(evContext lev, void *uap, int fd, const void *la, int lalen, + const void *ra, int ralen) { + struct qstream *sp = uap; + + ns_debug(ns_log_default, 1, "stream_send"); + + sp->flags &= ~STREAM_CONNECT_EV; + + if (fd == -1) { + /* connect failed */ + sq_remove(sp); + return; + } + if (evSelectFD(ev, sp->s_rfd, EV_WRITE, + stream_write, sp, &sp->evID_w) < 0) { + sq_remove(sp); + return; + } + sp->flags |= STREAM_WRITE_EV; +} + +static void +stream_write(evContext ctx, void *uap, int fd, int evmask) { + struct qstream *sp = uap; + struct iovec iov; + + ns_debug(ns_log_default, 1, "stream_write"); + INSIST(evmask & EV_WRITE); + INSIST(fd == sp->s_rfd); + if (sq_dowrite(sp) < 0) { + sq_remove(sp); + return; + } + if (sp->s_wbuf_free != sp->s_wbuf_send) + return; + + if (sp->s_wbuf) { + memput(sp->s_wbuf, sp->s_wbuf_end - sp->s_wbuf); + sp->s_wbuf = NULL; + } + (void) evDeselectFD(ev, sp->evID_w); + sp->flags &= ~STREAM_WRITE_EV; + sp->s_refcnt = 0; + iov = evConsIovec(sp->s_temp, INT16SZ); + INSIST_ERR(evRead(ctx, fd, &iov, 1, stream_getlen, sp, &sp->evID_r) != + -1); + sp->flags |= STREAM_READ_EV; +} + +static void +stream_getlen(evContext lev, void *uap, int fd, int bytes) { + struct qstream *sp = uap; + struct iovec iov; + + sp->flags &= ~STREAM_READ_EV; + if (bytes != INT16SZ) { + /* + * bytes == 0 is normal EOF; see if something unusual + * happened. + */ + if (bytes < 0) { + /* + * ECONNRESET happens frequently and is not worth + * logging. + */ + if (errno != ECONNRESET) + ns_info(ns_log_default, + "stream_getlen(%s): %s", + sin_ntoa(sp->s_from), strerror(errno)); + } else if (bytes != 0) + ns_error(ns_log_default, + "stream_getlen(%s): unexpected byte count %d", + sin_ntoa(sp->s_from), bytes); + sq_remove(sp); + return; + } + + /* + * Unpack the size, allocate memory for the query. This is + * tricky since in a low memory situation with possibly very + * large (64KB) queries, we want to make sure we can read at + * least the header since we need it to send back a SERVFAIL + * (owing to the out-of-memory condition). + */ + sp->s_size = ns_get16(sp->s_temp); + ns_debug(ns_log_default, 5, "stream message: %d bytes", sp->s_size); + + if (!(sp->flags & STREAM_MALLOC)) { + sp->s_bufsize = 64*1024-1; /* maximum tcp message size */ + sp->s_buf = (u_char *)memget(sp->s_bufsize); + if (sp->s_buf != NULL) + sp->flags |= STREAM_MALLOC; + else { + sp->s_buf = sp->s_temp; + sp->s_bufsize = HFIXEDSZ; + } + } + + iov = evConsIovec(sp->s_buf, sp->s_size); + if (evRead(lev, sp->s_rfd, &iov, 1, stream_getmsg, sp, &sp->evID_r) + == -1) + ns_panic(ns_log_default, 1, "evRead(fd %d): %s", + (void *)sp->s_rfd, strerror(errno)); + sp->flags |= STREAM_READ_EV; +} + +static void +stream_getmsg(evContext lev, void *uap, int fd, int bytes) { + struct qstream *sp = uap; + int buflen, n; + + sp->flags &= ~STREAM_READ_EV; + if (bytes == -1) { + ns_info(ns_log_default, "stream_getmsg(%s): %s", + sin_ntoa(sp->s_from), strerror(errno)); + sq_remove(sp); + return; + } + + gettime(&tt); + sp->s_time = tt.tv_sec; + + ns_debug(ns_log_default, 5, "sp %#x rfd %d size %d time %d next %#x", + sp, sp->s_rfd, sp->s_size, sp->s_time, sp->s_next); + ns_debug(ns_log_default, 5, "\tbufsize %d bytes %d", sp->s_bufsize, + bytes); + + /* + * Do we have enough memory for the query? If not, and if we have a + * query id, then we will send a SERVFAIL error back to the client. + */ + if (bytes != sp->s_size) { + HEADER *hp = (HEADER *)sp->s_buf; + + hp->qr = 1; + hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); + hp->ancount = htons(0); + hp->qdcount = htons(0); + hp->nscount = htons(0); + hp->arcount = htons(0); + hp->rcode = SERVFAIL; + writestream(sp, sp->s_buf, HFIXEDSZ); + sp->flags |= STREAM_DONE_CLOSE; + return; + } + + nameserIncr(sp->s_from.sin_addr, nssRcvdTCP); + sq_query(sp); + dispatch_message(sp->s_buf, bytes, sp->s_bufsize, sp, sp->s_from, -1, + sp->s_ifp); +} + +static void +datagram_read(evContext lev, void *uap, int fd, int evmask) { + interface *ifp = uap; + struct sockaddr_in from; + int from_len = sizeof from; + int n; + union { + HEADER h; /* Force alignment of 'buf'. */ + u_char buf[PACKETSZ+1]; + } u; + + n = recvfrom(fd, (char *)u.buf, sizeof u.buf, 0, + (struct sockaddr *)&from, &from_len); + + if (n < 0) { + switch (errno) { + case EINTR: + case EAGAIN: +#if (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + case EHOSTUNREACH: + case EHOSTDOWN: + case ENETUNREACH: + case ENETDOWN: + case ECONNREFUSED: +#ifdef ENONET + case ENONET: +#endif + /* + * These errors are expected and harmless, so we + * ignore them. + */ + return; + case EBADF: + case ENOTCONN: + case ENOTSOCK: + case EFAULT: + /* + * If one these happens, we're broken. + */ + ns_panic(ns_log_default, 1, "recvfrom: %s", + strerror(errno)); + default: + /* + * An error we don't expect. Log it and press + * on. + */ + ns_info(ns_log_default, "recvfrom: %s", + strerror(errno)); + return; + } + } + +#ifndef BSD + /* Handle bogosity on systems that need it. */ + if (n == 0) + return; +#endif + + ns_debug(ns_log_default, 1, "datagram from %s, fd %d, len %d", + sin_ntoa(from), fd, n); + + if (n > PACKETSZ) { + /* + * The message is too big. It's probably a response to + * one of our questions, so we truncate it and press on. + */ + n = trunc_adjust(u.buf, PACKETSZ, PACKETSZ); + ns_debug(ns_log_default, 1, "truncated oversize UDP packet"); + } + + gettime(&tt); /* Keep 'tt' current. */ + dispatch_message(u.buf, n, PACKETSZ, NULL, from, fd, ifp); +} + +static void +dispatch_message(u_char *msg, int msglen, int buflen, struct qstream *qsp, + struct sockaddr_in from, int dfd, interface *ifp) +{ + HEADER *hp = (HEADER *)msg; + + if (msglen < HFIXEDSZ) { + ns_debug(ns_log_default, 1, "dropping undersize message"); + return; + } + if (hp->qr) { + ns_resp(msg, msglen, from, qsp); + if (qsp) + sq_done(qsp); + /* Now is a safe time for housekeeping. */ + if (needs_prime_cache) + prime_cache(); + } else if (ifp != NULL) + ns_req(msg, msglen, buflen, qsp, from, dfd); + else { + ns_notice(ns_log_security, + "refused query on non-query socket from %s", + sin_ntoa(from)); + /* XXX Send refusal here. */ + } +} + +void +getnetconf(int periodic_scan) { + struct ifconf ifc; + struct ifreq ifreq; + struct in_addr ina; + interface *ifp; + char buf[32768], *cp, *cplim; + u_int32_t nm; + time_t my_generation = time(NULL); + int s, cpsize; + int found; + listen_info li; + u_int16_t port; + ip_match_element ime; + u_char *mask_ptr; + struct in_addr mask; + + ns_debug(ns_log_default, 1, "getnetconf(generation %lu)", + (u_long)my_generation); + + /* Get interface list from system. */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + if (!periodic_scan) + ns_panic(ns_log_default, 1, "socket(SOCK_RAW): %s", + strerror(errno)); + ns_error(ns_log_default, "socket(SOCK_RAW): %s", + strerror(errno)); + return; + } + + if (!iflist_initialized) { + INIT_LIST(iflist); + iflist_initialized = 1; + } + + if (local_addresses != NULL) + free_ip_match_list(local_addresses); + local_addresses = new_ip_match_list(); + if (local_networks != NULL) + free_ip_match_list(local_networks); + local_networks = new_ip_match_list(); + + ifc.ifc_len = sizeof buf; + ifc.ifc_buf = buf; + if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) + ns_panic(ns_log_default, 1, "get interface configuration: %s", + strerror(errno)); + + ns_debug(ns_log_default, 2, "getnetconf: SIOCGIFCONF: ifc_len = %d", + ifc.ifc_len); + + /* Parse system's interface list and open some sockets. */ + cplim = buf + ifc.ifc_len; /* skip over if's with big ifr_addr's */ + for (cp = buf; cp < cplim; cp += cpsize) { + memcpy(&ifreq, cp, sizeof ifreq); +#if defined HAVE_SA_LEN +#ifdef FIX_ZERO_SA_LEN + if (ifreq.ifr_addr.sa_len == 0) + ifreq.ifr_addr.sa_len = 16; +#endif + cpsize = sizeof ifreq.ifr_name + ifreq.ifr_addr.sa_len; +#elif defined SIOCGIFCONF_ADDR + cpsize = sizeof ifreq; +#else + cpsize = sizeof ifreq.ifr_name; + if (ioctl(s, SIOCGIFADDR, (char *)&ifreq) < 0) { + ns_notice(ns_log_default, + "get interface addr (%s): %s", + ifreq.ifr_name, strerror(errno)); + continue; + } +#endif + ina = ina_get((u_char *)&((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr); + if (ifreq.ifr_addr.sa_family != AF_INET) { + ns_debug(ns_log_default, 2, + "getnetconf: af %d != INET", + ifreq.ifr_addr.sa_family); + continue; + } + ns_debug(ns_log_default, 1, + "getnetconf: considering %s [%s]", + ifreq.ifr_name, inet_ntoa(ina)); + /* + * Don't test IFF_UP, packets may still be received at this + * address if any other interface is up. + */ + if (ina_hlong(ina) == INADDR_ANY) { + ns_debug(ns_log_default, 2, + "getnetconf: INADDR_ANY, ignoring."); + continue; + } + + INSIST(server_options != NULL); + INSIST(server_options->listen_list != NULL); + + found=0; + for (li = server_options->listen_list->first; + li != NULL; + li = li->next) { + if (ip_match_address(li->list, ina) > 0) { + found++; + /* + * Look for an already existing source + * interface address/port pair. + * This happens mostly when reinitializing. + * Also, if the machine has multiple point to + * point interfaces, then the local address + * may appear more than once. + */ + ifp = if_find(ina, li->port); + if (ifp != NULL) { + ns_debug(ns_log_default, 1, + "dup interface addr [%s].%u (%s)", + inet_ntoa(ina), + ntohs(li->port), + ifreq.ifr_name); + ifp->gen = my_generation; + continue; + } + + ifp = (interface *)memget(sizeof *ifp); + if (!ifp) + ns_panic(ns_log_default, 1, + "memget(interface)", NULL); + memset(ifp, 0, sizeof *ifp); + APPEND(iflist, ifp, link); + ifp->addr = ina; + ifp->port = li->port; + ifp->gen = my_generation; + ifp->flags = 0; + ifp->dfd = -1; + ifp->sfd = -1; + if (opensocket_d(ifp) < 0 || + opensocket_s(ifp) < 0) { + dq_remove(ifp); + found = 0; + break; + } + ns_info(ns_log_default, + "listening on [%s].%u (%s)", + inet_ntoa(ina), ntohs(li->port), + ifreq.ifr_name); + } + } + if (!found) + ns_debug(ns_log_default, 1, + "not listening on addr [%s] (%s)", + inet_ntoa(ina), ifreq.ifr_name); + + /* + * Add this interface's address to the list of local + * addresses if we haven't added it already. + */ + if (ip_match_address(local_addresses, ina) < 0) { + ime = new_ip_match_pattern(ina, 32); + add_to_ip_match_list(local_addresses, ime); + } + + /* + * Get interface flags. + */ + if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + ns_notice(ns_log_default, "get interface flags: %s", + strerror(errno)); + continue; + } + + if ((ifreq.ifr_flags & IFF_POINTOPOINT)) { + /* + * The local network for a PPP link is just the + * two ends of the link, so for each endpoint we + * add a pattern that will only match the endpoint. + */ + if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { + ns_notice(ns_log_default, "get dst addr: %s", + strerror(errno)); + continue; + } + + mask.s_addr = htonl(INADDR_BROADCAST); + + /* + * Our end. + * + * Only add it if we haven't seen it before. + */ + if (ip_match_network(local_networks, ina, mask) < 0) { + ime = new_ip_match_pattern(ina, 32); + add_to_ip_match_list(local_networks, ime); + } + + /* + * The other end. + */ + ina = ((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr; + /* + * Only add it if we haven't seen it before. + */ + if (ip_match_network(local_networks, ina, mask) < 0) { + ime = new_ip_match_pattern(ina, 32); + add_to_ip_match_list(local_networks, ime); + } + } else { + /* + * Add this interface's network and netmask to the + * list of local networks. + */ + +#ifdef SIOCGIFNETMASK /* XXX */ + if (ioctl(s, SIOCGIFNETMASK, (char *)&ifreq) < 0) { + ns_notice(ns_log_default, "get netmask: %s", + strerror(errno)); + continue; + } + /* + * Use ina_get because the ifreq structure might not + * be aligned. + */ + mask_ptr = (u_char *) + &((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr; + mask = ina_get(mask_ptr); +#else + mask = net_mask(ina); +#endif + + ina.s_addr &= mask.s_addr; /* make network address */ + + /* + * Only add it if we haven't seen it before. + */ + if (ip_match_network(local_networks, ina, mask) < 0) { + ime = new_ip_match_mask(ina, mask); + add_to_ip_match_list(local_networks, ime); + } + } + } + close(s); + + ns_debug(ns_log_default, 7, "local addresses:"); + dprint_ip_match_list(ns_log_default, local_addresses, 2, "", ""); + ns_debug(ns_log_default, 7, "local networks:"); + dprint_ip_match_list(ns_log_default, local_networks, 2, "", ""); + + /* + * now go through the iflist 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. + */ + dq_remove_gen(my_generation); + + if (EMPTY(iflist)) + ns_warning(ns_log_default, "not listening on any interfaces"); +} + +/* opensocket_d(ifp) + * Open datagram socket bound to interface address. + * Returns: + * 0 on success. + * -1 on failure. + */ +static int +opensocket_d(interface *ifp) { + struct sockaddr_in nsa; + const int on = 1; + int m, n; + int fd; + + memset(&nsa, 0, sizeof nsa); + nsa.sin_family = AF_INET; + nsa.sin_addr = ifp->addr; + nsa.sin_port = ifp->port; + + if ((ifp->dfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + ns_error(ns_log_default, "socket(SOCK_DGRAM): %s", + strerror(errno)); + return (-1); + } +#ifdef F_DUPFD /* XXX */ + /* + * Leave a space for stdio to work in. + */ + if ((fd = fcntl(ifp->dfd, F_DUPFD, 20)) != -1) { + close(ifp->dfd); + ifp->dfd = fd; + } else + ns_notice(ns_log_default, "fcntl(dfd, F_DUPFD, 20): %s", + strerror(errno)); +#endif + ns_debug(ns_log_default, 1, "ifp->addr %s d_dfd %d", + sin_ntoa(nsa), ifp->dfd); + if (setsockopt(ifp->dfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)) != 0) { + ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", + strerror(errno)); + /* XXX press on regardless, this is not too serious. */ + } +#ifdef SO_RCVBUF /* XXX */ + m = sizeof n; + if ((getsockopt(ifp->dfd, SOL_SOCKET, SO_RCVBUF, (char*)&n, &m) >= 0) + && (m == sizeof n) + && (n < rbufsize)) { + (void) setsockopt(ifp->dfd, SOL_SOCKET, SO_RCVBUF, + (char *)&rbufsize, sizeof(rbufsize)); + } +#endif /* SO_RCVBUF */ + if (bind(ifp->dfd, (struct sockaddr *)&nsa, sizeof nsa)) { + ns_error(ns_log_default, "bind(dfd=%d, %s): %s", + ifp->dfd, sin_ntoa(nsa), strerror(errno)); + return (-1); + } + if (evSelectFD(ev, ifp->dfd, EV_READ, datagram_read, ifp, + &ifp->evID_d) == -1) { + ns_error(ns_log_default, "evSelectFD(dfd=%d): %s", + ifp->dfd, strerror(errno)); + return (-1); + } + ifp->flags |= INTERFACE_FILE_VALID; + return (0); +} + +/* opensocket_s(ifp) + * Open stream (listener) socket bound to interface address. + * Returns: + * 0 on success. + * -1 on failure. + */ +static int +opensocket_s(interface *ifp) { + struct sockaddr_in nsa; + const int on = 1; + int n; + int fd; + + memset(&nsa, 0, sizeof nsa); + nsa.sin_family = AF_INET; + nsa.sin_addr = ifp->addr; + nsa.sin_port = ifp->port; + + /* + * Open stream (listener) port. + */ + n = 0; + again: + if ((ifp->sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ns_error(ns_log_default, "socket(SOCK_STREAM): %s", + strerror(errno)); + return (-1); + } +#ifdef F_DUPFD /* XXX */ + /* + * Leave a space for stdio to work in. + */ + if ((fd = fcntl(ifp->sfd, F_DUPFD, 20)) != -1) { + close(ifp->sfd); + ifp->sfd = fd; + } else + ns_notice(ns_log_default, "fcntl(sfd, F_DUPFD, 20): %s", + strerror(errno)); +#endif + if (setsockopt(ifp->sfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof on) != 0) { + ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", + strerror(errno)); + /* Consider that your first warning of trouble to come. */ + } + if (bind(ifp->sfd, (struct sockaddr *)&nsa, sizeof nsa) < 0) { + if (errno != EADDRINUSE || ++n > 4) { + if (errno == EADDRINUSE) + ns_error(ns_log_default, + "There may be a name server already running on %s", + sin_ntoa(nsa)); + else + ns_error(ns_log_default, + "bind(sfd=%d, %s): %s", ifp->sfd, + sin_ntoa(nsa), strerror(errno)); + return (-1); + } + + /* Retry opening the socket a few times */ + close(ifp->sfd); + ifp->sfd = -1; + sleep(30); + goto again; + } + if (evListen(ev, ifp->sfd, 5/*XXX*/, stream_accept, ifp, &ifp->evID_s) + == -1) { + ns_error(ns_log_default, "evListen(sfd=%d): %s", + ifp->sfd, strerror(errno)); + return (-1); + } + ifp->flags |= INTERFACE_CONN_VALID; + return (0); +} + +/* opensocket_f() + * Open datagram socket bound to no particular interface; use for ns_forw + * and sysquery. + */ +void +opensocket_f() { + static struct sockaddr_in prev_qsrc; + static int been_here; + static interface *prev_ifp; + struct sockaddr_in nsa; + const int on = 1; + int n, need_close; + interface *ifp; + + need_close = 0; + if (been_here) { + if (prev_ifp != NULL) + prev_ifp->flags &= ~INTERFACE_FORWARDING; + else if (server_options->query_source.sin_port == htons(0) || + prev_qsrc.sin_addr.s_addr != + server_options->query_source.sin_addr.s_addr || + prev_qsrc.sin_port != + server_options->query_source.sin_port) + need_close = 1; + } else + ds = -1; + + been_here = 1; + INSIST(server_options != NULL); + + if (need_close) { + evDeselectFD(ev, ds_evID); + close(ds); + ds = -1; + } + + /* + * If we're already listening on the query_source address and port, + * we don't need to open another socket. We mark the interface, so + * we'll notice we're in trouble if it goes away. + */ + ifp = if_find(server_options->query_source.sin_addr, + server_options->query_source.sin_port); + if (ifp != NULL) { + ifp->flags |= INTERFACE_FORWARDING; + prev_ifp = ifp; + ds = ifp->dfd; + ns_info(ns_log_default, "forwarding source address is %s", + sin_ntoa(server_options->query_source)); + return; + } + + /* + * If we're already using the correct query source, we're done. + */ + if (ds >= 0) + return; + + prev_qsrc = server_options->query_source; + prev_ifp = NULL; + + if ((ds = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + ns_panic(ns_log_default, 1, "socket(SOCK_DGRAM): %s", + strerror(errno)); + if (setsockopt(ds, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof on) != 0) { + ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", + strerror(errno)); + /* XXX press on regardless, this is not too serious. */ + } + if (bind(ds, (struct sockaddr *)&server_options->query_source, + sizeof server_options->query_source) < 0) + ns_panic(ns_log_default, 1, "opensocket_f: bind(%s): %s", + sin_ntoa(server_options->query_source), + strerror(errno)); + + n = sizeof nsa; + if (getsockname(ds, (struct sockaddr *)&nsa, &n) < 0) + ns_panic(ns_log_default, 1, "opensocket_f: getsockaddr: %s", + strerror(errno)); + + ns_debug(ns_log_default, 1, "fwd ds %d addr %s", ds, sin_ntoa(nsa)); + ns_info(ns_log_default, "Forwarding source address is %s", + sin_ntoa(nsa)); + + if (evSelectFD(ev, ds, EV_READ, datagram_read, NULL, &ds_evID) == -1) + ns_panic(ns_log_default, 1, "evSelectFD(fd %d): %s", + (void *)ds, strerror(errno)); + /* XXX: should probably use a different FileFunc that only accepts + * responses, since requests on this socket make no sense. + */ +} + +static void +setdebug(int new_debug) { +#ifdef DEBUG + int old_debug; + + if (!new_debug) + ns_debug(ns_log_default, 1, "Debug off"); + old_debug = debug; + debug = new_debug; + log_option(log_ctx, LOG_OPTION_DEBUG, debug); + log_option(log_ctx, LOG_OPTION_LEVEL, debug); + evSetDebug(ev, debug, log_get_stream(eventlib_channel)); + if (debug) { + if (!old_debug) + open_special_channels(); + ns_debug(ns_log_default, 1, "Debug level %d", debug); + if (!old_debug) { + ns_debug(ns_log_default, 1, "Version = %s", Version); + ns_debug(ns_log_default, 1, "conffile = %s", conffile); + } + } +#endif +} + +static SIG_FN +onhup(int sig) { + ns_need(MAIN_NEED_RELOAD); +} + +static SIG_FN +onintr(int sig) { + ns_need(MAIN_NEED_EXIT); +} + +static SIG_FN +setdumpflg(int sig) { + ns_need(MAIN_NEED_DUMP); +} + +#ifdef DEBUG +static SIG_FN +setIncrDbgFlg(int sig) { + desired_debug++; + ns_need(MAIN_NEED_DEBUG); +} + +static SIG_FN +setNoDbgFlg(int sig) { + desired_debug = 0; + ns_need(MAIN_NEED_DEBUG); +} +#endif /*DEBUG*/ + +#if defined(QRYLOG) && defined(SIGWINCH) +static SIG_FN +setQrylogFlg(int sig) { + ns_need(MAIN_NEED_QRYLOG); +} +#endif /*QRYLOG && SIGWINCH*/ + +static SIG_FN +setstatsflg(int sig) { + ns_need(MAIN_NEED_STATSDUMP); +} + +static SIG_FN +discard_pipe(int sig) { +#ifdef SIGPIPE_ONE_SHOT + int saved_errno = errno; + set_signal_handler(SIGPIPE, discard_pipe); + errno = saved_errno; +#endif +} + +/* +** Routines for managing stream queue +*/ + +static struct qstream * +sq_add() { + struct qstream *sqp; + + if (!(sqp = (struct qstream *)memget(sizeof *sqp))) { + ns_error(ns_log_default, "sq_add: memget: %s", + strerror(errno)); + return (NULL); + } + memset(sqp, 0, sizeof *sqp); + ns_debug(ns_log_default, 3, "sq_add(%#lx)", (u_long)sqp); + + sqp->flags = 0; + /* XXX should init other fields too? */ + sqp->s_next = streamq; + streamq = sqp; + return (sqp); +} + +/* sq_remove(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 +sq_remove(struct qstream *qp) { + struct qstream *qsp; + + ns_debug(ns_log_default, 2, "sq_remove(%#lx, %d) rfcnt=%d", + (u_long)qp, qp->s_rfd, qp->s_refcnt); + + if (qp->s_wbuf != NULL) { + memput(qp->s_wbuf, qp->s_wbuf_end - qp->s_wbuf); + qp->s_wbuf = NULL; + } + if (qp->flags & STREAM_MALLOC) + memput(qp->s_buf, qp->s_bufsize); + if (qp->flags & STREAM_READ_EV) + INSIST_ERR(evCancelRW(ev, qp->evID_r) != -1); + if (qp->flags & STREAM_WRITE_EV) + INSIST_ERR(evDeselectFD(ev, qp->evID_w) != -1); + if (qp->flags & STREAM_CONNECT_EV) + INSIST_ERR(evCancelConn(ev, qp->evID_c) != -1); + if (qp->flags & STREAM_AXFR) + ns_freexfr(qp); + (void) close(qp->s_rfd); + if (qp == streamq) + streamq = qp->s_next; + else { + for (qsp = streamq; + qsp && (qsp->s_next != qp); + qsp = qsp->s_next) + (void)NULL; + if (qsp) + qsp->s_next = qp->s_next; + } + memput(qp, sizeof *qp); +} + +/* void + * sq_flush(allbut) + * call sq_remove() on all open streams except `allbut' + * side effects: + * global list `streamq' modified + * idiocy: + * is N^2 due to the scan inside of sq_remove() + */ +void +sq_flush(struct qstream *allbut) { + struct qstream *sp, *spnext; + + for (sp = streamq; sp != NULL; sp = spnext) { + spnext = sp->s_next; + if (sp != allbut) + sq_remove(sp); + } +} + +/* int + * sq_openw(qs, buflen) + * add a write buffer to a stream + * return: + * 0 = success + * -1 = failure (check errno) + */ +int +sq_openw(struct qstream *qs, int buflen) { +#ifdef SO_LINGER /* XXX */ + static const struct linger ll = { 1, 120 }; +#endif + + INSIST(qs->s_wbuf == NULL); + qs->s_wbuf = (u_char *)memget(buflen); + if (qs->s_wbuf == NULL) + return (-1); + qs->s_wbuf_send = qs->s_wbuf; + qs->s_wbuf_free = qs->s_wbuf; + qs->s_wbuf_end = qs->s_wbuf + buflen; +#ifdef SO_LINGER /* XXX */ + /* kernels that map pages for IO end up failing if the pipe is full + * at exit and we take away the final buffer. this is really a kernel + * bug but it's harmless on systems that are not broken, so... + */ + setsockopt(qs->s_rfd, SOL_SOCKET, SO_LINGER, (char *)&ll, sizeof ll); +#endif + return (0); +} + +/* static void + * sq_dowrite(qs) + * try to submit data to the system, remove it from our queue. + */ +static int +sq_dowrite(struct qstream *qs) { + if (qs->s_wbuf_free > qs->s_wbuf_send) { + int n = write(qs->s_rfd, qs->s_wbuf_send, + qs->s_wbuf_free - qs->s_wbuf_send); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN +#if (EWOULDBLOCK != EAGAIN) + && errno != EWOULDBLOCK +#endif + ) + return (-1); + return (0); + } + qs->s_wbuf_send += n; + if (qs->s_wbuf_free > qs->s_wbuf_send) { + /* XXX: need some kind of delay here during which the + * socket will be deselected so we don't spin. + */ + n = qs->s_wbuf_free - qs->s_wbuf_send; + memmove(qs->s_wbuf, qs->s_wbuf_send, n); + qs->s_wbuf_send = qs->s_wbuf; + qs->s_wbuf_free = qs->s_wbuf + n; + } + } + if (qs->s_wbuf_free == qs->s_wbuf_send) + qs->s_wbuf_free = qs->s_wbuf_send = qs->s_wbuf; + return (0); +} + +/* void + * sq_flushw(qs) + * called when the socket becomes writable and we want to flush our + * buffers and the system's socket buffers. use as a closure with + * sq_writeh(). + */ +void +sq_flushw(struct qstream *qs) { + if (qs->s_wbuf_free == qs->s_wbuf_send) { + sq_writeh(qs, NULL); + sq_done(qs); + } +} + +/* static void + * sq_writable(ctx, uap, fd, evmask) + * glue between eventlib closures and qstream closures + */ +static void +sq_writable(evContext ctx, void *uap, int fd, int evmask) { + struct qstream *qs = uap; + + INSIST(evmask & EV_WRITE); + INSIST(fd == qs->s_rfd); + if (sq_dowrite(qs) < 0) { + sq_remove(qs); + return; + } + if (qs->s_wbuf_closure + && qs->s_wbuf_end - qs->s_wbuf_free >= HFIXEDSZ+2) /* XXX guess */ + (*qs->s_wbuf_closure)(qs); + if (sq_dowrite(qs) < 0) { + sq_remove(qs); + return; + } +} + +/* int + * sq_writeh(qs, closure) + * register a closure to be called when a stream becomes writable + * return: + * 0 = success + * -1 = failure (check errno) + */ +int +sq_writeh(struct qstream *qs, sq_closure c) { + if (c) { + if (!qs->s_wbuf_closure) { + if (evSelectFD(ev, qs->s_rfd, EV_WRITE, + sq_writable, qs, &qs->evID_w) < 0) { + return (-1); + } + qs->flags |= STREAM_WRITE_EV; + } + } else { + (void) evDeselectFD(ev, qs->evID_w); + qs->flags &= ~STREAM_WRITE_EV; + } + qs->s_wbuf_closure = c; + return (0); +} + +/* int + * sq_write(qs, buf, len) + * queue a message onto the stream, prepended by a two byte length field + * return: + * 0 = success + * -1 = failure (check errno; E2BIG means we can't handle this right now) + */ +int +sq_write(struct qstream *qs, const u_char *buf, int len) { + INSIST(qs->s_wbuf != NULL); + if (NS_INT16SZ + len > qs->s_wbuf_end - qs->s_wbuf_free) { + if (sq_dowrite(qs) < 0) + return (-1); + if (NS_INT16SZ + len > qs->s_wbuf_end - qs->s_wbuf_free) { + errno = E2BIG; + return (-1); + } + } + __putshort(len, qs->s_wbuf_free); + qs->s_wbuf_free += NS_INT16SZ; + memcpy(qs->s_wbuf_free, buf, len); + qs->s_wbuf_free += len; + return (0); +} + +/* int + * sq_here(sp) + * determine whether stream 'sp' is still on the streamq + * return: + * boolean: is it here? + */ +static int +sq_here(struct qstream *sp) { + 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(struct qstream *sp) { + sp->s_refcnt++; +} + +/* + * Note that the current request on a stream has completed, + * and that we should continue looking for requests on the stream. + */ +void +sq_done(struct qstream *sp) { + struct iovec iov; + + if (sp->s_wbuf != NULL) { + memput(sp->s_wbuf, sp->s_wbuf_end - sp->s_wbuf); + sp->s_wbuf = NULL; + } + if (sp->flags & STREAM_AXFR) + ns_freexfr(sp); + sp->s_refcnt = 0; + sp->s_time = tt.tv_sec; + if (sp->flags & STREAM_DONE_CLOSE) { + /* XXX */ + sq_remove(sp); + return; + } + iov = evConsIovec(sp->s_temp, INT16SZ); + if (evRead(ev, sp->s_rfd, &iov, 1, stream_getlen, sp, &sp->evID_r) == + -1) + ns_panic(ns_log_default, 1, "evRead(fd %d): %s", + (void *)sp->s_rfd, strerror(errno)); + sp->flags |= STREAM_READ_EV; +} + +/* void + * dq_remove_gen(gen) + * close/deallocate all the udp sockets (except 0.0.0.0) which are + * not from the current generation. + * side effects: + * global list `iflist' is modified. + */ +void +dq_remove_gen(time_t gen) { + interface *this, *next; + + for (this = HEAD(iflist); this != NULL; this = next) { + next = NEXT(this, link); + if (this->gen != gen && ina_hlong(this->addr) != INADDR_ANY) + dq_remove(this); + } +} + +/* void + * dq_remove_all() + * close/deallocate all interfaces. + * side effects: + * global list `iflist' is modified. + */ +void +dq_remove_all() { + interface *this, *next; + + for (this = HEAD(iflist); this != NULL; this = next) { + next = NEXT(this, link); + /* + * Clear the forwarding flag so we don't panic the server. + */ + this->flags &= ~INTERFACE_FORWARDING; + dq_remove(this); + } +} + +/* void + * dq_remove(interface *this) + * close/deallocate an interface's sockets. called on errors + * or if the interface disappears. + * side effects: + * global list `iflist' is modified. + */ +static void +dq_remove(interface *this) { + ns_notice(ns_log_default, "deleting interface [%s].%u", + inet_ntoa(this->addr), ntohs(this->port)); + + if ((this->flags & INTERFACE_FORWARDING) != 0) + ns_panic(ns_log_default, 0, + "forwarding interface [%s].%u gone", + inet_ntoa(this->addr), + ntohs(this->port)); + + /* Deallocate fields. */ + if ((this->flags & INTERFACE_FILE_VALID) != 0) + (void) evDeselectFD(ev, this->evID_d); + if (this->dfd >= 0) + (void) close(this->dfd); + if ((this->flags & INTERFACE_CONN_VALID) != 0) + (void) evCancelConn(ev, this->evID_s); + if (this->sfd >= 0) + (void) close(this->sfd); + + UNLINK(iflist, this, link); + memput(this, sizeof *this); +} + +/* struct in_addr + * net_mask(ina) + * makes a classful assumption in a classless world, and returns it. + */ +struct in_addr +net_mask(struct in_addr ina) { + u_long hl = ina_hlong(ina); + struct in_addr ret; + + if (IN_CLASSA(hl)) + hl = IN_CLASSA_NET; + else if (IN_CLASSB(hl)) + hl = IN_CLASSB_NET; + else if (IN_CLASSC(hl)) + hl = IN_CLASSC_NET; + else + hl = INADDR_BROADCAST; + ina_ulong(ret) = htonl(hl); + return (ret); +} + +/* aIsUs(addr) + * scan our list of interface addresses for "addr". + * returns: + * 0: address isn't one of our interfaces + * >0: address is one of our interfaces, or INADDR_ANY + */ +int +aIsUs(struct in_addr addr) { + interface *ifp; + + if (ina_hlong(addr) == INADDR_ANY || if_find(addr, 0) != NULL) + return (1); + return (0); +} + +/* interface * + * if_find(addr, port) + * scan our list of interface addresses for "addr" and port. + * port == 0 means match any port + * returns: + * pointer to interface with this address/port, or NULL if there isn't + * one. + */ +static interface * +if_find(struct in_addr addr, u_int16_t port) { + interface *ifp; + + for (ifp = HEAD(iflist); ifp != NULL; ifp = NEXT(ifp, link)) + if (ina_equal(addr, ifp->addr)) + if (port == 0 || ifp->port == port) + break; + return (ifp); +} + +/* + * These are here in case we ever want to get more clever, like perhaps + * using a bitmap to keep track of outstanding queries and a random + * allocation scheme to make it a little harder to predict them. Note + * that the resolver will need the same protection so the cleverness + * should be put there rather than here; this is just an interface layer. + */ + +void +nsid_init() { + nsid_state = res_randomid(); +} + +u_int16_t +nsid_next() { + if (nsid_state == 65535) + nsid_state = 0; + else + nsid_state++; + return (nsid_state); +} + +static void +deallocate_everything(void) { + FILE *f; + + f = write_open(server_options->memstats_filename); + + ns_freestats(); + qflush(); + sq_flush(NULL); + free_addinfo(); + ns_shutdown(); + dq_remove_all(); + if (local_addresses != NULL) + free_ip_match_list(local_addresses); + if (local_networks != NULL) + free_ip_match_list(local_networks); + destroyservicelist(); + destroyprotolist(); + shutdown_logging(); + evDestroy(ev); + if (conffile != NULL) + freestr(conffile); + if (user_name != NULL) + freestr(user_name); + if (group_name != NULL) + freestr(group_name); + if (chroot_dir != NULL) + freestr(chroot_dir); + if (f != NULL) { + memstats(f); + (void)fclose(f); + } +} + +static void +ns_exit(void) { + ns_info(ns_log_default, "named shutting down"); +#ifdef BIND_UPDATE + dynamic_about_to_exit(); +#endif + if (server_options && server_options->pid_filename) + (void)unlink(server_options->pid_filename); + ns_logstats(ev, NULL, evNowTime(), evConsTime(0, 0)); + if (NS_OPTION_P(OPTION_DEALLOC_ON_EXIT)) + deallocate_everything(); + exit(0); +} + +static void +use_desired_debug(void) { +#ifdef DEBUG + sigset_t set; + int bad; + + /* protect against race conditions by blocking debugging signals */ + + if (sigemptyset(&set) < 0) { + ns_error(ns_log_os, + "sigemptyset failed in use_desired_debug: %s", + strerror(errno)); + return; + } + if (sigaddset(&set, SIGUSR1) < 0) { + ns_error(ns_log_os, + "sigaddset SIGUSR1 failed in use_desired_debug: %s", + strerror(errno)); + return; + } + if (sigaddset(&set, SIGUSR2) < 0) { + ns_error(ns_log_os, + "sigaddset SIGUSR2 failed in use_desired_debug: %s", + strerror(errno)); + return; + } + if (sigprocmask(SIG_BLOCK, &set, NULL) < 0) { + ns_error(ns_log_os, + "sigprocmask to block USR1 and USR2 failed: %s", + strerror(errno)); + return; + } + setdebug(desired_debug); + if (sigprocmask(SIG_UNBLOCK, &set, NULL) < 0) + ns_error(ns_log_os, + "sigprocmask to unblock USR1 and USR2 failed: %s", + strerror(errno)); +#endif +} + +static void +toggle_qrylog(void) { + qrylog = !qrylog; + ns_notice(ns_log_default, "query log %s\n", qrylog ?"on" :"off"); +} + +#ifdef BIND_NOTIFY +static void +do_notify_after_load(void) { + evDo(ev, (const void *)notify_after_load); +} +#endif + +/* + * This is a functional interface to the global needs and options. + */ + +static const struct need_handler { + int need; + void (*handler)(void); + } need_handlers[] = { + { MAIN_NEED_RELOAD, ns_reload }, + { MAIN_NEED_MAINT, ns_maint }, + { MAIN_NEED_ENDXFER, endxfer }, + { MAIN_NEED_ZONELOAD, loadxfer }, + { MAIN_NEED_DUMP, doadump }, + { MAIN_NEED_STATSDUMP, ns_stats }, + { MAIN_NEED_EXIT, ns_exit }, + { MAIN_NEED_QRYLOG, toggle_qrylog }, + { MAIN_NEED_DEBUG, use_desired_debug }, +#ifdef BIND_NOTIFY + { MAIN_NEED_NOTIFY, do_notify_after_load }, +#endif + { 0, NULL } + }; + +void +ns_setoption(int option) { + ns_warning(ns_log_default, "used obsolete ns_setoption(%d)", option); +} + +void +ns_need(int need) { + needs |= need; +} + +int +ns_need_p(int need) { + return ((needs & need) != 0); +} + +static void +ns_handle_needs() { + const struct need_handler *nhp; + + for (nhp = need_handlers; nhp->need && nhp->handler; nhp++) { + if ((needs & nhp->need) != 0) { + /* + * Turn off flag first, handler might turn it back on. + */ + needs &= ~nhp->need; + (*nhp->handler)(); + } + } +} + + +void +writestream(struct qstream *sp, const u_char *msg, int msglen) { + if (sq_openw(sp, msglen + INT16SZ) == -1) { + sq_remove(sp); + return; + } + if (sq_write(sp, msg, msglen) == -1) { + sq_remove(sp); + return; + } + sq_writeh(sp, sq_flushw); +} + +static void +set_signal_handler(int sig, SIG_FN (*handler)()) { + struct sigaction sa; + + memset(&sa, 0, sizeof sa); + sa.sa_handler = handler; + if (sigemptyset(&sa.sa_mask) < 0) { + ns_error(ns_log_os, + "sigemptyset failed in set_signal_handler(%d): %s", + sig, strerror(errno)); + return; + } + if (sigaction(sig, &sa, NULL) < 0) + ns_error(ns_log_os, + "sigaction failed in set_signal_handler(%d): %s", + sig, strerror(errno)); +} + +static void +init_signals() { + set_signal_handler(SIGINT, setdumpflg); + set_signal_handler(SIGILL, setstatsflg); +#ifdef DEBUG + set_signal_handler(SIGUSR1, setIncrDbgFlg); + set_signal_handler(SIGUSR2, setNoDbgFlg); +#endif + set_signal_handler(SIGHUP, onhup); +#if defined(SIGWINCH) && defined(QRYLOG) /* XXX */ + set_signal_handler(SIGWINCH, setQrylogFlg); +#endif + set_signal_handler(SIGCHLD, reapchild); + set_signal_handler(SIGPIPE, discard_pipe); + set_signal_handler(SIGTERM, onintr); +#if defined(SIGXFSZ) /* XXX */ + /* Wierd DEC Hesiodism, harmless. */ + set_signal_handler(SIGXFSZ, onhup); +#endif +} + +static int +only_digits(const char *s) { + if (*s == '\0') + return (0); + while (*s != '\0') { + if (!isdigit(*s)) + return (0); + s++; + } + return (1); +} diff --git a/contrib/bind/bin/named/ns_maint.c b/contrib/bind/bin/named/ns_maint.c new file mode 100644 index 0000000..ad6e7f8 --- /dev/null +++ b/contrib/bind/bin/named/ns_maint.c @@ -0,0 +1,1210 @@ +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)ns_maint.c 4.39 (Berkeley) 3/2/91"; +static char rcsid[] = "$Id: ns_maint.c,v 8.38 1998/03/16 19:40:25 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <signal.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +static int xfers_running, /* # of xfers running */ + xfers_deferred, /* # of needed xfers not run yet */ + qserials_running, + nxfers(struct zoneinfo *, int), + bottom_of_zone(struct databuf *, int); + +static void startxfer(struct zoneinfo *), + abortxfer(struct zoneinfo *), + addxfer(struct zoneinfo *), + tryxfer(void), + purge_z_2(struct hashbuf *, int); + +#define qserial_qfull() (qserials_running == MAXQSERIAL) + +static time_t stats_time; + + /* State of all running zone transfers */ +static struct { + pid_t xfer_pid; + int xfer_state; /* see below */ + WAIT_T xfer_status; +} xferstatus[MAX_XFERS_RUNNING]; + +#define XFER_IDLE 0 +#define XFER_RUNNING 1 +#define XFER_DONE 2 + + +/* + * Perform maintenance on all zones that need it. + */ +void +ns_maint() { + struct zoneinfo *zp; + int zonenum, deleted; + + gettime(&tt); + + ns_debug(ns_log_maint, 1, "ns_maint()"); + + for (zp = zones, zonenum = 0; zp < &zones[nzones]; zp++, zonenum++) + zone_maint(zp); + ns_debug(ns_log_maint, 1, "exit ns_maint()"); +} + +/* + * Perform routine zone maintenance. + */ +void +zone_maint(struct zoneinfo *zp) { + gettime(&tt); + + ns_debug(ns_log_maint, 1, "zone_maint('%s'); now %lu", + zp->z_origin[0] == '\0' ? "." : zp->z_origin, + (u_long)tt.tv_sec); + +#ifdef DEBUG + if (debug >= 2) + printzoneinfo((zp - zones), ns_log_maint, 2); +#endif + + switch (zp->z_type) { + + case Z_SECONDARY: + /*FALLTHROUGH*/ +#ifdef STUBS + case Z_STUB: +#endif + if (zp->z_serial != 0 && + ((zp->z_lastupdate+zp->z_expire) < (u_int32_t)tt.tv_sec)) { + zp->z_serial = 0; + } + if (zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) { + ns_retrytime(zp, tt.tv_sec); + break; + } + if (zp->z_flags & Z_XFER_RUNNING) { + abortxfer(zp); + /* + * Check again in 30 seconds in case the first + * abort doesn't work. + */ + if (zp->z_time != 0 && zp->z_time <= tt.tv_sec) + zp->z_time = tt.tv_sec + 30; + break; + } + qserial_query(zp); + break; + +#ifdef BIND_UPDATE + case Z_PRIMARY: + if (! (zp->z_flags & Z_DYNAMIC)) + break; + if (tt.tv_sec >= zp->z_soaincrtime && + zp->z_soaincrintvl > 0 && + zp->z_flags & Z_NEED_SOAUPDATE) { + if (incr_serial(zp) < 0) { + /* Try again later. */ + ns_error(ns_log_maint, + "error updating serial number for %s from %d", + zp->z_origin, + zp->z_serial); + zp->z_soaincrtime = 0; + (void)schedule_soa_update(zp, 0); + } + + } + if (tt.tv_sec >= zp->z_dumptime && + zp->z_dumpintvl > 0 && + zp->z_flags & Z_NEED_DUMP) { + if (zonedump(zp) < 0) { + /* Try again later. */ + ns_error(ns_log_maint, + "zone dump for '%s' failed, rescheduling", + zp->z_origin); + zp->z_dumptime = 0; + (void)schedule_dump(zp); + } + } + break; +#endif /* BIND_UPDATE */ + + default: + break; + } + + /* + * It is essential that we never try to set a timer in the past + * or for now because doing so could cause an infinite loop. + */ + INSIST(zp->z_time == 0 || zp->z_time > tt.tv_sec); + + sched_zone_maint(zp); +} + +static void +do_zone_maint(evContext ctx, void *uap, struct timespec due, + struct timespec inter) { + ztimer_info zti = uap; + struct zoneinfo *zp; + + INSIST(zti != NULL); + + ns_debug(ns_log_maint, 1, "do_zone_maint for zone %s (class %s)", + zti->name, p_class(zti->class)); + zp = find_zone(zti->name, zti->type, zti->class); + if (zp == NULL) { + ns_error(ns_log_maint, + "do_zone_maint: %s zone '%s' (class %s) is not authoritative", + zoneTypeString(zp), zti->name, + p_class(zti->class)); + return; + } + + free_zone_timerinfo(zp); + + zp->z_flags &= ~Z_TIMER_SET; + zone_maint(zp); +} + +/* + * Figure out the next maintenance time for the zone and set a timer. + */ +void +sched_zone_maint(struct zoneinfo *zp) { + time_t next_maint = (time_t)0; + char *zone_name; + ztimer_info zti; + + if (zp->z_time != 0) + next_maint = zp->z_time; +#ifdef BIND_UPDATE + if (zp->z_type == z_master && (zp->z_flags & Z_DYNAMIC)) { + if (zp->z_soaincrintvl > 0 && + (next_maint == 0 || next_maint > zp->z_soaincrtime)) + next_maint = zp->z_soaincrtime; + if (zp->z_dumpintvl > 0 && + (next_maint == 0 || next_maint > zp->z_dumptime)) + next_maint = zp->z_dumptime; + } +#endif + + if (next_maint != 0) { + if (next_maint < tt.tv_sec) + next_maint = tt.tv_sec; + + if (zp->z_flags & Z_TIMER_SET) { + if (next_maint == zp->z_nextmaint) { + ns_debug(ns_log_maint, 1, + "no schedule change for zone '%s'", + zp->z_origin[0] == '\0' ? "." : + zp->z_origin); + return; + } + + if (evResetTimer(ev, zp->z_timer, + do_zone_maint, zp->z_timerinfo, + evConsTime(next_maint, 0), + evConsTime(0, 0)) < 0) { + ns_error(ns_log_maint, + "evChangeTimer failed in sched_zone_maint for zone '%s': %s", + zp->z_origin[0] == '\0' ? "." : + zp->z_origin, + strerror(errno)); + return; + } + } else { + zti = (ztimer_info)memget(sizeof *zti); + if (zti == NULL) + ns_panic(ns_log_maint, 1, + "memget failed in sched_zone_maint"); + zti->name = savestr(zp->z_origin, 1); + zti->class = zp->z_class; + zti->type = zp->z_type; + if (evSetTimer(ev, do_zone_maint, zti, + evConsTime(next_maint, 0), + evConsTime(0, 0), &zp->z_timer) < 0) { + ns_error(ns_log_maint, + "evSetTimer failed in sched_zone_maint for zone '%s': %s", + zp->z_origin[0] == '\0' ? "." : + zp->z_origin, + strerror(errno)); + return; + } + zp->z_flags |= Z_TIMER_SET; + zp->z_timerinfo = zti; + } + ns_debug(ns_log_maint, 1, + "next maintenance for zone '%s' in %lu sec", + zp->z_origin[0] == '\0' ? "." : zp->z_origin, + (u_long)(next_maint - tt.tv_sec)); + } else { + if (zp->z_flags & Z_TIMER_SET) { + free_zone_timerinfo(zp); + if (evClearTimer(ev, zp->z_timer) < 0) + ns_error(ns_log_maint, + "evClearTimer failed in sched_zone_maint for zone '%s': %s", + zp->z_origin[0] == '\0' ? "." : + zp->z_origin, + strerror(errno)); + zp->z_flags &= ~Z_TIMER_SET; + } + ns_debug(ns_log_maint, 1, + "no scheduled maintenance for zone '%s'", + zp->z_origin[0] == '\0' ? "." : zp->z_origin); + } + zp->z_nextmaint = next_maint; +} + +void +ns_cleancache(evContext ctx, void *uap, + struct timespec due, + struct timespec inter) +{ + int deleted; + + gettime(&tt); + INSIST(uap == NULL); + deleted = clean_cache(hashtab, 0); + ns_info(ns_log_maint, "Cleaned cache of %d RR%s", + deleted, (deleted==1) ? "" : "s"); +} + +/* + * Mark a zone "up to date" after named-xfer tells us this or we + * discover it through the qserial_*() logic. + * The caller is responsible for calling sched_zone_maint(zp). + */ +static void +markUpToDate(struct zoneinfo *zp) { + struct stat f_time; + + zp->z_flags &= ~Z_SYSLOGGED; + zp->z_lastupdate = tt.tv_sec; + ns_refreshtime(zp, tt.tv_sec); + /* + * 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) { + struct timeval t[2]; + + t[0] = tt; + t[1] = tt; + (void) utimes(zp->z_source, t); + } + /* 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? */ +} + +void +qserial_retrytime(struct zoneinfo *zp, time_t timebase) { + zp->z_time = timebase + 5 + (rand() % 25); +} + +/* + * Query for the serial number of a zone, so that we can check to see if + * we need to transfer it. If there are too many outstanding serial + * number queries, we'll try again later. + * The caller is responsible for calling sched_zone_maint(zp). + */ +void +qserial_query(struct zoneinfo *zp) { + struct qinfo *qp; + + ns_debug(ns_log_default, 1, "qserial_query(%s)", zp->z_origin); + + if (qserial_qfull()) { + qserial_retrytime(zp, tt.tv_sec); + return; + } + + qp = sysquery(zp->z_origin, zp->z_class, T_SOA, + zp->z_addr, zp->z_addrcnt, QUERY); + if (!qp) { + ns_info(ns_log_default, "qserial_query(%s): sysquery FAILED", + zp->z_origin); + /* XXX - this is bad, we should do something */ + qserial_retrytime(zp, tt.tv_sec); + return; + } + qp->q_flags |= Q_ZSERIAL; + qp->q_zquery = zp; + zp->z_flags |= Z_QSERIAL; + zp->z_xaddr = inaddr_any; + ns_refreshtime(zp, tt.tv_sec); + qserials_running++; + ns_debug(ns_log_default, 1, "qserial_query(%s) QUEUED", zp->z_origin); +} + +void +qserial_answer(struct qinfo *qp, u_int32_t serial, struct sockaddr_in from) { + struct zoneinfo *zp = qp->q_zquery; + + ns_debug(ns_log_default, 1, "qserial_answer(%s, %u)", 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. */ + ns_info(ns_log_default, "Err/TO getting serial# for \"%s\"", + zp->z_origin); + addxfer(zp); + } else if (SEQ_GT(serial, zp->z_serial) || !zp->z_serial) { + ns_debug(ns_log_default, 1, + "qserial_answer: zone is out of date"); + zp->z_xaddr = from.sin_addr; /* don't use qp->q_from */ + addxfer(zp); + } else if (SEQ_GT(zp->z_serial, serial)) { + if (!haveComplained((u_long)zp, (u_long)"went backward")) { + ns_notice(ns_log_default, + "Zone \"%s\" (class %d) SOA serial# (%u) rcvd from [%s] is < ours (%u)", + zp->z_origin, zp->z_class, serial, + inet_ntoa(from.sin_addr), zp->z_serial); + } + } else { + ns_debug(ns_log_default, 1, + "qserial_answer: zone serial is still OK"); + markUpToDate(zp); + sched_zone_maint(zp); + } +} + +/* + * Start an asynchronous zone transfer for a zone. + * Depends on current time being in tt. + * Caller must do sched_zone_maint(zp) after startxfer returns. + */ +static void +startxfer(struct zoneinfo *zp) { + char *argv[NSMAX + 20], argv_ns[NSMAX][MAXDNAME]; + int argc = 0, argc_ns = 0, pid, i; + u_int cnt; + char debug_str[10]; + char serial_str[10]; + char port_str[10]; + char class_str[10]; + char src_str[20]; + + ns_debug(ns_log_default, 1, "startxfer() %s", zp->z_origin); + + argv[argc++] = server_options->named_xfer; + argv[argc++] = "-z"; + argv[argc++] = zp->z_origin; + argv[argc++] = "-f"; + argv[argc++] = zp->z_source; + argv[argc++] = "-s"; + sprintf(serial_str, "%u", zp->z_serial); + argv[argc++] = serial_str; + if (zp->z_axfr_src.s_addr != 0) { + argv[argc++] = "-x"; + argv[argc++] = strcpy(src_str, inet_ntoa(zp->z_axfr_src)); + } + argv[argc++] = "-C"; + sprintf(class_str, "%d", zp->z_class); + argv[argc++] = class_str; + 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 (ina_hlong(zp->z_xaddr) != INADDR_ANY) { + /* + * Address was specified by the qserial logic, use it + * first. + */ + if (aIsUs(zp->z_xaddr) && + !haveComplained((u_long)zp, (u_long)startxfer)) { + ns_notice(ns_log_default, + "attempted to fetch zone %s from self (%s)", + zp->z_origin, inet_ntoa(zp->z_xaddr)); + } else + argv[argc++] = strcpy(argv_ns[argc_ns++], + inet_ntoa(zp->z_xaddr)); + } + /* + * Copy the server ip addresses into argv, after converting + * to ascii and saving the static inet_ntoa result. Skip zp->z_xaddr + * if seen. + */ + for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { + struct in_addr a; + + a = zp->z_addr[cnt]; + if (ina_equal(a, zp->z_xaddr)) + continue; + if (aIsUs(a) && + !haveComplained((u_long)zp, (u_long)startxfer)) { + ns_notice(ns_log_default, + "attempted to fetch zone %s from self (%s)", + zp->z_origin, inet_ntoa(a)); + continue; + } + argv[argc++] = strcpy(argv_ns[argc_ns++], inet_ntoa(a)); + } + + argv[argc] = NULL; + +#ifdef DEBUG + if (debug >= 1) { + char buffer[1024]; + char *curr, *last; + int len; + + curr = buffer; + last = &buffer[sizeof buffer - 1]; /* leave room for \0 */ + for (i = 0; i < argc; i++) { + len = strlen(argv[i]); + if (curr + len + 1 >= last) { + ns_debug(ns_log_xfer_in, 1, + "xfer args debug printout truncated"); + break; + } + strncpy(curr, argv[i], len); + curr += len; + *curr = ' '; + curr++; + } + *curr = '\0'; + ns_debug(ns_log_xfer_in, 1, buffer); + } +#endif /* DEBUG */ + + gettime(&tt); + for (i = 0; i < MAX_XFERS_RUNNING; i++) { + if (xferstatus[i].xfer_pid == 0) { + xferstatus[i].xfer_state = XFER_RUNNING; + break; + } + } + if ((pid = vfork()) == -1) { + ns_error(ns_log_default, "xfer vfork: %s", strerror(errno)); + zp->z_time = tt.tv_sec + 10; + return; + } + + if (pid == 0) { + /* Child. */ + execv(server_options->named_xfer, argv); + ns_error(ns_log_default, "can't exec %s: %s", + server_options->named_xfer, strerror(errno)); + _exit(XFER_FAIL); /* Avoid duplicate buffer flushes. */ + } + /* Parent. */ + xferstatus[i].xfer_pid = pid; /* XXX - small race condition here if we + * can't hold signals */ + ns_debug(ns_log_default, 1, "started xfer child %d", pid); + zp->z_flags &= ~Z_NEED_XFER; + zp->z_flags |= Z_XFER_RUNNING; + zp->z_xferpid = pid; + xfers_running++; + if (zp->z_max_transfer_time_in) + zp->z_time = tt.tv_sec + zp->z_max_transfer_time_in; + else + zp->z_time = tt.tv_sec + server_options->max_transfer_time_in; +} + +const char * +zoneTypeString(const struct zoneinfo *zp) { + static char ret[sizeof "(4294967296?)"]; /* 2^32 */ + + switch (zp->z_type) { + case Z_MASTER: return ("master"); + case Z_SLAVE: return ("slave"); +#ifdef STUBS + case Z_STUB: return ("stub"); +#endif + case Z_CACHE: return ("cache"); + default: + sprintf(ret, "(%u?)", (u_int32_t)zp->z_type); + return (ret); + } +} + +#ifdef DEBUG +void +printzoneinfo(int zonenum, int category, int level) { + struct timeval tt; + struct zoneinfo *zp = &zones[zonenum]; + + if (debug == 0) + return; + + if (!zp->z_origin) + return; + + gettime(&tt); + + ns_debug(category, level, "zone %d: %s, class %s, type %s", zonenum, + zp->z_origin[0] ? zp->z_origin : ".", + p_class(zp->z_class), zoneTypeString(zp)); + if (zp->z_source) + ns_debug(category, level, "\tsource %s", zp->z_source); + ns_debug(category, level, "\tflags %lx, serial %u, minimum %u", + (u_long)zp->z_flags, zp->z_serial, zp->z_minimum); + ns_debug(category, level, "\trefresh %u, retry %u, expire %u", + zp->z_refresh, zp->z_retry, zp->z_expire); + if (zp->z_time) + ns_debug(category, level, "\tz_time %lu (now %lu, left: %lu)", + zp->z_time, (u_long)tt.tv_sec, + (u_long)(zp->z_time - tt.tv_sec)); + else + ns_debug(category, level, "\tz_time %lu", zp->z_time); +#ifdef BIND_UPDATE + if (zp->z_type == z_master && zp->z_flags & Z_DYNAMIC) { + ns_debug(category, level, + "\tdumpintvl %lu, soaincrintvl %lu deferupdcnt %lu", + zp->z_dumpintvl, zp->z_soaincrintvl, + zp->z_deferupdcnt); + if (zp->z_soaincrtime) + ns_debug(category, level, + "\tz_soaincrtime %lu (now %lu, left: %lu)", + zp->z_soaincrtime, (u_long)tt.tv_sec, + (u_long)(zp->z_soaincrtime - tt.tv_sec)); + else + ns_debug(category, level, "\tz_soaincrtime %lu", + zp->z_soaincrtime); + if (zp->z_dumptime) + ns_debug(category, level, + "\tz_dumptime %lu (now %lu, left: %lu)", + zp->z_dumptime, (u_long)tt.tv_sec, + (u_long)(zp->z_dumptime - tt.tv_sec)); + else + ns_debug(category, level, "\tz_dumptime %lu", + zp->z_dumptime); + } +#endif +} +#endif /* DEBUG */ + +/* clean_cache(htp, all) + * Scan the entire cache looking for expired TTL's on nonauthoritative + * data, and remove it. if `all' is true, ignore TTL and rm everything. + * notes: + * this should be lazy and eventlib driven. + * return: + * number of deleted RRs. + */ +int +clean_cache(struct hashbuf *htp, int all) { + struct databuf *dp, *pdp; + struct namebuf *np, *pnp, *npn; + struct namebuf **npp, **nppend; + int deleted = 0; + + 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; + (void)NULL) { + if (dp->d_zone == DB_Z_CACHE && + (stale(dp) || all)) { + dp = rm_datum(dp, np, pdp, NULL); + deleted++; + } else { + pdp = dp; + dp = dp->d_next; + } + } /*for(pdp)*/ + + if (np->n_hash) { + /* Call recursively to remove subdomains. */ + deleted += clean_cache(np->n_hash, all); + + /* If now empty, free it */ + if (np->n_hash->h_cnt == 0) { + rm_hash(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; + } + } /*for(pnp)*/ + } /*for(npp)*/ + return (deleted); +} + +void +purge_zone(const char *dname, struct hashbuf *htp, int class) { + const char *fname; + struct databuf *dp, *pdp; + struct namebuf *np; + struct hashbuf *phtp = htp; + int root_zone = 0; + + ns_debug(ns_log_default, 1, "purge_zone(%s,%d)", dname, class); + if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname && + !ns_wildcard(NAME(*np))) { + for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { + if (dp->d_class == class) + dp = rm_datum(dp, np, pdp, NULL); + else { + pdp = dp; + dp = dp->d_next; + } + } + + if (*dname == '\0') + root_zone = 1; + + if (np->n_hash != NULL || root_zone) { + struct hashbuf *h; + + if (root_zone) + h = htp; + else + h = np->n_hash; + + purge_z_2(h, class); + if (h->h_cnt == 0 && !root_zone) { + rm_hash(np->n_hash); + np->n_hash = NULL; + } + } + + /* remove entry from cache, if required */ + if (np->n_hash == NULL && np->n_data == NULL) { + struct namebuf **npp, **nppend; + struct namebuf *npn, *pnp, *nnp; + + ns_debug(ns_log_default, 3, + "purge_zone: cleaning cache"); + + /* Walk parent hashtable looking for ourself. */ + if (np->n_parent) + phtp = np->n_parent->n_hash; + else + phtp = htp; /* top / root zone */ + + if (phtp) { + nppend = phtp->h_tab + phtp->h_size; + for (npp = phtp->h_tab; npp < nppend; npp++) { + for (pnp = NULL, nnp = *npp; + nnp != NULL; + nnp = npn) { + if (nnp == np) { + ns_debug(ns_log_default, 3, + "purge_zone: found our selves"); + npn = rm_name(nnp,npp,pnp); + phtp->h_cnt--; + } else { + npn = nnp->n_next; + pnp = nnp; + } + } + } + } + } + } +} + +static void +purge_z_2(htp, class) + struct hashbuf *htp; + int class; +{ + struct databuf *dp, *pdp; + 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) { + if (!bottom_of_zone(np->n_data, class)) { + for (pdp = NULL, dp = np->n_data; + dp != NULL; + (void)NULL) { + if (dp->d_class == class) + dp = rm_datum(dp, np, pdp, + NULL); + else { + pdp = dp; + dp = dp->d_next; + } + } + if (np->n_hash) { + /* call recursively to rm subdomains */ + purge_z_2(np->n_hash, class); + + /* if now empty, free it */ + if (np->n_hash->h_cnt == 0) { + rm_hash(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; + } + } + } +} + +static int +bottom_of_zone(struct databuf *dp, int class) { + int ret = 0; + + for ((void)NULL; dp; dp = dp->d_next) { + if (dp->d_class != class) + continue; + if (dp->d_zone == DB_Z_CACHE) + continue; + if (dp->d_rcode) /* This should not occur. */ + continue; + if (dp->d_type != T_SOA) + continue; + ret = 1; + break; + } + ns_debug(ns_log_default, 3, "bottom_of_zone() == %d", ret); + return (ret); +} + +/* + * Handle XFER limit for a nameserver. + */ +static int +nxfers(struct zoneinfo *zp, int delta) { + struct in_addr nsa; + struct nameser *nsp; + int ret; + + if (ina_hlong(zp->z_xaddr) != INADDR_ANY) + nsa = zp->z_xaddr; /* qserial overrode address */ + else if (!zp->z_addrcnt) + return (-1); + else + 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(struct zoneinfo *zp) { + if (zp->z_flags & (Z_XFER_GONE|Z_XFER_ABORTED)) { + int i; + + for (i = 0; i < MAX_XFERS_RUNNING; i++) { + if (xferstatus[i].xfer_pid == zp->z_xferpid) { + xferstatus[i].xfer_pid = 0; + xferstatus[i].xfer_state = XFER_IDLE; + break; + } + } + + if (zp->z_flags & Z_XFER_GONE) + ns_warning(ns_log_default, + "zone transfer timeout for \"%s\"; pid %lu missing", + zp->z_origin, (u_long)zp->z_xferpid); + else if (kill(zp->z_xferpid, SIGKILL) == -1) + ns_warning(ns_log_default, + "zone transfer timeout for \"%s\"; kill pid %lu: %s", + zp->z_origin, (u_long)zp->z_xferpid, + strerror(errno)); + else + ns_warning(ns_log_default, +"zone transfer timeout for \"%s\"; second kill \ +pid %lu - forgetting, processes may accumulate", + zp->z_origin, (u_long)zp->z_xferpid); + + zp->z_xferpid = 0; + xfers_running--; + (void)nxfers(zp, -1); + zp->z_flags &= ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE); + } else if (kill(zp->z_xferpid, SIGTERM) == -1) { + if (errno == ESRCH) + /* No warning on first time, it may have just exited */ + zp->z_flags |= Z_XFER_GONE; + else { + ns_warning(ns_log_default, + "zone transfer timeout for \"%s\"; pid %lu kill failed %s", + zp->z_origin, (u_long)zp->z_xferpid, + strerror(errno)); + zp->z_flags |= Z_XFER_ABORTED; + } + } else { + ns_notice(ns_log_default, + "zone transfer timeout for \"%s\"; pid %lu killed", + zp->z_origin, (u_long)zp->z_xferpid); + zp->z_flags |= Z_XFER_ABORTED; + } +} + +/* + * SIGCHLD signal handler: process exit of xfer's. + */ +void +reapchild(evContext ctx, void *uap, int sig) { + int pid, i; + WAIT_T status; + int saved_errno = errno; + + gettime(&tt); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + for (i = 0; i < MAX_XFERS_RUNNING; i++) { + if (xferstatus[i].xfer_pid == pid) { + xferstatus[i].xfer_status = status; + xferstatus[i].xfer_state = XFER_DONE; + ns_need(MAIN_NEED_ENDXFER); + break; + } + } + } + + errno = saved_errno; +} + +/* + * Finish processing of of finished xfers + */ +void +endxfer() { + struct zoneinfo *zp; + int exitstatus, pid, i; + WAIT_T status; + + gettime(&tt); + + for (i = 0; i < MAX_XFERS_RUNNING; i++) { + if (xferstatus[i].xfer_state != XFER_DONE) + continue; + pid = xferstatus[i].xfer_pid; + status = xferstatus[i].xfer_status; + exitstatus = WIFEXITED(status) ? WEXITSTATUS(status) : 0; + + for (zp = zones; zp < &zones[nzones]; zp++) { + if (zp->z_xferpid != pid) + continue; + xfers_running--; + (void) nxfers(zp, -1); + zp->z_xferpid = 0; + zp->z_flags &= + ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE); + ns_debug(ns_log_default, 1, + "\nendxfer: child %d zone %s returned status=%d termsig=%d", + pid, zp->z_origin, exitstatus, + WIFSIGNALED(status) ? WTERMSIG(status) : -1); + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) != SIGKILL) { + ns_notice(ns_log_default, + "named-xfer \"%s\" exited with signal %d", + zp->z_origin[0]?zp->z_origin:".", + WTERMSIG(status)); + } + ns_retrytime(zp, tt.tv_sec); + sched_zone_maint(zp); + } else { + switch (exitstatus) { + case XFER_UPTODATE: + markUpToDate(zp); + sched_zone_maint(zp); + break; + + case XFER_SUCCESS: + /* XXX should incorporate loadxfer() */ + zp->z_flags |= Z_NEED_RELOAD; + zp->z_flags &= ~Z_SYSLOGGED; + ns_need(MAIN_NEED_ZONELOAD); + break; + + case XFER_TIMEOUT: + if (!(zp->z_flags & Z_SYSLOGGED)) { + zp->z_flags |= Z_SYSLOGGED; + ns_notice(ns_log_default, + "zoneref: Masters for secondary zone \"%s\" unreachable", + zp->z_origin); + } + ns_retrytime(zp, tt.tv_sec); + sched_zone_maint(zp); + break; + + default: + if (!(zp->z_flags & Z_SYSLOGGED)) { + zp->z_flags |= Z_SYSLOGGED; + ns_notice(ns_log_default, + "named-xfer for \"%s\" exited %d", + zp->z_origin, + exitstatus); + } + /* FALLTHROUGH */ + case XFER_FAIL: + zp->z_flags |= Z_SYSLOGGED; + ns_retrytime(zp, tt.tv_sec); + sched_zone_maint(zp); + break; + } + break; + } + } + xferstatus[i].xfer_state = XFER_IDLE; + xferstatus[i].xfer_pid = 0; + } + tryxfer(); +} + +/* + * Try to start some xfers - new "fair scheduler" by Bob Halley @DEC (1995) + */ +static void +tryxfer() { + static struct zoneinfo *zp = NULL; + static struct zoneinfo *lastzones = NULL; + static int lastnzones = 0; + struct zoneinfo *startzp, *stopzp; + + /* initialize, and watch out for changes in zones! */ + if (lastzones != zones) { + if (lastzones != NULL) + ns_debug(ns_log_default, 3, "zones changed: %p != %p", + lastzones, zones); + lastzones = zones; + zp = zones; + } + + /* did zones shrink? */ + if (lastnzones > nzones) { + ns_debug(ns_log_default, 3, "zones shrunk"); + zp = zones; + } + lastnzones = nzones; + + if (zp == zones) + stopzp = &zones[nzones-1]; + else + stopzp = zp - 1; + + ns_debug(ns_log_default, 3, + "tryxfer start zp=%p stopzp=%p def=%d running=%d", + zp, stopzp, xfers_deferred, xfers_running); + + startzp = zp; + for (;;) { + int xfers; + + if (!xfers_deferred || + xfers_running >= server_options->transfers_in) + break; + + if ((xfers = nxfers(zp, 0)) != -1 && + xfers < server_options->transfers_per_ns && + (zp->z_flags & Z_NEED_XFER)) { + nxfers(zp, 1); + xfers_deferred--; + startxfer(zp); + sched_zone_maint(zp); + } + + if (zp == stopzp) { + ns_debug(ns_log_default, 3, "tryxfer stop mark"); + zp = startzp; + break; + } + + zp++; + /* wrap around? */ + if (zp == &zones[nzones]) + zp = zones; + } + ns_debug(ns_log_default, 3, "tryxfer stop zp=%p", zp); +} + +/* + * Reload zones whose transfers have completed. + */ +void +loadxfer() { + struct zoneinfo *zp; + + gettime(&tt); + for (zp = zones; zp < &zones[nzones]; zp++) { + if (zp->z_flags & Z_NEED_RELOAD) { + ns_debug(ns_log_default, 1, "loadxfer() \"%s\"", + zp->z_origin[0] ? zp->z_origin : "."); + zp->z_flags &= ~(Z_NEED_RELOAD|Z_AUTH); +/* XXX this is bad, should be done in ns_reload() for primary changes. */ + ns_stopxfrs(zp); + purge_zone(zp->z_origin, hashtab, zp->z_class); + if (!db_load(zp->z_source, zp->z_origin, zp, NULL)) + zp->z_flags |= Z_AUTH; + if (zp->z_flags & Z_TMP_FILE) + (void) unlink(zp->z_source); + sched_zone_maint(zp); + } + } +} + +/* + * Add this zone to the set of those needing transfers. + */ +static void +addxfer(struct zoneinfo *zp) { + if (!(zp->z_flags & Z_NEED_XFER)) { + zp->z_flags |= Z_NEED_XFER; + xfers_deferred++; + tryxfer(); + } +} + +/* + * Flush and reload data base. + */ +void +ns_reload() { + ns_notice(ns_log_default, "reloading nameserver"); + + qflush(); + sq_flush(NULL); +#ifdef FORCED_RELOAD + reloading = 1; /* to force transfer if secondary and backing up */ +#endif + ns_init(conffile); + time(&resettime); +#ifdef FORCED_RELOAD + reloading = 0; +#endif /* FORCED_RELOAD */ + ns_notice(ns_log_default, "Ready to answer queries."); +} diff --git a/contrib/bind/bin/named/ns_ncache.c b/contrib/bind/bin/named/ns_ncache.c new file mode 100644 index 0000000..413ccc6 --- /dev/null +++ b/contrib/bind/bin/named/ns_ncache.c @@ -0,0 +1,226 @@ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_ncache.c,v 8.17 1998/03/20 01:12:01 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/file.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.h" + +#include "named.h" + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eom) { \ + return; \ + } \ + } while (0) + +void +cache_n_resp(u_char *msg, int msglen, struct sockaddr_in from) { + struct databuf *dp; + HEADER *hp; + u_char *cp, *eom, *rdatap; + char dname[MAXDNAME]; + int n; + int type, class; + int Vcode; + int flags; + u_int16_t ancount; + u_int dlen; + + nameserIncr(from.sin_addr, nssRcvdNXD); + + hp = (HEADER *)msg; + cp = msg+HFIXEDSZ; + eom = msg + msglen; + + n = dn_expand(msg, eom, cp, dname, sizeof dname); + if (n < 0) { + ns_debug(ns_log_ncache, 1, + "Query expand name failed: cache_n_resp"); + hp->rcode = FORMERR; + return; + } + cp += n; + BOUNDS_CHECK(cp, 2 * INT16SZ); + GETSHORT(type, cp); + GETSHORT(class, cp); + ns_debug(ns_log_ncache, 1, "ncache: dname %s, type %d, class %d", + dname, type, class); + + ancount = ntohs(hp->ancount); + + while (ancount--) { + u_int32_t ttl; + u_int16_t atype; + u_int16_t aclass; + n = dn_skipname(cp, eom); + if (n < 0) { + ns_debug(ns_log_ncache, 3, "ncache: form error"); + return; + } + cp += n; + BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); + GETSHORT(atype, cp); + GETSHORT(aclass, cp); + if (atype != T_CNAME || aclass != class) { + ns_debug(ns_log_ncache, 3, "ncache: form error"); + return; + } + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + BOUNDS_CHECK(cp, dlen); + rdatap = cp; + n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); + if (n < 0) { + ns_debug(ns_log_ncache, 3, "ncache: form error"); + return; + } + cp += n; + if (cp != rdatap + dlen) { + ns_debug(ns_log_ncache, 3, "ncache: form error"); + return; + } + } + +#ifdef RETURNSOA + if (hp->nscount) { + u_int32_t ttl; + u_int16_t atype; + u_char *tp = cp; + u_char *cp1; + u_char data[MAXDATA]; + size_t len = sizeof data; + + /* we store NXDOMAIN as T_SOA regardless of the query type */ + if (hp->rcode == NXDOMAIN) + type = T_SOA; + + /* store ther SOA record */ + n = dn_skipname(tp, msg + msglen); + if (n < 0) { + ns_debug(ns_log_ncache, 3, "ncache: form error"); + return; + } + tp += n; + + BOUNDS_CHECK(tp, 3 * INT16SZ + INT32SZ); + GETSHORT(atype, tp); /* type */ + if (atype != T_SOA) { + ns_debug(ns_log_ncache, 3, + "ncache: type (%d) != T_SOA", atype); + goto no_soa; + } + tp += INT16SZ; /* class */ + GETLONG(ttl, tp); /* ttl */ + GETSHORT(dlen, tp); /* dlen */ + BOUNDS_CHECK(tp, dlen); + rdatap = tp; + + /* origin */ + n = dn_expand(msg, msg + msglen, tp, (char*)data, len); + if (n < 0) { + ns_debug(ns_log_ncache, 3, + "ncache: origin form error"); + return; + } + tp += n; + n = strlen((char*)data) + 1; + cp1 = data + n; + len -= n; + /* mail */ + n = dn_expand(msg, msg + msglen, tp, (char*)cp1, len); + if (n < 0) { + ns_debug(ns_log_ncache, 3, "ncache: mail form error"); + return; + } + tp += n; + n = strlen((char*)cp1) + 1; + cp1 += n; + len -= n; + n = 5 * INT32SZ; + BOUNDS_CHECK(tp, n); + memcpy(cp1, tp, n); + /* serial, refresh, retry, expire, min */ + cp1 += n; + len -= n; + tp += n; + if (tp != rdatap + dlen) { + ns_debug(ns_log_ncache, 3, "ncache: form error"); + return; + } + /* store the zone of the soa record */ + n = dn_expand(msg, msg + msglen, cp, (char*)cp1, len); + if (n < 0) { + ns_debug(ns_log_ncache, 3, "ncache: form error 2"); + return; + } + n = strlen((char*)cp1) + 1; + cp1 += n; + + dp = savedata(class, type, MIN(ttl, NTTL) + tt.tv_sec, data, + cp1 - data); + } else { + no_soa: +#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, NULL, flags, hashtab, from)) != OK) { + ns_debug(ns_log_ncache, 1, + "db_update failed (%d), cache_n_resp()", n); + db_freedata(dp); + return; + } + ns_debug(ns_log_ncache, 4, + "ncache succeeded: [%s %s %s] rcode:%d ttl:%ld", + dname, p_type(type), p_class(class), + dp->d_rcode, (long)(dp->d_ttl - tt.tv_sec)); +} diff --git a/contrib/bind/bin/named/ns_parser.c b/contrib/bind/bin/named/ns_parser.c new file mode 100644 index 0000000..47944ea --- /dev/null +++ b/contrib/bind/bin/named/ns_parser.c @@ -0,0 +1,2376 @@ +#ifndef lint +static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93 (BSDI)"; +#endif +#include <stdlib.h> +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define YYEMPTY (-1) +#define YYLEX yylex() +#define yyclearin (yychar=YYEMPTY) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +#line 2 "ns_parser.y" +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_parser.y,v 8.11 1997/12/04 07:03:05 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* Global C stuff goes here. */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.h" + +#include "named.h" +#include "ns_parseutil.h" +#include "ns_lexer.h" + +#define SYM_ZONE 0x010000 +#define SYM_SERVER 0x020000 +#define SYM_KEY 0x030000 +#define SYM_ACL 0x040000 +#define SYM_CHANNEL 0x050000 +#define SYM_PORT 0x060000 + +#define SYMBOL_TABLE_SIZE 29989 /* should always be prime */ +static symbol_table symtab; + +#define AUTH_TABLE_SIZE 397 /* should always be prime */ +static symbol_table authtab = NULL; + +static zone_config current_zone; +static int seen_zone; + +static options current_options; +static int seen_options; + +static topology_config current_topology; +static int seen_topology; + +static server_config current_server; +static int seen_server; + +static char *current_algorithm; +static char *current_secret; + +static log_config current_logging; +static int current_category; +static int chan_type; +static int chan_level; +static u_int chan_flags; +static int chan_facility; +static char *chan_name; +static int chan_versions; +static u_long chan_max_size; + +static log_channel lookup_channel(char *); +static void define_channel(char *, log_channel); +static char *canonical_name(char *); + +int yyparse(); + +#line 96 "ns_parser.y" +typedef union { + char * cp; + int s_int; + long num; + u_long ul_int; + u_int16_t us_int; + struct in_addr ip_addr; + ip_match_element ime; + ip_match_list iml; + key_info keyi; + enum axfr_format axfr_fmt; +} YYSTYPE; +#line 121 "y.tab.c" +#define L_EOS 257 +#define L_IPADDR 258 +#define L_NUMBER 259 +#define L_STRING 260 +#define L_QSTRING 261 +#define L_END_INCLUDE 262 +#define T_INCLUDE 263 +#define T_OPTIONS 264 +#define T_DIRECTORY 265 +#define T_PIDFILE 266 +#define T_NAMED_XFER 267 +#define T_DUMP_FILE 268 +#define T_STATS_FILE 269 +#define T_MEMSTATS_FILE 270 +#define T_FAKE_IQUERY 271 +#define T_RECURSION 272 +#define T_FETCH_GLUE 273 +#define T_QUERY_SOURCE 274 +#define T_LISTEN_ON 275 +#define T_PORT 276 +#define T_ADDRESS 277 +#define T_DATASIZE 278 +#define T_STACKSIZE 279 +#define T_CORESIZE 280 +#define T_DEFAULT 281 +#define T_UNLIMITED 282 +#define T_FILES 283 +#define T_HOSTSTATS 284 +#define T_DEALLOC_ON_EXIT 285 +#define T_TRANSFERS_IN 286 +#define T_TRANSFERS_OUT 287 +#define T_TRANSFERS_PER_NS 288 +#define T_TRANSFER_FORMAT 289 +#define T_MAX_TRANSFER_TIME_IN 290 +#define T_ONE_ANSWER 291 +#define T_MANY_ANSWERS 292 +#define T_NOTIFY 293 +#define T_AUTH_NXDOMAIN 294 +#define T_MULTIPLE_CNAMES 295 +#define T_CLEAN_INTERVAL 296 +#define T_INTERFACE_INTERVAL 297 +#define T_STATS_INTERVAL 298 +#define T_LOGGING 299 +#define T_CATEGORY 300 +#define T_CHANNEL 301 +#define T_SEVERITY 302 +#define T_DYNAMIC 303 +#define T_FILE 304 +#define T_VERSIONS 305 +#define T_SIZE 306 +#define T_SYSLOG 307 +#define T_DEBUG 308 +#define T_NULL_OUTPUT 309 +#define T_PRINT_TIME 310 +#define T_PRINT_CATEGORY 311 +#define T_PRINT_SEVERITY 312 +#define T_TOPOLOGY 313 +#define T_SERVER 314 +#define T_LONG_AXFR 315 +#define T_BOGUS 316 +#define T_TRANSFERS 317 +#define T_KEYS 318 +#define T_ZONE 319 +#define T_IN 320 +#define T_CHAOS 321 +#define T_HESIOD 322 +#define T_TYPE 323 +#define T_MASTER 324 +#define T_SLAVE 325 +#define T_STUB 326 +#define T_RESPONSE 327 +#define T_HINT 328 +#define T_MASTERS 329 +#define T_TRANSFER_SOURCE 330 +#define T_ALSO_NOTIFY 331 +#define T_ACL 332 +#define T_ALLOW_UPDATE 333 +#define T_ALLOW_QUERY 334 +#define T_ALLOW_TRANSFER 335 +#define T_SEC_KEY 336 +#define T_ALGID 337 +#define T_SECRET 338 +#define T_CHECK_NAMES 339 +#define T_WARN 340 +#define T_FAIL 341 +#define T_IGNORE 342 +#define T_FORWARD 343 +#define T_FORWARDERS 344 +#define T_ONLY 345 +#define T_FIRST 346 +#define T_IF_NO_ANSWER 347 +#define T_IF_NO_DOMAIN 348 +#define T_YES 349 +#define T_TRUE 350 +#define T_NO 351 +#define T_FALSE 352 +#define YYERRCODE 256 +short yylhs[] = { -1, + 0, 25, 25, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 27, 34, 28, 35, 35, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 38, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 5, 5, 4, 4, 3, 3, 43, 44, 40, 40, + 40, 40, 2, 2, 23, 23, 23, 23, 23, 21, + 21, 21, 22, 22, 22, 37, 37, 37, 37, 41, + 41, 41, 41, 20, 20, 20, 20, 42, 42, 42, + 39, 39, 45, 45, 46, 47, 29, 48, 48, 48, + 50, 49, 52, 49, 54, 54, 54, 54, 55, 55, + 56, 57, 57, 57, 57, 57, 58, 9, 9, 10, + 10, 59, 60, 60, 60, 60, 60, 60, 60, 53, + 53, 53, 8, 8, 61, 51, 51, 51, 7, 7, + 7, 6, 62, 30, 63, 63, 64, 64, 64, 64, + 64, 14, 14, 12, 12, 11, 11, 11, 11, 11, + 13, 17, 66, 65, 65, 65, 67, 33, 68, 68, + 68, 18, 19, 32, 70, 31, 69, 69, 15, 15, + 16, 16, 16, 16, 71, 71, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, + 75, 74, 74, 76, 76, 77, 1, 24, 24, +}; +short yylen[] = { 2, + 1, 1, 2, 1, 2, 2, 2, 2, 2, 2, + 1, 2, 2, 3, 0, 5, 2, 3, 0, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 5, 2, 0, 5, 2, 4, + 4, 4, 1, 1, 2, 2, 2, 2, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, + 2, 2, 0, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, + 0, 1, 2, 3, 1, 0, 5, 2, 3, 1, + 0, 6, 0, 6, 1, 1, 2, 1, 2, 2, + 2, 0, 1, 1, 2, 2, 3, 1, 1, 0, + 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, + 3, 1, 1, 1, 1, 2, 3, 1, 1, 1, + 1, 1, 0, 6, 2, 3, 2, 2, 2, 4, + 1, 2, 3, 1, 2, 1, 3, 3, 1, 3, + 1, 1, 1, 2, 3, 1, 0, 6, 2, 2, + 1, 3, 3, 5, 0, 5, 0, 3, 0, 1, + 1, 1, 1, 1, 2, 3, 2, 2, 4, 2, + 2, 4, 4, 4, 2, 2, 4, 1, 2, 3, + 1, 0, 1, 2, 3, 1, 1, 1, 1, +}; +short yydefred[] = { 0, + 0, 11, 0, 15, 96, 0, 0, 0, 167, 0, + 0, 2, 4, 0, 0, 0, 0, 0, 0, 12, + 13, 0, 0, 0, 143, 0, 208, 209, 0, 0, + 3, 5, 6, 7, 8, 9, 10, 14, 0, 0, + 0, 175, 180, 0, 0, 50, 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, 37, + 0, 0, 43, 44, 100, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 154, 0, 159, 0, 161, + 0, 20, 22, 21, 25, 23, 24, 69, 65, 66, + 67, 68, 26, 27, 28, 0, 0, 39, 0, 0, + 0, 0, 85, 86, 87, 80, 84, 81, 82, 83, + 30, 31, 88, 89, 90, 51, 52, 45, 46, 29, + 32, 33, 47, 48, 49, 0, 0, 0, 70, 71, + 72, 0, 76, 77, 78, 79, 36, 0, 16, 0, + 17, 140, 141, 101, 142, 139, 134, 103, 133, 97, + 0, 98, 151, 0, 0, 0, 0, 0, 0, 0, + 176, 0, 0, 0, 155, 152, 174, 0, 171, 0, + 0, 0, 0, 0, 207, 56, 55, 58, 53, 54, + 57, 61, 62, 64, 0, 0, 0, 0, 73, 74, + 75, 34, 0, 18, 0, 0, 99, 149, 147, 148, + 0, 144, 0, 145, 198, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 157, 158, + 160, 153, 0, 0, 169, 170, 168, 0, 42, 40, + 41, 95, 0, 0, 0, 0, 0, 166, 163, 162, + 0, 0, 146, 195, 196, 188, 181, 182, 184, 183, + 187, 0, 190, 0, 0, 0, 0, 191, 178, 0, + 185, 172, 173, 35, 38, 0, 93, 138, 135, 0, + 0, 132, 0, 0, 0, 125, 0, 0, 0, 0, + 123, 124, 0, 150, 0, 164, 201, 0, 0, 206, + 0, 0, 0, 0, 0, 0, 186, 94, 102, 0, + 136, 108, 0, 105, 126, 0, 119, 121, 122, 118, + 127, 128, 129, 104, 0, 130, 165, 189, 0, 199, + 197, 0, 204, 192, 193, 194, 137, 107, 0, 0, + 0, 0, 117, 131, 200, 205, 109, 110, 111, 115, + 116, +}; +short yydgoto[] = { 10, + 197, 122, 198, 201, 138, 164, 165, 289, 328, 329, + 96, 97, 98, 99, 42, 271, 259, 192, 193, 126, + 152, 212, 113, 100, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 23, 81, 82, 157, 158, 253, 118, + 83, 84, 119, 120, 254, 255, 24, 88, 89, 215, + 290, 216, 300, 325, 351, 352, 353, 301, 302, 303, + 291, 41, 178, 179, 261, 262, 30, 194, 181, 91, + 237, 238, 308, 311, 309, 312, 313, +}; +short yysindex[] = { 122, + -200, 0, -238, 0, 0, -227, -228, -188, 0, 0, + 122, 0, 0, -220, -176, -164, -158, -155, -149, 0, + 0, -147, -88, -4, 0, -188, 0, 0, 4, -188, + 0, 0, 0, 0, 0, 0, 0, 0, 79, -240, + 11, 0, 0, 16, 20, 0, -218, -60, -40, -34, + -24, -14, -53, -53, -53, -193, -95, -190, -190, -190, + -190, -53, -53, -98, -56, -41, -162, -31, -53, -53, + -53, 6, 19, 22, 102, 161, 163, -204, -57, 0, + -120, 36, 0, 0, 0, -73, -205, -115, 38, -241, + 177, 257, 258, 16, -69, 0, 49, 0, -29, 0, + -226, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -33, -36, 0, 31, 32, + 51, 185, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 16, 16, 0, 0, + 0, -277, 0, 0, 0, 0, 0, 188, 0, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 57, 0, 0, -162, -53, 56, 193, -118, 60, 104, + 0, 59, 63, -25, 0, 0, 0, 69, 0, -188, + -188, -11, -7, 203, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 16, -20, -16, -9, 0, 0, + 0, 0, 73, 0, 209, 210, 0, 0, 0, 0, + -143, 0, 77, 0, 0, 78, -53, 75, -184, 215, + -36, 216, 217, 218, 232, -277, -10, 99, 0, 0, + 0, 0, 113, 114, 0, 0, 0, -1, 0, 0, + 0, 0, 236, 73, 123, -214, -222, 0, 0, 0, + -81, 124, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 121, 0, 125, 16, 16, 16, 0, 0, 130, + 0, 0, 0, 0, 0, 131, 0, 0, 0, -104, + 132, 0, -202, 129, -221, 0, -53, -53, -53, -100, + 0, 0, 134, 0, 136, 0, 0, -96, 138, 0, + 271, 125, 141, 3, 8, 12, 0, 0, 0, 142, + 0, 0, 143, 0, 0, -89, 0, 0, 0, 0, + 0, 0, 0, 0, 144, 0, 0, 0, 146, 0, + 0, 147, 0, 0, 0, 0, 0, 0, -185, -190, + 76, 100, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; +short yyrindex[] = { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -85, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 149, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 284, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 155, 158, 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, 159, 160, + 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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 294, 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, 295, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 299, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 301, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 171, 0, 0, 172, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 173, 174, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; +short yygindex[] = { 0, + 311, 0, 0, 211, 266, 0, 0, 357, 0, 0, + 350, 95, 0, -80, 0, 0, 0, 253, 255, -58, + 0, 212, -43, -8, 0, 438, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 369, 0, 0, 0, 0, + 0, 0, 331, 333, 0, 199, 0, 0, 367, 0, + 0, 0, 0, 0, 105, 108, 0, 0, 0, 156, + 170, 0, 0, 283, 0, 201, 0, 0, 0, 0, + 0, 226, 0, 0, 157, 0, 152, +}; +#define YYTABLESIZE 465 +short yytable[] = { 29, + 128, 129, 130, 95, 159, 200, 222, 95, 196, 170, + 114, 115, 95, 184, 173, 85, 95, 43, 131, 132, + 319, 45, 22, 95, 334, 140, 141, 142, 338, 189, + 25, 95, 26, 292, 39, 95, 32, 179, 27, 28, + 95, 288, 102, 304, 95, 27, 28, 174, 95, 127, + 127, 127, 127, 94, 27, 28, 20, 27, 28, 86, + 87, 21, 209, 210, 211, 206, 207, 208, 123, 27, + 28, 27, 28, 357, 175, 176, 177, 166, 169, 293, + 33, 294, 116, 117, 295, 327, 296, 297, 298, 299, + 124, 125, 34, 94, 167, 187, 358, 94, 35, 241, + 322, 36, 94, 167, 249, 323, 94, 37, 250, 38, + 190, 191, 258, 94, 279, 251, 27, 28, 40, 149, + 150, 94, 151, 284, 248, 94, 44, 344, 136, 137, + 94, 219, 345, 90, 94, 46, 346, 173, 94, 267, + 268, 269, 101, 270, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 27, 28, 58, 59, 60, + 133, 307, 61, 62, 63, 64, 65, 66, 67, 68, + 174, 179, 69, 70, 71, 72, 73, 74, 27, 28, + 121, 243, 244, 265, 86, 87, 27, 28, 92, 93, + 27, 28, 75, 188, 314, 315, 316, 175, 176, 177, + 103, 293, 134, 294, 167, 108, 295, 162, 296, 297, + 298, 299, 260, 76, 77, 349, 350, 135, 78, 163, + 104, 199, 79, 80, 146, 195, 105, 139, 92, 93, + 27, 28, 92, 93, 27, 28, 106, 92, 93, 27, + 28, 92, 93, 27, 28, 225, 107, 169, 92, 93, + 27, 28, 260, 331, 332, 333, 92, 93, 27, 28, + 92, 93, 27, 28, 143, 92, 93, 27, 28, 92, + 93, 27, 28, 92, 93, 27, 28, 144, 188, 226, + 145, 169, 227, 147, 324, 148, 330, 153, 154, 155, + 156, 359, 161, 228, 172, 109, 110, 111, 112, 180, + 188, 188, 188, 182, 183, 186, 116, 205, 117, 195, + 213, 214, 229, 217, 220, 221, 224, 239, 230, 231, + 232, 240, 233, 234, 235, 242, 191, 247, 236, 190, + 252, 256, 257, 263, 46, 266, 264, 272, 274, 275, + 276, 127, 188, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 277, 281, 58, 59, 60, 225, + 285, 61, 62, 63, 64, 65, 66, 67, 68, 282, + 283, 69, 70, 71, 72, 73, 74, 1, 307, 287, + 306, 350, 310, 2, 3, 4, 317, 318, 321, 326, + 336, 75, 337, 226, 340, 341, 227, 343, 347, 1, + 354, 348, 355, 356, 349, 19, 63, 228, 188, 188, + 188, 177, 76, 77, 156, 59, 60, 78, 91, 92, + 5, 79, 80, 202, 120, 203, 229, 106, 112, 113, + 114, 204, 230, 231, 232, 6, 233, 234, 235, 218, + 7, 273, 236, 168, 185, 246, 245, 278, 31, 160, + 203, 202, 286, 8, 171, 335, 361, 9, 360, 320, + 223, 305, 280, 342, 339, +}; +short yycheck[] = { 8, + 59, 60, 61, 33, 125, 42, 125, 33, 42, 125, + 54, 55, 33, 94, 256, 256, 33, 26, 62, 63, + 125, 30, 261, 33, 125, 69, 70, 71, 125, 256, + 258, 33, 261, 256, 123, 33, 257, 123, 260, 261, + 33, 256, 261, 125, 33, 260, 261, 289, 33, 58, + 59, 60, 61, 123, 260, 261, 257, 260, 261, 300, + 301, 262, 340, 341, 342, 146, 147, 148, 259, 260, + 261, 260, 261, 259, 316, 317, 318, 86, 87, 302, + 257, 304, 276, 277, 307, 307, 309, 310, 311, 312, + 281, 282, 257, 123, 309, 125, 282, 123, 257, 125, + 303, 257, 123, 309, 125, 308, 123, 257, 125, 257, + 337, 338, 256, 123, 125, 125, 260, 261, 123, 324, + 325, 123, 327, 125, 205, 123, 123, 125, 291, 292, + 123, 175, 125, 123, 123, 256, 125, 256, 123, 324, + 325, 326, 123, 328, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 260, 261, 278, 279, 280, + 259, 258, 283, 284, 285, 286, 287, 288, 289, 290, + 289, 257, 293, 294, 295, 296, 297, 298, 260, 261, + 276, 190, 191, 227, 300, 301, 260, 261, 258, 259, + 260, 261, 313, 99, 275, 276, 277, 316, 317, 318, + 261, 302, 259, 304, 309, 259, 307, 281, 309, 310, + 311, 312, 221, 334, 335, 305, 306, 259, 339, 293, + 261, 258, 343, 344, 123, 259, 261, 259, 258, 259, + 260, 261, 258, 259, 260, 261, 261, 258, 259, 260, + 261, 258, 259, 260, 261, 256, 261, 256, 258, 259, + 260, 261, 261, 297, 298, 299, 258, 259, 260, 261, + 258, 259, 260, 261, 259, 258, 259, 260, 261, 258, + 259, 260, 261, 258, 259, 260, 261, 259, 184, 290, + 259, 290, 293, 123, 293, 123, 295, 345, 346, 347, + 348, 350, 257, 304, 257, 349, 350, 351, 352, 123, + 206, 207, 208, 47, 47, 257, 276, 123, 277, 259, + 123, 257, 323, 257, 259, 123, 257, 259, 329, 330, + 331, 259, 333, 334, 335, 257, 338, 125, 339, 337, + 258, 123, 123, 257, 256, 261, 259, 123, 123, 123, + 123, 350, 248, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 123, 257, 278, 279, 280, 256, + 125, 283, 284, 285, 286, 287, 288, 289, 290, 257, + 257, 293, 294, 295, 296, 297, 298, 256, 258, 257, + 257, 306, 258, 262, 263, 264, 257, 257, 257, 261, + 257, 313, 257, 290, 257, 125, 293, 257, 257, 0, + 257, 259, 257, 257, 305, 257, 123, 304, 314, 315, + 316, 257, 334, 335, 257, 257, 257, 339, 125, 125, + 299, 343, 344, 125, 257, 125, 323, 257, 257, 257, + 257, 121, 329, 330, 331, 314, 333, 334, 335, 174, + 319, 231, 339, 87, 95, 193, 192, 236, 11, 81, + 120, 119, 254, 332, 88, 300, 352, 336, 351, 290, + 178, 261, 237, 312, 308, +}; +#define YYFINAL 10 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 352 +#if YYDEBUG +char *yyname[] = { +"end-of-file",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,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,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,0,0,0,0,0,0,0,0,0,0,0,"L_EOS", +"L_IPADDR","L_NUMBER","L_STRING","L_QSTRING","L_END_INCLUDE","T_INCLUDE", +"T_OPTIONS","T_DIRECTORY","T_PIDFILE","T_NAMED_XFER","T_DUMP_FILE", +"T_STATS_FILE","T_MEMSTATS_FILE","T_FAKE_IQUERY","T_RECURSION","T_FETCH_GLUE", +"T_QUERY_SOURCE","T_LISTEN_ON","T_PORT","T_ADDRESS","T_DATASIZE","T_STACKSIZE", +"T_CORESIZE","T_DEFAULT","T_UNLIMITED","T_FILES","T_HOSTSTATS", +"T_DEALLOC_ON_EXIT","T_TRANSFERS_IN","T_TRANSFERS_OUT","T_TRANSFERS_PER_NS", +"T_TRANSFER_FORMAT","T_MAX_TRANSFER_TIME_IN","T_ONE_ANSWER","T_MANY_ANSWERS", +"T_NOTIFY","T_AUTH_NXDOMAIN","T_MULTIPLE_CNAMES","T_CLEAN_INTERVAL", +"T_INTERFACE_INTERVAL","T_STATS_INTERVAL","T_LOGGING","T_CATEGORY","T_CHANNEL", +"T_SEVERITY","T_DYNAMIC","T_FILE","T_VERSIONS","T_SIZE","T_SYSLOG","T_DEBUG", +"T_NULL_OUTPUT","T_PRINT_TIME","T_PRINT_CATEGORY","T_PRINT_SEVERITY", +"T_TOPOLOGY","T_SERVER","T_LONG_AXFR","T_BOGUS","T_TRANSFERS","T_KEYS","T_ZONE", +"T_IN","T_CHAOS","T_HESIOD","T_TYPE","T_MASTER","T_SLAVE","T_STUB","T_RESPONSE", +"T_HINT","T_MASTERS","T_TRANSFER_SOURCE","T_ALSO_NOTIFY","T_ACL", +"T_ALLOW_UPDATE","T_ALLOW_QUERY","T_ALLOW_TRANSFER","T_SEC_KEY","T_ALGID", +"T_SECRET","T_CHECK_NAMES","T_WARN","T_FAIL","T_IGNORE","T_FORWARD", +"T_FORWARDERS","T_ONLY","T_FIRST","T_IF_NO_ANSWER","T_IF_NO_DOMAIN","T_YES", +"T_TRUE","T_NO","T_FALSE", +}; +char *yyrule[] = { +"$accept : config_file", +"config_file : statement_list", +"statement_list : statement", +"statement_list : statement_list statement", +"statement : include_stmt", +"statement : options_stmt L_EOS", +"statement : logging_stmt L_EOS", +"statement : server_stmt L_EOS", +"statement : zone_stmt L_EOS", +"statement : acl_stmt L_EOS", +"statement : key_stmt L_EOS", +"statement : L_END_INCLUDE", +"statement : error L_EOS", +"statement : error L_END_INCLUDE", +"include_stmt : T_INCLUDE L_QSTRING L_EOS", +"$$1 :", +"options_stmt : T_OPTIONS $$1 '{' options '}'", +"options : option L_EOS", +"options : options option L_EOS", +"option :", +"option : T_DIRECTORY L_QSTRING", +"option : T_NAMED_XFER L_QSTRING", +"option : T_PIDFILE L_QSTRING", +"option : T_STATS_FILE L_QSTRING", +"option : T_MEMSTATS_FILE L_QSTRING", +"option : T_DUMP_FILE L_QSTRING", +"option : T_FAKE_IQUERY yea_or_nay", +"option : T_RECURSION yea_or_nay", +"option : T_FETCH_GLUE yea_or_nay", +"option : T_NOTIFY yea_or_nay", +"option : T_HOSTSTATS yea_or_nay", +"option : T_DEALLOC_ON_EXIT yea_or_nay", +"option : T_AUTH_NXDOMAIN yea_or_nay", +"option : T_MULTIPLE_CNAMES yea_or_nay", +"option : T_CHECK_NAMES check_names_type check_names_opt", +"option : T_LISTEN_ON maybe_port '{' address_match_list '}'", +"option : T_FORWARD forward_opt", +"$$2 :", +"option : T_FORWARDERS $$2 '{' opt_forwarders_list '}'", +"option : T_QUERY_SOURCE query_source", +"option : T_ALLOW_QUERY '{' address_match_list '}'", +"option : T_ALLOW_TRANSFER '{' address_match_list '}'", +"option : T_TOPOLOGY '{' address_match_list '}'", +"option : size_clause", +"option : transfer_clause", +"option : T_TRANSFER_FORMAT transfer_format", +"option : T_MAX_TRANSFER_TIME_IN L_NUMBER", +"option : T_CLEAN_INTERVAL L_NUMBER", +"option : T_INTERFACE_INTERVAL L_NUMBER", +"option : T_STATS_INTERVAL L_NUMBER", +"option : error", +"transfer_format : T_ONE_ANSWER", +"transfer_format : T_MANY_ANSWERS", +"maybe_wild_addr : L_IPADDR", +"maybe_wild_addr : '*'", +"maybe_wild_port : in_port", +"maybe_wild_port : '*'", +"query_source_address : T_ADDRESS maybe_wild_addr", +"query_source_port : T_PORT maybe_wild_port", +"query_source : query_source_address", +"query_source : query_source_port", +"query_source : query_source_address query_source_port", +"query_source : query_source_port query_source_address", +"maybe_port :", +"maybe_port : T_PORT in_port", +"yea_or_nay : T_YES", +"yea_or_nay : T_TRUE", +"yea_or_nay : T_NO", +"yea_or_nay : T_FALSE", +"yea_or_nay : L_NUMBER", +"check_names_type : T_MASTER", +"check_names_type : T_SLAVE", +"check_names_type : T_RESPONSE", +"check_names_opt : T_WARN", +"check_names_opt : T_FAIL", +"check_names_opt : T_IGNORE", +"forward_opt : T_ONLY", +"forward_opt : T_FIRST", +"forward_opt : T_IF_NO_ANSWER", +"forward_opt : T_IF_NO_DOMAIN", +"size_clause : T_DATASIZE size_spec", +"size_clause : T_STACKSIZE size_spec", +"size_clause : T_CORESIZE size_spec", +"size_clause : T_FILES size_spec", +"size_spec : any_string", +"size_spec : L_NUMBER", +"size_spec : T_DEFAULT", +"size_spec : T_UNLIMITED", +"transfer_clause : T_TRANSFERS_IN L_NUMBER", +"transfer_clause : T_TRANSFERS_OUT L_NUMBER", +"transfer_clause : T_TRANSFERS_PER_NS L_NUMBER", +"opt_forwarders_list :", +"opt_forwarders_list : forwarders_in_addr_list", +"forwarders_in_addr_list : forwarders_in_addr L_EOS", +"forwarders_in_addr_list : forwarders_in_addr_list forwarders_in_addr L_EOS", +"forwarders_in_addr : L_IPADDR", +"$$3 :", +"logging_stmt : T_LOGGING $$3 '{' logging_opts_list '}'", +"logging_opts_list : logging_opt L_EOS", +"logging_opts_list : logging_opts_list logging_opt L_EOS", +"logging_opts_list : error", +"$$4 :", +"logging_opt : T_CATEGORY category $$4 '{' channel_list '}'", +"$$5 :", +"logging_opt : T_CHANNEL channel_name $$5 '{' channel_opt_list '}'", +"channel_severity : any_string", +"channel_severity : T_DEBUG", +"channel_severity : T_DEBUG L_NUMBER", +"channel_severity : T_DYNAMIC", +"version_modifier : T_VERSIONS L_NUMBER", +"version_modifier : T_VERSIONS T_UNLIMITED", +"size_modifier : T_SIZE size_spec", +"maybe_file_modifiers :", +"maybe_file_modifiers : version_modifier", +"maybe_file_modifiers : size_modifier", +"maybe_file_modifiers : version_modifier size_modifier", +"maybe_file_modifiers : size_modifier version_modifier", +"channel_file : T_FILE L_QSTRING maybe_file_modifiers", +"facility_name : any_string", +"facility_name : T_SYSLOG", +"maybe_syslog_facility :", +"maybe_syslog_facility : facility_name", +"channel_syslog : T_SYSLOG maybe_syslog_facility", +"channel_opt : channel_file", +"channel_opt : channel_syslog", +"channel_opt : T_NULL_OUTPUT", +"channel_opt : T_SEVERITY channel_severity", +"channel_opt : T_PRINT_TIME yea_or_nay", +"channel_opt : T_PRINT_CATEGORY yea_or_nay", +"channel_opt : T_PRINT_SEVERITY yea_or_nay", +"channel_opt_list : channel_opt L_EOS", +"channel_opt_list : channel_opt_list channel_opt L_EOS", +"channel_opt_list : error", +"channel_name : any_string", +"channel_name : T_NULL_OUTPUT", +"channel : channel_name", +"channel_list : channel L_EOS", +"channel_list : channel_list channel L_EOS", +"channel_list : error", +"category_name : any_string", +"category_name : T_DEFAULT", +"category_name : T_NOTIFY", +"category : category_name", +"$$6 :", +"server_stmt : T_SERVER L_IPADDR $$6 '{' server_info_list '}'", +"server_info_list : server_info L_EOS", +"server_info_list : server_info_list server_info L_EOS", +"server_info : T_BOGUS yea_or_nay", +"server_info : T_TRANSFERS L_NUMBER", +"server_info : T_TRANSFER_FORMAT transfer_format", +"server_info : T_KEYS '{' key_list '}'", +"server_info : error", +"address_match_list : address_match_element L_EOS", +"address_match_list : address_match_list address_match_element L_EOS", +"address_match_element : address_match_simple", +"address_match_element : '!' address_match_simple", +"address_match_simple : L_IPADDR", +"address_match_simple : L_IPADDR '/' L_NUMBER", +"address_match_simple : L_NUMBER '/' L_NUMBER", +"address_match_simple : address_name", +"address_match_simple : '{' address_match_list '}'", +"address_name : any_string", +"key_ref : any_string", +"key_list_element : key_ref", +"key_list : key_list_element L_EOS", +"key_list : key_list key_list_element L_EOS", +"key_list : error", +"$$7 :", +"key_stmt : T_SEC_KEY $$7 any_string '{' key_definition '}'", +"key_definition : algorithm_id secret", +"key_definition : secret algorithm_id", +"key_definition : error", +"algorithm_id : T_ALGID any_string L_EOS", +"secret : T_SECRET any_string L_EOS", +"acl_stmt : T_ACL any_string '{' address_match_list '}'", +"$$8 :", +"zone_stmt : T_ZONE L_QSTRING optional_class $$8 optional_zone_options_list", +"optional_zone_options_list :", +"optional_zone_options_list : '{' zone_option_list '}'", +"optional_class :", +"optional_class : any_string", +"zone_type : T_MASTER", +"zone_type : T_SLAVE", +"zone_type : T_HINT", +"zone_type : T_STUB", +"zone_option_list : zone_option L_EOS", +"zone_option_list : zone_option_list zone_option L_EOS", +"zone_option : T_TYPE zone_type", +"zone_option : T_FILE L_QSTRING", +"zone_option : T_MASTERS '{' master_in_addr_list '}'", +"zone_option : T_TRANSFER_SOURCE maybe_wild_addr", +"zone_option : T_CHECK_NAMES check_names_opt", +"zone_option : T_ALLOW_UPDATE '{' address_match_list '}'", +"zone_option : T_ALLOW_QUERY '{' address_match_list '}'", +"zone_option : T_ALLOW_TRANSFER '{' address_match_list '}'", +"zone_option : T_MAX_TRANSFER_TIME_IN L_NUMBER", +"zone_option : T_NOTIFY yea_or_nay", +"zone_option : T_ALSO_NOTIFY '{' opt_notify_in_addr_list '}'", +"zone_option : error", +"master_in_addr_list : master_in_addr L_EOS", +"master_in_addr_list : master_in_addr_list master_in_addr L_EOS", +"master_in_addr : L_IPADDR", +"opt_notify_in_addr_list :", +"opt_notify_in_addr_list : notify_in_addr_list", +"notify_in_addr_list : notify_in_addr L_EOS", +"notify_in_addr_list : notify_in_addr_list notify_in_addr L_EOS", +"notify_in_addr : L_IPADDR", +"in_port : L_NUMBER", +"any_string : L_STRING", +"any_string : L_QSTRING", +}; +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 10000 +#define YYMAXDEPTH 10000 +#endif +#endif +#define YYINITSTACKSIZE 200 +int yydebug; +int yynerrs; +struct yystack { + short *ssp; + YYSTYPE *vsp; + short *ss; + YYSTYPE *vs; + int stacksize; + short *sslim; +}; +int yychar; /* some people use this, so we copy it in & out */ +int yyerrflag; /* must be global for yyerrok & YYRECOVERING */ +YYSTYPE yylval; +#line 1282 "ns_parser.y" + +static char * +canonical_name(char *name) { + char canonical[MAXDNAME]; + + if (strlen(name) >= MAXDNAME) + return (NULL); + strcpy(canonical, name); + if (makename(canonical, ".", sizeof canonical) < 0) + return (NULL); + return (savestr(canonical, 0)); +} + +static void +init_acls() { + ip_match_element ime; + ip_match_list iml; + struct in_addr address; + + /* Create the predefined ACLs */ + + address.s_addr = 0U; + + /* ACL "any" */ + ime = new_ip_match_pattern(address, 0); + iml = new_ip_match_list(); + add_to_ip_match_list(iml, ime); + define_acl(savestr("any", 1), iml); + + /* ACL "none" */ + ime = new_ip_match_pattern(address, 0); + ip_match_negate(ime); + iml = new_ip_match_list(); + add_to_ip_match_list(iml, ime); + define_acl(savestr("none", 1), iml); + + /* ACL "localhost" */ + ime = new_ip_match_localhost(); + iml = new_ip_match_list(); + add_to_ip_match_list(iml, ime); + define_acl(savestr("localhost", 1), iml); + + /* ACL "localnets" */ + ime = new_ip_match_localnets(); + iml = new_ip_match_list(); + add_to_ip_match_list(iml, ime); + define_acl(savestr("localnets", 1), iml); +} + +static void +free_sym_value(int type, void *value) { + ns_debug(ns_log_parser, 99, "free_sym_value: type %06x value %p", + type, value); + type &= ~0xffff; + switch (type) { + case SYM_ACL: + free_ip_match_list(value); + break; + case SYM_KEY: + free_key_info(value); + break; + default: + ns_panic(ns_log_parser, 1, + "unhandled case in free_sym_value()"); + /* NOTREACHED */ + break; + } +} + +static log_channel +lookup_channel(char *name) { + symbol_value value; + + if (lookup_symbol(symtab, name, SYM_CHANNEL, &value)) + return ((log_channel)(value.pointer)); + return (NULL); +} + +static void +define_channel(char *name, log_channel channel) { + symbol_value value; + + value.pointer = channel; + define_symbol(symtab, name, SYM_CHANNEL, value, SYMBOL_FREE_KEY); +} + +static void +define_builtin_channels() { + define_channel(savestr("default_syslog", 1), syslog_channel); + define_channel(savestr("default_debug", 1), debug_channel); + define_channel(savestr("default_stderr", 1), stderr_channel); + define_channel(savestr("null", 1), null_channel); +} + +static void +parser_setup() { + seen_options = 0; + seen_topology = 0; + symtab = new_symbol_table(SYMBOL_TABLE_SIZE, NULL); + if (authtab != NULL) + free_symbol_table(authtab); + authtab = new_symbol_table(AUTH_TABLE_SIZE, free_sym_value); + init_acls(); + define_builtin_channels(); +} + +static void +parser_cleanup() { + if (symtab != NULL) + free_symbol_table(symtab); + symtab = NULL; + /* + * We don't clean up authtab here because the ip_match_lists are in + * use. + */ +} + +/* + * Public Interface + */ + +ip_match_list +lookup_acl(char *name) { + symbol_value value; + + if (lookup_symbol(authtab, name, SYM_ACL, &value)) + return ((ip_match_list)(value.pointer)); + return (NULL); +} + +void +define_acl(char *name, ip_match_list iml) { + symbol_value value; + + INSIST(name != NULL); + INSIST(iml != NULL); + + value.pointer = iml; + define_symbol(authtab, name, SYM_ACL, value, + SYMBOL_FREE_KEY|SYMBOL_FREE_VALUE); + ns_debug(ns_log_parser, 7, "acl %s", name); + dprint_ip_match_list(ns_log_parser, iml, 2, "allow ", "deny "); +} + +key_info +lookup_key(char *name) { + symbol_value value; + + if (lookup_symbol(authtab, name, SYM_KEY, &value)) + return ((key_info)(value.pointer)); + return (NULL); +} + +void +define_key(char *name, key_info ki) { + symbol_value value; + + INSIST(name != NULL); + INSIST(ki != NULL); + + value.pointer = ki; + define_symbol(authtab, name, SYM_KEY, value, SYMBOL_FREE_VALUE); + dprint_key_info(ki); +} + +void +parse_configuration(const char *filename) { + FILE *config_stream; + + config_stream = fopen(filename, "r"); + if (config_stream == NULL) + ns_panic(ns_log_parser, 0, "can't open '%s'", filename); + + lexer_setup(); + parser_setup(); + lexer_begin_file(filename, config_stream); + (void)yyparse(); + lexer_end_file(); + parser_cleanup(); +} + +void +parser_initialize(void) { + lexer_initialize(); +} + +void +parser_shutdown(void) { + if (authtab != NULL) + free_symbol_table(authtab); + lexer_shutdown(); +} +#line 965 "y.tab.c" +/* allocate initial stack */ +#if defined(__STDC__) || defined(__cplusplus) +static int yyinitstack(struct yystack *sp) +#else +static int yyinitstack(sp) + struct yystack *sp; +#endif +{ + int newsize; + short *newss; + YYSTYPE *newvs; + + newsize = YYINITSTACKSIZE; + newss = (short *)malloc(newsize * sizeof *newss); + newvs = (YYSTYPE *)malloc(newsize * sizeof *newvs); + sp->ss = sp->ssp = newss; + sp->vs = sp->vsp = newvs; + if (newss == NULL || newvs == NULL) return -1; + sp->stacksize = newsize; + sp->sslim = newss + newsize - 1; + return 0; +} + +/* double stack size, up to YYMAXDEPTH */ +#if defined(__STDC__) || defined(__cplusplus) +static int yygrowstack(struct yystack *sp) +#else +static int yygrowstack(sp) + struct yystack *sp; +#endif +{ + int newsize, i; + short *newss; + YYSTYPE *newvs; + + if ((newsize = sp->stacksize) >= YYMAXDEPTH) return -1; + if ((newsize *= 2) > YYMAXDEPTH) newsize = YYMAXDEPTH; + i = sp->ssp - sp->ss; + if ((newss = (short *)realloc(sp->ss, newsize * sizeof *newss)) == NULL) + return -1; + sp->ss = newss; + sp->ssp = newss + i; + if ((newvs = (YYSTYPE *)realloc(sp->vs, newsize * sizeof *newvs)) == NULL) + return -1; + sp->vs = newvs; + sp->vsp = newvs + i; + sp->stacksize = newsize; + sp->sslim = newss + newsize - 1; + return 0; +} + +#define YYFREESTACK(sp) { free((sp)->ss); free((sp)->vs); } + +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +yyparse() +{ + register int yym, yyn, yystate, yych; + register YYSTYPE *yyvsp; + YYSTYPE yyval; + struct yystack yystk; +#if YYDEBUG + register char *yys; + extern char *getenv(); + + if (yys = getenv("YYDEBUG")) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = yych = YYEMPTY; + + if (yyinitstack(&yystk)) goto yyoverflow; + *yystk.ssp = yystate = 0; + +yyloop: + if (yyn = yydefred[yystate]) goto yyreduce; + if (yych < 0) + { + if ((yych = YYLEX) < 0) yych = 0; + yychar = yych; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yych <= YYMAXTOKEN) yys = yyname[yych]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yych, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yych) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yych) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yystk.ssp >= yystk.sslim && yygrowstack(&yystk)) + goto yyoverflow; + *++yystk.ssp = yystate = yytable[yyn]; + *++yystk.vsp = yylval; + yychar = yych = YYEMPTY; + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yych) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yych) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#ifdef lint + goto yynewerror; +#endif +yynewerror: + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yystk.ssp]) && + (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yystk.ssp, yytable[yyn]); +#endif + if (yystk.ssp >= yystk.sslim && yygrowstack(&yystk)) + goto yyoverflow; + *++yystk.ssp = yystate = yytable[yyn]; + *++yystk.vsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yystk.ssp); +#endif + if (yystk.ssp <= yystk.ss) goto yyabort; + --yystk.ssp; + --yystk.vsp; + } + } + } + else + { + if (yych == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yych <= YYMAXTOKEN) yys = yyname[yych]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yych, yys); + } +#endif + yychar = yych = YYEMPTY; + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyvsp = yystk.vsp; /* for speed in code under switch() */ + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 1: +#line 206 "ns_parser.y" +{ + /* nothing */ + } +break; +case 14: +#line 227 "ns_parser.y" +{ lexer_begin_file(yyvsp[-1].cp, NULL); } +break; +case 15: +#line 235 "ns_parser.y" +{ + if (seen_options) + parser_error(0, "cannot redefine options"); + current_options = new_options(); + } +break; +case 16: +#line 241 "ns_parser.y" +{ + if (!seen_options) + set_options(current_options, 0); + else + free_options(current_options); + current_options = NULL; + seen_options = 1; + } +break; +case 20: +#line 257 "ns_parser.y" +{ + if (current_options->directory != NULL) + freestr(current_options->directory); + current_options->directory = yyvsp[0].cp; + } +break; +case 21: +#line 263 "ns_parser.y" +{ + if (current_options->named_xfer != NULL) + freestr(current_options->named_xfer); + current_options->named_xfer = yyvsp[0].cp; + } +break; +case 22: +#line 269 "ns_parser.y" +{ + if (current_options->pid_filename != NULL) + freestr(current_options->pid_filename); + current_options->pid_filename = yyvsp[0].cp; + } +break; +case 23: +#line 275 "ns_parser.y" +{ + if (current_options->stats_filename != NULL) + freestr(current_options->stats_filename); + current_options->stats_filename = yyvsp[0].cp; + } +break; +case 24: +#line 281 "ns_parser.y" +{ + if (current_options->memstats_filename != NULL) + freestr(current_options->memstats_filename); + current_options->memstats_filename = yyvsp[0].cp; + } +break; +case 25: +#line 287 "ns_parser.y" +{ + if (current_options->dump_filename != NULL) + freestr(current_options->dump_filename); + current_options->dump_filename = yyvsp[0].cp; + } +break; +case 26: +#line 293 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_FAKE_IQUERY, yyvsp[0].num); + } +break; +case 27: +#line 297 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_NORECURSE, !yyvsp[0].num); + } +break; +case 28: +#line 301 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_NOFETCHGLUE, !yyvsp[0].num); + } +break; +case 29: +#line 305 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_NONOTIFY, !yyvsp[0].num); + } +break; +case 30: +#line 309 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_HOSTSTATS, yyvsp[0].num); + } +break; +case 31: +#line 313 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_DEALLOC_ON_EXIT, + yyvsp[0].num); + } +break; +case 32: +#line 318 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_NONAUTH_NXDOMAIN, + !yyvsp[0].num); + } +break; +case 33: +#line 323 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_MULTIPLE_CNAMES, + yyvsp[0].num); + } +break; +case 34: +#line 328 "ns_parser.y" +{ + current_options->check_names[yyvsp[-1].s_int] = yyvsp[0].s_int; + } +break; +case 35: +#line 332 "ns_parser.y" +{ + char port_string[10]; + symbol_value value; + + (void)sprintf(port_string, "%u", yyvsp[-3].us_int); + if (lookup_symbol(symtab, port_string, SYM_PORT, NULL)) + parser_error(0, + "cannot redefine listen-on for port %u", + ntohs(yyvsp[-3].us_int)); + else { + add_listen_on(current_options, yyvsp[-3].us_int, yyvsp[-1].iml); + value.pointer = NULL; + define_symbol(symtab, savestr(port_string, 1), + SYM_PORT, value, SYMBOL_FREE_KEY); + } + + } +break; +case 37: +#line 351 "ns_parser.y" +{ + if (current_options->fwdtab) { + free_forwarders(current_options->fwdtab); + current_options->fwdtab = NULL; + } + } +break; +case 40: +#line 360 "ns_parser.y" +{ + if (current_options->query_acl) + free_ip_match_list(current_options->query_acl); + current_options->query_acl = yyvsp[-1].iml; + } +break; +case 41: +#line 366 "ns_parser.y" +{ + if (current_options->transfer_acl) + free_ip_match_list(current_options->transfer_acl); + current_options->transfer_acl = yyvsp[-1].iml; + } +break; +case 42: +#line 372 "ns_parser.y" +{ + if (current_options->topology) + free_ip_match_list(current_options->topology); + current_options->topology = yyvsp[-1].iml; + } +break; +case 43: +#line 378 "ns_parser.y" +{ + /* To get around the $$ = $1 default rule. */ + } +break; +case 45: +#line 383 "ns_parser.y" +{ + current_options->transfer_format = yyvsp[0].axfr_fmt; + } +break; +case 46: +#line 387 "ns_parser.y" +{ + current_options->max_transfer_time_in = yyvsp[0].num * 60; + } +break; +case 47: +#line 391 "ns_parser.y" +{ + current_options->clean_interval = yyvsp[0].num * 60; + } +break; +case 48: +#line 395 "ns_parser.y" +{ + current_options->interface_interval = yyvsp[0].num * 60; + } +break; +case 49: +#line 399 "ns_parser.y" +{ + current_options->stats_interval = yyvsp[0].num * 60; + } +break; +case 51: +#line 406 "ns_parser.y" +{ + yyval.axfr_fmt = axfr_one_answer; + } +break; +case 52: +#line 410 "ns_parser.y" +{ + yyval.axfr_fmt = axfr_many_answers; + } +break; +case 53: +#line 415 "ns_parser.y" +{ yyval.ip_addr = yyvsp[0].ip_addr; } +break; +case 54: +#line 416 "ns_parser.y" +{ yyval.ip_addr.s_addr = htonl(INADDR_ANY); } +break; +case 55: +#line 419 "ns_parser.y" +{ yyval.us_int = yyvsp[0].us_int; } +break; +case 56: +#line 420 "ns_parser.y" +{ yyval.us_int = htons(0); } +break; +case 57: +#line 424 "ns_parser.y" +{ + current_options->query_source.sin_addr = yyvsp[0].ip_addr; + } +break; +case 58: +#line 430 "ns_parser.y" +{ + current_options->query_source.sin_port = yyvsp[0].us_int; + } +break; +case 63: +#line 441 "ns_parser.y" +{ yyval.us_int = htons(NS_DEFAULTPORT); } +break; +case 64: +#line 442 "ns_parser.y" +{ yyval.us_int = yyvsp[0].us_int; } +break; +case 65: +#line 446 "ns_parser.y" +{ + yyval.num = 1; + } +break; +case 66: +#line 450 "ns_parser.y" +{ + yyval.num = 1; + } +break; +case 67: +#line 454 "ns_parser.y" +{ + yyval.num = 0; + } +break; +case 68: +#line 458 "ns_parser.y" +{ + yyval.num = 0; + } +break; +case 69: +#line 462 "ns_parser.y" +{ + if (yyvsp[0].num == 1 || yyvsp[0].num == 0) { + yyval.num = yyvsp[0].num; + } else { + parser_warning(0, + "number should be 0 or 1; assuming 1"); + yyval.num = 1; + } + } +break; +case 70: +#line 474 "ns_parser.y" +{ + yyval.s_int = primary_trans; + } +break; +case 71: +#line 478 "ns_parser.y" +{ + yyval.s_int = secondary_trans; + } +break; +case 72: +#line 482 "ns_parser.y" +{ + yyval.s_int = response_trans; + } +break; +case 73: +#line 488 "ns_parser.y" +{ + yyval.s_int = warn; + } +break; +case 74: +#line 492 "ns_parser.y" +{ + yyval.s_int = fail; + } +break; +case 75: +#line 496 "ns_parser.y" +{ + yyval.s_int = ignore; + } +break; +case 76: +#line 502 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_FORWARD_ONLY, 1); + } +break; +case 77: +#line 506 "ns_parser.y" +{ + set_boolean_option(current_options, OPTION_FORWARD_ONLY, 0); + } +break; +case 78: +#line 510 "ns_parser.y" +{ + parser_warning(0, "forward if-no-answer is unimplemented"); + } +break; +case 79: +#line 514 "ns_parser.y" +{ + parser_warning(0, "forward if-no-domain is unimplemented"); + } +break; +case 80: +#line 520 "ns_parser.y" +{ + current_options->data_size = yyvsp[0].ul_int; + } +break; +case 81: +#line 524 "ns_parser.y" +{ + current_options->stack_size = yyvsp[0].ul_int; + } +break; +case 82: +#line 528 "ns_parser.y" +{ + current_options->core_size = yyvsp[0].ul_int; + } +break; +case 83: +#line 532 "ns_parser.y" +{ + current_options->files = yyvsp[0].ul_int; + } +break; +case 84: +#line 538 "ns_parser.y" +{ + u_long result; + + if (unit_to_ulong(yyvsp[0].cp, &result)) + yyval.ul_int = result; + else { + parser_error(0, "invalid unit string '%s'", yyvsp[0].cp); + /* 0 means "use default" */ + yyval.ul_int = 0; + } + freestr(yyvsp[0].cp); + } +break; +case 85: +#line 551 "ns_parser.y" +{ + yyval.ul_int = (u_long)yyvsp[0].num; + } +break; +case 86: +#line 555 "ns_parser.y" +{ + yyval.ul_int = 0; + } +break; +case 87: +#line 559 "ns_parser.y" +{ + yyval.ul_int = ULONG_MAX; + } +break; +case 88: +#line 565 "ns_parser.y" +{ + current_options->transfers_in = (u_long) yyvsp[0].num; + } +break; +case 89: +#line 569 "ns_parser.y" +{ + current_options->transfers_out = (u_long) yyvsp[0].num; + } +break; +case 90: +#line 573 "ns_parser.y" +{ + current_options->transfers_per_ns = (u_long) yyvsp[0].num; + } +break; +case 93: +#line 583 "ns_parser.y" +{ + /* nothing */ + } +break; +case 94: +#line 587 "ns_parser.y" +{ + /* nothing */ + } +break; +case 95: +#line 593 "ns_parser.y" +{ + add_forwarder(current_options, yyvsp[0].ip_addr); + } +break; +case 96: +#line 603 "ns_parser.y" +{ + current_logging = begin_logging(); + } +break; +case 97: +#line 607 "ns_parser.y" +{ + end_logging(current_logging, 1); + current_logging = NULL; + } +break; +case 101: +#line 619 "ns_parser.y" +{ + current_category = yyvsp[0].s_int; + } +break; +case 103: +#line 624 "ns_parser.y" +{ + chan_type = log_null; + chan_flags = 0; + chan_level = log_info; + } +break; +case 104: +#line 630 "ns_parser.y" +{ + log_channel current_channel = NULL; + + if (lookup_channel(yyvsp[-4].cp) != NULL) { + parser_error(0, "can't redefine channel '%s'", yyvsp[-4].cp); + freestr(yyvsp[-4].cp); + } else { + switch (chan_type) { + case log_file: + current_channel = + log_new_file_channel(chan_flags, + chan_level, + chan_name, NULL, + chan_versions, + chan_max_size); + freestr(chan_name); + chan_name = NULL; + break; + case log_syslog: + current_channel = + log_new_syslog_channel(chan_flags, + chan_level, + chan_facility); + break; + case log_null: + current_channel = log_new_null_channel(); + break; + default: + ns_panic(ns_log_parser, 1, + "unknown channel type: %d", + chan_type); + } + if (current_channel == NULL) + ns_panic(ns_log_parser, 0, + "couldn't create channel"); + define_channel(yyvsp[-4].cp, current_channel); + } + } +break; +case 105: +#line 671 "ns_parser.y" +{ + symbol_value value; + + if (lookup_symbol(constants, yyvsp[0].cp, SYM_LOGGING, &value)) { + chan_level = value.integer; + } else { + parser_error(0, "unknown severity '%s'", yyvsp[0].cp); + chan_level = log_debug(99); + } + freestr(yyvsp[0].cp); + } +break; +case 106: +#line 683 "ns_parser.y" +{ + chan_level = log_debug(1); + } +break; +case 107: +#line 687 "ns_parser.y" +{ + chan_level = yyvsp[0].num; + } +break; +case 108: +#line 691 "ns_parser.y" +{ + chan_level = 0; + chan_flags |= LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; + } +break; +case 109: +#line 698 "ns_parser.y" +{ + chan_versions = yyvsp[0].num; + chan_flags |= LOG_TRUNCATE; + } +break; +case 110: +#line 703 "ns_parser.y" +{ + chan_versions = LOG_MAX_VERSIONS; + chan_flags |= LOG_TRUNCATE; + } +break; +case 111: +#line 710 "ns_parser.y" +{ + chan_max_size = yyvsp[0].ul_int; + } +break; +case 112: +#line 716 "ns_parser.y" +{ + chan_versions = 0; + chan_max_size = ULONG_MAX; + } +break; +case 113: +#line 721 "ns_parser.y" +{ + chan_max_size = ULONG_MAX; + } +break; +case 114: +#line 725 "ns_parser.y" +{ + chan_versions = 0; + } +break; +case 117: +#line 733 "ns_parser.y" +{ + chan_flags |= LOG_CLOSE_STREAM; + chan_type = log_file; + chan_name = yyvsp[-1].cp; + } +break; +case 118: +#line 741 "ns_parser.y" +{ yyval.cp = yyvsp[0].cp; } +break; +case 119: +#line 742 "ns_parser.y" +{ yyval.cp = savestr("syslog", 1); } +break; +case 120: +#line 745 "ns_parser.y" +{ yyval.s_int = LOG_DAEMON; } +break; +case 121: +#line 747 "ns_parser.y" +{ + symbol_value value; + + if (lookup_symbol(constants, yyvsp[0].cp, SYM_SYSLOG, &value)) { + yyval.s_int = value.integer; + } else { + parser_error(0, "unknown facility '%s'", yyvsp[0].cp); + yyval.s_int = LOG_DAEMON; + } + freestr(yyvsp[0].cp); + } +break; +case 122: +#line 761 "ns_parser.y" +{ + chan_type = log_syslog; + chan_facility = yyvsp[0].s_int; + } +break; +case 123: +#line 767 "ns_parser.y" +{ /* nothing to do */ } +break; +case 124: +#line 768 "ns_parser.y" +{ /* nothing to do */ } +break; +case 125: +#line 770 "ns_parser.y" +{ + chan_type = log_null; + } +break; +case 126: +#line 773 "ns_parser.y" +{ /* nothing to do */ } +break; +case 127: +#line 775 "ns_parser.y" +{ + if (yyvsp[0].num) + chan_flags |= LOG_TIMESTAMP; + else + chan_flags &= ~LOG_TIMESTAMP; + } +break; +case 128: +#line 782 "ns_parser.y" +{ + if (yyvsp[0].num) + chan_flags |= LOG_PRINT_CATEGORY; + else + chan_flags &= ~LOG_PRINT_CATEGORY; + } +break; +case 129: +#line 789 "ns_parser.y" +{ + if (yyvsp[0].num) + chan_flags |= LOG_PRINT_LEVEL; + else + chan_flags &= ~LOG_PRINT_LEVEL; + } +break; +case 134: +#line 803 "ns_parser.y" +{ yyval.cp = savestr("null", 1); } +break; +case 135: +#line 807 "ns_parser.y" +{ + log_channel channel; + symbol_value value; + + if (current_category >= 0) { + channel = lookup_channel(yyvsp[0].cp); + if (channel != NULL) { + add_log_channel(current_logging, + current_category, channel); + } else + parser_error(0, "unknown channel '%s'", yyvsp[0].cp); + } + freestr(yyvsp[0].cp); + } +break; +case 140: +#line 829 "ns_parser.y" +{ yyval.cp = savestr("default", 1); } +break; +case 141: +#line 830 "ns_parser.y" +{ yyval.cp = savestr("notify", 1); } +break; +case 142: +#line 834 "ns_parser.y" +{ + symbol_value value; + + if (lookup_symbol(constants, yyvsp[0].cp, SYM_CATEGORY, &value)) + yyval.s_int = value.integer; + else { + parser_error(0, "invalid logging category '%s'", + yyvsp[0].cp); + yyval.s_int = -1; + } + freestr(yyvsp[0].cp); + } +break; +case 143: +#line 853 "ns_parser.y" +{ + char *ip_printable; + symbol_value value; + + ip_printable = inet_ntoa(yyvsp[0].ip_addr); + value.pointer = NULL; + if (lookup_symbol(symtab, ip_printable, SYM_SERVER, NULL)) + seen_server = 1; + else + seen_server = 0; + if (seen_server) + parser_error(0, "cannot redefine server '%s'", + ip_printable); + else + define_symbol(symtab, savestr(ip_printable, 1), + SYM_SERVER, value, + SYMBOL_FREE_KEY); + current_server = begin_server(yyvsp[0].ip_addr); + } +break; +case 144: +#line 873 "ns_parser.y" +{ + end_server(current_server, !seen_server); + } +break; +case 147: +#line 883 "ns_parser.y" +{ + set_server_option(current_server, SERVER_INFO_BOGUS, yyvsp[0].num); + } +break; +case 148: +#line 887 "ns_parser.y" +{ + set_server_transfers(current_server, (int)yyvsp[0].num); + } +break; +case 149: +#line 891 "ns_parser.y" +{ + set_server_transfer_format(current_server, yyvsp[0].axfr_fmt); + } +break; +case 152: +#line 903 "ns_parser.y" +{ + ip_match_list iml; + + iml = new_ip_match_list(); + if (yyvsp[-1].ime != NULL) + add_to_ip_match_list(iml, yyvsp[-1].ime); + yyval.iml = iml; + } +break; +case 153: +#line 912 "ns_parser.y" +{ + if (yyvsp[-1].ime != NULL) + add_to_ip_match_list(yyvsp[-2].iml, yyvsp[-1].ime); + yyval.iml = yyvsp[-2].iml; + } +break; +case 155: +#line 921 "ns_parser.y" +{ + if (yyvsp[0].ime != NULL) + ip_match_negate(yyvsp[0].ime); + yyval.ime = yyvsp[0].ime; + } +break; +case 156: +#line 929 "ns_parser.y" +{ + yyval.ime = new_ip_match_pattern(yyvsp[0].ip_addr, 32); + } +break; +case 157: +#line 933 "ns_parser.y" +{ + if (yyvsp[0].num < 0 || yyvsp[0].num > 32) { + parser_error(0, "mask bits out of range; skipping"); + yyval.ime = NULL; + } else { + yyval.ime = new_ip_match_pattern(yyvsp[-2].ip_addr, yyvsp[0].num); + if (yyval.ime == NULL) + parser_error(0, + "address/mask mismatch; skipping"); + } + } +break; +case 158: +#line 945 "ns_parser.y" +{ + struct in_addr ia; + + if (yyvsp[-2].num > 255) { + parser_error(0, "address out of range; skipping"); + yyval.ime = NULL; + } else { + if (yyvsp[0].num < 0 || yyvsp[0].num > 32) { + parser_error(0, + "mask bits out of range; skipping"); + yyval.ime = NULL; + } else { + ia.s_addr = htonl((yyvsp[-2].num & 0xff) << 24); + yyval.ime = new_ip_match_pattern(ia, yyvsp[0].num); + if (yyval.ime == NULL) + parser_error(0, + "address/mask mismatch; skipping"); + } + } + } +break; +case 160: +#line 967 "ns_parser.y" +{ + char name[256]; + + /* + * We want to be able to clean up this iml later so + * we give it a name and treat it like any other acl. + */ + sprintf(name, "__internal_%p", yyvsp[-1].iml); + define_acl(savestr(name, 1), yyvsp[-1].iml); + yyval.ime = new_ip_match_indirect(yyvsp[-1].iml); + } +break; +case 161: +#line 981 "ns_parser.y" +{ + ip_match_list iml; + + iml = lookup_acl(yyvsp[0].cp); + if (iml == NULL) { + parser_error(0, "unknown ACL '%s'", yyvsp[0].cp); + yyval.ime = NULL; + } else + yyval.ime = new_ip_match_indirect(iml); + freestr(yyvsp[0].cp); + } +break; +case 162: +#line 999 "ns_parser.y" +{ + key_info ki; + + ki = lookup_key(yyvsp[0].cp); + if (ki == NULL) { + parser_error(0, "unknown key '%s'", yyvsp[0].cp); + yyval.keyi = NULL; + } else + yyval.keyi = ki; + freestr(yyvsp[0].cp); + } +break; +case 163: +#line 1013 "ns_parser.y" +{ + if (yyvsp[0].keyi == NULL) + parser_error(0, "empty key not added to server list "); + else + add_server_key_info(current_server, yyvsp[0].keyi); + } +break; +case 167: +#line 1027 "ns_parser.y" +{ + current_algorithm = NULL; + current_secret = NULL; + } +break; +case 168: +#line 1032 "ns_parser.y" +{ + key_info ki; + + if (lookup_key(yyvsp[-3].cp) != NULL) { + parser_error(0, "can't redefine key '%s'", yyvsp[-3].cp); + freestr(yyvsp[-3].cp); + } else { + if (current_algorithm == NULL || + current_secret == NULL) + parser_error(0, "skipping bad key '%s'", yyvsp[-3].cp); + else { + ki = new_key_info(yyvsp[-3].cp, current_algorithm, + current_secret); + define_key(yyvsp[-3].cp, ki); + } + } + } +break; +case 169: +#line 1052 "ns_parser.y" +{ + current_algorithm = yyvsp[-1].cp; + current_secret = yyvsp[0].cp; + } +break; +case 170: +#line 1057 "ns_parser.y" +{ + current_algorithm = yyvsp[0].cp; + current_secret = yyvsp[-1].cp; + } +break; +case 171: +#line 1062 "ns_parser.y" +{ + current_algorithm = NULL; + current_secret = NULL; + } +break; +case 172: +#line 1068 "ns_parser.y" +{ yyval.cp = yyvsp[-1].cp; } +break; +case 173: +#line 1071 "ns_parser.y" +{ yyval.cp = yyvsp[-1].cp; } +break; +case 174: +#line 1079 "ns_parser.y" +{ + if (lookup_acl(yyvsp[-3].cp) != NULL) { + parser_error(0, "can't redefine ACL '%s'", yyvsp[-3].cp); + freestr(yyvsp[-3].cp); + } else + define_acl(yyvsp[-3].cp, yyvsp[-1].iml); + } +break; +case 175: +#line 1093 "ns_parser.y" +{ + int sym_type; + symbol_value value; + char *zone_name; + + if (!seen_options) + parser_error(0, + "no options statement before first zone; using previous/default"); + sym_type = SYM_ZONE | (yyvsp[0].num & 0xffff); + value.pointer = NULL; + zone_name = canonical_name(yyvsp[-1].cp); + if (zone_name == NULL) { + parser_error(0, "can't make zone name '%s' canonical", + yyvsp[-1].cp); + seen_zone = 1; + zone_name = savestr("__bad_zone__", 1); + } else { + seen_zone = lookup_symbol(symtab, zone_name, sym_type, + NULL); + if (seen_zone) { + parser_error(0, + "cannot redefine zone '%s' class %d", + zone_name, yyvsp[0].num); + } else + define_symbol(symtab, zone_name, sym_type, + value, 0); + } + freestr(yyvsp[-1].cp); + current_zone = begin_zone(zone_name, yyvsp[0].num); + } +break; +case 176: +#line 1124 "ns_parser.y" +{ end_zone(current_zone, !seen_zone); } +break; +case 179: +#line 1132 "ns_parser.y" +{ + yyval.num = C_IN; + } +break; +case 180: +#line 1136 "ns_parser.y" +{ + symbol_value value; + + if (lookup_symbol(constants, yyvsp[0].cp, SYM_CLASS, &value)) + yyval.num = value.integer; + else { + /* the zone validator will give the error */ + yyval.num = C_NONE; + } + freestr(yyvsp[0].cp); + } +break; +case 181: +#line 1150 "ns_parser.y" +{ + yyval.s_int = Z_MASTER; + } +break; +case 182: +#line 1154 "ns_parser.y" +{ + yyval.s_int = Z_SLAVE; + } +break; +case 183: +#line 1158 "ns_parser.y" +{ + yyval.s_int = Z_HINT; + } +break; +case 184: +#line 1162 "ns_parser.y" +{ + yyval.s_int = Z_STUB; + } +break; +case 187: +#line 1172 "ns_parser.y" +{ + if (!set_zone_type(current_zone, yyvsp[0].s_int)) + parser_warning(0, "zone type already set; skipping"); + } +break; +case 188: +#line 1177 "ns_parser.y" +{ + if (!set_zone_filename(current_zone, yyvsp[0].cp)) + parser_warning(0, + "zone filename already set; skipping"); + } +break; +case 190: +#line 1184 "ns_parser.y" +{ + set_zone_transfer_source(current_zone, yyvsp[0].ip_addr); + } +break; +case 191: +#line 1188 "ns_parser.y" +{ + if (!set_zone_checknames(current_zone, yyvsp[0].s_int)) + parser_warning(0, + "zone checknames already set; skipping"); + } +break; +case 192: +#line 1194 "ns_parser.y" +{ + if (!set_zone_update_acl(current_zone, yyvsp[-1].iml)) + parser_warning(0, + "zone update acl already set; skipping"); + } +break; +case 193: +#line 1200 "ns_parser.y" +{ + if (!set_zone_query_acl(current_zone, yyvsp[-1].iml)) + parser_warning(0, + "zone query acl already set; skipping"); + } +break; +case 194: +#line 1206 "ns_parser.y" +{ + if (!set_zone_transfer_acl(current_zone, yyvsp[-1].iml)) + parser_warning(0, + "zone transfer acl already set; skipping"); + } +break; +case 195: +#line 1212 "ns_parser.y" +{ + if (!set_zone_transfer_time_in(current_zone, yyvsp[0].num*60)) + parser_warning(0, + "zone max transfer time (in) already set; skipping"); + } +break; +case 196: +#line 1218 "ns_parser.y" +{ + set_zone_notify(current_zone, yyvsp[0].num); + } +break; +case 199: +#line 1226 "ns_parser.y" +{ + /* nothing */ + } +break; +case 200: +#line 1230 "ns_parser.y" +{ + /* nothing */ + } +break; +case 201: +#line 1236 "ns_parser.y" +{ + add_zone_master(current_zone, yyvsp[0].ip_addr); + } +break; +case 204: +#line 1246 "ns_parser.y" +{ + /* nothing */ + } +break; +case 205: +#line 1250 "ns_parser.y" +{ + /* nothing */ + } +break; +case 206: +#line 1256 "ns_parser.y" +{ + add_zone_notify(current_zone, yyvsp[0].ip_addr); + } +break; +case 207: +#line 1266 "ns_parser.y" +{ + if (yyvsp[0].num < 0 || yyvsp[0].num > 65535) { + parser_warning(0, + "invalid IP port number '%d'; setting port to 0", + yyvsp[0].num); + yyvsp[0].num = 0; + } else + yyval.us_int = htons(yyvsp[0].num); + } +break; +#line 2319 "y.tab.c" + } + yystk.ssp -= yym; + yystate = *yystk.ssp; + yystk.vsp -= yym; + yym = yylhs[yyn]; + yych = yychar; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yystk.ssp = YYFINAL; + *++yystk.vsp = yyval; + if (yych < 0) + { + if ((yych = YYLEX) < 0) yych = 0; + yychar = yych; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yych <= YYMAXTOKEN) yys = yyname[yych]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yych, yys); + } +#endif + } + if (yych == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yystk.ssp, yystate); +#endif + if (yystk.ssp >= yystk.sslim && yygrowstack(&yystk)) + goto yyoverflow; + *++yystk.ssp = yystate; + *++yystk.vsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + YYFREESTACK(&yystk); + return (1); +yyaccept: + YYFREESTACK(&yystk); + return (0); +} diff --git a/contrib/bind/bin/named/ns_parser.h b/contrib/bind/bin/named/ns_parser.h new file mode 100644 index 0000000..1d23613 --- /dev/null +++ b/contrib/bind/bin/named/ns_parser.h @@ -0,0 +1,110 @@ +#define YYEMPTY (-1) +#define L_EOS 257 +#define L_IPADDR 258 +#define L_NUMBER 259 +#define L_STRING 260 +#define L_QSTRING 261 +#define L_END_INCLUDE 262 +#define T_INCLUDE 263 +#define T_OPTIONS 264 +#define T_DIRECTORY 265 +#define T_PIDFILE 266 +#define T_NAMED_XFER 267 +#define T_DUMP_FILE 268 +#define T_STATS_FILE 269 +#define T_MEMSTATS_FILE 270 +#define T_FAKE_IQUERY 271 +#define T_RECURSION 272 +#define T_FETCH_GLUE 273 +#define T_QUERY_SOURCE 274 +#define T_LISTEN_ON 275 +#define T_PORT 276 +#define T_ADDRESS 277 +#define T_DATASIZE 278 +#define T_STACKSIZE 279 +#define T_CORESIZE 280 +#define T_DEFAULT 281 +#define T_UNLIMITED 282 +#define T_FILES 283 +#define T_HOSTSTATS 284 +#define T_DEALLOC_ON_EXIT 285 +#define T_TRANSFERS_IN 286 +#define T_TRANSFERS_OUT 287 +#define T_TRANSFERS_PER_NS 288 +#define T_TRANSFER_FORMAT 289 +#define T_MAX_TRANSFER_TIME_IN 290 +#define T_ONE_ANSWER 291 +#define T_MANY_ANSWERS 292 +#define T_NOTIFY 293 +#define T_AUTH_NXDOMAIN 294 +#define T_MULTIPLE_CNAMES 295 +#define T_CLEAN_INTERVAL 296 +#define T_INTERFACE_INTERVAL 297 +#define T_STATS_INTERVAL 298 +#define T_LOGGING 299 +#define T_CATEGORY 300 +#define T_CHANNEL 301 +#define T_SEVERITY 302 +#define T_DYNAMIC 303 +#define T_FILE 304 +#define T_VERSIONS 305 +#define T_SIZE 306 +#define T_SYSLOG 307 +#define T_DEBUG 308 +#define T_NULL_OUTPUT 309 +#define T_PRINT_TIME 310 +#define T_PRINT_CATEGORY 311 +#define T_PRINT_SEVERITY 312 +#define T_TOPOLOGY 313 +#define T_SERVER 314 +#define T_LONG_AXFR 315 +#define T_BOGUS 316 +#define T_TRANSFERS 317 +#define T_KEYS 318 +#define T_ZONE 319 +#define T_IN 320 +#define T_CHAOS 321 +#define T_HESIOD 322 +#define T_TYPE 323 +#define T_MASTER 324 +#define T_SLAVE 325 +#define T_STUB 326 +#define T_RESPONSE 327 +#define T_HINT 328 +#define T_MASTERS 329 +#define T_TRANSFER_SOURCE 330 +#define T_ALSO_NOTIFY 331 +#define T_ACL 332 +#define T_ALLOW_UPDATE 333 +#define T_ALLOW_QUERY 334 +#define T_ALLOW_TRANSFER 335 +#define T_SEC_KEY 336 +#define T_ALGID 337 +#define T_SECRET 338 +#define T_CHECK_NAMES 339 +#define T_WARN 340 +#define T_FAIL 341 +#define T_IGNORE 342 +#define T_FORWARD 343 +#define T_FORWARDERS 344 +#define T_ONLY 345 +#define T_FIRST 346 +#define T_IF_NO_ANSWER 347 +#define T_IF_NO_DOMAIN 348 +#define T_YES 349 +#define T_TRUE 350 +#define T_NO 351 +#define T_FALSE 352 +typedef union { + char * cp; + int s_int; + long num; + u_long ul_int; + u_int16_t us_int; + struct in_addr ip_addr; + ip_match_element ime; + ip_match_list iml; + key_info keyi; + enum axfr_format axfr_fmt; +} YYSTYPE; +extern YYSTYPE yylval; diff --git a/contrib/bind/bin/named/ns_parser.y b/contrib/bind/bin/named/ns_parser.y new file mode 100644 index 0000000..77dee0b --- /dev/null +++ b/contrib/bind/bin/named/ns_parser.y @@ -0,0 +1,1473 @@ +%{ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_parser.y,v 8.11 1997/12/04 07:03:05 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* Global C stuff goes here. */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.h" + +#include "named.h" +#include "ns_parseutil.h" +#include "ns_lexer.h" + +#define SYM_ZONE 0x010000 +#define SYM_SERVER 0x020000 +#define SYM_KEY 0x030000 +#define SYM_ACL 0x040000 +#define SYM_CHANNEL 0x050000 +#define SYM_PORT 0x060000 + +#define SYMBOL_TABLE_SIZE 29989 /* should always be prime */ +static symbol_table symtab; + +#define AUTH_TABLE_SIZE 397 /* should always be prime */ +static symbol_table authtab = NULL; + +static zone_config current_zone; +static int seen_zone; + +static options current_options; +static int seen_options; + +static topology_config current_topology; +static int seen_topology; + +static server_config current_server; +static int seen_server; + +static char *current_algorithm; +static char *current_secret; + +static log_config current_logging; +static int current_category; +static int chan_type; +static int chan_level; +static u_int chan_flags; +static int chan_facility; +static char *chan_name; +static int chan_versions; +static u_long chan_max_size; + +static log_channel lookup_channel(char *); +static void define_channel(char *, log_channel); +static char *canonical_name(char *); + +int yyparse(); + +%} + +%union { + char * cp; + int s_int; + long num; + u_long ul_int; + u_int16_t us_int; + struct in_addr ip_addr; + ip_match_element ime; + ip_match_list iml; + key_info keyi; + enum axfr_format axfr_fmt; +} + +/* Lexical analyzer return values. */ +%token L_EOS +%token <ip_addr> L_IPADDR +%token <num> L_NUMBER +%token <cp> L_STRING +%token <cp> L_QSTRING +%token L_END_INCLUDE + +/* Include support */ +%token T_INCLUDE + +/* Items related to the "options" statement: */ +%token T_OPTIONS +%token T_DIRECTORY T_PIDFILE T_NAMED_XFER +%token T_DUMP_FILE T_STATS_FILE T_MEMSTATS_FILE +%token T_FAKE_IQUERY T_RECURSION T_FETCH_GLUE +%token T_QUERY_SOURCE T_LISTEN_ON T_PORT T_ADDRESS +%type <us_int> in_port +%type <us_int> maybe_port +%type <us_int> maybe_wild_port +%type <ip_addr> maybe_wild_addr +%token T_DATASIZE T_STACKSIZE T_CORESIZE +%token T_DEFAULT T_UNLIMITED +%token T_FILES +%token T_HOSTSTATS T_DEALLOC_ON_EXIT +%token T_TRANSFERS_IN T_TRANSFERS_OUT T_TRANSFERS_PER_NS +%token T_TRANSFER_FORMAT T_MAX_TRANSFER_TIME_IN +%token T_ONE_ANSWER T_MANY_ANSWERS +%type <axfr_fmt> transfer_format +%token T_NOTIFY T_AUTH_NXDOMAIN T_MULTIPLE_CNAMES +%token T_CLEAN_INTERVAL T_INTERFACE_INTERVAL T_STATS_INTERVAL + +/* Items used for the "logging" statement: */ +%token T_LOGGING T_CATEGORY T_CHANNEL T_SEVERITY T_DYNAMIC +%token T_FILE T_VERSIONS T_SIZE +%token T_SYSLOG T_DEBUG T_NULL_OUTPUT +%token T_PRINT_TIME T_PRINT_CATEGORY T_PRINT_SEVERITY +%type <s_int> category +%type <cp> category_name channel_name facility_name +%type <s_int> maybe_syslog_facility + +/* Items used for the "topology" statement: */ +%token T_TOPOLOGY + +/* ip_match_list */ +%type <ime> address_match_simple address_match_element address_name +%type <iml> address_match_list + +/* Items used for "server" statements: */ +%token T_SERVER +%token T_LONG_AXFR +%token T_BOGUS +%token T_TRANSFERS +%token T_KEYS + +/* Items used for "zone" statements: */ +%token T_ZONE +%type <num> optional_class +%type <s_int> zone_type +%token T_IN T_CHAOS T_HESIOD +%token T_TYPE +%token T_MASTER T_SLAVE T_STUB T_RESPONSE +%token T_HINT +%token T_MASTERS T_TRANSFER_SOURCE +%token T_ALSO_NOTIFY + +/* Items used for access control lists and "allow" clauses: */ +%token T_ACL +%token T_ALLOW_UPDATE T_ALLOW_QUERY T_ALLOW_TRANSFER + +/* Items related to the "key" statement: */ +%token T_SEC_KEY T_ALGID T_SECRET +%type <keyi> key_ref +%type <cp> algorithm_id secret + +/* Items used for "size_spec" clauses: */ +%type <ul_int> size_spec + +/* Items used for a "check-names" clause: */ +%token T_CHECK_NAMES +%type <s_int> check_names_type +%type <s_int> check_names_opt +%token T_WARN T_FAIL T_IGNORE + +/* Items used for "forward" clauses: */ +%token T_FORWARD T_FORWARDERS +%token T_ONLY T_FIRST T_IF_NO_ANSWER T_IF_NO_DOMAIN + +/* Items used for yes/no responses: */ +%type <num> yea_or_nay +%token T_YES T_TRUE T_NO T_FALSE + +/* Miscellaneous items (used in several places): */ +%type <cp> any_string + +%% +config_file: statement_list + { + /* nothing */ + } + ; + +statement_list: statement + | statement_list statement + ; + +statement: include_stmt + | options_stmt L_EOS + | logging_stmt L_EOS + | server_stmt L_EOS + | zone_stmt L_EOS + | acl_stmt L_EOS + | key_stmt L_EOS + | L_END_INCLUDE + | error L_EOS + | error L_END_INCLUDE + ; + +include_stmt: T_INCLUDE L_QSTRING L_EOS { lexer_begin_file($2, NULL); } + ; + +/* + * Options + */ + +options_stmt: T_OPTIONS + { + if (seen_options) + parser_error(0, "cannot redefine options"); + current_options = new_options(); + } + '{' options '}' + { + if (!seen_options) + set_options(current_options, 0); + else + free_options(current_options); + current_options = NULL; + seen_options = 1; + } + ; + +options: option L_EOS + | options option L_EOS + ; + +option: /* Empty */ + | T_DIRECTORY L_QSTRING + { + if (current_options->directory != NULL) + freestr(current_options->directory); + current_options->directory = $2; + } + | T_NAMED_XFER L_QSTRING + { + if (current_options->named_xfer != NULL) + freestr(current_options->named_xfer); + current_options->named_xfer = $2; + } + | T_PIDFILE L_QSTRING + { + if (current_options->pid_filename != NULL) + freestr(current_options->pid_filename); + current_options->pid_filename = $2; + } + | T_STATS_FILE L_QSTRING + { + if (current_options->stats_filename != NULL) + freestr(current_options->stats_filename); + current_options->stats_filename = $2; + } + | T_MEMSTATS_FILE L_QSTRING + { + if (current_options->memstats_filename != NULL) + freestr(current_options->memstats_filename); + current_options->memstats_filename = $2; + } + | T_DUMP_FILE L_QSTRING + { + if (current_options->dump_filename != NULL) + freestr(current_options->dump_filename); + current_options->dump_filename = $2; + } + | T_FAKE_IQUERY yea_or_nay + { + set_boolean_option(current_options, OPTION_FAKE_IQUERY, $2); + } + | T_RECURSION yea_or_nay + { + set_boolean_option(current_options, OPTION_NORECURSE, !$2); + } + | T_FETCH_GLUE yea_or_nay + { + set_boolean_option(current_options, OPTION_NOFETCHGLUE, !$2); + } + | T_NOTIFY yea_or_nay + { + set_boolean_option(current_options, OPTION_NONOTIFY, !$2); + } + | T_HOSTSTATS yea_or_nay + { + set_boolean_option(current_options, OPTION_HOSTSTATS, $2); + } + | T_DEALLOC_ON_EXIT yea_or_nay + { + set_boolean_option(current_options, OPTION_DEALLOC_ON_EXIT, + $2); + } + | T_AUTH_NXDOMAIN yea_or_nay + { + set_boolean_option(current_options, OPTION_NONAUTH_NXDOMAIN, + !$2); + } + | T_MULTIPLE_CNAMES yea_or_nay + { + set_boolean_option(current_options, OPTION_MULTIPLE_CNAMES, + $2); + } + | T_CHECK_NAMES check_names_type check_names_opt + { + current_options->check_names[$2] = $3; + } + | T_LISTEN_ON maybe_port '{' address_match_list '}' + { + char port_string[10]; + symbol_value value; + + (void)sprintf(port_string, "%u", $2); + if (lookup_symbol(symtab, port_string, SYM_PORT, NULL)) + parser_error(0, + "cannot redefine listen-on for port %u", + ntohs($2)); + else { + add_listen_on(current_options, $2, $4); + value.pointer = NULL; + define_symbol(symtab, savestr(port_string, 1), + SYM_PORT, value, SYMBOL_FREE_KEY); + } + + } + | T_FORWARD forward_opt + | T_FORWARDERS + { + if (current_options->fwdtab) { + free_forwarders(current_options->fwdtab); + current_options->fwdtab = NULL; + } + } + '{' opt_forwarders_list '}' + | T_QUERY_SOURCE query_source + | T_ALLOW_QUERY '{' address_match_list '}' + { + if (current_options->query_acl) + free_ip_match_list(current_options->query_acl); + current_options->query_acl = $3; + } + | T_ALLOW_TRANSFER '{' address_match_list '}' + { + if (current_options->transfer_acl) + free_ip_match_list(current_options->transfer_acl); + current_options->transfer_acl = $3; + } + | T_TOPOLOGY '{' address_match_list '}' + { + if (current_options->topology) + free_ip_match_list(current_options->topology); + current_options->topology = $3; + } + | size_clause + { + /* To get around the $$ = $1 default rule. */ + } + | transfer_clause + | T_TRANSFER_FORMAT transfer_format + { + current_options->transfer_format = $2; + } + | T_MAX_TRANSFER_TIME_IN L_NUMBER + { + current_options->max_transfer_time_in = $2 * 60; + } + | T_CLEAN_INTERVAL L_NUMBER + { + current_options->clean_interval = $2 * 60; + } + | T_INTERFACE_INTERVAL L_NUMBER + { + current_options->interface_interval = $2 * 60; + } + | T_STATS_INTERVAL L_NUMBER + { + current_options->stats_interval = $2 * 60; + } + | error + ; + +transfer_format: T_ONE_ANSWER + { + $$ = axfr_one_answer; + } + | T_MANY_ANSWERS + { + $$ = axfr_many_answers; + } + ; + +maybe_wild_addr: L_IPADDR { $$ = $1; } + | '*' { $$.s_addr = htonl(INADDR_ANY); } + ; + +maybe_wild_port: in_port { $$ = $1; } + | '*' { $$ = htons(0); } + ; + +query_source_address: T_ADDRESS maybe_wild_addr + { + current_options->query_source.sin_addr = $2; + } + ; + +query_source_port: T_PORT maybe_wild_port + { + current_options->query_source.sin_port = $2; + } + ; + +query_source: query_source_address + | query_source_port + | query_source_address query_source_port + | query_source_port query_source_address + ; + +maybe_port: /* nothing */ { $$ = htons(NS_DEFAULTPORT); } + | T_PORT in_port { $$ = $2; } + ; + +yea_or_nay: T_YES + { + $$ = 1; + } + | T_TRUE + { + $$ = 1; + } + | T_NO + { + $$ = 0; + } + | T_FALSE + { + $$ = 0; + } + | L_NUMBER + { + if ($1 == 1 || $1 == 0) { + $$ = $1; + } else { + parser_warning(0, + "number should be 0 or 1; assuming 1"); + $$ = 1; + } + } + ; + +check_names_type: T_MASTER + { + $$ = primary_trans; + } + | T_SLAVE + { + $$ = secondary_trans; + } + | T_RESPONSE + { + $$ = response_trans; + } + ; + +check_names_opt: T_WARN + { + $$ = warn; + } + | T_FAIL + { + $$ = fail; + } + | T_IGNORE + { + $$ = ignore; + } + ; + +forward_opt: T_ONLY + { + set_boolean_option(current_options, OPTION_FORWARD_ONLY, 1); + } + | T_FIRST + { + set_boolean_option(current_options, OPTION_FORWARD_ONLY, 0); + } + | T_IF_NO_ANSWER + { + parser_warning(0, "forward if-no-answer is unimplemented"); + } + | T_IF_NO_DOMAIN + { + parser_warning(0, "forward if-no-domain is unimplemented"); + } + ; + +size_clause: T_DATASIZE size_spec + { + current_options->data_size = $2; + } + | T_STACKSIZE size_spec + { + current_options->stack_size = $2; + } + | T_CORESIZE size_spec + { + current_options->core_size = $2; + } + | T_FILES size_spec + { + current_options->files = $2; + } + ; + +size_spec: any_string + { + u_long result; + + if (unit_to_ulong($1, &result)) + $$ = result; + else { + parser_error(0, "invalid unit string '%s'", $1); + /* 0 means "use default" */ + $$ = 0; + } + freestr($1); + } + | L_NUMBER + { + $$ = (u_long)$1; + } + | T_DEFAULT + { + $$ = 0; + } + | T_UNLIMITED + { + $$ = ULONG_MAX; + } + ; + +transfer_clause: T_TRANSFERS_IN L_NUMBER + { + current_options->transfers_in = (u_long) $2; + } + | T_TRANSFERS_OUT L_NUMBER + { + current_options->transfers_out = (u_long) $2; + } + | T_TRANSFERS_PER_NS L_NUMBER + { + current_options->transfers_per_ns = (u_long) $2; + } + ; + +opt_forwarders_list: /* nothing */ + | forwarders_in_addr_list + ; + +forwarders_in_addr_list: forwarders_in_addr L_EOS + { + /* nothing */ + } + | forwarders_in_addr_list forwarders_in_addr L_EOS + { + /* nothing */ + } + ; + +forwarders_in_addr: L_IPADDR + { + add_forwarder(current_options, $1); + } + ; + +/* + * Logging + */ + +logging_stmt: T_LOGGING + { + current_logging = begin_logging(); + } + '{' logging_opts_list '}' + { + end_logging(current_logging, 1); + current_logging = NULL; + } + ; + +logging_opts_list: logging_opt L_EOS + | logging_opts_list logging_opt L_EOS + | error + ; + +logging_opt: T_CATEGORY category + { + current_category = $2; + } + '{' channel_list '}' + | T_CHANNEL channel_name + { + chan_type = log_null; + chan_flags = 0; + chan_level = log_info; + } + '{' channel_opt_list '}' + { + log_channel current_channel = NULL; + + if (lookup_channel($2) != NULL) { + parser_error(0, "can't redefine channel '%s'", $2); + freestr($2); + } else { + switch (chan_type) { + case log_file: + current_channel = + log_new_file_channel(chan_flags, + chan_level, + chan_name, NULL, + chan_versions, + chan_max_size); + freestr(chan_name); + chan_name = NULL; + break; + case log_syslog: + current_channel = + log_new_syslog_channel(chan_flags, + chan_level, + chan_facility); + break; + case log_null: + current_channel = log_new_null_channel(); + break; + default: + ns_panic(ns_log_parser, 1, + "unknown channel type: %d", + chan_type); + } + if (current_channel == NULL) + ns_panic(ns_log_parser, 0, + "couldn't create channel"); + define_channel($2, current_channel); + } + } + ; + +channel_severity: any_string + { + symbol_value value; + + if (lookup_symbol(constants, $1, SYM_LOGGING, &value)) { + chan_level = value.integer; + } else { + parser_error(0, "unknown severity '%s'", $1); + chan_level = log_debug(99); + } + freestr($1); + } + | T_DEBUG + { + chan_level = log_debug(1); + } + | T_DEBUG L_NUMBER + { + chan_level = $2; + } + | T_DYNAMIC + { + chan_level = 0; + chan_flags |= LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; + } + ; + +version_modifier: T_VERSIONS L_NUMBER + { + chan_versions = $2; + chan_flags |= LOG_TRUNCATE; + } + | T_VERSIONS T_UNLIMITED + { + chan_versions = LOG_MAX_VERSIONS; + chan_flags |= LOG_TRUNCATE; + } + ; + +size_modifier: T_SIZE size_spec + { + chan_max_size = $2; + } + ; + +maybe_file_modifiers: /* nothing */ + { + chan_versions = 0; + chan_max_size = ULONG_MAX; + } + | version_modifier + { + chan_max_size = ULONG_MAX; + } + | size_modifier + { + chan_versions = 0; + } + | version_modifier size_modifier + | size_modifier version_modifier + ; + +channel_file: T_FILE L_QSTRING maybe_file_modifiers + { + chan_flags |= LOG_CLOSE_STREAM; + chan_type = log_file; + chan_name = $2; + } + ; + + +facility_name: any_string { $$ = $1; } + | T_SYSLOG { $$ = savestr("syslog", 1); } + ; + +maybe_syslog_facility: /* nothing */ { $$ = LOG_DAEMON; } + | facility_name + { + symbol_value value; + + if (lookup_symbol(constants, $1, SYM_SYSLOG, &value)) { + $$ = value.integer; + } else { + parser_error(0, "unknown facility '%s'", $1); + $$ = LOG_DAEMON; + } + freestr($1); + } + ; + +channel_syslog: T_SYSLOG maybe_syslog_facility + { + chan_type = log_syslog; + chan_facility = $2; + } + ; + +channel_opt: channel_file { /* nothing to do */ } + | channel_syslog { /* nothing to do */ } + | T_NULL_OUTPUT + { + chan_type = log_null; + } + | T_SEVERITY channel_severity { /* nothing to do */ } + | T_PRINT_TIME yea_or_nay + { + if ($2) + chan_flags |= LOG_TIMESTAMP; + else + chan_flags &= ~LOG_TIMESTAMP; + } + | T_PRINT_CATEGORY yea_or_nay + { + if ($2) + chan_flags |= LOG_PRINT_CATEGORY; + else + chan_flags &= ~LOG_PRINT_CATEGORY; + } + | T_PRINT_SEVERITY yea_or_nay + { + if ($2) + chan_flags |= LOG_PRINT_LEVEL; + else + chan_flags &= ~LOG_PRINT_LEVEL; + } + ; + +channel_opt_list: channel_opt L_EOS + | channel_opt_list channel_opt L_EOS + | error + ; + +channel_name: any_string + | T_NULL_OUTPUT { $$ = savestr("null", 1); } + ; + +channel: channel_name + { + log_channel channel; + symbol_value value; + + if (current_category >= 0) { + channel = lookup_channel($1); + if (channel != NULL) { + add_log_channel(current_logging, + current_category, channel); + } else + parser_error(0, "unknown channel '%s'", $1); + } + freestr($1); + } + ; + +channel_list: channel L_EOS + | channel_list channel L_EOS + | error + ; + +category_name: any_string + | T_DEFAULT { $$ = savestr("default", 1); } + | T_NOTIFY { $$ = savestr("notify", 1); } + ; + +category: category_name + { + symbol_value value; + + if (lookup_symbol(constants, $1, SYM_CATEGORY, &value)) + $$ = value.integer; + else { + parser_error(0, "invalid logging category '%s'", + $1); + $$ = -1; + } + freestr($1); + } + ; + +/* + * Server Information + */ + +server_stmt: T_SERVER L_IPADDR + { + char *ip_printable; + symbol_value value; + + ip_printable = inet_ntoa($2); + value.pointer = NULL; + if (lookup_symbol(symtab, ip_printable, SYM_SERVER, NULL)) + seen_server = 1; + else + seen_server = 0; + if (seen_server) + parser_error(0, "cannot redefine server '%s'", + ip_printable); + else + define_symbol(symtab, savestr(ip_printable, 1), + SYM_SERVER, value, + SYMBOL_FREE_KEY); + current_server = begin_server($2); + } + '{' server_info_list '}' + { + end_server(current_server, !seen_server); + } + ; + +server_info_list: server_info L_EOS + | server_info_list server_info L_EOS + ; + +server_info: T_BOGUS yea_or_nay + { + set_server_option(current_server, SERVER_INFO_BOGUS, $2); + } + | T_TRANSFERS L_NUMBER + { + set_server_transfers(current_server, (int)$2); + } + | T_TRANSFER_FORMAT transfer_format + { + set_server_transfer_format(current_server, $2); + } + | T_KEYS '{' key_list '}' + | error + ; + +/* + * Address Matching + */ + +address_match_list: address_match_element L_EOS + { + ip_match_list iml; + + iml = new_ip_match_list(); + if ($1 != NULL) + add_to_ip_match_list(iml, $1); + $$ = iml; + } + | address_match_list address_match_element L_EOS + { + if ($2 != NULL) + add_to_ip_match_list($1, $2); + $$ = $1; + } + ; + +address_match_element: address_match_simple + | '!' address_match_simple + { + if ($2 != NULL) + ip_match_negate($2); + $$ = $2; + } + ; + +address_match_simple: L_IPADDR + { + $$ = new_ip_match_pattern($1, 32); + } + | L_IPADDR '/' L_NUMBER + { + if ($3 < 0 || $3 > 32) { + parser_error(0, "mask bits out of range; skipping"); + $$ = NULL; + } else { + $$ = new_ip_match_pattern($1, $3); + if ($$ == NULL) + parser_error(0, + "address/mask mismatch; skipping"); + } + } + | L_NUMBER '/' L_NUMBER + { + struct in_addr ia; + + if ($1 > 255) { + parser_error(0, "address out of range; skipping"); + $$ = NULL; + } else { + if ($3 < 0 || $3 > 32) { + parser_error(0, + "mask bits out of range; skipping"); + $$ = NULL; + } else { + ia.s_addr = htonl(($1 & 0xff) << 24); + $$ = new_ip_match_pattern(ia, $3); + if ($$ == NULL) + parser_error(0, + "address/mask mismatch; skipping"); + } + } + } + | address_name + | '{' address_match_list '}' + { + char name[256]; + + /* + * We want to be able to clean up this iml later so + * we give it a name and treat it like any other acl. + */ + sprintf(name, "__internal_%p", $2); + define_acl(savestr(name, 1), $2); + $$ = new_ip_match_indirect($2); + } + ; + +address_name: any_string + { + ip_match_list iml; + + iml = lookup_acl($1); + if (iml == NULL) { + parser_error(0, "unknown ACL '%s'", $1); + $$ = NULL; + } else + $$ = new_ip_match_indirect(iml); + freestr($1); + } + ; + +/* + * Keys + */ + +key_ref: any_string + { + key_info ki; + + ki = lookup_key($1); + if (ki == NULL) { + parser_error(0, "unknown key '%s'", $1); + $$ = NULL; + } else + $$ = ki; + freestr($1); + } + ; + +key_list_element: key_ref + { + if ($1 == NULL) + parser_error(0, "empty key not added to server list "); + else + add_server_key_info(current_server, $1); + } + ; + +key_list: key_list_element L_EOS + | key_list key_list_element L_EOS + | error + ; + +key_stmt: T_SEC_KEY + { + current_algorithm = NULL; + current_secret = NULL; + } + any_string '{' key_definition '}' + { + key_info ki; + + if (lookup_key($3) != NULL) { + parser_error(0, "can't redefine key '%s'", $3); + freestr($3); + } else { + if (current_algorithm == NULL || + current_secret == NULL) + parser_error(0, "skipping bad key '%s'", $3); + else { + ki = new_key_info($3, current_algorithm, + current_secret); + define_key($3, ki); + } + } + } + ; + +key_definition: algorithm_id secret + { + current_algorithm = $1; + current_secret = $2; + } + | secret algorithm_id + { + current_algorithm = $2; + current_secret = $1; + } + | error + { + current_algorithm = NULL; + current_secret = NULL; + } + ; + +algorithm_id: T_ALGID any_string L_EOS { $$ = $2; } + ; + +secret: T_SECRET any_string L_EOS { $$ = $2; } + ; + +/* + * ACLs + */ + +acl_stmt: T_ACL any_string '{' address_match_list '}' + { + if (lookup_acl($2) != NULL) { + parser_error(0, "can't redefine ACL '%s'", $2); + freestr($2); + } else + define_acl($2, $4); + } + ; + +/* + * Zones + */ + +zone_stmt: T_ZONE L_QSTRING optional_class + { + int sym_type; + symbol_value value; + char *zone_name; + + if (!seen_options) + parser_error(0, + "no options statement before first zone; using previous/default"); + sym_type = SYM_ZONE | ($3 & 0xffff); + value.pointer = NULL; + zone_name = canonical_name($2); + if (zone_name == NULL) { + parser_error(0, "can't make zone name '%s' canonical", + $2); + seen_zone = 1; + zone_name = savestr("__bad_zone__", 1); + } else { + seen_zone = lookup_symbol(symtab, zone_name, sym_type, + NULL); + if (seen_zone) { + parser_error(0, + "cannot redefine zone '%s' class %d", + zone_name, $3); + } else + define_symbol(symtab, zone_name, sym_type, + value, 0); + } + freestr($2); + current_zone = begin_zone(zone_name, $3); + } + optional_zone_options_list + { end_zone(current_zone, !seen_zone); } + ; + +optional_zone_options_list: /* Empty */ + | '{' zone_option_list '}' + ; + +optional_class: /* Empty */ + { + $$ = C_IN; + } + | any_string + { + symbol_value value; + + if (lookup_symbol(constants, $1, SYM_CLASS, &value)) + $$ = value.integer; + else { + /* the zone validator will give the error */ + $$ = C_NONE; + } + freestr($1); + } + ; + +zone_type: T_MASTER + { + $$ = Z_MASTER; + } + | T_SLAVE + { + $$ = Z_SLAVE; + } + | T_HINT + { + $$ = Z_HINT; + } + | T_STUB + { + $$ = Z_STUB; + } + ; + +zone_option_list: zone_option L_EOS + | zone_option_list zone_option L_EOS + ; + +zone_option: T_TYPE zone_type + { + if (!set_zone_type(current_zone, $2)) + parser_warning(0, "zone type already set; skipping"); + } + | T_FILE L_QSTRING + { + if (!set_zone_filename(current_zone, $2)) + parser_warning(0, + "zone filename already set; skipping"); + } + | T_MASTERS '{' master_in_addr_list '}' + | T_TRANSFER_SOURCE maybe_wild_addr + { + set_zone_transfer_source(current_zone, $2); + } + | T_CHECK_NAMES check_names_opt + { + if (!set_zone_checknames(current_zone, $2)) + parser_warning(0, + "zone checknames already set; skipping"); + } + | T_ALLOW_UPDATE '{' address_match_list '}' + { + if (!set_zone_update_acl(current_zone, $3)) + parser_warning(0, + "zone update acl already set; skipping"); + } + | T_ALLOW_QUERY '{' address_match_list '}' + { + if (!set_zone_query_acl(current_zone, $3)) + parser_warning(0, + "zone query acl already set; skipping"); + } + | T_ALLOW_TRANSFER '{' address_match_list '}' + { + if (!set_zone_transfer_acl(current_zone, $3)) + parser_warning(0, + "zone transfer acl already set; skipping"); + } + | T_MAX_TRANSFER_TIME_IN L_NUMBER + { + if (!set_zone_transfer_time_in(current_zone, $2*60)) + parser_warning(0, + "zone max transfer time (in) already set; skipping"); + } + | T_NOTIFY yea_or_nay + { + set_zone_notify(current_zone, $2); + } + | T_ALSO_NOTIFY '{' opt_notify_in_addr_list '}' + | error + ; + +master_in_addr_list: master_in_addr L_EOS + { + /* nothing */ + } + | master_in_addr_list master_in_addr L_EOS + { + /* nothing */ + } + ; + +master_in_addr: L_IPADDR + { + add_zone_master(current_zone, $1); + } + ; + +opt_notify_in_addr_list: /* nothing */ + | notify_in_addr_list + ; + +notify_in_addr_list: notify_in_addr L_EOS + { + /* nothing */ + } + | notify_in_addr_list notify_in_addr L_EOS + { + /* nothing */ + } + ; + +notify_in_addr: L_IPADDR + { + add_zone_notify(current_zone, $1); + } + ; + +/* + * Misc. + */ + +in_port: L_NUMBER + { + if ($1 < 0 || $1 > 65535) { + parser_warning(0, + "invalid IP port number '%d'; setting port to 0", + $1); + $1 = 0; + } else + $$ = htons($1); + } + ; + +any_string: L_STRING + | L_QSTRING + ; + +%% + +static char * +canonical_name(char *name) { + char canonical[MAXDNAME]; + + if (strlen(name) >= MAXDNAME) + return (NULL); + strcpy(canonical, name); + if (makename(canonical, ".", sizeof canonical) < 0) + return (NULL); + return (savestr(canonical, 0)); +} + +static void +init_acls() { + ip_match_element ime; + ip_match_list iml; + struct in_addr address; + + /* Create the predefined ACLs */ + + address.s_addr = 0U; + + /* ACL "any" */ + ime = new_ip_match_pattern(address, 0); + iml = new_ip_match_list(); + add_to_ip_match_list(iml, ime); + define_acl(savestr("any", 1), iml); + + /* ACL "none" */ + ime = new_ip_match_pattern(address, 0); + ip_match_negate(ime); + iml = new_ip_match_list(); + add_to_ip_match_list(iml, ime); + define_acl(savestr("none", 1), iml); + + /* ACL "localhost" */ + ime = new_ip_match_localhost(); + iml = new_ip_match_list(); + add_to_ip_match_list(iml, ime); + define_acl(savestr("localhost", 1), iml); + + /* ACL "localnets" */ + ime = new_ip_match_localnets(); + iml = new_ip_match_list(); + add_to_ip_match_list(iml, ime); + define_acl(savestr("localnets", 1), iml); +} + +static void +free_sym_value(int type, void *value) { + ns_debug(ns_log_parser, 99, "free_sym_value: type %06x value %p", + type, value); + type &= ~0xffff; + switch (type) { + case SYM_ACL: + free_ip_match_list(value); + break; + case SYM_KEY: + free_key_info(value); + break; + default: + ns_panic(ns_log_parser, 1, + "unhandled case in free_sym_value()"); + /* NOTREACHED */ + break; + } +} + +static log_channel +lookup_channel(char *name) { + symbol_value value; + + if (lookup_symbol(symtab, name, SYM_CHANNEL, &value)) + return ((log_channel)(value.pointer)); + return (NULL); +} + +static void +define_channel(char *name, log_channel channel) { + symbol_value value; + + value.pointer = channel; + define_symbol(symtab, name, SYM_CHANNEL, value, SYMBOL_FREE_KEY); +} + +static void +define_builtin_channels() { + define_channel(savestr("default_syslog", 1), syslog_channel); + define_channel(savestr("default_debug", 1), debug_channel); + define_channel(savestr("default_stderr", 1), stderr_channel); + define_channel(savestr("null", 1), null_channel); +} + +static void +parser_setup() { + seen_options = 0; + seen_topology = 0; + symtab = new_symbol_table(SYMBOL_TABLE_SIZE, NULL); + if (authtab != NULL) + free_symbol_table(authtab); + authtab = new_symbol_table(AUTH_TABLE_SIZE, free_sym_value); + init_acls(); + define_builtin_channels(); +} + +static void +parser_cleanup() { + if (symtab != NULL) + free_symbol_table(symtab); + symtab = NULL; + /* + * We don't clean up authtab here because the ip_match_lists are in + * use. + */ +} + +/* + * Public Interface + */ + +ip_match_list +lookup_acl(char *name) { + symbol_value value; + + if (lookup_symbol(authtab, name, SYM_ACL, &value)) + return ((ip_match_list)(value.pointer)); + return (NULL); +} + +void +define_acl(char *name, ip_match_list iml) { + symbol_value value; + + INSIST(name != NULL); + INSIST(iml != NULL); + + value.pointer = iml; + define_symbol(authtab, name, SYM_ACL, value, + SYMBOL_FREE_KEY|SYMBOL_FREE_VALUE); + ns_debug(ns_log_parser, 7, "acl %s", name); + dprint_ip_match_list(ns_log_parser, iml, 2, "allow ", "deny "); +} + +key_info +lookup_key(char *name) { + symbol_value value; + + if (lookup_symbol(authtab, name, SYM_KEY, &value)) + return ((key_info)(value.pointer)); + return (NULL); +} + +void +define_key(char *name, key_info ki) { + symbol_value value; + + INSIST(name != NULL); + INSIST(ki != NULL); + + value.pointer = ki; + define_symbol(authtab, name, SYM_KEY, value, SYMBOL_FREE_VALUE); + dprint_key_info(ki); +} + +void +parse_configuration(const char *filename) { + FILE *config_stream; + + config_stream = fopen(filename, "r"); + if (config_stream == NULL) + ns_panic(ns_log_parser, 0, "can't open '%s'", filename); + + lexer_setup(); + parser_setup(); + lexer_begin_file(filename, config_stream); + (void)yyparse(); + lexer_end_file(); + parser_cleanup(); +} + +void +parser_initialize(void) { + lexer_initialize(); +} + +void +parser_shutdown(void) { + if (authtab != NULL) + free_symbol_table(authtab); + lexer_shutdown(); +} diff --git a/contrib/bind/bin/named/ns_parseutil.c b/contrib/bind/bin/named/ns_parseutil.c new file mode 100644 index 0000000..aed15af --- /dev/null +++ b/contrib/bind/bin/named/ns_parseutil.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* Global C stuff goes here. */ + + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" +#include "ns_parseutil.h" + + +/* + * Symbol Table + */ + +symbol_table +new_symbol_table(int size_guess, free_function free_value) { + symbol_table st; + + st = (symbol_table)memget(sizeof (struct symbol_table)); + if (st == NULL) + panic("memget failed in new_symbol_table()", NULL); + st->table = (symbol_entry *)memget(size_guess * sizeof *st->table); + if (st->table == NULL) + panic("memget failed in new_symbol_table()", NULL); + memset(st->table, 0, size_guess * sizeof (symbol_entry)); + st->size = size_guess; /* size_guess should be prime */ + st->free_value = free_value; + return (st); +} + +void +free_symbol(symbol_table st, symbol_entry ste) { + if (ste->flags & SYMBOL_FREE_KEY) + freestr(ste->key); + if (ste->flags & SYMBOL_FREE_VALUE) + (st->free_value)(ste->type, ste->value.pointer); +} + +void +free_symbol_table(symbol_table st) { + int i; + symbol_entry ste, ste_next; + + for (i = 0; i < st->size; i++) { + for (ste = st->table[i]; ste != NULL; ste = ste_next) { + ste_next = ste->next; + free_symbol(st, ste); + memput(ste, sizeof *ste); + } + } + memput(st->table, st->size * sizeof (symbol_entry)); + memput(st, sizeof *st); +} + +void +dprint_symbol_table(int level, symbol_table st) { + int i; + symbol_entry ste; + + for (i = 0; i < st->size; i++) { + for (ste = st->table[i]; ste != NULL; ste = ste->next) + ns_debug(ns_log_parser, level, + "%7d: (%s: %d %p/%d %04x) ", + i, ste->key, ste->type, ste->value.pointer, + ste->value.integer, ste->flags); + } +} + +/* + * P. J. Weinberger's hash function, adapted from p. 436 of + * _Compilers: Principles, Techniques, and Tools_, Aho, Sethi + * and Ullman, Addison-Wesley, 1986, ISBN 0-201-10088-6. + */ +static int +symbol_hash(const char *key, int prime) { + const char *s; + unsigned int h = 0; + unsigned int g; + int c; + + for (s = key; *s != '\0'; s++) { + c = *s; + if (isascii(c) && isupper(c)) + c = tolower(c); + h = ( h << 4 ) + c; + if ((g = ( h & 0xf0000000 )) != 0) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + return (h % prime); +} + +int +lookup_symbol(symbol_table st, const char *key, int type, + symbol_value *value) { + int hash; + symbol_entry ste; + + hash = symbol_hash(key, st->size); + for (ste = st->table[hash]; ste != NULL; ste = ste->next) + if ((type == 0 || ste->type == type) && + strcasecmp(ste->key, key) == 0) + break; + if (ste != NULL) { + if (value != NULL) + *value = ste->value; + return (1); + } + return (0); +} + +void +define_symbol(symbol_table st, char *key, int type, symbol_value value, + unsigned int flags) { + int hash; + symbol_entry ste; + + hash = symbol_hash(key, st->size); + for (ste = st->table[hash]; ste != NULL; ste = ste->next) + if ((type == 0 || ste->type == type) && + strcasecmp(ste->key, key) == 0) + break; + if (ste == NULL) { + ste = (symbol_entry)memget(sizeof *ste); + if (ste == NULL) + panic("memget failed in define_symbol()", NULL); + ste->key = key; + ste->type = type; + ste->value = value; + ste->flags = flags; + ste->next = st->table[hash]; + st->table[hash] = ste; + } else { + ns_debug(ns_log_parser, 7, "redefined symbol %s type %d", + key, type); + free_symbol(st, ste); + ste->key = key; + ste->value = value; + ste->flags = flags; + } +} + +void +undefine_symbol(symbol_table st, char *key, int type) { + int hash; + symbol_entry prev_ste, ste, next_ste; + + hash = symbol_hash(key, st->size); + for (prev_ste = NULL, ste = st->table[hash]; + ste != NULL; + prev_ste = ste, ste = ste->next) + if ((type == 0 || ste->type == type) && + strcasecmp(ste->key, key) == 0) + break; + if (ste != NULL) { + free_symbol(st, ste); + if (prev_ste != NULL) + prev_ste->next = ste->next; + else + st->table[hash] = ste->next; + memput(ste, sizeof *ste); + } +} + +/* + * Conversion Routines + */ + +int +unit_to_ulong(char *in, u_long *out) { + int c, units_done = 0; + u_long result = 0L; + + INSIST(in != NULL); + + for (; (c = *in) != '\0'; in++) { + if (units_done) + return (0); + if (isdigit(c)) { + result *= 10; + result += (c - '0'); + } else { + switch (c) { + case 'k': + case 'K': + result *= 1024; + units_done = 1; + break; + case 'm': + case 'M': + result *= (1024*1024); + units_done = 1; + break; + case 'g': + case 'G': + result *= (1024*1024*1024); + units_done = 1; + break; + default: + return (0); + } + } + } + + *out = result; + return (1); +} diff --git a/contrib/bind/bin/named/ns_parseutil.h b/contrib/bind/bin/named/ns_parseutil.h new file mode 100644 index 0000000..d241bea --- /dev/null +++ b/contrib/bind/bin/named/ns_parseutil.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef NS_PARSEUTIL_H +#define NS_PARSEUTIL_H + +/* + * Symbol Table + */ + +#define SYMBOL_FREE_KEY 0x01 +#define SYMBOL_FREE_VALUE 0x02 + +typedef union symbol_value { + void *pointer; + int integer; +} symbol_value; + +typedef void (*free_function)(int, void *); + +typedef struct symbol_entry { + char *key; + int type; + symbol_value value; + unsigned int flags; + struct symbol_entry *next; +} *symbol_entry; + +typedef struct symbol_table { + int size; + symbol_entry *table; + free_function free_value; +} *symbol_table; + +symbol_table new_symbol_table(int, free_function); +void free_symbol(symbol_table, symbol_entry); +void free_symbol_table(symbol_table); +void dprint_symbol_table(int, symbol_table); +int lookup_symbol(symbol_table, const char *, int, + symbol_value *); +void define_symbol(symbol_table, char *, int, symbol_value, + unsigned int); +void undefine_symbol(symbol_table, char *, int type); + +/* + * Conversion Routines + */ + +int unit_to_ulong(char *, u_long *); + +#endif /* !NS_PARSEUTIL_H */ diff --git a/contrib/bind/bin/named/ns_req.c b/contrib/bind/bin/named/ns_req.c new file mode 100644 index 0000000..ee60ce4 --- /dev/null +++ b/contrib/bind/bin/named/ns_req.c @@ -0,0 +1,1669 @@ +#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 8.46 1998/03/27 00:21:03 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#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 <errno.h> +#include <fcntl.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +struct addinfo { + char *a_dname; /* domain name */ + char *a_rname; /* referred by */ + u_int16_t a_rtype; /* referred by */ + u_int16_t a_class; /* class for address */ +}; + +#ifndef BIND_UPDATE +enum req_action { Finish, Refuse, Return }; +#endif + +static struct addinfo addinfo[NADDRECS]; +static void addname(const char *, const char *, + u_int16_t, u_int16_t); +static void copyCharString(u_char **, const char *); + +static enum req_action req_query(HEADER *hp, u_char **cpp, u_char *eom, + struct qstream *qsp, + int *buflenp, int *msglenp, + u_char *msg, int dfd, + struct sockaddr_in from); + +static enum req_action req_iquery(HEADER *hp, u_char **cpp, u_char *eom, + int *buflenp, u_char *msg, + struct sockaddr_in from); + +#ifdef BIND_NOTIFY +static enum req_action req_notify(HEADER *hp, u_char **cpp, u_char *eom, + u_char *msg,struct sockaddr_in from); +#endif + +/* + * Process request using database; assemble and send response. + */ +void +ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, + struct sockaddr_in from, int dfd) +{ + HEADER *hp = (HEADER *) msg; + u_char *cp, *eom; + enum req_action action; + int n; + +#ifdef DEBUG + if (debug > 3) { + ns_debug(ns_log_packet, 3, "ns_req(from %s)", sin_ntoa(from)); + fp_nquery(msg, msglen, log_get_stream(packet_channel)); + } +#endif + + /* + * It's not a response so 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; + buflen -= HFIXEDSZ; + + free_addinfo(); /* sets addcount to zero */ + dnptrs[0] = NULL; + + switch (hp->opcode) { + case ns_o_query: + action = req_query(hp, &cp, eom, qsp, + &buflen, &msglen, + msg, dfd, from); + break; + + case ns_o_iquery: + action = req_iquery(hp, &cp, eom, &buflen, msg, from); + break; + +#ifdef BIND_NOTIFY + case ns_o_notify: + action = req_notify(hp, &cp, eom, msg, from); + break; +#endif + +#ifdef BIND_UPDATE + case ns_o_update: + action = req_update(hp, cp, eom, msg, qsp, dfd, from); + break; +#endif /* BIND_UPDATE */ + + default: + ns_debug(ns_log_default, 1, + "ns_req: Opcode %d not implemented", hp->opcode); + /* XXX - should syslog, limited by haveComplained */ + hp->qdcount = htons(0); + hp->ancount = htons(0); + hp->nscount = htons(0); + hp->arcount = htons(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; + cp = eom; + /*FALLTHROUGH*/ + case Finish: + /* rest of the function handles this case */ + break; + default: + panic("ns_req: bad action variable", NULL); + /*NOTREACHED*/ + } + + /* + * apply final polish + */ + hp->qr = 1; /* set Response flag */ + hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); + + n = doaddinfo(hp, cp, buflen); + cp += n; + buflen -= n; + +#ifdef DEBUG + ns_debug(ns_log_default, 1, + "ns_req: answer -> %s fd=%d id=%d size=%d", + sin_ntoa(from), (qsp == NULL) ? dfd : qsp->s_rfd, + ntohs(hp->id), cp - msg); + if (debug >= 10) + fp_nquery(msg, cp - msg, log_get_stream(packet_channel)); +#endif /*DEBUG*/ + if (qsp == NULL) { + if (sendto(dfd, (char*)msg, cp - msg, 0, + (struct sockaddr *)&from, + sizeof(from)) < 0) { + if (!haveComplained(ina_ulong(from.sin_addr), + (u_long)sendtoStr)) + ns_info(ns_log_default, + "ns_req: sendto(%s): %s", + sin_ntoa(from), strerror(errno)); + nameserIncr(from.sin_addr, nssSendtoErr); + } + nameserIncr(from.sin_addr, nssSentAns); + if (hp->rcode == NXDOMAIN) + nameserIncr(from.sin_addr, nssSentNXD); + if (!hp->aa) + nameserIncr(from.sin_addr, nssSentNaAns); + } else + writestream(qsp, msg, cp - msg); + + /* Is now a safe time? */ + if (needs_prime_cache) + prime_cache(); +} + +#ifdef BIND_NOTIFY +int +findZonePri(const struct zoneinfo *zp, const struct sockaddr_in from) { + struct in_addr ina; + int i; + + ina = from.sin_addr; + for (i = 0; (u_int)i < zp->z_addrcnt; i++) + if (ina_equal(zp->z_addr[i], ina)) + return (i); + return (-1); +} + +static enum req_action +req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg, + struct sockaddr_in from) +{ + int n, type, class, zn; + char dnbuf[MAXDNAME]; + struct namebuf *np; + const char *fname; + struct hashbuf *htp = hashtab; /* lookup relative to root */ + + /* valid notify's have one question and zero answers */ + if ((ntohs(hp->qdcount) != 1) + || ntohs(hp->ancount) != 0 + || ntohs(hp->nscount) != 0 + || ntohs(hp->arcount) != 0) { + ns_debug(ns_log_notify, 1, + "FORMERR Notify header counts wrong"); + hp->qdcount = htons(0); + hp->ancount = htons(0); + hp->nscount = htons(0); + hp->arcount = htons(0); + hp->rcode = FORMERR; + return (Finish); + } + + n = dn_expand(msg, eom, *cpp, dnbuf, sizeof dnbuf); + if (n < 0) { + ns_debug(ns_log_notify, 1, + "FORMERR Query expand name failed"); + hp->rcode = FORMERR; + return (Finish); + } + *cpp += n; + if (*cpp + 2 * INT16SZ > eom) { + ns_debug(ns_log_notify, 1, + "FORMERR notify too short"); + hp->rcode = FORMERR; + return (Finish); + } + GETSHORT(type, *cpp); + GETSHORT(class, *cpp); + ns_info(ns_log_notify, "rcvd NOTIFY(%s, %s, %s) from %s", + dnbuf, p_class(class), p_type(type), sin_ntoa(from)); + /* XXX - when answers are allowed, we'll need to do compression + * correctly here, and we will need to check for packet underflow. + */ + np = nlookup(dnbuf, &htp, &fname, 0); + if (!np) { + ns_info(ns_log_notify, + "rcvd NOTIFY for \"%s\", name not in cache", + dnbuf); + hp->rcode = SERVFAIL; + return (Finish); + } + zn = findMyZone(np, class); + if (zn == DB_Z_CACHE || zones[zn].z_type != z_slave) { + /* this can come if a user did an AXFR of some zone somewhere + * and that zone's server now wants to tell us that the SOA + * has changed. AXFR's always come from nonpriv ports so it + * isn't possible to know whether it was the server or just + * "dig". this condition can be avoided by using secure zones + * since that way only real secondaries can AXFR from you. + */ + ns_info(ns_log_notify, + "NOTIFY for non-secondary name (%s), from %s", + dnbuf, sin_ntoa(from)); + goto refuse; + } + if (findZonePri(&zones[zn], from) == -1) { + ns_info(ns_log_notify, + "NOTIFY from non-master server (zone %s), from %s", + zones[zn].z_origin, sin_ntoa(from)); + goto refuse; + } + switch (type) { + case T_SOA: + if (strcasecmp(dnbuf, zones[zn].z_origin) != 0) { + ns_info(ns_log_notify, + "NOTIFY(SOA) for non-origin (%s), from %s", + dnbuf, sin_ntoa(from)); + goto refuse; + } + if (zones[zn].z_flags & + (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL|Z_XFER_RUNNING)) { + ns_info(ns_log_notify, + "NOTIFY(SOA) for zone already xferring (%s)", + dnbuf); + goto noerror; + } + zones[zn].z_time = tt.tv_sec; + qserial_query(&zones[zn]); + sched_zone_maint(&zones[zn]); + break; + default: + /* unimplemented, but it's not a protocol error, just + * something to be ignored. + */ + break; + } + noerror: + hp->rcode = NOERROR; + return (Finish); + refuse: + hp->rcode = REFUSED; + return (Finish); +} +#endif /*BIND_NOTIFY*/ + +static enum req_action +req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, + int *buflenp, int *msglenp, u_char *msg, int dfd, + struct sockaddr_in from) +{ + int n, class, type, count, zone, foundname, founddata, omsglen, cname; + u_int16_t id; + u_char **dpp, *omsg, *answers; + char dnbuf[MAXDNAME], *dname; + const char *fname; + struct hashbuf *htp; + struct databuf *nsp[NSMAX]; + struct namebuf *np, *anp; + struct qinfo *qp; + struct zoneinfo *zp; + struct databuf *dp; + + nameserIncr(from.sin_addr, nssRcvdQ); + + nsp[0] = NULL; + dpp = dnptrs; + *dpp++ = msg; + *dpp = NULL; + /* + * Make gcc happy. + */ + omsglen = 0; + omsg = NULL; + id = 0; + + /* valid queries have one question and zero answers */ + if ((ntohs(hp->qdcount) != 1) + || ntohs(hp->ancount) != 0 + || ntohs(hp->nscount) != 0 + || ntohs(hp->arcount) != 0) { + ns_debug(ns_log_default, 1, + "FORMERR Query header counts wrong"); + hp->qdcount = htons(0); + hp->ancount = htons(0); + hp->nscount = htons(0); + hp->arcount = htons(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) { + ns_debug(ns_log_default, 1, + "FORMERR Query expand name failed"); + hp->rcode = FORMERR; + return (Finish); + } + *cpp += n; + if (*cpp + 2 * INT16SZ > eom) { + ns_debug(ns_log_default, 1, + "FORMERR Query message length short"); + hp->rcode = FORMERR; + return (Finish); + } + GETSHORT(type, *cpp); + GETSHORT(class, *cpp); + if (*cpp < eom) { + ns_debug(ns_log_default, 6, + "message length > received message"); + *msglenp = *cpp - msg; + } + + qtypeIncr(type); + + /* + * Process query. + */ + if (type == T_AXFR) { + /* Refuse request if not a TCP connection. */ + if (qsp == NULL) { + ns_info(ns_log_default, + "rejected UDP AXFR from %s for \"%s\"", + sin_ntoa(from), *dnbuf ? dnbuf : "."); + return (Refuse); + } + /* The position of this is subtle. */ + nameserIncr(from.sin_addr, nssRcvdAXFR); + hp->rd = 0; /* Recursion not possible. */ + } + *buflenp -= *msglenp; + count = 0; + founddata = 0; + dname = dnbuf; + cname = 0; + +#ifdef QRYLOG + if (qrylog) { + ns_info(ns_log_queries, "XX /%s/%s/%s", + inet_ntoa(from.sin_addr), + (dname[0] == '\0') ? "." : dname, + p_type(type)); + } +#endif /*QRYLOG*/ + + try_again: + foundname = 0; + ns_debug(ns_log_default, 1, "req: nlookup(%s) id %d type=%d class=%d", + dname, ntohs(hp->id), type, class); + htp = hashtab; /* lookup relative to root */ + if ((anp = np = nlookup(dname, &htp, &fname, 0)) == NULL) + fname = ""; + ns_debug(ns_log_default, 1, "req: %s '%s' as '%s' (cname=%d)", + np == NULL ? "missed" : "found", + dname, fname, cname); + +#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; + ns_debug(ns_log_default, 3, + "ypkludge: hit as '%s'", dname); + return (Finish); + } + } +#endif /*YPKLUDGE*/ + + /* + * Begin Access Control Point + */ + + zone = DB_Z_CACHE; + if (np) { + struct namebuf *access_np; + + /* + * Find out which zone this will be answered from. Note + * that we look for a zone with the same class as ours. + * The np that we found in the database might not be the + * one we asked for (i.e. dname might not equal fname). This + * is OK, since if a name doesn't exist, we need to go up + * the tree until we find the closest enclosing zone that + * is of the same class. + */ + for (access_np = np; access_np != NULL; + access_np = np_parent(access_np)) { + dp = access_np->n_data; + while (dp && dp->d_class != class) + dp = dp->d_next; + if (dp != NULL) { + zone = dp->d_zone; + break; + } + } + } + + zp = &zones[zone]; + + /* + * Are queries allowed from this host? + */ + if (type != T_AXFR) { + ip_match_list query_acl; + + if (zp->z_query_acl != NULL) + query_acl = zp->z_query_acl; + else + query_acl = server_options->query_acl; + + if (query_acl != NULL + && !ip_address_allowed(query_acl, from.sin_addr)) { + ns_notice(ns_log_security, + "unapproved query from %s for \"%s\"", + sin_ntoa(from), *dname ? dname : "."); + return (Refuse); + } + } else { + ip_match_list transfer_acl; + + /* Do they have permission to do a zone transfer? */ + + if (zp->z_transfer_acl != NULL) + transfer_acl = zp->z_transfer_acl; + else + transfer_acl = server_options->transfer_acl; + + if (transfer_acl != NULL + && !ip_address_allowed(transfer_acl, from.sin_addr)) { + ns_notice(ns_log_security, + "unapproved AXFR from %s for \"%s\" (acl)", + sin_ntoa(from), *dname ? dname : "."); + return (Refuse); + } + + /* Are we authoritative? */ + + if ((zp->z_flags & Z_AUTH) == 0) { + ns_notice(ns_log_security, + "unapproved AXFR from %s for \"%s\" (not auth)", + sin_ntoa(from), *dname ? dname : "."); + return (Refuse); + } + + /* Is the name at a zone cut? */ + + if (strcasecmp(zp->z_origin, dname) != 0) { + ns_notice(ns_log_security, + "unapproved AXFR from %s for \"%s\" (not zone top)", + sin_ntoa(from), *dname ? dname : "."); + return (Refuse); + } + + ns_info(ns_log_security, "approved AXFR from %s for \"%s\"", + sin_ntoa(from), *dname ? dname : "."); + } + + /* + * End Access Control Point + */ + + /* + * Yow! + */ + if (!strcasecmp(dnbuf, "VERSION.BIND") && + class == C_CHAOS && type == T_TXT) { + u_char *tp; + + hp->ancount = htons(1); + hp->nscount = htons(0); + hp->arcount = htons(0); + hp->rcode = NOERROR; + hp->aa = 1; + hp->ra = 0; + copyCharString(cpp, "VERSION"); /* Name */ + copyCharString(cpp, "BIND"); + *(*cpp)++ = 0x00; + PUTSHORT(T_TXT, *cpp); /* Type */ + PUTSHORT(C_CHAOS, *cpp); /* Class */ + PUTLONG(0, *cpp); /* TTL */ + tp = *cpp; /* Temp RdLength */ + PUTSHORT(0, *cpp); + copyCharString(cpp, ShortVersion); + PUTSHORT((*cpp) - (tp + INT16SZ), tp); /* Real RdLength */ + *msglenp = *cpp - msg; /* Total message length */ + return (Finish); + } + + /* + * If we don't know anything about the requested name, + * go look for nameservers. + */ + if (!np || fname != dname) + goto fetchns; + + foundname++; + answers = *cpp; + count = *cpp - msg; + + /* Look for NXDOMAIN record with appropriate class + * if found return immediately + */ + for (dp = np->n_data; dp; dp = dp->d_next) { + if (!stale(dp) && (dp->d_rcode == NXDOMAIN) && + (dp->d_class == class)) { +#ifdef RETURNSOA + n = finddata(np, class, T_SOA, hp, &dname, + buflenp, &count); + if (n != 0) { + if (count) { + *cpp += n; + *buflenp -= n; + *msglenp += n; + hp->nscount = htons((u_int16_t)count); + } + if (hp->rcode == NOERROR_NODATA) { + /* this should not occur */ + hp->rcode = NOERROR; + return (Finish); + } + } +#endif + hp->rcode = NXDOMAIN; + /* + * XXX forcing AA all the time isn't right, but + * we have to work that way by default + * for compatibility with older servers. + */ + if (!NS_OPTION_P(OPTION_NONAUTH_NXDOMAIN)) + hp->aa = 1; + ns_debug(ns_log_default, 3, "NXDOMAIN aa = %d", + hp->aa); + return (Finish); + } + } + + /* + * If not NXDOMAIN, the NOERROR_NODATA record might be + * anywhere in the chain. Have to go through the grind. + */ + + 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) { + ns_debug(ns_log_default, 1, + "T_AXFR refused: no data"); + return (Refuse); + } + goto fetchns; + } + + if (hp->rcode == NOERROR_NODATA) { + hp->rcode = NOERROR; +#ifdef RETURNSOA + if (count) { + *cpp += n; + *buflenp -= n; + *msglenp += n; + hp->nscount = htons(count); + } +#endif + founddata = 1; + return (Finish); + } + + *cpp += n; + *buflenp -= n; + *msglenp += n; + hp->ancount = htons(ntohs(hp->ancount) + (u_int16_t)count); + if (fname != dname && type != T_CNAME && type != T_ANY) { + if (cname++ >= MAXCNAMES) { + ns_debug(ns_log_default, 3, + "resp: leaving, MAXCNAMES exceeded"); + hp->rcode = SERVFAIL; + return (Finish); + } + goto try_again; + } + founddata = 1; + ns_debug(ns_log_default, 3, + "req: foundname=%d, count=%d, founddata=%d, cname=%d", + foundname, count, founddata, cname); + + if (type == T_AXFR) { + ns_xfr(qsp, np, zone, class, type, hp->opcode, ntohs(hp->id)); + return (Return); + } + + fetchns: + /* + * If we're already out of room in the response, we're done. + */ + if (hp->tc) + return (Finish); + + /* + * Look for name servers to refer to and fill in the authority + * section or record the address for forwarding the query + * (recursion desired). + */ + free_nsp(nsp); + nsp[0] = NULL; + count = 0; + switch (findns(&np, class, nsp, &count, 0)) { + case NXDOMAIN: + /* We are authoritative for this np. */ + if (!foundname) + hp->rcode = NXDOMAIN; + ns_debug(ns_log_default, 3, "req: leaving (%s, rcode %d)", + dname, hp->rcode); + if (class != C_ANY) { + hp->aa = 1; + if (np && (!foundname || !founddata)) { + n = doaddauth(hp, *cpp, *buflenp, np, nsp[0]); + *cpp += n; + *buflenp -= n; +#ifdef ADDAUTH + } else if (ntohs(hp->ancount) != 0) { + /* don't add NS records for NOERROR NODATA + as some servers can get confused */ + free_nsp(nsp); + switch (findns(&np, class, nsp, &count, 1)) { + case NXDOMAIN: + case SERVFAIL: + break; + default: + if (np && + (type != T_NS || np != anp) + ) { + n = add_data(np, nsp, *cpp, + *buflenp, &count); + if (n < 0) { + hp->tc = 1; + n = (-n); + } + *cpp += n; + *buflenp -= n; + hp->nscount = + htons((u_int16_t) + count); + } + } +#endif /*ADDAUTH*/ + } + } + free_nsp(nsp); + return (Finish); + + case SERVFAIL: + /* We're authoritative but the zone isn't loaded. */ + if (!founddata && + !(NS_OPTION_P(OPTION_FORWARD_ONLY) && + server_options->fwdtab)) { + hp->rcode = SERVFAIL; + free_nsp(nsp); + 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 || NS_OPTION_P(OPTION_NORECURSE)) { + /* + * If the qtype was NS, and the np of the authority is + * the same as the np of the data, we don't need to add + * another copy of the answer here in the authority + * section. + */ + if (!founddata || type != T_NS || anp != np) { + n = add_data(np, nsp, *cpp, *buflenp, &count); + if (n < 0) { + hp->tc = 1; + n = (-n); + } + *cpp += n; + *buflenp -= n; + hp->nscount = htons((u_int16_t)count); + } + free_nsp(nsp); + + /* Our caller will handle the Additional section. */ + 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 *)memget((unsigned) *msglenp); + if (omsg == NULL) { + ns_info(ns_log_default, "ns_req: Out Of Memory"); + hp->rcode = SERVFAIL; + free_nsp(nsp); + return (Finish); + } + id = hp->id; + omsglen = *msglenp; + memcpy(omsg, msg, omsglen); + n = res_mkquery(QUERY, dname, class, type, + NULL, 0, NULL, msg, + *msglenp + *buflenp); + if (n < 0) { + ns_info(ns_log_default, "res_mkquery(%s) failed", + dname); + hp->rcode = SERVFAIL; + free_nsp(nsp); + return (Finish); + } + *msglenp = n; + } + n = ns_forw(nsp, msg, *msglenp, from, qsp, dfd, &qp, + dname, class, type, np, 0); + if (n != FW_OK && cname) + memput(omsg, omsglen); + 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 (NAME(*np)[0] == '\0') { + ns_notice(ns_log_default, + "ns_req: no address for root server"); + hp->rcode = SERVFAIL; + free_nsp(nsp); + return (Finish); + } + for (dp = np->n_data; dp ; dp = dp->d_next) + if (dp->d_zone && match(dp, class, T_NS)) + break; + if (dp) { + /* + * we know the child zone exists but are + * missing glue. + * + * nslookup has called sysquery() to get the + * missing glue. + * + * for UDP, drop the response and let the + * client retry. for TCP, we should probably + * (XXX) hold open the TCP connection for a + * while in case the sysquery() comes back + * soon. meanwhile we SERVFAIL. + */ + if (qsp) + goto do_servfail; + break; + } + np = np_parent(np); + } + goto fetchns; /* Try again. */ + case FW_SERVFAIL: + do_servfail: + hp->rcode = SERVFAIL; + free_nsp(nsp); + return (Finish); + } + free_nsp(nsp); + return (Return); +} + +static enum req_action +req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp, + u_char *msg, struct sockaddr_in from) +{ + int dlen, alen, n, type, class, count; + char dnbuf[MAXDNAME], anbuf[PACKETSZ], *data, *fname; + + nameserIncr(from.sin_addr, nssRcvdIQ); + + if (ntohs(hp->ancount) != 1 + || ntohs(hp->qdcount) != 0 + || ntohs(hp->nscount) != 0 + || ntohs(hp->arcount) != 0) { + ns_debug(ns_log_default, 1, + "FORMERR IQuery header counts wrong"); + hp->qdcount = htons(0); + hp->ancount = htons(0); + hp->nscount = htons(0); + hp->arcount = htons(0); + hp->rcode = FORMERR; + return (Finish); + } + + /* + * Skip domain name, get class, and type. + */ + if ((n = dn_skipname(*cpp, eom)) < 0) { + ns_debug(ns_log_default, 1, + "FORMERR IQuery packet name problem"); + hp->rcode = FORMERR; + return (Finish); + } + *cpp += n; + if (*cpp + 3 * INT16SZ + INT32SZ > eom) { + ns_debug(ns_log_default, 1, + "FORMERR IQuery message too short"); + hp->rcode = FORMERR; + return (Finish); + } + GETSHORT(type, *cpp); + GETSHORT(class, *cpp); + *cpp += INT32SZ; /* ttl */ + GETSHORT(dlen, *cpp); + *cpp += dlen; + if (*cpp != eom) { + ns_debug(ns_log_default, 1, + "FORMERR IQuery message length off"); + hp->rcode = FORMERR; + return (Finish); + } + + /* + * not all inverse queries are handled. + */ + switch (type) { + case T_A: + if (!NS_OPTION_P(OPTION_FAKE_IQUERY) || dlen != INT32SZ) + return (Refuse); + break; + default: + return (Refuse); + } + ns_debug(ns_log_default, 1, + "req: IQuery class %d type %d", class, type); + + fname = (char *)msg + HFIXEDSZ; + alen = (char *)*cpp - fname; + if ((size_t)alen > sizeof anbuf) + return (Refuse); + memcpy(anbuf, fname, alen); + data = anbuf + alen - dlen; + *cpp = (u_char *)fname; + *buflenp -= HFIXEDSZ; + count = 0; + +#ifdef QRYLOG + if (qrylog) { + char tmp[sizeof "255.255.255.255"]; + + strcpy(tmp, inet_ntoa(from.sin_addr)); + ns_info(ns_log_queries, "XX /%s/%s/-%s", + tmp, inet_ntoa(ina_get((u_char *)data)), + p_type(type)); + } +#endif /*QRYLOG*/ + + /* + * We can only get here if the option "fake-iquery" is on in the boot + * file. + * + * What we do here is send back a bogus response of "[dottedquad]". + * A better strategy would be to turn this into a PTR query, but that + * would legitimize inverse queries in a way they do not deserve. + */ + sprintf(dnbuf, "[%s]", inet_ntoa(ina_get((u_char *)data))); + *buflenp -= QFIXEDSZ; + n = dn_comp(dnbuf, *cpp, *buflenp, NULL, NULL); + if (n < 0) { + hp->tc = 1; + return (Finish); + } + *cpp += n; + PUTSHORT((u_int16_t)type, *cpp); + PUTSHORT((u_int16_t)class, *cpp); + *buflenp -= n; + count++; + + ns_debug(ns_log_default, 1, "req: IQuery %d records", count); + hp->qdcount = htons((u_int16_t)count); + if (alen > *buflenp) { + hp->tc = 1; + return (Finish); + } + memcpy(*cpp, anbuf, alen); + *cpp += alen; + return (Finish); +} + +/* + * Test a datum for validity and return non-zero if it is out of date. + */ +int +stale(struct databuf *dp) { + struct zoneinfo *zp = &zones[dp->d_zone]; + + switch (zp->z_type) { + + case z_master: + return (0); + +#ifdef STUBS + case z_stub: + /* root stub zones have DB_F_HINT set */ + if (dp->d_flags & DB_F_HINT) + return (0); + /* FALLTROUGH */ +#endif + case z_slave: + /* + * Check to see whether a secondary zone has expired or + * time warped; if so clear authority flag for zone, + * schedule the zone for immediate maintenance, and + * return true. + */ + if ((int32_t)(tt.tv_sec - zp->z_lastupdate) + > (int32_t)zp->z_expire) { + ns_debug(ns_log_default, 1, + "stale: secondary zone %s expired", + zp->z_origin); + if (!haveComplained((u_long)zp, (u_long)stale)) { + ns_notice(ns_log_default, + "secondary zone \"%s\" expired", + zp->z_origin); + } + zp->z_flags &= ~Z_AUTH; + if (!(zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING))) { + zp->z_time = tt.tv_sec; + sched_zone_maint(zp); + } + return (1); + } + if (zp->z_lastupdate > tt.tv_sec) { + if (!haveComplained((u_long)zp, (u_long)stale)) { + ns_notice(ns_log_default, + "secondary zone \"%s\" time warp", + zp->z_origin); + } + zp->z_flags &= ~Z_AUTH; + if (!(zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING))) { + zp->z_time = tt.tv_sec; + sched_zone_maint(zp); + } + return (1); + } + return (0); + + case z_hint: + if (dp->d_flags & DB_F_HINT || + dp->d_ttl >= (u_int32_t)tt.tv_sec) + return (0); + ns_debug(ns_log_default, 3, "stale: ttl %d %ld (x%lx)", + dp->d_ttl, (long)(dp->d_ttl - tt.tv_sec), + (u_long)dp->d_flags); + return (1); + + default: + /* FALLTHROUGH */ ; + + } + panic("stale: impossible condition", NULL); + /* NOTREACHED */ + return (0); /* Make gcc happy. */ +} + +/* + * Copy databuf into a resource record for replies. + * Return size of RR if OK, -1 if buffer is full. + */ +int +make_rr(const char *name, struct databuf *dp, u_char *buf, + int buflen, int doadd, u_char **comp_ptrs, u_char **edp) +{ + u_char *cp; + u_char *cp1, *sp; + struct zoneinfo *zp; + int32_t n; + int16_t type = dp->d_type; + u_int32_t ttl; +#ifdef BIND_UPDATE + u_int32_t serial; +#endif + + ns_debug(ns_log_default, 5, + "make_rr(%s, %lx, %lx, %d, %d) %d zone %d ttl %lu", + name, (u_long)dp, (u_long)buf, + buflen, doadd, dp->d_size, dp->d_zone, (u_long)dp->d_ttl); + + if (dp->d_rcode +#ifdef RETURNSOA + && dp->d_size == 0 +#endif + ) + panic("make_rr: impossible d_rcode value", NULL); + + zp = &zones[dp->d_zone]; + /* check for outdated RR before updating comp_ptrs[] by dn_comp() */ + if (zp->z_type == Z_CACHE) { + if ((dp->d_flags & DB_F_HINT) != 0 + || dp->d_ttl < (u_int32_t)tt.tv_sec) { + ttl = 0; + } else + ttl = dp->d_ttl - (u_int32_t) tt.tv_sec; + } else { + if (dp->d_ttl != USE_MINIMUM) + ttl = dp->d_ttl; + else + ttl = zp->z_minimum; /* really default */ + } + + buflen -= RRFIXEDSZ; + if (buflen < 0) + return (-1); +#ifdef RETURNSOA + if (dp->d_rcode) { + name = (char *)dp->d_data; + name += strlen(name) +1; + name += strlen(name) +1; + name += 5 * INT32SZ; + type = T_SOA; + } +#endif + if ((n = dn_comp(name, buf, buflen, comp_ptrs, edp)) < 0) + return (-1); + cp = buf + n; + buflen -= n; + if (buflen < 0) + return (-1); + PUTSHORT((u_int16_t)type, cp); + PUTSHORT((u_int16_t)dp->d_class, cp); + PUTLONG(ttl, cp); + sp = cp; + cp += INT16SZ; + switch (type) { + case T_CNAME: + case T_MG: + case T_MR: + case T_PTR: + n = dn_comp((char *)dp->d_data, cp, buflen, comp_ptrs, 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, comp_ptrs, edp); + if (n < 0) + return (-1); + PUTSHORT((u_int16_t)n, sp); + cp += n; + if (doadd) + addname((char*)dp->d_data, name, + type, dp->d_class); + break; + + case T_SOA: + case T_MINFO: + case T_RP: + cp1 = dp->d_data; + n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); + if (n < 0) + return (-1); + cp += n; + buflen -= type == T_SOA ? n + 5 * INT32SZ : n; + if (buflen < 0) + return (-1); + cp1 += strlen((char *)cp1) + 1; + n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); + if (n < 0) + return (-1); + cp += n; + if (type == T_SOA) { + cp1 += strlen((char *)cp1) + 1; +#ifdef BIND_UPDATE + if (zp->z_flags & Z_NEED_SOAUPDATE) + if (incr_serial(zp) < 0) + ns_error(ns_log_default, + "error updating serial number for %s from %d", + zp->z_origin, zp->z_serial); +#endif + n = 5 * INT32SZ; + memcpy(cp, cp1, n); + cp += n; + } + n = (u_int16_t)((cp - sp) - INT16SZ); + PUTSHORT((u_int16_t)n, sp); + break; + + case T_NAPTR: + /* cp1 == our data/ cp == data of RR */ + cp1 = dp->d_data; + + /* copy order */ + buflen -= INT16SZ; + if (buflen < 0) + return (-1); + memcpy(cp, cp1, INT16SZ); + cp += INT16SZ; + cp1 += INT16SZ; + n = (u_int16_t)((cp - sp) - INT16SZ); + ns_debug(ns_log_default, 1, "current size n = %u", n); + + /* copy preference */ + buflen -= INT16SZ; + if (buflen < 0) + return (-1); + memcpy(cp, cp1, INT16SZ); + cp += INT16SZ; + cp1 += INT16SZ; + n = (u_int16_t)((cp - sp) - INT16SZ); + ns_debug(ns_log_default, 1, "current size n = %u", n); + + /* Flags */ + n = *cp1++; + ns_debug(ns_log_default, 1, "size of n at flags = %d", n); + buflen -= n + 1; + if (buflen < 0) + return (-1); + *cp++ = n; + memcpy(cp, cp1, n); + cp += n; + cp1 += n; + n = (u_int16_t)((cp - sp) - INT16SZ); + ns_debug(ns_log_default, 1, "current size n = %u", n); + + /* Service */ + n = *cp1++; + buflen -= n + 1; + if (buflen < 0) + return (-1); + *cp++ = n; + memcpy(cp, cp1, n); + cp += n; + cp1 += n; + n = (u_int16_t)((cp - sp) - INT16SZ); + ns_debug(ns_log_default, 1, "current size n = %u", n); + + /* Regexp */ + n = *cp1++; + buflen -= n + 1; + if (buflen < 0) + return (-1); + *cp++ = n; + memcpy(cp, cp1, n); + cp += n; + cp1 += n; + n = (u_int16_t)((cp - sp) - INT16SZ); + ns_debug(ns_log_default, 1, "current size n = %u", n); + + /* Replacement */ + ns_debug(ns_log_default, 1, "Replacement = %s", cp1); + n = dn_comp((char *)cp1, cp, buflen, dnptrs, edp); + ns_debug(ns_log_default, 1, "dn_comp's n = %u", n); + if (n < 0) + return (-1); + cp += n; + + /* save data length */ + n = (u_int16_t)((cp - sp) - INT16SZ); + ns_debug(ns_log_default, 1, "saved size n = %u", n); + PUTSHORT((u_int16_t)n, sp); + + break; + + case T_MX: + case T_AFSDB: + case T_RT: + case T_SRV: + /* cp1 == our data/ cp == data of RR */ + cp1 = dp->d_data; + + if ((buflen -= INT16SZ) < 0) + return (-1); + + /* copy preference */ + memcpy(cp, cp1, INT16SZ); + cp += INT16SZ; + cp1 += INT16SZ; + + if (type == T_SRV) { + buflen -= INT16SZ*2; + if (buflen < 0) + return (-1); + memcpy(cp, cp1, INT16SZ*2); + cp += INT16SZ*2; + cp1 += INT16SZ*2; + } + + n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, 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, name, type, dp->d_class); + break; + + case T_PX: + cp1 = dp->d_data; + + if ((buflen -= INT16SZ) < 0) + return (-1); + + /* copy preference */ + memcpy(cp, cp1, INT16SZ); + cp += INT16SZ; + cp1 += INT16SZ; + + n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); + if (n < 0) + return (-1); + cp += n; + buflen -= n; + cp1 += strlen((char *)cp1) + 1; + n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); + if (n < 0) + return (-1); + cp += n; + + /* save data length */ + n = (u_int16_t)((cp - sp) - INT16SZ); + PUTSHORT((u_int16_t)n, sp); + break; + + case T_SIG: + /* cp1 == our data; cp == data of target RR */ + cp1 = dp->d_data; + + /* first just copy over the type_covered, algorithm, */ + /* labels, orig ttl, two timestamps, and the footprint */ + if ((dp->d_size - 18) > buflen) + return (-1); /* out of room! */ + memcpy(cp, cp1, 18); + cp += 18; + cp1 += 18; + buflen -= 18; + + /* then the signer's name */ + n = dn_comp((char *)cp1, cp, buflen, NULL, NULL); + if (n < 0) + return (-1); + cp += n; + buflen -= n; + cp1 += strlen((char*)cp1)+1; + + /* finally, we copy over the variable-length signature */ + n = dp->d_size - (u_int16_t)((cp1 - dp->d_data)); + if (n > buflen) + return (-1); /* out of room! */ + memcpy(cp, cp1, n); + cp += n; + + /* save data length & return */ + n = (u_int16_t)((cp - sp) - INT16SZ); + PUTSHORT((u_int16_t)n, sp); + break; + + default: + if (dp->d_size > buflen) + return (-1); + memcpy(cp, dp->d_data, dp->d_size); + PUTSHORT((u_int16_t)dp->d_size, sp); + cp += dp->d_size; + } + return (cp - buf); +} + +static void +addname(const char *dname, const char *rname, + u_int16_t rtype, u_int16_t class) +{ + struct addinfo *ap; + int n; + + for (ap = addinfo, n = addcount; --n >= 0; ap++) + if (strcasecmp(ap->a_dname, dname) == 0) + return; + + /* add domain name to additional section */ + if (addcount < NADDRECS) { + addcount++; + ap->a_dname = savestr(dname, 1); + ap->a_rname = savestr(rname, 1); + ap->a_rtype = rtype; + ap->a_class = class; + } +} + +/* + * Lookup addresses for names in addinfo and put into the message's + * additional section. + */ +int +doaddinfo(HEADER *hp, u_char *msg, int msglen) { + struct namebuf *np; + struct databuf *dp; + struct addinfo *ap; + u_char *cp; + struct hashbuf *htp; + const char *fname; + int n, count; + + if (!addcount) + return (0); + + ns_debug(ns_log_default, 3, "doaddinfo() addcount = %d", addcount); + + if (hp->tc) { + ns_debug(ns_log_default, 4, + "doaddinfo(): tc already set, bailing"); + return (0); + } + + count = 0; + cp = msg; + for (ap = addinfo; --addcount >= 0; ap++) { + int foundany = 0, + foundcname = 0, + save_count = count, + save_msglen = msglen; + u_char *save_cp = cp; + + ns_debug(ns_log_default, 3, + "do additional \"%s\" (from \"%s\")", + ap->a_dname, ap->a_rname); + 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; + ns_debug(ns_log_default, 3, "found it"); + /* look for the data */ + delete_stale(np); + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (dp->d_rcode) + continue; + if (match(dp, (int)ap->a_class, T_CNAME) || + match(dp, C_IN, T_CNAME)) { + foundcname++; + break; + } + if (!match(dp, (int)ap->a_class, T_A) && + !match(dp, C_IN, T_A) && + !match(dp, (int)ap->a_class, T_AAAA) && + !match(dp, C_IN, T_AAAA)) { + continue; + } + foundany++; + /* + * Should be smart and eliminate duplicate + * data here. XXX + */ + if ((n = make_rr(ap->a_dname, dp, cp, msglen, 0, + dnptrs, dnptrs_end)) < 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 + */ + ns_debug(ns_log_default, 5, + "addinfo: not enough room, remaining msglen = %d", + save_msglen); + cp = save_cp; + msglen = save_msglen; + count = save_count; + break; + } + ns_debug(ns_log_default, 5, + "addinfo: adding address data n = %d", n); + cp += n; + msglen -= n; + count++; + } + next_rr: + if (!NS_OPTION_P(OPTION_NOFETCHGLUE) && + !foundcname && !foundany) { + /* ask a real server for this info */ + (void) sysquery(ap->a_dname, (int)ap->a_class, T_A, + NULL, 0, QUERY); + } + if (foundcname) { + if (!haveComplained(nhash(ap->a_dname), + nhash(ap->a_rname))) { + ns_info(ns_log_cname, + "\"%s %s %s\" points to a CNAME (%s)", + ap->a_rname, p_class(ap->a_class), + p_type(ap->a_rtype), ap->a_dname); + } + } + freestr(ap->a_dname); + freestr(ap->a_rname); + } + hp->arcount = htons((u_int16_t)count); + return (cp - msg); +} + +int +doaddauth(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)) { + ns_debug(ns_log_default, 1, + "doaddauth: can't add stale '%s' (%d)", + dnbuf, buflen); + return (0); + } + n = make_rr(dnbuf, dp, cp, buflen, 1, dnptrs, dnptrs_end); + if (n <= 0) { + ns_debug(ns_log_default, 1, + "doaddauth: can't add oversize '%s' (%d) (n=%d)", + dnbuf, buflen, n); + if (n < 0) { + hp->tc = 1; + } + return (0); + } + hp->nscount = htons(ntohs(hp->nscount) + 1); + return (n); +} + +void +free_addinfo() { + struct addinfo *ap; + + for (ap = addinfo; --addcount >= 0; ap++) { + freestr(ap->a_dname); + freestr(ap->a_rname); + } + addcount = 0; +} + +void +free_nsp(struct databuf **nsp) { + while (*nsp) { + DRCNTDEC(*nsp); + if ((*nsp)->d_rcnt) + ns_debug(ns_log_default, 3, "free_nsp: %s rcnt %d", + (*nsp)->d_data, (*nsp)->d_rcnt); + else { + ns_debug(ns_log_default, 3, + "free_nsp: %s rcnt %d delayed", + (*nsp)->d_data, (*nsp)->d_rcnt); + db_freedata(*nsp); /* delayed free */ + } + *nsp++ = NULL; + } +} + +static void +copyCharString(u_char **dst, const char *src) { + size_t len = strlen(src) & 0xff; + *(*dst)++ = (u_char) len; + memcpy(*dst, src, len); + *dst += len; +} diff --git a/contrib/bind/bin/named/ns_resp.c b/contrib/bind/bin/named/ns_resp.c new file mode 100644 index 0000000..012f89e --- /dev/null +++ b/contrib/bind/bin/named/ns_resp.c @@ -0,0 +1,3298 @@ +#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 8.56 1998/03/16 19:40:07 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#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 <errno.h> +#include <limits.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +static u_int8_t norootlogged[MAXCLASS]; /* XXX- should be a bitmap */ + +static const char skipnameFailedAnswer[] = "skipname failed in answer", + skipnameFailedAuth[] = "skipname failed in authority", + skipnameFailedQuery[] = "skipname failed in query", + outofDataQuery[] = "ran out of data in query", + outofDataAnswer[] = "ran out of data in answer", + notSingleQuery[] = "not exactly one query", + expandFailedQuery[] = "dn_expand failed in query", + expandFailedAnswer[] = "dn_expand failed in answer", + expandFailedAuth[] = "dn_expand failed in authority", + outofDataAuth[] = "ran out of data in authority", + dlenOverrunAnswer[] = "dlen overrun in answer", + dlenOverrunAuth[] = "dlen overrun in authority", + dlenUnderrunAnswer[] = "dlen underrun in answer", + outofDataFinal[] = "out of data in final pass", + outofDataAFinal[] = "out of data after final pass", + badNameFound[] = "found an invalid domain name", + wrongQuestion[] = "answer to wrong question", + danglingCname[] = "dangling CNAME pointer"; + +struct db_list { + struct db_list *db_next; + struct databuf *db_dp; +}; + +struct flush_set { + char * fs_name; + int fs_type; + int fs_class; + u_int fs_cred; + struct db_list *fs_list; + struct db_list *fs_last; +}; + +static void rrsetadd(struct flush_set *, const char *, + struct databuf *), + rrsetupdate(struct flush_set *, int flags, + struct sockaddr_in), + flushrrset(struct flush_set *, struct sockaddr_in), + free_flushset(struct flush_set *, int); +static int rrsetcmp(char *, struct db_list *), + check_root(void), + check_ns(void), + rrextract(u_char *, int, u_char *, + struct databuf **, char *, int, + struct sockaddr_in, char **); +static void sysnotify_slaves(const char *, const char *, + int, int, int *, int *); +static void sysnotify_ns(const char *, const char *, + int, int, int *, int *); +static void add_related_additional(char *); +static void free_related_additional(void); +static int related_additional(char *); +static void freestr_maybe(char **); + +#define MAX_RELATED 100 + +static int num_related = 0; +static char *related[MAX_RELATED]; + +static char * +learntFrom(struct qinfo *qp, struct sockaddr_in *server) { + static char *buf = NULL; + char *a, *ns, *na; + struct databuf *db; + int i; + + a = ns = na = "<Not Available>"; + + for (i = 0; (u_int)i < qp->q_naddr; i++) { + if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, + server->sin_addr)) { + db = qp->q_addr[i].ns; + if (db != NULL) { + if (NS_OPTION_P(OPTION_HOSTSTATS)) { + char nsbuf[20]; + + if (db->d_ns != NULL) { + strcpy(nsbuf, + inet_ntoa(db->d_ns->addr)); + ns = nsbuf; + } else { + ns = zones[db->d_zone] + .z_origin; + } + } + if (db->d_rcode == 0) + na = (char*)qp->q_addr[i].ns->d_data; + } + + if (NS_OPTION_P(OPTION_HOSTSTATS)) { + char abuf[20]; + + db = qp->q_addr[i].nsdata; + if (db != NULL) { + if (db->d_ns != NULL) { + strcpy(abuf, + inet_ntoa(db->d_ns->addr)); + a = abuf; + } else { + a = zones[db->d_zone].z_origin; + } + } + } + break; + } + } + + if (a == ns && ns == na) /* all "UNKNOWN" */ + return (NULL); + + if (*a == '\0') + a = "\".\""; + if (*ns == '\0') + ns = "\".\""; + if (*na == '\0') + na = "\".\""; + + if (NS_OPTION_P(OPTION_HOSTSTATS)) { + static const char fmt[] = " '%s': learnt (A=%s,NS=%s)"; + + buf = newstr(sizeof fmt + strlen(na) + strlen(a) + strlen(ns), + 0); + if (buf == NULL) + return (NULL); + sprintf(buf, fmt, na, a, ns); + } else { + static const char fmt[] = " '%s'"; + + buf = newstr(sizeof fmt + strlen(na), 0); + if (buf == NULL) + return (NULL); + sprintf(buf, fmt, na); + } + + return (buf); +} + +void +ns_resp(u_char *msg, int msglen, struct sockaddr_in from, struct qstream *qsp) { + struct qinfo *qp; + HEADER *hp; + struct qserv *qs; + struct databuf *ns, *ns2; + u_char *cp; + u_char *eom = msg + msglen; + struct flush_set *flushset = NULL; + int flushset_size = 0; + struct sockaddr_in *nsa; + struct databuf *nsp[NSMAX]; + int i, c, n, qdcount, ancount, aucount, nscount, arcount, arfirst; + u_int qtype, qclass; + int restart; /* flag for processing cname response */ + int validanswer, dbflags; + int cname, lastwascname, externalcname; + int count, founddata, foundname; + int buflen; + int newmsglen; + char name[MAXDNAME], qname[MAXDNAME], aname[MAXDNAME]; + char msgbuf[MAXDNAME]; + char *dname, tmpdomain[MAXDNAME]; + const char *fname; + const char *formerrmsg = "brain damage"; + u_char newmsg[PACKETSZ]; + u_char **dpp, *tp; + time_t rtrip; + struct hashbuf *htp; + struct namebuf *np; + struct fwdinfo *fwd; + struct databuf *dp; + int forcecmsg = 0; + char *tname = NULL; + + nameserIncr(from.sin_addr, nssRcvdR); + nsp[0] = NULL; + hp = (HEADER *) msg; + if ((qp = qfindid(hp->id)) == NULL ) { + ns_debug(ns_log_default, 1, "DUP? dropped (id %d)", + ntohs(hp->id)); + nameserIncr(from.sin_addr, nssRcvdDupR); + return; + } + + ns_debug(ns_log_default, 2, "Response (%s %s %s) nsid=%d id=%d", + (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 high level formatting problems by parsing the header. + */ + qdcount = ntohs(hp->qdcount); + ancount = ntohs(hp->ancount); + aucount = ntohs(hp->nscount); /* !!! */ + arcount = ntohs(hp->arcount); + free_addinfo(); /* sets addcount to zero */ + cp = msg + HFIXEDSZ; + dpp = dnptrs; + *dpp++ = msg; + if ((*cp & INDIR_MASK) == 0) + *dpp++ = cp; + *dpp = NULL; + if (qdcount == 1) { + n = dn_expand(msg, eom, cp, qname, sizeof(qname)); + if (n <= 0) { + formerrmsg = expandFailedQuery; + goto formerr; + } + cp += n; + if (cp + 2 * INT16SZ > eom) { + formerrmsg = outofDataQuery; + goto formerr; + } + GETSHORT(qtype, cp); + GETSHORT(qclass, cp); + if (!ns_nameok(qname, qclass, NULL, response_trans, + ns_ownercontext(qtype, response_trans), + qname, from.sin_addr)) { + formerrmsg = badNameFound; + goto refused; + } + if (cp > eom) { + formerrmsg = outofDataQuery; + goto formerr; + } + if (qp->q_msg && qp->q_msglen && + !res_nameinquery(qname, qtype, qclass, + qp->q_msg, qp->q_msg + qp->q_msglen)) { + sprintf(msgbuf, + "query section mismatch (%s %s %s)", + qname, p_class(qclass), p_type(qtype)); + formerrmsg = msgbuf; + goto formerr; + } + if (strcasecmp(qp->q_name, qname) != 0 || + qp->q_class != qclass || + qp->q_type != qtype) { + formerrmsg = wrongQuestion; + goto formerr; + } + } else { + strcpy(qname, qp->q_name); + qclass = qp->q_class; + qtype = qp->q_type; + } + + /* cp now points after the query section. */ + + /* + * 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 (it's sick) + * The server can't understand our query and return FORMERR + * In all these cases, we drop the packet, disable retries on + * this server and immediately force a retry. + */ + if ((hp->rcode != NOERROR && hp->rcode != NXDOMAIN) + || (hp->opcode != QUERY +#ifdef BIND_NOTIFY + && hp->opcode != NS_NOTIFY_OP +#endif + )) { + ns_debug(ns_log_default, 2, + "resp: error (ret %d, op %d), dropped", + hp->rcode, hp->opcode); + switch (hp->rcode) { + case SERVFAIL: + nameserIncr(from.sin_addr, nssRcvdFail); + break; + case FORMERR: + nameserIncr(from.sin_addr, nssRcvdFErr); + break; + default: + nameserIncr(from.sin_addr, nssRcvdErr); + break; + } + /* mark server as bad */ + if (!qp->q_fwd) + for (i = 0; i < (int)qp->q_naddr; i++) + if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, + from.sin_addr)) + qp->q_addr[i].nretry = MAXRETRY; + /* + * XXX: doesn't handle responses sent from the wrong + * interface on a multihomed server. + */ + if (qp->q_fwd || + ina_equal(qp->q_addr[qp->q_curaddr].ns_addr.sin_addr, + from.sin_addr)) + retry(qp); + return; + } + + if (qdcount != 1) { + /* We don't generate or forward these (yet). */ + formerrmsg = notSingleQuery; + goto formerr; + } + + /* + * 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. + * XXX - should put this in STATS somewhere. + */ + for (fwd = server_options->fwdtab; fwd; fwd = fwd->next) + if (ina_equal(fwd->fwdaddr.sin_addr, from.sin_addr)) + 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. + * Workaround: disable recursion on authoritative servers so that + * the ambiguity does not arise. + */ + /* + * If we weren't using a forwarder, find the qinfo pointer and update + * the rtt and fact that we have called on this server before. + */ + if (fwd == NULL) { + struct timeval *stp; + + for (n = 0, qs = qp->q_addr; + (u_int)n < qp->q_naddr; + n++, qs++) + if (ina_equal(qs->ns_addr.sin_addr, from.sin_addr)) + break; + if ((u_int)n >= qp->q_naddr) { + if (!haveComplained(ina_ulong(from.sin_addr), + (u_long)"unexpected source")) { + ns_info(ns_log_default, + "Response from unexpected source (%s)", + sin_ntoa(from)); + } + /* + * 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? + */ + /* 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); + } + + ns_debug(ns_log_default, 3, + "stime %lu/%lu now %lu/%lu rtt %ld", + (u_long)stp->tv_sec, (u_long)stp->tv_usec, + (u_long)tt.tv_sec, (u_long)tt.tv_usec, + (long)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) { + u_long t; + + if (ns->d_nstime == 0) + t = rtrip; + else + t = ns->d_nstime * ALPHA + + + (1 - ALPHA) * rtrip; + if (t > 65535) + t = 65535; + ns->d_nstime = (u_int16_t)t; + } + + /* + * 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; + ns_debug(ns_log_default, 2, + "NS #%d addr %s used, rtt %d", + n, sin_ntoa(qs->ns_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; + (u_int)n < qp->q_naddr; + n++, qs++) { + u_long t; + + 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) + t = (rtrip * BETA); + else + t = ns2->d_nstime * BETA + + + (1 - ALPHA) * rtrip; + } else + t = ns2->d_nstime * GAMMA; + if (t > 65535) + t = 65535; + ns2->d_nstime = (u_int16_t)t; + ns_debug(ns_log_default, 2, "NS #%d %s rtt now %d", n, + sin_ntoa(qs->ns_addr), + ns2->d_nstime); + } + } + +#ifdef BIND_NOTIFY + /* + * For now, NOTIFY isn't defined for ANCOUNT!=0, AUCOUNT!=0, + * or ADCOUNT!=0. Therefore the only real work to be done for + * a NOTIFY-QR is to remove it from the query queue. + */ + if (hp->opcode == NS_NOTIFY_OP) { + qremove(qp); + return; + } +#endif + + /* + * Non-authoritative, no answer, no error, with referral. + */ + if (hp->rcode == NOERROR && !hp->aa && ancount == 0 && aucount > 0 +#ifdef BIND_NOTIFY + && hp->opcode != NS_NOTIFY_OP +#endif + ) { + u_char *tp; + int type, class; +#ifdef DEBUG + if (debug > 0) + fp_nquery(msg, msglen, log_get_stream(packet_channel)); +#endif + /* + * Since there is no answer section (ancount == 0), + * we must be pointing at the authority section (aucount > 0). + */ + tp = cp; + n = dn_expand(msg, eom, tp, name, sizeof name); + if (n < 0) { + formerrmsg = expandFailedAuth; + goto formerr; + } + tp += n; + if (tp + 2 * INT16SZ > eom) { + formerrmsg = outofDataAuth; + goto formerr; + } + GETSHORT(type, tp); + GETSHORT(class, tp); + if (!ns_nameok(name, class, NULL, response_trans, + ns_ownercontext(type, response_trans), + name, from.sin_addr)) { + formerrmsg = badNameFound; + goto refused; + } + + /* + * If the answer delegates us either to the same level in + * the hierarchy or closer to the root, we consider this + * server lame. Note that for now we only log the message + * if the T_NS was C_IN, which is technically wrong (NS is + * visible in all classes) but necessary anyway (non-IN + * classes tend to not have good strong delegation graphs). + */ + + if (type == T_NS && samedomain(qp->q_domain, name)) { + nameserIncr(from.sin_addr, nssRcvdLDel); + /* mark server as bad */ + if (!qp->q_fwd) + for (i = 0; i < (int)qp->q_naddr; i++) + if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, + from.sin_addr)) + qp->q_addr[i].nretry = MAXRETRY; + if (class == C_IN && + !haveComplained(ina_ulong(from.sin_addr), + nhash(qp->q_domain))) { + char *learnt_from = learntFrom(qp, &from); + + ns_info(ns_log_lame_servers, + "Lame server on '%s' (in '%s'?): %s%s", + qname, qp->q_domain, + sin_ntoa(from), + (learnt_from == NULL) ? "" : + learnt_from); + if (learnt_from != NULL) + freestr(learnt_from); + } + + /* XXX - doesn't handle responses sent from the wrong + * interface on a multihomed server + */ + if (qp->q_fwd || + ina_equal(qp->q_addr[qp->q_curaddr].ns_addr.sin_addr, + from.sin_addr)) + retry(qp); + return; + } + } + + if (qp->q_flags & Q_ZSERIAL) { + if (hp->aa && ancount > 0 && hp->rcode == NOERROR && + qtype == T_SOA && ((qclass == C_IN) || (qclass == C_HS))) + { + int n; + u_int type, class, dlen; + u_int32_t serial; + u_char *tp = cp; + u_char *rdatap; + + n = dn_expand(msg, eom, tp, name, sizeof name); + if (n < 0) { + formerrmsg = expandFailedAnswer; + goto formerr; + } + tp += n; /* name */ + if (tp + 3 * INT16SZ + INT32SZ > eom) { + formerrmsg = outofDataAnswer; + goto formerr; + } + GETSHORT(type, tp); /* type */ + GETSHORT(class, tp); /* class */ + tp += INT32SZ; /* ttl */ + GETSHORT(dlen, tp); /* dlen */ + rdatap = tp; /* start of rdata */ + if (!ns_nameok(name, class, NULL, response_trans, + ns_ownercontext(type, response_trans), + name, from.sin_addr)) { + formerrmsg = badNameFound; + goto refused; + } + if (strcasecmp(qname, name) || + qtype != type || + qclass != class) { + sprintf(msgbuf, + "qserial answer mismatch (%s %s %s)", + name, p_class(class), p_type(type)); + formerrmsg = msgbuf; + goto formerr; + } + if (0 >= (n = dn_skipname(tp, eom))) { + formerrmsg = skipnameFailedAnswer; + goto formerr; + } + tp += n; /* mname */ + if (0 >= (n = dn_skipname(tp, eom))) { + formerrmsg = skipnameFailedAnswer; + goto formerr; + } + tp += n; /* rname */ + if (tp + 5 * INT32SZ > eom) { + formerrmsg = dlenUnderrunAnswer; + goto formerr; + } + GETLONG(serial, tp); + tp += 4 * INT32SZ; /* Skip rest of SOA. */ + if ((u_int)(tp - rdatap) != dlen) { + formerrmsg = dlenOverrunAnswer; + goto formerr; + } + + qserial_answer(qp, serial, from); + qremove(qp); + } else { + retry(qp); + } + return; + } + + /* + * Add the info received in the response to the data base. + */ + arfirst = ancount + aucount; + c = arfirst + arcount; + + /* -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; + + if (qp->q_flags & Q_SYSTEM) + dbflags = DB_NOTAUTH | DB_NODATA; + else + dbflags = DB_NOTAUTH | DB_NODATA | DB_NOHINTS; + count = c; + if (qp->q_flags & Q_PRIMING) + dbflags |= DB_PRIMING; + 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 */ + } + + /* retry using tcp provided this was not a tcp query */ + if (!(qp->q_flags & Q_USEVC)) { + qp->q_flags |= Q_USEVC; + unsched(qp); + schedretry(qp, 60); + if (tcp_send(qp) != NOERROR) + /* + * We're probably in trouble if tcp_send + * failed, but we'll try to press on because + * there isn't anything else to do. + */ + retry(qp); + return; + } else if (!qsp) { + /* outstanding udp response */ + return; + } + + /* XXX truncated tcp response */ + ns_error(ns_log_default, + "ns_resp: TCP truncated: \"%s\" %s %s", + qname, p_class(qclass), p_type(qtype)); + /* mark this server as bad */ + if (!qp->q_fwd) + for (i = 0; i < (int)qp->q_naddr; i++) + if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, + from.sin_addr)) + qp->q_addr[i].nretry = MAXRETRY; + + /* try another server, it may have a bigger write buffer */ + retry(qp); + return; + } + + tp = cp; + + restart = 0; + validanswer = 0; + nscount = 0; + cname = 0; + lastwascname = 0; + externalcname = 0; + strcpy(aname, qname); + + if (count) { + /* allocate 1 extra record for end of set detection */ + flushset_size = (count + 1) * sizeof *flushset; + flushset = memget(flushset_size); + if (flushset == NULL) + panic("flushset: out of memory", NULL); + memset(flushset, 0, flushset_size); + } else + flushset = NULL; + + for (i = 0; i < count; i++) { + struct databuf *dp; + int type; + + freestr_maybe(&tname); + if (cp >= eom) { + free_related_additional(); + if (flushset != NULL) + free_flushset(flushset, flushset_size); + formerrmsg = outofDataFinal; + goto formerr; + } + n = rrextract(msg, msglen, cp, &dp, name, sizeof name, from, + &tname); + if (n < 0) { + free_related_additional(); + freestr_maybe(&tname); + if (flushset != NULL) + free_flushset(flushset, flushset_size); + formerrmsg = outofDataFinal; + if (hp->rcode == REFUSED) + goto refused; + else + goto formerr; + } + cp += n; + if (!dp) + continue; + type = dp->d_type; + if (i < ancount) { + /* Answer section. */ + if (externalcname || strcasecmp(name, aname) != 0) { + if (!externalcname) + ns_info(ns_log_resp_checks, + "wrong ans. name (%s != %s)", + name[0] ? name : ".", + aname[0] ? aname : "."); + else + ns_debug(ns_log_resp_checks, 3, + "ignoring answer '%s' after external cname", + name); + db_freedata(dp); + continue; + } + if (type == T_CNAME && + qtype != T_CNAME && qtype != T_ANY) { + strcpy(aname, (char *)dp->d_data); + if (!samedomain(aname, qp->q_domain)) + externalcname = 1; + cname++; + lastwascname = 1; + } else { + validanswer = 1; + lastwascname = 0; + } + + if (tname != NULL) { + add_related_additional(tname); + tname = NULL; + } + + dp->d_cred = (hp->aa && !strcasecmp(name, qname)) + ? DB_C_AUTH + : DB_C_ANSWER; + } else { + /* After answer section. */ + if (lastwascname) { + ns_debug(ns_log_resp_checks, 3, + "last was cname, ignoring auth. and add."); + db_freedata(dp); + break; + } + if (i < arfirst) { + /* Authority section. */ + switch (type) { + case T_NS: + case T_SOA: + if (!samedomain(aname, name)){ + ns_info(ns_log_resp_checks, + "bad referral (%s !< %s)", + aname[0] ? aname : ".", + name[0] ? name : "."); + db_freedata(dp); + continue; + } else if (!samedomain(name, + qp->q_domain)) { + if (!externalcname) + ns_info(ns_log_resp_checks, + "bad referral (%s !< %s)", + name[0] ? name : ".", + qp->q_domain[0] ? + qp->q_domain : "."); + db_freedata(dp); + continue; + } + if (type == T_NS) { + nscount++; + add_related_additional(tname); + tname = NULL; + } + break; + case T_NXT: + /* XXX check */ + break; + case T_SIG: + /* XXX check that it relates to an + NS or SOA or NXT */ + break; + default: + ns_info(ns_log_resp_checks, + "invalid RR type '%s' in authority section (name = '%s') from %s", + p_type(type), name, + sin_ntoa(from)); + db_freedata(dp); + continue; + } + } else { + /* Additional section. */ + switch (type) { + case T_A: + case T_AAAA: + if (externalcname || + !samedomain(name, qp->q_domain)) { + ns_debug(ns_log_resp_checks, 3, + "ignoring additional info '%s' type %s", + name, p_type(type)); + db_freedata(dp); + continue; + } + if (!related_additional(name)) { + ns_info(ns_log_resp_checks, + "unrelated additional info '%s' type %s from %s", + name, p_type(type), + sin_ntoa(from)); + db_freedata(dp); + continue; + } + break; + case T_KEY: + /* XXX check? */ + break; + case T_SIG: + /* + * XXX a SIG RR should relate + * to some other RR in this section, + * although if it's the last RR + * it might be a transaction signature. + */ + break; + default: + ns_info(ns_log_resp_checks, + "invalid RR type '%s' in additional section (name = '%s') from %s", + p_type(type), name, + sin_ntoa(from)); + db_freedata(dp); + continue; + } + } + dp->d_cred = (qp->q_flags & Q_PRIMING) + ? DB_C_ANSWER + : DB_C_ADDITIONAL; + } + rrsetadd(flushset, name, dp); + } + free_related_additional(); + freestr_maybe(&tname); + if (flushset != NULL) { + rrsetupdate(flushset, dbflags, from); + free_flushset(flushset, flushset_size); + } + if (lastwascname && !externalcname) + ns_info(ns_log_cname, "%s (%s)", danglingCname, aname); + + if (cp > eom) { + formerrmsg = outofDataAFinal; + goto formerr; + } + + if ((qp->q_flags & Q_SYSTEM) && ancount) { + if ((qp->q_flags & Q_PRIMING) && !check_root()) { + /* mark server as bad */ + if (!qp->q_fwd) + for (i = 0; i < (int)qp->q_naddr; i++) + if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, + from.sin_addr)) + qp->q_addr[i].nretry = MAXRETRY; + /* XXX - doesn't handle responses sent from + * the wronginterface on a multihomed server + */ + if (qp->q_fwd || + qp->q_addr[qp->q_curaddr].ns_addr.sin_addr.s_addr + == from.sin_addr.s_addr) + retry(qp); + return; + } + ns_debug(ns_log_default, 3, + "resp: leaving, SYSQUERY ancount %d", ancount); +#ifdef BIND_NOTIFY + if (qp->q_notifyzone != DB_Z_CACHE) { + struct zoneinfo *zp = &zones[qp->q_notifyzone]; + + /* Clear this first since sysnotify() might set it. */ + qp->q_notifyzone = DB_Z_CACHE; + sysnotify(zp->z_origin, zp->z_class, ns_t_soa); + } +#endif + qremove(qp); + return; + } + + if (ancount && count && !validanswer) { + /* + * Everything passed validation but we didn't get the + * final answer. The response must have contained + * a dangling CNAME. Force a restart of the query. + * + * Don't set restart if count==0, since this means + * the response was truncated in the answer section, + * causing us to set count to 0 which will cause + * validanswer to be 0 as well even though the answer + * section probably contained valid RRs (just not + * a complete set). + * XXX - this works right if we can just forward this + * response to the client, but not if we found a CNAME + * in a prior response and restarted the query. + */ + restart = 1; + } + + /* + * An answer to a T_ANY query or a successful answer to a + * regular query with no indirection, then just return answer. + */ + if (!restart && ancount && (qtype == T_ANY || !qp->q_cmsglen)) { + ns_debug(ns_log_default, 3, + "resp: got as much answer as there is"); + goto return_msg; + } + + /* + * We might want to cache this negative answer. + * + * if ancount != 0 and rcode == NOERROR we cannot determine if the + * CNAME chain has been processed to completion or not, so just + * restart the query. DNS needs a NODATA return code! + */ + if (((hp->rcode == NXDOMAIN) && (cname == ancount)) || + ((hp->rcode == NOERROR) && (ancount == 0) && (nscount == 0))) + { + cache_n_resp(msg, msglen, from); + + if (!qp->q_cmsglen) { + ns_debug(ns_log_default, 3, + "resp: leaving NO: auth = %d", hp->aa); + goto return_msg; + } + forcecmsg = 1; + } + + /* + * All messages in here need further processing. i.e. they + * are either CNAMEs or we got referred again. + */ + count = 0; + founddata = 0; + dname = name; + /* + * If restart==0 and ancount > 0, we should + * have some valid data because because the data in the answer + * section is owned by the query name and that passes the + * validation test by definition + * + * XXX - the restart stuff doesn't work if any of the answer RRs + * is not cacheable (TTL==0 or unknown RR type), since all of the + * answer must pass through the cache and be re-assembled. + */ + if ((forcecmsg && qp->q_cmsglen) || + ((!restart || !cname) && qp->q_cmsglen && ancount)) { + ns_debug(ns_log_default, 1, "Cname second pass"); + newmsglen = MIN(PACKETSZ, qp->q_cmsglen); + memcpy(newmsg, qp->q_cmsg, newmsglen); + } else { + newmsglen = MIN(PACKETSZ, msglen); + memcpy(newmsg, msg, newmsglen); + } + hp = (HEADER *) newmsg; + hp->ancount = htons(0); + hp->nscount = htons(0); + hp->arcount = htons(0); + hp->rcode = NOERROR; + dnptrs[0] = newmsg; + dnptrs[1] = NULL; + cp = newmsg + HFIXEDSZ; + /* + * Keep in mind that none of this code works when QDCOUNT>1. + * cp ends up pointed just past the query section in both cases. + */ + /* + * Arrange for dname to contain the query name. The query + * name can be either the original query name if restart==0 + * or the target of the last CNAME if we are following a + * CNAME chain and were referred. + */ + n = dn_expand(newmsg, newmsg + newmsglen, cp, dname, sizeof name); + if (n < 0) { + ns_debug(ns_log_default, 1, "dn_expand failed"); + goto servfail; + } + if (!res_dnok(dname)) { + ns_debug(ns_log_default, 1, "bad name (%s)", dname); + goto servfail; + } + cp += n + QFIXEDSZ; + buflen = sizeof(newmsg) - (cp - newmsg); + + cname = 0; + + try_again: + ns_debug(ns_log_default, 1, "resp: nlookup(%s) qtype=%d", dname, + qtype); + foundname = 0; + fname = ""; + htp = hashtab; /* lookup relative to root */ + np = nlookup(dname, &htp, &fname, 0); + ns_debug(ns_log_default, 1, "resp: %s '%s' as '%s' (cname=%d)", + np == NULL ? "missed" : "found", dname, fname, cname); + if (np == NULL || fname != dname) + goto fetch_ns; + + foundname++; + count = cp - newmsg; + /* + * Look for NXDOMAIN record. + */ + for (dp = np->n_data; dp; dp = dp->d_next) { + if (!stale(dp) && (dp->d_rcode == NXDOMAIN) && + (dp->d_class == (int)qclass)) { +#ifdef RETURNSOA + n = finddata(np, qclass, T_SOA, hp, &dname, + &buflen, &count); + if ( n != 0) { + if (count) { + cp += n; + buflen -= n; + newmsglen += n; + hp->nscount = htons((u_int16_t)count); + } + if (hp->rcode == NOERROR_NODATA) { + hp->rcode = NOERROR; + goto return_newmsg; + } + } +#endif + hp->rcode = NXDOMAIN; + /* + * XXX forcing AA all the time isn't right, but + * we have to work that way by default + * for compatibility with older servers. + */ + if (!NS_OPTION_P(OPTION_NONAUTH_NXDOMAIN)) + hp->aa = 1; + ns_debug(ns_log_default, 3, "resp: NXDOMAIN aa = %d", + hp->aa); + goto return_newmsg; + } + } + n = finddata(np, qclass, qtype, hp, &dname, &buflen, &count); + if (n == 0) + goto fetch_ns; /* NO data available */ + if (hp->rcode) { + if (hp->rcode == NOERROR_NODATA) + hp->rcode = NOERROR; +#ifdef RETURNSOA + if (count) { + cp += n; + buflen -= n; + hp->nscount = htons((u_int16_t)count); + } +#endif + goto return_newmsg; + } + cp += n; + buflen -= n; + hp->ancount = htons(ntohs(hp->ancount) + (u_int16_t)count); + if (fname != dname && qtype != T_CNAME && qtype != T_ANY) { + cname++; + goto try_again; + } + founddata = 1; + + ns_debug(ns_log_default, 3, + "resp: foundname=%d, count=%d, founddata=%d, cname=%d", + foundname, count, founddata, cname); + + fetch_ns: + if (hp->tc) + goto return_newmsg; + + /* + * Look for name servers to refer to and fill in the authority + * section or record the address for forwarding the query + * (recursion desired). + */ + free_nsp(nsp); + switch (findns(&np, qclass, nsp, &count, 0)) { + case NXDOMAIN: /* shouldn't happen */ + ns_debug(ns_log_default, 3, "req: leaving (%s, rcode %d)", + dname, hp->rcode); + if (!foundname) + hp->rcode = NXDOMAIN; + if (qclass != C_ANY) { + hp->aa = 1; + if (np && (!foundname || !founddata)) { + 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, &count); + 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) { + ns_debug(ns_log_default, 1, + "resp: MAXQUERIES exceeded (%s %s %s)", + dname, p_class(qclass), p_type(qtype)); + ns_info(ns_log_default, + "MAXQUERIES exceeded, possible data loop in resolving (%s)", + dname); + goto servfail; + } + + /* Reset the query control structure */ + + ns_freeqns(qp, "ns_resp"); + qp->q_naddr = 0; + qp->q_curaddr = 0; + qp->q_fwd = server_options->fwdtab; + + if (qp->q_domain != NULL) + freestr(qp->q_domain); + getname(np, tmpdomain, sizeof tmpdomain); + qp->q_domain = savestr(tmpdomain, 1); + + if ((n = nslookup(nsp, qp, dname, "ns_resp")) <= 0) { + if (n < 0) { + ns_debug(ns_log_default, 3, + "resp: nslookup reports danger"); + if (cname) /* a remote CNAME that does not have data */ + goto return_newmsg; + goto servfail; + } else { + ns_debug(ns_log_default, 3, + "resp: no addrs found for NS's"); + /* + * Timeout while sysquery looks up the NS addresses. + * + * Hopefully we'll have them when the client asks + * again. + * + * too bad we can't just wait for the sysquery + * response to restart this query (it's too hard). + * + * We could try to crawl back up the tree looking + * for reachable servers, but we may have just + * gotten delegated down here by a response with + * no A RRs for the servers. If we blindly tried + * this strategy, we bang on the same server forever. + */ + goto timeout; + } + } + for (n = 0; (u_int)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) { + ns_debug(ns_log_default, 3, + "resp: leaving, MAXCNAMES exceeded"); + goto servfail; + } + ns_debug(ns_log_default, 1, "q_cname = %d", qp->q_cname); + ns_debug(ns_log_default, 3, + "resp: building recursive query; nslookup"); + if (qp->q_cmsg == NULL) { + qp->q_cmsg = qp->q_msg; + qp->q_cmsglen = qp->q_msglen; + qp->q_cmsgsize = qp->q_msgsize; + } else if (qp->q_msg != NULL) + memput(qp->q_msg, qp->q_msgsize); + qp->q_msg = (u_char *)memget(PACKETSZ); + if (qp->q_msg == NULL) { + ns_notice(ns_log_default, "resp: memget error"); + goto servfail; + } + qp->q_msgsize = PACKETSZ; + n = res_mkquery(QUERY, dname, qclass, qtype, + NULL, 0, NULL, qp->q_msg, PACKETSZ); + if (n < 0) { + ns_info(ns_log_default, "resp: res_mkquery(%s) failed", + dname); + goto servfail; + } + if (qp->q_name != NULL) + freestr(qp->q_name); + qp->q_name = savestr(dname, 1); + qp->q_msglen = n; + hp = (HEADER *) qp->q_msg; + hp->rd = 0; + } else + hp = (HEADER *) qp->q_msg; + hp->id = qp->q_nsid = htons(nsid_next()); + if (qp->q_fwd) + hp->rd = 1; + unsched(qp); + schedretry(qp, retrytime(qp)); + nsa = Q_NEXTADDR(qp, 0); + ns_debug(ns_log_default, 1, + "resp: forw -> %s ds=%d nsid=%d id=%d %dms", + sin_ntoa(*nsa), 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_nquery(qp->q_msg, qp->q_msglen, + log_get_stream(packet_channel)); +#endif + if (qp->q_flags & Q_USEVC) { + if (tcp_send(qp) != NOERROR) { + if (!haveComplained(ina_ulong(nsa->sin_addr), + (u_long)tcpsendStr)) + ns_info(ns_log_default, + "ns_forw: tcp_send(%s) failed: %s", + sin_ntoa(*nsa), strerror(errno)); + } + } else if (sendto(ds, (char*)qp->q_msg, qp->q_msglen, 0, + (struct sockaddr *)nsa, + sizeof(struct sockaddr_in)) < 0) + { + if (!haveComplained(ina_ulong(nsa->sin_addr), + (u_long)sendtoStr)) + ns_info(ns_log_default, "ns_resp: sendto(%s): %s", + sin_ntoa(*nsa), strerror(errno)); + 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); + ns_debug(ns_log_default, 3, "resp: Query sent."); + free_nsp(nsp); + return; + + formerr: + if (!haveComplained(ina_ulong(from.sin_addr), (u_long)formerrmsg)) + ns_info(ns_log_resp_checks, "Malformed response from %s (%s)", + sin_ntoa(from), formerrmsg); + free_nsp(nsp); + return; + + return_msg: + nameserIncr(from.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 = (NS_OPTION_P(OPTION_NORECURSE) == 0); + (void) send_msg(msg, msglen, qp); + qremove(qp); + free_nsp(nsp); + return; + + return_newmsg: + nameserIncr(qp->q_from.sin_addr, nssSentAns); + + if (!hp->aa) + nameserIncr(qp->q_from.sin_addr, nssSentNaAns); + if (hp->rcode == NXDOMAIN) + nameserIncr(qp->q_from.sin_addr, nssSentNXD); + n = doaddinfo(hp, cp, buflen); + cp += n; + buflen -= n; + hp->qr = 1; + hp->id = qp->q_id; + hp->rd = 1; + hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); + (void) send_msg(newmsg, cp - newmsg, qp); + qremove(qp); + free_nsp(nsp); + return; + + refused: + hp = (HEADER *)(qp->q_cmsglen ? qp->q_cmsg : qp->q_msg); + hp->rcode = REFUSED; + hp->qr = 1; + hp->id = qp->q_id; + hp->rd = 1; + hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); + (void) send_msg((u_char *)hp, + (qp->q_cmsglen ? qp->q_cmsglen : qp->q_msglen), + qp); + qremove(qp); + free_nsp(nsp); + return; + + servfail: + nameserIncr(qp->q_from.sin_addr, nssSentFail); + hp = (HEADER *)(qp->q_cmsglen ? qp->q_cmsg : qp->q_msg); + hp->rcode = SERVFAIL; + hp->qr = 1; + hp->id = qp->q_id; + hp->rd = 1; + hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); + (void) send_msg((u_char *)hp, + (qp->q_cmsglen ? qp->q_cmsglen : qp->q_msglen), + qp); + qremove(qp); + free_nsp(nsp); + return; + + timeout: + if (qp->q_stream) + sq_remove(qp->q_stream); + qremove(qp); + free_nsp(nsp); + return; +} + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eom) { \ + hp->rcode = FORMERR; \ + return (-1); \ + } \ + } while (0) + +static int +rrextract(u_char *msg, int msglen, u_char *rrp, struct databuf **dpp, + char *dname, int namelen, struct sockaddr_in from, char **tnamep) +{ + u_char *cp, *eom, *rdatap; + u_int class, type, dlen; + int n, n1; + u_int32_t ttl; + u_char *cp1, data[MAXDATA*2]; + HEADER *hp = (HEADER *)msg; + enum context context; + + if (tnamep != NULL) + *tnamep = NULL; + + *dpp = NULL; + cp = rrp; + eom = msg + msglen; + if ((n = dn_expand(msg, eom, cp, dname, namelen)) < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + if (ttl > MAXIMUM_TTL) { + ns_debug(ns_log_default, 5, "%s: converted TTL > %u to 0", + dname, MAXIMUM_TTL); + ttl = 0; + } + GETSHORT(dlen, cp); + BOUNDS_CHECK(cp, dlen); + rdatap = cp; + if (!ns_nameok(dname, class, NULL, response_trans, + ns_ownercontext(type, response_trans), + dname, from.sin_addr)) { + hp->rcode = REFUSED; + return (-1); + } + ns_debug(ns_log_default, 3, + "rrextract: dname %s type %d class %d ttl %d", + dname, type, class, ttl); + /* + * Convert the resource record data into the internal + * database format. + * + * On entry to the switch: + * CP points to the RDATA section of the wire-format RR. + * DLEN is its length. + * The memory area at DATA is available for processing. + * + * On exit from the switch: + * CP has been incremented past the RR. + * CP1 points to the RDATA section of the database-format RR. + * N contains the length of the RDATA section of the dbase-format RR. + * + * The new data at CP1 for length N will be copied into the database, + * so it need not be in any particular storage location. + */ + switch (type) { + case T_A: + if (dlen != INT32SZ) { + hp->rcode = FORMERR; + return (-1); + } + /*FALLTHROUGH*/ + case T_WKS: + case T_HINFO: + case T_TXT: + case T_X25: + case T_ISDN: + case T_NSAP: + case T_AAAA: + case T_LOC: + case T_KEY: + cp1 = cp; + n = dlen; + cp += n; + break; + + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + n = dn_expand(msg, eom, cp, (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + if (!ns_nameok((char *)data, class, NULL, response_trans, + type == T_PTR ?ns_ptrcontext(dname) :domain_ctx, + dname, from.sin_addr)) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 = data; + n = strlen((char *)data) + 1; + if (tnamep != NULL && (type == T_NS || type == T_MB)) + *tnamep = savestr((char *)cp1, 1); + break; + + case T_SOA: + context = hostname_ctx; + goto soa_rp_minfo; + case T_RP: + case T_MINFO: + context = mailname_ctx; + /* FALLTHROUGH */ + soa_rp_minfo: + n = dn_expand(msg, eom, cp, (char *)data, sizeof data); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + if (!ns_nameok((char *)data, class, NULL, response_trans, + context, dname, from.sin_addr)) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + /* + * The next use of 'cp' is dn_expand(), so we don't have + * to BOUNDS_CHECK() here. + */ + cp1 = data + (n = strlen((char *)data) + 1); + n1 = sizeof(data) - n; + if (type == T_SOA) + n1 -= 5 * INT32SZ; + n = dn_expand(msg, eom, cp, (char *)cp1, n1); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + if (type == T_RP) + context = domain_ctx; + else + context = mailname_ctx; + if (!ns_nameok((char *)cp1, class, NULL, response_trans, + context, dname, from.sin_addr)) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 += strlen((char *)cp1) + 1; + if (type == T_SOA) { + n = 5 * INT32SZ; + BOUNDS_CHECK(cp, n); + memcpy(cp1, cp, n); + cp += n; + cp1 += n; + } + n = cp1 - data; + cp1 = data; + break; + + case T_NAPTR: + /* Grab weight and port. */ + BOUNDS_CHECK(cp, INT16SZ*2); + memcpy(data, cp, INT16SZ*2); + cp1 = data + INT16SZ*2; + cp += INT16SZ*2; + + /* Flags */ + BOUNDS_CHECK(cp, 1); + n = *cp++; + BOUNDS_CHECK(cp, n); + *cp1++ = n; + memcpy(cp1, cp, n); + cp += n; cp1 += n; + + /* Service */ + BOUNDS_CHECK(cp, 1); + n = *cp++; + BOUNDS_CHECK(cp, n); + *cp1++ = n; + memcpy(cp1, cp, n); + cp += n; cp1 += n; + + /* Regexp */ + BOUNDS_CHECK(cp, 1); + n = *cp++; + BOUNDS_CHECK(cp, n); + *cp1++ = n; + memcpy(cp1, cp, n); + cp += n; cp1 += n; + + /* Replacement */ + n = dn_expand(msg, eom, cp, (char *)cp1, + sizeof data - (cp1 - data)); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + if (!ns_nameok((char *)cp1, class, NULL, response_trans, + hostname_ctx, dname, from.sin_addr)) { + 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; + + case T_MX: + case T_AFSDB: + case T_RT: + case T_SRV: + /* grab preference */ + BOUNDS_CHECK(cp, INT16SZ); + memcpy(data, cp, INT16SZ); + cp1 = data + INT16SZ; + cp += INT16SZ; + + if (type == T_SRV) { + /* Grab weight and port. */ + BOUNDS_CHECK(cp, INT16SZ*2); + memcpy(cp1, cp, INT16SZ*2); + cp1 += INT16SZ*2; + cp += INT16SZ*2; + } + + /* get name */ + n = dn_expand(msg, eom, cp, (char *)cp1, + sizeof data - (cp1 - data)); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + if (!ns_nameok((char *)cp1, class, NULL, response_trans, + hostname_ctx, dname, from.sin_addr)) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + + if (tnamep != NULL) + *tnamep = savestr((char *)cp1, 1); + + /* compute end of data */ + cp1 += strlen((char *)cp1) + 1; + /* compute size of data */ + n = cp1 - data; + cp1 = data; + break; + + case T_PX: + /* grab preference */ + BOUNDS_CHECK(cp, INT16SZ); + memcpy(data, cp, INT16SZ); + cp1 = data + INT16SZ; + cp += INT16SZ; + + /* get MAP822 name */ + n = dn_expand(msg, eom, cp, (char *)cp1, + sizeof data - INT16SZ); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + if (!ns_nameok((char *)cp1, class, NULL, response_trans, + domain_ctx, dname, from.sin_addr)) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + /* + * The next use of 'cp' is dn_expand(), so we don't have + * to BOUNDS_CHECK() here. + */ + cp1 += (n = strlen((char *)cp1) + 1); + n1 = sizeof(data) - n; + n = dn_expand(msg, eom, cp, (char *)cp1, n1); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + if (!ns_nameok((char *)cp1, class, NULL, response_trans, + domain_ctx, dname, from.sin_addr)) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 += strlen((char *)cp1) + 1; + n = cp1 - data; + cp1 = data; + break; + + case T_SIG: { + u_long origTTL, exptime, signtime, timetilexp, now; + + /* Check signature time, expiration, and adjust TTL. */ + /* This code is similar to that in db_load.c. */ + + /* Skip coveredType, alg, labels */ + BOUNDS_CHECK(cp, INT16SZ + 1 + 1 + 3*INT32SZ); + cp1 = cp + INT16SZ + 1 + 1; + GETLONG(origTTL, cp1); + GETLONG(exptime, cp1); + GETLONG(signtime, cp1); + now = time(NULL); /* Get current time in GMT/UTC */ + + /* Don't let bogus name servers increase the signed TTL */ + if (ttl > origTTL) { + ns_debug(ns_log_default, 3, + "shrinking SIG TTL from %d to origTTL %d", + ttl, origTTL); + ttl = origTTL; + } + + /* Don't let bogus signers "sign" in the future. */ + if (signtime > now) { + ns_debug(ns_log_default, 3, + "ignoring SIG: signature date %s is in the future", + p_secstodate (signtime)); + return ((cp - rrp) + dlen); + } + + /* Ignore received SIG RR's that are already expired. */ + if (exptime <= now) { + ns_debug(ns_log_default, 3, + "ignoring SIG: expiration %s is in the past", + p_secstodate (exptime)); + return ((cp - rrp) + dlen); + } + + /* Lop off the TTL at the expiration time. */ + timetilexp = exptime - now; + if (timetilexp < ttl) { + ns_debug(ns_log_default, 3, + "shrinking expiring %s SIG TTL from %d to %d", + p_secstodate (exptime), ttl, timetilexp); + ttl = timetilexp; + } + + /* The following code is copied from named-xfer.c. */ + cp1 = (u_char *)data; + + /* first just copy over the type_covered, algorithm, */ + /* labels, orig ttl, two timestamps, and the footprint */ + BOUNDS_CHECK(cp, 18); + memcpy(cp1, cp, 18); + cp += 18; + cp1 += 18; + + /* then the signer's name */ + n = dn_expand(msg, eom, cp, (char *)cp1, (sizeof data) - 18); + if (n < 0) { + hp->rcode = FORMERR; + return (-1); + } + cp += n; + cp1 += strlen((char*)cp1)+1; + + /* finally, we copy over the variable-length signature. + Its size is the total data length, minus what we copied. */ + if (18 + (u_int)n > dlen) { + hp->rcode = FORMERR; + return (-1); + } + n = dlen - (18 + n); + if (n > ((int)(sizeof data) - (int)(cp1 - (u_char *)data))) { + hp->rcode = FORMERR; + return (-1); /* out of room! */ + } + memcpy(cp1, cp, n); + cp += n; + cp1 += n; + + /* compute size of data */ + n = cp1 - (u_char *)data; + cp1 = (u_char *)data; + break; + } + + default: + ns_debug(ns_log_default, 3, "unknown type %d", type); + return ((cp - rrp) + dlen); + } + + if (cp > eom) { + hp->rcode = FORMERR; + return (-1); + } + if ((u_int)(cp - rdatap) != dlen) { + ns_debug(ns_log_default, 3, + "encoded rdata length is %u, but actual length was %u", + dlen, (u_int)(cp - rdatap)); + hp->rcode = FORMERR; + return (-1); + } + if (n > MAXDATA) { + ns_debug(ns_log_default, 1, + "update type %d: %d bytes is too much data", + type, n); + hp->rcode = FORMERR; + return (-1); + } + + ttl += tt.tv_sec; + *dpp = savedata(class, type, ttl, cp1, n); + return (cp - rrp); +} + +int +send_msg(u_char *msg, int msglen, struct qinfo *qp) { + if (qp->q_flags & Q_SYSTEM) + return (1); + if (!qp->q_stream && (msglen > PACKETSZ)) + msglen = trunc_adjust(msg, msglen, PACKETSZ); + ns_debug(ns_log_default, 1, "send_msg -> %s (%s %d) id=%d", + sin_ntoa(qp->q_from), + qp->q_stream == NULL ? "UDP" : "TCP", + qp->q_stream == NULL ? qp->q_dfd : qp->q_stream->s_rfd, + ntohs(qp->q_id)); +#ifdef DEBUG + if (debug > 4) { + struct qinfo *tqp; + + for (tqp = nsqhead; tqp != NULL; tqp = tqp->q_link) { + ns_debug(ns_log_default, 4, + "qp %#lx q_id: %d q_nsid: %d q_msglen: %d", + (u_long)tqp, tqp->q_id, + tqp->q_nsid, tqp->q_msglen); + ns_debug(ns_log_default, 4, + "\tq_naddr: %d q_curaddr: %d", + tqp->q_naddr, tqp->q_curaddr); + ns_debug(ns_log_default, 4, + "\tq_next: %#lx q_link: %#lx", + (u_long)qp->q_next, (u_long)qp->q_link); + } + } + if (debug >= 6) + fp_nquery(msg, msglen, log_get_stream(packet_channel)); +#endif /* DEBUG */ + if (qp->q_stream == NULL) { + if (sendto(qp->q_dfd, (char*)msg, msglen, 0, + (struct sockaddr *)&qp->q_from, + sizeof(qp->q_from)) < 0) { + if (!haveComplained(ina_ulong(qp->q_from.sin_addr), + (u_long)sendtoStr)) +#if defined(SPURIOUS_ECONNREFUSED) + if (errno != ECONNREFUSED) +#endif + ns_info(ns_log_default, + "send_msg: sendto(%s): %s", + sin_ntoa(qp->q_from), + strerror(errno)); + nameserIncr(qp->q_from.sin_addr, nssSendtoErr); + return (1); + } + } else + writestream(qp->q_stream, (u_char*)msg, msglen); + 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; + struct qinfo *oqp; +{ + char dname[MAXDNAME]; + + 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; + ns_debug(ns_log_default, 2, "prime: %s", dname); + (void) sysquery(dname, class, type, NULL, 0, QUERY); +} +#endif + +void +prime_cache() { + struct qinfo *qp; + + /* + * XXX - should this always be skipped if OPTION_FORWARD_ONLY + * or should it be another option? What about when we are + * doing selective forwarding? + */ + if (!NS_OPTION_P(OPTION_FORWARD_ONLY)) { + ns_debug(ns_log_default, 1, "prime_cache: priming = %d", + priming); + if (!priming && fcachetab->h_tab[0] != NULL) { + priming++; + if (!(qp = sysquery("", C_IN, T_NS, NULL, 0, QUERY))) + priming = 0; + else + qp->q_flags |= (Q_SYSTEM | Q_PRIMING); + } + } + needs_prime_cache = 0; + return; +} + +#ifdef BIND_NOTIFY +/* + * sysnotify(dname, class, type) + * cause a NOTIFY request to be sysquery()'d to each secondary server + * of the zone that "dname" is within. + */ +void +sysnotify(const char *dname, int class, int type) { + const char *zname, *fname; + int nns, na, zn, n; + struct zoneinfo *zp; + struct hashbuf *htp; + struct namebuf *np; + struct databuf *dp; + + ns_debug(ns_log_notify, 3, "sysnotify(%s, %s, %s)", + dname, p_class(class), p_type(type)); + htp = hashtab; + np = nlookup(dname, &htp, &fname, 0); + if (np == NULL) { + ns_warning(ns_log_notify, "sysnotify: can't find \"%s\"", + dname); + return; + } + zn = findMyZone(np, class); + if (zn == DB_Z_CACHE) { + ns_warning(ns_log_notify, "sysnotify: not auth for zone %s", + dname); + return; + } + zp = &zones[zn]; + if (zp->z_notify == znotify_no || + (zp->z_notify == znotify_use_default && + NS_OPTION_P(OPTION_NONOTIFY))) + return; + if (zp->z_type != z_master && zp->z_type != z_slave) { + ns_warning(ns_log_notify, "sysnotify: %s not master or slave", + dname); + return; + } + zname = zp->z_origin; + nns = na = 0; + if (zp->z_type == z_master) + sysnotify_slaves(dname, zname, class, zn, &nns, &na); + if (zp->z_notify_count != 0) { + struct in_addr *also_addr = zp->z_also_notify; + int i; + + for (i = 0; i < zp->z_notify_count; i++) { + sysquery(dname, class, T_SOA, also_addr, 1, + NS_NOTIFY_OP); + also_addr++; + } + nns += zp->z_notify_count; + na += zp->z_notify_count; + } + if (nns != 0 || na != 0) + ns_info(ns_log_notify, + "Sent NOTIFY for \"%s %s %s\" (%s); %d NS, %d A", + dname, p_class(class), p_type(type), zname, nns, na); +} + +static void +sysnotify_slaves(const char *dname, const char *zname, int class, int zn, + int *nns, int *na) +{ + const char *mname, *fname; + struct hashbuf *htp; + struct namebuf *np; + struct databuf *dp; + + /* + * Master. + */ + htp = hashtab; + np = nlookup(zname, &htp, &fname, 0); + if (!np) { + ns_warning(ns_log_notify, + "sysnotify: found name \"%s\" but not zone", + dname); + return; + } + mname = NULL; + for (dp = np->n_data; dp; dp = dp->d_next) { + if (dp->d_zone == DB_Z_CACHE || !match(dp, class, T_SOA)) + continue; + if (mname) { + ns_notice(ns_log_notify, + "multiple SOA's for zone \"%s\"?", + zname); + return; + } + mname = (char *) dp->d_data; + } + if (mname == NULL) { + ns_notice(ns_log_notify, "no SOA found for zone \"%s\"", + zname); + return; + } + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (dp->d_zone == DB_Z_CACHE || !match(dp, class, T_NS)) + continue; + if (strcasecmp((char*)dp->d_data, mname) == 0) + continue; + sysnotify_ns(dname, (char *)dp->d_data, class, zn, nns, na); + } +} + +static void +sysnotify_ns(const char *dname, const char *aname, + int class, int zn, int *nns, int *na) +{ + struct databuf *adp; + struct namebuf *anp; + const char *fname; + struct in_addr nss[NSMAX]; + struct hashbuf *htp; + int is_us, nsc; + + htp = hashtab; + anp = nlookup(aname, &htp, &fname, 0); + nsc = 0; + is_us = 0; + if (anp != NULL) + for (adp = anp->n_data; adp; adp = adp->d_next) { + struct in_addr ina; + + if (!match(adp, class, T_A)) + continue; + ina = ina_get(adp->d_data); + if (aIsUs(ina)) { + is_us = 1; + continue; + } + if (nsc < NSMAX) + nss[nsc++] = ina; + } /*next A*/ + if (nsc == 0) { + if (!is_us) { + struct qinfo *qp; + + qp = sysquery(aname, class, T_A, 0, 0, QUERY); + if (qp != NULL) + qp->q_notifyzone = zn; + } + return; + } + (void) sysquery(dname, class, T_SOA, nss, nsc, NS_NOTIFY_OP); + (*nns)++; + *na += nsc; +} +#endif /*BIND_NOTIFY*/ + +struct qinfo * +sysquery(const char *dname, int class, int type, + struct in_addr *nss, int nsc, int opcode) +{ + struct qinfo *qp, *oqp; + HEADER *hp; + char tmpdomain[MAXDNAME]; + struct namebuf *np; + struct databuf *nsp[NSMAX]; + struct hashbuf *htp; + struct sockaddr_in *nsa; + const char *fname; + int n, count; + + nsp[0] = NULL; + ns_debug(ns_log_default, 3, "sysquery(%s, %d, %d, %#x, %d)", + dname, class, type, nss, nsc); + qp = qnew(dname, class, type); + + if (nss && nsc) + np = NULL; + else { + htp = hashtab; + if (priming && dname[0] == '\0') { + np = NULL; + } else if ((np = nlookup(dname, &htp, &fname, 1)) == NULL) { + ns_info(ns_log_default, + "sysquery: nlookup error on %s?", + dname); + err1: + ns_freeqry(qp); + return (NULL); + } + + n = findns(&np, class, nsp, &count, 0); + switch (n) { + case NXDOMAIN: + case SERVFAIL: + ns_info(ns_log_default, + "sysquery: findns error (%s) on %s?", + n == NXDOMAIN ? "NXDOMAIN" : "SERVFAIL", + dname); + err2: + free_nsp(nsp); + goto err1; + } + } + + /* 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 = server_options->fwdtab; + qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; + qp->q_flags |= Q_SYSTEM; + + getname(np, tmpdomain, sizeof tmpdomain); + qp->q_domain = savestr(tmpdomain, 1); + + if ((qp->q_msg = (u_char *)memget(PACKETSZ)) == NULL) { + ns_notice(ns_log_default, "sysquery: memget failed"); + goto err2; + } + qp->q_msgsize = PACKETSZ; + n = res_mkquery(opcode, dname, class, + type, NULL, 0, NULL, + qp->q_msg, PACKETSZ); + if (n < 0) { + ns_info(ns_log_default, + "sysquery: res_mkquery(%s) failed", dname); + goto err2; + } + qp->q_msglen = n; + hp = (HEADER *) qp->q_msg; + hp->id = qp->q_nsid = htons(nsid_next()); + hp->rd = (qp->q_fwd ? 1 : 0); + + /* First check for an already pending query for this data */ + for (oqp = nsqhead; oqp != NULL; oqp = oqp->q_link) { + if ((oqp != qp) + && (oqp->q_msglen == qp->q_msglen) + && memcmp(oqp->q_msg+2, qp->q_msg + 2, + qp->q_msglen - 2) == 0 + ) { +#ifdef BIND_NOTIFY + /* XXX - need fancier test to suppress duplicate + * NOTIFYs to the same server (compare nss?) + */ + if (opcode != NS_NOTIFY_OP) +#endif /*BIND_NOTIFY*/ + { + ns_debug(ns_log_default, 3, + "sysquery: duplicate"); + goto err2; + } + } + } + + 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 { + fetch_a: + count = nslookup(nsp, qp, dname, "sysquery"); + if (count <= 0) { + if (count < 0) { + ns_info(ns_log_default, + "sysquery: nslookup reports danger (%s)", + dname); + goto err2; + } else if (np && NAME(*np)[0] == '\0') { + /* + * It's not too serious if we don't have + * the root server addresses if we have to + * go through a forwarder anyway. Don't + * bother to log it, since prime_cache() + * won't do anything about it as currently + * implemented. + * + * XXX - should we skip setting + * needs_prime_cache as well? + * + * XXX - what happens when we implement + * selective forwarding? + */ + if (!NS_OPTION_P(OPTION_FORWARD_ONLY)) + ns_warning(ns_log_default, + "sysquery: no addrs found for root NS (%s)", + dname); + if (class == C_IN && !priming) + needs_prime_cache = 1; + goto err2; + } + if (np) { + free_nsp(nsp); + nsp[0] = NULL; + np = np_parent(np); + n = findns(&np, class, nsp, &count, 0); + switch (n) { + case NXDOMAIN: /*FALLTHROUGH*/ + case SERVFAIL: + ns_info(ns_log_default, + "sysquery: findns error (%d) on %s?", + n, dname); + goto err2; + } + goto fetch_a; + } + goto err2; + } + } + + schedretry(qp, retrytime(qp)); + if (qp->q_fwd == NULL) + qp->q_addr[0].stime = tt; /* XXX - why not every? */ + nsa = Q_NEXTADDR(qp, 0); + + ns_debug(ns_log_default, 1, + "sysquery: send -> %s dfd=%d nsid=%d id=%d retry=%ld", + sin_ntoa(*nsa), qp->q_dfd, + ntohs(qp->q_nsid), ntohs(qp->q_id), + (long)qp->q_time); +#ifdef DEBUG + if (debug >= 10) + fp_nquery(qp->q_msg, qp->q_msglen, + log_get_stream(packet_channel)); +#endif + if (sendto(qp->q_dfd, (char*)qp->q_msg, qp->q_msglen, 0, + (struct sockaddr *)nsa, + sizeof(struct sockaddr_in)) < 0) { + if (!haveComplained(ina_ulong(nsa->sin_addr), + (u_long)sendtoStr)) + ns_info(ns_log_default, "sysquery: sendto(%s): %s", + sin_ntoa(*nsa), strerror(errno)); + nameserIncr(nsa->sin_addr, nssSendtoErr); + } + nameserIncr(nsa->sin_addr, nssSentSysQ); + free_nsp(nsp); + return (qp); +} + +/* + * Check the list of root servers after receiving a response + * to a query for the root servers. + */ +static int +check_root() { + struct databuf *dp, *pdp; + struct namebuf *np; + int count = 0; + + priming = 0; + for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) + if (NAME(*np)[0] == '\0') + break; + if (np == NULL) { + ns_notice(ns_log_default, "check_root: Can't find root!"); + return (0); + } + for (dp = np->n_data; dp != NULL; dp = dp->d_next) + if (dp->d_type == T_NS) + count++; + ns_debug(ns_log_default, 1, "%d root servers", count); + if (count < MINROOTS) { + ns_notice(ns_log_default, + "check_root: %d root servers after query to root server < min", + count); + return (0); + } + pdp = NULL; + dp = np->n_data; + while (dp != NULL) { + if (dp->d_type == T_NS && dp->d_zone == DB_Z_CACHE && + dp->d_ttl < (u_int32_t)tt.tv_sec) { + ns_debug(ns_log_default, 1, + "deleting old root server '%s'", + dp->d_data); + dp = rm_datum(dp, np, pdp, NULL); + /* SHOULD DELETE FROM HINTS ALSO */ + continue; + } + pdp = dp; + dp = dp->d_next; + } + if (check_ns()) + return (1); + else { + priming = 1; + return (0); + } +} + +/* + * Check the root to make sure that for each NS record we have a A RR + */ +static int +check_ns() { + struct databuf *dp, *tdp; + struct namebuf *np, *tnp; + struct hashbuf *htp; + char *dname; + int found_arr; + const char *fname; + time_t curtime; + int servers = 0, rrsets = 0; + + ns_debug(ns_log_default, 2, "check_ns()"); + + curtime = (u_int32_t) tt.tv_sec; + for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) { + if (NAME(*np)[0] != '\0') + continue; + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + int cnames = 0; + + if (dp->d_rcode) + continue; + + if (dp->d_type != T_NS) + continue; + + servers++; + + /* look for A records */ + dname = (caddr_t) dp->d_data; + htp = hashtab; + tnp = nlookup(dname, &htp, &fname, 0); + if (tnp == NULL || fname != dname) { + ns_debug(ns_log_default, 3, + "check_ns: %s: not found %s %#lx", + dname, fname, (u_long)tnp); + sysquery(dname, dp->d_class, T_A, NULL, + 0, QUERY); + continue; + } + /* look for name server addresses */ + found_arr = 0; + delete_stale(tnp); + for (tdp = tnp->n_data; + tdp != NULL; + tdp = tdp->d_next) { + if (tdp->d_rcode) + continue; + if (tdp->d_type == T_CNAME) + cnames++; + if (tdp->d_type != T_A || + tdp->d_class != dp->d_class) + continue; + if ((tdp->d_zone == DB_Z_CACHE) && + (tdp->d_ttl < (u_int32_t)curtime)) { + ns_debug(ns_log_default, 3, + "check_ns: stale entry '%s'", + NAME(*tnp)); + found_arr = 0; + break; + } + found_arr++; + } + if (found_arr) + rrsets++; + else if (cnames > 0) + ns_info(ns_log_default, + "Root NS %s -> CNAME %s", + NAME(*np), NAME(*tnp)); + else + sysquery(dname, dp->d_class, T_A, NULL, + 0, QUERY); + } + } + + ns_debug(ns_log_default, 2, "check_ns: %d %d", servers, rrsets); + return ((servers <= 2) + ? (rrsets == servers) + : ((rrsets * 2) >= servers) + ); +} + +/* int findns(npp, class, nsp, countp, flag) + * Find NS's or an SOA + * npp, class: + * dname whose most enclosing 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} + * *countp is bogus, but nsp[] has a single SOA returned in it. + * SERVFAIL: we are auth but zone isn't loaded; or, no root servers found + * *countp and nsp[] are bogus. + * OK: we are not authoritative, and here are the NS records we found. + * *countp and nsp[] return NS records of interest. + */ +int +findns(struct namebuf **npp, int class, + struct databuf **nsp, int *countp, int flag) +{ + struct namebuf *np = *npp; + struct databuf *dp; + struct databuf **nspp; + struct hashbuf *htp; + + nsp[0] = NULL; + + if (priming && (np == NULL || NAME(*np)[0] == '\0')) + htp = fcachetab; + else + htp = hashtab; + + try_again: + if (htp == fcachetab && class == C_IN && !priming) + /* + * XXX - do we want to set needs_prime_cache if + * OPTION_FORWARD_ONLY? + */ + needs_prime_cache = 1; + if (np == NULL) { + /* find the root */ + for (np = htp->h_tab[0]; np != NULL; np = np->n_next) + if (NAME(*np)[0] == '\0') + break; + } + while (np != NULL) { + ns_debug(ns_log_default, 5, "findns: np %#x '%s'", np, + NAME(*np)); + /* 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 != DB_Z_CACHE && + ((zones[dp->d_zone].z_type == Z_PRIMARY) || + (zones[dp->d_zone].z_type == Z_SECONDARY)) && + match(dp, class, T_SOA)) { + ns_debug(ns_log_default, 3, + "findns: SOA found"); + if (zones[dp->d_zone].z_flags & Z_AUTH) { + *npp = np; + nsp[0] = dp; + nsp[1] = NULL; + DRCNTINC(dp); + 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; + delete_stale(np); + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!match(dp, class, T_NS)) + continue; + if (dp->d_rcode) + 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 == DB_Z_CACHE) && + (dp->d_ttl < (u_int32_t)tt.tv_sec) && + !(dp->d_flags & DB_F_HINT)) { + ns_debug(ns_log_default, 1, + "findns: stale entry '%s'", + NAME(*np)); + /* + * We may have already added NS databufs + * and are going to throw them away. Fix + * reference counts. We don't need to free + * them here as we just got them from the + * cache. + */ + while (nspp > &nsp[0]) { + nspp--; + DRCNTDEC(*nspp); + } + nsp[0] = NULL; + goto try_parent; + } + if (nspp < &nsp[NSMAX-1]) { + *nspp++ = dp; + DRCNTINC(dp); + } + } + + *countp = nspp - nsp; + if (*countp > 0) { + ns_debug(ns_log_default, 3, + "findns: %d NS's added for '%s'", + *countp, NAME(*np)); + *nspp = NULL; + *npp = np; + return (OK); /* Success, got some NS's */ + } + try_parent: + np = np_parent(np); + } + if (htp == hashtab) { + htp = fcachetab; + goto try_again; + } + ns_debug(ns_log_default, 1, + "findns: No root nameservers for class %s?", p_class(class)); + if ((unsigned)class < MAXCLASS && norootlogged[class] == 0) { + norootlogged[class] = 1; + ns_info(ns_log_default, "No root nameservers for class %s", + 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(struct namebuf *np, int class, int type, + HEADER *hp, char **dnamep, int *lenp, int *countp) +{ + struct databuf *dp; + char *cp; + int buflen, n, count = 0; + + delete_stale(np); + +#ifdef ROUND_ROBIN + if (type != T_ANY && type != T_PTR) { + /* cycle order of RRs, for a load balancing effect... */ + + struct databuf **dpp; + + for (dpp = &np->n_data; (dp = *dpp) != NULL; + dpp = &dp->d_next) { + if (dp->d_next && wanted(dp, class, type)) { + 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; +#ifdef DEBUG + if (buflen > PACKETSZ) + ns_debug(ns_log_default, 1, "finddata(): buflen=%d", buflen); +#endif + cp = ((char *)hp) + *countp; + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!wanted(dp, class, type)) { + if (type == T_CNAME && class == dp->d_class) { + /* any data means no CNAME exists */ + *countp = 0; + return 0; + } + continue; + } + if (dp->d_cred == DB_C_ADDITIONAL) { +#ifdef NOADDITIONAL + continue; +#else + /* 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 earlier when this datum is + * used often. + */ + dp->d_ttl = tt.tv_sec + + + 0.95 * (int) (dp->d_ttl - tt.tv_sec); +#endif + } + /* -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... + */ + ns_info(ns_log_default, + "NODATA & data for \"%s\" type %d class %d", + *dnamep, type, class); + continue; + } + if (type == T_ANY) + continue; + hp->rcode = NOERROR_NODATA; + if (dp->d_size == 0) { /* !RETURNSOA */ + *countp = 0; + return 1; /* XXX - we have to report success */ + } + } + if (dp->d_rcode == NXDOMAIN) { + if (count != 0) { + /* + * This should not happen, yet it might... + */ + ns_info(ns_log_default, + "NXDOMAIN & data for \"%s\" type %d class %d", + *dnamep, type, class); + continue; + } + hp->rcode = NXDOMAIN; + if (dp->d_size == 0) { /* !RETURNSOA */ + *countp = 0; + return 1; /* XXX - we have to report success */ + } + } + + /* Don't put anything but key or sig RR's in response to + requests for key or sig */ + if (((type == T_SIG) || (type == T_KEY)) && + (!((dp->d_type == T_SIG) || (dp->d_type == T_KEY))) ) + continue; + + if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, + dnptrs, dnptrs_end)) < 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 != DB_Z_CACHE && + (zones[dp->d_zone].z_flags & Z_AUTH) != 0 && + class != C_ANY) + hp->aa = 1; /* XXX */ +#endif + if (dp->d_type == T_CNAME) { + /* don't alias if querying for key, sig, nxt, or any */ + if ((type != T_KEY) && + (type != T_SIG) && + (type != T_NXT) && + (type != T_ANY)) { /* or T_NS? */ + *dnamep = (caddr_t) dp->d_data; + } + break; + } + } + /* + * Cache invalidate the other RR's of same type + * if some have timed out + */ + ns_debug(ns_log_default, 3, "finddata: added %d class %d type %d RRs", + count, class, type); + *countp = count; + return (*lenp - buflen); +} + +/* + * Do we want this data record based on the class and type? + * (We always return found unexpired SIG RR's that cover the wanted rrtype.) + */ +int +wanted(const struct databuf *dp, int class, int type) { + const u_char *cp; + int coveredType; + time_t expiration; +#ifdef DEBUG + char pclass[15], ptype[15]; +#endif + +#ifdef DEBUG + strcpy(pclass, p_class(class)); + strcpy(ptype, p_type(type)); + ns_debug(ns_log_default, 3, "wanted(%#x, %s %s) [%s %s]", + dp, pclass, ptype, + p_class(dp->d_class), p_type(dp->d_type)); +#endif + + if (dp->d_class != class && class != C_ANY) + return (0); + /* + * Must check SIG for expiration below, other matches + * return OK here. + */ + if (type == dp->d_type && (type != T_SIG)) + return (1); + /* 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); + + /* First, look at the type of RR. */ + switch (dp->d_type) { + + /* Cases to deal with: + T_ANY search, return all unexpired SIGs. + T_SIG search, return all unexpired SIGs. + T_<foo> search, return all unexp SIG <FOO>s. + */ + case T_SIG: + cp = dp->d_data; + GETSHORT(coveredType, cp); + cp += INT16SZ + INT32SZ; /* skip alg, labels, & orig TTL */ + GETLONG(expiration,cp); + + if (type == T_ANY || type == T_SIG || type == coveredType) { + if (expiration > time(0)) + return (1); /* Unexpired matching SIG */ + } + return (0); /* We don't return this SIG. */ + + case T_ANY: + return (1); + case T_CNAME: + if (dp->d_rcode != NOERROR_NODATA) + return (1); + else + break; + } + /* OK, now look at the type of query. */ + 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 != DB_Z_CACHE + && (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 occured. Typically you are + * adding NS records to a response. + */ +int +add_data(struct namebuf *np, struct databuf **dpp, + u_char *cp, int buflen, int *countp) +{ + struct databuf *dp; + char dname[MAXDNAME]; + int n, bytes; + + bytes = *countp = 0; + getname(np, dname, sizeof(dname)); + for (dp = *dpp++; dp != NULL; dp = *dpp++) { + if (stale(dp)) + continue; /* ignore old cache entry */ + if (dp->d_rcode) + continue; + if ((n = make_rr(dname, dp, cp, buflen, 1, + dnptrs, dnptrs_end)) < 0) + return (-bytes); /* Truncation */ + cp += n; + buflen -= n; + bytes += n; + (*countp)++; + } + return (bytes); +} + +static void +rrsetadd(struct flush_set *flushset, const char *name, struct databuf *dp) { + struct flush_set *fs = flushset; + struct db_list *dbl; + + while (fs->fs_name && ( + strcasecmp(fs->fs_name,name) || + (fs->fs_class != dp->d_class) || + (fs->fs_type != dp->d_type) || + (fs->fs_cred != dp->d_cred))) { + fs++; + } + if (!fs->fs_name) { + fs->fs_name = savestr(name, 1); + fs->fs_class = dp->d_class; + fs->fs_type = dp->d_type; + fs->fs_cred = dp->d_cred; + fs->fs_list = NULL; + fs->fs_last = NULL; + } + dbl = (struct db_list *)memget(sizeof(struct db_list)); + if (!dbl) + panic("rrsetadd: out of memory", NULL); + dbl->db_next = NULL; + dbl->db_dp = dp; + if (fs->fs_last == NULL) + fs->fs_list = dbl; + else + fs->fs_last->db_next = dbl; + fs->fs_last = dbl; +} + +static int +ttlcheck(const char *name, struct db_list *dbl, int update) { + int type = dbl->db_dp->d_type; + int class = dbl->db_dp->d_class; + struct hashbuf *htp = hashtab; + const char *fname; + struct namebuf *np; + struct db_list *dbp = dbl; + struct databuf *dp; + u_int32_t ttl = 0; /* Make gcc happy. */ + int first; + + + np = nlookup(name, &htp, &fname, 0); + if (np == NULL || fname != name || ns_wildcard(NAME(*np))) + return (1); + + /* check that all the ttl's we have are the same, if not return 1 */ + first = 1; + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!match(dp, class, type)) + continue; + if (first) { + /* we can't update zone data so return early */ + if (dp->d_zone != DB_Z_CACHE) + return (0); + ttl = dp->d_ttl; + first = 0; + } else if (ttl != dp->d_ttl) + return (1); + } + + /* there are no records of this type in the cache */ + if (first) + return(1); + + /* + * the ttls of all records we have in the cache are the same + * if the ttls differ in the new set we don't want it. + */ + + /* check that all the ttl's we have are the same, if not return 0 */ + first = 1; + while (dbp) { + if (first) { + ttl = dbp->db_dp->d_ttl; + first = 0; + } else if (ttl != dbp->db_dp->d_ttl) { + return(0); + } + dbp = dbp->db_next; + } + + /* update ttl if required */ + if (update) { + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!match(dp, class, type)) + continue; + if (dp->d_ttl > ttl) + break; + dp->d_ttl = ttl; + fixttl(dp); + } + } + + return(1); +} + +static int +rrsetcmp(name, dbl) + char *name; + struct db_list *dbl; +{ + int type = dbl->db_dp->d_type; + int class = dbl->db_dp->d_class; + struct hashbuf *htp = hashtab; + const char *fname; + struct namebuf *np; + struct db_list *dbp = dbl; + struct databuf *dp; + int exists = 0; + + + np = nlookup(name, &htp, &fname, 0); + if (np == NULL || fname != name || ns_wildcard(NAME(*np))) { + ns_debug(ns_log_default, 3, "rrsetcmp: name not in database"); + return (-1); + } + + /* check that all entries in dbl are in the cache */ + while (dbp) { + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (match(dp, class, type)) + exists++; + if (!db_cmp(dp, dbp->db_dp) +#ifdef NOADDITIONAL + && ((dp->d_cred == dbp->db_dp->d_cred) || + (dp->d_cred != DB_C_ADDITIONAL)) +#endif + ) + break; + } + if (!dp) { + ns_debug(ns_log_default, 3, + "rrsetcmp: %srecord%s in database", + exists ? "" : "no ", exists ? " not" : "s"); + return (exists ? 1 : -1); + } + dbp = dbp->db_next; + } + + /* Check that all cache entries are in the list. */ + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (!match(dp, class, type)) + continue; +#ifdef NCACHE + if (dp->d_rcode) + return (1); +#endif + dbp = dbl; + while (dbp) { + if (!db_cmp(dp, dbp->db_dp)) + break; + dbp = dbp->db_next; + } + if (!dbp) { + ns_debug(ns_log_default, 3, + "rrsetcmp: record not in rrset"); + return (1); + } + } + ns_debug(ns_log_default, 3, "rrsetcmp: rrsets matched"); + return (0); +} + +static void +rrsetupdate(struct flush_set * flushset, int flags, struct sockaddr_in from) { + struct flush_set *fs = flushset; + struct db_list *dbp, *odbp; + int n; + + while (fs->fs_name) { + ns_debug(ns_log_default, 2, "rrsetupdate: %s", + fs->fs_name[0] ? fs->fs_name : "."); + if ((n = rrsetcmp(fs->fs_name, fs->fs_list)) && + ttlcheck(fs->fs_name, fs->fs_list, 0)) { + if (n > 0) + flushrrset(fs, from); + + dbp = fs->fs_list; + while (dbp) { + n = db_update(fs->fs_name, dbp->db_dp, + dbp->db_dp, NULL, flags, + hashtab, from); + ns_debug(ns_log_default, 3, + "rrsetupdate: %s %d", + fs->fs_name[0] ? fs->fs_name : ".", + n); + if (n != OK) + db_freedata(dbp->db_dp); + odbp = dbp; + dbp = dbp->db_next; + memput(odbp, sizeof *odbp); + } + } else { + if (n == 0) + (void)ttlcheck(fs->fs_name,fs->fs_list, 1); + dbp = fs->fs_list; + while (dbp) { + db_freedata(dbp->db_dp); + odbp = dbp; + dbp = dbp->db_next; + memput(odbp, sizeof *odbp); + } + } + fs->fs_list = NULL; + fs++; + } +} + +static void +flushrrset(struct flush_set * fs, struct sockaddr_in from) { + struct databuf *dp; + int n; + + ns_debug(ns_log_default, 2, "flushrrset(%s, %s, %s, %d)", + fs->fs_name[0]?fs->fs_name:".", p_type(fs->fs_type), + p_class(fs->fs_class), fs->fs_cred); + dp = savedata(fs->fs_class, fs->fs_type, 0, NULL, 0); + dp->d_zone = DB_Z_CACHE; + dp->d_cred = fs->fs_cred; + dp->d_clev = 0; + do { + n = db_update(fs->fs_name, dp, NULL, NULL, DB_DELETE, hashtab, + from); + ns_debug(ns_log_default, 3, "flushrrset: %d", n); + } while (n == OK); + db_freedata(dp); +} + +static void +free_flushset(struct flush_set *flushset, int flushset_size) { + struct flush_set *fs; + + for (fs = flushset; fs->fs_name != NULL; fs++) + freestr(fs->fs_name); + memput(flushset, flushset_size); +} + +/* + * This is best thought of as a "cache invalidate" function. + * It is called whenever a piece of data is determined to have + * become invalid either through a timeout or a validation + * failure. It is better to have no information, than to + * have partial information you pass off as complete. + */ +void +delete_all(struct namebuf *np, int class, int type) { + struct databuf *dp, *pdp; + + ns_debug(ns_log_default, 3, "delete_all(%#x:\"%s\" %s %s)", + np, NAME(*np), p_class(class), p_type(type)); + pdp = NULL; + dp = np->n_data; + while (dp != NULL) { + if (dp->d_zone == DB_Z_CACHE && (dp->d_flags & DB_F_HINT) == 0 + && match(dp, class, type)) { + dp = rm_datum(dp, np, pdp, NULL); + continue; + } + pdp = dp; + dp = dp->d_next; + } +} + +/* delete_stale(np) + * for all RRs associated with this name, check for staleness (& delete) + * arguments: + * np = pointer to namebuf to be cleaned. + * returns: + * void. + * side effects: + * delete_all() can be called, freeing memory and relinking chains. + */ +void +delete_stale(np) + struct namebuf *np; +{ + struct databuf *dp; + again: + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (dp->d_zone == DB_Z_CACHE && stale(dp)) { + delete_all(np, dp->d_class, dp->d_type); + goto again; + } + } +} + + +/* + * Adjust answer message so that it fits in outlen. Set tc if required. + * + * If outlen = msglen, can be used to verify qdcount, ancount, nscount + * and arcount. + * + * return new length + */ + +int +trunc_adjust(u_char *msg, int msglen, int outlen) { + register HEADER *hp; + u_int qdcount, ancount, nscount, arcount, dlen; + u_char *cp = msg, *cp1, *eom_in, *eom_out; + int n; + + eom_in = msg + msglen; + eom_out = msg + outlen; + + hp = (HEADER *)msg; + qdcount = ntohs(hp->qdcount); + ancount = ntohs(hp->ancount); + nscount = ntohs(hp->nscount); + arcount = ntohs(hp->arcount); + cp += HFIXEDSZ; + + while ((qdcount || ancount || nscount || arcount) && + cp < eom_in && cp < eom_out) { + + cp1 = cp; /* use temporary in case we break */ + + n = dn_skipname(cp1, eom_in); + if (n < 0) + break; + cp1 += n + 2 * INT16SZ; /* type, class */ + + if (!qdcount) { + cp1 += INT32SZ; /* ttl */ + if (cp1 + INT16SZ > eom_in) + break; + GETSHORT(dlen, cp1); + cp1 += dlen; + } + + if (cp1 > eom_in || cp1 > eom_out) + break; + + cp = cp1; + + if (qdcount) + qdcount--; + else if (ancount) + ancount--; + else if (nscount) + nscount--; + else + arcount--; + } + + if (qdcount || ancount || nscount || arcount) { + ns_debug(ns_log_default, 1, + "trunc_adjust:%s %d %d %d %d %d, %d %d %d %d %d", + hp->tc?" tc":"", msglen, + ntohs(hp->qdcount), ntohs(hp->ancount), + ntohs(hp->nscount), ntohs(hp->arcount), + cp-msg, qdcount, ancount, nscount, arcount); + hp->tc = 1; + hp->qdcount = htons(ntohs(hp->qdcount) - qdcount); + hp->ancount = htons(ntohs(hp->ancount) - ancount); + hp->nscount = htons(ntohs(hp->nscount) - nscount); + hp->arcount = htons(ntohs(hp->arcount) - arcount); + } + ENSURE(cp <= eom_out); + return (cp - msg); +} + +static void +add_related_additional(char *name) { + int i; + + if (num_related >= MAX_RELATED - 1) + return; + for (i = 0; i < num_related; i++) + if (strcasecmp(name, related[i]) == 0) { + freestr(name); + return; + } + related[num_related++] = name; +} + +static void +free_related_additional() { + int i; + + for (i = 0; i < num_related; i++) + freestr(related[i]); + num_related = 0; +} + +static int +related_additional(char *name) { + int i; + + for (i = 0; i < num_related; i++) + if (strcasecmp(name, related[i]) == 0) + return (1); + return (0); +} + +static void +freestr_maybe(char **tname) { + if (tname == NULL || *tname == NULL) + return; + freestr(*tname); + *tname = NULL; +} diff --git a/contrib/bind/bin/named/ns_stats.c b/contrib/bind/bin/named/ns_stats.c new file mode 100644 index 0000000..5be0257 --- /dev/null +++ b/contrib/bind/bin/named/ns_stats.c @@ -0,0 +1,415 @@ +#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 8.18 1998/02/13 19:50:24 halley Exp $"; +#endif /* not lint */ + +/* + * 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. + */ + +/* + * Portions Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> +#include <isc/tree.h> + +#include "port_after.h" + +#ifdef HAVE_GETRUSAGE /* XXX */ +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#include "named.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", "NSAP_PTR", "SIG", + "KEY", "PX", "invalid(GPOS)", "AAAA", "LOC", + 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(FILE *); + +void +ns_stats() { + time_t timenow = time(NULL); + FILE *f; + int i; + + ns_notice(ns_log_statistics, "dumping nameserver stats"); + + if (!(f = fopen(server_options->stats_filename, "a"))) { + ns_notice(ns_log_statistics, "cannot open stat file, \"%s\"", + server_options->stats_filename); + return; + } + + fprintf(f, "+++ Statistics Dump +++ (%ld) %s", + (long)timenow, checked_ctime(&timenow)); + fprintf(f, "%ld\ttime since boot (secs)\n", + (long)(timenow - boottime)); + fprintf(f, "%ld\ttime since reset (secs)\n", + (long)(timenow - resettime)); + + /* query type statistics */ + fprintf(f, "%lu\tUnknown query types\n", (u_long)typestats[0]); + for(i=1; i < T_ANY+1; i++) + if (typestats[i]) { + if (typenames[i] != NULL) + fprintf(f, "%lu\t%s queries\n", + (u_long)typestats[i], typenames[i]); + else + fprintf(f, "%lu\ttype %d queries\n", + (u_long)typestats[i], i); + } + + /* name server statistics */ + nameserStats(f); + + fprintf(f, "++ Memory Statistics ++\n"); + memstats(f); + fprintf(f, "-- Memory Statistics --\n"); + fprintf(f, "--- Statistics Dump --- (%ld) %s", + (long)timenow, checked_ctime(&timenow)); + (void) my_fclose(f); + ns_notice(ns_log_statistics, "done dumping nameserver stats"); +} + +void +qtypeIncr(qtype) + int qtype; +{ + if (qtype < T_A || qtype > T_ANY) + qtype = 0; /* bad type */ + typestats[qtype]++; +} + +static tree *nameserTree; +static int nameserInit; + +static FILE *nameserStatsFile; +static const char *statNames[nssLast] = { + "RR", /* sent us an answer */ + "RNXD", /* sent us a negative response */ + "RFwdR", /* sent us a response we had to fwd */ + "RDupR", /* sent us an extra answer */ + "RFail", /* sent us a SERVFAIL */ + "RFErr", /* sent us a FORMERR */ + "RErr", /* sent us some other error */ + "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 */ + "SDupQ", /* sent them a retry */ + "SErr", /* sent failed (in sendto) */ + "RQ", /* sent us a query */ + "RIQ", /* sent us an inverse query */ + "RFwdQ", /* sent us a query we had to fwd */ + "RDupQ", /* sent us a retry */ + "RTCP", /* sent us a query using TCP */ + "SFwdR", /* fwdd a response to them */ + "SFail", /* sent them a SERVFAIL */ + "SFErr", /* sent them a FORMERR */ + "SNaAns", /* sent them a non autoritative answer */ + "SNXD", /* sent them a negative response */ + }; + +/* + * Note that addresses in network byte order always have the high byte first. + * XXX - this is horribly IPv4 dependent, but it's performance critical. + */ +static int +nameserCompar(const tree_t t1, const tree_t t2) { + u_char *p1 = (u_char *)t1, *p2 = (u_char *)t2; + int i; + + for (i = INADDRSZ; i > 0; i--) { + u_char c1 = *p1++, c2 = *p2++; + + if (c1 < c2) + return (-1); + if (c1 > c2) + return (1); + } + 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 == NULL && (flags & NS_F_INSERT) != 0) { + ns = (struct nameser *)memget(sizeof(struct nameser)); + if (ns == NULL) { + nomem: if (!haveComplained((u_long)nameserFind, 0)) + ns_notice(ns_log_statistics, + "nameserFind: memget failed; %s", + strerror(errno)); + return (NULL); + } + memset(ns, 0, sizeof *ns); + ns->addr = addr; + if (!tree_add(&nameserTree, nameserCompar, (tree_t)ns, NULL)) { + int save = errno; + memput(ns, sizeof *ns); + errno = save; + goto nomem; + } + } + return (ns); +} + +static void +nameserStatsOut(f, stats) + FILE *f; + u_long stats[]; +{ + int i; + const char *pre = "\t"; + + for (i = 0; i < (int)nssLast; i++) { + fprintf(f, "%s%lu", pre, (u_long)stats[i]); + pre = ((i+1) % 5) ? " " : " "; + } + fputc('\n', f); +} + +static void +nameserStatsHdr(f) + FILE *f; +{ + int i; + const 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); +} + +static void +nameserStats(f) + FILE *f; +{ + nameserStatsFile = f; + fprintf(f, "++ Name Server Statistics ++\n"); + nameserStatsHdr(f); + fprintf(f, "(Global)\n"); + nameserStatsOut(f, globalStats); + if (NS_OPTION_P(OPTION_HOSTSTATS)) + tree_trav(&nameserTree, nameserStatsTravUAR); + fprintf(f, "-- Name Server Statistics --\n"); + nameserStatsFile = NULL; +} + +void +ns_logstats(evContext ctx, void *uap, struct timespec due, + struct timespec inter) +{ + char buffer[1024]; + char buffer2[32], header[64]; + time_t timenow = time(NULL); + int i; +#ifdef HAVE_GETRUSAGE + struct rusage usage, childu; +#endif /*HAVE_GETRUSAGE*/ + +#ifdef HAVE_GETRUSAGE +# define tv_float(tv) ((tv).tv_sec + ((tv).tv_usec / 1000000.0)) + + getrusage(RUSAGE_SELF, &usage); + getrusage(RUSAGE_CHILDREN, &childu); + + sprintf(buffer, "CPU=%gu/%gs CHILDCPU=%gu/%gs", + tv_float(usage.ru_utime), tv_float(usage.ru_stime), + tv_float(childu.ru_utime), tv_float(childu.ru_stime)); + ns_info(ns_log_statistics, "USAGE %lu %lu %s", (u_long)timenow, + (u_long)boottime, buffer); +# undef tv_float +#endif /*HAVE_GETRUSAGE*/ + + sprintf(header, "NSTATS %lu %lu", (u_long)timenow, (u_long)boottime); + strcpy(buffer, header); + + for (i = 0; i < T_ANY+1; i++) { + if (typestats[i]) { + if (typenames[i]) + sprintf(buffer2, " %s=%lu", + typenames[i], typestats[i]); + else + sprintf(buffer2, " %d=%lu", i, typestats[i]); + if (strlen(buffer) + strlen(buffer2) > + sizeof(buffer) - 1) { + ns_info(ns_log_statistics, buffer); + strcpy(buffer, header); + } + strcat(buffer, buffer2); + } + } + ns_info(ns_log_statistics, buffer); + + sprintf(header, "XSTATS %lu %lu", (u_long)timenow, (u_long)boottime); + strcpy(buffer, header); + for (i = 0; i < (int)nssLast; i++) { + sprintf(buffer2, " %s=%lu", + statNames[i]?statNames[i]:"?", (u_long)globalStats[i]); + if (strlen(buffer) + strlen(buffer2) > sizeof(buffer) - 1) { + ns_info(ns_log_statistics, buffer); + strcpy(buffer, header); + } + strcat(buffer, buffer2); + } + ns_info(ns_log_statistics, buffer); +} + +static void +nameserFree(void *uap) { + struct nameser *ns = uap; + + memput(ns, sizeof *ns); +} + +void +ns_freestats(void) { + if (nameserTree == NULL) + return; + tree_mung(&nameserTree, nameserFree); + nameserInit = 0; +} diff --git a/contrib/bind/bin/named/ns_udp.c b/contrib/bind/bin/named/ns_udp.c new file mode 100644 index 0000000..8b1af5e --- /dev/null +++ b/contrib/bind/bin/named/ns_udp.c @@ -0,0 +1,123 @@ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_udp.c,v 8.5 1997/05/21 19:52:26 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/file.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <nlist.h> +#include <resolv.h> +#include <stdio.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.h" + +#include "named.h" + +void +ns_udp() { +#if defined(CHECK_UDP_SUM) || defined(FIX_UDP_SUM) + struct nlist nl[2]; + int fd; + int sum; + u_long res, offset; + + nl[0].n_name = UDPSUM; + nl[1].n_name = 0; + + if (nlist(KSYMS, nl)) { + ns_debug(ns_log_default, 1, "ns_udp: nlist (%s,%s) failed", + KSYMS, UDPSUM); + return; + } + + ns_debug(ns_log_default, 1, "ns_udp: %s %d %lu (%ld)", + nl[0].n_name, nl[0].n_type, nl[0].n_value, nl[0].n_value); + + if (!nl[0].n_type) + return; + + if ((fd = open(KMEM, O_RDWR, 0)) < 0) { + ns_debug(ns_log_default, 1, "ns_udp: open %s failed: %s", KMEM, + strerror(errno)); + return; + } + + offset = nl[0].n_value; +#ifdef KMAP + offset &= ((~0UL)>>1); +#endif + + res = lseek(fd, offset, SEEK_SET); + if (res != offset) { + ns_debug(ns_log_default, 1, "ns_udp: lseek %lu failed %lu: %s", + offset, res, strerror(errno)); + goto cleanup; + } + + if (read(fd, &sum, sizeof(sum)) != sizeof(sum)) { + ns_debug(ns_log_default, 1, "ns_udp: read failed: %s", + strerror(errno)); + goto cleanup; + } + + ns_debug(ns_log_default, 1, "ns_udp: %d", sum); + if (sum == 0) { +#ifdef FIX_UDP_SUM + sum = 1; + lseek(fd, offset, SEEK_SET); + if (res != offset) { + ns_debug(ns_log_default, 1, + "ns_udp: lseek %lu failed %lu: %s", + offset, res, strerror(errno)); + goto cleanup; + } + if (write(fd, &sum, sizeof(sum)) != sizeof(sum)) { + ns_debug(ns_log_default, 1, "ns_udp: write failed: %s", + strerror(errno)); + goto cleanup; + } + ns_warning(ns_log_default, "ns_udp: check sums turned on"); +#else + ns_panic(ns_log_default, 0, + "ns_udp: checksums NOT turned on, exiting"); +#endif + } +cleanup: + close(fd); +#endif +} diff --git a/contrib/bind/bin/named/ns_update.c b/contrib/bind/bin/named/ns_update.c new file mode 100644 index 0000000..e6fdd8d --- /dev/null +++ b/contrib/bind/bin/named/ns_update.c @@ -0,0 +1,2393 @@ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_update.c,v 8.24 1998/03/20 00:49:16 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Based on the Dynamic DNS reference implementation by Viraj Bais + * <viraj_bais@ccm.fm.intel.com> + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +#define WRITEABLE_MASK (S_IWUSR | S_IWGRP | S_IWOTH) + +/* XXXRTH almost all funcs. in here should be static! + map rdata_dump to db_to_textual + map rdata_expand to wire_to_db + make a textual_to_db and use it in merge_logs? + replace all this "map" stuff with the new routines (from 4.9.5 I think) + */ + +/* from ns_req.c */ + +static struct map m_opcode[] = { + { "nxdomain", NXDOMAIN }, + { "yxdomain", YXDOMAIN }, + { "nxrrset", NXRRSET }, + { "yxrrset", YXRRSET }, + { "delete", DELETE }, + { "add", ADD }, +}; +#define M_OPCODE_CNT (sizeof(m_opcode) / sizeof(struct map)) + +/* XXXRTH workaround map difficulties */ +#define M_CLASS_CNT m_class_cnt +#define M_TYPE_CNT m_type_cnt + +static char *opcodes[] = { + "delete", + "add", + "", + "nxdomain", + "", + "", + "yxdomain", + "yxrrset", + "nxrrset", + "", + "", +}; + + +/* from db_load.c */ + +static struct map m_section[] = { + { "zone", S_ZONE }, + { "prereq", S_PREREQ }, + { "update", S_UPDATE }, + { "reserved", S_ADDT }, +}; +#define M_SECTION_CNT (sizeof(m_section) / sizeof(struct map)) + + +/* from ns_req.c */ + +static ns_updrec *rrecp_start = NULL, *rrecp_last = NULL; + + +/* forward */ +static int findzone(const char *, int, int, int *, int); +static int rdata_expand(const u_char *, const u_char *, const u_char *, + u_int, size_t, u_char *, size_t); + + +static FILE * +open_transaction_log(struct zoneinfo *zp) { + FILE *fp; + + fp = fopen(zp->z_updatelog, "a+"); + if (fp == NULL) { + ns_error(ns_log_update, "can't open %s: %s", zp->z_updatelog, + strerror(errno)); + return (NULL); + } + if (ftell(fp) == 0L) { + fprintf(fp, "%s", LogSignature); + } + return (fp); +} + + +static int +close_transaction_log(struct zoneinfo *zp, FILE *fp) { + if (fflush(fp) == EOF) { + ns_error(ns_log_update, "fflush() of %s failed: %s", + zp->z_updatelog, strerror(errno)); + return (-1); + } + if (fsync(fileno(fp)) < 0) { + ns_error(ns_log_update, "fsync() of %s failed: %s", + zp->z_updatelog, strerror(errno)); + return (-1); + } + if (fclose(fp) == EOF) { + ns_error(ns_log_update, "fclose() of %s failed: %s", + zp->z_updatelog, strerror(errno)); + return (-1); + } + return (0); +} + +/* + * printupdatelog(srcaddr, firstp, hp, zp, old_serial) + * append an ascii form to the zone's transaction log file. + */ +static void +printupdatelog(struct sockaddr_in srcaddr, + ns_updrec *firstp, + HEADER *hp, + struct zoneinfo *zp, + u_int32_t old_serial) +{ + struct databuf *dp; + struct map *mp; + ns_updrec *rrecp; + int opcode; + char time[25]; + FILE *fp; + + if (!firstp) + return; + + fp = open_transaction_log(zp); + if (fp == NULL) + return; + + sprintf(time, "at %lu", (u_long)tt.tv_sec); + fprintf(fp, "[DYNAMIC_UPDATE] id %u from %s %s (named pid %ld):\n", + hp->id, sin_ntoa(srcaddr), time, (long)getpid()); + for (rrecp = firstp; rrecp; rrecp = rrecp->r_next) { + INSIST(zp == &zones[rrecp->r_zone]); + switch (rrecp->r_section) { + case S_ZONE: + fprintf(fp, "zone:\torigin %s class %s serial %u\n", + zp->z_origin, p_class(zp->z_class), + old_serial); + break; + case S_PREREQ: + opcode = rrecp->r_opcode; + fprintf(fp, "prereq:\t{%s} %s. %s ", + opcodes[opcode], rrecp->r_dname, + p_class(zp->z_class)); + if (opcode == NXRRSET || opcode == YXRRSET) { + fprintf(fp, "%s ", p_type(rrecp->r_type)); + if ((dp = rrecp->r_dp) && dp->d_size > 0) { + dp->d_class = zp->z_class; + (void) rdata_dump(dp, fp); + } + } + fprintf(fp, "\n"); + break; + case S_UPDATE: + opcode = rrecp->r_opcode; + fprintf(fp, "update:\t{%s} %s. ", + opcodes[opcode], rrecp->r_dname); + if (opcode == ADD) + fprintf(fp, "%u ", rrecp->r_ttl); + fprintf(fp, "%s ", p_class(zp->z_class)); + if (rrecp->r_type != T_ANY) + fprintf(fp, "%s ", p_type(rrecp->r_type)); + if ((dp = rrecp->r_dp) && dp->d_size > 0) { + dp->d_class = zp->z_class; + (void) rdata_dump(dp, fp); + } + fprintf(fp, "\n"); + break; + case S_ADDT: + break; + default: + ns_panic(ns_log_update, 1, + "printupdatelog - impossible condition"); + /*NOTREACHED*/ + } + } + fprintf(fp, "\n"); + (void) close_transaction_log(zp, fp); +} + +static void +cancel_soa_update(struct zoneinfo *zp) { + ns_debug(ns_log_update, 3, "cancel_soa_update for %s", zp->z_origin); + zp->z_flags &= ~Z_NEED_SOAUPDATE; + zp->z_soaincrtime = 0; + zp->z_updatecnt = 0; +} + +/* + * Figure out when a SOA serial number update should happen. + * Returns non-zero if the caller should call sched_zone_maint(zp). + */ +int +schedule_soa_update(struct zoneinfo *zp, int numupdated) { + (void) gettime(&tt); + + zp->z_flags |= Z_NEED_SOAUPDATE; + + /* + * Only z_deferupdcnt updates are allowed before we force + * a serial update. + */ + zp->z_updatecnt += numupdated; + if (zp->z_updatecnt >= zp->z_deferupdcnt) { + if (incr_serial(zp) < 0) { + ns_error(ns_log_update, + "error updating serial number for %s from %d", + zp->z_origin, zp->z_serial); + } else + return (0); + /* + * Note we continue scheduling if for some reason + * incr_serial fails. + */ + } + + if (zp->z_soaincrintvl > 0) { + /* We want automatic updates in this zone. */ + if (zp->z_soaincrtime > 0) { + /* Already scheduled. */ + ns_debug(ns_log_update, 3, + "schedule_soa_update('%s'): already scheduled", + zp->z_origin); + return (0); + } else { + /* First update since the soa was last incremented. */ + zp->z_updatecnt = numupdated; + zp->z_soaincrtime = tt.tv_sec + zp->z_soaincrintvl; + /* + * Never schedule soaincrtime to occur after + * dumptime. + */ + if (zp->z_soaincrtime > zp->z_dumptime) + zp->z_soaincrtime = zp->z_dumptime; + ns_debug(ns_log_update, 3, + "schedule_soa_update('%s'): scheduled for %lu", + zp->z_origin, (u_long)zp->z_soaincrtime); + return (1); + } + } + return (0); +} + +/* + * Figure out when a zone dump should happen. + * Returns non-zero if the caller should call sched_zone_maint(zp). + */ +int +schedule_dump(struct zoneinfo *zp) { + time_t half; + + (void) gettime(&tt); + + zp->z_flags |= Z_NEED_DUMP; + + if (zp->z_dumpintvl > 0) { + /* We want automatic dumping in this zone. */ + if (zp->z_dumptime > 0) { + /* Already scheduled. */ + ns_debug(ns_log_update, 3, + "schedule_dump('%s'): already scheduled", + zp->z_origin); + return (0); + } else { + /* + * Set new dump time for dynamic zone. Use a random + * number in the last half of the dump limit; we want + * it to be substantially correct while still + * preventing dump synchronization among various + * dynamic zones. + */ + half = (zp->z_dumpintvl + 1) / 2; + zp->z_dumptime = tt.tv_sec + half + (rand() % half); + /* + * Never schedule soaincrtime to occur after + * dumptime. + */ + if (zp->z_soaincrtime > zp->z_dumptime) + zp->z_soaincrtime = zp->z_dumptime; + ns_debug(ns_log_update, 3, + "schedule_dump('%s'): scheduled for %lu", + zp->z_origin, (u_long)zp->z_dumptime); + return (1); + } + } + return (0); +} + +/* + * int + * process_prereq(rec, rcodep) + * Process one prerequisite. + * returns: + * >0 prerequisite was satisfied. + * =0 prerequisite was not satisfied, or an error occurred. + * side effects: + * sets *rcodep if an error occurs or prerequisite isn't satisfied. + */ +static int +process_prereq(ns_updrec *ur, int *rcodep, u_int16_t zclass) { + const char *dname = ur->r_dname; + u_int16_t class = ur->r_class; + u_int16_t type = ur->r_type; + u_int32_t ttl = ur->r_ttl; + struct databuf *rdp = ur->r_dp; + const char *fname; + struct hashbuf *htp; + struct namebuf *np; + struct databuf *dp; + + /* + * An element in the list might have already been + * processed if it is in the same RRset as a previous + * RRset Exists (value dependent) prerequisite. + */ + if (rdp && (rdp->d_mark & D_MARK_FOUND) != 0) { + /* Already processed. */ + return (1); + } + if (ttl != 0) { + ns_debug(ns_log_update, 1, + "process_prereq: ttl!=0 in prereq section"); + *rcodep = FORMERR; + return (0); + } + htp = hashtab; + np = nlookup(dname, &htp, &fname, 0); + if (fname != dname) + np = NULL; /* Matching by wildcard not allowed here. */ + if (class == C_ANY) { + if (rdp->d_size) { + ns_debug(ns_log_update, 1, + "process_prereq: empty rdata required in prereq section with class=ANY"); + *rcodep = FORMERR; + return (0); + } + if (type == T_ANY) { + /* Name is in use. */ + ur->r_opcode = YXDOMAIN; + if (np == NULL || np->n_data == NULL) { + /* + * Name does not exist or is + * an empty nonterminal. + */ + ns_debug(ns_log_update, 1, + "process_prereq: %s not in use", + dname); + *rcodep = NXDOMAIN; + return (0); + } + } else { + /* RRset exists (value independent). */ + int found = 0; + + ur->r_opcode = YXRRSET; + if (np != NULL) + for (dp = np->n_data; + dp && !found; + dp = dp->d_next) + if (match(dp, class, type)) + found = 1; + if (!found) { + ns_debug(ns_log_update, 1, + "process_prereq: RRset (%s,%s,%s) does not exist", + dname, p_type(type), p_class(zclass)); + *rcodep = NXRRSET; + return (0); + } + } + } else if (class == C_NONE) { + if (rdp->d_size) { + ns_debug(ns_log_update, 1, + "process_prereq: empty rdata required in prereq section with class=NONE"); + *rcodep = FORMERR; + return (0); + } + if (type == T_ANY) { + /* Name is not in use. */ + ur->r_opcode = NXDOMAIN; + if (np != NULL && np->n_data != NULL) { + /* + * Name exists and is not an + * empty nonterminal. + */ + ns_debug(ns_log_update, 1, + "process_prereq: %s exists", + dname); + *rcodep = YXDOMAIN; + return (0); + } + } else { + /* RRset does not exist. */ + int found = 0; + + ur->r_opcode = NXRRSET; + class = zclass; + if (np != NULL) + for (dp = np->n_data; + dp && !found; + dp = dp->d_next) + if (match(dp, class, type)) + found = 1; + if (found) { + ns_debug(ns_log_update, 1, + "process_prereq: RRset (%s,%s) exists", + dname, p_type(type)); + *rcodep = YXRRSET; + return (0); + } + } + } else if (class == zclass) { + /* + * RRset exists (value dependent). + * + * Check for RRset equality also. + */ + ns_updrec *tmp; + + ur->r_opcode = YXRRSET; + if (!rdp) { + ns_debug(ns_log_update, 1, + "process_prereq: nonempty rdata required in prereq section with class=%s", + p_class(class)); + *rcodep = FORMERR; + return (0); + } + htp = hashtab; + np = nlookup(dname, &htp, &fname, 0); + if (np == NULL || fname != dname) { + *rcodep = NXRRSET; + return (0); + } + for (dp = np->n_data; dp; dp = dp->d_next) { + if (match(dp, class, type)) { + int found = 0; + + for (tmp = ur; + tmp && !found; + tmp = tmp->r_next) { + if (tmp->r_section != S_PREREQ) + break; + if (!db_cmp(dp, tmp->r_dp)) { + tmp->r_dp->d_mark |= + D_MARK_FOUND; + found = 1; + } + } + if (!found) { + *rcodep = NXRRSET; + return (0); + } + } + } + for (tmp = ur; tmp; tmp = tmp->r_next) + if (tmp->r_section == S_PREREQ && + !strcasecmp(dname, tmp->r_dname) && + tmp->r_class == class && + tmp->r_type == type && + (ur->r_dp->d_mark & D_MARK_FOUND) == 0) { + *rcodep = NXRRSET; + return (0); + } else { + tmp->r_opcode = YXRRSET; + } + } else { + ns_debug(ns_log_update, 1, + "process_prereq: incorrect class %s", + p_class(class)); + *rcodep = FORMERR; + return (0); + } + /* Through the gauntlet, and out. */ + return (1); +} + +/* + * int + * prescan_update(ur, rcodep) + * Process one prerequisite. + * returns: + * >0 update looks OK (format wise; who knows if it will succeed?) + * =0 update has something wrong with it. + * side effects: + * sets *rcodep if an error occurs or prerequisite isn't satisfied. + */ +static int +prescan_update(ns_updrec *ur, int *rcodep, u_int16_t zclass) { + const char *dname = ur->r_dname; + u_int16_t class = ur->r_class; + u_int16_t type = ur->r_type; + u_int32_t ttl = ur->r_ttl; + struct databuf *rdp = ur->r_dp; + const char *fname; + struct hashbuf *htp; + struct namebuf *np; + + if (class == zclass) { + if (type == T_ANY || + type == T_AXFR || type == T_IXFR || + type == T_MAILA || type == T_MAILB) { + ns_debug(ns_log_update, 1, + "prescan_update: invalid type (%s)", + p_type(type)); + *rcodep = FORMERR; + return (0); + } + } else if (class == C_ANY) { + if (ttl != 0 || rdp->d_size || + type == T_AXFR || type == T_IXFR || + type == T_MAILA || type == T_MAILB) { + ns_debug(ns_log_update, 1, + "prescan_update: formerr(#2)"); + *rcodep = FORMERR; + return (0); + } + } else if (class == C_NONE) { + if (ttl != 0 || type == T_ANY || + type == T_AXFR || type == T_IXFR || + type == T_MAILA || type == T_MAILB) { + ns_debug(ns_log_update, 1, + "prescan_update: formerr(#3)"); + *rcodep = FORMERR; + return (0); + } + } else { + ns_debug(ns_log_update, 1, + "prescan_update: invalid class (%s)", + p_class(class)); + *rcodep = FORMERR; + return (0); + } + /* No format errors found. */ + return (1); +} + +/* + * int + * process_updates(firstp, rcodep, from) + * Process prerequisites and apply updates from the list to the database. + * returns: + * number of successful updates, 0 if none were successful. + * side effects: + * *rcodep gets the transaction return code. + * can schedule maintainance for zone dumps and soa.serial# increments. + */ +static int +process_updates(ns_updrec *firstp, int *rcodep, struct sockaddr_in from) { + int i, j, n, dbflags, matches, zonenum; + int numupdated = 0, soaupdated = 0, schedmaint = 0; + u_int16_t zclass; + ns_updrec *ur; + const char *fname; + struct databuf *dp, *savedp; + struct zoneinfo *zp; + int zonelist[MAXDNAME]; + + *rcodep = SERVFAIL; + if (!firstp) + return (0); + if (firstp->r_section == S_ZONE) { + zclass = firstp->r_class; + zonenum = firstp->r_zone; + zp = &zones[zonenum]; + } else { + ns_debug(ns_log_update, 1, + "process_updates: missing zone record"); + return (0); + } + + /* Process prereq records and prescan update records. */ + for (ur = firstp; ur != NULL; ur = ur->r_next) { + const char * dname = ur->r_dname; + u_int16_t class = ur->r_class; + u_int16_t type = ur->r_type; + u_int32_t ttl = ur->r_ttl; + struct databuf *rdp = ur->r_dp; + u_int section = ur->r_section; + + ns_debug(ns_log_update, 3, +"process_update: record section=%s, dname=%s, \ +class=%s, type=%s, ttl=%d, dp=0x%0x", + p_section(section, ns_o_update), dname, + p_class(class), p_type(type), ttl, rdp); + + matches = findzone(dname, zclass, MAXDNAME, + zonelist, MAXDNAME); + ur->r_zone = 0; + for (j = 0; j < matches && !ur->r_zone; j++) + if (zonelist[j] == zonenum) + ur->r_zone = zonelist[j]; + if (!ur->r_zone) { + ns_debug(ns_log_update, 1, + "process_updates: record does not belong to the zone %s", + zones[zonenum].z_origin); + *rcodep = NOTZONE; + return (0); + } + + switch (section) { + case S_ZONE: + break; + case S_PREREQ: + if (!process_prereq(ur, rcodep, zclass)) + return (0); /* *rcodep has been set. */ + ns_debug(ns_log_update, 3, "prerequisite satisfied"); + break; + case S_UPDATE: + if (!prescan_update(ur, rcodep, zclass)) + return (0); /* *rcodep has been set. */ + ns_debug(ns_log_update, 3, "update prescan succeeded"); + break; + case S_ADDT: + break; + default: + ns_panic(ns_log_update, 1, + "process_updates: impossible section"); + /* NOTREACHED */ + } + } + + /* Now process the records in update section. */ + for (ur = firstp; ur != NULL; ur = ur->r_next) { + const char * dname = ur->r_dname; + u_int16_t class = ur->r_class; + + if (ur->r_section != S_UPDATE) + continue; + dbflags = 0; + savedp = NULL; + dp = ur->r_dp; + if (class == zp->z_class) { + /* ADD databuf dp to hash table */ + /* + * Handling of various SOA/WKS/CNAME scenarios + * is done in db_update(). + */ + ur->r_opcode = ADD; + dbflags |= DB_NODATA; + n = db_update(dname, dp, dp, &savedp, + dbflags, hashtab, from); + if (n != OK) { + ns_debug(ns_log_update, 3, + "process_updates: failed to add databuf (%d)", + n); + } else { + ns_debug(ns_log_update, 3, + "process_updates: added databuf 0x%0x", + dp); + dp->d_mark = D_MARK_ADDED; + numupdated++; + if (dp->d_type == T_SOA) + soaupdated = 1; + } + } else if (class == C_ANY || class == C_NONE) { + /* + * DELETE databuf's matching dp from the hash table. + * + * handling of various SOA/NS scenarios done + * in db_update(). + */ + ur->r_opcode = DELETE; + /* + * we know we're deleting now, and db_update won't + * match with class==C_NONE, so we use the zone's + * class. + */ + if (class == C_NONE) + ur->r_dp->d_class = zp->z_class; + dbflags |= DB_DELETE; + n = db_update(dname, dp, NULL, &savedp, + dbflags, hashtab, from); + if (n != OK) + ns_debug(ns_log_update, 3, + "process_updates: delete failed"); + else { + ns_debug(ns_log_update, 3, + "process_updates: delete succeeded"); + numupdated++; + } + } + /* + * Even an addition could have caused some deletions like + * replacing old SOA or CNAME or WKS record or records of + * lower cred/clev. + * + * We need to save the deleted databuf's in case we wish to + * abort this update transaction and roll back all updates + * applied from this packet. + */ + ur->r_deldp = savedp; + } + + /* + * If we got here, things are OK, so set rcodep to indicate so. + */ + *rcodep = NOERROR; + + if (!numupdated) + return (0); + + /* + * schedule maintenance for dumps and SOA.serial# increment + * (this also sets Z_NEED_DUMP and Z_NEED_SOAUPDATE appropriately) + */ + schedmaint = 0; + if (schedule_dump(zp)) + schedmaint = 1; + if (soaupdated) { + /* + * SOA updated by this update transaction, so + * we need to set the zone serial number, stop any + * automatic updates that may be pending, and send out + * a NOTIFY message. + */ + zp->z_serial = get_serial_unchecked(zp); + cancel_soa_update(zp); + schedmaint = 1; +#ifdef BIND_NOTIFY + sysnotify(zp->z_origin, zp->z_class, T_SOA); +#endif + } else { + if (schedule_soa_update(zp, numupdated)) + schedmaint = 1; + } + if (schedmaint) + sched_zone_maint(zp); + return (numupdated); +} + +static enum req_action +req_update_private(HEADER *hp, u_char *cp, u_char *eom, u_char *msg, + struct qstream *qsp, int dfd, struct sockaddr_in from) +{ + char dnbuf[MAXDNAME], *dname; + u_int zocount, prcount, upcount, adcount, class, type, dlen; + u_int32_t ttl; + int i, n, cnt, found, matches, zonenum, numupdated = 0; + int rcode = NOERROR; + u_int c, section; + u_char rdata[MAXDATA]; + struct qinfo *qp; + struct databuf *dp, *nsp[NSMAX]; + struct databuf **nspp = &nsp[0]; + struct zoneinfo *zp; + ns_updrec *rrecp; + int zonelist[MAXDNAME]; + int should_use_tcp; + u_int32_t old_serial; + + nsp[0] = NULL; + + zocount = ntohs(hp->qdcount); + prcount = ntohs(hp->ancount); + upcount = ntohs(hp->nscount); + adcount = ntohs(hp->arcount); + + /* Process zone section. */ + ns_debug(ns_log_update, 3, "req_update: section ZONE, count %d", + zocount); + if ((n = dn_expand(msg, eom, cp, dnbuf, sizeof(dnbuf))) < 0) { + ns_debug(ns_log_update, 1, "req_update: expand name failed"); + hp->rcode = FORMERR; + return (Finish); + } + dname = dnbuf; + cp += n; + if (cp + 2 * INT16SZ > eom) { + ns_debug(ns_log_update, 1, "req_update: too short"); + hp->rcode = FORMERR; + return (Finish); + } + GETSHORT(type, cp); + GETSHORT(class, cp); + if (zocount != 1 || type != T_SOA) { + ns_debug(ns_log_update, 1, + "req_update: incorrect count or type for zone section: %d", + zocount); + hp->rcode = FORMERR; + return (Finish); + } + + matches = findzone(dname, class, 0, zonelist, MAXDNAME); + if (matches == 1) { + zonenum = zonelist[0]; + zp = &zones[zonenum]; + old_serial = get_serial(zp); + if (zp->z_class != (int)class || + (zp->z_type != z_master && zp->z_type != z_slave)) + matches = 0; + } + if (matches != 1) { + ns_debug(ns_log_update, 1, + "req_update: non-authoritative server for %s", + dname); + hp->rcode = NOTAUTH; + return (Finish); + } + + /* + * Begin Access Control Point + */ + + if (!ip_address_allowed(zp->z_update_acl, from.sin_addr)) { + ns_notice(ns_log_security, "unapproved update from %s for %s", + sin_ntoa(from), *dname ? dname : "."); + return (Refuse); + } + + /* + * End Access Control Point + */ + + /* XXXVIX should check update key when we have one. */ + + /* we should be authoritative */ + if (!(zp->z_flags & Z_AUTH)) { + ns_debug(ns_log_update, 1, + "req_update: zone %s: Z_AUTH not set", + dname); + hp->rcode = NOTAUTH; + return (Finish); + } + + if (zp->z_type == Z_SECONDARY) { + /* + * XXX the code below is broken. Until fixed, we just + * refuse. + */ + return (Refuse); + + /* We are a slave for this zone, forward it to the master. */ + for (cnt = 0; cnt < zp->z_addrcnt; cnt++) + *nspp++ = savedata(zp->z_class, T_A, USE_MINIMUM, + (u_char *)&zp->z_addr[cnt].s_addr, + INT32SZ); + *nspp = NULL; + /* + * If the request came in over TCP, forward it over TCP + */ + should_use_tcp = (qsp != NULL); + n = ns_forw(nsp, msg, eom-msg, from, qsp, dfd, &qp, + dname, class, type, NULL, should_use_tcp); + free_nsp(nsp); + switch (n) { + case FW_OK: + case FW_DUP: + return (Return); + case FW_NOSERVER: + /* should not happen */ + case FW_SERVFAIL: + hp->rcode = SERVFAIL; + return (Finish); + } + } + /* + * We are the primary master server for this zone, + * proceed further and process update packet + */ + if (!(zp->z_flags & Z_DYNAMIC)) { + ns_debug(ns_log_update, 1, + "req_update: dynamic flag not set for zone %s", + dname); + return (Refuse); + } + ns_debug(ns_log_update, 3, + "req_update: update request for zone %s, class %s", + zp->z_origin, p_class(class)); + rrecp_start = res_mkupdrec(S_ZONE, dname, class, type, 0); + rrecp_start->r_zone = zonenum; + rrecp_start->r_prev = NULL; + rrecp_start->r_next = NULL; + rrecp_last = rrecp_start; + + /* + * Parse the prerequisite and update sections for format errors. + */ + for (i = 0; (u_int)i < prcount + upcount; i++) { + if ((n = dn_expand(msg, eom, cp, dnbuf, sizeof(dnbuf))) < 0) { + ns_debug(ns_log_update, 1, + "req_update: expand name failed"); + hp->rcode = FORMERR; + return (Finish); + } + dname = dnbuf; + cp += n; + if (cp + RRFIXEDSZ > eom) { + ns_debug(ns_log_update, 1, + "req_update: overrun in answer"); + hp->rcode = FORMERR; + return (Finish); + } + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + n = 0; + dp = NULL; + if (dlen > 0) { + if (cp + dlen > eom) { + ns_debug(ns_log_update, 1, + "req_update: bad dlen"); + hp->rcode = FORMERR; + return (Finish); + } + n = rdata_expand(msg, eom, cp, type, dlen, + rdata, sizeof rdata); + if (n == 0 || n > MAXDATA) { + ns_debug(ns_log_update, 1, + "req_update: failed to expand record"); + hp->rcode = FORMERR; + return (Finish); + } + cp += dlen; + } + section = ((u_int)i < prcount) ? S_PREREQ : S_UPDATE; + rrecp = res_mkupdrec(section, dname, class, type, ttl); + dp = savedata(class, type, ttl, rdata, n); + dp->d_zone = zonenum; + dp->d_cred = DB_C_ZONE; + dp->d_clev = nlabels(zp->z_origin); + /* XXX - also record in dp->d_ns, which host this came from */ + rrecp->r_dp = dp; + /* Append the current record to the end of list of records. */ + rrecp_last->r_next = rrecp; + rrecp->r_prev = rrecp_last; + rrecp->r_next = NULL; + rrecp_last = rrecp; + if (cp > eom) { + ns_info(ns_log_update, + "Malformed response from %s (overrun)", + inet_ntoa(from.sin_addr)); + hp->rcode = FORMERR; + return (Finish); + } + } + + /* Now process all parsed records in the prereq and update sections. */ + numupdated = process_updates(rrecp_start, &rcode, from); + hp->rcode = rcode; + if (numupdated <= 0) { + ns_error(ns_log_update, + "error processing update packet id %d from %s", + hp->id, sin_ntoa(from)); + return (Finish); + } + + /* Make a log of the update. */ + (void) printupdatelog(from, rrecp_start, hp, zp, old_serial); + + return (Finish); +} + +static void +free_rrecp(ns_updrec **startpp, ns_updrec **lastpp, int rcode, + struct sockaddr_in from) +{ + ns_updrec *rrecp, *first_rrecp, *next_rrecp; + struct databuf *dp, *tmpdp; + char *dname, *msg; + + REQUIRE(startpp != NULL && lastpp != NULL); + + if (rcode == NOERROR) { + first_rrecp = *startpp; + msg = "free_rrecp: update transaction succeeded, cleaning up"; + } else { + first_rrecp = *lastpp; + msg = "free_rrecp: update transaction aborted, rolling back"; + } + ns_debug(ns_log_update, 1, msg); + for (rrecp = first_rrecp; rrecp != NULL; rrecp = next_rrecp) { + if (rcode == NOERROR) + next_rrecp = rrecp->r_next; + else + next_rrecp = rrecp->r_prev; + if (rrecp->r_section != S_UPDATE) { + if (rrecp->r_dp) + db_freedata(rrecp->r_dp); + res_freeupdrec(rrecp); + continue; + } + dname = rrecp->r_dname; + dp = rrecp->r_dp; + if ((dp->d_mark & D_MARK_ADDED) != 0) { + if (rcode == NOERROR) { + /* + * This databuf is now a part of hashtab, + * or has been deleted by a subsequent update. + * Either way, we must not free it. + */ + dp->d_mark &= ~D_MARK_ADDED; + } else { + /* Delete the databuf. */ + if (db_update(dname, dp, NULL, NULL, + DB_DELETE, hashtab, from) + != OK) { + ns_error(ns_log_update, + "free_rrecp: failed to delete databuf: dname=%s, type=%s", + dname, p_type(dp->d_type)); + } else { + ns_debug(ns_log_update, 3, + "free_rrecp: deleted databuf 0x%0x", + dp); + /* + * XXXRTH + * + * We used to db_freedata() here, + * but I removed it because 'dp' was + * part of a hashtab before we called + * db_update(), and since our delete + * has succeeded, it should have been + * freed. + */ + } + } + } else { + /* + * Databuf's matching this were deleted by this + * update, or were never executed (because we bailed + * out early). + */ + db_freedata(dp); + } + + /* Process deleted databuf's. */ + dp = rrecp->r_deldp; + while (dp != NULL) { + tmpdp = dp; + dp = dp->d_next; + if (rcode == NOERROR) { + if (tmpdp->d_rcnt) + ns_debug(ns_log_update, 1, + "free_rrecp: type = %d, rcnt = %d", + p_type(tmpdp->d_type), + tmpdp->d_rcnt); + else { + tmpdp->d_next = NULL; + db_freedata(tmpdp); + } + } else { + /* Add the databuf back. */ + tmpdp->d_mark &= ~D_MARK_DELETED; + if (db_update(dname, tmpdp, tmpdp, NULL, + 0, hashtab, from) != OK) { + ns_error(ns_log_update, + "free_rrecp: failed to add back databuf: dname=%s, type=%s", + dname, p_type(tmpdp->d_type)); + } else { + ns_debug(ns_log_update, 3, + "free_rrecp: added back databuf 0x%0x", + tmpdp); + } + } + } + res_freeupdrec(rrecp); + } + *startpp = NULL; + *lastpp = NULL; +} + +enum req_action +req_update(HEADER *hp, u_char *cp, u_char *eom, u_char *msg, + struct qstream *qsp, int dfd, struct sockaddr_in from) +{ + enum req_action ret; + + ret = req_update_private(hp, cp, eom, msg, qsp, dfd, from); + free_rrecp(&rrecp_start, &rrecp_last, hp->rcode, from); + if (ret == Finish) { + hp->qdcount = hp->ancount = hp->nscount = hp->arcount = 0; + memset(msg + HFIXEDSZ, 0, (eom - msg) - HFIXEDSZ); + } + return (ret); +} + +/* + * expand rdata portion of a compressed resource record at cp into cp1 + * and return the length of the expanded rdata (length of the compressed + * rdata is "dlen"). + */ +static int +rdata_expand(const u_char *msg, const u_char *eom, const u_char *cp, + u_int type, size_t dlen, u_char *cp1, size_t size) +{ + const u_char *cpinit = cp; + const u_char *cp1init = cp1; + int n, i; + + switch (type) { + case T_A: + if (dlen != INT32SZ) + return (0); + /*FALLTHROUGH*/ + case T_WKS: + case T_HINFO: + case T_TXT: + case T_X25: + case T_ISDN: + case T_NSAP: + case T_LOC: + if (size < dlen) + return (0); + memcpy(cp1, cp, dlen); + return (dlen); + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + n = dn_expand(msg, eom, cp, (char *)cp1, size); + if (n < 0 || (u_int)n != dlen) + return (0); + return (strlen((char *)cp1) + 1); + case T_MINFO: + case T_SOA: + case T_RP: + /* Get two compressed domain names. */ + for (i = 0; i < 2; i++) { + n = dn_expand(msg, eom, cp, (char *)cp1, size); + if (n < 0) + return (0); + cp += n; + n = strlen((char *)cp1) + 1; + cp1 += n; + size -= n; + } + if (type == T_SOA) { + n = 5 * INT32SZ; + if (size < (size_t)n || cp + n > eom) + return(0); + size -= n; + memcpy(cp1, cp, n); + cp += n; + cp1 += n; + } + if (cp != cpinit + dlen) + return (0); + return (cp1 - cp1init); + case T_MX: + case T_AFSDB: + case T_RT: + case T_SRV: + /* Grab preference. */ + if (size < INT16SZ || cp + INT16SZ > eom) + return (0); + size -= INT16SZ; + memcpy(cp1, cp, INT16SZ); + cp += INT16SZ; + cp1 += INT16SZ; + + if (type == T_SRV) { + if (size < INT16SZ*2 || cp + INT16SZ*2 > eom) + return (0); + size -= INT16SZ*2; + /* Grab weight and port. */ + memcpy(cp1, cp, INT16SZ*2); + cp1 += INT16SZ*2; + cp += INT16SZ*2; + } + + /* Get name. */ + n = dn_expand(msg, eom, cp, (char *)cp1, size); + if (n < 0) + return (0); + cp += n; + n = strlen((char *)cp1) + 1; + cp1 += n; + if (cp != cpinit + dlen) + return (0); + return (cp1 - cp1init); + case T_PX: + /* Grab preference. */ + if (size < INT16SZ || cp + INT16SZ > eom) + return (0); + size -= INT16SZ; + memcpy(cp1, cp, INT16SZ); + cp += INT16SZ; + cp1 += INT16SZ; + /* Get MAP822 name. */ + n = dn_expand(msg, eom, cp, (char *)cp1, size); + if (n < 0) + return (0); + cp += n; + n = strlen((char *)cp1) + 1; + cp1 += n; + size -= n; + n = dn_expand(msg, eom, cp, (char *)cp1, size); + if (n < 0) + return (0); + cp += n; + n = strlen((char *)cp1) + 1; + cp1 += n; + if (cp != cpinit + dlen) + return (0); + return (cp1 - cp1init); + default: + ns_debug(ns_log_update, 3, "unknown type %d", type); + return (0); + } +} + +/* + * Print out rdata portion of a resource record from a databuf into a file. + * + * XXX - similar code in db_dump() should be replaced by a call to this + * function. + */ +void +rdata_dump(struct databuf *dp, FILE *fp) { + u_int32_t n, addr; + u_char *cp, *end; + int i, j; + const char *proto; + + cp = (u_char *)dp->d_data; + switch (dp->d_type) { + case T_A: + switch (dp->d_class) { + case C_IN: + case C_HS: + GETLONG(n, cp); + n = htonl(n); + fputs(inet_ntoa(*(struct in_addr *)&n), fp); + break; + } + if (dp->d_nstime) + fprintf(fp, ";\tNT=%d", dp->d_nstime); + 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++) != '\0') { + fprintf(fp, "\"%.*s\"", (int)n, cp); + cp += n; + } else + fprintf(fp, "\"\""); + if ((n = *cp++) != '\0') + 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. ( ", cp); +#if defined(RETURNSOA) && defined(NCACHE) + if (dp->d_rcode == NXDOMAIN) + fputs(";", fp); +#endif + cp += strlen((char *)cp) + 1; + GETLONG(n, cp); + fprintf(fp, "%u", n); + GETLONG(n, cp); + fprintf(fp, " %u", n); + GETLONG(n, cp); + fprintf(fp, " %u", n); + GETLONG(n, cp); + fprintf(fp, " %u", n); + GETLONG(n, cp); + fprintf(fp, " %u )", n); +#if defined(RETURNSOA) && defined(NCACHE) + if (dp->d_rcode == NXDOMAIN) + fprintf(fp, ";%s.;NXDOMAIN;\t-$", cp); +#endif + break; + case T_MX: + case T_AFSDB: + case T_RT: + GETSHORT(n, cp); + fprintf(fp, "%u", n); + fprintf(fp, " %s.", cp); + break; + case T_PX: + GETSHORT(n, cp); + fprintf(fp, "%u", n); + fprintf(fp, " %s.", cp); + cp += strlen((char *)cp) + 1; + 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++) != '\0') { + for (j = n; j > 0 && cp < end; j--) + if (*cp == '\n') { + (void) putc('\\', fp); + (void) putc(*cp++, fp); + } else + (void) putc(*cp++, fp); + } + } + /* XXXVIX need to keep the segmentation (see 4.9.5). */ + (void) fputs("\"", fp); + break; + case T_NSAP: + (void) fputs(inet_nsap_ntoa(dp->d_size, dp->d_data, NULL), fp); + break; + case T_LOC: + (void) fputs(loc_ntoa(dp->d_data, NULL), fp); + break; + case T_WKS: + GETLONG(addr, cp); + addr = htonl(addr); + fputs(inet_ntoa(*(struct in_addr *)&addr), fp); + proto = protocolname((u_char)*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; + default: + fprintf(fp, "\t;?d_type=%d?", dp->d_type); + } +} + +/* + * Return the number of authoritative zones that "dname" could belong to by + * stripping up to "depth" labels from dname. Up to the first "maxzones" + * authoritative zone numbers will be stored in "zonelist", ordered + * deepest match first. + */ +static int +findzone(const char *dname, int class, int depth, int *zonelist, int maxzones){ + char *tmpdname; + char tmpdnamebuf[MAXDNAME]; + char *zonename, *cp; + int tmpdnamelen, zonenamelen, zonenum, i, j, c; + int matches = 0; + int escaped, found, done; + + ns_debug(ns_log_update, 4, "findzone(dname=%s, class=%d, depth=%d, \ +zonelist=0x%x, maxzones=%d)", + dname, class, depth, zonelist, maxzones); +#ifdef DEBUG + if (debug >= 5) { + ns_debug(ns_log_update, 5, "zone dump:"); + for (zonenum = 1; zonenum < nzones; zonenum++) + printzoneinfo(zonenum, ns_log_update, 5); + } +#endif + + strcpy(tmpdnamebuf, dname); + tmpdname = tmpdnamebuf; + /* + * The code to handle trailing dots and escapes is adapted + * from samedomain(). + */ + tmpdnamelen = strlen(tmpdname); + /* + * Ignore a trailing label separator (i.e. an unescaped dot) + * in 'tmpdname'. + */ + if (tmpdnamelen && tmpdname[tmpdnamelen-1] == '.') { + escaped = 0; + /* note this loop doesn't get executed if tmpdnamelen==1 */ + for (j = tmpdnamelen - 2; j >= 0; j--) + if (tmpdname[j] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else { + break; + } + if (!escaped) { + tmpdnamelen--; + tmpdname[tmpdnamelen] = '\0'; + } + } + + for (done = i = 0; i <= depth && !done; i++) { + for (zonenum = 1; zonenum < nzones; zonenum++) { + if (zones[zonenum].z_type == z_nil) + continue; + if (zones[zonenum].z_class != class) + continue; + zonename = zones[zonenum].z_origin; + zonenamelen = strlen(zonename); + /* + * Ignore a trailing label separator + * (i.e. an unescaped dot) in 'zonename'. + */ + if (zonenamelen && zonename[zonenamelen-1] == '.') { + escaped = 0; + for (j = zonenamelen - 2; j >= 0; j--) + if (zonename[j] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else { + break; + } + if (!escaped) + zonenamelen--; + } + + if (tmpdnamelen != zonenamelen) + continue; + ns_debug(ns_log_update, 5, + "about to strncasecmp('%s', '%s', %d)", + tmpdname, zonename, tmpdnamelen); + /* XXXRTH I'm doing a special test for zonenamelen == 0 + because I worry that some implementations of + strncasecmp might not handle comparisions where + n==0 correctly */ + if (zonenamelen == 0 || + !strncasecmp(tmpdname, zonename, tmpdnamelen)) { + ns_debug(ns_log_update, 5, "match"); + zonelist[matches++] = zonenum; + if (matches == maxzones) { + /* XXX should signal error */ + return (matches); + } + } + } + + /* + * Strip off the first label if we're not already at + * the root label. + */ + if (*tmpdname != '\0') { + for (escaped = found = 0; + (c = *tmpdname) && !found; + tmpdname++) { + if (!escaped && (c == '.')) + /* + * Note the loop increment will + * make tmpdname point past the '.' + * before the '!found' test causes + * us to exit the loop. + */ + found = 1; + + if (escaped) + escaped = 0; + else if (c == '\\') + escaped = 1; + } + } else + done = 1; + + tmpdnamelen = strlen(tmpdname); + } + ns_debug(ns_log_update, 4, + "findzone: returning %d match(es)", matches); + return (matches); +} + +/* + * reapply lost updates from log file for the zone to the zone + * + * returns -1 on error, 0 on success, 1 if dump reload needed + */ +int +merge_logs(struct zoneinfo *zp) { + char origin[MAXDNAME], data[MAXDATA], dnbuf[MAXDNAME], sclass[3]; + char buf[BUFSIZ], buf2[100]; + FILE *fp; + u_int32_t serial, ttl, old_serial, new_serial; + char *dname, *cp, *cp1; + int type, class; + int i, c, section, opcode, matches, zonenum, err, multiline; + int nonempty_lineno = -1, prev_pktdone = 0, cont = 0, inside_next = 0; + int id, rcode = NOERROR; + u_int32_t n; + struct map *mp; + ns_updrec *rrecp; + struct databuf *dp; + struct in_addr ina; + int zonelist[MAXDNAME]; + struct stat st; + u_char *serialp; + struct sockaddr_in empty_from; + int datasize; + + empty_from.sin_family = AF_INET; + empty_from.sin_addr.s_addr = htonl(INADDR_ANY); + empty_from.sin_port = htons(0); + + /* XXX - much of this stuff is similar to that in nsupdate.c + * getword_str() was used in nsupdate.c for reasons described there + * getword() is used here just to be consistent with db_load() + */ + + /* If there is no log file, just return. */ + if (stat(zp->z_updatelog, &st) < 0) { + if (errno != ENOENT) + ns_error(ns_log_update, + "unexpected stat(%s) failure: %s", + zp->z_updatelog, strerror(errno)); + return (-1); + } + fp = fopen(zp->z_updatelog, "r"); + if (fp == NULL) { + ns_error(ns_log_update, "fopen(%s) failed: %s", + zp->z_updatelog, strerror(errno)); + return (-1); + } + + /* + * See if we really have a log file -- it might be a zone dump + * that was in the process of being renamed, or it might + * be garbage! + */ + + if (fgets(buf, sizeof(buf), fp)==NULL) { + ns_error(ns_log_update, "fgets() from %s failed: %s", + zp->z_updatelog, strerror(errno)); + fclose(fp); + return (-1); + } + if (strcmp(buf, DumpSignature) == 0) { + /* It's a dump; finish rename that was interrupted. */ + ns_info(ns_log_update, + "completing interrupted dump rename for %s", + zp->z_source); + if (rename(zp->z_updatelog, zp->z_source) < 0) { + ns_error(ns_log_update, "rename(%s,%s) failed: %s", + zp->z_updatelog, zp->z_source, + strerror(errno)); + return (-1); + } + fclose(fp); + /* Finally, tell caller to reload zone. */ + return (1); + } + if (strcmp(buf, LogSignature) != 0) { + /* Not a dump and not a log; complain and then bail out. */ + ns_error(ns_log_update, "invalid log file %s", + zp->z_updatelog); + fclose(fp); + return (-1); + } + + ns_debug(ns_log_update, 3, "merging logs for %s from %s", + zp->z_origin, zp->z_updatelog); + lineno = 1; + rrecp_start = NULL; + rrecp_last = NULL; + for (;;) { + if (!getword(buf, sizeof buf, fp, 0)) { + if (lineno == (nonempty_lineno + 1)) { + /* + * End of a nonempty line inside an update + * packet or not inside an update packet. + */ + continue; + } + /* + * Empty line or EOF. + * + * Marks completion of current update packet. + */ + inside_next = 0; + prev_pktdone = 1; + cont = 1; + } else { + nonempty_lineno = lineno; + } + + if (!strcasecmp(buf, "[DYNAMIC_UPDATE]")) { + err = 0; + rcode = NOERROR; + cp = fgets(buf, sizeof buf, fp); + if (cp != NULL) + lineno++; + if (cp == NULL || !sscanf((char *)cp, "id %d", &id)) + id = -1; + inside_next = 1; + prev_pktdone = 1; + cont = 1; + } else if (!strcasecmp(buf, "[INCR_SERIAL]")) { + /* XXXRTH not enough error checking here */ + cp = fgets(buf, sizeof buf, fp); + if (cp != NULL) + lineno++; + if (cp == NULL || + !sscanf((char *)cp, "from %u to %u", + &old_serial, &new_serial)) { + ns_error(ns_log_update, + "incr_serial problem with %s", + zp->z_updatelog); + } else { + serial = get_serial(zp); + if (serial != old_serial) { + ns_error(ns_log_update, + "serial number mismatch (log=%u, zone=%u) in %s", old_serial, + serial, zp->z_updatelog); + } else { + set_serial(zp, new_serial); + /* + * The zone has changed; make sure + * a dump is scheduled. + */ + (void)schedule_dump(zp); + sched_zone_maint(zp); + ns_info(ns_log_update, + "set serial to %u (log file %s)", + new_serial, zp->z_updatelog); + } + } + prev_pktdone = 1; + cont = 1; + } + if (prev_pktdone) { + if (rrecp_start) { + n = process_updates(rrecp_start, &rcode, + empty_from); + if (n > 0) + ns_info(ns_log_update, + "successfully merged update id %d from log file %s", + id, zp->z_updatelog); + else + ns_error(ns_log_update, + "error merging update id %d from log file %s", + id, zp->z_updatelog); + free_rrecp(&rrecp_start, &rrecp_last, rcode, + empty_from); + } + prev_pktdone = 0; + if (feof(fp)) + break; + } + if (cont) { + cont = 0; + continue; + } + if (!inside_next) + continue; + /* + * inside the same update packet, + * continue accumulating records. + */ + section = -1; + n = strlen(buf); + if (buf[n-1] == ':') + buf[--n] = '\0'; + for (mp = m_section; mp < m_section+M_SECTION_CNT; mp++) + if (!strcasecmp(buf, mp->token)) { + section = mp->val; + break; + } + ttl = 0; + type = -1; + class = zp->z_class; + n = 0; + data[0] = '\0'; + switch (section) { + case S_ZONE: + cp = fgets(buf, sizeof buf, fp); + if (!cp) + *buf = '\0'; + n = sscanf(cp, "origin %s class %s serial %ul", + origin, sclass, &serial); + if (n != 3 || strcasecmp(origin, zp->z_origin)) + err++; + if (cp) + lineno++; + if (!err && serial != zp->z_serial) { + ns_error(ns_log_update, + "serial number mismatch in update id %d (log=%u, zone=%u) in %s", + id, serial, zp->z_serial, + zp->z_updatelog); + inside_next = 0; + err++; + } + if (!err && inside_next) { + int success; + + dname = origin; + type = T_SOA; + class = sym_ston(__p_class_syms, sclass, + &success); + if (!success) { + err++; + break; + } + matches = findzone(dname, class, 0, + zonelist, MAXDNAME); + if (matches) + zonenum = zonelist[0]; + else + err++; + } + break; + case S_PREREQ: + case S_UPDATE: + /* Operation code. */ + if (!getword(buf, sizeof buf, fp, 0)) { + err++; + break; + } + opcode = -1; + if (buf[0] == '{') { + n = strlen(buf); + for (i = 0; (u_int32_t)i < n; i++) + buf[i] = buf[i+1]; + if (buf[n-2] == '}') + buf[n-2] = '\0'; + } + for (mp = m_opcode; mp < m_opcode+M_OPCODE_CNT; mp++) + if (!strcasecmp(buf, mp->token)) { + opcode = mp->val; + break; + } + if (opcode == -1) { + err++; + break; + } + /* Owner's domain name. */ + if (!getword((char *)dnbuf, sizeof dnbuf, fp, 0)) { + err++; + break; + } + n = strlen((char *)dnbuf) - 1; + if (dnbuf[n] == '.') + dnbuf[n] = '\0'; + dname = dnbuf; + ttl = 0; + type = -1; + class = zp->z_class; + n = 0; + data[0] = '\0'; + (void) getword(buf, sizeof buf, fp, 1); + if (isdigit(buf[0])) { /* ttl */ + ttl = strtoul(buf, 0, 10); + if (errno == ERANGE && ttl == ULONG_MAX) { + err++; + break; + } + (void) getword(buf, sizeof buf, fp, 1); + } + + /* possibly class */ + if (buf[0] != '\0') { + int success; + int maybe_class; + + maybe_class = sym_ston(__p_class_syms, + buf, + &success); + if (success) { + class = maybe_class; + (void) getword(buf, + sizeof buf, + fp, 1); + } + } + /* possibly type */ + if (buf[0] != '\0') { + int success; + int maybe_type; + + maybe_type = sym_ston(__p_type_syms, + buf, + &success); + + if (success) { + type = maybe_type; + (void) getword(buf, + sizeof buf, + fp, 1); + } + } + if (buf[0] != '\0') /* possibly rdata */ + /* + * Convert the ascii data 'buf' to the proper + * format based on the type and pack into + * 'data'. + * + * XXX - same as in db_load(), + * consolidation needed + */ + switch (type) { + case T_A: + if (!inet_aton(buf, &ina)) { + err++; + break; + } + n = ntohl(ina.s_addr); + cp = data; + PUTLONG(n, cp); + n = INT32SZ; + break; + case T_HINFO: + case T_ISDN: + n = strlen(buf); + data[0] = n; + memcpy(data+1, buf, n); + n++; + if (!getword(buf, sizeof buf, + fp, 0)) { + i = 0; + } else { + endline(fp); + i = strlen(buf); + } + data[n] = i; + memcpy(data+n+1, buf, i); + break; + case T_SOA: + case T_MINFO: + case T_RP: + (void) strcpy(data, buf); + cp = data + strlen(data) + 1; + if (!getword((char *)cp, + sizeof data - (cp - data), + fp, 1)) { + err++; + break; + } + cp += strlen((char *)cp) + 1; + if (type != T_SOA) { + n = cp - data; + break; + } + if (class != zp->z_class || + strcasecmp(dname, zp->z_origin)) { + err++; + break; + } + c = getnonblank(fp, zp->z_updatelog); + if (c == '(') { + multiline = 1; + } else { + multiline = 0; + ungetc(c, fp); + } + for (i = 0; i < 5; i++) { + n = getnum(fp, zp->z_updatelog, + GETNUM_SERIAL); + if (getnum_error) { + err++; + break; + } + PUTLONG(n, cp); + } + if (multiline && + getnonblank(fp, zp->z_updatelog) + != ')') { + err++; + break; + } + endline(fp); + break; + case T_WKS: + if (!inet_aton(buf, &ina)) { + err++; + break; + } + n = ntohl(ina.s_addr); + cp = data; + PUTLONG(n, cp); + *cp = (char)getprotocol(fp, + zp->z_updatelog + ); + n = INT32SZ + sizeof(char); + n = getservices((int)n, data, + fp, zp->z_updatelog); + break; + case T_NS: + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_PTR: + (void) strcpy(data, buf); + if (makename(data, origin, + sizeof(data)) == -1) { + err++; + break; + } + n = strlen(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 */ + cp = data; + PUTSHORT((u_int16_t)n, cp); + if (!getword(buf, sizeof(buf), + fp, 1)) { + err++; + break; + } + (void) strcpy((char *)cp, buf); + if (makename((char *)cp, origin, + sizeof(data) - (cp-data)) + == -1) { + err++; + break; + } + /* advance pointer to end of data */ + cp += strlen((char *)cp) +1; + /* now save length */ + n = (cp - data); + break; + case T_PX: + n = 0; + data[0] = '\0'; + cp = buf; + while (isdigit(*cp)) + n = n * 10 + (*cp++ - '0'); + cp = data; + PUTSHORT((u_int16_t)n, cp); + for (i = 0; i < 2; i++) { + if (!getword(buf, + sizeof(buf), + fp, 0)) { + err++; + break; + } + (void) strcpy((char *)cp, + buf); + cp += strlen((char *)cp) + 1; + } + n = cp - data; + break; + case T_TXT: + case T_X25: + i = strlen(buf); + cp = data; + datasize = sizeof data; + cp1 = buf; + while (i > 255) { + if (datasize < 256) { + ns_error(ns_log_update, + "record too big"); + return (-1); + } + datasize -= 255; + *cp++ = 255; + memcpy(cp, cp1, 255); + cp += 255; + cp1 += 255; + i -= 255; + } + if (datasize < i + 1) { + ns_error(ns_log_update, + "record too big"); + return (-1); + } + *cp++ = i; + memcpy(cp, cp1, i); + cp += i; + n = cp - data; + endline(fp); + /* XXXVIX: segmented texts 4.9.5 */ + break; + case T_NSAP: + n = inet_nsap_addr(buf, + (u_char *)data, + sizeof data); + endline(fp); + break; + case T_LOC: + cp = buf + (n = strlen(buf)); + *cp = ' '; + cp++; + while ((i = getc(fp), *cp = i, + i != EOF) + && *cp != '\n' + && (n < MAXDATA)) { + cp++; + n++; + } + if (*cp == '\n') + ungetc(*cp, fp); + *cp = '\0'; + n = loc_aton(buf, (u_char *)data); + if (n == 0) { + err++; + break; + } + endline(fp); + break; + default: + err++; + } + if (section == S_PREREQ) { + ttl = 0; + if (opcode == NXDOMAIN) { + class = C_NONE; + type = T_ANY; + n = 0; + } else if (opcode == YXDOMAIN) { + class = C_ANY; + type = T_ANY; + n = 0; + } else if (opcode == NXRRSET) { + class = C_NONE; + n = 0; + } else if (opcode == YXRRSET) { + if (n == 0) + class = C_ANY; + } + } else { /* section == S_UPDATE */ + if (opcode == DELETE) { + if (n == 0) { + class = C_ANY; + if (type == -1) + type = T_ANY; + } else { + class = C_NONE; + } + } + } + break; + case S_ADDT: + default: + ns_debug(ns_log_update, 1, + "cannot interpret section: %d", section); + inside_next = 0; + err++; + } + if (err) { + inside_next = 0; + ns_debug(ns_log_update, 1, + "merge of update id %d failed due to error at line %d", + id, lineno); + free_rrecp(&rrecp_start, &rrecp_last, rcode, + empty_from); + continue; + } + rrecp = res_mkupdrec(section, dname, class, type, ttl); + if (section != S_ZONE) { + dp = savedata(class, type, ttl, (u_char *)data, n); + dp->d_zone = zonenum; + dp->d_cred = DB_C_ZONE; + dp->d_clev = nlabels(zp->z_origin); + rrecp->r_dp = dp; + } else { + rrecp->r_zone = zonenum; + } + if (rrecp_start == NULL) { + rrecp_start = rrecp; + rrecp_last = rrecp; + rrecp->r_prev = NULL; + rrecp->r_next = NULL; + } else { + rrecp_last->r_next = rrecp; + rrecp->r_prev = rrecp_last; + rrecp->r_next = NULL; + rrecp_last = rrecp; + } + } /* for (;;) */ + + fclose(fp); + return (0); +} + + +/* + * Create a disk database to back up zones + */ +int +zonedump(zp) + struct zoneinfo *zp; +{ + FILE *fp; + const char *fname; + struct hashbuf *htp; + char *op; + struct stat st; + char tmp_name[MAXPATHLEN]; + int escaped; + char c; + + /* + * We must check to see if Z_NEED_SOAUPDATE is set, and if so + * we must do it. This won't be the case normally + * (when called from ns_maint()), but it is possible if we're + * exiting named. + */ + + if (zp->z_flags & Z_NEED_SOAUPDATE) { + u_int32_t serial, old_serial; + + old_serial = get_serial(zp); + serial = old_serial + 1; + if (serial == 0) + serial = 1; + set_serial(zp, serial); + } + + /* Only dump zone if there is a cache specified */ + if (zp->z_source && *(zp->z_source)) { + ns_debug(ns_log_update, 1, "zonedump(%s)", zp->z_source); + + if (strlen(zp->z_source)+strlen(DumpSuffix) >= MAXPATHLEN) { + ns_error(ns_log_update, + "filename %s too long in zonedump", + zp->z_source); + /* + * This problem won't ever get better, so we + * clear the "need dump" flag. + */ + zp->z_flags &= ~Z_NEED_DUMP; + return (-1); + } + (void)sprintf(tmp_name, "%s%s", zp->z_source, DumpSuffix); + if ((fp = write_open(tmp_name)) == NULL) { + ns_error(ns_log_update, "fopen() of %s failed: %s", + tmp_name, strerror(errno)); + return (-1); + } + fprintf(fp, "%s", DumpSignature); + op = zp->z_origin; + escaped = 0; + while (*op && (((c = *op++) != '.') || escaped)) + escaped = (c == '\\') && !escaped; + gettime(&tt); + htp = hashtab; + if (nlookup(zp->z_origin, &htp, &fname, 0) != NULL) { + if (db_dump(htp, fp, zp-zones, op) != OK) { + ns_error(ns_log_update, + "error dumping zone file %s", + zp->z_source); + (void)fclose(fp); + return (-1); + } + } + if (fflush(fp) == EOF) { + ns_error(ns_log_update, "fflush() of %s failed: %s", + tmp_name, strerror(errno)); + return (-1); + } + if (fsync(fileno(fp)) < 0) { + ns_error(ns_log_update, "fsync() of %s failed: %s", + tmp_name, strerror(errno)); + return (-1); + } + if (fclose(fp) == EOF) { + ns_error(ns_log_update, "fclose() of %s failed: %s", + tmp_name, strerror(errno)); + return (-1); + } + /* + * Try to make read only, so people will be less likely to + * edit dynamic domains. + */ + if (stat(tmp_name, &st) < 0) { + ns_error(ns_log_update, + "stat(%s) failed, pressing on: %s", + tmp_name, strerror(errno)); + } else { + zp->z_ftime = st.st_mtime; + st.st_mode &= ~WRITEABLE_MASK; + if (chmod(tmp_name, st.st_mode) < 0) + ns_error(ns_log_update, + "chmod(%s,%o) failed, pressing on: %s", + tmp_name, st.st_mode, + strerror(errno)); + } + if (rename(tmp_name, zp->z_updatelog) < 0) { + ns_error(ns_log_update, "rename(%s,%s) failed: %s", + tmp_name, zp->z_updatelog, strerror(errno)); + return (-1); + } + if (rename(zp->z_updatelog, zp->z_source) < 0) { + ns_error(ns_log_update, "rename(%s,%s) failed: %s", + zp->z_updatelog, zp->z_source, + strerror(errno)); + return (-1); + } + } else + ns_debug(ns_log_update, 1, "zonedump: no zone to dump"); + + zp->z_flags &= ~Z_NEED_DUMP; + zp->z_dumptime = 0; + return (0); +} + +struct databuf * +findzonesoa(struct zoneinfo *zp) { + struct hashbuf *htp; + struct namebuf *np; + struct databuf *dp; + const char *fname; + + htp = hashtab; + np = nlookup(zp->z_origin, &htp, &fname, 0); + if (np == NULL || fname != zp->z_origin) + return (NULL); + foreach_rr(dp, np, T_SOA, zp->z_class, zp - zones) + return (dp); + return (NULL); +} + +u_char * +findsoaserial(u_char *data) { + char *cp = (char *)data; + + cp += strlen(cp) + 1; /* Nameserver. */ + cp += strlen(cp) + 1; /* Mailbox. */ + return ((u_char *)cp); +} + +u_int32_t +get_serial_unchecked(struct zoneinfo *zp) { + struct databuf *dp; + u_char *cp; + u_int32_t ret; + + dp = findzonesoa(zp); + if (!dp) + ns_panic(ns_log_update, 1, + "get_serial_unchecked(%s): can't locate zone SOA", + zp->z_origin); + cp = findsoaserial(dp->d_data); + GETLONG(ret, cp); + return (ret); +} + +u_int32_t +get_serial(struct zoneinfo *zp) { + u_int32_t ret; + + ret = get_serial_unchecked(zp); + if (ret != zp->z_serial) + ns_panic(ns_log_update, 1, + "get_serial(%s): db and zone serial numbers differ", + zp->z_origin); + return (ret); +} + +void +set_serial(struct zoneinfo *zp, u_int32_t serial) { + struct databuf *dp; + u_char *cp; + + dp = findzonesoa(zp); + if (!dp) + ns_panic(ns_log_update, 1, + "set_serial(%s): can't locate zone SOA", + zp->z_origin); + cp = findsoaserial(dp->d_data); + PUTLONG(serial, cp); + zp->z_serial = serial; + zp->z_flags &= ~Z_NEED_SOAUPDATE; + zp->z_soaincrtime = 0; + zp->z_updatecnt = 0; +#ifdef BIND_NOTIFY + sysnotify(zp->z_origin, zp->z_class, T_SOA); +#endif + /* + * Note: caller is responsible for scheduling a dump + */ +} + +/* + * Increment serial number in zoneinfo structure and hash table SOA databuf + */ + +int +incr_serial(struct zoneinfo *zp) { + u_int32_t serial, old_serial; + FILE *fp; + time_t t; + + old_serial = get_serial(zp); + serial = old_serial + 1; + if (serial == 0) + serial = 1; + set_serial(zp, serial); + + (void) gettime(&tt); + t = (time_t)tt.tv_sec; + fp = open_transaction_log(zp); + if (fp == NULL) + return (-1); + fprintf(fp, "[INCR_SERIAL] from %u to %u %s\n", + old_serial, serial, checked_ctime(&t)); + if (close_transaction_log(zp, fp)<0) + return (-1); + + /* + * This shouldn't happen, but we check to be sure. + */ + if (!(zp->z_flags & Z_NEED_DUMP)) { + ns_warning(ns_log_update, + "incr_serial: Z_NEED_DUMP not set for zone '%s'", + zp->z_origin); + (void)schedule_dump(zp); + } + + sched_zone_maint(zp); + + return (0); +} + +void +dynamic_about_to_exit(void) { + struct zoneinfo *zp; + + ns_debug(ns_log_update, 1, + "shutting down; dumping zones that need it"); + for (zp = zones; zp < &zones[nzones]; zp++) { + if ((zp->z_flags & Z_DYNAMIC) && + ((zp->z_flags & Z_NEED_SOAUPDATE) || + (zp->z_flags & Z_NEED_DUMP))) + (void)zonedump(zp); + } +} diff --git a/contrib/bind/bin/named/ns_xfr.c b/contrib/bind/bin/named/ns_xfr.c new file mode 100644 index 0000000..52d3464 --- /dev/null +++ b/contrib/bind/bin/named/ns_xfr.c @@ -0,0 +1,642 @@ +#if !defined(lint) && !defined(SABER) +static char rcsid[] = "$Id: ns_xfr.c,v 8.25 1998/03/25 18:47:34 halley Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <fcntl.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "named.h" + +static struct qs_x_lev *sx_freelev(struct qs_x_lev *lev); + +static void sx_newmsg(struct qstream *qsp), + sx_sendlev(struct qstream *qsp), + sx_sendsoa(struct qstream *qsp); + +static int sx_flush(struct qstream *qsp), + sx_addrr(struct qstream *qsp, + const char *dname, + struct databuf *dp), + sx_nsrrs(struct qstream *qsp), + sx_allrrs(struct qstream *qsp), + sx_pushlev(struct qstream *qsp, struct namebuf *np); + +/* + * void + * ns_xfr(qsp, znp, zone, class, type, opcode, id) + * Initiate a concurrent (event driven) outgoing zone transfer. + */ +void +ns_xfr(struct qstream *qsp, struct namebuf *znp, + int zone, int class, int type, + int opcode, int id) +{ + FILE *rfp; + int fdstat; + pid_t pid; + server_info si; +#ifdef SO_SNDBUF + static const int sndbuf = XFER_BUFSIZE * 2; +#endif +#ifdef SO_SNDLOWAT + static const int sndlowat = XFER_BUFSIZE; +#endif + + ns_info(ns_log_xfer_out, "zone transfer of \"%s\" (%s) to %s", + zones[zone].z_origin, p_class(class), sin_ntoa(qsp->s_from)); + +#ifdef SO_SNDBUF + /* + * The default seems to be 4K, and we'd like it to have enough room + * to parallelize sending the pushed data with accumulating more + * write() data from us. + */ + (void) setsockopt(qsp->s_rfd, SOL_SOCKET, SO_SNDBUF, + (char *)&sndbuf, sizeof sndbuf); +#endif +#ifdef SO_SNDLOWAT + /* + * We don't want select() to show writability 'til we can write + * an XFER_BUFSIZE block of data. + */ + (void) setsockopt(qsp->s_rfd, SOL_SOCKET, SO_SNDLOWAT, + (char *)&sndlowat, sizeof sndlowat); +#endif + if (sq_openw(qsp, 64*1024) == -1) + goto abort; + memset(&qsp->xfr, 0, sizeof qsp->xfr); + qsp->xfr.top = znp; + qsp->xfr.zone = zone; + qsp->xfr.class = class; + qsp->xfr.id = id; + qsp->xfr.opcode = opcode; + qsp->xfr.msg = memget(XFER_BUFSIZE); + if (!qsp->xfr.msg) + goto abort; + qsp->xfr.eom = qsp->xfr.msg + XFER_BUFSIZE; + qsp->xfr.cp = NULL; + qsp->xfr.state = s_x_firstsoa; + zones[zone].z_numxfrs++; + qsp->flags |= STREAM_AXFR; + + si = find_server(qsp->s_from.sin_addr); + if (si != NULL && si->transfer_format != axfr_use_default) + qsp->xfr.transfer_format = si->transfer_format; + else + qsp->xfr.transfer_format = server_options->transfer_format; + + if (sx_pushlev(qsp, znp) < 0) { + abort: + (void) shutdown(qsp->s_rfd, 2); + sq_remove(qsp); + return; + } + (void) sq_writeh(qsp, sx_sendsoa); +} + +/* + * void + * ns_stopxfrs(zp) + * Stop (abort, reset) all transfers of the zone specified by 'zp'. + */ +void +ns_stopxfrs(struct zoneinfo *zp) { + struct qstream *this, *next; + u_int zone = (u_int)(zp - zones); + + for (this = streamq; this; this = next) { + next = this->s_next; + if (this->xfr.zone == zone) { + (void) shutdown(this->s_rfd, 2); + sq_remove(this); + } + } + INSIST(zp->z_numxfrs == 0); +} + +/* + * void + * ns_freexfr(qsp) + * Free all xfr-related dynamic data associated with qsp. + */ +void +ns_freexfr(struct qstream *qsp) { + if (qsp->xfr.msg != NULL) { + memput(qsp->xfr.msg, XFER_BUFSIZE); + qsp->xfr.msg = NULL; + } + while (qsp->xfr.lev) + qsp->xfr.lev = sx_freelev(qsp->xfr.lev); + zones[qsp->xfr.zone].z_numxfrs--; + qsp->flags &= ~STREAM_AXFR; +} + +/* + * u_char * + * renew_msg(msg) + * init the header of a message, reset the compression pointers, and + * reset the write pointer to the first byte following the header. + */ +static void +sx_newmsg(struct qstream *qsp) { + HEADER *hp = (HEADER *)qsp->xfr.msg; + + memset(hp, 0, HFIXEDSZ); + hp->id = htons(qsp->xfr.id); + hp->opcode = qsp->xfr.opcode; + hp->qr = 1; + hp->rcode = NOERROR; + + qsp->xfr.ptrs[0] = qsp->xfr.msg; + qsp->xfr.ptrs[1] = NULL; + + qsp->xfr.cp = qsp->xfr.msg + HFIXEDSZ; +} + +/* + * int + * sx_flush(qsp) + * flush the intermediate buffer out to the stream IO system. + * return: + * passed through from sq_write(). + */ +static int +sx_flush(struct qstream *qsp) { + int ret; + +#ifdef DEBUG + if (debug >= 10) + fp_nquery(qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg, + log_get_stream(packet_channel)); +#endif + ret = sq_write(qsp, qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg); + if (ret >= 0) + qsp->xfr.cp = NULL; + return (ret); +} + +/* + * int + * sx_addrr(qsp, name, dp) + * add name/dp's RR to the current assembly message. if it won't fit, + * write current message out, renew the message, and then RR should fit. + * return: + * -1 = the sx_flush() failed so we could not queue the full message. + * 0 = one way or another, everything is fine. + * side effects: + * on success, the ANCOUNT is incremented and the pointers are advanced. + */ +static int +sx_addrr(struct qstream *qsp, const char *dname, struct databuf *dp) { + HEADER *hp = (HEADER *)qsp->xfr.msg; + u_char **edp = qsp->xfr.ptrs + sizeof qsp->xfr.ptrs / sizeof(u_char*); + int n; + + if (qsp->xfr.cp != NULL) { + if (qsp->xfr.transfer_format == axfr_one_answer && + sx_flush(qsp) < 0) + return (-1); + } + if (qsp->xfr.cp == NULL) + sx_newmsg(qsp); + n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, + 0, qsp->xfr.ptrs, edp); + if (n < 0) { + if (sx_flush(qsp) < 0) + return (-1); + if (qsp->xfr.cp == NULL) + sx_newmsg(qsp); + n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, + 0, qsp->xfr.ptrs, edp); + INSIST(n >= 0); + } + hp->ancount = htons(ntohs(hp->ancount) + 1); + qsp->xfr.cp += n; + return (0); +} + +/* + * int + * sx_soarr(qsp) + * add the SOA RR's at the current level's top np to the assembly message. + * return: + * 0 = success + * -1 = write buffer full, cannot continue at this time + * side effects: + * if progress was made, header and pointers will be advanced. + */ +static int +sx_soarr(struct qstream *qsp) { + struct databuf *dp; + + foreach_rr(dp, qsp->xfr.top, T_SOA, qsp->xfr.class, qsp->xfr.zone) { + if (sx_addrr(qsp, zones[qsp->xfr.zone].z_origin, dp) < 0) { + /* RR wouldn't fit. Bail out. */ + return (-1); + } + return (0); + } + ns_panic(ns_log_xfer_out, 1, "no SOA at zone top"); +} + +/* + * int + * sx_nsrrs(qsp) + * add the NS RR's at the current level's current np, + * to the assembly message + * return: + * >1 = number of NS RRs added, note that there may be more + * 0 = success, there are no more NS RRs at this level + * -1 = write buffer full, cannot continue at this time + * side effects: + * if progress was made, header and pointers will be advanced. + * note: + * this is meant for AXFR, which includes glue as part of the answer + * sections. this is different from and incompatible with the additional + * data of a referral response. + */ +static int +sx_nsrrs(struct qstream *qsp) { + struct databuf *dp, *tdp, *gdp; + struct namebuf *gnp, *tnp, *top; + struct hashbuf *htp; + const char *fname; + int rrcount, class; + + class = qsp->xfr.class; + top = qsp->xfr.top; + rrcount = 0; + for ((void)NULL; + (dp = qsp->xfr.lev->dp) != NULL; + qsp->xfr.lev->dp = dp->d_next) { + /* XYZZY foreach_rr? */ + if (dp->d_class != class && class != C_ANY) + continue; + if (dp->d_rcode) + continue; + /* + * It might not be in the same zone, if we are authoritative + * for both parent and child, but it does have to be a zone. + * + * XXX: this is sort of a bug, since it means we merge the + * @ NS RRset into our parent's zone. But that is what + * db_load() does, so for now we have no choice. + */ + if (dp->d_zone == DB_Z_CACHE) + continue; + if (dp->d_type != T_NS) + continue; + if (!(qsp->xfr.lev->flags & SXL_GLUING)) { + if (sx_addrr(qsp, qsp->xfr.lev->dname, dp) < 0) { + /* RR wouldn't fit. Bail out. */ + return (-1); + } + rrcount++; + } + + /* + * 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; + gnp = nlookup((char *)dp->d_data, &htp, &fname, 0); + if (gnp == NULL || fname != (char *)dp->d_data) + continue; + for (tnp = gnp; + tnp != NULL && tnp != top; + tnp = tnp->n_parent) + (void)NULL; + if (tnp == NULL && NAME(*top)[0] != '\0') + continue; /* name server is not below top domain */ + for (tnp = gnp; + tnp != NULL && tnp != top; + tnp = tnp->n_parent) { + foreach_rr(tdp, tnp, T_NS, class, DB_Z_CACHE) + break; + /* If we found a zone cut, we're outta here. */ + if (tdp != NULL) + break; + } + /* If name server is not in a delegated zone, skip it. */ + if (tnp == top || (tnp == NULL && NAME(*top)[0] == '\0')) + continue; + /* Now we know glue records are needed. Send them. */ + qsp->xfr.lev->flags |= SXL_GLUING; + foreach_rr(gdp, gnp, T_A, class, DB_Z_CACHE) + if (sx_addrr(qsp, fname, gdp) < 0) { + /* + * Rats. We already sent the NS RR, too. + * Note that SXL_GLUING is being left on. + */ + return (-1); + } + qsp->xfr.lev->flags &= ~SXL_GLUING; + } + return (rrcount); +} + +/* + * int + * sx_allrrs(qsp) + * add the non-(SOA,NS) RR's at the current level's current np, + * to the assembly message + * return: + * >0 = number of RR's added, note that there may be more + * 0 = success, there are no more RRs at this level + * -1 = write buffer full, cannot continue at this time + * side effects: + * if progress was made, header and pointers will be advanced. + * note: + * this is meant for AXFR, which includes glue as part of the answer + * sections. this is different from and incompatible with the additional + * data of a referral response. + */ +static int +sx_allrrs(struct qstream *qsp) { + struct databuf *dp, *tdp, *gdp; + struct namebuf *gnp, *tnp, *top; + struct hashbuf *htp; + const char *fname; + int rrcount, class; + u_int zone; + + class = qsp->xfr.class; + top = qsp->xfr.top; + zone = qsp->xfr.zone; + rrcount = 0; + for ((void)NULL; + (dp = qsp->xfr.lev->dp) != NULL; + qsp->xfr.lev->dp = dp->d_next) { + /* XYZZY foreach_rr? */ + if (dp->d_class != class && class != C_ANY) + continue; + if (dp->d_rcode) + continue; + if (dp->d_zone != zone || stale(dp)) + continue; + if (dp->d_type == T_SOA || dp->d_type == T_NS) + continue; + /* XXXRTH I presume this is still relevant and that + this is the right place... */ +#if 0 /* Not yet implemented. Only a SHOULD in the I-D. -gnu@toad.com */ + /* skip the SIG AXFR record because we did it first too. */ + if (dp->d_type == T_SIG) { + int sig_rrtype = GETSHORT (dp->d_data); + if (sig_rrtype == T_AXFR) + continue; + } +#endif /* 0 */ + INSIST(!(qsp->xfr.lev->flags & SXL_GLUING)); + + if (sx_addrr(qsp, qsp->xfr.lev->dname, dp) < 0) { + /* RR wouldn't fit. Bail out. */ + return (-1); + } + rrcount++; + } + return (rrcount); +} + +/* + * void + * sx_sendlev(qsp) + * send all the RRs at the current level (really a domain name), and + * do a decomposed recursion to get all subdomains up to and including + * but not exceeding bottom zone cuts. + * side effects: + * advances qsp->xfr pointers. changes qsp->xfr.lev quite often. + * causes messages to be sent to a remote TCP client. changes the + * qsp->xfr.state at the end of the topmost level. changes the + * qsp->xfr.lev->state several times per domain name. + */ +static void +sx_sendlev(struct qstream *qsp) { + struct qs_x_lev *lev; + int rrcount; + + again: + lev = qsp->xfr.lev; + switch (lev->state) { + case sxl_ns: { + while (lev->dp) { + rrcount = sx_nsrrs(qsp); + /* If we can't pack this one in, come back later. */ + if (rrcount < 0) + return; + /* + * NS RRs other than those at the + * zone top are zone cuts. + */ + if (rrcount > 0 && qsp->xfr.top != lev->np) + lev->flags |= SXL_ZONECUT; + } + /* No more DP's for the NS RR pass on this NP. */ + if (lev->flags & SXL_ZONECUT) { + /* Zone cut, so go directly to end of level. */ + break; + } + /* No NS RR's, so it's safe to send other types. */ + lev->state = sxl_all; + lev->dp = lev->np->n_data; + goto again; + } + case sxl_all: { + while (lev->dp) { + /* If we can't pack this one in, come back later. */ + if (sx_allrrs(qsp) < 0) + return; + } + /* No more non-NS DP's for this NP, do subdomains. */ + lev->state = sxl_sub; + goto again; + } + case sxl_sub: { + struct namebuf *np; + + /* Get next in-use hash chain if we're not following one. */ + while (lev->nnp == NULL) { + /* If no, or no more subdomains, end of level. */ + if (lev->npp == NULL || lev->npp == lev->npe) + break; + lev->nnp = *lev->npp++; + } + /* If we encountered the end of the level, we're outta here. */ + if ((np = lev->nnp) == NULL) + break; + /* Next time, we'll do the following NP, or the next chain. */ + lev->nnp = np->n_next; + /* Skip our own NP if it appears as a subdom (as in root). */ + if (np != lev->np) + sx_pushlev(qsp, np); + goto again; + } + default: + abort(); + } + + /* End of level. Pop it off the stack. */ + + if ((qsp->xfr.lev = sx_freelev(lev)) == NULL) { + /* End of topmost level. */ + qsp->xfr.state = s_x_lastsoa; + sq_writeh(qsp, sx_sendsoa); + return; + } + goto again; +} + +/* + * void + * sx_sendsoa(qsp) + * send either the first or last SOA needed for an AXFR. + * side effects: + * changes qsp->xfr.state. adds RR to output buffer. + */ +static void +sx_sendsoa(struct qstream *qsp) { + if (sx_soarr(qsp) == -1) + return; /* No state change, come back here later. */ + + switch (qsp->xfr.state) { + case s_x_firstsoa: { + /* Next thing to do is send the zone. */ + qsp->xfr.state = s_x_zone; + sq_writeh(qsp, sx_sendlev); + break; + } + case s_x_lastsoa: { + /* Next thing to do is go back and wait for another query. */ + (void)sx_flush(qsp); + qsp->xfr.state = s_x_done; + sq_writeh(qsp, sq_flushw); + break; + } + default: { + ns_panic(ns_log_xfer_out, 1, + "unexpected state %d in sx_sendsoa", qsp->xfr.state); + } + } +} + +/* int + * sx_pushlev(qsp, np) + * manage the decomposed recursion. set up for a new level (domain). + * returns: + * 0 = success + * -1 = failure (check errno) + */ +static int +sx_pushlev(struct qstream *qsp, struct namebuf *np) { + struct qs_x_lev *new = memget(sizeof *new); + struct hashbuf *htp; + + if (!new) { + errno = ENOMEM; + return (-1); + } + memset(new, 0, sizeof *new); + new->state = sxl_ns; + new->np = np; + new->dp = np->n_data; + getname(np, new->dname, sizeof new->dname); + /* + * 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 = ((new->dname[0] == '\0') ? hashtab : np->n_hash); + if (htp) { + new->npp = htp->h_tab; + new->npe = htp->h_tab + htp->h_size; + } else { + new->npp = NULL; + new->npe = NULL; + } + new->nnp = NULL; + new->next = qsp->xfr.lev; + qsp->xfr.lev = new; + return (0); +} + +/* + * qs_x_lev * + * sx_freelev(lev) + * free the memory occupied by a level descriptor + * return: + * pointer to "next" level descriptor + */ +static struct qs_x_lev * +sx_freelev(struct qs_x_lev *lev) { + struct qs_x_lev *next = lev->next; + + memput(lev, sizeof *lev); + return (next); +} diff --git a/contrib/bind/bin/named/pathnames.c b/contrib/bind/bin/named/pathnames.c new file mode 100644 index 0000000..2ba2415 --- /dev/null +++ b/contrib/bind/bin/named/pathnames.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * $Id: pathnames.c,v 8.5 1997/05/21 19:52:28 halley Exp $ + */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> + +#include "port_after.h" + +#include "named.h" + +int +main(int argc, char *argv[], char *envp[]) { + char *arg; + + argc--, argv++; + while (argc-- && (arg = *argv++) != NULL) + if (!strcasecmp("_PATH_XFER", arg)) + puts(_PATH_XFER); + else if (!strcasecmp("_PATH_PIDFILE", arg)) + puts(_PATH_PIDFILE); + else if (!strcasecmp("_PATH_NAMED", arg)) + puts(_PATH_NAMED); + else + exit(1); + exit(0); +} diff --git a/contrib/bind/bin/named/pathtemplate.h b/contrib/bind/bin/named/pathtemplate.h new file mode 100644 index 0000000..d339ef8 --- /dev/null +++ b/contrib/bind/bin/named/pathtemplate.h @@ -0,0 +1,70 @@ +/* + * $Id: pathtemplate.h,v 8.1 1998/03/19 19:53:21 halley Exp $ + */ + +/* + * Copyright (c) 1996, 1997 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include <paths.h> + +#ifndef _PATH_CONF +#define _PATH_CONF "%DESTETC%/named.conf" +#endif + +#ifndef _PATH_DEBUG +#define _PATH_DEBUG "named.run" +#endif + +#ifndef _PATH_DUMPFILE +#define _PATH_DUMPFILE "named_dump.db" +#endif + +#ifndef _PATH_NAMED +#define _PATH_NAMED "%DESTSBIN%/named" +#endif + +#ifndef _PATH_PIDFILE +#define _PATH_PIDFILE "%DESTRUN%/named.pid" +#endif + +#ifndef _PATH_STATS +#define _PATH_STATS "named.stats" +#endif + +#ifndef _PATH_MEMSTATS +#define _PATH_MEMSTATS "named.memstats" +#endif + +#ifndef _PATH_TMPXFER +#define _PATH_TMPXFER "xfer.ddt.XXXXXX" +#endif + +#ifndef _PATH_XFER +#define _PATH_XFER "%DESTEXEC%/named-xfer" +#endif + +#ifndef _PATH_XFERTRACE +#define _PATH_XFERTRACE "xfer.trace" +#endif + +#ifndef _PATH_XFERDDT +#define _PATH_XFERDDT "xfer.ddt" +#endif + +#ifndef _PATH_DEVNULL +#define _PATH_DEVNULL "/dev/null" +#endif diff --git a/contrib/bind/bin/named/test/127.0.0.zone b/contrib/bind/bin/named/test/127.0.0.zone new file mode 100644 index 0000000..b9b7bf5 --- /dev/null +++ b/contrib/bind/bin/named/test/127.0.0.zone @@ -0,0 +1,11 @@ +$ORIGIN 0.0.127.in-addr.arpa. + +@ 1D IN SOA localhost. root.localhost. ( + 42 ; serial (d. adams) + 3H ; refresh + 15M ; retry + 1W ; expiry + 1D ) ; minimum + + 1D IN NS localhost. +1 1D IN PTR localhost. diff --git a/contrib/bind/bin/named/test/localhost.zone b/contrib/bind/bin/named/test/localhost.zone new file mode 100644 index 0000000..ad5e68e --- /dev/null +++ b/contrib/bind/bin/named/test/localhost.zone @@ -0,0 +1,10 @@ +$ORIGIN localhost. +@ 1D IN SOA @ root ( + 42 ; serial (d. adams) + 3H ; refresh + 15M ; retry + 1W ; expiry + 1D ) ; minimum + + 1D IN NS @ + 1D IN A 127.0.0.1 diff --git a/contrib/bind/bin/named/test/named.conf b/contrib/bind/bin/named/test/named.conf new file mode 100644 index 0000000..b852604 --- /dev/null +++ b/contrib/bind/bin/named/test/named.conf @@ -0,0 +1,29 @@ +// This is a configuration file for named (from BIND 8.1 or later). +// It would normally be installed as /etc/named.conf. + +options { +// directory "/var/named"; + check-names master warn; /* default. */ + datasize 20M; +}; + +zone "localhost" IN { + type master; + file "localhost.zone"; + check-names fail; + allow-update { none; }; + allow-transfer { any; }; +}; + +zone "0.0.127.in-addr.arpa" IN { + type master; + file "127.0.0.zone"; + check-names fail; + allow-update { none; }; + allow-transfer { any; }; +}; + +zone "." IN { + type hint; + file "root.hint"; +}; diff --git a/contrib/bind/bin/named/test/root.hint b/contrib/bind/bin/named/test/root.hint new file mode 100644 index 0000000..0b8f372 --- /dev/null +++ b/contrib/bind/bin/named/test/root.hint @@ -0,0 +1,37 @@ + +; <<>> DiG 2.2 <<>> @192.5.5.241 +; (1 server found) +;; res options: init recurs defnam dnsrch +;; got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10 +;; flags: qr aa rd; QUERY: 1, ANSWER: 9, AUTHORITY: 0, ADDITIONAL: 9 +;; QUERY SECTION: +;; ., type = NS, class = IN + +;; ANSWER SECTION: +. 6D IN NS C.ROOT-SERVERS.NET. +. 6D IN NS D.ROOT-SERVERS.NET. +. 6D IN NS E.ROOT-SERVERS.NET. +. 6D IN NS I.ROOT-SERVERS.NET. +. 6D IN NS F.ROOT-SERVERS.NET. +. 6D IN NS G.ROOT-SERVERS.NET. +. 6D IN NS A.ROOT-SERVERS.NET. +. 6D IN NS H.ROOT-SERVERS.NET. +. 6D IN NS B.ROOT-SERVERS.NET. + +;; ADDITIONAL SECTION: +C.ROOT-SERVERS.NET. 5w6d16h IN A 192.33.4.12 +D.ROOT-SERVERS.NET. 5w6d16h IN A 128.8.10.90 +E.ROOT-SERVERS.NET. 5w6d16h IN A 192.203.230.10 +I.ROOT-SERVERS.NET. 5w6d16h IN A 192.36.148.17 +F.ROOT-SERVERS.NET. 5w6d16h IN A 192.5.5.241 +G.ROOT-SERVERS.NET. 5w6d16h IN A 192.112.36.4 +A.ROOT-SERVERS.NET. 5w6d16h IN A 198.41.0.4 +H.ROOT-SERVERS.NET. 5w6d16h IN A 128.63.2.53 +B.ROOT-SERVERS.NET. 5w6d16h IN A 128.9.0.107 + +;; Total query time: 8 msec +;; FROM: wisdom.home.vix.com to SERVER: 192.5.5.241 +;; WHEN: Fri Nov 22 00:08:05 1996 +;; MSG SIZE sent: 17 rcvd: 312 + diff --git a/contrib/bind/bin/named/version.c b/contrib/bind/bin/named/version.c new file mode 100644 index 0000000..9468be2 --- /dev/null +++ b/contrib/bind/bin/named/version.c @@ -0,0 +1,89 @@ +/* + * @(#)Version.c 4.9 (Berkeley) 7/21/90 + * $Id: version.c,v 8.2 1997/04/24 23:59:02 vixie Exp $ + */ + +#ifndef lint +char sccsid[] = "@(#)named %VERSION% %WHEN% %WHOANDWHERE%"; +char rcsid[] = "$Id: version.c,v 8.2 1997/04/24 23:59:02 vixie Exp $"; +#endif /* not lint */ + +char Version[] = "named %VERSION% %WHEN%\n\t%WHOANDWHERE%"; +char ShortVersion[] = "%VERSION%"; + +#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 */ |