summaryrefslogtreecommitdiffstats
path: root/contrib/bind/bin/named/db_load.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bind/bin/named/db_load.c')
-rw-r--r--contrib/bind/bin/named/db_load.c2218
1 files changed, 2218 insertions, 0 deletions
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
OpenPOWER on IntegriCloud