#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.22 1996/08/05 08:31:30 vixie Exp $";
#endif /* not lint */

/*
 * ++Copyright++ 1986, 1988, 1990
 * -
 * Copyright (c) 1986, 1988, 1990
 *    The Regents of the University of California.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 * 	This product includes software developed by the University of
 * 	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * -
 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies, and that
 * the name of Digital Equipment Corporation not be used in advertising or
 * publicity pertaining to distribution of the document or software without
 * specific, written prior permission.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 * -
 * --Copyright--
 */

/*
 * Load data base from ascii backupfile.  Format similar to RFC 883.
 */

#include <sys/types.h>
#ifndef WINNT
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <stdio.h>
#ifndef WINNT
#include <syslog.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <ctype.h>
#include <netdb.h>
#include <resolv.h>
#include <errno.h>

#include "named.h"

static int		gettoken __P((register FILE *, const char *)),
			getnonblank __P((FILE *, const char *)),
			getprotocol __P((FILE *, const char *)),
			getservices __P((int, char *, FILE *, const char *));
static void		makename __P((char *, const char *));
static int		makename_ok __P((char *name, const char *origin,
					 int class,
					 enum transport transport,
					 enum context context,
					 const char *filename, int lineno));

static int		empty_token = 0;
int	getnum_error;
u_int inet_nsap_addr();
int inet_pton();

/*
 * Map class and type names to number
 */
struct map {
	char	token[8];
	int	val;
};

struct map m_class[] = {
	{ "in",		C_IN },
#ifdef notdef
	{ "any",	C_ANY },	/* any is a QCLASS, not CLASS */
#endif
	{ "chaos",	C_CHAOS },
	{ "hs",		C_HS },
};
#define M_CLASS_CNT (sizeof(m_class) / sizeof(struct map))

struct map m_type[] = {
	{ "a",		T_A },
	{ "ns",		T_NS },
	{ "cname",	T_CNAME },
	{ "soa",	T_SOA },
	{ "mb",		T_MB },
	{ "mg",		T_MG },
	{ "mr",		T_MR },
	{ "null",	T_NULL },
	{ "wks",	T_WKS },
	{ "ptr",	T_PTR },
	{ "hinfo",	T_HINFO },
	{ "minfo",	T_MINFO },
	{ "mx",		T_MX },
	{ "uinfo",	T_UINFO },
	{ "txt",	T_TXT },
	{ "rp",		T_RP },
	{ "afsdb",	T_AFSDB },
	{ "x25",	T_X25 },
	{ "isdn",	T_ISDN },
	{ "rt",		T_RT },
	{ "nsap",	T_NSAP },
	{ "nsap_ptr",	T_NSAP_PTR },
	{ "uid",	T_UID },
	{ "gid",	T_GID },
	{ "px",		T_PX },
	{ "aaaa",	T_AAAA },
#ifdef notdef
	{ "any",	T_ANY },	/* any is a QTYPE, not TYPE */
#endif
#ifdef LOC_RR
	{ "loc",	T_LOC },
#endif /* LOC_RR */
#ifdef ALLOW_T_UNSPEC
	{ "unspec",	T_UNSPEC },
#endif /* ALLOW_T_UNSPEC */
};
#define M_TYPE_CNT (sizeof(m_type) / sizeof(struct map))

/*
 * Parser token values
 */
#define CURRENT	1
#define DOT	2
#define AT	3
#define DNAME	4
#define INCLUDE	5
#define ORIGIN	6
#define ERROR	7

static int clev;	/* a zone deeper in a heirachy has more credability */

#define MAKENAME_OK(N)	if (!makename_ok(N, origin, class, \
					 transport, context, \
					 filename, lineno)) { \
				errs++; \
				sprintf(buf, "bad name \"%s\"", N); \
				goto err; \
			}

/* 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(filename, in_origin, zp, def_domain)
	const char *filename, *in_origin;
	struct zoneinfo *zp;
	const char *def_domain;
{
	static int read_soa, read_ns, rrcount;
	register char *cp;
	register struct map *mp;
	char domain[MAXDNAME];
	char origin[MAXDNAME];
	char tmporigin[MAXDNAME];
	char buf[MAXDATA];
	char data[MAXDATA];
	const char *cp1, *op;
	int c, class, type, dbflags, dataflags, multiline;
	u_int32_t ttl;
	struct databuf *dp;
	FILE *fp;
	int slineno, i, errs, didinclude;
	register u_int32_t n;
#ifndef WINNT
	struct stat sb;
#else
	struct _stat sb;
#endif
	struct in_addr ina;
	int escape;
	enum transport transport;
	enum context context;
#ifdef DO_WARN_SERIAL
	u_int32_t serial;
#endif

	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 = db_getclev(in_origin);
	}

	dprintf(1, (ddt,"db_load(%s, %s, %d, %s)\n",
		    filename, in_origin, zp - zones,
		    def_domain ? def_domain : "Nil"));

	(void) strcpy(origin, in_origin);
	if ((fp = fopen(filename, "r")) == NULL) {
		syslog(LOG_WARNING, "%s: %m", filename);
		dprintf(1, (ddt, "db_load: error opening file %s \n",
			    filename));
		return (-1);
	}
	if (zp->z_type == Z_CACHE) {
		dbflags = DB_NODATA | DB_NOHINTS;
		dataflags = DB_F_HINT;
#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);
#ifndef WINNT
	if (fstat(fileno(fp), &sb) < 0) {
#else
	if (_fstat(fileno(fp), &sb) < 0) {
#endif
		syslog(LOG_WARNING, "%s: %m", filename);
		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((char *)buf, sizeof(buf), fp, 0))
				/* file name*/
				break;
			if (!getword(tmporigin, sizeof(tmporigin), fp, 1))
				strcpy(tmporigin, origin);
			else {
				makename(tmporigin, origin);
				endline(fp);
			}
			didinclude = 1;
			errs += db_load((char *)buf, tmporigin, zp, domain);
			continue;

		case ORIGIN:
			(void) strcpy((char *)buf, origin);
			if (!getword(origin, sizeof(origin), fp, 1))
				break;
			dprintf(3, (ddt, "db_load: origin %s, buf %s\n",
				    origin, buf));
			makename(origin, buf);
			dprintf(3, (ddt, "db_load: origin now %s\n", origin));
			continue;

		case DNAME:
			if (!getword(domain, sizeof(domain), fp, 1))
				break;
			n = strlen(domain) - 1;
			if (domain[n] == '.')
				domain[n] = '\0';
			else if (*origin) {
				(void) strcat(domain, ".");
				(void) strcat(domain, origin);
			}
			goto gotdomain;

		case AT:
			(void) strcpy(domain, origin);
			goto gotdomain;

		case DOT:
			domain[0] = '\0';
			/* FALLTHROUGH */
		case CURRENT:
		gotdomain:
			if (!getword((char *)buf, sizeof(buf), fp, 0)) {
				if (c == CURRENT)
					continue;
				break;
			}
			cp = buf;
			ttl = USE_MINIMUM;
			if (isdigit(*cp)) {
				n = 0;
				do {
				    if (n > (((u_int32_t)INT_MAX - (u_int32_t)(*cp - '0')) / (u_int32_t)10)) {
					syslog(LOG_INFO, 
					   "%s: line %d: number > %lu\n",
					   filename, lineno, (u_long)INT_MAX);
					n = INT_MAX;
					cp++;
				    } else
  					n = n * 10 + (*cp++ - '0');
				}
				while (isdigit(*cp));
				if (zp->z_type == Z_CACHE) {
				    /* this allows the cache entry to age */
				    /* while sitting on disk (powered off) */
				    if (n > (u_int32_t)max_cache_ttl)
					n = max_cache_ttl;
				    n += sb.st_mtime;
				}
				ttl = n;
				if (!getword((char *)buf, sizeof(buf), fp, 0))
					break;
			}
			for (mp = m_class; mp < m_class+M_CLASS_CNT; mp++)
				if (!strcasecmp((char *)buf, mp->token)) {
					class = mp->val;
					(void) getword((char *)buf,
						       sizeof(buf), fp, 0);
					break;
				}
			for (mp = m_type; mp < m_type+M_TYPE_CNT; mp++)
				if (!strcasecmp((char *)buf, mp->token)) {
					type = mp->val;
					goto fndtype;
				}
			dprintf(1, (ddt, "%s: Line %d: Unknown type: %s.\n",
				    filename, lineno, buf));
			errs++;
 			syslog(LOG_NOTICE, "%s: Line %d: Unknown type: %s.\n",
				filename, lineno, buf);
			break;
		fndtype:
			context = ns_ownercontext(type, transport);
			if (!ns_nameok(domain, class, transport, context)) {
				errs++;
				syslog(LOG_NOTICE,
				       "%s:%d: owner name error\n",
				       filename, lineno);
				break;
			}
#ifdef ALLOW_T_UNSPEC
			/* Don't do anything here for T_UNSPEC...
			 * read input separately later
			 */
                        if (type != T_UNSPEC) {
#endif
			context = domain_ctx;
			switch (type) {
			case T_SOA:
			case T_MINFO:
			case T_RP:
			case T_NS:
			case T_CNAME:
			case T_MB:
			case T_MG:
			case T_MR:
			case T_PTR:
				escape = 1;
				break;
			default:
				escape = 0;
			}
			    if (!getword((char *)buf, sizeof(buf), fp, escape))
				break;
			    dprintf(3,
				    (ddt,
				     "d='%s', c=%d, t=%d, ttl=%d, data='%s'\n",
				     domain, class, type, ttl, buf));
#ifdef ALLOW_T_UNSPEC
                        }
#endif
			/*
			 * Convert the ascii data 'buf' to the proper format
			 * based on the type and pack into 'data'.
			 */
			switch (type) {
			case T_A:
				if (!inet_aton(buf, &ina))
					goto err;
				n = ntohl(ina.s_addr);
				cp = data;
				PUTLONG(n, cp);
				n = INT32SZ;
				break;

			case T_HINFO:
			case T_ISDN:
				n = strlen((char *)buf);
				if (n > 255) {
				    syslog(LOG_INFO,
					"%s: line %d: %s too long",
					filename, lineno, (type == T_ISDN) ?
					"ISDN-address" : "CPU type");
				    n = 255;
				}
				data[0] = n;
				bcopy(buf, (char *)data + 1, (int)n);
				if (n == 0)
					goto err;
				n++;
				if (!getword((char *)buf, sizeof(buf), fp, 0))
					i = 0;
				else {
					endline(fp);
					i = strlen((char *)buf);
				}
				if (i == 0) {
					if (type == T_ISDN) {
						data[n++] = 0;
						break;
					}
					else
						/* goto err; */
						    /* XXX tolerate for now */
						data[n++] = 1;
						data[n++] = '?';
						syslog(LOG_INFO,
						    "%s: line %d: OS-type missing",
						    filename,
						    empty_token ? (lineno - 1) : lineno);
						break;
				}
				if (i > 255) {
				    syslog(LOG_INFO,
					   "%s:%d: %s too long",
					   filename, lineno, (type == T_ISDN) ?
					   "ISDN-sa" : "OS type");
				    i = 255;
				}
				data[n] = i;
				bcopy(buf, data + n + 1, i);
				n += i + 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:
				(void) strcpy((char *)data, (char *)buf);

				MAKENAME_OK(data);
				cp = data + strlen((char *)data) + 1;
				if (!getword((char *)cp,
					     (sizeof data) - (cp - data),
					     fp, 1))
					goto err;
				if (type == T_RP)
					context = domain_ctx;
				else
					context = mailname_ctx;
				MAKENAME_OK(cp);
				cp += strlen((char *)cp) + 1;
				if (type != T_SOA) {
					n = cp - data;
					break;
				}
				if (class != zp->z_class) {
					errs++;
					syslog(LOG_INFO,
					       "%s:%d: %s",
					       filename, lineno,
					       "SOA class not same as zone's");
				}
				if (strcasecmp(zp->z_origin, domain) != 0) {
					errs++;
					syslog(LOG_ERR,
			"%s: line %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);
				}
#ifdef DO_WARN_SERIAL
				serial = zp->z_serial;
#endif
				zp->z_serial = getnum(fp, filename,
						      GETNUM_SERIAL);
				if (getnum_error)
					errs++;
				n = (u_int32_t) zp->z_serial;
				PUTLONG(n, cp);
#ifdef DO_WARN_SERIAL
				if (serial && SEQ_GT(serial, zp->z_serial)) {
					syslog(LOG_NOTICE,
			"%s:%d: WARNING: new serial number < old (%lu < %lu)",
						filename , lineno,
						zp->z_serial, serial);
				}
#endif
				zp->z_refresh = getnum(fp, filename,
						       GETNUM_NONE);
				if (getnum_error) {
					errs++;
					zp->z_refresh = INIT_REFRESH;
				}
				n = (u_int32_t) zp->z_refresh;
				PUTLONG(n, cp);
				if (zp->z_type == Z_SECONDARY
#if defined(STUBS) 
				    || zp->z_type == Z_STUB
#endif
				    ) {
					ns_refreshtime(zp, MIN(sb.st_mtime,
							       tt.tv_sec));
				}
				zp->z_retry = getnum(fp, filename,
						     GETNUM_NONE);
				if (getnum_error) {
					errs++;
					zp->z_retry = INIT_REFRESH;
				}
				n = (u_int32_t) zp->z_retry;
				PUTLONG(n, cp);
				zp->z_expire = getnum(fp, filename,
						      GETNUM_NONE);
				if (getnum_error) {
					errs++;
					zp->z_expire = INIT_REFRESH;
				}
				n = (u_int32_t) zp->z_expire;
				PUTLONG (n, cp);
				zp->z_minimum = getnum(fp, filename,
						       GETNUM_NONE);
				if (getnum_error) {
					errs++;
					zp->z_minimum = 120;
				}
				n = (u_int32_t) zp->z_minimum;
				PUTLONG (n, cp);
				n = cp - data;
				if (multiline) {
					if (getnonblank(fp, filename) != ')')
						goto err;
				}
                                read_soa++;
#ifndef WINNT
				if (zp->z_expire < zp->z_refresh ) {
#else
				if ((u_int32_t)zp->z_expire < zp->z_refresh ) {
#endif
				    syslog(LOG_WARNING,
    "%s: WARNING SOA expire value is less then SOA refresh (%lu < %lu)",
				    filename, zp->z_expire, zp->z_refresh);
				}
				endline(fp);
				break;

			case T_UID:
			case T_GID:
				n = 0;
				cp = buf;
				while (isdigit(*cp))
					n = n * 10 + (*cp++ - '0');
				if (cp == buf)
					goto err;
				cp = data;
				PUTLONG(n, cp);
				n = INT32SZ;
				break;

			case T_WKS:
				/* Address */
				if (!inet_aton(buf, &ina))
					goto err;
				n = ntohl(ina.s_addr);
				cp = data;
				PUTLONG(n, cp);
				*cp = (char)getprotocol(fp, filename);
				/* Protocol */
				n = INT32SZ + sizeof(char);
				/* Services */
				n = getservices((int)n, data, fp, filename);
				break;

			case T_NS:
				if (strcasecmp(zp->z_origin, domain) == 0)
					read_ns++;
				context = hostname_ctx;
				goto cname_etc;
			case T_CNAME:
			case T_MB:
			case T_MG:
			case T_MR:
				context = domain_ctx;
				goto cname_etc;
			case T_PTR:
				context = ns_ptrcontext(domain);
			cname_etc:
				(void) strcpy((char *)data, (char *)buf);
				MAKENAME_OK(data);
				n = strlen((char *)data) + 1;
				break;

			case T_UINFO:
				cp = strchr((char *)buf, '&');
				bzero(data, sizeof data);
				if ( cp != NULL) {
					(void) strncpy((char *)data,
					    (char *)buf, cp - buf);
					op = strchr(domain, '.');
					if ( op != NULL)
					    (void) strncat((char *)data,
						domain,op-domain);
					else
						(void) strcat((char *)data,
						    domain);
					(void) strcat((char *)data,
					    (char *)++cp);
				} else
					(void) strcpy((char *)data,
					    (char *)buf);
				n = strlen((char *)data) + 1;
				break;
			case T_MX:
			case T_AFSDB:
			case T_RT:
				n = 0;
				cp = buf;
				while (isdigit(*cp))
					n = n * 10 + (*cp++ - '0');
				/* catch bad values */
				if ((cp == buf) || (n > 65535))
					goto err;

				cp = data;
				PUTSHORT((u_int16_t)n, cp);

				if (!getword((char *)buf, sizeof(buf), fp, 1))
					goto err;
				(void) strcpy((char *)cp, (char *)buf);
				context = hostname_ctx;
				MAKENAME_OK(cp);
				/* advance pointer to end of data */
				cp += strlen((char *)cp) +1;

				/* now save length */
				n = (cp - data);
				break;

			case 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))
					goto err;
				cp = data;
				PUTSHORT((u_int16_t)n, cp);

				if (!getword((char *)buf, sizeof(buf), fp, 0))
					goto err;
				(void) strcpy((char *)cp, (char *)buf);
				MAKENAME_OK(cp);
				/* advance pointer to next field */
				cp += strlen((char *)cp) +1;
				if (!getword((char *)buf, sizeof(buf), fp, 0))
					goto err;
				(void) strcpy((char *)cp, (char *)buf);
				MAKENAME_OK(cp);
				/* advance pointer to end of data */
				cp += strlen((char *)cp) + 1;

				/* now save length */
				n = (cp - data);
				break;

			case T_TXT:
			case T_X25:
				i = strlen((char *)buf);
				cp = data;
				cp1 = buf;
				/*
				 * there is expansion here so make sure we
				 * don't overflow data
				 */
				if (i > (sizeof data) * 255 / 256) {
				    syslog(LOG_INFO,
					"%s: line %d: TXT record truncated",
					filename, lineno);
				    i = (sizeof data) * 255 / 256;
				}
				while (i > 255) {
				    *cp++ = (char)255;
				    bcopy(cp1, cp, 255);
				    cp += 255;
				    cp1 += 255;
				    i -= 255;
				}
				*cp++ = i;
				bcopy(cp1, cp, i);
				cp += i;
				n = cp - data;
				endline(fp);
				break;

			case T_NSAP:
				n = inet_nsap_addr(buf, (u_char *)data,
						   sizeof data);
				if (n == 0)
					goto err;
				endline(fp);
				break;
			case T_AAAA:
				if (inet_pton(AF_INET6, buf, data) <= 0)
					goto err;
				n = IN6ADDRSZ;
				endline(fp);
				break;
#ifdef LOC_RR
			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') /* 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;
#endif /* LOC_RR */
#ifdef ALLOW_T_UNSPEC
                        case T_UNSPEC:
                                {
                                    int rcode;
                                    fgets(buf, sizeof(buf), fp);
				    dprintf(1, (ddt, "loading T_UNSPEC\n"));
				    if (rcode = atob(buf,
						     strlen((char*)buf),
						     data, sizeof data,
						     &n)) {
					if (rcode == CONV_OVERFLOW) {
						errs++;
						syslog(LOG_INFO,
				       "Load T_UNSPEC: input buffer overflow");
					} else {
						errs++;
						syslog(LOG_INFO,
				     "Load T_UNSPEC: Data in bad atob format");
					}
                                    }
                                }
                                break;
#endif /* ALLOW_T_UNSPEC */

			default:
				goto err;
			}
#ifndef PURGE_ZONE
#ifdef STUBS
			if (type == T_SOA && zp->z_type == Z_STUB)
				continue;
#endif
#endif
#ifdef NO_GLUE
			/*
			 * Ignore data outside the zone.
			 */
			if (zp->z_type != Z_CACHE &&
			    !samedomain(domain, zp->z_origin))
			{
				syslog(LOG_INFO,
			    "%s:%d: data \"%s\" outside zone \"%s\" (ignored)",
				       filename, lineno, domain, zp->z_origin);
				continue;
			}
#endif /*NO_GLUE*/
			dp = savedata(class, type, (u_int32_t)ttl,
				      (u_char *)data, (int)n);
			dp->d_zone = zp - zones;
			dp->d_flags = dataflags;
			dp->d_cred = DB_C_ZONE;
			dp->d_clev = clev;
			if ((c = db_update(domain, dp, dp, dbflags,
					   (dataflags & DB_F_HINT)
					   ? fcachetab
					   : hashtab))
			    != OK) {
#ifdef DEBUG
				if (debug && (c != DATAEXISTS))
					fprintf(ddt, "update failed %s %d\n", 
						domain, type);
#endif
				free((char*) dp);
			} else {
				rrcount++;
			}
			continue;

		case ERROR:
			break;
		}
 err:
		errs++;
		syslog(LOG_NOTICE, "%s: line %d: database format error (%s)",
			filename, empty_token ? (lineno - 1) : lineno, 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++;
				syslog(LOG_WARNING,
				       "Zone \"%s\" (file %s): %s",
				       zp->z_origin, filename, msg);
			}
		}
	}
#ifdef SECURE_ZONES
	build_secure_netlist(zp);
#endif
	if (!def_domain)
		syslog(errs ? LOG_WARNING : LOG_INFO,
		       "%s zone \"%s\" %s (serial %lu)",
		       zoneTypeString(zp), zp->z_origin,
		       errs ? "rejected due to errors" : "loaded",
		       (u_long)zp->z_serial);
	if (errs)
		zp->z_flags |= Z_DB_BAD;
#ifdef BIND_NOTIFY
	/* XXX: this needs to be delayed, both according to the spec, and
	 *	because the metadata needed by sysnotify() (and its sysquery())
	 *	could be in other zones that we (at startup) havn't loaded yet.
	 */
	if (!errs && !def_domain &&
	    (zp->z_type == Z_PRIMARY || zp->z_type == Z_SECONDARY))
		sysnotify(zp->z_origin, zp->z_class, T_SOA);
#endif
	return (errs);
}

static int
gettoken(fp, src)
	register FILE *fp;
	const char *src;
{
	register int c;
	char op[32];

	for (;;) {
		c = getc(fp);
	top:
		switch (c) {
		case EOF:
			return (EOF);

		case '$':
			if (getword(op, sizeof(op), fp, 0)) {
				if (!strcasecmp("include", op))
					return (INCLUDE);
				if (!strcasecmp("origin", op))
					return (ORIGIN);
			}
			syslog(LOG_NOTICE,
			       "%s: line %d: Unknown $ option: $%s\n", 
			       src, lineno, op);
			return (ERROR);

		case ';':
			while ((c = getc(fp)) != EOF && c != '\n')
				;
			goto top;

		case ' ':
		case '\t':
			return (CURRENT);

		case '.':
			return (DOT);

		case '@':
			return (AT);

		case '\n':
			lineno++;
			continue;

		default:
			(void) ungetc(c, fp);
			return (DNAME);
		}
	}
}

/* int
 * getword(buf, size, fp, 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
 *	1 = word was read
 */
int
getword(buf, size, fp, preserve)
	char *buf;
	int size;
	FILE *fp;
	int preserve;
{
	register char *cp = buf;
	register 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')
				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 &&
					    (c == '\\' || c == '.')) {
						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 && (c == '\\' || c == '.')) {
				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')
				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);
}

/*
From: kagotani@cs.titech.ac.jp
Message-Id: <9007040716.AA26646@saeko.cs.titech.ac.jp>
Subject: named bug report and fix
Date: Wed, 04 Jul 90 16:16:52 JST

I found a bug in the BIND source code. Named with this bug parses
the serial_no field of SOA records incorrectly. For example:
        expression      internal
        in files        expression      I expect
        1.              1000            10000
        1.2             10002           10002
        1.23            100023          10023
        2.3             20003           20003
Especially I can not accept that "2.3" is treated as if it is
smaller than "1.23" in their internal expressions.

[ if you define SENSIBLE_DOTS in ../conf/options.h, you get
  m. kagotani's expected behaviour.  this is NOT compatible
  with pre-4.9 versions of BIND.  --vix ]
*/

int
getnum(fp, src, opt)
	FILE *fp;
	const char *src;
	int opt;
{
	register 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) {
				syslog(LOG_NOTICE, "%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) {
		syslog(LOG_INFO, 
		       "%s:%d: number after the decimal point exceeds 9999", 
		       src, lineno);
		getnum_error = 1;
		return (0);
	}
	if (seendecimal) {
		syslog(LOG_INFO,
		       "%s:%d: decimal serial number interpreted as %d",
		       src, lineno, n+m);
	}
	return (n + m);
}

static int
getnonblank(fp, src)
	FILE *fp;
	const char *src;
{
	register int c;

	while ( (c = getc(fp)) != EOF ) {
		if (isspace(c)) {
			if (c == '\n')
				lineno++;
			continue;
		}
		if (c == ';') {
			while ((c = getc(fp)) != EOF && c != '\n')
				;
			if (c == '\n')
				lineno++;
			continue;
		}
		return(c);
	}
	syslog(LOG_INFO, "%s: line %d: unexpected EOF", src, lineno);
	return (EOF);
}

/*
 * Take name and fix it according to following rules:
 * "." means root.
 * "@" means current origin.
 * "name." means no changes.
 * "name" means append origin.
 */
static void
makename(name, origin)
	char *name;
	const char *origin;
{
	int n;

	if (origin[0] == '.')
		origin++;
	n = strlen(name);
	if (n == 1) {
		if (name[0] == '.') {
			name[0] = '\0';
			return;
		}
		if (name[0] == '@') {
			(void) strcpy(name, origin);
			return;
		}
	}
	if (n > 0) {
		if (name[n - 1] == '.')
			name[n - 1] = '\0';
		else if (origin[0] != '\0') {
			name[n] = '.';
			(void) strcpy(name + n + 1, origin);
		}
	}
}

static int
makename_ok(name, origin, class, transport, context, filename, lineno)
	char *name;
	const char *origin;
	int class;
	enum transport transport;
	enum context context;
	const char *filename;
	int lineno;
{
	int ret = 1;

	makename(name, origin);
	if (!ns_nameok(name, class, transport, context)) {
		syslog(LOG_INFO, "%s:%d: database naming error\n",
		       filename, lineno);
		ret = 0;
	}
	return (ret);
}

void
endline(fp)
	register FILE *fp;
{
	register 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

static int
getprotocol(fp, src)
	FILE *fp;
	const char *src;
{
	int  k;
	char b[MAXLEN];

	(void) getword(b, sizeof(b), fp, 0);
		
	k = protocolnumber(b);
	if (k == -1)
		syslog(LOG_INFO, "%s: line %d: unknown protocol: %s.",
		       src, lineno, b);
	return(k);
}

static int
getservices(n, data, fp, src)
	int n;
	char *data;
	FILE *fp;
	const char *src;
{
	int j, ch;
	int k;
	int maxl;
	int bracket;
	char b[MAXLEN];
	char bm[MAXPORT/8];

	for (j = 0; j < MAXPORT/8; j++)
		bm[j] = 0;
	maxl = 0;
	bracket = 0;
	while (getword(b, sizeof(b), fp, 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')
				;
			if (ch == '\n')
				lineno++;
			break;
		}
		k = servicenumber(b);
		if (k == -1) {
			syslog(LOG_INFO,
			       "%s: line %d: Unknown service '%s'",
			       src, lineno, b);
			continue;
		}
		if ((k < MAXPORT) && (k)) {
			bm[k/8] |= (0x80>>(k%8));
			if (k > maxl)
				maxl=k;
		}
		else {
			syslog(LOG_INFO,
			       "%s: line %d: port no. (%d) too big\n",
			       src, lineno, k);
			dprintf(1, (ddt,
				    "%s: line %d: port no. (%d) too big\n",
				    src, lineno, k));
		}
	}
	if (bracket)
		syslog(LOG_INFO, "%s: line %d: missing close paren\n",
		       src, lineno);
	maxl = maxl/8+1;
	bcopy(bm, data+n, maxl);
	return (maxl+n);
}

/* get_netlist(fp, netlistp, allow)
 *	get list of nets from 'fp', put on *netlistp, 'allow' controls
 *	whether hosts, nets, or both shall be accepted without warnings.
 *	(note that they are always accepted; 'allow' just controls the
 *	warnings.)
 */
void
get_netlist(fp, netlistp, allow, print_tag)
	FILE *fp;
	struct netinfo **netlistp;
	int allow;
	char *print_tag;
{
	struct netinfo *ntp, **end;
	char buf[BUFSIZ], *maskp;
	struct in_addr ina;

	for (end = netlistp; *end; end = &(**end).next)
		;
	ntp = NULL;
	dprintf(1, (ddt, "get_netlist(%s)", print_tag));
	while (getword(buf, sizeof(buf), fp, 0)) {
		if (strlen(buf) == 0)
			break;
		if ((maskp = strchr(buf, '&')) != NULL)
			*maskp++ = '\0';
		dprintf(1, (ddt," %s", buf));
		if (!ntp) {
			ntp = (struct netinfo *)malloc(sizeof(struct netinfo));
			if (!ntp)
				panic(errno, "malloc(netinfo)");
		}
		if (!inet_aton(buf, &ntp->my_addr)) {
			syslog(LOG_INFO, "%s contains bogus element (%s)",
			       print_tag, buf);
			continue;	
		}
		if (maskp) {
			if (!inet_aton(maskp, &ina)) {
				syslog(LOG_INFO,
				       "%s element %s has bad mask (%s)",
				       print_tag, buf, maskp);
				continue;
			}
		} else {
			if (allow & ALLOW_HOSTS)
				ina.s_addr = 0xffffffff;	/* "exact" */
			else
				ina.s_addr = net_mask(ntp->my_addr);
		}
		ntp->next = NULL;
		ntp->mask = ina.s_addr;
		ntp->addr = ntp->my_addr.s_addr & ntp->mask;

		/* Check for duplicates */
		if (addr_on_netlist(ntp->my_addr, *netlistp))
			continue;

		if (ntp->addr != ntp->my_addr.s_addr) {
			ina.s_addr = ntp->addr;
			syslog(LOG_INFO,
			       "%s element (%s) mask problem (%s)",
				print_tag, buf, inet_ntoa(ina));
		}

		*end = ntp;
		end = &ntp->next;
		ntp = NULL;
	}
	if (ntp)
		free((char *)ntp);
	
	dprintf(1, (ddt, "\n"));
#ifdef DEBUG
	if (debug > 2)
		for (ntp = *netlistp;  ntp != NULL;  ntp = ntp->next) {
			fprintf(ddt, "ntp x%lx addr x%lx mask x%lx",
				(u_long)ntp, (u_long)ntp->addr,
				(u_long)ntp->mask);
			fprintf(ddt, " my_addr x%lx",
				(u_long)ntp->my_addr.s_addr);
			fprintf(ddt, " %s", inet_ntoa(ntp->my_addr));
			fprintf(ddt, " next x%lx\n", (u_long)ntp->next);
		}
#endif
}

struct netinfo *
addr_on_netlist(addr, netlist)
	struct in_addr	addr;
	struct netinfo	*netlist;
{
	u_int32_t	a = addr.s_addr;
	struct netinfo	*t;

	for (t = netlist;  t != NULL;  t = t->next)
		if (t->addr == (a & t->mask))
			return t;
	return NULL;
}

int
position_on_netlist(addr, netlist)
	struct in_addr	addr;
	struct netinfo	*netlist;
{
	u_int32_t	a = addr.s_addr;
	struct netinfo	*t;
	int		position = 0;

	for (t = netlist;  t != NULL;  t = t->next)
		if (t->addr == (a & t->mask))
			break;
		else
			position++;
	return position;
}

void
free_netlist(netlistp)
	struct netinfo **netlistp;
{
	register struct netinfo *ntp, *next;

	for (ntp = *netlistp;  ntp != NULL;  ntp = next) {
		next = ntp->next;
		free((char *)ntp);
	}
	*netlistp = NULL;
}
