#if !defined(lint) && !defined(SABER) static const char rcsid[] = "$Id: ns_config.c,v 8.135 2002/05/24 03:04:59 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1996-2000 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. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * 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 Check Point Software Technologies Incorporated 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 CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef HAVE_GETRUSAGE /* XXX */ #include #endif #include "named.h" #include "ns_parseutil.h" /* Private. */ static int tmpnum = 0; static int config_initialized = 0; static int need_logging_free = 0; static int default_logging_installed; static int options_installed = 0; static int logging_installed = 0; static int default_options_installed; static char **logging_categories; static char *current_pid_filename = NULL; #define ZONE_SYM_TABLE_SIZE 4973 static symbol_table zone_symbol_table; /* Zones */ void free_zone_timerinfo(struct zoneinfo *zp) { if (zp->z_timerinfo != NULL) { zp->z_timerinfo->name = freestr(zp->z_timerinfo->name); memput(zp->z_timerinfo, sizeof *zp->z_timerinfo); zp->z_timerinfo = NULL; } else ns_error(ns_log_config, "timer for zone '%s' had no timerinfo", zp->z_origin); } void free_zone_contents(struct zoneinfo *zp, int undefine_sym) { INSIST(zp != NULL); if (undefine_sym) undefine_symbol(zone_symbol_table, zp->z_origin, zp->z_class); if (zp->z_flags & Z_TIMER_SET) { free_zone_timerinfo(zp); if (evClearTimer(ev, zp->z_timer) < 0) ns_error(ns_log_config, "evClearTimer for zone '%s' failed in ns_init: %s", zp->z_origin, strerror(errno)); } if (zp->z_origin != NULL) zp->z_origin = freestr(zp->z_origin); if (zp->z_source != NULL) zp->z_source = freestr(zp->z_source); if (zp->z_ixfr_base != NULL) zp->z_ixfr_base = freestr(zp->z_ixfr_base); if (zp->z_ixfr_tmp != NULL) zp->z_ixfr_tmp = freestr(zp->z_ixfr_tmp); if (zp->z_update_acl != NULL) free_ip_match_list(zp->z_update_acl); zp->z_update_acl = NULL; if (zp->z_query_acl != NULL) free_ip_match_list(zp->z_query_acl); zp->z_query_acl = NULL; if (zp->z_transfer_acl != NULL) free_ip_match_list(zp->z_transfer_acl); zp->z_transfer_acl = NULL; #ifdef BIND_UPDATE if (zp->z_updatelog != NULL) zp->z_updatelog = freestr(zp->z_updatelog); #endif /* BIND_UPDATE */ #ifdef BIND_NOTIFY if (zp->z_also_notify != NULL) memput(zp->z_also_notify, zp->z_notify_count * sizeof *zp->z_also_notify); zp->z_also_notify = NULL; #endif if (zp->z_fwdtab != NULL) free_forwarders(zp->z_fwdtab); zp->z_fwdtab = NULL; block_signals(); if (LINKED(zp, z_reloadlink)) UNLINK(reloadingzones, zp, z_reloadlink); unblock_signals(); } static void release_zone(struct zoneinfo *zp) { INSIST(zp != NULL); free_zone_contents(zp, 0); memput(zp, sizeof *zp); } struct zoneinfo * find_zone(const char *name, int class) { struct zoneinfo *zp; symbol_value value; ns_debug(ns_log_config, 3, "find_zone(%s, %d)", *name ? name : ".", class); if (lookup_symbol(zone_symbol_table, name, class, &value)) { INSIST(value.integer >= 0 && value.integer < nzones); ns_debug(ns_log_config, 3, "find_zone: existing zone %d", value.integer); zp = &zones[value.integer]; return (zp); } ns_debug(ns_log_config, 3, "find_zone: unknown zone"); return (NULL); } static struct zoneinfo * new_zone(void) { struct zoneinfo *zp; if (EMPTY(freezones)) make_new_zones(); zp = HEAD(freezones); UNLINK(freezones, zp, z_freelink); return (zp); } /* * Check out a zoneinfo structure and return non-zero if it's OK. */ static int validate_zone(struct zoneinfo *zp) { char filename[MAXPATHLEN+1]; /* Check name */ if (!res_dnok(zp->z_origin)) { ns_error(ns_log_config, "invalid zone name '%s'", zp->z_origin); return (0); } /* Check class */ if (zp->z_class == C_ANY || zp->z_class == C_NONE) { ns_error(ns_log_config, "invalid class %d for zone '%s'", zp->z_class, zp->z_origin); return (0); } /* Check type. */ if (zp->z_type == 0) { ns_error(ns_log_config, "no type specified for zone '%s'", zp->z_origin); return (0); } if (zp->z_type == z_cache && ns_samename(zp->z_origin, "") != 1) { ns_error(ns_log_config, "only the root zone may be a cache zone (zone '%s')", zp->z_origin); return (0); } if (zp->z_type == z_hint && ns_samename(zp->z_origin, "") != 1) { ns_error(ns_log_config, "only the root zone may be a hint zone (zone '%s')", zp->z_origin); return (0); } /* Check filename. */ if (zp->z_type == z_master && zp->z_source == NULL) { ns_error(ns_log_config, "'file' statement missing for master zone %s", zp->z_origin); return (0); } /* * XXX We should run filename through an OS-specific * validator here. */ if (zp->z_source != NULL && strlen(zp->z_source) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for zone '%s'", zp->z_origin); return (0); } if (zp->z_ixfr_base != NULL && strlen(zp->z_ixfr_base) > MAXPATHLEN) { ns_error(ns_log_config, "ixfr filename too long for zone '%s'", zp->z_origin); return (0); } if (zp->z_ixfr_tmp != NULL && strlen(zp->z_ixfr_tmp) > MAXPATHLEN) { ns_error(ns_log_config, "tmp ixfr filename too long for zone '%s'", zp->z_origin); return (0); } /* Check masters */ if (zp->z_addrcnt != 0) { if (zp->z_type == z_master || zp->z_type == z_hint || zp->z_type == z_cache) { ns_error(ns_log_config, "'masters' statement present for %s zone '%s'", (zp->z_type == z_master) ? "master" : (zp->z_type == z_hint) ? "hint" : "cache", zp->z_origin); return (0); } } else { if (zp->z_type == z_slave || zp->z_type == z_stub) { ns_error(ns_log_config, "no 'masters' statement for non-master zone '%s'", zp->z_origin); return (0); } } /* Check allow-update and allow-transfer. */ if (zp->z_update_acl || zp->z_transfer_acl) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'allow-{update,transfer}' option for non-{master,slave} zone '%s'", zp->z_origin); return (0); } } /* Check allow-query. */ if (zp->z_query_acl) { if (zp->z_type != z_master && zp->z_type != z_slave && #ifdef FORWARD_ALLOWS zp->z_type != z_forward && #endif zp->z_type != z_stub) { ns_error(ns_log_config, #ifdef FORWARD_ALLOWS "'allow-query' option for hint zone '%s'", #else "'allow-query' option for non-{master,slave,stub} zone '%s'", #endif zp->z_origin); return (0); } } #ifdef BIND_NOTIFY /* Check notify */ if (zp->z_notify != notify_use_default) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'notify' given for non-master, non-slave zone '%s'", zp->z_origin); return (0); } } /* Check also-notify */ if (zp->z_notify_count != 0) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'also-notify' given for non-master, non-slave zone '%s'", zp->z_origin); return (0); } } #endif #ifdef BIND_UPDATE /* XXX need more checking here */ if (!zp->z_updatelog && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".log" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.log", zp->z_source); zp->z_updatelog = savestr(filename, 1); } /* Check forward */ if (zp->z_optset & OPTION_FORWARD_ONLY) { if (zp->z_type == z_hint) { ns_error(ns_log_config, "'forward' given for hint zone '%s'", zp->z_origin); return (0); } } /* Check forwarders */ if (zp->z_fwdtab) { if (zp->z_type == z_hint) { ns_error(ns_log_config, "'forwarders' given for hint zone '%s'", zp->z_origin); return (0); } } if (zp->z_type == z_master) { if (!zp->z_soaincrintvl) zp->z_soaincrintvl = SOAINCRINTVL; if (!zp->z_dumpintvl) zp->z_dumpintvl = DUMPINTVL; if (!zp->z_deferupdcnt) zp->z_deferupdcnt = DEFERUPDCNT; } #endif /* BIND_UPDATE */ if (!zp->z_ixfr_base && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".ixfr" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.ixfr", zp->z_source); zp->z_ixfr_base = savestr(filename, 1); } if (!zp->z_ixfr_tmp && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".ixfr.tmp" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.ixfr.tmp", zp->z_source); zp->z_ixfr_tmp = savestr(filename, 1); } return (1); } /* * Start building a new zoneinfo structure. Returns an opaque * zone_config suitable for use by the parser. */ zone_config begin_zone(char *name, int class) { zone_config zh; struct zoneinfo *zp; /* * require: name is canonical, class is a valid class */ ns_debug(ns_log_config, 3, "begin_zone('%s', %d)", (*name == '\0') ? "." : name, class); zp = (struct zoneinfo *)memget(sizeof (struct zoneinfo)); if (zp == NULL) panic("memget failed in begin_zone", NULL); memset(zp, 0, sizeof (struct zoneinfo)); zp->z_origin = name; zp->z_class = class; zp->z_checknames = not_set; if (server_options->flags & OPTION_MAINTAIN_IXFR_BASE) zp->z_maintain_ixfr_base = 1; else zp->z_maintain_ixfr_base = 0; zp->z_max_log_size_ixfr = server_options->max_log_size_ixfr; zh.opaque = zp; return (zh); } /* * Merge new configuration information into an existing zone. The * new zoneinfo must be valid. */ static void update_zone_info(struct zoneinfo *zp, struct zoneinfo *new_zp) { char buf[MAXPATHLEN+1]; int i; INSIST(zp != NULL); INSIST(new_zp != NULL); ns_debug(ns_log_config, 1, "update_zone_info('%s', %d)", (*new_zp->z_origin == '\0') ? "." : new_zp->z_origin, new_zp->z_type); #ifdef BIND_UPDATE /* * A dynamic master zone that's becoming non-dynamic may need to be * dumped before we start the update. */ if ((zp->z_flags & Z_DYNAMIC) && !(new_zp->z_flags & Z_DYNAMIC) && ((zp->z_flags & Z_NEED_SOAUPDATE) || (zp->z_flags & Z_NEED_DUMP))) (void) zonedump(zp, ISNOTIXFR); #endif /* * First do the simple stuff, making sure to free * any data that was dynamically allocated. */ if (zp->z_origin != NULL) (void)freestr(zp->z_origin); zp->z_origin = new_zp->z_origin; new_zp->z_origin = NULL; zp->z_maintain_ixfr_base = new_zp->z_maintain_ixfr_base; zp->z_max_log_size_ixfr = new_zp->z_max_log_size_ixfr; zp->z_class = new_zp->z_class; zp->z_type = new_zp->z_type; zp->z_checknames = new_zp->z_checknames; for (i = 0; i < new_zp->z_addrcnt; i++) { zp->z_addr[i] = new_zp->z_addr[i]; zp->z_keys[i] = new_zp->z_keys[i]; } zp->z_addrcnt = new_zp->z_addrcnt; if (zp->z_update_acl) free_ip_match_list(zp->z_update_acl); zp->z_update_acl = new_zp->z_update_acl; new_zp->z_update_acl = NULL; if (zp->z_query_acl) free_ip_match_list(zp->z_query_acl); zp->z_query_acl = new_zp->z_query_acl; new_zp->z_query_acl = NULL; zp->z_axfr_src = new_zp->z_axfr_src; if (zp->z_transfer_acl) free_ip_match_list(zp->z_transfer_acl); zp->z_transfer_acl = new_zp->z_transfer_acl; new_zp->z_transfer_acl = NULL; zp->z_max_transfer_time_in = new_zp->z_max_transfer_time_in; #ifdef BIND_NOTIFY zp->z_notify = new_zp->z_notify; if (zp->z_also_notify) memput(zp->z_also_notify, zp->z_notify_count * sizeof *zp->z_also_notify); zp->z_also_notify = new_zp->z_also_notify; zp->z_notify_count = new_zp->z_notify_count; new_zp->z_also_notify = NULL; new_zp->z_notify_count = 0; #endif if ((new_zp->z_flags & Z_FORWARD_SET) != 0) zp->z_flags |= Z_FORWARD_SET; else zp->z_flags &= ~Z_FORWARD_SET; if (zp->z_fwdtab != NULL) free_forwarders(zp->z_fwdtab); zp->z_fwdtab = new_zp->z_fwdtab; new_zp->z_fwdtab = NULL; zp->z_dialup = new_zp->z_dialup; zp->z_options = new_zp->z_options; zp->z_optset = new_zp->z_optset; #ifdef BIND_UPDATE if (new_zp->z_flags & Z_DYNAMIC) zp->z_flags |= Z_DYNAMIC; else zp->z_flags &= ~Z_DYNAMIC; zp->z_soaincrintvl = new_zp->z_soaincrintvl; zp->z_dumpintvl = new_zp->z_dumpintvl; zp->z_deferupdcnt = new_zp->z_deferupdcnt; if (zp->z_updatelog) (void)freestr(zp->z_updatelog); zp->z_updatelog = new_zp->z_updatelog; new_zp->z_updatelog = NULL; #endif /* BIND_UPDATE */ zp->z_port = new_zp->z_port; /* * Now deal with files. */ switch (zp->z_type) { case z_cache: ns_panic(ns_log_config, 1, "impossible condition"); break; case z_hint: ns_debug(ns_log_config, 1, "source = %s", new_zp->z_source); zp->z_refresh = 0; /* No dumping. */ if (zp->z_source != NULL && strcmp(new_zp->z_source, zp->z_source) == 0 && (reconfiging || !zonefile_changed_p(zp))) { ns_debug(ns_log_config, 1, "cache is up to date"); break; } /* File has changed, or hasn't been loaded yet. */ if (zp->z_source) { zp->z_source = freestr(zp->z_source); ns_stopxfrs(zp); purge_zone(zp->z_origin, fcachetab, zp->z_class); } zp->z_source = new_zp->z_source; new_zp->z_source = NULL; if (zp->z_ixfr_base) (void)freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp) (void)freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; ns_debug(ns_log_config, 1, "reloading hint zone"); (void) db_load(zp->z_source, zp->z_origin, zp, NULL, ISNOTIXFR); break; case z_master: ns_debug(ns_log_config, 1, "source = %s", new_zp->z_source); /* * If we've loaded this file, and the file hasn't changed * then there's no need to reload. */ if (zp->z_source != NULL && strcmp(new_zp->z_source, zp->z_source) == 0 && (reconfiging || !zonefile_changed_p(zp))) { ns_debug(ns_log_config, 1, "zone is up to date"); break; } #ifdef BIND_UPDATE if (zp->z_source && (zp->z_flags & Z_DYNAMIC)) ns_warning(ns_log_config, "source file of dynamic zone '%s' has changed", zp->z_origin); primary_reload: #endif /* BIND_UPDATE */ if (zp->z_source != NULL) (void)freestr(zp->z_source); zp->z_source = new_zp->z_source; new_zp->z_source = NULL; if (zp->z_ixfr_base != NULL) (void)freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp != NULL) (void)freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; if (reload_master(zp) == 1) { /* * Note that going to primary_reload * unconditionally reloads the zone. */ new_zp->z_source = savestr(zp->z_source, 1); new_zp->z_ixfr_base = savestr(zp->z_ixfr_base, 1); new_zp->z_ixfr_tmp = savestr(zp->z_ixfr_tmp, 1); goto primary_reload; } break; case z_slave: #ifdef STUBS case z_stub: #endif ns_debug(ns_log_config, 1, "addrcnt = %d", zp->z_addrcnt); if (!new_zp->z_source) { /* * We will always transfer this zone again * after a reload. */ sprintf(buf, "NsTmp%ld.%d", (long)getpid(), tmpnum++); new_zp->z_source = savestr(buf, 1); zp->z_flags |= Z_TMP_FILE; } else zp->z_flags &= ~Z_TMP_FILE; /* * If we had a backup file name, and it was changed, * free old zone and start over. If we don't have * current zone contents, try again now in case * we have a new server on the list. */ if (zp->z_source != NULL && (strcmp(new_zp->z_source, zp->z_source) != 0 || ((!reconfiging) && zonefile_changed_p(zp)))) { ns_debug(ns_log_config, 1, "backup file changed or missing"); zp->z_source = freestr(zp->z_source); zp->z_serial = 0; /* force xfer */ ns_stopxfrs(zp); /* * We only need to reload if we have ever * successfully transferred the zone. */ if ((zp->z_flags & Z_AUTH) != 0) { zp->z_flags &= ~Z_AUTH; /* * Purge old data and mark the parent for * reloading so that NS records are present * during the zone transfer. */ do_reload(zp->z_origin, zp->z_type, zp->z_class, 1); } } if (zp->z_source == NULL) { zp->z_source = new_zp->z_source; new_zp->z_source = NULL; } if (zp->z_ixfr_base != NULL) (void)freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp != NULL) freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; if ((!noexpired || ((zp->z_flags & Z_EXPIRED) == 0)) && ((zp->z_flags & Z_AUTH) == 0)) zoneinit(zp); else { /* ** Force slave to try transfer soon after SIGHUP. */ if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0 && reloading && !reconfiging) { qserial_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); } } break; case z_forward: /* * We don't know if the forwarder's list has changed * so just purge the cache. In the future we may want * see if the forwarders list has changed and only * do this then. */ clean_cache_from(zp->z_origin, hashtab); break; } if ((zp->z_flags & Z_FOUND) != 0 && /* already found? */ (zp - zones) != DB_Z_CACHE) /* cache never sets Z_FOUND */ ns_error(ns_log_config, "Zone \"%s\" declared more than once", zp->z_origin); zp->z_flags |= Z_FOUND; ns_debug(ns_log_config, 1, "zone[%d] type %d: '%s' z_time %lu, z_refresh %u", zp-zones, zp->z_type, *(zp->z_origin) == '\0' ? "." : zp->z_origin, (u_long)zp->z_time, zp->z_refresh); } /* * Finish constructing a new zone. If valid, the constructed zone is * merged into the zone database. The zone_config used is invalid after * end_zone() completes. */ void end_zone(zone_config zh, int should_install) { struct zoneinfo *zp, *new_zp; const char *zname; symbol_value value; new_zp = zh.opaque; INSIST(new_zp != NULL); zname = (new_zp->z_origin[0] == '\0') ? "." : new_zp->z_origin; ns_debug(ns_log_config, 3, "end_zone('%s', %d)", zname, should_install); if (!should_install) { release_zone(new_zp); return; } if (!validate_zone(new_zp)) { ns_error(ns_log_config, "zone '%s' did not validate, skipping", zname); release_zone(new_zp); return; } zp = find_zone(new_zp->z_origin, new_zp->z_class); if (zp != NULL && zp->z_type != new_zp->z_type) { remove_zone(zp, "redefined"); zp = NULL; } if (zp == NULL) { zp = new_zone(); INSIST(zp != NULL); value.integer = (zp - zones); define_symbol(zone_symbol_table, new_zp->z_origin, new_zp->z_class, value, 0); } ns_debug(ns_log_config, 5, "zone '%s', type = %d, class = %d", zname, new_zp->z_type, new_zp->z_class); if (new_zp->z_source != NULL) ns_debug(ns_log_config, 5, " file = %s", new_zp->z_source); ns_debug(ns_log_config, 5, " checknames = %d", new_zp->z_checknames); if (new_zp->z_addrcnt != 0) { int i; ns_debug(ns_log_config, 5, " masters:"); for (i = 0; i < new_zp->z_addrcnt; i++) ns_debug(ns_log_config, 5, " %s", inet_ntoa(new_zp->z_addr[i])); } update_zone_info(zp, new_zp); release_zone(new_zp); zh.opaque = NULL; } int set_zone_type(zone_config zh, int type) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if type already set for this zone */ if (zp->z_type != 0) return (0); zp->z_type = type; return (1); } int set_zone_filename(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_source != NULL) return (0); zp->z_source = filename; return (1); } int set_zone_checknames(zone_config zh, enum severity s) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if checknames already set for this zone */ if (zp->z_checknames != not_set) return (0); zp->z_checknames = s; return (1); } int set_zone_ixfr_file(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_ixfr_base != NULL) return (0); zp->z_ixfr_base = filename; if (zp->z_ixfr_tmp == NULL) { int len = strlen(zp->z_ixfr_base) + (sizeof ".tmp"); char *str = (char *) memget(len); sprintf(str, "%s.tmp", zp->z_ixfr_base); zp->z_ixfr_tmp = savestr(str, 1); memput(str, len); } return (1); } int set_zone_ixfr_tmp(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_ixfr_tmp != NULL) return (0); zp->z_ixfr_tmp = filename; return (1); } int set_zone_dialup(zone_config zh, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); if (value) { zp->z_dialup = zdialup_yes; #ifdef BIND_NOTIFY zp->z_notify = notify_yes; #endif } else zp->z_dialup = zdialup_no; return (1); } int set_zone_notify(zone_config zh, enum notify value) { #ifdef BIND_NOTIFY struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_notify = value; #endif return (1); } int set_zone_maintain_ixfr_base(zone_config zh, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_maintain_ixfr_base = value; return (1); } int set_zone_update_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if update_acl already set for this zone */ if (zp->z_update_acl != NULL) return (0); zp->z_update_acl = iml; #ifdef BIND_UPDATE if (!ip_match_is_none(iml)) zp->z_flags |= Z_DYNAMIC; else ns_debug(ns_log_config, 3, "update acl is none for '%s'", zp->z_origin); #endif return (1); } int set_zone_query_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if allow-query acl already set for this zone */ if (zp->z_query_acl != NULL) return (0); zp->z_query_acl = iml; return (1); } int set_zone_master_port(zone_config zh, u_short port) { struct zoneinfo *zp = zh.opaque; zp->z_port = port; return (1); } int set_zone_transfer_source(zone_config zh, struct in_addr ina) { struct zoneinfo *zp = zh.opaque; zp->z_axfr_src = ina; return (1); } int set_zone_transfer_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if allow-transfer acl already set for this zone */ if (zp->z_transfer_acl != NULL) return (0); zp->z_transfer_acl = iml; return (1); } int set_zone_transfer_time_in(zone_config zh, long max_time) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if max-transfer-time-in already set for this zone */ if (zp->z_max_transfer_time_in) return (0); zp->z_max_transfer_time_in = max_time; return (1); } int set_zone_max_log_size_ixfr(zone_config zh, int size) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_max_log_size_ixfr = size; return (0); } int set_zone_pubkey(zone_config zh, const int flags, const int proto, const int alg, const char *str) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); INSIST(zp != NULL && zp->z_origin != NULL); return (add_trusted_key(zp->z_origin, flags, proto, alg, str)); } int set_trusted_key(const char *name, const int flags, const int proto, const int alg, const char *str) { INSIST(name != NULL); return (add_trusted_key(name, flags, proto, alg, str)); } int add_zone_master(zone_config zh, struct in_addr address, struct dst_key * key) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_addr[zp->z_addrcnt] = address; zp->z_keys[zp->z_addrcnt] = key; zp->z_addrcnt++; if (zp->z_addrcnt >= NSMAX) { ns_warning(ns_log_config, "NSMAX reached for zone '%s'", zp->z_origin); zp->z_addrcnt = NSMAX - 1; } return (1); } int add_zone_notify(zone_config zh, struct in_addr address) { #ifdef BIND_NOTIFY struct zoneinfo *zp; int i; zp = zh.opaque; INSIST(zp != NULL); /* Check for duplicates. */ for (i = 0; i < zp->z_notify_count; i++) { if (memcmp(zp->z_also_notify + i, &address, sizeof address) == 0) { ns_warning(ns_log_config, "duplicate also-notify address ignored [%s] for zone '%s'", inet_ntoa(address), zp->z_origin); return (1); } } i = 0; if (zp->z_also_notify == NULL) { zp->z_also_notify = memget(sizeof *zp->z_also_notify); if (zp->z_also_notify == NULL) i = 1; } else { register size_t size; register struct in_addr *an_tmp; size = zp->z_notify_count * sizeof *zp->z_also_notify; an_tmp = memget(size + sizeof *zp->z_also_notify); if (an_tmp == NULL) { i = 1; } else { memcpy(an_tmp, zp->z_also_notify, size); memput(zp->z_also_notify, size); zp->z_also_notify = an_tmp; } } if (i == 0) { zp->z_also_notify[zp->z_notify_count] = address; zp->z_notify_count++; } else { ns_warning(ns_log_config, "also-notify add failed (memget) [%s] for zone '%s'", inet_ntoa(address), zp->z_origin); } #endif return (1); } /* Options */ options new_options() { options op; char hostname[256]; op = (options)memget(sizeof (struct options)); if (op == NULL) panic("memget failed in new_options()", NULL); op->version = savestr(ShortVersion, 1); if (gethostname(hostname, sizeof(hostname)) == 0) op->hostname = savestr(hostname, 1); else op->hostname = NULL; op->directory = savestr(".", 1); op->pid_filename = savestr(_PATH_PIDFILE, 1); op->named_xfer = savestr(_PATH_XFER, 1); op->dump_filename = savestr(_PATH_DUMPFILE, 1); op->stats_filename = savestr(_PATH_STATS, 1); op->memstats_filename = savestr(_PATH_MEMSTATS, 1); op->flags = DEFAULT_OPTION_FLAGS; op->transfers_in = DEFAULT_XFERS_RUNNING; op->transfers_per_ns = DEFAULT_XFERS_PER_NS; op->transfers_out = 0; op->serial_queries = MAXQSERIAL; op->transfer_format = axfr_one_answer; op->max_transfer_time_in = MAX_XFER_TIME; memset(&op->query_source, 0, sizeof op->query_source); op->query_source.sin_family = AF_INET; op->query_source.sin_addr.s_addr = htonl(INADDR_ANY); op->query_source.sin_port = htons(0); /* INPORT_ANY */ op->axfr_src.s_addr = 0; #ifdef BIND_NOTIFY op->notify_count = 0; op->also_notify = NULL; #endif op->blackhole_acl = NULL; op->query_acl = NULL; op->transfer_acl = NULL; op->recursion_acl = NULL; op->sortlist = NULL; op->topology = NULL; op->data_size = 0UL; /* use system default */ op->stack_size = 0UL; /* use system default */ op->core_size = 0UL; /* use system default */ op->files = ULONG_MAX; /* unlimited */ op->check_names[primary_trans] = fail; op->check_names[secondary_trans] = warn; op->check_names[response_trans] = ignore; op->listen_list = NULL; op->fwdtab = NULL; /* XXX init forwarding */ op->clean_interval = 3600; op->interface_interval = 3600; op->stats_interval = 3600; op->ordering = NULL; op->max_ncache_ttl = DEFAULT_MAX_NCACHE_TTL; op->max_host_stats = 0; op->lame_ttl = NTTL; op->heartbeat_interval = 3600; op->max_log_size_ixfr = 0; op->minroots = MINROOTS; op->preferred_glue = 0; #ifdef BIND_NOTIFY op->notify = notify_yes; #endif return (op); } void free_options(options op) { INSIST(op != NULL); if (op->hostname) op->hostname = freestr(op->hostname); if (op->version) op->version = freestr(op->version); if (op->directory) op->directory = freestr(op->directory); if (op->pid_filename) op->pid_filename = freestr(op->pid_filename); if (op->named_xfer) op->named_xfer = freestr(op->named_xfer); if (op->dump_filename) op->dump_filename = freestr(op->dump_filename); if (op->stats_filename) op->stats_filename = freestr(op->stats_filename); if (op->memstats_filename) op->memstats_filename = freestr(op->memstats_filename); #ifdef BIND_NOTIFY if (op->also_notify) free_also_notify(op); #endif if (op->blackhole_acl) free_ip_match_list(op->blackhole_acl); if (op->query_acl) free_ip_match_list(op->query_acl); if (op->recursion_acl) free_ip_match_list(op->recursion_acl); if (op->transfer_acl) free_ip_match_list(op->transfer_acl); if (op->sortlist) free_ip_match_list(op->sortlist); if (op->ordering) free_rrset_order_list(op->ordering); if (op->topology) free_ip_match_list(op->topology); if (op->listen_list) free_listen_info_list(op->listen_list); if (op->fwdtab) free_forwarders(op->fwdtab); memput(op, sizeof *op); } static void set_boolean_option(u_int *op_flags, int bool_opt, int value) { INSIST(op_flags != NULL); switch (bool_opt) { #ifdef HITCOUNTS case OPTION_HITCOUNT: #endif /* HITCOUNTS */ case OPTION_NORECURSE: case OPTION_NOFETCHGLUE: case OPTION_FORWARD_ONLY: case OPTION_FAKE_IQUERY: case OPTION_SUPNOTIFY_INITIAL: case OPTION_NONAUTH_NXDOMAIN: case OPTION_MULTIPLE_CNAMES: case OPTION_USE_IXFR: case OPTION_MAINTAIN_IXFR_BASE: case OPTION_HOSTSTATS: case OPTION_DEALLOC_ON_EXIT: case OPTION_USE_ID_POOL: case OPTION_NORFC2308_TYPE1: case OPTION_NODIALUP: case OPTION_TREAT_CR_AS_SPACE: if (value) *op_flags |= bool_opt; else *op_flags &= ~bool_opt; break; default: panic("unexpected option in set_boolean_option", NULL); } } void set_global_boolean_option(options op, int bool_opt, int value) { INSIST(op != NULL); set_boolean_option(&op->flags, bool_opt, value); } void set_zone_boolean_option(zone_config zh, int bool_opt, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); set_boolean_option(&zp->z_options, bool_opt, value); /* Flag that zone option overrides corresponding global option */ zp->z_optset |= bool_opt; } #ifdef HAVE_GETRUSAGE enum limit { Datasize, Stacksize, Coresize, Files }; static struct rlimit initial_data_size; static struct rlimit initial_stack_size; static struct rlimit initial_core_size; static struct rlimit initial_num_files; static void get_initial_limits() { int fdlimit = evHighestFD(ev) + 1; # ifdef RLIMIT_DATA if (getrlimit(RLIMIT_DATA, &initial_data_size) < 0) ns_warning(ns_log_config, "getrlimit(DATA): %s", strerror(errno)); # endif # ifdef RLIMIT_STACK if (getrlimit(RLIMIT_STACK, &initial_stack_size) < 0) ns_warning(ns_log_config, "getrlimit(STACK): %s", strerror(errno)); # endif # ifdef RLIMIT_CORE if (getrlimit(RLIMIT_CORE, &initial_core_size) < 0) ns_warning(ns_log_config, "getrlimit(CORE): %s", strerror(errno)); # endif # ifdef RLIMIT_NOFILE if (getrlimit(RLIMIT_NOFILE, &initial_num_files) < 0) ns_warning(ns_log_config, "getrlimit(NOFILE): %s", strerror(errno)); else if (initial_num_files.rlim_cur > fdlimit) { initial_num_files.rlim_cur = fdlimit; if (initial_num_files.rlim_cur > initial_num_files.rlim_max) initial_num_files.rlim_max = fdlimit; if (setrlimit(RLIMIT_NOFILE, &initial_num_files) < 0) { ns_warning(ns_log_config, "setrlimit(files): %s", strerror(errno)); } else { ns_warning(ns_log_config, "limit files set to fdlimit (%d)", fdlimit); } } # endif } static void ns_rlimit(enum limit limit, u_long limit_value) { struct rlimit limits, old_limits; int rlimit = -1; int fdlimit = evHighestFD(ev) + 1; const char *name; rlimit_type value; if (limit_value == ULONG_MAX) { #ifndef RLIMIT_FILE_INFINITY if (limit == Files) value = MIN((rlimit_type)evHighestFD(ev) + 1, initial_num_files.rlim_max); else #endif value = (rlimit_type)RLIM_INFINITY; } else value = (rlimit_type)limit_value; limits.rlim_cur = limits.rlim_max = value; switch (limit) { case Datasize: #ifdef RLIMIT_DATA rlimit = RLIMIT_DATA; #endif name = "max data size"; if (value == 0) limits = initial_data_size; break; case Stacksize: #ifdef RLIMIT_STACK rlimit = RLIMIT_STACK; #endif name = "max stack size"; if (value == 0) limits = initial_stack_size; break; case Coresize: #ifdef RLIMIT_CORE rlimit = RLIMIT_CORE; #endif name = "max core size"; if (value == 0) limits = initial_core_size; break; case Files: #ifdef RLIMIT_NOFILE rlimit = RLIMIT_NOFILE; #endif name = "max number of open files"; if (value == 0) limits = initial_num_files; if ((int)value > fdlimit) limits.rlim_cur = limits.rlim_max = value = fdlimit; break; default: name = NULL; /* Make gcc happy. */ panic("impossible condition in ns_rlimit()", NULL); } if (rlimit == -1) { ns_warning(ns_log_config, "limit \"%s\" not supported on this system - ignored", name); return; } if (getrlimit(rlimit, &old_limits) < 0) { ns_warning(ns_log_config, "getrlimit(%s): %s", name, strerror(errno)); } if (user_id != 0 && limits.rlim_max == RLIM_INFINITY) limits.rlim_cur = limits.rlim_max = old_limits.rlim_max; if (setrlimit(rlimit, &limits) < 0) { ns_warning(ns_log_config, "setrlimit(%s): %s", name, strerror(errno)); return; } else { if (value == 0) ns_debug(ns_log_config, 3, "%s is default", name); else if (value == RLIM_INFINITY) ns_debug(ns_log_config, 3, "%s is unlimited", name); else #ifdef RLIMIT_LONGLONG ns_debug(ns_log_config, 3, "%s is %llu", name, (unsigned long long)value); #else ns_debug(ns_log_config, 3, "%s is %lu", name, value); #endif } } #endif /* HAVE_GETRUSAGE */ listen_info_list new_listen_info_list() { listen_info_list ll; ll = (listen_info_list)memget(sizeof (struct listen_info_list)); if (ll == NULL) panic("memget failed in new_listen_info_list()", NULL); ll->first = NULL; ll->last = NULL; return (ll); } void free_listen_info_list(listen_info_list ll) { listen_info li, next_li; INSIST(ll != NULL); for (li = ll->first; li != NULL; li = next_li) { next_li = li->next; free_ip_match_list(li->list); memput(li, sizeof *li); } memput(ll, sizeof *ll); } void add_listen_on(options op, u_short port, ip_match_list iml) { listen_info_list ll; listen_info ni; INSIST(op != NULL); if (op->listen_list == NULL) op->listen_list = new_listen_info_list(); ll = op->listen_list; ni = (listen_info)memget(sizeof (struct listen_info)); if (ni == NULL) panic("memget failed in add_listen_on", NULL); ni->port = port; ni->list = iml; ni->next = NULL; if (ll->last != NULL) ll->last->next = ni; ll->last = ni; if (ll->first == NULL) ll->first = ni; } FILE * write_open(char *filename) { FILE *stream; int fd; struct stat sb; int regular; if (stat(filename, &sb) < 0) { if (errno != ENOENT) { ns_error(ns_log_os, "write_open: stat of %s failed: %s", filename, strerror(errno)); return (NULL); } regular = 1; } else regular = (sb.st_mode & S_IFREG); if (!regular) { ns_error(ns_log_os, "write_open: %s isn't a regular file", filename); return (NULL); } (void)unlink(filename); fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (fd < 0) return (NULL); (void) fchown(fd, user_id, group_id); stream = fdopen(fd, "w"); if (stream == NULL) { (void)unlink(filename); (void)close(fd); } return (stream); } void update_pid_file() { FILE *fp; REQUIRE(server_options != NULL); REQUIRE(server_options->pid_filename != NULL); /* XXX */ ns_debug(ns_log_default, 1, "update_pid_file()"); if (current_pid_filename != NULL) { (void)unlink(current_pid_filename); current_pid_filename = freestr(current_pid_filename); } current_pid_filename = savestr(server_options->pid_filename, 0); if (current_pid_filename == NULL) { ns_error(ns_log_config, "savestr() failed in update_pid_file()"); return; } fp = write_open(current_pid_filename); if (fp != NULL) { (void) fprintf(fp, "%ld\n", (long)getpid()); (void) fclose(fp); } else ns_error(ns_log_config, "couldn't create pid file '%s'", server_options->pid_filename); } /* * XXX This function will eventually be public and will be relocated to * the UNIX OS support library. */ static int os_change_directory(const char *name) { struct stat sb; if (name == NULL || *name == '\0') { errno = EINVAL; return (0); } if (chdir(name) < 0) return (0); if (stat(name, &sb) < 0) { ns_error(ns_log_os, "stat(%s) failed: %s", name, strerror(errno)); return (1); } if (sb.st_mode & S_IWOTH) ns_warning(ns_log_os, "directory %s is world-writable", name); return (1); } static void periodic_getnetconf(evContext ctx, void *uap, struct timespec due, struct timespec inter) { UNUSED(ctx); UNUSED(uap); UNUSED(due); UNUSED(inter); getnetconf(1); } static int clean_interval = 0; static int interface_interval = 0; static int stats_interval = 0; static int heartbeat_interval = 0; static void set_interval_timer(int which_timer, int interval) { evTimerID *tid = NULL; evTimerFunc func = NULL; int changed = 0; switch (which_timer) { case CLEAN_TIMER: if (clean_interval != interval) changed = 1; clean_interval = interval; tid = &clean_timer; func = ns_cleancache; break; case INTERFACE_TIMER: if (interface_interval != interval) changed = 1; interface_interval = interval; tid = &interface_timer; func = periodic_getnetconf; break; case STATS_TIMER: if (stats_interval != interval) changed = 1; stats_interval = interval; tid = &stats_timer; func = ns_logstats; break; case HEARTBEAT_TIMER: if (heartbeat_interval != interval) changed = 1; heartbeat_interval = interval; tid = &heartbeat_timer; func = ns_heartbeat; break; default: ns_panic(ns_log_config, 1, "set_interval_timer: unknown timer %d", which_timer); } if ((active_timers & which_timer) != 0) { if (interval > 0) { if (changed && evResetTimer(ev, *tid, func, NULL, evAddTime(evNowTime(), evConsTime(interval, 0)), evConsTime(interval, 0)) < 0) ns_error(ns_log_config, "evResetTimer %d interval %d failed: %s", which_timer, interval, strerror(errno)); } else { if (evClearTimer(ev, *tid) < 0) ns_error(ns_log_config, "evClearTimer %d failed: %s", which_timer, strerror(errno)); else active_timers &= ~which_timer; } } else if (interval > 0) { if (evSetTimer(ev, func, NULL, evAddTime(evNowTime(), evConsTime(interval, 0)), evConsTime(interval, 0), tid) < 0) ns_error(ns_log_config, "evSetTimer %d interval %d failed: %s", which_timer, interval, strerror(errno)); else active_timers |= which_timer; } } /* * Set all named global options based on the global options structure * generated by the parser. */ void set_options(options op, int is_default) { INSIST(op != NULL); if (op->listen_list == NULL) { ip_match_list iml; ip_match_element ime; struct in_addr address; op->listen_list = new_listen_info_list(); address.s_addr = htonl(INADDR_ANY); iml = new_ip_match_list(); ime = new_ip_match_pattern(address, 0); add_to_ip_match_list(iml, ime); add_listen_on(op, htons(NS_DEFAULTPORT), iml); } if (op->topology == NULL) { ip_match_list iml; ip_match_element ime; /* default topology is { localhost; localnets; } */ iml = new_ip_match_list(); ime = new_ip_match_localhost(); add_to_ip_match_list(iml, ime); ime = new_ip_match_localnets(); add_to_ip_match_list(iml, ime); op->topology = iml; } if (server_options != NULL) free_options(server_options); server_options = op; /* XXX should validate pid filename */ INSIST(op->pid_filename != NULL); if (op->directory && !os_change_directory(op->directory)) ns_panic(ns_log_config, 0, "can't change directory to %s: %s", op->directory, strerror(errno)); /* XXX currently a value of 0 means "use default"; it would be better if the options block had a "attributes updated" vector (like the way X deals with GC updates) */ if (!op->transfers_in) op->transfers_in = DEFAULT_XFERS_RUNNING; else if (op->transfers_in > MAX_XFERS_RUNNING) { ns_warning(ns_log_config, "the maximum number of concurrent inbound transfers is %d", MAX_XFERS_RUNNING); op->transfers_in = MAX_XFERS_RUNNING; } if (!op->transfers_per_ns) op->transfers_per_ns = DEFAULT_XFERS_PER_NS; if (!op->max_transfer_time_in) op->max_transfer_time_in = MAX_XFER_TIME; /* XXX currently transfers_out is not used */ if (!op->max_ncache_ttl) op->max_ncache_ttl = DEFAULT_MAX_NCACHE_TTL; else if (op->max_ncache_ttl > max_cache_ttl) op->max_ncache_ttl = max_cache_ttl; if (op->lame_ttl > (3 * NTTL)) op->lame_ttl = 3 * NTTL; /* * Limits */ #ifdef HAVE_GETRUSAGE ns_rlimit(Datasize, op->data_size); ns_rlimit(Stacksize, op->stack_size); ns_rlimit(Coresize, op->core_size); ns_rlimit(Files, op->files); #else ns_info(ns_log_config, "cannot set resource limits on this system"); #endif /* * Timers */ set_interval_timer(CLEAN_TIMER, server_options->clean_interval); set_interval_timer(INTERFACE_TIMER, server_options->interface_interval); set_interval_timer(STATS_TIMER, server_options->stats_interval); set_interval_timer(HEARTBEAT_TIMER, server_options->heartbeat_interval); options_installed = 1; default_options_installed = is_default; } void use_default_options() { set_options(new_options(), 1); } /* * rrset order types */ static struct res_sym order_table [] = { { unknown_order, " unknown ", NULL }, /* can't match */ { fixed_order, "fixed", NULL }, { cyclic_order, "cyclic", NULL }, { random_order, "random", NULL }, { unknown_order, NULL, NULL } }; /* * Return the print name of the ordering value. */ const char * p_order(int order) { return (__sym_ntos(order_table, order, (int *)0)); } /* * Lookup the ordering by name and return the matching enum value. */ enum ordering lookup_ordering(const char *name) { int i; for (i = 0; order_table[i].name != NULL; i++) if (strcasecmp(name,order_table[i].name) == 0) return ((enum ordering)order_table[i].number); return (unknown_order); } /* * rrset-order Lists */ rrset_order_list new_rrset_order_list() { rrset_order_list rol ; rol = (rrset_order_list)memget(sizeof (struct rrset_order_list)); if (rol == NULL) panic("memget failed in new_rrset_order_list", NULL); rol->first = NULL; rol->last = NULL; return (rol); } void free_rrset_order_list(rrset_order_list rol) { rrset_order_element roe, next_element; for (roe = rol->first; roe != NULL; roe = next_element) { next_element = roe->next; roe->name = freestr(roe->name); memput(roe, sizeof (*roe)); } memput(rol, sizeof (*rol)); } void add_to_rrset_order_list(rrset_order_list rol, rrset_order_element roe) { INSIST(rol != NULL); INSIST(roe != NULL); if (rol->last != NULL) rol->last->next = roe; roe->next = NULL; rol->last = roe; if (rol->first == NULL) rol->first = roe; } #ifdef notyet /* XXX this isn't being used yet, but it probably should be. Where? */ void dprint_rrset_order_list(int category, rrset_order_list rol, int indent, char *allow, char *deny) { rrset_order_element roe ; char spaces[40+1]; INSIST(rol != NULL); if (indent > 40) indent = 40; if (indent) memset(spaces, ' ', indent); spaces[indent] = '\0'; for (roe = rol->first; roe != NULL; roe = roe->next) { ns_debug(category, 7, "%sclass %s type %s name %s order %s", spaces, p_class(roe->class), p_type(roe->type), roe->name, p_order(roe->order)); } } #endif rrset_order_element new_rrset_order_element(int class, int type, char *name, enum ordering order) { rrset_order_element roe; int i ; roe = (rrset_order_element)memget(sizeof (struct rrset_order_element)); if (roe == NULL) panic("memget failed in new_rrset_order_element", NULL); roe->class = class ; roe->type = type ; roe->name = name; roe->order = order; i = strlen(roe->name) - 1; INSIST (i >= 0); if (roe->name[i - 1] == '.') { /* We compare from right to left so we don't need a dot on the end. */ roe->name[i - 1] = '\0' ; } return roe ; } /* * IP Matching Lists */ ip_match_list new_ip_match_list() { ip_match_list iml; iml = (ip_match_list)memget(sizeof (struct ip_match_list)); if (iml == NULL) panic("memget failed in new_ip_match_list", NULL); iml->first = NULL; iml->last = NULL; return (iml); } void free_ip_match_list(ip_match_list iml) { ip_match_element ime, next_element; for (ime = iml->first; ime != NULL; ime = next_element) { next_element = ime->next; memput(ime, sizeof *ime); } memput(iml, sizeof *iml); } ip_match_element new_ip_match_pattern(struct in_addr address, u_int mask_bits) { ip_match_element ime; u_int32_t mask; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_pattern", NULL); ime->type = ip_match_pattern; ime->flags = 0; ime->u.direct.address = address; if (mask_bits == 0) /* can't shift >= the size of a type in bits, so we deal with an empty mask here */ mask = 0; else { /* set the 'mask_bits' most significant bits */ mask = 0xffffffffU; mask >>= (32 - mask_bits); mask <<= (32 - mask_bits); } mask = ntohl(mask); ime->u.direct.mask.s_addr = mask; ime->next = NULL; if (!ina_onnet(ime->u.direct.address, ime->u.direct.address, ime->u.direct.mask)) { memput(ime, sizeof *ime); ime = NULL; } return (ime); } ip_match_element new_ip_match_mask(struct in_addr address, struct in_addr mask) { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_pattern", NULL); ime->type = ip_match_pattern; ime->flags = 0; ime->u.direct.address = address; ime->u.direct.mask = mask; ime->next = NULL; if (!ina_onnet(ime->u.direct.address, ime->u.direct.address, ime->u.direct.mask)) { memput(ime, sizeof *ime); ime = NULL; } return (ime); } ip_match_element new_ip_match_indirect(ip_match_list iml) { ip_match_element ime; INSIST(iml != NULL); ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_indirect", NULL); ime->type = ip_match_indirect; ime->flags = 0; ime->u.indirect.list = iml; ime->next = NULL; return (ime); } ip_match_element new_ip_match_key(DST_KEY *dst_key) { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_key", NULL); ime->type = ip_match_key; ime->flags = 0; ime->u.key.key = dst_key; return (ime); } ip_match_element new_ip_match_localhost() { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_localhost", NULL); ime->type = ip_match_localhost; ime->flags = 0; ime->u.indirect.list = NULL; ime->next = NULL; return (ime); } ip_match_element new_ip_match_localnets() { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_localnets", NULL); ime->type = ip_match_localnets; ime->flags = 0; ime->u.indirect.list = NULL; ime->next = NULL; return (ime); } void ip_match_negate(ip_match_element ime) { if (ime->flags & IP_MATCH_NEGATE) ime->flags &= ~IP_MATCH_NEGATE; else ime->flags |= IP_MATCH_NEGATE; } void add_to_ip_match_list(ip_match_list iml, ip_match_element ime) { INSIST(iml != NULL); INSIST(ime != NULL); if (iml->last != NULL) iml->last->next = ime; ime->next = NULL; iml->last = ime; if (iml->first == NULL) iml->first = ime; } void dprint_ip_match_list(int category, ip_match_list iml, int indent, const char *allow, const char *deny) { ip_match_element ime; char spaces[40+1]; char addr_text[sizeof "255.255.255.255"]; char mask_text[sizeof "255.255.255.255"]; INSIST(iml != NULL); if (indent > 40) indent = 40; if (indent) memset(spaces, ' ', indent); spaces[indent] = '\0'; for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: memset(addr_text, 0, sizeof addr_text); strncpy(addr_text, inet_ntoa(ime->u.direct.address), ((sizeof addr_text) - 1)); memset(mask_text, 0, sizeof mask_text); strncpy(mask_text, inet_ntoa(ime->u.direct.mask), ((sizeof mask_text) - 1)); ns_debug(category, 7, "%s%saddr: %s, mask: %s", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, addr_text, mask_text); break; case ip_match_localhost: ns_debug(category, 7, "%s%slocalhost", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow); break; case ip_match_localnets: ns_debug(category, 7, "%s%slocalnets", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow); break; case ip_match_indirect: ns_debug(category, 7, "%s%sindirect list %p", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, ime->u.indirect.list); if (ime->u.indirect.list != NULL) dprint_ip_match_list(category, ime->u.indirect.list, indent+2, allow, deny); break; case ip_match_key: ns_debug(category, 7, "%s%skey %s", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, ime->u.key.key->dk_key_name); break; default: panic("unexpected ime type in dprint_ip_match_list()", NULL); } } } int ip_match_addr_or_key(ip_match_list iml, struct in_addr address, DST_KEY *key) { ip_match_element ime; int ret; int indirect; INSIST(iml != NULL); for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: if (key == NULL) { indirect = 0; break; } else { if (ns_samename(ime->u.key.key->dk_key_name, key->dk_key_name) == 1) return (1); else continue; } default: indirect = 0; panic("unexpected ime type in ip_match_addr_or_key()", NULL); } if (indirect) { ret = ip_match_addr_or_key(ime->u.indirect.list, address, key); if (ret > 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; return (ret); } } else { if (ina_onnet(address, ime->u.direct.address, ime->u.direct.mask)) { if (ime->flags & IP_MATCH_NEGATE) return (0); else return (1); } } } return (-1); } int ip_match_address(ip_match_list iml, struct in_addr address) { return ip_match_addr_or_key(iml, address, NULL); } int ip_addr_or_key_allowed(ip_match_list iml, struct in_addr address, DST_KEY *key) { int ret; if (iml == NULL) return (0); ret = ip_match_addr_or_key(iml, address, key); if (ret < 0) ret = 0; return (ret); } int ip_address_allowed(ip_match_list iml, struct in_addr address) { return(ip_addr_or_key_allowed(iml, address, NULL)); } int ip_match_network(ip_match_list iml, struct in_addr address, struct in_addr mask) { ip_match_element ime; int ret; int indirect; INSIST(iml != NULL); for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: indirect = 0; break; default: indirect = 0; /* Make gcc happy. */ panic("unexpected ime type in ip_match_network()", NULL); } if (indirect) { ret = ip_match_network(ime->u.indirect.list, address, mask); if (ret >= 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; return (ret); } } else { if (address.s_addr == ime->u.direct.address.s_addr && mask.s_addr == ime->u.direct.mask.s_addr) { if (ime->flags & IP_MATCH_NEGATE) return (0); else return (1); } } } return (-1); } int distance_of_address(ip_match_list iml, struct in_addr address) { ip_match_element ime; int ret; int indirect; int distance; INSIST(iml != NULL); for (distance = 1, ime = iml->first; ime != NULL; ime = ime->next, distance++) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: indirect = 0; return (-1); default: indirect = 0; /* Make gcc happy. */ panic("unexpected ime type in distance_of_address()", NULL); } if (indirect) { ret = ip_match_address(ime->u.indirect.list, address); if (ret >= 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; if (distance > MAX_TOPOLOGY_DISTANCE) distance = MAX_TOPOLOGY_DISTANCE; if (ret) return (distance); else return (MAX_TOPOLOGY_DISTANCE); } } else { if (ina_onnet(address, ime->u.direct.address, ime->u.direct.mask)) { if (distance > MAX_TOPOLOGY_DISTANCE) distance = MAX_TOPOLOGY_DISTANCE; if (ime->flags & IP_MATCH_NEGATE) return (MAX_TOPOLOGY_DISTANCE); else return (distance); } } } return (UNKNOWN_TOPOLOGY_DISTANCE); } int ip_match_is_none(ip_match_list iml) { ip_match_element ime; if ((iml == NULL) || (iml->first == NULL)) return (1); ime = iml->first; if (ime->type == ip_match_indirect) { if (ime->flags & IP_MATCH_NEGATE) return (0); iml = ime->u.indirect.list; if ((iml == NULL) || (iml->first == NULL)) return (0); ime = iml->first; } if (ime->type == ip_match_pattern) { if ((ime->flags & IP_MATCH_NEGATE) && ime->u.direct.address.s_addr == 0 && ime->u.direct.mask.s_addr == 0) return (1); } return (0); } /* * find_forwarder finds the fwddata structure for an address, * allocating one if we can't find one already existing. */ static struct fwddata * find_forwarder(struct in_addr address) { struct fwddata *fdp; struct fwddata **fdpp = NULL; register int i; for (i = 0; i < fwddata_count; i++) { fdp = fwddata[i]; if (fdp == NULL) { if (fdpp == NULL) fdpp = &fwddata[i]; continue; } if (memcmp(&fdp->fwdaddr.sin_addr, &address, sizeof(address)) == 0) { fdp->ref_count++; return (fdp); } } fdp = (struct fwddata *)memget(sizeof(struct fwddata)); if (!fdp) panic("memget failed in find_forwarder", NULL); memset(&fdp->fwdaddr, 0, sizeof(fdp->fwdaddr)); fdp->fwdaddr.sin_family = AF_INET; fdp->fwdaddr.sin_addr = address; fdp->fwdaddr.sin_port = ns_port; fdp->ns = savedata(C_IN, T_NS, 0, NULL, 0); if (!fdp->ns) panic("memget failed in find_forwarder", NULL); fdp->nsdata = savedata(C_IN, T_A, 0, NULL, 0); if (!fdp->nsdata) panic("memget failed in find_forwarder", NULL); fdp->nsdata->d_nstime = 1 + (int)(25.0*rand()/(RAND_MAX + 1.0)); fdp->ref_count = 1; if (fdpp != NULL) { *fdpp = fdp; return (fdp); } i = 0; if (fwddata == NULL) { fwddata = memget(sizeof *fwddata); if (fwddata == NULL) i = 1; } else { register size_t size; register struct fwddata **an_tmp; size = fwddata_count * sizeof *fwddata; an_tmp = memget(size + sizeof *fwddata); if (an_tmp == NULL) { i = 1; } else { memcpy(an_tmp, fwddata, size); memput(fwddata, size); fwddata = an_tmp; } } if (i == 0) { fwddata[fwddata_count] = fdp; fwddata_count++; } else { ns_warning(ns_log_config, "forwarder add failed (memget) [%s]", inet_ntoa(address)); } return (fdp); } /* * Forwarder glue * * XXX This will go away when the rest of bind understands * forward zones. */ static void add_forwarder(struct fwdinfo **fipp, struct in_addr address) { struct fwdinfo *fip = *fipp, *ftp = NULL; struct fwddata *fdp; #ifdef FWD_LOOP if (aIsUs(address)) { ns_error(ns_log_config, "forwarder '%s' ignored, my address", inet_ntoa(address)); return; } #endif /* FWD_LOOP */ /* On multiple forwarder lines, move to end of the list. */ while (fip != NULL && fip->next != NULL) fip = fip->next; fdp = find_forwarder(address); ftp = (struct fwdinfo *)memget(sizeof(struct fwdinfo)); if (!ftp) panic("memget failed in add_forwarder", NULL); ftp->fwddata = fdp; ftp->next = NULL; if (fip == NULL) *fipp = ftp; /* First time only */ else fip->next = ftp; } void free_also_notify(options op) { #ifdef BIND_NOTIFY memput(op->also_notify, op->notify_count * sizeof *op->also_notify); op->also_notify = NULL; op->notify_count = 0; #endif } int add_global_also_notify(options op, struct in_addr address) { #ifdef BIND_NOTIFY int i; INSIST(op != NULL); ns_debug(ns_log_config, 2, "adding global notify %s", inet_ntoa(address)); /* Check for duplicates. */ for (i = 0; i < op->notify_count; i++) { if (memcmp(op->also_notify + i, &address, sizeof address) == 0) { ns_warning(ns_log_config, "duplicate global also-notify address ignored [%s]", inet_ntoa(address)); return (1); } } i = 0; if (op->also_notify == NULL) { op->also_notify = memget(sizeof *op->also_notify); if (op->also_notify == NULL) i = 1; } else { register size_t size; register struct in_addr *an_tmp; size = op->notify_count * sizeof *op->also_notify; an_tmp = memget(size + sizeof *op->also_notify); if (an_tmp == NULL) { i = 1; } else { memcpy(an_tmp, op->also_notify, size); memput(op->also_notify, size); op->also_notify = an_tmp; } } if (i == 0) { op->also_notify[op->notify_count] = address; op->notify_count++; } else { ns_warning(ns_log_config, "global also-notify add failed (memget) [%s]", inet_ntoa(address)); } #endif return (1); } void add_global_forwarder(options op, struct in_addr address) { INSIST(op != NULL); ns_debug(ns_log_config, 2, "adding default forwarder %s", inet_ntoa(address)); add_forwarder(&op->fwdtab, address); } void set_zone_forward(zone_config zh) { struct zoneinfo *zp; zp = zh.opaque; zp->z_flags |= Z_FORWARD_SET; set_zone_boolean_option(zh, OPTION_FORWARD_ONLY, 0); } void add_zone_forwarder(zone_config zh, struct in_addr address) { struct zoneinfo *zp; const char *zname; zp = zh.opaque; INSIST(zp != NULL); zname = (zp->z_origin[0] == '\0') ? "." : zp->z_origin; ns_debug(ns_log_config, 2, "adding forwarder %s for zone zone '%s'", inet_ntoa(address), zname); zp->z_flags |= Z_FORWARD_SET; add_forwarder(&zp->z_fwdtab, address); } void free_forwarders(struct fwdinfo *fwdtab) { struct fwdinfo *ftp, *fnext; int i; for (ftp = fwdtab; ftp != NULL; ftp = fnext) { fnext = ftp->next; if (--ftp->fwddata->ref_count == 0) { for (i = 0 ; i < fwddata_count; i++) if (fwddata[i] == ftp->fwddata) { fwddata[i] = NULL; break; } db_detach(&ftp->fwddata->ns); db_detach(&ftp->fwddata->nsdata); memput(ftp->fwddata, sizeof *ftp->fwddata); } memput(ftp, sizeof *ftp); } fwdtab = NULL; } /* * Servers */ static server_info new_server(struct in_addr address) { server_info si; si = (server_info)memget(sizeof (struct server_info)); if (si == NULL) panic("memget failed in new_server()", NULL); si->address = address; si->flags = 0U; si->transfers = 0; si->transfer_format = axfr_use_default; si->key_list = NULL; si->next = NULL; if (server_options->flags & OPTION_MAINTAIN_IXFR_BASE) si->flags |= SERVER_INFO_SUPPORT_IXFR; else si->flags &= ~SERVER_INFO_SUPPORT_IXFR; si->flags |= SERVER_INFO_EDNS; return (si); } static void free_server(server_info si) { if (si->key_list) free_key_info_list(si->key_list); memput(si, sizeof *si); } server_info find_server(struct in_addr address) { server_info si; for (si = nameserver_info; si != NULL; si = si->next) if (si->address.s_addr == address.s_addr) break; return (si); } static void add_server(server_info si) { ip_match_element ime; si->next = nameserver_info; nameserver_info = si; /* * To ease transition, we'll add bogus nameservers to an * ip matching list. This will probably be redone when the * merging of nameserver data structures occurs. */ if (si->flags & SERVER_INFO_BOGUS) { ime = new_ip_match_pattern(si->address, 32); INSIST(ime != NULL); add_to_ip_match_list(bogus_nameservers, ime); } ns_debug(ns_log_config, 3, "server %s: flags %08x transfers %d", inet_ntoa(si->address), si->flags, si->transfers); if (si->key_list != NULL) dprint_key_info_list(si->key_list); } static void free_nameserver_info() { server_info si_next, si; for (si = nameserver_info; si != NULL; si = si_next) { si_next = si->next; free_server(si); } nameserver_info = NULL; if (bogus_nameservers != NULL) { free_ip_match_list(bogus_nameservers); bogus_nameservers = NULL; } } static void free_secretkey_info() { if (secretkey_info != NULL) { free_key_info_list(secretkey_info); secretkey_info = NULL; } } server_config begin_server(struct in_addr address) { server_config sc; sc.opaque = new_server(address); return (sc); } void end_server(server_config sc, int should_install) { server_info si; si = sc.opaque; INSIST(si != NULL); if (should_install) add_server(si); else free_server(si); sc.opaque = NULL; } void set_server_option(server_config sc, int bool_opt, int value) { server_info si; si = sc.opaque; INSIST(si != NULL); switch (bool_opt) { case SERVER_INFO_BOGUS: case SERVER_INFO_SUPPORT_IXFR: case SERVER_INFO_EDNS: if (value) si->flags |= bool_opt; else si->flags &= ~bool_opt; break; default: panic("unexpected option in set_server_option", NULL); } } void set_server_transfers(server_config sc, int transfers) { server_info si; si = sc.opaque; INSIST(si != NULL); if (transfers < 0) transfers = 0; si->transfers = transfers; } void set_server_transfer_format(server_config sc, enum axfr_format transfer_format) { server_info si; si = sc.opaque; INSIST(si != NULL); si->transfer_format = transfer_format; } void add_server_key_info(server_config sc, DST_KEY *dst_key) { server_info si; si = sc.opaque; INSIST(si != NULL); if (si->key_list == NULL) si->key_list = new_key_info_list(); add_to_key_info_list(si->key_list, dst_key); } /* * Keys */ DST_KEY * new_key_info(char *name, char *algorithm, char *secret) { DST_KEY *dst_key; int alg, blen; u_char buffer[1024]; INSIST(name != NULL); INSIST(algorithm != NULL); INSIST(secret != NULL); alg = tsig_alg_value(algorithm); if (alg == -1) { ns_warning(ns_log_config, "Unsupported TSIG algorithm %s", algorithm); return (NULL); } blen = b64_pton(secret, buffer, sizeof(buffer)); if (blen < 0) { ns_warning(ns_log_config, "Invalid TSIG secret \"%s\"", secret); return (NULL); } dst_key = dst_buffer_to_key(name, alg, NS_KEY_TYPE_AUTH_ONLY|NS_KEY_NAME_ENTITY, NS_KEY_PROT_ANY, buffer, blen); if (dst_key == NULL) ns_warning(ns_log_config, "dst_buffer_to_key failed in new_key_info"); return (dst_key); } void free_key_info(DST_KEY *dst_key) { INSIST(dst_key != NULL); dst_free_key(dst_key); } DST_KEY * find_key(char *name, char *algorithm) { key_list_element ke; if (secretkey_info == NULL) return (NULL); for (ke = secretkey_info->first; ke != NULL; ke = ke->next) { DST_KEY *dst_key = ke->key; if (ns_samename(name, dst_key->dk_key_name) != 1) continue; if (algorithm == NULL || dst_key->dk_alg == tsig_alg_value(algorithm)) break; } if (ke == NULL) return (NULL); return (ke->key); } void dprint_key_info(DST_KEY *dst_key) { INSIST(dst_key != NULL); ns_debug(ns_log_config, 7, "key %s", dst_key->dk_key_name); ns_debug(ns_log_config, 7, " algorithm %d", dst_key->dk_alg); } key_info_list new_key_info_list() { key_info_list kil; kil = (key_info_list)memget(sizeof (struct key_info_list)); if (kil == NULL) panic("memget failed in new_key_info_list()", NULL); kil->first = NULL; kil->last = NULL; return (kil); } void free_key_info_list(key_info_list kil) { key_list_element kle, kle_next; INSIST(kil != NULL); for (kle = kil->first; kle != NULL; kle = kle_next) { kle_next = kle->next; /* note we do NOT free kle->info */ memput(kle, sizeof *kle); } memput(kil, sizeof *kil); } void add_to_key_info_list(key_info_list kil, DST_KEY *dst_key) { key_list_element kle; INSIST(kil != NULL); INSIST(dst_key != NULL); kle = (key_list_element)memget(sizeof (struct key_list_element)); if (kle == NULL) panic("memget failed in add_to_key_info_list()", NULL); kle->key = dst_key; if (kil->last != NULL) kil->last->next = kle; kle->next = NULL; kil->last = kle; if (kil->first == NULL) kil->first = kle; } void dprint_key_info_list(key_info_list kil) { key_list_element kle; INSIST(kil != NULL); for (kle = kil->first; kle != NULL; kle = kle->next) dprint_key_info(kle->key); } /* * Logging. */ log_config begin_logging() { log_config log_cfg; log_context lc; log_cfg = (log_config)memget(sizeof (struct log_config)); if (log_cfg == NULL) ns_panic(ns_log_config, 0, "memget failed creating log_config"); if (log_new_context(ns_log_max_category, logging_categories, &lc) < 0) ns_panic(ns_log_config, 0, "log_new_context() failed: %s", strerror(errno)); log_cfg->log_ctx = lc; log_cfg->eventlib_channel = NULL; log_cfg->packet_channel = NULL; log_cfg->default_debug_active = 0; return (log_cfg); } void add_log_channel(log_config log_cfg, int category, log_channel chan) { log_channel_type type; INSIST(log_cfg != NULL); type = log_get_channel_type(chan); if (category == ns_log_eventlib) { if (type != log_file && type != log_null) { ns_error(ns_log_config, "must specify a file or null channel for the eventlib category"); return; } if (log_cfg->eventlib_channel != NULL) { ns_error(ns_log_config, "only one channel allowed for the eventlib category"); return; } log_cfg->eventlib_channel = chan; } if (category == ns_log_packet) { if (type != log_file && type != log_null) { ns_error(ns_log_config, "must specify a file or null channel for the packet category"); return; } if (log_cfg->packet_channel != NULL) { ns_error(ns_log_config, "only one channel allowed for the packet category"); return; } log_cfg->packet_channel = chan; } if (log_add_channel(log_cfg->log_ctx, category, chan) < 0) { ns_error(ns_log_config, "log_add_channel() failed"); return; } if (chan == debug_channel) log_cfg->default_debug_active = 1; } void open_special_channels() { int using_null = 0; if (log_open_stream(eventlib_channel) == NULL) { eventlib_channel = null_channel; using_null = 1; } if (log_open_stream(packet_channel) == NULL) { packet_channel = null_channel; using_null = 1; } if (using_null && log_open_stream(null_channel) == NULL) ns_panic(ns_log_config, 1, "couldn't open null channel"); } void set_logging(log_config log_cfg, int is_default) { log_context lc; INSIST(log_cfg != NULL); lc = log_cfg->log_ctx; /* * Add the default category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_default)) { add_log_channel(log_cfg, ns_log_default, debug_channel); add_log_channel(log_cfg, ns_log_default, syslog_channel); } /* * Add the panic category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_panic)) { add_log_channel(log_cfg, ns_log_panic, stderr_channel); add_log_channel(log_cfg, ns_log_panic, syslog_channel); } /* * Add the eventlib category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_eventlib)) add_log_channel(log_cfg, ns_log_eventlib, debug_channel); /* * Add the packet category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_packet)) add_log_channel(log_cfg, ns_log_packet, debug_channel); #ifdef DEBUG /* * Preserve debugging state. */ log_option(lc, LOG_OPTION_DEBUG, debug); log_option(lc, LOG_OPTION_LEVEL, debug); #endif /* * Special case for query-log, so we can co-exist with the command * line option and SIGWINCH. */ if (log_category_is_active(lc, ns_log_queries)) qrylog = 1; /* * Cleanup the old context. */ if (need_logging_free) log_free_context(log_ctx); /* * The default file channels will never have their reference counts * drop to zero, and so they will not be closed by the logging system * when log_free_context() is called. We don't want to keep files * open unnecessarily, and we want them to behave like user-created * channels, so we close them here. */ if (log_get_stream(debug_channel) != stderr) (void)log_close_stream(debug_channel); (void)log_close_stream(null_channel); /* * Install the new context. */ log_ctx = lc; eventlib_channel = log_cfg->eventlib_channel; packet_channel = log_cfg->packet_channel; #ifdef DEBUG if (debug) { open_special_channels(); evSetDebug(ev, debug, log_get_stream(eventlib_channel)); } #endif log_ctx_valid = 1; need_logging_free = 1; logging_installed = 1; default_logging_installed = is_default; } void end_logging(log_config log_cfg, int should_install) { if (should_install) set_logging(log_cfg, 0); else log_free_context(log_cfg->log_ctx); memput(log_cfg, sizeof (struct log_config)); } void use_default_logging() { log_config log_cfg; log_cfg = begin_logging(); set_logging(log_cfg, 1); memput(log_cfg, sizeof (struct log_config)); } static void init_default_log_channels() { u_int flags; const char *name; FILE *stream; syslog_channel = log_new_syslog_channel(0, log_info, ISC_FACILITY); if (syslog_channel == NULL || log_inc_references(syslog_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create syslog_channel"); flags = LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; if (foreground) { name = NULL; stream = stderr; } else { name = _PATH_DEBUG; stream = NULL; } debug_channel = log_new_file_channel(flags, log_info, name, stream, 0, ULONG_MAX); if (debug_channel == NULL || log_inc_references(debug_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create debug_channel"); log_set_file_owner(debug_channel, user_id, group_id); stderr_channel = log_new_file_channel(0, log_info, NULL, stderr, 0, ULONG_MAX); if (stderr_channel == NULL || log_inc_references(stderr_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create stderr_channel"); log_set_file_owner(stderr_channel, user_id, group_id); null_channel = log_new_file_channel(LOG_CHANNEL_OFF, log_info, _PATH_DEVNULL, NULL, 0, ULONG_MAX); if (null_channel == NULL || log_inc_references(null_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create null_channel"); log_set_file_owner(null_channel, user_id, group_id); } static void shutdown_default_log_channels() { log_free_channel(syslog_channel); log_free_channel(debug_channel); log_free_channel(stderr_channel); log_free_channel(null_channel); } void init_logging() { int size; const struct ns_sym *s; char category_name[256]; size = ns_log_max_category * (sizeof (char *)); logging_categories = (char **)memget(size); if (logging_categories == NULL) ns_panic(ns_log_config, 0, "memget failed in init_logging"); memset(logging_categories, 0, size); for (s = category_constants; s != NULL && s->name != NULL; s++) { sprintf(category_name, "%s: ", s->name); logging_categories[s->number] = savestr(category_name, 1); } init_default_log_channels(); use_default_logging(); } void shutdown_logging() { int size; const struct ns_sym *s; evSetDebug(ev, 0, NULL); shutdown_default_log_channels(); log_free_context(log_ctx); for (s = category_constants; s != NULL && s->name != NULL; s++) logging_categories[s->number] = freestr(logging_categories[s->number]); size = ns_log_max_category * (sizeof (char *)); memput(logging_categories, size); logging_categories = NULL; } /* * Main Loader */ void init_configuration() { /* * Remember initial limits for use if "default" is specified in * a config file. */ #ifdef HAVE_GETRUSAGE get_initial_limits(); #endif zone_symbol_table = new_symbol_table(ZONE_SYM_TABLE_SIZE, NULL); use_default_options(); parser_initialize(); ns_ctl_initialize(); config_initialized = 1; } void shutdown_configuration() { REQUIRE(config_initialized); ns_ctl_shutdown(); if (server_options != NULL) { free_options(server_options); server_options = NULL; } if (current_pid_filename != NULL) current_pid_filename = freestr(current_pid_filename); free_nameserver_info(); free_secretkey_info(); free_symbol_table(zone_symbol_table); parser_shutdown(); if (fwddata != NULL) memput(fwddata, fwddata_count * sizeof *fwddata); fwddata = NULL; fwddata_count = 0; config_initialized = 0; } time_t load_configuration(const char *filename) { time_t mtime; REQUIRE(config_initialized); ns_debug(ns_log_config, 3, "load configuration %s", filename); loading = 1; /* * Clean up any previous configuration and initialize * global data structures we'll be updating. */ free_nameserver_info(); free_secretkey_info(); bogus_nameservers = new_ip_match_list(); options_installed = 0; logging_installed = 0; mtime = parse_configuration(filename); /* * If the user didn't specify logging or options, but they previously * had specified one or both of them, then we need to * re-establish the default environment. We have to be careful * about when we install default options because the parser * must respect limits (e.g. data-size, number of open files) * specified in the options file. In the ordinary case where the * options section isn't changing on a zone reload, it would be bad * to lower these limits temporarily, because we might not survive * to the point where they get raised back again. The logging case * has similar motivation -- we don't want to override the existing * logging scheme (perhaps causing log messages to go somewhere * unexpected) when the user hasn't expressed a desire for a new * scheme. */ if (!logging_installed) use_default_logging(); if (!options_installed && !default_options_installed) { use_default_options(); ns_warning(ns_log_config, "re-establishing default options"); } update_pid_file(); /* Init or reinit the interface/port list and associated sockets. */ getnetconf(0); opensocket_f(); initial_configuration = 0; loading = 0; /* release queued notifies */ notify_afterload(); return (mtime); }