/*- * Copyright (c) 1986, 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint /*static char sccsid[] = "from: @(#)ns_init.c 4.38 (Berkeley) 3/21/91";*/ static char rcsid[] = "$Id: ns_init.c,v 1.1 1995/10/18 08:47:50 deraadt Exp $"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #undef nsaddr #include "pathnames.h" #include "ns.h" #include "db.h" struct zoneinfo *zones; /* zone information */ int nzones; /* number of zones in use */ int forward_only = 0; /* run only as a slave */ char *cache_file; char *localdomain; /* "default" for non-dotted names */ int maint_interval = 15*60; /* minimum ns_maint() interval */ extern int lineno; /* * Read boot file for configuration info. */ ns_init(bootfile) char *bootfile; { register struct zoneinfo *zp; struct zoneinfo *find_zone(); char buf[BUFSIZ], obuf[BUFSIZ], *source; extern char *calloc(); FILE *fp; int type; extern int needmaint; struct stat f_time; static int loads = 0; /* number of times loaded */ static int tmpnum = 0; /* unique number for tmp zone files */ #ifdef ALLOW_UPDATES char *cp, *flag; #endif #ifdef DEBUG if (debug) fprintf(ddt,"\nns_init(%s)\n", bootfile); #endif gettime(&tt); if ((fp = fopen(bootfile, "r")) == NULL) { syslog(LOG_ERR, "%s: %m", bootfile); exit(1); } lineno = 0; if (loads == 0) { if ((zones = (struct zoneinfo *)calloc(64, sizeof(struct zoneinfo))) == NULL) { syslog(LOG_ERR, "Not enough memory to allocate initial zones array"); exit(1); } nzones = 1; /* zone zero is cache data */ /* allocate cache hash table, formerly the root hash table. */ hashtab = savehash((struct hashbuf *)NULL); /* allocate root-hints/file-cache hash table */ fcachetab = savehash((struct hashbuf *)NULL); /* init zone data */ zones[0].z_type = Z_CACHE; } else { /* Mark previous zones as not yet found in boot file. */ for (zp = &zones[1]; zp < &zones[nzones]; zp++) zp->z_state &= ~Z_FOUND; if (localdomain) free(localdomain); localdomain = NULL; free_forwarders(); forward_only = 0; } #ifdef DEBUG if (debug >= 3) { fprintf(ddt,"\n content of zones before loading \n"); content_zone(nzones - 1); } #endif while (!feof(fp) && !ferror(fp)) { if (!getword(buf, sizeof(buf), fp)) continue; /* read named.boot keyword and process args */ if (strcasecmp(buf, "directory") == 0) { (void) getword(buf, sizeof(buf), fp); if (chdir(buf) < 0) { syslog(LOG_CRIT, "directory %s: %m\n", buf); exit(1); } continue; } else if (strcasecmp(buf, "sortlist") == 0) { get_sort_list(fp); continue; } else if (strcasecmp(buf, "forwarders") == 0) { get_forwarders(fp); continue; } else if (strcasecmp(buf, "slave") == 0) { forward_only++; endline(fp); continue; } else if (strcasecmp(buf, "domain") == 0) { if (getword(buf, sizeof(buf), fp)) localdomain = savestr(buf); endline(fp); continue; } else if (strcasecmp(buf, "cache") == 0) type = Z_CACHE; else if (strcasecmp(buf, "primary") == 0) type = Z_PRIMARY; else if (strcasecmp(buf, "secondary") == 0) type = Z_SECONDARY; else { syslog(LOG_ERR, "%s: line %d: unknown field '%s'\n", bootfile, lineno, buf); endline(fp); continue; } /* * read zone origin */ if (!getword(obuf, sizeof(obuf), fp)) { syslog(LOG_ERR, "%s: line %d: missing origin\n", bootfile, lineno); continue; } #ifdef DEBUG if (debug) fprintf(ddt, "zone origin %s", obuf); #endif if (obuf[0] == '.' && obuf[1] == '\0') obuf[0] = '\0'; /* * read source file or host address */ if (!getword(buf, sizeof(buf), fp)) { syslog(LOG_ERR, "%s: line %d: missing source or addr\n", bootfile, lineno); continue; } /* check for previous instance of this zone (reload) */ if ((zp = find_zone(obuf, type)) == 0) { if (type == Z_CACHE) { zp = &zones[0]; zp->z_origin = ""; goto gotzone; } for (zp = &zones[1]; zp < &zones[nzones]; zp++) if (zp->z_type == 0) goto gotzone; /* * this code assume that nzones never decreases */ if (nzones % 64 == 0) { #ifdef DEBUG if (debug > 1) fprintf(ddt, "Reallocating zones structure\n"); #endif DEBUG /* * Realloc() not used since it might damage zones * if an error occurs */ if ((zp = (struct zoneinfo *) malloc((64 + nzones) * sizeof(struct zoneinfo))) == NULL) { syslog(LOG_ERR, "no memory for more zones"); #ifdef DEBUG if (debug) fprintf(ddt, "Out of memory for new zones\n"); #endif DEBUG endline(fp); continue; } bcopy((char *)zones, (char *)zp, nzones * sizeof(struct zoneinfo)); bzero((char *)&zp[nzones], 64 * sizeof(struct zoneinfo)); free(zones); zones = zp; } zp = &zones[nzones++]; gotzone: zp->z_origin = savestr(obuf); zp->z_type = type; } zp->z_addrcnt = 0; switch (type) { case Z_CACHE: source = savestr(buf); #ifdef DEBUG if (debug) fprintf(ddt,", source = %s\n", source); #endif zp->z_refresh = 0; /* by default, no dumping */ if (getword(buf, sizeof(buf), fp)) { #ifdef notyet zp->z_refresh = atoi(buf); if (zp->z_refresh <= 0) { syslog(LOG_ERR, "%s: line %d: bad refresh time '%s', ignored\n", bootfile, lineno, buf); zp->z_refresh = 0; } else if (cache_file == NULL) cache_file = source; #else syslog(LOG_WARNING, "%s: line %d: cache refresh ignored\n", bootfile, lineno); #endif endline(fp); } /* * If we've loaded this file, and the file has * not been modified and contains no $include, * then there's no need to reload. */ if (zp->z_source && strcmp(source, zp->z_source) == 0 && (zp->z_state & Z_INCLUDE) == 0 && stat(zp->z_source, &f_time) == 0 && zp->z_ftime == f_time.st_mtime) { #ifdef DEBUG if (debug) fprintf(ddt, "cache is up to date\n"); #endif break; /* zone is already up to date */ } /* file has changed, or hasn't been loaded yet */ if (zp->z_source) { free(zp->z_source); remove_zone(fcachetab, 0); } zp->z_source = source; #ifdef DEBUG if (debug) fprintf(ddt, "reloading zone\n"); #endif (void) db_load(zp->z_source, zp->z_origin, zp, 0); break; case Z_PRIMARY: source = savestr(buf); #ifdef ALLOW_UPDATES if (getword(buf, sizeof(buf), fp)) { endline(fp); flag = buf; while (flag) { cp = index(flag, ','); if (cp) *cp++ = 0; if (strcasecmp(flag, "dynamic") == 0) zp->z_state |= Z_DYNAMIC; else if (strcasecmp(flag, "addonly") == 0) zp->z_state |= Z_DYNADDONLY; else { syslog(LOG_ERR, "%s: line %d: bad flag '%s'\n", bootfile, lineno, flag); } flag = cp; } } #else ALLOW_UPDATES endline(fp); #endif #ifdef DEBUG if (debug) fprintf(ddt,", source = %s\n", source); #endif /* * If we've loaded this file, and the file has * not been modified and contains no $include, * then there's no need to reload. */ if (zp->z_source && strcmp(source, zp->z_source) == 0 && (zp->z_state & Z_INCLUDE) == 0 && stat(zp->z_source, &f_time) == 0 && zp->z_ftime == f_time.st_mtime) { #ifdef DEBUG if (debug) fprintf(ddt, "zone is up to date\n"); #endif break; /* zone is already up to date */ } if (zp->z_source) { free(zp->z_source); remove_zone(hashtab, zp - zones); } zp->z_source = source; zp->z_auth = 0; #ifdef DEBUG if (debug) fprintf(ddt, "reloading zone\n"); #endif if (db_load(zp->z_source, zp->z_origin, zp, 0) == 0) zp->z_auth = 1; #ifdef ALLOW_UPDATES /* Guarantee calls to ns_maint() */ zp->z_refresh = maint_interval; #else ALLOW_UPDATES zp->z_refresh = 0; /* no maintenance needed */ zp->z_time = 0; #endif ALLOW_UPDATES break; case Z_SECONDARY: source = 0; #ifdef DEBUG if (debug) fprintf(ddt,"\n\taddrs: "); #endif do { zp->z_addr[zp->z_addrcnt].s_addr = inet_addr(buf); if (zp->z_addr[zp->z_addrcnt].s_addr == (u_long)-1) { source = savestr(buf); endline(fp); break; } #ifdef DEBUG if (debug) fprintf(ddt,"%s, ",buf); #endif if (++zp->z_addrcnt > NSMAX - 1) { zp->z_addrcnt = NSMAX - 1; #ifdef DEBUG if (debug) fprintf(ddt, "\nns.h NSMAX reached\n"); #endif } } while (getword(buf, sizeof(buf), fp)); #ifdef DEBUG if (debug) fprintf(ddt,"addrcnt = %d\n", zp->z_addrcnt); #endif if (source == 0) { /* * We will always transfer this zone again * after a reload. */ sprintf(buf, "/%s/NsTmp%d", _PATH_TMPDIR, tmpnum++); source = savestr(buf); zp->z_state |= Z_TMP_FILE; } else zp->z_state &= ~Z_TMP_FILE; /* * If we had a backup file name, and it was changed, * free old zone and start over. If we don't have * current zone contents, try again now in case * we have a new server on the list. */ if (zp->z_source && strcmp(source, zp->z_source)) { #ifdef DEBUG if (debug) fprintf(ddt,"backup file changed\n"); #endif free(zp->z_source); zp->z_source = 0; zp->z_auth = 0; zp->z_serial = 0; /* force xfer */ remove_zone(hashtab, zp - zones); } if (zp->z_source) free(source); else zp->z_source = source; if (zp->z_auth == 0) zoneinit(zp); break; } zp->z_state |= Z_FOUND; #ifdef DEBUG if (debug) fprintf(ddt,"zone[%d] type %d: '%s'", zp-zones, type, *(zp->z_origin) == '\0' ? "." : zp->z_origin); #endif if (zp->z_refresh && zp->z_time == 0) zp->z_time = zp->z_refresh + tt.tv_sec; if (zp->z_time <= tt.tv_sec) needmaint = 1; #ifdef DEBUG if (debug) fprintf(ddt, " z_time %d, z_refresh %d\n", zp->z_time, zp->z_refresh); #endif } (void) fclose(fp); /* erase all old zones that were not found */ for (zp = &zones[1]; zp < &zones[nzones]; zp++) if (zp->z_type && (zp->z_state & Z_FOUND) == 0) { remove_zone(hashtab, zp - zones); bzero((char *) zp, sizeof(*zp)); #ifdef DEBUG if(debug >=2) fprintf(ddt,"\n zone no %d was removed \n", zp - zones); #endif } #ifdef DEBUG if(debug >= 2) { fprintf(ddt,"\n content of zones after loading \n"); content_zone(nzones-1); } #endif /* * Schedule calls to ns_maint(). */ if (needmaint == 0) sched_maint(); #ifdef DEBUG if (debug) fprintf(ddt,"exit ns_init()%s\n", needmaint ? ", need maintenance immediately" : ""); #endif loads++; } zoneinit(zp) register struct zoneinfo *zp; { extern int needmaint; struct stat sb; /* * Try to load zone from backup file, * if one was specified and it exists. * If not, or if the data are out of date, * we will refresh the zone from a primary * immediately. */ if (zp->z_source == NULL) return; if (stat(zp->z_source, &sb) == -1 || db_load(zp->z_source, zp->z_origin, zp, 0) != 0) { /* * Set zone to be refreshed immediately. */ zp->z_refresh = INIT_REFRESH; zp->z_retry = INIT_REFRESH; zp->z_time = tt.tv_sec; needmaint = 1; } else zp->z_auth = 1; } #ifdef ALLOW_UPDATES /* * Look for the authoritative zone with the longest matching RHS of dname * and return its zone # or zero if not found. */ findzone(dname, class) char *dname; int class; { char *dZoneName, *zoneName, *index(); int dZoneNameLen, zoneNameLen; int maxMatchLen = 0; int maxMatchZoneNum = 0; int zoneNum; #ifdef DEBUG if (debug >= 4) fprintf(ddt, "findzone(dname=%s, class=%d)\n", dname, class); if (debug >= 5) { fprintf(ddt, "zone dump:\n"); for (zoneNum = 1; zoneNum < nzones; zoneNum++) printzoneinfo(zoneNum); } #endif DEBUG dZoneName = index(dname, '.'); if (dZoneName == NULL) dZoneName = ""; /* root */ else dZoneName++; /* There is a '.' in dname, so use remainder of string as the zone name */ dZoneNameLen = strlen(dZoneName); for (zoneNum = 1; zoneNum < nzones; zoneNum++) { zoneName = (zones[zoneNum]).z_origin; zoneNameLen = strlen(zoneName); /* The zone name may or may not end with a '.' */ if (zoneName[zoneNameLen - 1] == '.') zoneNameLen--; if (dZoneNameLen != zoneNameLen) continue; #ifdef DEBUG if (debug >= 5) fprintf(ddt, "about to strncasecmp('%s', '%s', %d)\n", dZoneName, zoneName, dZoneNameLen); #endif if (strncasecmp(dZoneName, zoneName, dZoneNameLen) == 0) { #ifdef DEBUG if (debug >= 5) fprintf(ddt, "match\n"); #endif /* * See if this is as long a match as any so far. * Check if "<=" instead of just "<" so that if * root domain (whose name length is 0) matches, * we use it's zone number instead of just 0 */ if (maxMatchLen <= zoneNameLen) { maxMatchZoneNum = zoneNum; maxMatchLen = zoneNameLen; } } #ifdef DEBUG else if (debug >= 5) fprintf(ddt, "no match\n"); #endif } #ifdef DEBUG if (debug >= 4) fprintf(ddt, "findzone: returning %d\n", maxMatchZoneNum); #endif DEBUG return (maxMatchZoneNum); } #endif ALLOW_UPDATES soa_zinfo(zp, cp, eom) register struct zoneinfo *zp; register u_char *cp; u_char *eom; { cp += 3 * sizeof(u_short); cp += sizeof(u_long); cp += dn_skipname(cp, eom); cp += dn_skipname(cp, eom); GETLONG(zp->z_serial, cp); GETLONG(zp->z_refresh, cp); gettime(&tt); zp->z_time = tt.tv_sec + zp->z_refresh; GETLONG(zp->z_retry, cp); GETLONG(zp->z_expire, cp); GETLONG(zp->z_minimum, cp); } get_forwarders(fp) FILE *fp; { char buf[BUFSIZ]; register struct fwdinfo *fip = NULL, *ftp = NULL; extern struct sockaddr_in nsaddr; extern struct fwdinfo *fwdtab; #ifdef DEBUG if (debug) fprintf(ddt,"forwarders "); #endif /* on mulitple forwarder lines, move to end of the list */ if (fwdtab != NULL) for (fip = fwdtab; fip->next != NULL; fip = fip->next) ; while (getword(buf, sizeof(buf), fp)) { if (strlen(buf) == 0) break; #ifdef DEBUG if (debug) fprintf(ddt," %s",buf); #endif if (ftp == NULL) ftp = (struct fwdinfo *)malloc(sizeof(struct fwdinfo)); if (isdigit(buf[0]) && (ftp->fwdaddr.sin_addr.s_addr = inet_addr(buf)) != (u_long)-1) { ftp->fwdaddr.sin_port = nsaddr.sin_port; ftp->fwdaddr.sin_family = AF_INET; } else { syslog(LOG_ERR, "'%s' (ignored, NOT dotted quad)", buf); #ifdef DEBUG if (debug) fprintf(ddt," (ignored, NOT dotted quad)"); #endif continue; } ftp->next = NULL; if (fwdtab == NULL) fwdtab = ftp; /* First time only */ else fip->next = ftp; fip = ftp; ftp = NULL; } if (ftp) free((char *)ftp); #ifdef DEBUG if (debug) fprintf(ddt,"\n"); if (debug > 2) for (ftp = fwdtab; ftp != NULL; ftp = ftp->next) fprintf(ddt,"ftp x%x %s next x%x\n", ftp, inet_ntoa(ftp->fwdaddr.sin_addr), ftp->next); #endif } free_forwarders() { extern struct fwdinfo *fwdtab; register struct fwdinfo *ftp, *fnext; for (ftp = fwdtab; ftp != NULL; ftp = fnext) { fnext = ftp->next; free((char *)ftp); } fwdtab = NULL; } struct zoneinfo * find_zone(name, type) char *name; int type; { register struct zoneinfo *zp; for (zp = &zones[1]; zp < &zones[nzones]; zp++) if (zp->z_type == type && strcasecmp(name, zp->z_origin) == 0) { #ifdef DEBUG if (debug > 1) fprintf(ddt, ", old zone (%d)", zp - zones); #endif return (zp); } #ifdef DEBUG if(debug > 1) fprintf(ddt, ", new zone"); #endif return ((struct zoneinfo *)NULL); } /* prints out the content of zones */ content_zone(end) int end; { int i; for (i = 1; i <= end; i++) printzoneinfo(i); }