/* * Copyright (c) 1986, 1988, 1990 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. */ #ifndef lint /*static char sccsid[] = "from: @(#)db_load.c 4.38 (Berkeley) 3/2/91";*/ static char rcsid[] = "$Id: db_load.c,v 1.1 1995/10/18 08:47:49 deraadt Exp $"; #endif /* not lint */ /* * Load data base from ascii backupfile. Format similar to RFC 883. */ #include #include #include #include #include #include #include #include #include #include "ns.h" #include "db.h" extern char *index(); extern int max_cache_ttl; /* * Map class and type names to number */ struct map { char token[8]; int val; }; struct map m_class[] = { "in", C_IN, #ifdef notdef "any", C_ANY, /* any is a QCLASS, not CLASS */ #endif "chaos", C_CHAOS, "hs", C_HS, }; #define NCLASS (sizeof(m_class)/sizeof(struct map)) struct map m_type[] = { "a", T_A, "ns", T_NS, "cname", T_CNAME, "soa", T_SOA, "mb", T_MB, "mg", T_MG, "mr", T_MR, "null", T_NULL, "wks", T_WKS, "ptr", T_PTR, "hinfo", T_HINFO, "minfo", T_MINFO, "mx", T_MX, "uinfo", T_UINFO, "txt", T_TXT, "uid", T_UID, "gid", T_GID, #ifdef notdef "any", T_ANY, /* any is a QTYPE, not TYPE */ #endif #ifdef ALLOW_T_UNSPEC "unspec", T_UNSPEC, #endif ALLOW_T_UNSPEC }; #define NTYPE (sizeof(m_type)/sizeof(struct map)) /* * Parser token values */ #define CURRENT 1 #define DOT 2 #define AT 3 #define DNAME 4 #define INCLUDE 5 #define ORIGIN 6 #define ERROR 7 int lineno; /* current line number */ /* * Load the database from 'filename'. Origin is appended to all domain * names in the file. */ db_load(filename, in_origin, zp, doinginclude) char *filename, *in_origin; struct zoneinfo *zp; { register u_char *cp; register struct map *mp; char domain[MAXDNAME]; char origin[MAXDNAME]; char tmporigin[MAXDNAME]; u_char buf[MAXDATA]; u_char data[MAXDATA]; u_char *cp1; char *op; int c; int class, type, ttl, dbflags, dataflags; int read_soa = 0; /* number of soa's read */ struct databuf *dp; FILE *fp; int slineno, i, errs = 0, didinclude = 0; register u_long n; struct stat sb; #ifdef DEBUG if (debug) fprintf(ddt,"db_load(%s, %s, %d, %d)\n", filename, in_origin, zp - zones, doinginclude); #endif (void) strcpy(origin, in_origin); if ((fp = fopen(filename, "r")) == NULL) { syslog(LOG_ERR, "%s: %m", filename); #ifdef DEBUG if (debug) fprintf(ddt,"db_load: error opening file %s\n", filename); #endif return (-1); } if (zp->z_type == Z_CACHE) { dbflags = DB_NODATA | DB_NOHINTS; dataflags = DB_F_HINT; } else { dbflags = DB_NODATA; dataflags = 0; } gettime(&tt); if (fstat(fileno(fp), &sb) < 0) { syslog(LOG_ERR, "%s: %m", filename); sb.st_mtime = (int)tt.tv_sec; } slineno = lineno; lineno = 1; domain[0] = '\0'; class = C_IN; zp->z_state &= ~(Z_INCLUDE|Z_DB_BAD); while ((c = gettoken(fp)) != EOF) { switch (c) { case INCLUDE: if (!getword(buf, sizeof(buf), fp)) /* file name */ break; if (!getword(tmporigin, sizeof(tmporigin), fp)) strcpy(tmporigin, origin); else { makename(tmporigin, origin); endline(fp); } didinclude = 1; errs += db_load(buf, tmporigin, zp, 1); continue; case ORIGIN: (void) strcpy((char *)buf, origin); if (!getword(origin, sizeof(origin), fp)) break; #ifdef DEBUG if (debug > 3) fprintf(ddt,"db_load: origin %s, buf %s\n", origin, buf); #endif makename(origin, buf); #ifdef DEBUG if (debug > 3) fprintf(ddt,"db_load: origin now %s\n", origin); #endif continue; case DNAME: if (!getword(domain, sizeof(domain), fp)) break; n = strlen(domain) - 1; if (domain[n] == '.') domain[n] = '\0'; else if (*origin) { (void) strcat(domain, "."); (void) strcat(domain, origin); } goto gotdomain; case AT: (void) strcpy(domain, origin); goto gotdomain; case DOT: domain[0] = '\0'; /* fall thru ... */ case CURRENT: gotdomain: if (!getword(buf, sizeof(buf), fp)) { if (c == CURRENT) continue; break; } cp = buf; ttl = 0; if (isdigit(*cp)) { n = 0; do n = n * 10 + (*cp++ - '0'); while (isdigit(*cp)); if (zp->z_type == Z_CACHE) { /* this allows the cache entry to age */ /* while sitting on disk (powered off) */ if (n > max_cache_ttl) n = max_cache_ttl; n += sb.st_mtime; } ttl = n; if (!getword(buf, sizeof(buf), fp)) break; } for (mp = m_class; mp < m_class+NCLASS; mp++) if (!strcasecmp((char *)buf, mp->token)) { class = mp->val; (void) getword(buf, sizeof(buf), fp); break; } for (mp = m_type; mp < m_type+NTYPE; mp++) if (!strcasecmp((char *)buf, mp->token)) { type = mp->val; goto fndtype; } #ifdef DEBUG if (debug) fprintf(ddt,"Line %d: Unknown type: %s.\n", lineno, buf); #endif errs++; syslog(LOG_ERR, "Line %d: Unknown type: %s.\n", lineno, buf); break; fndtype: #ifdef ALLOW_T_UNSPEC /* Don't do anything here for T_UNSPEC... * read input separately later */ if (type != T_UNSPEC) { #endif ALLOW_T_UNSPEC if (!getword(buf, sizeof(buf), fp)) break; #ifdef DEBUG if (debug >= 3) fprintf(ddt, "d='%s', c=%d, t=%d, ttl=%d, data='%s'\n", domain, class, type, ttl, buf); #endif #ifdef ALLOW_T_UNSPEC } #endif ALLOW_T_UNSPEC /* * Convert the ascii data 'buf' to the proper format * based on the type and pack into 'data'. */ switch (type) { case T_A: n = ntohl((u_long)inet_addr((char *)buf)); cp = data; PUTLONG(n, cp); n = sizeof(u_long); break; case T_HINFO: n = strlen((char *)buf); if (n > 255) { syslog(LOG_WARNING, "%s: line %d: CPU type too long", filename, lineno); n = 255; } data[0] = n; bcopy(buf, (char *)data + 1, (int)n); n++; if (!getword(buf, sizeof(buf), fp)) break; i = strlen((char *)buf); if (i > 255) { syslog(LOG_WARNING, "%s: line %d: OS type too long", filename, lineno); i = 255; } data[n] = i; bcopy(buf, data + n + 1, i); n += i + 1; endline(fp); break; case T_SOA: case T_MINFO: (void) strcpy((char *)data, (char *)buf); makename(data, origin); cp = data + strlen((char *)data) + 1; if (!getword(cp, sizeof(data) - (cp - data),fp)) { n = cp - data; break; } makename(cp, origin); cp += strlen((char *)cp) + 1; if (type == T_MINFO) { n = cp - data; break; } if (getnonblank(fp) != '(') goto err; zp->z_serial = getnum(fp); n = (u_long) zp->z_serial; PUTLONG(n, cp); zp->z_refresh = getnum(fp); n = (u_long) zp->z_refresh; PUTLONG(n, cp); if (zp->z_type == Z_SECONDARY) zp->z_time = sb.st_mtime + zp->z_refresh; zp->z_retry = getnum(fp); n = (u_long) zp->z_retry; PUTLONG(n, cp); zp->z_expire = getnum(fp); n = (u_long) zp->z_expire; PUTLONG (n, cp); zp->z_minimum = getnum(fp); n = (u_long) zp->z_minimum; PUTLONG (n, cp); n = cp - data; if (getnonblank(fp) != ')') goto err; read_soa++; endline(fp); break; case T_UID: case T_GID: n = 0; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); if (cp == buf) goto err; cp = data; PUTLONG(n, cp); n = sizeof(long); break; case T_WKS: /* Address */ n = ntohl((u_long)inet_addr((char *)buf)); cp = data; PUTLONG(n, cp); *cp = getprotocol(fp, filename); /* Protocol */ n = sizeof(u_long) + sizeof(char); /* Services */ n = getservices((int)n, data, fp, filename); break; case T_NS: case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: (void) strcpy((char *)data, (char *)buf); makename(data, origin); n = strlen((char *)data) + 1; break; case T_UINFO: cp = (u_char *)index((char *)buf, '&'); bzero(data, sizeof(data)); if ( cp != NULL) { (void) strncpy((char *)data, (char *)buf, cp - buf); op = index(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: 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_short)n, cp); if (!getword(buf, sizeof(buf), fp)) break; (void) strcpy((char *)cp, (char *)buf); makename(cp, origin); /* get pointer to place in data */ cp += strlen((char *)cp) +1; /* now save length */ n = (cp - data); break; case T_TXT: i = strlen((char *)buf); cp = data; cp1 = buf; /* * there is expansion here so make sure we * don't overflow data */ if (i > sizeof(data) * 255 / 256) { syslog(LOG_WARNING, "%s: line %d: TXT record truncated", filename, lineno); i = sizeof(data) * 255 / 256; } while (i > 255) { *cp++ = 255; bcopy(cp1, cp, 255); cp += 255; cp1 += 255; i -= 255; } *cp++ = i; bcopy(cp1, cp, i); cp += i; n = cp - data; endline(fp); break; #ifdef ALLOW_T_UNSPEC case T_UNSPEC: { int rcode; fgets(buf, sizeof(buf), fp); #ifdef DEBUG if (debug) fprintf(ddt, "loading T_UNSPEC\n"); #endif DEBUG if (rcode = atob(buf, strlen((char *)buf), data, sizeof(data), &n)) { if (rcode == CONV_OVERFLOW) { #ifdef DEBUG if (debug) fprintf(ddt, "Load T_UNSPEC: input buffer overflow\n"); #endif DEBUG errs++; syslog(LOG_ERR, "Load T_UNSPEC: input buffer overflow"); } else { #ifdef DEBUG if (debug) fprintf(ddt, "Load T_UNSPEC: Data in bad atob format\n"); #endif DEBUG errs++; syslog(LOG_ERR, "Load T_UNSPEC: Data in bad atob format"); } } } break; #endif ALLOW_T_UNSPEC default: goto err; } dp = savedata(class, type, (u_long)ttl, data, (int)n); dp->d_zone = zp - zones; dp->d_flags = dataflags; if ((c = db_update(domain, dp, dp, dbflags, (zp->z_type == Z_CACHE)? fcachetab : hashtab)) < 0) { #ifdef DEBUG if (debug && (c != DATAEXISTS)) fprintf(ddt,"update failed\n"); #endif } continue; case ERROR: break; } err: errs++; syslog(LOG_ERR, "%s: line %d: database format error (%s)", filename, lineno, buf); #ifdef DEBUG if (debug) fprintf(ddt,"%s: line %d: database format error ('%s', %d)\n", filename, lineno, buf, n); #endif while ((c = getc(fp)) != EOF && c != '\n') ; if (c == '\n') lineno++; } (void) fclose(fp); lineno = slineno; if (doinginclude == 0) { if (didinclude) { zp->z_state |= Z_INCLUDE; zp->z_ftime = 0; } else zp->z_ftime = sb.st_mtime; zp->z_lastupdate = sb.st_mtime; if (zp->z_type != Z_CACHE && read_soa != 1) { errs++; if (read_soa == 0) syslog(LOG_ERR, "%s: no SOA record", filename); else syslog(LOG_ERR, "%s: multiple SOA records", filename); } } if (errs) zp->z_state |= Z_DB_BAD; return (errs); } int gettoken(fp) register FILE *fp; { register int c; char op[32]; for (;;) { c = getc(fp); top: switch (c) { case EOF: return (EOF); case '$': if (getword(op, sizeof(op), fp)) { if (!strcasecmp("include", op)) return (INCLUDE); if (!strcasecmp("origin", op)) return (ORIGIN); } #ifdef DEBUG if (debug) fprintf(ddt,"Line %d: Unknown $ option: $%s\n", lineno, op); #endif syslog(LOG_ERR,"Line %d: Unknown $ option: $%s\n", 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); } } } /* * Get next word, skipping blanks & comments. */ getword(buf, size, fp) char *buf; int size; FILE *fp; { register char *cp; register int c; for (cp = buf; (c = getc(fp)) != EOF; ) { if (c == ';') { while ((c = getc(fp)) != EOF && c != '\n') ; c = '\n'; } if (c == '\n') { if (cp != buf) ungetc(c, fp); else lineno++; break; } if (isspace(c)) { while (isspace(c = getc(fp)) && c != '\n') ; ungetc(c, fp); if (cp != buf) /* Trailing whitespace */ break; continue; /* Leading whitespace */ } if (c == '"') { while ((c = getc(fp)) != EOF && c != '"' && c != '\n') { if (c == '\\') { if ((c = getc(fp)) == EOF) c = '\\'; if (c == '\n') lineno++; } if (cp >= buf+size-1) break; *cp++ = c; } if (c == '\n') { lineno++; break; } continue; } if (c == '\\') { if ((c = getc(fp)) == EOF) c = '\\'; if (c == '\n') lineno++; } if (cp >= buf+size-1) break; *cp++ = c; } *cp = '\0'; return (cp != buf); } getnum(fp) FILE *fp; { register int c, n; int seendigit = 0; int seendecimal = 0; for (n = 0; (c = getc(fp)) != EOF; ) { if (isspace(c)) { if (c == '\n') lineno++; if (seendigit) break; continue; } if (c == ';') { while ((c = getc(fp)) != EOF && c != '\n') ; if (c == '\n') lineno++; if (seendigit) break; continue; } if (!isdigit(c)) { if (c == ')' && seendigit) { (void) ungetc(c, fp); break; } if (seendecimal || c != '.') { syslog(LOG_ERR, "line %d: expected a number", lineno); #ifdef DEBUG if (debug) fprintf(ddt,"line %d: expected a number", lineno); #endif exit(1); /* XXX why exit */ } else { if (!seendigit) n = 1; n = n * 1000 ; seendigit = 1; seendecimal = 1; } continue; } n = n * 10 + (c - '0'); seendigit = 1; } return (n); } getnonblank(fp) FILE *fp; { register int c; while ( (c = getc(fp)) != EOF ) { if (isspace(c)) { if (c == '\n') lineno++; continue; } if (c == ';') { while ((c = getc(fp)) != EOF && c != '\n') ; if (c == '\n') lineno++; continue; } return(c); } syslog(LOG_ERR, "line %d: unexpected EOF", lineno); #ifdef DEBUG if (debug) fprintf(ddt, "line %d: unexpected EOF", lineno); #endif return (EOF); } /* * Take name and fix it according to following rules: * "." means root. * "@" means current origin. * "name." means no changes. * "name" means append origin. */ makename(name, origin) char *name, *origin; { int n; if (origin[0] == '.') origin++; n = strlen(name); if (n == 1) { if (name[0] == '.') { name[0] = '\0'; return; } if (name[0] == '@') { (void) strcpy(name, origin); return; } } if (n > 0) { if (name[n - 1] == '.') name[n - 1] = '\0'; else if (origin[0] != '\0') { name[n] = '.'; (void) strcpy(name + n + 1, origin); } } } endline(fp) register FILE *fp; { register int c; while (c = getc(fp)) if (c == '\n') { (void) ungetc(c,fp); break; } else if (c == EOF) break; } #define MAXPORT 256 #define MAXLEN 24 getprotocol(fp, src) FILE *fp; char *src; { int k; char b[MAXLEN]; (void) getword(b, sizeof(b), fp); k = protocolnumber(b); if(k == -1) syslog(LOG_ERR, "%s: line %d: unknown protocol: %s.", src, lineno, b); return(k); } int getservices(n, data, fp, src) int n; char *data, *src; FILE *fp; { int j, ch; int k; int maxl; int bracket; char b[MAXLEN]; char bm[MAXPORT/8]; for (j = 0; j < MAXPORT/8; j++) bm[j] = 0; maxl = 0; bracket = 0; while (getword(b, sizeof(b), fp) || bracket) { if (feof(fp) || ferror(fp)) break; if (strlen(b) == 0) continue; if ( b[0] == '(') { bracket++; continue; } if ( b[0] == ')') { bracket = 0; while ((ch = getc(fp)) != EOF && ch != '\n') ; if (ch == '\n') lineno++; break; } k = servicenumber(b); if (k == -1) { syslog(LOG_WARNING, "%s: line %d: Unknown service '%s'", src, lineno, b); continue; } if ((k < MAXPORT) && (k)) { bm[k/8] |= (0x80>>(k%8)); if (k > maxl) maxl=k; } else { syslog(LOG_WARNING, "%s: line %d: port no. (%d) too big\n", src, lineno, k); #ifdef DEBUG if (debug) fprintf(ddt, "%s: line %d: port no. (%d) too big\n", src, lineno, k); #endif } } if (bracket) syslog(LOG_WARNING, "%s: line %d: missing close paren\n", src, lineno); maxl = maxl/8+1; bcopy(bm, data+n, maxl); return(maxl+n); } get_sort_list(fp) FILE *fp; { extern struct netinfo **enettab; register struct netinfo *ntp, **end = enettab; extern struct netinfo *findnetinfo(); struct in_addr addr; char buf[BUFSIZ]; #ifdef DEBUG if (debug) fprintf(ddt,"sortlist "); #endif while (getword(buf, sizeof(buf), fp)) { if (strlen(buf) == 0) break; #ifdef DEBUG if (debug) fprintf(ddt," %s",buf); #endif addr.s_addr = inet_addr(buf); if (addr.s_addr == (unsigned)-1) { /* resolve name to address - XXX */ continue; } /* Check for duplicates, then add to linked list */ if (findnetinfo(addr)) continue; ntp = (struct netinfo *)malloc(sizeof(struct netinfo)); ntp->my_addr = addr; ntp->next = NULL; ntp->mask = net_mask(ntp->my_addr); ntp->net = ntp->my_addr.s_addr & ntp->mask; if (ntp->net != addr.s_addr) { struct in_addr tmpaddr; tmpaddr.s_addr = ntp->net; syslog(LOG_WARNING, "sortlist: addr %s != %s", buf, inet_ntoa(tmpaddr)); #ifdef DEBUG if (debug) fprintf(ddt, "\nsortlist: addr %s != %s\n", buf, inet_ntoa(tmpaddr)); #endif } *end = ntp; end = &ntp->next; } #ifdef DEBUG if (debug) fprintf(ddt,"\n"); if (debug > 2) printnetinfo(*enettab); if (debug > 4) { extern struct netinfo *nettab; fprintf(ddt, "\nFull sort list:\n"); printnetinfo(nettab); } #endif } free_sort_list() { extern struct netinfo **enettab; register struct netinfo *ntp, *next; for (ntp = *enettab; ntp != NULL; ntp = next) { next = ntp->next; free((char *)ntp); } *enettab = NULL; }