diff options
Diffstat (limited to 'ntpd/ntp_config.c')
-rw-r--r-- | ntpd/ntp_config.c | 4957 |
1 files changed, 4957 insertions, 0 deletions
diff --git a/ntpd/ntp_config.c b/ntpd/ntp_config.c new file mode 100644 index 0000000..0f48983 --- /dev/null +++ b/ntpd/ntp_config.c @@ -0,0 +1,4957 @@ +/* ntp_config.c + * + * This file contains the ntpd configuration code. + * + * Written By: Sachin Kamboj + * University of Delaware + * Newark, DE 19711 + * Some parts borrowed from the older ntp_config.c + * Copyright (c) 2006 + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_NETINFO +# include <netinfo/ni.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif +#include <signal.h> +#ifndef SIGCHLD +# define SIGCHLD SIGCLD +#endif +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif + +#include <isc/net.h> +#include <isc/result.h> + +#include "ntp.h" +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_filegen.h" +#include "ntp_stdlib.h" +#include "lib_strbuf.h" +#include "ntp_assert.h" +#include "ntp_random.h" +/* + * [Bug 467]: Some linux headers collide with CONFIG_PHONE and CONFIG_KEYS + * so #include these later. + */ +#include "ntp_config.h" +#include "ntp_cmdargs.h" +#include "ntp_scanner.h" +#include "ntp_parser.h" +#include "ntpd-opts.h" + + +/* Bison still(!) does not emit usable prototypes for the calling code */ +int yyparse (struct FILE_INFO *ip_file); + +/* list of servers from command line for config_peers() */ +int cmdline_server_count; +char ** cmdline_servers; + +/* set to zero if admin doesn't want memory locked */ +int do_memlock = 1; + +/* + * "logconfig" building blocks + */ +struct masks { + const char * const name; + const u_int32 mask; +}; + +static struct masks logcfg_class[] = { + { "clock", NLOG_OCLOCK }, + { "peer", NLOG_OPEER }, + { "sync", NLOG_OSYNC }, + { "sys", NLOG_OSYS }, + { NULL, 0 } +}; + +/* logcfg_noclass_items[] masks are complete and must not be shifted */ +static struct masks logcfg_noclass_items[] = { + { "allall", NLOG_SYSMASK | NLOG_PEERMASK | NLOG_CLOCKMASK | NLOG_SYNCMASK }, + { "allinfo", NLOG_SYSINFO | NLOG_PEERINFO | NLOG_CLOCKINFO | NLOG_SYNCINFO }, + { "allevents", NLOG_SYSEVENT | NLOG_PEEREVENT | NLOG_CLOCKEVENT | NLOG_SYNCEVENT }, + { "allstatus", NLOG_SYSSTATUS | NLOG_PEERSTATUS | NLOG_CLOCKSTATUS | NLOG_SYNCSTATUS }, + { "allstatistics", NLOG_SYSSTATIST | NLOG_PEERSTATIST | NLOG_CLOCKSTATIST | NLOG_SYNCSTATIST }, + /* the remainder are misspellings of clockall, peerall, sysall, and syncall. */ + { "allclock", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OCLOCK }, + { "allpeer", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OPEER }, + { "allsys", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OSYS }, + { "allsync", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OSYNC }, + { NULL, 0 } +}; + +/* logcfg_class_items[] masks are shiftable by NLOG_O* counts */ +static struct masks logcfg_class_items[] = { + { "all", NLOG_INFO | NLOG_EVENT | NLOG_STATUS | NLOG_STATIST }, + { "info", NLOG_INFO }, + { "events", NLOG_EVENT }, + { "status", NLOG_STATUS }, + { "statistics", NLOG_STATIST }, + { NULL, 0 } +}; + +typedef struct peer_resolved_ctx_tag { + int flags; + int host_mode; /* T_* token identifier */ + u_short family; + keyid_t keyid; + u_char hmode; /* MODE_* */ + u_char version; + u_char minpoll; + u_char maxpoll; + u_int32 ttl; + const char * group; +} peer_resolved_ctx; + +/* Limits */ +#define MAXPHONE 10 /* maximum number of phone strings */ +#define MAXPPS 20 /* maximum length of PPS device string */ + +/* + * Miscellaneous macros + */ +#define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0') +#define ISSPACE(c) ((c) == ' ' || (c) == '\t') + +/* + * Definitions of things either imported from or exported to outside + */ +extern int yydebug; /* ntp_parser.c (.y) */ +int curr_include_level; /* The current include level */ +struct FILE_INFO *fp[MAXINCLUDELEVEL+1]; +config_tree cfgt; /* Parser output stored here */ +struct config_tree_tag *cfg_tree_history; /* History of configs */ +char *sys_phone[MAXPHONE] = {NULL}; /* ACTS phone numbers */ +char default_keysdir[] = NTP_KEYSDIR; +char *keysdir = default_keysdir; /* crypto keys directory */ +char * saveconfigdir; +#if defined(HAVE_SCHED_SETSCHEDULER) +int config_priority_override = 0; +int config_priority; +#endif + +const char *config_file; +static char default_ntp_signd_socket[] = +#ifdef NTP_SIGND_PATH + NTP_SIGND_PATH; +#else + ""; +#endif +char *ntp_signd_socket = default_ntp_signd_socket; +#ifdef HAVE_NETINFO +struct netinfo_config_state *config_netinfo = NULL; +int check_netinfo = 1; +#endif /* HAVE_NETINFO */ +#ifdef SYS_WINNT +char *alt_config_file; +LPTSTR temp; +char config_file_storage[MAX_PATH]; +char alt_config_file_storage[MAX_PATH]; +#endif /* SYS_WINNT */ + +#ifdef HAVE_NETINFO +/* + * NetInfo configuration state + */ +struct netinfo_config_state { + void *domain; /* domain with config */ + ni_id config_dir; /* ID config dir */ + int prop_index; /* current property */ + int val_index; /* current value */ + char **val_list; /* value list */ +}; +#endif + +struct REMOTE_CONFIG_INFO remote_config; /* Remote configuration buffer and + pointer info */ +int input_from_file = 1; /* A boolean flag, which when set, indicates that + the input is to be taken from the configuration + file, instead of the remote-configuration buffer + */ + +int old_config_style = 1; /* A boolean flag, which when set, + * indicates that the old configuration + * format with a newline at the end of + * every command is being used + */ +int cryptosw; /* crypto command called */ + +extern char *stats_drift_file; /* name of the driftfile */ + +#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED +/* + * backwards compatibility flags + */ +bc_entry bc_list[] = { + { T_Bc_bugXXXX, 1 } /* default enabled */ +}; + +/* + * declare an int pointer for each flag for quick testing without + * walking bc_list. If the pointer is consumed by libntp rather + * than ntpd, declare it in a libntp source file pointing to storage + * initialized with the appropriate value for other libntp clients, and + * redirect it to point into bc_list during ntpd startup. + */ +int *p_bcXXXX_enabled = &bc_list[0].enabled; +#endif + +/* FUNCTION PROTOTYPES */ + +static void init_syntax_tree(config_tree *); +static void apply_enable_disable(attr_val_fifo *q, int enable); + +#ifdef FREE_CFG_T +static void free_auth_node(config_tree *); +static void free_all_config_trees(void); + +static void free_config_access(config_tree *); +static void free_config_auth(config_tree *); +static void free_config_fudge(config_tree *); +static void free_config_logconfig(config_tree *); +static void free_config_monitor(config_tree *); +static void free_config_nic_rules(config_tree *); +static void free_config_other_modes(config_tree *); +static void free_config_peers(config_tree *); +static void free_config_phone(config_tree *); +static void free_config_reset_counters(config_tree *); +static void free_config_rlimit(config_tree *); +static void free_config_setvar(config_tree *); +static void free_config_system_opts(config_tree *); +static void free_config_tinker(config_tree *); +static void free_config_tos(config_tree *); +static void free_config_trap(config_tree *); +static void free_config_ttl(config_tree *); +static void free_config_unpeers(config_tree *); +static void free_config_vars(config_tree *); + +#ifdef SIM +static void free_config_sim(config_tree *); +#endif +static void destroy_address_fifo(address_fifo *); +#define FREE_ADDRESS_FIFO(pf) \ + do { \ + destroy_address_fifo(pf); \ + (pf) = NULL; \ + } while (0) + void free_all_config_trees(void); /* atexit() */ +static void free_config_tree(config_tree *ptree); +#endif /* FREE_CFG_T */ + +static void destroy_restrict_node(restrict_node *my_node); +static int is_sane_resolved_address(sockaddr_u *peeraddr, int hmode); +static void save_and_apply_config_tree(void); +static void destroy_int_fifo(int_fifo *); +#define FREE_INT_FIFO(pf) \ + do { \ + destroy_int_fifo(pf); \ + (pf) = NULL; \ + } while (0) +static void destroy_string_fifo(string_fifo *); +#define FREE_STRING_FIFO(pf) \ + do { \ + destroy_string_fifo(pf); \ + (pf) = NULL; \ + } while (0) +static void destroy_attr_val_fifo(attr_val_fifo *); +#define FREE_ATTR_VAL_FIFO(pf) \ + do { \ + destroy_attr_val_fifo(pf); \ + (pf) = NULL; \ + } while (0) +static void destroy_filegen_fifo(filegen_fifo *); +#define FREE_FILEGEN_FIFO(pf) \ + do { \ + destroy_filegen_fifo(pf); \ + (pf) = NULL; \ + } while (0) +static void destroy_restrict_fifo(restrict_fifo *); +#define FREE_RESTRICT_FIFO(pf) \ + do { \ + destroy_restrict_fifo(pf); \ + (pf) = NULL; \ + } while (0) +static void destroy_setvar_fifo(setvar_fifo *); +#define FREE_SETVAR_FIFO(pf) \ + do { \ + destroy_setvar_fifo(pf); \ + (pf) = NULL; \ + } while (0) +static void destroy_addr_opts_fifo(addr_opts_fifo *); +#define FREE_ADDR_OPTS_FIFO(pf) \ + do { \ + destroy_addr_opts_fifo(pf); \ + (pf) = NULL; \ + } while (0) + +static void config_logconfig(config_tree *); +static void config_monitor(config_tree *); +static void config_rlimit(config_tree *); +static void config_system_opts(config_tree *); +static void config_tinker(config_tree *); +static void config_tos(config_tree *); +static void config_vars(config_tree *); + +#ifdef SIM +static sockaddr_u *get_next_address(address_node *addr); +static void config_sim(config_tree *); +static void config_ntpdsim(config_tree *); +#else /* !SIM follows */ +static void config_ntpd(config_tree *); +static void config_other_modes(config_tree *); +static void config_auth(config_tree *); +static void config_access(config_tree *); +static void config_phone(config_tree *); +static void config_setvar(config_tree *); +static void config_ttl(config_tree *); +static void config_trap(config_tree *); +static void config_fudge(config_tree *); +static void config_peers(config_tree *); +static void config_unpeers(config_tree *); +static void config_nic_rules(config_tree *); +static void config_reset_counters(config_tree *); +static u_char get_correct_host_mode(int token); +static int peerflag_bits(peer_node *); +#endif /* !SIM */ + +#ifdef WORKER +static void peer_name_resolved(int, int, void *, const char *, const char *, + const struct addrinfo *, + const struct addrinfo *); +static void unpeer_name_resolved(int, int, void *, const char *, const char *, + const struct addrinfo *, + const struct addrinfo *); +static void trap_name_resolved(int, int, void *, const char *, const char *, + const struct addrinfo *, + const struct addrinfo *); +#endif + +enum gnn_type { + t_UNK, /* Unknown */ + t_REF, /* Refclock */ + t_MSK /* Network Mask */ +}; + +static void ntpd_set_tod_using(const char *); +static char * normal_dtoa(double); +static u_int32 get_pfxmatch(const char **, struct masks *); +static u_int32 get_match(const char *, struct masks *); +static u_int32 get_logmask(const char *); +#ifndef SIM +static int getnetnum(const char *num, sockaddr_u *addr, int complain, + enum gnn_type a_type); +#endif + + +/* FUNCTIONS FOR INITIALIZATION + * ---------------------------- + */ + +#ifdef FREE_CFG_T +static void +free_auth_node( + config_tree *ptree + ) +{ + if (ptree->auth.keys) { + free(ptree->auth.keys); + ptree->auth.keys = NULL; + } + + if (ptree->auth.keysdir) { + free(ptree->auth.keysdir); + ptree->auth.keysdir = NULL; + } + + if (ptree->auth.ntp_signd_socket) { + free(ptree->auth.ntp_signd_socket); + ptree->auth.ntp_signd_socket = NULL; + } +} +#endif /* DEBUG */ + + +static void +init_syntax_tree( + config_tree *ptree + ) +{ + ZERO(*ptree); +} + + +#ifdef FREE_CFG_T +static void +free_all_config_trees(void) +{ + config_tree *ptree; + config_tree *pnext; + + ptree = cfg_tree_history; + + while (ptree != NULL) { + pnext = ptree->link; + free_config_tree(ptree); + ptree = pnext; + } +} + + +static void +free_config_tree( + config_tree *ptree + ) +{ +#if defined(_MSC_VER) && defined (_DEBUG) + _CrtCheckMemory(); +#endif + + if (ptree->source.value.s != NULL) + free(ptree->source.value.s); + + free_config_other_modes(ptree); + free_config_auth(ptree); + free_config_tos(ptree); + free_config_monitor(ptree); + free_config_access(ptree); + free_config_tinker(ptree); + free_config_rlimit(ptree); + free_config_system_opts(ptree); + free_config_logconfig(ptree); + free_config_phone(ptree); + free_config_setvar(ptree); + free_config_ttl(ptree); + free_config_trap(ptree); + free_config_fudge(ptree); + free_config_vars(ptree); + free_config_peers(ptree); + free_config_unpeers(ptree); + free_config_nic_rules(ptree); + free_config_reset_counters(ptree); +#ifdef SIM + free_config_sim(ptree); +#endif + free_auth_node(ptree); + + free(ptree); + +#if defined(_MSC_VER) && defined (_DEBUG) + _CrtCheckMemory(); +#endif +} +#endif /* FREE_CFG_T */ + + +#ifdef SAVECONFIG +/* Dump all trees */ +int +dump_all_config_trees( + FILE *df, + int comment + ) +{ + config_tree * cfg_ptr; + int return_value; + + return_value = 0; + for (cfg_ptr = cfg_tree_history; + cfg_ptr != NULL; + cfg_ptr = cfg_ptr->link) + return_value |= dump_config_tree(cfg_ptr, df, comment); + + return return_value; +} + + +/* The config dumper */ +int +dump_config_tree( + config_tree *ptree, + FILE *df, + int comment + ) +{ + peer_node *peern; + unpeer_node *unpeern; + attr_val *atrv; + address_node *addr; + address_node *peer_addr; + address_node *fudge_addr; + filegen_node *fgen_node; + restrict_node *rest_node; + addr_opts_node *addr_opts; + setvar_node *setv_node; + nic_rule_node *rule_node; + int_node *i_n; + int_node *flags; + int_node *counter_set; + string_node *str_node; + + const char *s; + char *s1; + char *s2; + char timestamp[80]; + int enable; + + DPRINTF(1, ("dump_config_tree(%p)\n", ptree)); + + if (comment) { + if (!strftime(timestamp, sizeof(timestamp), + "%Y-%m-%d %H:%M:%S", + localtime(&ptree->timestamp))) + timestamp[0] = '\0'; + + fprintf(df, "# %s %s %s\n", + timestamp, + (CONF_SOURCE_NTPQ == ptree->source.attr) + ? "ntpq remote config from" + : "startup configuration file", + ptree->source.value.s); + } + + /* For options I didn't find documentation I'll just output its name and the cor. value */ + atrv = HEAD_PFIFO(ptree->vars); + for ( ; atrv != NULL; atrv = atrv->link) { + switch (atrv->type) { +#ifdef DEBUG + default: + fprintf(df, "\n# dump error:\n" + "# unknown vars type %d (%s) for %s\n", + atrv->type, token_name(atrv->type), + token_name(atrv->attr)); + break; +#endif + case T_Double: + fprintf(df, "%s %s\n", keyword(atrv->attr), + normal_dtoa(atrv->value.d)); + break; + + case T_Integer: + fprintf(df, "%s %d\n", keyword(atrv->attr), + atrv->value.i); + break; + + case T_String: + fprintf(df, "%s \"%s\"", keyword(atrv->attr), + atrv->value.s); + if (T_Driftfile == atrv->attr && + atrv->link != NULL && + T_WanderThreshold == atrv->link->attr) { + atrv = atrv->link; + fprintf(df, " %s\n", + normal_dtoa(atrv->value.d)); + } else { + fprintf(df, "\n"); + } + break; + } + } + + atrv = HEAD_PFIFO(ptree->logconfig); + if (atrv != NULL) { + fprintf(df, "logconfig"); + for ( ; atrv != NULL; atrv = atrv->link) + fprintf(df, " %c%s", atrv->attr, atrv->value.s); + fprintf(df, "\n"); + } + + if (ptree->stats_dir) + fprintf(df, "statsdir \"%s\"\n", ptree->stats_dir); + + i_n = HEAD_PFIFO(ptree->stats_list); + if (i_n != NULL) { + fprintf(df, "statistics"); + for ( ; i_n != NULL; i_n = i_n->link) + fprintf(df, " %s", keyword(i_n->i)); + fprintf(df, "\n"); + } + + fgen_node = HEAD_PFIFO(ptree->filegen_opts); + for ( ; fgen_node != NULL; fgen_node = fgen_node->link) { + atrv = HEAD_PFIFO(fgen_node->options); + if (atrv != NULL) { + fprintf(df, "filegen %s", + keyword(fgen_node->filegen_token)); + for ( ; atrv != NULL; atrv = atrv->link) { + switch (atrv->attr) { +#ifdef DEBUG + default: + fprintf(df, "\n# dump error:\n" + "# unknown filegen option token %s\n" + "filegen %s", + token_name(atrv->attr), + keyword(fgen_node->filegen_token)); + break; +#endif + case T_File: + fprintf(df, " file %s", + atrv->value.s); + break; + + case T_Type: + fprintf(df, " type %s", + keyword(atrv->value.i)); + break; + + case T_Flag: + fprintf(df, " %s", + keyword(atrv->value.i)); + break; + } + } + fprintf(df, "\n"); + } + } + + atrv = HEAD_PFIFO(ptree->auth.crypto_cmd_list); + if (atrv != NULL) { + fprintf(df, "crypto"); + for ( ; atrv != NULL; atrv = atrv->link) { + fprintf(df, " %s %s", keyword(atrv->attr), + atrv->value.s); + } + fprintf(df, "\n"); + } + + if (ptree->auth.revoke != 0) + fprintf(df, "revoke %d\n", ptree->auth.revoke); + + if (ptree->auth.keysdir != NULL) + fprintf(df, "keysdir \"%s\"\n", ptree->auth.keysdir); + + if (ptree->auth.keys != NULL) + fprintf(df, "keys \"%s\"\n", ptree->auth.keys); + + atrv = HEAD_PFIFO(ptree->auth.trusted_key_list); + if (atrv != NULL) { + fprintf(df, "trustedkey"); + for ( ; atrv != NULL; atrv = atrv->link) { + if (T_Integer == atrv->type) + fprintf(df, " %d", atrv->value.i); + else if (T_Intrange == atrv->type) + fprintf(df, " (%d ... %d)", + atrv->value.r.first, + atrv->value.r.last); +#ifdef DEBUG + else + fprintf(df, "\n# dump error:\n" + "# unknown trustedkey attr type %d\n" + "trustedkey", atrv->type); +#endif + } + fprintf(df, "\n"); + } + + if (ptree->auth.control_key) + fprintf(df, "controlkey %d\n", ptree->auth.control_key); + + if (ptree->auth.request_key) + fprintf(df, "requestkey %d\n", ptree->auth.request_key); + + /* dump enable list, then disable list */ + for (enable = 1; enable >= 0; enable--) { + atrv = (enable) + ? HEAD_PFIFO(ptree->enable_opts) + : HEAD_PFIFO(ptree->disable_opts); + if (atrv != NULL) { + fprintf(df, "%s", (enable) + ? "enable" + : "disable"); + for ( ; atrv != NULL; atrv = atrv->link) + fprintf(df, " %s", + keyword(atrv->value.i)); + fprintf(df, "\n"); + } + } + + atrv = HEAD_PFIFO(ptree->orphan_cmds); + if (atrv != NULL) { + fprintf(df, "tos"); + for ( ; atrv != NULL; atrv = atrv->link) { + switch (atrv->type) { +#ifdef DEBUG + default: + fprintf(df, "\n# dump error:\n" + "# unknown tos attr type %d %s\n" + "tos", atrv->type, + token_name(atrv->type)); + break; +#endif + case T_Double: + fprintf(df, " %s %s", + keyword(atrv->attr), + normal_dtoa(atrv->value.d)); + break; + } + } + fprintf(df, "\n"); + } + + atrv = HEAD_PFIFO(ptree->rlimit); + if (atrv != NULL) { + fprintf(df, "rlimit"); + for ( ; atrv != NULL; atrv = atrv->link) { + INSIST(T_Integer == atrv->type); + fprintf(df, " %s %d", keyword(atrv->attr), + atrv->value.i); + } + fprintf(df, "\n"); + } + + atrv = HEAD_PFIFO(ptree->tinker); + if (atrv != NULL) { + fprintf(df, "tinker"); + for ( ; atrv != NULL; atrv = atrv->link) { + INSIST(T_Double == atrv->type); + fprintf(df, " %s %s", keyword(atrv->attr), + normal_dtoa(atrv->value.d)); + } + fprintf(df, "\n"); + } + + if (ptree->broadcastclient) + fprintf(df, "broadcastclient\n"); + + peern = HEAD_PFIFO(ptree->peers); + for ( ; peern != NULL; peern = peern->link) { + addr = peern->addr; + fprintf(df, "%s", keyword(peern->host_mode)); + switch (addr->type) { +#ifdef DEBUG + default: + fprintf(df, "# dump error:\n" + "# unknown peer family %d for:\n" + "%s", addr->type, + keyword(peern->host_mode)); + break; +#endif + case AF_UNSPEC: + break; + + case AF_INET: + fprintf(df, " -4"); + break; + + case AF_INET6: + fprintf(df, " -6"); + break; + } + fprintf(df, " %s", addr->address); + + if (peern->minpoll != 0) + fprintf(df, " minpoll %u", peern->minpoll); + + if (peern->maxpoll != 0) + fprintf(df, " maxpoll %u", peern->maxpoll); + + if (peern->ttl != 0) { + if (strlen(addr->address) > 8 + && !memcmp(addr->address, "127.127.", 8)) + fprintf(df, " mode %u", peern->ttl); + else + fprintf(df, " ttl %u", peern->ttl); + } + + if (peern->peerversion != NTP_VERSION) + fprintf(df, " version %u", peern->peerversion); + + if (peern->peerkey != 0) + fprintf(df, " key %u", peern->peerkey); + + if (peern->group != NULL) + fprintf(df, " ident \"%s\"", peern->group); + + atrv = HEAD_PFIFO(peern->peerflags); + for ( ; atrv != NULL; atrv = atrv->link) { + INSIST(T_Flag == atrv->attr); + INSIST(T_Integer == atrv->type); + fprintf(df, " %s", keyword(atrv->value.i)); + } + + fprintf(df, "\n"); + + addr_opts = HEAD_PFIFO(ptree->fudge); + for ( ; addr_opts != NULL; addr_opts = addr_opts->link) { + peer_addr = peern->addr; + fudge_addr = addr_opts->addr; + + s1 = peer_addr->address; + s2 = fudge_addr->address; + + if (strcmp(s1, s2)) + continue; + + fprintf(df, "fudge %s", s1); + + for (atrv = HEAD_PFIFO(addr_opts->options); + atrv != NULL; + atrv = atrv->link) { + + switch (atrv->type) { +#ifdef DEBUG + default: + fprintf(df, "\n# dump error:\n" + "# unknown fudge atrv->type %d\n" + "fudge %s", atrv->type, + s1); + break; +#endif + case T_Double: + fprintf(df, " %s %s", + keyword(atrv->attr), + normal_dtoa(atrv->value.d)); + break; + + case T_Integer: + fprintf(df, " %s %d", + keyword(atrv->attr), + atrv->value.i); + break; + + case T_String: + fprintf(df, " %s %s", + keyword(atrv->attr), + atrv->value.s); + break; + } + } + fprintf(df, "\n"); + } + } + + addr = HEAD_PFIFO(ptree->manycastserver); + if (addr != NULL) { + fprintf(df, "manycastserver"); + for ( ; addr != NULL; addr = addr->link) + fprintf(df, " %s", addr->address); + fprintf(df, "\n"); + } + + addr = HEAD_PFIFO(ptree->multicastclient); + if (addr != NULL) { + fprintf(df, "multicastclient"); + for ( ; addr != NULL; addr = addr->link) + fprintf(df, " %s", addr->address); + fprintf(df, "\n"); + } + + + for (unpeern = HEAD_PFIFO(ptree->unpeers); + unpeern != NULL; + unpeern = unpeern->link) + fprintf(df, "unpeer %s\n", unpeern->addr->address); + + atrv = HEAD_PFIFO(ptree->mru_opts); + if (atrv != NULL) { + fprintf(df, "mru"); + for ( ; atrv != NULL; atrv = atrv->link) + fprintf(df, " %s %d", keyword(atrv->attr), + atrv->value.i); + fprintf(df, "\n"); + } + + atrv = HEAD_PFIFO(ptree->discard_opts); + if (atrv != NULL) { + fprintf(df, "discard"); + for ( ; atrv != NULL; atrv = atrv->link) + fprintf(df, " %s %d", keyword(atrv->attr), + atrv->value.i); + fprintf(df, "\n"); + } + + + for (rest_node = HEAD_PFIFO(ptree->restrict_opts); + rest_node != NULL; + rest_node = rest_node->link) { + + if (NULL == rest_node->addr) { + s = "default"; + flags = HEAD_PFIFO(rest_node->flags); + for ( ; flags != NULL; flags = flags->link) + if (T_Source == flags->i) { + s = "source"; + break; + } + } else { + s = rest_node->addr->address; + } + fprintf(df, "restrict %s", s); + if (rest_node->mask != NULL) + fprintf(df, " mask %s", + rest_node->mask->address); + flags = HEAD_PFIFO(rest_node->flags); + for ( ; flags != NULL; flags = flags->link) + if (T_Source != flags->i) + fprintf(df, " %s", keyword(flags->i)); + fprintf(df, "\n"); + } + + rule_node = HEAD_PFIFO(ptree->nic_rules); + for ( ; rule_node != NULL; rule_node = rule_node->link) { + fprintf(df, "interface %s %s\n", + keyword(rule_node->action), + (rule_node->match_class) + ? keyword(rule_node->match_class) + : rule_node->if_name); + } + + str_node = HEAD_PFIFO(ptree->phone); + if (str_node != NULL) { + fprintf(df, "phone"); + for ( ; str_node != NULL; str_node = str_node->link) + fprintf(df, " \"%s\"", str_node->s); + fprintf(df, "\n"); + } + + setv_node = HEAD_PFIFO(ptree->setvar); + for ( ; setv_node != NULL; setv_node = setv_node->link) { + s1 = quote_if_needed(setv_node->var); + s2 = quote_if_needed(setv_node->val); + fprintf(df, "setvar %s = %s", s1, s2); + free(s1); + free(s2); + if (setv_node->isdefault) + fprintf(df, " default"); + fprintf(df, "\n"); + } + + i_n = HEAD_PFIFO(ptree->ttl); + if (i_n != NULL) { + fprintf(df, "ttl"); + for( ; i_n != NULL; i_n = i_n->link) + fprintf(df, " %d", i_n->i); + fprintf(df, "\n"); + } + + addr_opts = HEAD_PFIFO(ptree->trap); + for ( ; addr_opts != NULL; addr_opts = addr_opts->link) { + addr = addr_opts->addr; + fprintf(df, "trap %s", addr->address); + atrv = HEAD_PFIFO(addr_opts->options); + for ( ; atrv != NULL; atrv = atrv->link) { + switch (atrv->attr) { +#ifdef DEBUG + default: + fprintf(df, "\n# dump error:\n" + "# unknown trap token %d\n" + "trap %s", atrv->attr, + addr->address); + break; +#endif + case T_Port: + fprintf(df, " port %d", atrv->value.i); + break; + + case T_Interface: + fprintf(df, " interface %s", + atrv->value.s); + break; + } + } + fprintf(df, "\n"); + } + + counter_set = HEAD_PFIFO(ptree->reset_counters); + if (counter_set != NULL) { + fprintf(df, "reset"); + for ( ; counter_set != NULL; + counter_set = counter_set->link) + fprintf(df, " %s", keyword(counter_set->i)); + fprintf(df, "\n"); + } + + return 0; +} +#endif /* SAVECONFIG */ + + + +/* generic fifo routines for structs linked by 1st member */ +void * +append_gen_fifo( + void *fifo, + void *entry + ) +{ + gen_fifo *pf; + gen_node *pe; + + pf = fifo; + pe = entry; + if (NULL == pf) + pf = emalloc_zero(sizeof(*pf)); + else + CHECK_FIFO_CONSISTENCY(*pf); + if (pe != NULL) + LINK_FIFO(*pf, pe, link); + CHECK_FIFO_CONSISTENCY(*pf); + + return pf; +} + + +void * +concat_gen_fifos( + void *first, + void *second + ) +{ + gen_fifo *pf1; + gen_fifo *pf2; + + pf1 = first; + pf2 = second; + if (NULL == pf1) + return pf2; + if (NULL == pf2) + return pf1; + + CONCAT_FIFO(*pf1, *pf2, link); + free(pf2); + + return pf1; +} + + +/* FUNCTIONS FOR CREATING NODES ON THE SYNTAX TREE + * ----------------------------------------------- + */ + +attr_val * +create_attr_dval( + int attr, + double value + ) +{ + attr_val *my_val; + + my_val = emalloc_zero(sizeof(*my_val)); + my_val->attr = attr; + my_val->value.d = value; + my_val->type = T_Double; + + return my_val; +} + + +attr_val * +create_attr_ival( + int attr, + int value + ) +{ + attr_val *my_val; + + my_val = emalloc_zero(sizeof(*my_val)); + my_val->attr = attr; + my_val->value.i = value; + my_val->type = T_Integer; + + return my_val; +} + + +attr_val * +create_attr_uval( + int attr, + u_int value + ) +{ + attr_val *my_val; + + my_val = emalloc_zero(sizeof(*my_val)); + my_val->attr = attr; + my_val->value.u = value; + my_val->type = T_U_int; + + return my_val; +} + + +attr_val * +create_attr_rangeval( + int attr, + int first, + int last + ) +{ + attr_val *my_val; + + my_val = emalloc_zero(sizeof(*my_val)); + my_val->attr = attr; + my_val->value.r.first = first; + my_val->value.r.last = last; + my_val->type = T_Intrange; + + return my_val; +} + + +attr_val * +create_attr_sval( + int attr, + char *s + ) +{ + attr_val *my_val; + + my_val = emalloc_zero(sizeof(*my_val)); + my_val->attr = attr; + if (NULL == s) /* free() hates NULL */ + s = estrdup(""); + my_val->value.s = s; + my_val->type = T_String; + + return my_val; +} + + +int_node * +create_int_node( + int val + ) +{ + int_node *i_n; + + i_n = emalloc_zero(sizeof(*i_n)); + i_n->i = val; + + return i_n; +} + + +string_node * +create_string_node( + char *str + ) +{ + string_node *sn; + + sn = emalloc_zero(sizeof(*sn)); + sn->s = str; + + return sn; +} + + +address_node * +create_address_node( + char * addr, + int type + ) +{ + address_node *my_node; + + NTP_REQUIRE(NULL != addr); + NTP_REQUIRE(AF_INET == type || + AF_INET6 == type || AF_UNSPEC == type); + my_node = emalloc_zero(sizeof(*my_node)); + my_node->address = addr; + my_node->type = (u_short)type; + + return my_node; +} + + +void +destroy_address_node( + address_node *my_node + ) +{ + if (NULL == my_node) + return; + NTP_REQUIRE(NULL != my_node->address); + + free(my_node->address); + free(my_node); +} + + +peer_node * +create_peer_node( + int hmode, + address_node * addr, + attr_val_fifo * options + ) +{ + peer_node *my_node; + attr_val *option; + int freenode; + int errflag = 0; + + my_node = emalloc_zero(sizeof(*my_node)); + + /* Initialize node values to default */ + my_node->peerversion = NTP_VERSION; + + /* Now set the node to the read values */ + my_node->host_mode = hmode; + my_node->addr = addr; + + /* + * the options FIFO mixes items that will be saved in the + * peer_node as explicit members, such as minpoll, and + * those that are moved intact to the peer_node's peerflags + * FIFO. The options FIFO is consumed and reclaimed here. + */ + + if (options != NULL) + CHECK_FIFO_CONSISTENCY(*options); + while (options != NULL) { + UNLINK_FIFO(option, *options, link); + if (NULL == option) { + free(options); + break; + } + + freenode = 1; + /* Check the kind of option being set */ + switch (option->attr) { + + case T_Flag: + APPEND_G_FIFO(my_node->peerflags, option); + freenode = 0; + break; + + case T_Minpoll: + if (option->value.i < NTP_MINPOLL || + option->value.i > UCHAR_MAX) { + msyslog(LOG_INFO, + "minpoll: provided value (%d) is out of range [%d-%d])", + option->value.i, NTP_MINPOLL, + UCHAR_MAX); + my_node->minpoll = NTP_MINPOLL; + } else { + my_node->minpoll = + (u_char)option->value.u; + } + break; + + case T_Maxpoll: + if (option->value.i < 0 || + option->value.i > NTP_MAXPOLL) { + msyslog(LOG_INFO, + "maxpoll: provided value (%d) is out of range [0-%d])", + option->value.i, NTP_MAXPOLL); + my_node->maxpoll = NTP_MAXPOLL; + } else { + my_node->maxpoll = + (u_char)option->value.u; + } + break; + + case T_Ttl: + if (option->value.u >= MAX_TTL) { + msyslog(LOG_ERR, "ttl: invalid argument"); + errflag = 1; + } else { + my_node->ttl = (u_char)option->value.u; + } + break; + + case T_Mode: + my_node->ttl = option->value.u; + break; + + case T_Key: + if (option->value.u >= KEYID_T_MAX) { + msyslog(LOG_ERR, "key: invalid argument"); + errflag = 1; + } else { + my_node->peerkey = + (keyid_t)option->value.u; + } + break; + + case T_Version: + if (option->value.u >= UCHAR_MAX) { + msyslog(LOG_ERR, "version: invalid argument"); + errflag = 1; + } else { + my_node->peerversion = + (u_char)option->value.u; + } + break; + + case T_Ident: + my_node->group = option->value.s; + break; + + default: + msyslog(LOG_ERR, + "Unknown peer/server option token %s", + token_name(option->attr)); + errflag = 1; + } + if (freenode) + free(option); + } + + /* Check if errors were reported. If yes, ignore the node */ + if (errflag) { + free(my_node); + my_node = NULL; + } + + return my_node; +} + + +unpeer_node * +create_unpeer_node( + address_node *addr + ) +{ + unpeer_node * my_node; + u_int u; + char * pch; + + my_node = emalloc_zero(sizeof(*my_node)); + + /* + * From the parser's perspective an association ID fits into + * its generic T_String definition of a name/address "address". + * We treat all valid 16-bit numbers as association IDs. + */ + pch = addr->address; + while (*pch && isdigit(*pch)) + pch++; + + if (!*pch + && 1 == sscanf(addr->address, "%u", &u) + && u <= ASSOCID_MAX) { + my_node->assocID = (associd_t)u; + destroy_address_node(addr); + my_node->addr = NULL; + } else { + my_node->assocID = 0; + my_node->addr = addr; + } + + return my_node; +} + +filegen_node * +create_filegen_node( + int filegen_token, + attr_val_fifo * options + ) +{ + filegen_node *my_node; + + my_node = emalloc_zero(sizeof(*my_node)); + my_node->filegen_token = filegen_token; + my_node->options = options; + + return my_node; +} + + +restrict_node * +create_restrict_node( + address_node * addr, + address_node * mask, + int_fifo * flags, + int line_no + ) +{ + restrict_node *my_node; + + my_node = emalloc_zero(sizeof(*my_node)); + my_node->addr = addr; + my_node->mask = mask; + my_node->flags = flags; + my_node->line_no = line_no; + + return my_node; +} + + +static void +destroy_restrict_node( + restrict_node *my_node + ) +{ + /* With great care, free all the memory occupied by + * the restrict node + */ + destroy_address_node(my_node->addr); + destroy_address_node(my_node->mask); + destroy_int_fifo(my_node->flags); + free(my_node); +} + + +static void +destroy_int_fifo( + int_fifo * fifo + ) +{ + int_node * i_n; + + if (fifo != NULL) { + for (;;) { + UNLINK_FIFO(i_n, *fifo, link); + if (i_n == NULL) + break; + free(i_n); + } + free(fifo); + } +} + + +static void +destroy_string_fifo( + string_fifo * fifo + ) +{ + string_node * sn; + + if (fifo != NULL) { + for (;;) { + UNLINK_FIFO(sn, *fifo, link); + if (sn == NULL) + break; + free(sn->s); + free(sn); + } + free(fifo); + } +} + + +static void +destroy_attr_val_fifo( + attr_val_fifo * av_fifo + ) +{ + attr_val * av; + + if (av_fifo != NULL) { + for (;;) { + UNLINK_FIFO(av, *av_fifo, link); + if (av == NULL) + break; + if (T_String == av->type) + free(av->value.s); + free(av); + } + free(av_fifo); + } +} + + +static void +destroy_filegen_fifo( + filegen_fifo * fifo + ) +{ + filegen_node * fg; + + if (fifo != NULL) { + for (;;) { + UNLINK_FIFO(fg, *fifo, link); + if (fg == NULL) + break; + destroy_attr_val_fifo(fg->options); + free(fg); + } + free(fifo); + } +} + + +static void +destroy_restrict_fifo( + restrict_fifo * fifo + ) +{ + restrict_node * rn; + + if (fifo != NULL) { + for (;;) { + UNLINK_FIFO(rn, *fifo, link); + if (rn == NULL) + break; + destroy_restrict_node(rn); + } + free(fifo); + } +} + + +static void +destroy_setvar_fifo( + setvar_fifo * fifo + ) +{ + setvar_node * sv; + + if (fifo != NULL) { + for (;;) { + UNLINK_FIFO(sv, *fifo, link); + if (sv == NULL) + break; + free(sv->var); + free(sv->val); + free(sv); + } + free(fifo); + } +} + + +static void +destroy_addr_opts_fifo( + addr_opts_fifo * fifo + ) +{ + addr_opts_node * aon; + + if (fifo != NULL) { + for (;;) { + UNLINK_FIFO(aon, *fifo, link); + if (aon == NULL) + break; + destroy_address_node(aon->addr); + destroy_attr_val_fifo(aon->options); + free(aon); + } + free(fifo); + } +} + + +setvar_node * +create_setvar_node( + char * var, + char * val, + int isdefault + ) +{ + setvar_node * my_node; + char * pch; + + /* do not allow = in the variable name */ + pch = strchr(var, '='); + if (NULL != pch) + *pch = '\0'; + + /* Now store the string into a setvar_node */ + my_node = emalloc_zero(sizeof(*my_node)); + my_node->var = var; + my_node->val = val; + my_node->isdefault = isdefault; + + return my_node; +} + + +nic_rule_node * +create_nic_rule_node( + int match_class, + char *if_name, /* interface name or numeric address */ + int action + ) +{ + nic_rule_node *my_node; + + NTP_REQUIRE(match_class != 0 || if_name != NULL); + + my_node = emalloc_zero(sizeof(*my_node)); + my_node->match_class = match_class; + my_node->if_name = if_name; + my_node->action = action; + + return my_node; +} + + +addr_opts_node * +create_addr_opts_node( + address_node * addr, + attr_val_fifo * options + ) +{ + addr_opts_node *my_node; + + my_node = emalloc_zero(sizeof(*my_node)); + my_node->addr = addr; + my_node->options = options; + + return my_node; +} + + +#ifdef SIM +script_info * +create_sim_script_info( + double duration, + attr_val_fifo * script_queue + ) +{ + script_info *my_info; + attr_val *my_attr_val; + + my_info = emalloc_zero(sizeof(*my_info)); + + /* Initialize Script Info with default values*/ + my_info->duration = duration; + my_info->prop_delay = NET_DLY; + my_info->proc_delay = PROC_DLY; + + /* Traverse the script_queue and fill out non-default values */ + + for (my_attr_val = HEAD_PFIFO(script_queue); + my_attr_val != NULL; + my_attr_val = my_attr_val->link) { + + /* Set the desired value */ + switch (my_attr_val->attr) { + + case T_Freq_Offset: + my_info->freq_offset = my_attr_val->value.d; + break; + + case T_Wander: + my_info->wander = my_attr_val->value.d; + break; + + case T_Jitter: + my_info->jitter = my_attr_val->value.d; + break; + + case T_Prop_Delay: + my_info->prop_delay = my_attr_val->value.d; + break; + + case T_Proc_Delay: + my_info->proc_delay = my_attr_val->value.d; + break; + + default: + msyslog(LOG_ERR, "Unknown script token %d", + my_attr_val->attr); + } + } + + return my_info; +} +#endif /* SIM */ + + +#ifdef SIM +static sockaddr_u * +get_next_address( + address_node *addr + ) +{ + const char addr_prefix[] = "192.168.0."; + static int curr_addr_num = 1; +#define ADDR_LENGTH 16 + 1 /* room for 192.168.1.255 */ + char addr_string[ADDR_LENGTH]; + sockaddr_u *final_addr; + struct addrinfo *ptr; + int gai_err; + + final_addr = emalloc(sizeof(*final_addr)); + + if (addr->type == T_String) { + snprintf(addr_string, sizeof(addr_string), "%s%d", + addr_prefix, curr_addr_num++); + printf("Selecting ip address %s for hostname %s\n", + addr_string, addr->address); + gai_err = getaddrinfo(addr_string, "ntp", NULL, &ptr); + } else { + gai_err = getaddrinfo(addr->address, "ntp", NULL, &ptr); + } + + if (gai_err) { + fprintf(stderr, "ERROR!! Could not get a new address\n"); + exit(1); + } + memcpy(final_addr, ptr->ai_addr, ptr->ai_addrlen); + fprintf(stderr, "Successful in setting ip address of simulated server to: %s\n", + stoa(final_addr)); + freeaddrinfo(ptr); + + return final_addr; +} +#endif /* SIM */ + + +#ifdef SIM +server_info * +create_sim_server( + address_node * addr, + double server_offset, + script_info_fifo * script + ) +{ + server_info *my_info; + + my_info = emalloc_zero(sizeof(*my_info)); + my_info->server_time = server_offset; + my_info->addr = get_next_address(addr); + my_info->script = script; + UNLINK_FIFO(my_info->curr_script, *my_info->script, link); + + return my_info; +} +#endif /* SIM */ + +sim_node * +create_sim_node( + attr_val_fifo * init_opts, + server_info_fifo * servers + ) +{ + sim_node *my_node; + + my_node = emalloc(sizeof(*my_node)); + my_node->init_opts = init_opts; + my_node->servers = servers; + + return my_node; +} + + + + +/* FUNCTIONS FOR PERFORMING THE CONFIGURATION + * ------------------------------------------ + */ + +#ifndef SIM +static void +config_other_modes( + config_tree * ptree + ) +{ + sockaddr_u addr_sock; + address_node * addr_node; + + if (ptree->broadcastclient) + proto_config(PROTO_BROADCLIENT, ptree->broadcastclient, + 0., NULL); + + addr_node = HEAD_PFIFO(ptree->manycastserver); + while (addr_node != NULL) { + ZERO_SOCK(&addr_sock); + AF(&addr_sock) = addr_node->type; + if (1 == getnetnum(addr_node->address, &addr_sock, 1, + t_UNK)) { + proto_config(PROTO_MULTICAST_ADD, + 0, 0., &addr_sock); + sys_manycastserver = 1; + } + addr_node = addr_node->link; + } + + /* Configure the multicast clients */ + addr_node = HEAD_PFIFO(ptree->multicastclient); + if (addr_node != NULL) { + do { + ZERO_SOCK(&addr_sock); + AF(&addr_sock) = addr_node->type; + if (1 == getnetnum(addr_node->address, + &addr_sock, 1, t_UNK)) { + proto_config(PROTO_MULTICAST_ADD, 0, 0., + &addr_sock); + } + addr_node = addr_node->link; + } while (addr_node != NULL); + proto_config(PROTO_MULTICAST_ADD, 1, 0., NULL); + } +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +destroy_address_fifo( + address_fifo * pfifo + ) +{ + address_node * addr_node; + + if (pfifo != NULL) { + for (;;) { + UNLINK_FIFO(addr_node, *pfifo, link); + if (addr_node == NULL) + break; + destroy_address_node(addr_node); + } + free(pfifo); + } +} + + +static void +free_config_other_modes( + config_tree *ptree + ) +{ + FREE_ADDRESS_FIFO(ptree->manycastserver); + FREE_ADDRESS_FIFO(ptree->multicastclient); +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_auth( + config_tree *ptree + ) +{ + attr_val * my_val; + int first; + int last; + int i; + int count; +#ifdef AUTOKEY + int item; +#endif + + /* Crypto Command */ +#ifdef AUTOKEY + item = -1; /* quiet warning */ + my_val = HEAD_PFIFO(ptree->auth.crypto_cmd_list); + for (; my_val != NULL; my_val = my_val->link) { + switch (my_val->attr) { + + default: + INSIST(0); + break; + + case T_Host: + item = CRYPTO_CONF_PRIV; + break; + + case T_Ident: + item = CRYPTO_CONF_IDENT; + break; + + case T_Pw: + item = CRYPTO_CONF_PW; + break; + + case T_Randfile: + item = CRYPTO_CONF_RAND; + break; + + case T_Digest: + item = CRYPTO_CONF_NID; + break; + } + crypto_config(item, my_val->value.s); + } +#endif /* AUTOKEY */ + + /* Keysdir Command */ + if (ptree->auth.keysdir) { + if (keysdir != default_keysdir) + free(keysdir); + keysdir = estrdup(ptree->auth.keysdir); + } + + + /* ntp_signd_socket Command */ + if (ptree->auth.ntp_signd_socket) { + if (ntp_signd_socket != default_ntp_signd_socket) + free(ntp_signd_socket); + ntp_signd_socket = estrdup(ptree->auth.ntp_signd_socket); + } + +#ifdef AUTOKEY + if (ptree->auth.cryptosw && !cryptosw) { + crypto_setup(); + cryptosw = 1; + } +#endif /* AUTOKEY */ + + /* + * Count the number of trusted keys to preallocate storage and + * size the hash table. + */ + count = 0; + my_val = HEAD_PFIFO(ptree->auth.trusted_key_list); + for (; my_val != NULL; my_val = my_val->link) { + if (T_Integer == my_val->type) { + first = my_val->value.i; + if (first > 1 && first <= NTP_MAXKEY) + count++; + } else { + REQUIRE(T_Intrange == my_val->type); + first = my_val->value.r.first; + last = my_val->value.r.last; + if (!(first > last || first < 1 || + last > NTP_MAXKEY)) { + count += 1 + last - first; + } + } + } + auth_prealloc_symkeys(count); + + /* Keys Command */ + if (ptree->auth.keys) + getauthkeys(ptree->auth.keys); + + /* Control Key Command */ + if (ptree->auth.control_key) + ctl_auth_keyid = (keyid_t)ptree->auth.control_key; + + /* Requested Key Command */ + if (ptree->auth.request_key) { + DPRINTF(4, ("set info_auth_keyid to %08lx\n", + (u_long) ptree->auth.request_key)); + info_auth_keyid = (keyid_t)ptree->auth.request_key; + } + + /* Trusted Key Command */ + my_val = HEAD_PFIFO(ptree->auth.trusted_key_list); + for (; my_val != NULL; my_val = my_val->link) { + if (T_Integer == my_val->type) { + first = my_val->value.i; + if (first >= 1 && first <= NTP_MAXKEY) { + authtrust(first, TRUE); + } else { + msyslog(LOG_NOTICE, + "Ignoring invalid trustedkey %d, min 1 max %d.", + first, NTP_MAXKEY); + } + } else { + first = my_val->value.r.first; + last = my_val->value.r.last; + if (first > last || first < 1 || + last > NTP_MAXKEY) { + msyslog(LOG_NOTICE, + "Ignoring invalid trustedkey range %d ... %d, min 1 max %d.", + first, last, NTP_MAXKEY); + } else { + for (i = first; i <= last; i++) { + authtrust(i, TRUE); + } + } + } + } + +#ifdef AUTOKEY + /* crypto revoke command */ + if (ptree->auth.revoke) + sys_revoke = 1UL << ptree->auth.revoke; +#endif /* AUTOKEY */ +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_auth( + config_tree *ptree + ) +{ + destroy_attr_val_fifo(ptree->auth.crypto_cmd_list); + ptree->auth.crypto_cmd_list = NULL; + destroy_attr_val_fifo(ptree->auth.trusted_key_list); + ptree->auth.trusted_key_list = NULL; +} +#endif /* FREE_CFG_T */ + + +static void +config_tos( + config_tree *ptree + ) +{ + attr_val * tos; + int item; + double val; + + item = -1; /* quiet warning */ + tos = HEAD_PFIFO(ptree->orphan_cmds); + for (; tos != NULL; tos = tos->link) { + val = tos->value.d; + switch(tos->attr) { + + default: + INSIST(0); + break; + + case T_Ceiling: + if (val > STRATUM_UNSPEC - 1) { + msyslog(LOG_WARNING, + "Using maximum tos ceiling %d, %g requested", + STRATUM_UNSPEC - 1, val); + val = STRATUM_UNSPEC - 1; + } + item = PROTO_CEILING; + break; + + case T_Floor: + item = PROTO_FLOOR; + break; + + case T_Cohort: + item = PROTO_COHORT; + break; + + case T_Orphan: + item = PROTO_ORPHAN; + break; + + case T_Orphanwait: + item = PROTO_ORPHWAIT; + break; + + case T_Mindist: + item = PROTO_MINDISP; + break; + + case T_Maxdist: + item = PROTO_MAXDIST; + break; + + case T_Minclock: + item = PROTO_MINCLOCK; + break; + + case T_Maxclock: + item = PROTO_MAXCLOCK; + break; + + case T_Minsane: + item = PROTO_MINSANE; + break; + + case T_Beacon: + item = PROTO_BEACON; + break; + } + proto_config(item, 0, val, NULL); + } +} + + +#ifdef FREE_CFG_T +static void +free_config_tos( + config_tree *ptree + ) +{ + FREE_ATTR_VAL_FIFO(ptree->orphan_cmds); +} +#endif /* FREE_CFG_T */ + + +static void +config_monitor( + config_tree *ptree + ) +{ + int_node *pfilegen_token; + const char *filegen_string; + const char *filegen_file; + FILEGEN *filegen; + filegen_node *my_node; + attr_val *my_opts; + int filegen_type; + int filegen_flag; + + /* Set the statistics directory */ + if (ptree->stats_dir) + stats_config(STATS_STATSDIR, ptree->stats_dir); + + /* NOTE: + * Calling filegen_get is brain dead. Doing a string + * comparison to find the relavant filegen structure is + * expensive. + * + * Through the parser, we already know which filegen is + * being specified. Hence, we should either store a + * pointer to the specified structure in the syntax tree + * or an index into a filegen array. + * + * Need to change the filegen code to reflect the above. + */ + + /* Turn on the specified statistics */ + pfilegen_token = HEAD_PFIFO(ptree->stats_list); + for (; pfilegen_token != NULL; pfilegen_token = pfilegen_token->link) { + filegen_string = keyword(pfilegen_token->i); + filegen = filegen_get(filegen_string); + if (NULL == filegen) { + msyslog(LOG_ERR, + "stats %s unrecognized", + filegen_string); + continue; + } + DPRINTF(4, ("enabling filegen for %s statistics '%s%s'\n", + filegen_string, filegen->dir, + filegen->fname)); + filegen_flag = filegen->flag; + filegen_flag |= FGEN_FLAG_ENABLED; + filegen_config(filegen, statsdir, filegen_string, + filegen->type, filegen_flag); + } + + /* Configure the statistics with the options */ + my_node = HEAD_PFIFO(ptree->filegen_opts); + for (; my_node != NULL; my_node = my_node->link) { + filegen_string = keyword(my_node->filegen_token); + filegen = filegen_get(filegen_string); + if (NULL == filegen) { + msyslog(LOG_ERR, + "filegen category '%s' unrecognized", + filegen_string); + continue; + } + filegen_file = filegen_string; + + /* Initialize the filegen variables to their pre-configuration states */ + filegen_flag = filegen->flag; + filegen_type = filegen->type; + + /* "filegen ... enabled" is the default (when filegen is used) */ + filegen_flag |= FGEN_FLAG_ENABLED; + + my_opts = HEAD_PFIFO(my_node->options); + for (; my_opts != NULL; my_opts = my_opts->link) { + switch (my_opts->attr) { + + case T_File: + filegen_file = my_opts->value.s; + break; + + case T_Type: + switch (my_opts->value.i) { + + default: + INSIST(0); + break; + + case T_None: + filegen_type = FILEGEN_NONE; + break; + + case T_Pid: + filegen_type = FILEGEN_PID; + break; + + case T_Day: + filegen_type = FILEGEN_DAY; + break; + + case T_Week: + filegen_type = FILEGEN_WEEK; + break; + + case T_Month: + filegen_type = FILEGEN_MONTH; + break; + + case T_Year: + filegen_type = FILEGEN_YEAR; + break; + + case T_Age: + filegen_type = FILEGEN_AGE; + break; + } + break; + + case T_Flag: + switch (my_opts->value.i) { + + case T_Link: + filegen_flag |= FGEN_FLAG_LINK; + break; + + case T_Nolink: + filegen_flag &= ~FGEN_FLAG_LINK; + break; + + case T_Enable: + filegen_flag |= FGEN_FLAG_ENABLED; + break; + + case T_Disable: + filegen_flag &= ~FGEN_FLAG_ENABLED; + break; + + default: + msyslog(LOG_ERR, + "Unknown filegen flag token %d", + my_opts->value.i); + exit(1); + } + break; + + default: + msyslog(LOG_ERR, + "Unknown filegen option token %d", + my_opts->attr); + exit(1); + } + } + filegen_config(filegen, statsdir, filegen_file, + filegen_type, filegen_flag); + } +} + + +#ifdef FREE_CFG_T +static void +free_config_monitor( + config_tree *ptree + ) +{ + if (ptree->stats_dir) { + free(ptree->stats_dir); + ptree->stats_dir = NULL; + } + + FREE_INT_FIFO(ptree->stats_list); + FREE_FILEGEN_FIFO(ptree->filegen_opts); +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_access( + config_tree *ptree + ) +{ + static int warned_signd; + attr_val * my_opt; + restrict_node * my_node; + int_node * curr_flag; + sockaddr_u addr; + sockaddr_u mask; + struct addrinfo hints; + struct addrinfo * ai_list; + struct addrinfo * pai; + int rc; + int restrict_default; + u_short flags; + u_short mflags; + int range_err; + const char * signd_warning = +#ifdef HAVE_NTP_SIGND + "MS-SNTP signd operations currently block ntpd degrading service to all clients."; +#else + "mssntp restrict bit ignored, this ntpd was configured without --enable-ntp-signd."; +#endif + + /* Configure the mru options */ + my_opt = HEAD_PFIFO(ptree->mru_opts); + for (; my_opt != NULL; my_opt = my_opt->link) { + + range_err = FALSE; + + switch (my_opt->attr) { + + case T_Incalloc: + if (0 <= my_opt->value.i) + mru_incalloc = my_opt->value.u; + else + range_err = TRUE; + break; + + case T_Incmem: + if (0 <= my_opt->value.i) + mru_incalloc = (my_opt->value.u * 1024U) + / sizeof(mon_entry); + else + range_err = TRUE; + break; + + case T_Initalloc: + if (0 <= my_opt->value.i) + mru_initalloc = my_opt->value.u; + else + range_err = TRUE; + break; + + case T_Initmem: + if (0 <= my_opt->value.i) + mru_initalloc = (my_opt->value.u * 1024U) + / sizeof(mon_entry); + else + range_err = TRUE; + break; + + case T_Mindepth: + if (0 <= my_opt->value.i) + mru_mindepth = my_opt->value.u; + else + range_err = TRUE; + break; + + case T_Maxage: + mru_maxage = my_opt->value.i; + break; + + case T_Maxdepth: + if (0 <= my_opt->value.i) + mru_maxdepth = my_opt->value.u; + else + mru_maxdepth = UINT_MAX; + break; + + case T_Maxmem: + if (0 <= my_opt->value.i) + mru_maxdepth = (my_opt->value.u * 1024U) / + sizeof(mon_entry); + else + mru_maxdepth = UINT_MAX; + break; + + default: + msyslog(LOG_ERR, + "Unknown mru option %s (%d)", + keyword(my_opt->attr), my_opt->attr); + exit(1); + } + if (range_err) + msyslog(LOG_ERR, + "mru %s %d out of range, ignored.", + keyword(my_opt->attr), my_opt->value.i); + } + + /* Configure the discard options */ + my_opt = HEAD_PFIFO(ptree->discard_opts); + for (; my_opt != NULL; my_opt = my_opt->link) { + + switch (my_opt->attr) { + + case T_Average: + if (0 <= my_opt->value.i && + my_opt->value.i <= UCHAR_MAX) + ntp_minpoll = (u_char)my_opt->value.u; + else + msyslog(LOG_ERR, + "discard average %d out of range, ignored.", + my_opt->value.i); + break; + + case T_Minimum: + ntp_minpkt = my_opt->value.i; + break; + + case T_Monitor: + mon_age = my_opt->value.i; + break; + + default: + msyslog(LOG_ERR, + "Unknown discard option %s (%d)", + keyword(my_opt->attr), my_opt->attr); + exit(1); + } + } + + /* Configure the restrict options */ + my_node = HEAD_PFIFO(ptree->restrict_opts); + for (; my_node != NULL; my_node = my_node->link) { + /* Parse the flags */ + flags = 0; + mflags = 0; + + curr_flag = HEAD_PFIFO(my_node->flags); + for (; curr_flag != NULL; curr_flag = curr_flag->link) { + switch (curr_flag->i) { + + default: + INSIST(0); + break; + + case T_Ntpport: + mflags |= RESM_NTPONLY; + break; + + case T_Source: + mflags |= RESM_SOURCE; + break; + + case T_Flake: + flags |= RES_FLAKE; + break; + + case T_Ignore: + flags |= RES_IGNORE; + break; + + case T_Kod: + flags |= RES_KOD; + break; + + case T_Mssntp: + flags |= RES_MSSNTP; + break; + + case T_Limited: + flags |= RES_LIMITED; + break; + + case T_Lowpriotrap: + flags |= RES_LPTRAP; + break; + + case T_Nomodify: + flags |= RES_NOMODIFY; + break; + + case T_Nomrulist: + flags |= RES_NOMRULIST; + break; + + case T_Nopeer: + flags |= RES_NOPEER; + break; + + case T_Noquery: + flags |= RES_NOQUERY; + break; + + case T_Noserve: + flags |= RES_DONTSERVE; + break; + + case T_Notrap: + flags |= RES_NOTRAP; + break; + + case T_Notrust: + flags |= RES_DONTTRUST; + break; + + case T_Version: + flags |= RES_VERSION; + break; + } + } + + if ((RES_MSSNTP & flags) && !warned_signd) { + warned_signd = 1; + fprintf(stderr, "%s\n", signd_warning); + msyslog(LOG_WARNING, "%s", signd_warning); + } + + /* It would be swell if we could identify the line number */ + if ((RES_KOD & flags) && !(RES_LIMITED & flags)) { + char *kod_where = (my_node->addr) + ? my_node->addr->address + : (mflags & RESM_SOURCE) + ? "source" + : "default"; + char *kod_warn = "KOD does nothing without LIMITED."; + + fprintf(stderr, "restrict %s: %s\n", kod_where, kod_warn); + msyslog(LOG_WARNING, "restrict %s: %s", kod_where, kod_warn); + } + + ZERO_SOCK(&addr); + ai_list = NULL; + pai = NULL; + restrict_default = 0; + + if (NULL == my_node->addr) { + ZERO_SOCK(&mask); + if (!(RESM_SOURCE & mflags)) { + /* + * The user specified a default rule + * without a -4 / -6 qualifier, add to + * both lists + */ + restrict_default = 1; + } else { + /* apply "restrict source ..." */ + DPRINTF(1, ("restrict source template mflags %x flags %x\n", + mflags, flags)); + hack_restrict(RESTRICT_FLAGS, NULL, + NULL, mflags, flags, 0); + continue; + } + } else { + /* Resolve the specified address */ + AF(&addr) = (u_short)my_node->addr->type; + + if (getnetnum(my_node->addr->address, + &addr, 1, t_UNK) != 1) { + /* + * Attempt a blocking lookup. This + * is in violation of the nonblocking + * design of ntpd's mainline code. The + * alternative of running without the + * restriction until the name resolved + * seems worse. + * Ideally some scheme could be used for + * restrict directives in the startup + * ntp.conf to delay starting up the + * protocol machinery until after all + * restrict hosts have been resolved. + */ + ai_list = NULL; + ZERO(hints); + hints.ai_protocol = IPPROTO_UDP; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = my_node->addr->type; + rc = getaddrinfo(my_node->addr->address, + "ntp", &hints, + &ai_list); + if (rc) { + msyslog(LOG_ERR, + "restrict: ignoring line %d, address/host '%s' unusable.", + my_node->line_no, + my_node->addr->address); + continue; + } + INSIST(ai_list != NULL); + pai = ai_list; + INSIST(pai->ai_addr != NULL); + INSIST(sizeof(addr) >= + pai->ai_addrlen); + memcpy(&addr, pai->ai_addr, + pai->ai_addrlen); + INSIST(AF_INET == AF(&addr) || + AF_INET6 == AF(&addr)); + } + + SET_HOSTMASK(&mask, AF(&addr)); + + /* Resolve the mask */ + if (my_node->mask) { + ZERO_SOCK(&mask); + AF(&mask) = my_node->mask->type; + if (getnetnum(my_node->mask->address, + &mask, 1, t_MSK) != 1) { + msyslog(LOG_ERR, + "restrict: ignoring line %d, mask '%s' unusable.", + my_node->line_no, + my_node->mask->address); + continue; + } + } + } + + /* Set the flags */ + if (restrict_default) { + AF(&addr) = AF_INET; + AF(&mask) = AF_INET; + hack_restrict(RESTRICT_FLAGS, &addr, + &mask, mflags, flags, 0); + AF(&addr) = AF_INET6; + AF(&mask) = AF_INET6; + } + + do { + hack_restrict(RESTRICT_FLAGS, &addr, + &mask, mflags, flags, 0); + if (pai != NULL && + NULL != (pai = pai->ai_next)) { + INSIST(pai->ai_addr != NULL); + INSIST(sizeof(addr) >= + pai->ai_addrlen); + ZERO_SOCK(&addr); + memcpy(&addr, pai->ai_addr, + pai->ai_addrlen); + INSIST(AF_INET == AF(&addr) || + AF_INET6 == AF(&addr)); + SET_HOSTMASK(&mask, AF(&addr)); + } + } while (pai != NULL); + + if (ai_list != NULL) + freeaddrinfo(ai_list); + } +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_access( + config_tree *ptree + ) +{ + FREE_ATTR_VAL_FIFO(ptree->mru_opts); + FREE_ATTR_VAL_FIFO(ptree->discard_opts); + FREE_RESTRICT_FIFO(ptree->restrict_opts); +} +#endif /* FREE_CFG_T */ + + +static void +config_rlimit( + config_tree *ptree + ) +{ + attr_val * rlimit_av; + + rlimit_av = HEAD_PFIFO(ptree->rlimit); + for (; rlimit_av != NULL; rlimit_av = rlimit_av->link) { + switch (rlimit_av->attr) { + + default: + INSIST(0); + break; + + case T_Memlock: + if (rlimit_av->value.i != 0) { +#if defined(RLIMIT_MEMLOCK) + ntp_rlimit(RLIMIT_MEMLOCK, + (rlim_t)(rlimit_av->value.i * 1024 * 1024), + 1024 * 1024, + "MB"); +#else + /* STDERR as well would be fine... */ + msyslog(LOG_WARNING, "'rlimit memlock' specified but is not available on this system."); +#endif /* RLIMIT_MEMLOCK */ + } else { + do_memlock = 0; + } + break; + + case T_Stacksize: +#if defined(RLIMIT_STACK) + ntp_rlimit(RLIMIT_STACK, + (rlim_t)(rlimit_av->value.i * 4096), + 4096, + "4k"); +#else + /* STDERR as well would be fine... */ + msyslog(LOG_WARNING, "'rlimit stacksize' specified but is not available on this system."); +#endif /* RLIMIT_STACK */ + break; + + case T_Filenum: +#if defined(RLIMIT_NOFILE) + ntp_rlimit(RLIMIT_NOFILE, + (rlim_t)(rlimit_av->value.i), + 1, + ""); +#else + /* STDERR as well would be fine... */ + msyslog(LOG_WARNING, "'rlimit filenum' specified but is not available on this system."); +#endif /* RLIMIT_NOFILE */ + break; + + } + } +} + + +static void +config_tinker( + config_tree *ptree + ) +{ + attr_val * tinker; + int item; + + item = -1; /* quiet warning */ + tinker = HEAD_PFIFO(ptree->tinker); + for (; tinker != NULL; tinker = tinker->link) { + switch (tinker->attr) { + + default: + INSIST(0); + break; + + case T_Allan: + item = LOOP_ALLAN; + break; + + case T_Dispersion: + item = LOOP_PHI; + break; + + case T_Freq: + item = LOOP_FREQ; + break; + + case T_Huffpuff: + item = LOOP_HUFFPUFF; + break; + + case T_Panic: + item = LOOP_PANIC; + break; + + case T_Step: + item = LOOP_MAX; + break; + + case T_Stepout: + item = LOOP_MINSTEP; + break; + + case T_Tick: + item = LOOP_TICK; + break; + } + loop_config(item, tinker->value.d); + } +} + + +#ifdef FREE_CFG_T +static void +free_config_rlimit( + config_tree *ptree + ) +{ + FREE_ATTR_VAL_FIFO(ptree->rlimit); +} + +static void +free_config_tinker( + config_tree *ptree + ) +{ + FREE_ATTR_VAL_FIFO(ptree->tinker); +} +#endif /* FREE_CFG_T */ + + +/* + * config_nic_rules - apply interface listen/ignore/drop items + */ +#ifndef SIM +static void +config_nic_rules( + config_tree *ptree + ) +{ + nic_rule_node * curr_node; + sockaddr_u addr; + nic_rule_match match_type; + nic_rule_action action; + char * if_name; + char * pchSlash; + int prefixlen; + int addrbits; + + curr_node = HEAD_PFIFO(ptree->nic_rules); + + if (curr_node != NULL + && (HAVE_OPT( NOVIRTUALIPS ) || HAVE_OPT( INTERFACE ))) { + msyslog(LOG_ERR, + "interface/nic rules are not allowed with --interface (-I) or --novirtualips (-L)%s", + (input_from_file) ? ", exiting" : ""); + if (input_from_file) + exit(1); + else + return; + } + + for (; curr_node != NULL; curr_node = curr_node->link) { + prefixlen = -1; + if_name = curr_node->if_name; + if (if_name != NULL) + if_name = estrdup(if_name); + + switch (curr_node->match_class) { + + default: + /* + * this assignment quiets a gcc "may be used + * uninitialized" warning and is here for no + * other reason. + */ + match_type = MATCH_ALL; + INSIST(FALSE); + break; + + case 0: + /* + * 0 is out of range for valid token T_... + * and in a nic_rules_node indicates the + * interface descriptor is either a name or + * address, stored in if_name in either case. + */ + INSIST(if_name != NULL); + pchSlash = strchr(if_name, '/'); + if (pchSlash != NULL) + *pchSlash = '\0'; + if (is_ip_address(if_name, AF_UNSPEC, &addr)) { + match_type = MATCH_IFADDR; + if (pchSlash != NULL + && 1 == sscanf(pchSlash + 1, "%d", + &prefixlen)) { + addrbits = 8 * + SIZEOF_INADDR(AF(&addr)); + prefixlen = max(-1, prefixlen); + prefixlen = min(prefixlen, + addrbits); + } + } else { + match_type = MATCH_IFNAME; + if (pchSlash != NULL) + *pchSlash = '/'; + } + break; + + case T_All: + match_type = MATCH_ALL; + break; + + case T_Ipv4: + match_type = MATCH_IPV4; + break; + + case T_Ipv6: + match_type = MATCH_IPV6; + break; + + case T_Wildcard: + match_type = MATCH_WILDCARD; + break; + } + + switch (curr_node->action) { + + default: + /* + * this assignment quiets a gcc "may be used + * uninitialized" warning and is here for no + * other reason. + */ + action = ACTION_LISTEN; + INSIST(FALSE); + break; + + case T_Listen: + action = ACTION_LISTEN; + break; + + case T_Ignore: + action = ACTION_IGNORE; + break; + + case T_Drop: + action = ACTION_DROP; + break; + } + + add_nic_rule(match_type, if_name, prefixlen, + action); + timer_interfacetimeout(current_time + 2); + if (if_name != NULL) + free(if_name); + } +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_nic_rules( + config_tree *ptree + ) +{ + nic_rule_node *curr_node; + + if (ptree->nic_rules != NULL) { + for (;;) { + UNLINK_FIFO(curr_node, *ptree->nic_rules, link); + if (NULL == curr_node) + break; + free(curr_node->if_name); + free(curr_node); + } + free(ptree->nic_rules); + ptree->nic_rules = NULL; + } +} +#endif /* FREE_CFG_T */ + + +static void +apply_enable_disable( + attr_val_fifo * fifo, + int enable + ) +{ + attr_val *curr_flag; + int option; +#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED + bc_entry *pentry; +#endif + + for (curr_flag = HEAD_PFIFO(fifo); + curr_flag != NULL; + curr_flag = curr_flag->link) { + + option = curr_flag->value.i; + switch (option) { + + default: + msyslog(LOG_ERR, + "can not apply enable/disable token %d, unknown", + option); + break; + + case T_Auth: + proto_config(PROTO_AUTHENTICATE, enable, 0., NULL); + break; + + case T_Bclient: + proto_config(PROTO_BROADCLIENT, enable, 0., NULL); + break; + + case T_Calibrate: + proto_config(PROTO_CAL, enable, 0., NULL); + break; + + case T_Kernel: + proto_config(PROTO_KERNEL, enable, 0., NULL); + break; + + case T_Monitor: + proto_config(PROTO_MONITOR, enable, 0., NULL); + break; + + case T_Ntp: + proto_config(PROTO_NTP, enable, 0., NULL); + break; + + case T_Mode7: + proto_config(PROTO_MODE7, enable, 0., NULL); + break; + + case T_Stats: + proto_config(PROTO_FILEGEN, enable, 0., NULL); + break; + +#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED + case T_Bc_bugXXXX: + pentry = bc_list; + while (pentry->token) { + if (pentry->token == option) + break; + pentry++; + } + if (!pentry->token) { + msyslog(LOG_ERR, + "compat token %d not in bc_list[]", + option); + continue; + } + pentry->enabled = enable; + break; +#endif + } + } +} + + +static void +config_system_opts( + config_tree *ptree + ) +{ + apply_enable_disable(ptree->enable_opts, 1); + apply_enable_disable(ptree->disable_opts, 0); +} + + +#ifdef FREE_CFG_T +static void +free_config_system_opts( + config_tree *ptree + ) +{ + FREE_ATTR_VAL_FIFO(ptree->enable_opts); + FREE_ATTR_VAL_FIFO(ptree->disable_opts); +} +#endif /* FREE_CFG_T */ + + +static void +config_logconfig( + config_tree *ptree + ) +{ + attr_val * my_lc; + + my_lc = HEAD_PFIFO(ptree->logconfig); + for (; my_lc != NULL; my_lc = my_lc->link) { + switch (my_lc->attr) { + + case '+': + ntp_syslogmask |= get_logmask(my_lc->value.s); + break; + + case '-': + ntp_syslogmask &= ~get_logmask(my_lc->value.s); + break; + + case '=': + ntp_syslogmask = get_logmask(my_lc->value.s); + break; + default: + INSIST(0); + break; + } + } +} + + +#ifdef FREE_CFG_T +static void +free_config_logconfig( + config_tree *ptree + ) +{ + FREE_ATTR_VAL_FIFO(ptree->logconfig); +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_phone( + config_tree *ptree + ) +{ + int i; + string_node * sn; + + i = 0; + sn = HEAD_PFIFO(ptree->phone); + for (; sn != NULL; sn = sn->link) { + /* need to leave array entry for NULL terminator */ + if (i < COUNTOF(sys_phone) - 1) { + sys_phone[i++] = estrdup(sn->s); + sys_phone[i] = NULL; + } else { + msyslog(LOG_INFO, + "phone: Number of phone entries exceeds %lu. Ignoring phone %s...", + (u_long)(COUNTOF(sys_phone) - 1), sn->s); + } + } +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_phone( + config_tree *ptree + ) +{ + FREE_STRING_FIFO(ptree->phone); +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_setvar( + config_tree *ptree + ) +{ + setvar_node *my_node; + size_t varlen, vallen, octets; + char * str; + + str = NULL; + my_node = HEAD_PFIFO(ptree->setvar); + for (; my_node != NULL; my_node = my_node->link) { + varlen = strlen(my_node->var); + vallen = strlen(my_node->val); + octets = varlen + vallen + 1 + 1; + str = erealloc(str, octets); + snprintf(str, octets, "%s=%s", my_node->var, + my_node->val); + set_sys_var(str, octets, (my_node->isdefault) + ? DEF + : 0); + } + if (str != NULL) + free(str); +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_setvar( + config_tree *ptree + ) +{ + FREE_SETVAR_FIFO(ptree->setvar); +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_ttl( + config_tree *ptree + ) +{ + int i = 0; + int_node *curr_ttl; + + curr_ttl = HEAD_PFIFO(ptree->ttl); + for (; curr_ttl != NULL; curr_ttl = curr_ttl->link) { + if (i < COUNTOF(sys_ttl)) + sys_ttl[i++] = (u_char)curr_ttl->i; + else + msyslog(LOG_INFO, + "ttl: Number of TTL entries exceeds %lu. Ignoring TTL %d...", + (u_long)COUNTOF(sys_ttl), curr_ttl->i); + } + sys_ttlmax = i - 1; +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_ttl( + config_tree *ptree + ) +{ + FREE_INT_FIFO(ptree->ttl); +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_trap( + config_tree *ptree + ) +{ + addr_opts_node *curr_trap; + attr_val *curr_opt; + sockaddr_u addr_sock; + sockaddr_u peeraddr; + struct interface *localaddr; + struct addrinfo hints; + char port_text[8]; + settrap_parms *pstp; + u_short port; + int err_flag; + int rc; + + /* silence warning about addr_sock potentially uninitialized */ + AF(&addr_sock) = AF_UNSPEC; + + curr_trap = HEAD_PFIFO(ptree->trap); + for (; curr_trap != NULL; curr_trap = curr_trap->link) { + err_flag = 0; + port = 0; + localaddr = NULL; + + curr_opt = HEAD_PFIFO(curr_trap->options); + for (; curr_opt != NULL; curr_opt = curr_opt->link) { + if (T_Port == curr_opt->attr) { + if (curr_opt->value.i < 1 + || curr_opt->value.i > USHRT_MAX) { + msyslog(LOG_ERR, + "invalid port number " + "%d, trap ignored", + curr_opt->value.i); + err_flag = 1; + } + port = (u_short)curr_opt->value.i; + } + else if (T_Interface == curr_opt->attr) { + /* Resolve the interface address */ + ZERO_SOCK(&addr_sock); + if (getnetnum(curr_opt->value.s, + &addr_sock, 1, t_UNK) != 1) { + err_flag = 1; + break; + } + + localaddr = findinterface(&addr_sock); + + if (NULL == localaddr) { + msyslog(LOG_ERR, + "can't find interface with address %s", + stoa(&addr_sock)); + err_flag = 1; + } + } + } + + /* Now process the trap for the specified interface + * and port number + */ + if (!err_flag) { + if (!port) + port = TRAPPORT; + ZERO_SOCK(&peeraddr); + rc = getnetnum(curr_trap->addr->address, + &peeraddr, 1, t_UNK); + if (1 != rc) { +#ifndef WORKER + msyslog(LOG_ERR, + "trap: unable to use IP address %s.", + curr_trap->addr->address); +#else /* WORKER follows */ + /* + * save context and hand it off + * for name resolution. + */ + ZERO(hints); + hints.ai_protocol = IPPROTO_UDP; + hints.ai_socktype = SOCK_DGRAM; + snprintf(port_text, sizeof(port_text), + "%u", port); + hints.ai_flags = Z_AI_NUMERICSERV; + pstp = emalloc_zero(sizeof(*pstp)); + if (localaddr != NULL) { + hints.ai_family = localaddr->family; + pstp->ifaddr_nonnull = 1; + memcpy(&pstp->ifaddr, + &localaddr->sin, + sizeof(pstp->ifaddr)); + } + rc = getaddrinfo_sometime( + curr_trap->addr->address, + port_text, &hints, + INITIAL_DNS_RETRY, + &trap_name_resolved, + pstp); + if (!rc) + msyslog(LOG_ERR, + "config_trap: getaddrinfo_sometime(%s,%s): %m", + curr_trap->addr->address, + port_text); +#endif /* WORKER */ + continue; + } + /* port is at same location for v4 and v6 */ + SET_PORT(&peeraddr, port); + + if (NULL == localaddr) + localaddr = ANY_INTERFACE_CHOOSE(&peeraddr); + else + AF(&peeraddr) = AF(&addr_sock); + + if (!ctlsettrap(&peeraddr, localaddr, 0, + NTP_VERSION)) + msyslog(LOG_ERR, + "set trap %s -> %s failed.", + latoa(localaddr), + stoa(&peeraddr)); + } + } +} + + +/* + * trap_name_resolved() + * + * Callback invoked when config_trap()'s DNS lookup completes. + */ +# ifdef WORKER +static void +trap_name_resolved( + int rescode, + int gai_errno, + void * context, + const char * name, + const char * service, + const struct addrinfo * hints, + const struct addrinfo * res + ) +{ + settrap_parms *pstp; + struct interface *localaddr; + sockaddr_u peeraddr; + + (void)gai_errno; + (void)service; + (void)hints; + pstp = context; + if (rescode) { + msyslog(LOG_ERR, + "giving up resolving trap host %s: %s (%d)", + name, gai_strerror(rescode), rescode); + free(pstp); + return; + } + INSIST(sizeof(peeraddr) >= res->ai_addrlen); + ZERO(peeraddr); + memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); + localaddr = NULL; + if (pstp->ifaddr_nonnull) + localaddr = findinterface(&pstp->ifaddr); + if (NULL == localaddr) + localaddr = ANY_INTERFACE_CHOOSE(&peeraddr); + if (!ctlsettrap(&peeraddr, localaddr, 0, NTP_VERSION)) + msyslog(LOG_ERR, "set trap %s -> %s failed.", + latoa(localaddr), stoa(&peeraddr)); + free(pstp); +} +# endif /* WORKER */ +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_trap( + config_tree *ptree + ) +{ + FREE_ADDR_OPTS_FIFO(ptree->trap); +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_fudge( + config_tree *ptree + ) +{ + addr_opts_node *curr_fudge; + attr_val *curr_opt; + sockaddr_u addr_sock; + address_node *addr_node; + struct refclockstat clock_stat; + int err_flag; + + curr_fudge = HEAD_PFIFO(ptree->fudge); + for (; curr_fudge != NULL; curr_fudge = curr_fudge->link) { + err_flag = 0; + + /* Get the reference clock address and + * ensure that it is sane + */ + addr_node = curr_fudge->addr; + ZERO_SOCK(&addr_sock); + if (getnetnum(addr_node->address, &addr_sock, 1, t_REF) + != 1) { + err_flag = 1; + msyslog(LOG_ERR, + "unrecognized fudge reference clock address %s, line ignored", + stoa(&addr_sock)); + } + + if (!ISREFCLOCKADR(&addr_sock)) { + err_flag = 1; + msyslog(LOG_ERR, + "inappropriate address %s for the fudge command, line ignored", + stoa(&addr_sock)); + } + + /* Parse all the options to the fudge command */ + ZERO(clock_stat); + curr_opt = HEAD_PFIFO(curr_fudge->options); + for (; curr_opt != NULL; curr_opt = curr_opt->link) { + switch (curr_opt->attr) { + + case T_Time1: + clock_stat.haveflags |= CLK_HAVETIME1; + clock_stat.fudgetime1 = curr_opt->value.d; + break; + + case T_Time2: + clock_stat.haveflags |= CLK_HAVETIME2; + clock_stat.fudgetime2 = curr_opt->value.d; + break; + + case T_Stratum: + clock_stat.haveflags |= CLK_HAVEVAL1; + clock_stat.fudgeval1 = curr_opt->value.i; + break; + + case T_Refid: + clock_stat.haveflags |= CLK_HAVEVAL2; + clock_stat.fudgeval2 = 0; + memcpy(&clock_stat.fudgeval2, + curr_opt->value.s, + min(strlen(curr_opt->value.s), 4)); + break; + + case T_Flag1: + clock_stat.haveflags |= CLK_HAVEFLAG1; + if (curr_opt->value.i) + clock_stat.flags |= CLK_FLAG1; + else + clock_stat.flags &= ~CLK_FLAG1; + break; + + case T_Flag2: + clock_stat.haveflags |= CLK_HAVEFLAG2; + if (curr_opt->value.i) + clock_stat.flags |= CLK_FLAG2; + else + clock_stat.flags &= ~CLK_FLAG2; + break; + + case T_Flag3: + clock_stat.haveflags |= CLK_HAVEFLAG3; + if (curr_opt->value.i) + clock_stat.flags |= CLK_FLAG3; + else + clock_stat.flags &= ~CLK_FLAG3; + break; + + case T_Flag4: + clock_stat.haveflags |= CLK_HAVEFLAG4; + if (curr_opt->value.i) + clock_stat.flags |= CLK_FLAG4; + else + clock_stat.flags &= ~CLK_FLAG4; + break; + + default: + msyslog(LOG_ERR, + "Unexpected fudge flag %s (%d) for %s", + token_name(curr_opt->attr), + curr_opt->attr, stoa(&addr_sock)); + exit(curr_opt->attr ? curr_opt->attr : 1); + } + } +# ifdef REFCLOCK + if (!err_flag) + refclock_control(&addr_sock, &clock_stat, NULL); +# endif + } +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_fudge( + config_tree *ptree + ) +{ + FREE_ADDR_OPTS_FIFO(ptree->fudge); +} +#endif /* FREE_CFG_T */ + + +static void +config_vars( + config_tree *ptree + ) +{ + attr_val *curr_var; + int len; + + curr_var = HEAD_PFIFO(ptree->vars); + for (; curr_var != NULL; curr_var = curr_var->link) { + /* Determine which variable to set and set it */ + switch (curr_var->attr) { + + case T_Broadcastdelay: + proto_config(PROTO_BROADDELAY, 0, curr_var->value.d, NULL); + break; + + case T_Tick: + loop_config(LOOP_TICK, curr_var->value.d); + break; + + case T_Driftfile: + if ('\0' == curr_var->value.s[0]) { + stats_drift_file = 0; + msyslog(LOG_INFO, "config: driftfile disabled"); + } else + stats_config(STATS_FREQ_FILE, curr_var->value.s); + break; + + case T_Ident: + sys_ident = curr_var->value.s; + break; + + case T_WanderThreshold: /* FALLTHROUGH */ + case T_Nonvolatile: + wander_threshold = curr_var->value.d; + break; + + case T_Leapfile: + stats_config(STATS_LEAP_FILE, curr_var->value.s); + break; + + case T_Pidfile: + stats_config(STATS_PID_FILE, curr_var->value.s); + break; + + case T_Logfile: + if (-1 == change_logfile(curr_var->value.s, TRUE)) + msyslog(LOG_ERR, + "Cannot open logfile %s: %m", + curr_var->value.s); + break; + + case T_Saveconfigdir: + if (saveconfigdir != NULL) + free(saveconfigdir); + len = strlen(curr_var->value.s); + if (0 == len) { + saveconfigdir = NULL; + } else if (DIR_SEP != curr_var->value.s[len - 1] +#ifdef SYS_WINNT /* slash is also a dir. sep. on Windows */ + && '/' != curr_var->value.s[len - 1] +#endif + ) { + len++; + saveconfigdir = emalloc(len + 1); + snprintf(saveconfigdir, len + 1, + "%s%c", + curr_var->value.s, + DIR_SEP); + } else { + saveconfigdir = estrdup( + curr_var->value.s); + } + break; + + case T_Automax: +#ifdef AUTOKEY + sys_automax = curr_var->value.i; +#endif + break; + + default: + msyslog(LOG_ERR, + "config_vars(): unexpected token %d", + curr_var->attr); + } + } +} + + +#ifdef FREE_CFG_T +static void +free_config_vars( + config_tree *ptree + ) +{ + FREE_ATTR_VAL_FIFO(ptree->vars); +} +#endif /* FREE_CFG_T */ + + +/* Define a function to check if a resolved address is sane. + * If yes, return 1, else return 0; + */ +static int +is_sane_resolved_address( + sockaddr_u * peeraddr, + int hmode + ) +{ + if (!ISREFCLOCKADR(peeraddr) && ISBADADR(peeraddr)) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + stoa(peeraddr)); + return 0; + } + /* + * Shouldn't be able to specify multicast + * address for server/peer! + * and unicast address for manycastclient! + */ + if ((T_Server == hmode || T_Peer == hmode || T_Pool == hmode) + && IS_MCAST(peeraddr)) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + stoa(peeraddr)); + return 0; + } + if (T_Manycastclient == hmode && !IS_MCAST(peeraddr)) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + stoa(peeraddr)); + return 0; + } + + if (IS_IPV6(peeraddr) && !ipv6_works) + return 0; + + /* Ok, all tests succeeded, now we can return 1 */ + return 1; +} + + +#ifndef SIM +static u_char +get_correct_host_mode( + int token + ) +{ + switch (token) { + + case T_Server: + case T_Pool: + case T_Manycastclient: + return MODE_CLIENT; + + case T_Peer: + return MODE_ACTIVE; + + case T_Broadcast: + return MODE_BROADCAST; + + default: + return 0; + } +} + + +/* + * peerflag_bits() get config_peers() peerflags value from a + * peer_node's queue of flag attr_val entries. + */ +static int +peerflag_bits( + peer_node *pn + ) +{ + int peerflags; + attr_val *option; + + /* translate peerflags options to bits */ + peerflags = 0; + option = HEAD_PFIFO(pn->peerflags); + for (; option != NULL; option = option->link) { + switch (option->value.i) { + + default: + INSIST(0); + break; + + case T_Autokey: + peerflags |= FLAG_SKEY; + break; + + case T_Burst: + peerflags |= FLAG_BURST; + break; + + case T_Iburst: + peerflags |= FLAG_IBURST; + break; + + case T_Noselect: + peerflags |= FLAG_NOSELECT; + break; + + case T_Preempt: + peerflags |= FLAG_PREEMPT; + break; + + case T_Prefer: + peerflags |= FLAG_PREFER; + break; + + case T_True: + peerflags |= FLAG_TRUE; + break; + + case T_Xleave: + peerflags |= FLAG_XLEAVE; + break; + } + } + + return peerflags; +} + + +static void +config_peers( + config_tree *ptree + ) +{ + sockaddr_u peeraddr; + struct addrinfo hints; + peer_node * curr_peer; + peer_resolved_ctx * ctx; + u_char hmode; + + /* add servers named on the command line with iburst implied */ + for (; + cmdline_server_count > 0; + cmdline_server_count--, cmdline_servers++) { + + ZERO_SOCK(&peeraddr); + /* + * If we have a numeric address, we can safely + * proceed in the mainline with it. Otherwise, hand + * the hostname off to the blocking child. + */ + if (is_ip_address(*cmdline_servers, AF_UNSPEC, + &peeraddr)) { + + SET_PORT(&peeraddr, NTP_PORT); + if (is_sane_resolved_address(&peeraddr, + T_Server)) + peer_config( + &peeraddr, + NULL, + NULL, + MODE_CLIENT, + NTP_VERSION, + 0, + 0, + FLAG_IBURST, + 0, + 0, + NULL); + } else { + /* we have a hostname to resolve */ +# ifdef WORKER + ctx = emalloc_zero(sizeof(*ctx)); + ctx->family = AF_UNSPEC; + ctx->host_mode = T_Server; + ctx->hmode = MODE_CLIENT; + ctx->version = NTP_VERSION; + ctx->flags = FLAG_IBURST; + + ZERO(hints); + hints.ai_family = (u_short)ctx->family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + getaddrinfo_sometime(*cmdline_servers, + "ntp", &hints, + INITIAL_DNS_RETRY, + &peer_name_resolved, + (void *)ctx); +# else /* !WORKER follows */ + msyslog(LOG_ERR, + "hostname %s can not be used, please use IP address instead.", + curr_peer->addr->address); +# endif + } + } + + /* add associations from the configuration file */ + curr_peer = HEAD_PFIFO(ptree->peers); + for (; curr_peer != NULL; curr_peer = curr_peer->link) { + ZERO_SOCK(&peeraddr); + /* Find the correct host-mode */ + hmode = get_correct_host_mode(curr_peer->host_mode); + INSIST(hmode != 0); + + if (T_Pool == curr_peer->host_mode) { + AF(&peeraddr) = curr_peer->addr->type; + peer_config( + &peeraddr, + curr_peer->addr->address, + NULL, + hmode, + curr_peer->peerversion, + curr_peer->minpoll, + curr_peer->maxpoll, + peerflag_bits(curr_peer), + curr_peer->ttl, + curr_peer->peerkey, + curr_peer->group); + /* + * If we have a numeric address, we can safely + * proceed in the mainline with it. Otherwise, hand + * the hostname off to the blocking child. + */ + } else if (is_ip_address(curr_peer->addr->address, + curr_peer->addr->type, &peeraddr)) { + + SET_PORT(&peeraddr, NTP_PORT); + if (is_sane_resolved_address(&peeraddr, + curr_peer->host_mode)) + peer_config( + &peeraddr, + NULL, + NULL, + hmode, + curr_peer->peerversion, + curr_peer->minpoll, + curr_peer->maxpoll, + peerflag_bits(curr_peer), + curr_peer->ttl, + curr_peer->peerkey, + curr_peer->group); + } else { + /* we have a hostname to resolve */ +# ifdef WORKER + ctx = emalloc_zero(sizeof(*ctx)); + ctx->family = curr_peer->addr->type; + ctx->host_mode = curr_peer->host_mode; + ctx->hmode = hmode; + ctx->version = curr_peer->peerversion; + ctx->minpoll = curr_peer->minpoll; + ctx->maxpoll = curr_peer->maxpoll; + ctx->flags = peerflag_bits(curr_peer); + ctx->ttl = curr_peer->ttl; + ctx->keyid = curr_peer->peerkey; + ctx->group = curr_peer->group; + + ZERO(hints); + hints.ai_family = ctx->family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + getaddrinfo_sometime(curr_peer->addr->address, + "ntp", &hints, + INITIAL_DNS_RETRY, + &peer_name_resolved, ctx); +# else /* !WORKER follows */ + msyslog(LOG_ERR, + "hostname %s can not be used, please use IP address instead.", + curr_peer->addr->address); +# endif + } + } +} +#endif /* !SIM */ + +/* + * peer_name_resolved() + * + * Callback invoked when config_peers()'s DNS lookup completes. + */ +#ifdef WORKER +static void +peer_name_resolved( + int rescode, + int gai_errno, + void * context, + const char * name, + const char * service, + const struct addrinfo * hints, + const struct addrinfo * res + ) +{ + sockaddr_u peeraddr; + peer_resolved_ctx * ctx; + u_short af; + const char * fam_spec; + + (void)gai_errno; + (void)service; + (void)hints; + ctx = context; + + DPRINTF(1, ("peer_name_resolved(%s) rescode %d\n", name, rescode)); + + if (rescode) { +#ifndef IGNORE_DNS_ERRORS + free(ctx); + msyslog(LOG_ERR, + "giving up resolving host %s: %s (%d)", + name, gai_strerror(rescode), rescode); +#else /* IGNORE_DNS_ERRORS follows */ + getaddrinfo_sometime(name, service, hints, + INITIAL_DNS_RETRY, + &peer_name_resolved, context); +#endif + return; + } + + /* Loop to configure a single association */ + for (; res != NULL; res = res->ai_next) { + memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); + if (is_sane_resolved_address(&peeraddr, + ctx->host_mode)) { + NLOG(NLOG_SYSINFO) { + af = ctx->family; + fam_spec = (AF_INET6 == af) + ? "(AAAA) " + : (AF_INET == af) + ? "(A) " + : ""; + msyslog(LOG_INFO, "DNS %s %s-> %s", + name, fam_spec, + stoa(&peeraddr)); + } + peer_config( + &peeraddr, + NULL, + NULL, + ctx->hmode, + ctx->version, + ctx->minpoll, + ctx->maxpoll, + ctx->flags, + ctx->ttl, + ctx->keyid, + ctx->group); + break; + } + } + free(ctx); +} +#endif /* WORKER */ + + +#ifdef FREE_CFG_T +static void +free_config_peers( + config_tree *ptree + ) +{ + peer_node *curr_peer; + + if (ptree->peers != NULL) { + for (;;) { + UNLINK_FIFO(curr_peer, *ptree->peers, link); + if (NULL == curr_peer) + break; + destroy_address_node(curr_peer->addr); + destroy_attr_val_fifo(curr_peer->peerflags); + free(curr_peer); + } + free(ptree->peers); + ptree->peers = NULL; + } +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_unpeers( + config_tree *ptree + ) +{ + sockaddr_u peeraddr; + struct addrinfo hints; + unpeer_node * curr_unpeer; + struct peer * p; + const char * name; + int rc; + + curr_unpeer = HEAD_PFIFO(ptree->unpeers); + for (; curr_unpeer != NULL; curr_unpeer = curr_unpeer->link) { + /* + * Either AssocID will be zero, and we unpeer by name/ + * address addr, or it is nonzero and addr NULL. + */ + if (curr_unpeer->assocID) { + p = findpeerbyassoc(curr_unpeer->assocID); + if (p != NULL) { + msyslog(LOG_NOTICE, "unpeered %s", + stoa(&p->srcadr)); + peer_clear(p, "GONE"); + unpeer(p); + } + + continue; + } + + ZERO(peeraddr); + AF(&peeraddr) = curr_unpeer->addr->type; + name = curr_unpeer->addr->address; + rc = getnetnum(name, &peeraddr, 0, t_UNK); + /* Do we have a numeric address? */ + if (rc > 0) { + DPRINTF(1, ("unpeer: searching for %s\n", + stoa(&peeraddr))); + p = findexistingpeer(&peeraddr, NULL, NULL, -1, 0); + if (p != NULL) { + msyslog(LOG_NOTICE, "unpeered %s", + stoa(&peeraddr)); + peer_clear(p, "GONE"); + unpeer(p); + } + + continue; + } + /* + * It's not a numeric IP address, it's a hostname. + * Check for associations with a matching hostname. + */ + for (p = peer_list; p != NULL; p = p->p_link) + if (p->hostname != NULL) + if (!strcasecmp(p->hostname, name)) + break; + if (p != NULL) { + msyslog(LOG_NOTICE, "unpeered %s", name); + peer_clear(p, "GONE"); + unpeer(p); + } + /* Resolve the hostname to address(es). */ +# ifdef WORKER + ZERO(hints); + hints.ai_family = curr_unpeer->addr->type; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + getaddrinfo_sometime(name, "ntp", &hints, + INITIAL_DNS_RETRY, + &unpeer_name_resolved, NULL); +# else /* !WORKER follows */ + msyslog(LOG_ERR, + "hostname %s can not be used, please use IP address instead.", + name); +# endif + } +} +#endif /* !SIM */ + + +/* + * unpeer_name_resolved() + * + * Callback invoked when config_unpeers()'s DNS lookup completes. + */ +#ifdef WORKER +static void +unpeer_name_resolved( + int rescode, + int gai_errno, + void * context, + const char * name, + const char * service, + const struct addrinfo * hints, + const struct addrinfo * res + ) +{ + sockaddr_u peeraddr; + struct peer * peer; + u_short af; + const char * fam_spec; + + (void)context; + (void)hints; + DPRINTF(1, ("unpeer_name_resolved(%s) rescode %d\n", name, rescode)); + + if (rescode) { + msyslog(LOG_ERR, "giving up resolving unpeer %s: %s (%d)", + name, gai_strerror(rescode), rescode); + return; + } + /* + * Loop through the addresses found + */ + for (; res != NULL; res = res->ai_next) { + INSIST(res->ai_addrlen <= sizeof(peeraddr)); + memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); + DPRINTF(1, ("unpeer: searching for peer %s\n", + stoa(&peeraddr))); + peer = findexistingpeer(&peeraddr, NULL, NULL, -1, 0); + if (peer != NULL) { + af = AF(&peeraddr); + fam_spec = (AF_INET6 == af) + ? "(AAAA) " + : (AF_INET == af) + ? "(A) " + : ""; + msyslog(LOG_NOTICE, "unpeered %s %s-> %s", name, + fam_spec, stoa(&peeraddr)); + peer_clear(peer, "GONE"); + unpeer(peer); + } + } +} +#endif /* WORKER */ + + +#ifdef FREE_CFG_T +static void +free_config_unpeers( + config_tree *ptree + ) +{ + unpeer_node *curr_unpeer; + + if (ptree->unpeers != NULL) { + for (;;) { + UNLINK_FIFO(curr_unpeer, *ptree->unpeers, link); + if (NULL == curr_unpeer) + break; + destroy_address_node(curr_unpeer->addr); + free(curr_unpeer); + } + free(ptree->unpeers); + } +} +#endif /* FREE_CFG_T */ + + +#ifndef SIM +static void +config_reset_counters( + config_tree *ptree + ) +{ + int_node *counter_set; + + for (counter_set = HEAD_PFIFO(ptree->reset_counters); + counter_set != NULL; + counter_set = counter_set->link) { + switch (counter_set->i) { + default: + DPRINTF(1, ("config_reset_counters %s (%d) invalid\n", + keyword(counter_set->i), counter_set->i)); + break; + + case T_Allpeers: + peer_all_reset(); + break; + + case T_Auth: + reset_auth_stats(); + break; + + case T_Ctl: + ctl_clr_stats(); + break; + + case T_Io: + io_clr_stats(); + break; + + case T_Mem: + peer_clr_stats(); + break; + + case T_Sys: + proto_clr_stats(); + break; + + case T_Timer: + timer_clr_stats(); + break; + } + } +} +#endif /* !SIM */ + + +#ifdef FREE_CFG_T +static void +free_config_reset_counters( + config_tree *ptree + ) +{ + FREE_INT_FIFO(ptree->reset_counters); +} +#endif /* FREE_CFG_T */ + + +#ifdef SIM +static void +config_sim( + config_tree *ptree + ) +{ + int i; + server_info *serv_info; + attr_val *init_stmt; + sim_node *sim_n; + + /* Check if a simulate block was found in the configuration code. + * If not, return an error and exit + */ + sim_n = HEAD_PFIFO(ptree->sim_details); + if (NULL == sim_n) { + fprintf(stderr, "ERROR!! I couldn't find a \"simulate\" block for configuring the simulator.\n"); + fprintf(stderr, "\tCheck your configuration file.\n"); + exit(1); + } + + /* Process the initialization statements + * ------------------------------------- + */ + init_stmt = HEAD_PFIFO(sim_n->init_opts); + for (; init_stmt != NULL; init_stmt = init_stmt->link) { + switch(init_stmt->attr) { + + case T_Beep_Delay: + simulation.beep_delay = init_stmt->value.d; + break; + + case T_Sim_Duration: + simulation.end_time = init_stmt->value.d; + break; + + default: + fprintf(stderr, + "Unknown simulator init token %d\n", + init_stmt->attr); + exit(1); + } + } + + /* Process the server list + * ----------------------- + */ + simulation.num_of_servers = 0; + serv_info = HEAD_PFIFO(sim_n->servers); + for (; serv_info != NULL; serv_info = serv_info->link) + simulation.num_of_servers++; + simulation.servers = emalloc(simulation.num_of_servers * + sizeof(simulation.servers[0])); + + i = 0; + serv_info = HEAD_PFIFO(sim_n->servers); + for (; serv_info != NULL; serv_info = serv_info->link) { + if (NULL == serv_info) { + fprintf(stderr, "Simulator server list is corrupt\n"); + exit(1); + } else { + simulation.servers[i] = *serv_info; + simulation.servers[i].link = NULL; + i++; + } + } + + printf("Creating server associations\n"); + create_server_associations(); + fprintf(stderr,"\tServer associations successfully created!!\n"); +} + + +#ifdef FREE_CFG_T +static void +free_config_sim( + config_tree *ptree + ) +{ + sim_node *sim_n; + server_info *serv_n; + script_info *script_n; + + if (NULL == ptree->sim_details) + return; + sim_n = HEAD_PFIFO(ptree->sim_details); + free(ptree->sim_details); + ptree->sim_details = NULL; + if (NULL == sim_n) + return; + + FREE_ATTR_VAL_FIFO(sim_n->init_opts); + for (;;) { + UNLINK_FIFO(serv_n, *sim_n->servers, link); + if (NULL == serv_n) + break; + free(serv_n->curr_script); + if (serv_n->script != NULL) { + for (;;) { + UNLINK_FIFO(script_n, *serv_n->script, + link); + if (script_n == NULL) + break; + free(script_n); + } + free(serv_n->script); + } + free(serv_n); + } + free(sim_n); +} +#endif /* FREE_CFG_T */ +#endif /* SIM */ + + +/* Define two different config functions. One for the daemon and the other for + * the simulator. The simulator ignores a lot of the standard ntpd configuration + * options + */ +#ifndef SIM +static void +config_ntpd( + config_tree *ptree + ) +{ + config_nic_rules(ptree); + io_open_sockets(); + config_monitor(ptree); + config_auth(ptree); + config_tos(ptree); + config_access(ptree); + config_tinker(ptree); + config_rlimit(ptree); + config_system_opts(ptree); + config_logconfig(ptree); + config_phone(ptree); + config_setvar(ptree); + config_ttl(ptree); + config_trap(ptree); + config_vars(ptree); + config_other_modes(ptree); + config_peers(ptree); + config_unpeers(ptree); + config_fudge(ptree); + config_reset_counters(ptree); + +#ifdef TEST_BLOCKING_WORKER + { + struct addrinfo hints; + + ZERO(hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + getaddrinfo_sometime("www.cnn.com", "ntp", &hints, + INITIAL_DNS_RETRY, + gai_test_callback, (void *)1); + hints.ai_family = AF_INET6; + getaddrinfo_sometime("ipv6.google.com", "ntp", &hints, + INITIAL_DNS_RETRY, + gai_test_callback, (void *)0x600); + } +#endif +} +#endif /* !SIM */ + + +#ifdef SIM +static void +config_ntpdsim( + config_tree *ptree + ) +{ + printf("Configuring Simulator...\n"); + printf("Some ntpd-specific commands in the configuration file will be ignored.\n"); + + config_tos(ptree); + config_monitor(ptree); + config_tinker(ptree); + if (0) + config_rlimit(ptree); /* not needed for the simulator */ + config_system_opts(ptree); + config_logconfig(ptree); + config_vars(ptree); + config_sim(ptree); +} +#endif /* SIM */ + + +/* + * config_remotely() - implements ntpd side of ntpq :config + */ +void +config_remotely( + sockaddr_u * remote_addr + ) +{ + struct FILE_INFO remote_cuckoo; + char origin[128]; + + snprintf(origin, sizeof(origin), "remote config from %s", + stoa(remote_addr)); + ZERO(remote_cuckoo); + remote_cuckoo.fname = origin; + remote_cuckoo.line_no = 1; + remote_cuckoo.col_no = 1; + input_from_file = 0; + + init_syntax_tree(&cfgt); + yyparse(&remote_cuckoo); + cfgt.source.attr = CONF_SOURCE_NTPQ; + cfgt.timestamp = time(NULL); + cfgt.source.value.s = estrdup(stoa(remote_addr)); + + DPRINTF(1, ("Finished Parsing!!\n")); + + save_and_apply_config_tree(); + + input_from_file = 1; +} + + +/* + * getconfig() - process startup configuration file e.g /etc/ntp.conf + */ +void +getconfig( + int argc, + char ** argv + ) +{ + char line[256]; + +#ifdef DEBUG + atexit(free_all_config_trees); +#endif +#ifndef SYS_WINNT + config_file = CONFIG_FILE; +#else + temp = CONFIG_FILE; + if (!ExpandEnvironmentStringsA(temp, config_file_storage, + sizeof(config_file_storage))) { + msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m"); + exit(1); + } + config_file = config_file_storage; + + temp = ALT_CONFIG_FILE; + if (!ExpandEnvironmentStringsA(temp, alt_config_file_storage, + sizeof(alt_config_file_storage))) { + msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m"); + exit(1); + } + alt_config_file = alt_config_file_storage; +#endif /* SYS_WINNT */ + + /* + * install a non default variable with this daemon version + */ + snprintf(line, sizeof(line), "daemon_version=\"%s\"", Version); + set_sys_var(line, strlen(line) + 1, RO); + + /* + * Set up for the first time step to install a variable showing + * which syscall is being used to step. + */ + set_tod_using = &ntpd_set_tod_using; + + getCmdOpts(argc, argv); + init_syntax_tree(&cfgt); + curr_include_level = 0; + if ( + (fp[curr_include_level] = F_OPEN(FindConfig(config_file), "r")) == NULL +#ifdef HAVE_NETINFO + /* If there is no config_file, try NetInfo. */ + && check_netinfo && !(config_netinfo = get_netinfo_config()) +#endif /* HAVE_NETINFO */ + ) { + msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file)); +#ifndef SYS_WINNT + io_open_sockets(); + + return; +#else + /* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */ + + if ((fp[curr_include_level] = F_OPEN(FindConfig(alt_config_file), "r")) == NULL) { + + /* + * Broadcast clients can sometimes run without + * a configuration file. + */ + msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file)); + io_open_sockets(); + + return; + } + cfgt.source.value.s = estrdup(alt_config_file); +#endif /* SYS_WINNT */ + } else + cfgt.source.value.s = estrdup(config_file); + + + /*** BULK OF THE PARSER ***/ +#ifdef DEBUG + yydebug = !!(debug >= 5); +#endif + yyparse(fp[curr_include_level]); + + DPRINTF(1, ("Finished Parsing!!\n")); + + cfgt.source.attr = CONF_SOURCE_FILE; + cfgt.timestamp = time(NULL); + + save_and_apply_config_tree(); + + while (curr_include_level != -1) + FCLOSE(fp[curr_include_level--]); + +#ifdef HAVE_NETINFO + if (config_netinfo) + free_netinfo_config(config_netinfo); +#endif /* HAVE_NETINFO */ +} + + +void +save_and_apply_config_tree(void) +{ + config_tree *ptree; +#ifndef SAVECONFIG + config_tree *punlinked; +#endif + + /* + * Keep all the configuration trees applied since startup in + * a list that can be used to dump the configuration back to + * a text file. + */ + ptree = emalloc(sizeof(*ptree)); + memcpy(ptree, &cfgt, sizeof(*ptree)); + ZERO(cfgt); + + LINK_TAIL_SLIST(cfg_tree_history, ptree, link, config_tree); + +#ifdef SAVECONFIG + if (HAVE_OPT( SAVECONFIGQUIT )) { + FILE *dumpfile; + int err; + int dumpfailed; + + dumpfile = fopen(OPT_ARG( SAVECONFIGQUIT ), "w"); + if (NULL == dumpfile) { + err = errno; + mfprintf(stderr, + "can not create save file %s, error %d %m\n", + OPT_ARG(SAVECONFIGQUIT), err); + exit(err); + } + + dumpfailed = dump_all_config_trees(dumpfile, 0); + if (dumpfailed) + fprintf(stderr, + "--saveconfigquit %s error %d\n", + OPT_ARG( SAVECONFIGQUIT ), + dumpfailed); + else + fprintf(stderr, + "configuration saved to %s\n", + OPT_ARG( SAVECONFIGQUIT )); + + exit(dumpfailed); + } +#endif /* SAVECONFIG */ + + /* The actual configuration done depends on whether we are configuring the + * simulator or the daemon. Perform a check and call the appropriate + * function as needed. + */ + +#ifndef SIM + config_ntpd(ptree); +#else + config_ntpdsim(ptree); +#endif + + /* + * With configure --disable-saveconfig, there's no use keeping + * the config tree around after application, so free it. + */ +#ifndef SAVECONFIG + UNLINK_SLIST(punlinked, cfg_tree_history, ptree, link, + config_tree); + INSIST(punlinked == ptree); + free_config_tree(ptree); +#endif +} + + +static void +ntpd_set_tod_using( + const char *which + ) +{ + char line[128]; + + snprintf(line, sizeof(line), "settimeofday=\"%s\"", which); + set_sys_var(line, strlen(line) + 1, RO); +} + + +static char * +normal_dtoa( + double d + ) +{ + char * buf; + char * pch_e; + char * pch_nz; + + LIB_GETBUF(buf); + snprintf(buf, LIB_BUFLENGTH, "%g", d); + + /* use lowercase 'e', strip any leading zeroes in exponent */ + pch_e = strchr(buf, 'e'); + if (NULL == pch_e) { + pch_e = strchr(buf, 'E'); + if (NULL == pch_e) + return buf; + *pch_e = 'e'; + } + pch_e++; + if ('-' == *pch_e) + pch_e++; + pch_nz = pch_e; + while ('0' == *pch_nz) + pch_nz++; + if (pch_nz == pch_e) + return buf; + strlcpy(pch_e, pch_nz, LIB_BUFLENGTH - (pch_e - buf)); + + return buf; +} + + +/* FUNCTIONS COPIED FROM THE OLDER ntp_config.c + * -------------------------------------------- + */ + + +/* + * get_pfxmatch - find value for prefixmatch + * and update char * accordingly + */ +static u_int32 +get_pfxmatch( + const char ** pstr, + struct masks * m + ) +{ + while (m->name != NULL) { + if (strncmp(*pstr, m->name, strlen(m->name)) == 0) { + *pstr += strlen(m->name); + return m->mask; + } else { + m++; + } + } + return 0; +} + +/* + * get_match - find logmask value + */ +static u_int32 +get_match( + const char * str, + struct masks * m + ) +{ + while (m->name != NULL) { + if (strcmp(str, m->name) == 0) + return m->mask; + else + m++; + } + return 0; +} + +/* + * get_logmask - build bitmask for ntp_syslogmask + */ +static u_int32 +get_logmask( + const char * str + ) +{ + const char * t; + u_int32 offset; + u_int32 mask; + + mask = get_match(str, logcfg_noclass_items); + if (mask != 0) + return mask; + + t = str; + offset = get_pfxmatch(&t, logcfg_class); + mask = get_match(t, logcfg_class_items); + + if (mask) + return mask << offset; + else + msyslog(LOG_ERR, "logconfig: '%s' not recognized - ignored", + str); + + return 0; +} + + +#ifdef HAVE_NETINFO + +/* + * get_netinfo_config - find the nearest NetInfo domain with an ntp + * configuration and initialize the configuration state. + */ +static struct netinfo_config_state * +get_netinfo_config(void) +{ + ni_status status; + void *domain; + ni_id config_dir; + struct netinfo_config_state *config; + + if (ni_open(NULL, ".", &domain) != NI_OK) return NULL; + + while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) { + void *next_domain; + if (ni_open(domain, "..", &next_domain) != NI_OK) { + ni_free(next_domain); + break; + } + ni_free(domain); + domain = next_domain; + } + if (status != NI_OK) { + ni_free(domain); + return NULL; + } + + config = emalloc(sizeof(*config)); + config->domain = domain; + config->config_dir = config_dir; + config->prop_index = 0; + config->val_index = 0; + config->val_list = NULL; + + return config; +} + + +/* + * free_netinfo_config - release NetInfo configuration state + */ +static void +free_netinfo_config( + struct netinfo_config_state *config + ) +{ + ni_free(config->domain); + free(config); +} + + +/* + * gettokens_netinfo - return tokens from NetInfo + */ +static int +gettokens_netinfo ( + struct netinfo_config_state *config, + char **tokenlist, + int *ntokens + ) +{ + int prop_index = config->prop_index; + int val_index = config->val_index; + char **val_list = config->val_list; + + /* + * Iterate through each keyword and look for a property that matches it. + */ + again: + if (!val_list) { + for (; prop_index < COUNTOF(keywords); prop_index++) + { + ni_namelist namelist; + struct keyword current_prop = keywords[prop_index]; + ni_index index; + + /* + * For each value associated in the property, we're going to return + * a separate line. We squirrel away the values in the config state + * so the next time through, we don't need to do this lookup. + */ + NI_INIT(&namelist); + if (NI_OK == ni_lookupprop(config->domain, + &config->config_dir, current_prop.text, + &namelist)) { + + /* Found the property, but it has no values */ + if (namelist.ni_namelist_len == 0) continue; + + config->val_list = + emalloc(sizeof(char*) * + (namelist.ni_namelist_len + 1)); + val_list = config->val_list; + + for (index = 0; + index < namelist.ni_namelist_len; + index++) { + char *value; + + value = namelist.ni_namelist_val[index]; + val_list[index] = estrdup(value); + } + val_list[index] = NULL; + + break; + } + ni_namelist_free(&namelist); + } + config->prop_index = prop_index; + } + + /* No list; we're done here. */ + if (!val_list) + return CONFIG_UNKNOWN; + + /* + * We have a list of values for the current property. + * Iterate through them and return each in order. + */ + if (val_list[val_index]) { + int ntok = 1; + int quoted = 0; + char *tokens = val_list[val_index]; + + msyslog(LOG_INFO, "%s %s", keywords[prop_index].text, val_list[val_index]); + + (const char*)tokenlist[0] = keywords[prop_index].text; + for (ntok = 1; ntok < MAXTOKENS; ntok++) { + tokenlist[ntok] = tokens; + while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted)) + quoted ^= (*tokens++ == '"'); + + if (ISEOL(*tokens)) { + *tokens = '\0'; + break; + } else { /* must be space */ + *tokens++ = '\0'; + while (ISSPACE(*tokens)) + tokens++; + if (ISEOL(*tokens)) + break; + } + } + + if (ntok == MAXTOKENS) { + /* HMS: chomp it to lose the EOL? */ + msyslog(LOG_ERR, + "gettokens_netinfo: too many tokens. Ignoring: %s", + tokens); + } else { + *ntokens = ntok + 1; + } + + config->val_index++; /* HMS: Should this be in the 'else'? */ + + return keywords[prop_index].keytype; + } + + /* We're done with the current property. */ + prop_index = ++config->prop_index; + + /* Free val_list and reset counters. */ + for (val_index = 0; val_list[val_index]; val_index++) + free(val_list[val_index]); + free(val_list); + val_list = config->val_list = NULL; + val_index = config->val_index = 0; + + goto again; +} +#endif /* HAVE_NETINFO */ + + +/* + * getnetnum - return a net number (this is crude, but careful) + * + * returns 1 for success, and mysteriously, 0 for most failures, and + * -1 if the address found is IPv6 and we believe IPv6 isn't working. + */ +#ifndef SIM +static int +getnetnum( + const char *num, + sockaddr_u *addr, + int complain, + enum gnn_type a_type /* ignored */ + ) +{ + NTP_REQUIRE(AF_UNSPEC == AF(addr) || + AF_INET == AF(addr) || + AF_INET6 == AF(addr)); + + if (!is_ip_address(num, AF(addr), addr)) + return 0; + + if (IS_IPV6(addr) && !ipv6_works) + return -1; + +# ifdef ISC_PLATFORM_HAVESALEN + addr->sa.sa_len = SIZEOF_SOCKADDR(AF(addr)); +# endif + SET_PORT(addr, NTP_PORT); + + DPRINTF(2, ("getnetnum given %s, got %s\n", num, stoa(addr))); + + return 1; +} +#endif /* !SIM */ + +#if defined(HAVE_SETRLIMIT) +void +ntp_rlimit( + int rl_what, + rlim_t rl_value, + int rl_scale, + char * rl_sstr + ) +{ + struct rlimit rl; + + switch (rl_what) { +# ifdef RLIMIT_MEMLOCK + case RLIMIT_MEMLOCK: + /* + * The default RLIMIT_MEMLOCK is very low on Linux systems. + * Unless we increase this limit malloc calls are likely to + * fail if we drop root privilege. To be useful the value + * has to be larger than the largest ntpd resident set size. + */ + DPRINTF(2, ("ntp_rlimit: MEMLOCK: %d %s\n", + (int)(rl_value / rl_scale), rl_sstr)); + rl.rlim_cur = rl.rlim_max = rl_value; + if (setrlimit(RLIMIT_MEMLOCK, &rl) == -1) + msyslog(LOG_ERR, "Cannot set RLIMIT_MEMLOCK: %m"); + break; +# endif /* RLIMIT_MEMLOCK */ + +# ifdef RLIMIT_NOFILE + case RLIMIT_NOFILE: + /* + * For large systems the default file descriptor limit may + * not be enough. + */ + DPRINTF(2, ("ntp_rlimit: NOFILE: %d %s\n", + (int)(rl_value / rl_scale), rl_sstr)); + rl.rlim_cur = rl.rlim_max = rl_value; + if (setrlimit(RLIMIT_NOFILE, &rl) == -1) + msyslog(LOG_ERR, "Cannot set RLIMIT_NOFILE: %m"); + break; +# endif /* RLIMIT_NOFILE */ + +# ifdef RLIMIT_STACK + case RLIMIT_STACK: + /* + * Provide a way to set the stack limit to something + * smaller, so that we don't lock a lot of unused + * stack memory. + */ + DPRINTF(2, ("ntp_rlimit: STACK: %d %s pages\n", + (int)(rl_value / rl_scale), rl_sstr)); + if (-1 == getrlimit(RLIMIT_STACK, &rl)) { + msyslog(LOG_ERR, "getrlimit() failed: %m"); + } else { + if (rl_value > rl.rlim_max) { + msyslog(LOG_WARNING, + "ntp_rlimit: using maximum allowed stack limit %lu instead of %lu.", + (u_long)rl.rlim_max, + (u_long)rl_value); + rl_value = rl.rlim_max; + } + if (-1 == setrlimit(RLIMIT_STACK, &rl)) { + msyslog(LOG_ERR, + "ntp_rlimit: Cannot adjust stack limit: %m"); + } + } + break; +# endif /* RLIMIT_STACK */ + + default: + INSIST(!"Unexpected setrlimit() case!"); + break; + } +} +#endif /* HAVE_SETRLIMIT */ |