/*      $NetBSD: ntp_config.c,v 1.26 2024/08/18 20:47:17 christos Exp $ */

/* 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 <time.h>

#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_clockdev.h"
#include "ntp_filegen.h"
#include "ntp_stdlib.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"

#ifndef IGNORE_DNS_ERRORS
# define DNSFLAGS 0
#else
# define DNSFLAGS GAIR_F_IGNDNSERR
#endif

extern int yyparse(void);

/* Bug 2817 */
#if defined(HAVE_SYS_MMAN_H)
# include <sys/mman.h>
#endif

/*
* Poll Skew List
*/

static psl_item psl[17-3+1];    /* values for polls 3-17 */
                               /* To simplify the runtime code we */
                               /* don't want to have to special-case */
                               /* dealing with a default */


/* list of servers from command line for config_peers() */
int     cmdline_server_count;
char ** cmdline_servers;

/* Current state of memory locking:
* -1: default
*  0: memory locking disabled
*  1: Memory locking enabled
*/
int     cur_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;
       int             was_initializing;
} peer_resolved_ctx;

/* Limits */
#define MAXPHONE        10      /* maximum number of phone strings */
#define MAXPPS          20      /* maximum length of PPS device string */

/*
* Poll Skew List array has an entry for each supported poll
* interval.
*/
#define PSL_ENTRIES     (NTP_MAXPOLL - NTP_MINPOLL + 1)
static psl_item psl[PSL_ENTRIES];

/*
* Miscellaneous macros
*/
#define ISEOL(c)        ((c) == '#' || (c) == '\n' || (c) == '\0')
#define ISSPACE(c)      ((c) == ' ' || (c) == '\t')

#define _UC(str)        ((char *)(intptr_t)(str))

/*
* Definitions of things either imported from or exported to outside
*/
extern int yydebug;                     /* ntp_parser.c (.y) */
config_tree cfgt;                       /* Parser output stored here */
config_tree *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 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_device(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_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_vars(config_tree *);

#ifdef SIM
static void free_config_sim(config_tree *);
#else   /* !SIM follows */
static void free_config_peers(config_tree *);
static void free_config_unpeers(config_tree *);
static int is_sane_resolved_address(sockaddr_u *peeraddr, int hmode);
#endif  /* !SIM */
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 void save_and_apply_config_tree(int/*BOOL*/ from_file);
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 *, int/*BOOL*/ input_from_file);
static void config_other_modes(config_tree *);
static void config_auth(config_tree *);
static void attrtopsl(u_char log2_poll, attr_val *avp);
static void config_access(config_tree *);
static void config_mdnstries(config_tree *);
static void config_phone(config_tree *);
static void config_setvar(config_tree *);
static int  config_tos_clock(config_tree *);
static void config_ttl(config_tree *);
static void config_trap(config_tree *);
static void config_fudge(config_tree *);
static void config_device(config_tree *);
static void config_peers(config_tree *);
static void config_unpeers(config_tree *);
static void config_nic_rules(config_tree *, int/*BOOL*/ input_from_file);
static void config_reset_counters(config_tree *);
static u_char get_correct_host_mode(int token);
static int peerflag_bits(peer_node *);

#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  /* WORKER */
#endif  /* !SIM */

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 *);
static int/*BOOL*/ is_refclk_addr(const address_node * addr);

static int getnetnum(const char *num, sockaddr_u *addr, int complain,
                    enum gnn_type a_type);

#if defined(__GNUC__) /* this covers CLANG, too */
static void  __attribute__((__noreturn__,format(printf,1,2))) fatal_error(const char *fmt, ...)
#elif defined(_MSC_VER)
static void __declspec(noreturn) fatal_error(const char *fmt, ...)
#else
static void fatal_error(const char *fmt, ...)
#endif
{
       va_list va;

       va_start(va, fmt);
       mvsyslog(LOG_EMERG, fmt, va);
       va_end(va);
       _exit(1);
}


/* 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);
       ptree->mdnstries = 5;
}


#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_device(ptree);
       free_config_vars(ptree);
       free_config_nic_rules(ptree);
       free_config_reset_counters(ptree);
#ifdef SIM
       free_config_sim(ptree);
#else   /* !SIM follows */
       free_config_peers(ptree);
       free_config_unpeers(ptree);
#endif  /* !SIM */
       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;
       time_t          now = time(NULL);
       struct tm       tm = *localtime(&now);

       fprintf(df, "#NTF:D %04d%02d%02d@%02d:%02d:%02d\n",
               tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
               tm.tm_hour, tm.tm_min, tm.tm_sec);
       fprintf(df, "#NTF:V %s\n", Version);

       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 *counter_set;
       string_node *str_node;

       const char *s = NULL;
       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 without documentation we just output the name
        * and its data 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 if (T_Leapfile == atrv->attr) {
                               fputs((atrv->flag
                                      ? " checkhash\n"
                                      : " ignorehash\n"),
                                     df);
                       } 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_Integer:
                               if (atrv->attr == T_Basedate) {
                                       struct calendar jd;
                                       ntpcal_rd_to_date(&jd, atrv->value.i + DAY_NTP_STARTS);
                                       fprintf(df, " %s \"%04hu-%02hu-%02hu\"",
                                               keyword(atrv->attr), jd.year,
                                               (u_short)jd.month,
                                               (u_short)jd.monthday);
                               } else {
                                       fprintf(df, " %s %d",
                                       keyword(atrv->attr),
                                       atrv->value.i);
                               }
                               break;

                       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_opts = HEAD_PFIFO(ptree->device);
               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, "device %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 device atrv->type %d\n"
                                               "device %s", atrv->type,
                                               s1);
                                       break;
#endif
                               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");
       }

       atrv = HEAD_PFIFO(ptree->pollskewlist);
       if (atrv != NULL) {
               fprintf(df, "pollskewlist");
               for ( ; atrv != NULL; atrv = atrv->link) {
                       if (-1 == atrv->attr) {
                               fprintf(df, " default");
                       } else {
                               fprintf(df, " %d", atrv->attr);
                       }
                       fprintf(df, " %d|%d",
                               atrv->value.r.first, atrv->value.r.last);
               }
               fprintf(df, "\n");
       }

       for (rest_node = HEAD_PFIFO(ptree->restrict_opts);
            rest_node != NULL;
            rest_node = rest_node->link) {
               int/*BOOL*/ is_default = FALSE;
               int/*BOOL*/ omit_mask;
               sockaddr_u mask;
               sockaddr_u onesmask;

               s = NULL;
               atrv = HEAD_PFIFO(rest_node->flag_tok_fifo);
               for (; atrv != NULL; atrv = atrv->link) {
                       if (   T_Integer == atrv->type
                           && T_Source == atrv->attr) {
                               s = keyword(T_Source);
                               break;
                       }
               }
               if (NULL == rest_node->addr) {
                       if (NULL == s) {
                               s = keyword(T_Default);
                               /* Don't need to set is_default here */
                       }
               } else {
                       const char *ap = rest_node->addr->address;
                       const char *mp = "";

                       if (rest_node->mask)
                               mp = rest_node->mask->address;

                       if (   rest_node->addr->type == AF_INET
                           && !strcmp(mp, "0.0.0.0")
                           && !strcmp(ap, mp)) {
                               is_default = TRUE;
                               s = "-4 default";
                       } else if (   rest_node->mask
                                  && rest_node->mask->type == AF_INET6
                                  && !strcmp(mp, "::")
                                  && !strcmp(ap, mp)) {
                               is_default = TRUE;
                               s = "-6 default";
                       } else {
                               if (NULL == s) {
                                       s = ap;
                               } else {
                                       LIB_GETBUF(s1);
                                       snprintf(s1, LIB_BUFLENGTH,
                                                "%s %s",
                                                keyword(T_Source), ap);
                                       s = s1;
                               }
                       }
               }
               fprintf(df, "%s %s",
                       keyword(rest_node->remove
                                       ? T_Delrestrict
                                       : T_Restrict),
                       s);
               if (rest_node->mask != NULL && !is_default) {
                       ZERO(mask);
                       AF(&mask) = AF_UNSPEC;
                       omit_mask = (0 != getnetnum(rest_node->mask->address,
                                                   &mask, 0, t_UNK));
                       if (omit_mask) {
                               SET_HOSTMASK(&onesmask, AF(&mask));
                               omit_mask = SOCK_EQ(&mask, &onesmask);
                       }
                       if (!omit_mask) {
                               fprintf(df, " mask %s",
                                       rest_node->mask->address);
                       }
               }
               if (-1 != rest_node->ippeerlimit) {
                       fprintf(df, " ippeerlimit %d",
                               rest_node->ippeerlimit);
               }
               atrv = HEAD_PFIFO(rest_node->flag_tok_fifo);
               for ( ; atrv != NULL; atrv = atrv->link) {
                       if (   T_Integer == atrv->type
                           && T_Source != atrv->attr) {
                               fprintf(df, " %s", keyword(atrv->attr));
                       }
               }
               fprintf(df, "\n");
/**/
#if 0
msyslog(LOG_INFO, "Dumping flag_tok_fifo:");
atrv = HEAD_PFIFO(rest_node->flag_tok_fifo);
for ( ; atrv != NULL; atrv = atrv->link) {
       msyslog(LOG_INFO, "- flag_tok_fifo: flags: %08x", atrv->flag);
       switch(atrv->type) {
           case T_Integer:
               msyslog(LOG_INFO, "- T_Integer: attr <%s>/%d, value %d",
                       keyword(atrv->attr), atrv->attr, atrv->value.i);
               break;
           default:
               msyslog(LOG_INFO, "- Other: attr <%s>/%d, value ???",
                       keyword(atrv->attr), atrv->attr);
               break;

       }
}
#endif
/**/
       }

       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;
}

void*
destroy_gen_fifo(
       void        *fifo,
       fifo_deleter func
       )
{
       any_node *      np  = NULL;
       any_node_fifo * pf1 = fifo;

       if (pf1 != NULL) {
               if (!func)
                       func = free;
               for (;;) {
                       UNLINK_FIFO(np, *pf1, link);
                       if (np == NULL)
                               break;
                       (*func)(np);
               }
               free(pf1);
       }
       return NULL;
}

/* FUNCTIONS FOR CREATING NODES ON THE SYNTAX TREE
* -----------------------------------------------
*/

void
destroy_attr_val(
       attr_val *      av
       )
{
       if (av) {
               if (T_String == av->type)
                       free(av->value.s);
               free(av);
       }
}

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_rval(
       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,
       const 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 = _UC(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;

       REQUIRE(NULL != addr);
       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;
       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 (is_refclk_addr(addr)) {
                               msyslog(LOG_ERR, "'ttl' does not apply for refclocks");
                               errflag = 1;
                       } else 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:
                       if (is_refclk_addr(addr)) {
                               my_node->ttl = option->value.u;
                       } else {
                               msyslog(LOG_ERR, "'mode' does not apply for network peers");
                               errflag = 1;
                       }
                       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_long          u;
       const 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.
        */
       for (u = 0, pch = (u_char*)addr->address; isdigit(*pch); ++pch) {
               /* accumulate with overflow retention */
               u = (10 * u + *pch - '0') | (u & 0xFF000000u);
       }

       if (!*pch && u <= ASSOCID_MAX) {
               my_node->assocID = (associd_t)u;
               my_node->addr = NULL;
               destroy_address_node(addr);
       } 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,
       short           ippeerlimit,
       attr_val_fifo * flag_tok_fifo,
       int/*BOOL*/     remove,
       int             nline,
       int             ncol
)
{
       restrict_node *my_node;

       my_node = emalloc_zero(sizeof(*my_node));
       my_node->addr = addr;
       my_node->mask = mask;
       my_node->ippeerlimit = ippeerlimit;
       my_node->flag_tok_fifo = flag_tok_fifo;
       my_node->remove = remove;
       my_node->line_no = nline;
       my_node->column = ncol;

       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_attr_val_fifo(my_node->flag_tok_fifo);
       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;
                       destroy_attr_val(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;

       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
       my_val = HEAD_PFIFO(ptree->auth.crypto_cmd_list);
       for (; my_val != NULL; my_val = my_val->link) {
               switch (my_val->attr) {

               default:
                       fatal_error("config_auth: attr-token=%d", my_val->attr);

               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 > 2 && ptree->auth.revoke < 32)
               sys_revoke = (u_char)ptree->auth.revoke;
       else if (ptree->auth.revoke)
               msyslog(LOG_ERR,
                       "'revoke' value %d ignored",
                       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 */


#ifndef SIM
/* Configure low-level clock-related parameters. Return TRUE if the
* clock might need adjustment like era-checking after the call, FALSE
* otherwise.
*/
static int/*BOOL*/
config_tos_clock(
       config_tree* ptree
)
{
       int             ret;
       attr_val* tos;

       ret = FALSE;
       tos = HEAD_PFIFO(ptree->orphan_cmds);
       for (; tos != NULL; tos = tos->link) {
               switch (tos->attr) {

               default:
                       break;

               case T_Basedate:
                       basedate_set_day(tos->value.i);
                       ret = TRUE;
                       break;
               }
       }

       if (basedate_get_day() <= NTP_TO_UNIX_DAYS) {
               basedate_set_day(basedate_eval_buildstamp() - 11);
       }
       return ret;
}
#endif  /* !SIM */


static void
config_tos(
       config_tree *ptree
       )
{
       char const      improper_operation_msg[] =
                               " - daemon will not operate properly!";
       attr_val *      tos;
       int             item;
       double          val;

       /* [Bug 2896] For the daemon to work properly it is essential
        * that minsane < minclock <= maxclock.
        *
        * If either constraint is violated, the daemon will be or might
        * become dysfunctional. Fixing the values is too fragile here,
        * since three variables with interdependecies are involved. We
        * just log an error but do not stop: This might be caused by
        * remote config, and it might be fixed by remote config, too.
        */
       int l_maxclock  = sys_maxclock;
       int l_minclock  = sys_minclock;
       int l_minsane   = sys_minsane;
       int l_floor     = sys_floor;
       int l_ceiling   = sys_ceiling;

       /* -*- phase one: inspect / sanitize the values */
       tos = HEAD_PFIFO(ptree->orphan_cmds);
       for (; tos != NULL; tos = tos->link) {
               /* not all attributes are doubles (any more), so loading
                * 'val' in all cases is not a good idea: It should be
                * done as needed in every case processed here.
                */
               switch(tos->attr) {
               default:
                       break;

               case T_Bcpollbstep:
                       val = tos->value.d;
                       if (val > 4) {
                               msyslog(LOG_WARNING,
                                       "Using maximum tos bcpollbstep %d, %d requested",
                                       4, (int)val);
                               tos->value.d = 4;
                       } else if (val < 0) {
                               msyslog(LOG_WARNING,
                                       "Using minimum tos bcpollbstep %d, %d requested",
                                       0, (int)val);
                               tos->value.d = 0;
                       }
                       break;

               case T_Floor:
                       l_floor = (int)tos->value.d;
                       if (l_floor > STRATUM_UNSPEC - 1) {
                               msyslog(LOG_WARNING,
                                       "Using maximum tos floor %d, %d requested",
                                       STRATUM_UNSPEC - 1, l_floor);
                               tos->value.d = STRATUM_UNSPEC - 1;
                       }
                       else if (l_floor < 0) {
                               msyslog(LOG_WARNING,
                                       "Using minimum tos floor %d, %d requested",
                                       0, l_floor);
                               tos->value.d = 0;
                       }
                       l_floor = (int)tos->value.d;
                       break;

               case T_Ceiling:
                       l_ceiling = (int)tos->value.d;
                       if (l_ceiling > STRATUM_UNSPEC - 1) {
                               msyslog(LOG_WARNING,
                                       "Using maximum tos ceiling %d, %d requested",
                                       STRATUM_UNSPEC - 1, l_ceiling);
                               tos->value.d = STRATUM_UNSPEC - 1;
                       }
                       else if (l_ceiling < 0) {
                               msyslog(LOG_WARNING,
                                       "Using minimum tos ceiling %d, %d requested",
                                       0, l_ceiling);
                               tos->value.d = 0;
                       }
                       l_ceiling = (int)tos->value.d;
                       break;

               case T_Minclock:
                       val = tos->value.d;
                       if ((int)tos->value.d < 1)
                               tos->value.d = 1;
                       l_minclock = (int)tos->value.d;
                       break;

               case T_Maxclock:
                       val = tos->value.d;
                       if ((int)tos->value.d < 1)
                               tos->value.d = 1;
                       l_maxclock = (int)tos->value.d;
                       break;

               case T_Minsane:
                       val = tos->value.d;
                       if ((int)tos->value.d < 0)
                               tos->value.d = 0;
                       l_minsane = (int)tos->value.d;
                       break;
               }
       }

       if ( ! (l_minsane < l_minclock && l_minclock <= l_maxclock)) {
               msyslog(LOG_ERR, "Must have tos "
                       "minsane (%d) < minclock (%d) <= maxclock (%d)%s",
                       l_minsane, l_minclock, l_maxclock,
                       improper_operation_msg);
       }

       if (l_floor > l_ceiling) {
               msyslog(LOG_ERR, "Must have tos "
                       "floor (%d) <= ceiling (%d)%s",
                       l_floor, l_ceiling, improper_operation_msg);
       }

       /* -*- phase two: forward the values to the protocol machinery */
       tos = HEAD_PFIFO(ptree->orphan_cmds);
       for (; tos != NULL; tos = tos->link) {
               switch(tos->attr) {

               default:
                       fatal_error("config-tos: attr-token=%d", tos->attr);

               case T_Bcpollbstep:
                       item = PROTO_BCPOLLBSTEP;
                       break;

               case T_Ceiling:
                       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;

               case T_Basedate:
                       continue; /* SKIP proto-config for this! */
               }
               proto_config(item, 0, tos->value.d, 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, TRUE);
       }

       /* 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:
                                       fatal_error("config-monitor: type-token=%d", my_opts->value.i);

                               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;
       sockaddr_u              addr;
       sockaddr_u              mask;
       struct addrinfo         hints;
       struct addrinfo *       ai_list;
       struct addrinfo *       pai;
       int                     rc;
       int/*BOOL*/             success;
       int/*BOOL*/             restrict_default;
       u_short                 rflags;
       u_short                 mflags;
       short                   ippeerlimit;
       int                     range_err;
       attr_val *              atrv;
       attr_val *              dflt_psl_atr;
       const char *            signd_warning =
#ifdef HAVE_NTP_SIGND
           "MS-SNTP signd operations currently block ntpd degrading service to all clients.\n";
#else
           "mssntp restrict bit ignored, this ntpd was configured without --enable-ntp-signd.\n";
#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 each line of restrict options */
       my_node = HEAD_PFIFO(ptree->restrict_opts);

       for (; my_node != NULL; my_node = my_node->link) {

               /* Grab the ippeerlmit */
               ippeerlimit = my_node->ippeerlimit;

               /* Parse the flags */
               rflags = 0;
               mflags = 0;

               my_opt = HEAD_PFIFO(my_node->flag_tok_fifo);
               for (; my_opt != NULL; my_opt = my_opt->link) {
                       switch (my_opt->attr) {

                       default:
                               fatal_error("config_access: Unknown flag-type-token=%s/%d", keyword(my_opt->attr), my_opt->attr);

                       case T_Ntpport:
                               mflags |= RESM_NTPONLY;
                               break;

                       case T_Source:
                               mflags |= RESM_SOURCE;
                               break;

                       case T_Flake:
                               rflags |= RES_FLAKE;
                               break;

                       case T_Ignore:
                               rflags |= RES_IGNORE;
                               break;

                       case T_Kod:
                               rflags |= RES_KOD;
                               break;

                       case T_Limited:
                               rflags |= RES_LIMITED;
                               break;

                       case T_Lowpriotrap:
                               rflags |= RES_LPTRAP;
                               break;

                       case T_Mssntp:
                               rflags |= RES_MSSNTP;
                               break;

                       case T_Nomodify:
                               rflags |= RES_NOMODIFY;
                               break;

                       case T_Nomrulist:
                               rflags |= RES_NOMRULIST;
                               break;

                       case T_Noepeer:
                               rflags |= RES_NOEPEER;
                               break;

                       case T_Nopeer:
                               rflags |= RES_NOPEER;
                               break;

                       case T_Noquery:
                               rflags |= RES_NOQUERY;
                               break;

                       case T_Noserve:
                               rflags |= RES_DONTSERVE;
                               break;

                       case T_Notrap:
                               rflags |= RES_NOTRAP;
                               break;

                       case T_Notrust:
                               rflags |= RES_DONTTRUST;
                               break;

                       case T_ServerresponseFuzz:
                               rflags |= RES_SRVRSPFUZ;
                               break;

                       case T_Version:
                               rflags |= RES_VERSION;
                               break;
                       }
               }

               if ((RES_MSSNTP & rflags) && !warned_signd) {
                       warned_signd = TRUE;
                       fprintf(stderr, "%s", signd_warning);
                       msyslog(LOG_WARNING, "%s", signd_warning);
               }

               if ((RES_KOD & rflags) && !(RES_LIMITED & rflags)) {
                       const char *kod_where = (my_node->addr)
                                         ? my_node->addr->address
                                         : (mflags & RESM_SOURCE)
                                           ? "source"
                                           : "default";
                       const char *kod_warn = "'kod' does nothing without 'limited'.\n";

                       fprintf(stderr, "line %d col %d restrict %s: %s",
                               my_node->line_no, my_node->column,
                               kod_where, kod_warn);
                       msyslog(LOG_WARNING, "line %d col %d restrict %s: %s",
                               my_node->line_no, my_node->column,
                               kod_where, kod_warn);
               }

               ZERO_SOCK(&addr);
               ai_list = NULL;
               pai = NULL;
               restrict_default = FALSE;

               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 = TRUE;
                       } else {
                               /* apply "restrict source ..." */
                               success = hack_restrict(RESTRICT_FLAGS,
                                                       NULL, NULL,
                                                       ippeerlimit,
                                                       mflags, rflags,
                                                       0);
                               if (!success) {
                                       msyslog(LOG_ERR,
                                               "unable to save restrict source");
                               }
                               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: line %d col %d"
                                               " address/host '%s' unusable.",
                                               my_node->line_no,
                                               my_node->column,
                                               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));
                       }

                       /* default to all-ones mask for single address */
                       SET_HOSTMASK(&mask, AF(&addr));

                       /* Ignore mask if addr from hostname [Bug 3872] */
                       if (NULL == ai_list && 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: line %d col %d"
                                               " mask '%s' unusable.",
                                               my_node->line_no,
                                               my_node->column,
                                               my_node->mask->address);
                                       continue;
                               }
                       }
               }

               /* Set the flags */
               if (restrict_default) {
                       AF(&addr) = AF_INET;
                       AF(&mask) = AF_INET;
                       success = hack_restrict(
                                       RESTRICT_FLAGS,
                                       &addr,
                                       &mask,
                                       ippeerlimit,
                                       mflags,
                                       rflags,
                                       0
                                       );
                       if (!success) {
                               msyslog(LOG_ERR,
                                       "unable to save %s %s restriction",
                                       stoa(&addr), stoa(&mask));
                       }
                       AF(&addr) = AF_INET6;
                       AF(&mask) = AF_INET6;
               }

               do {
                       success = hack_restrict(
                                       my_node->remove
                                               ? RESTRICT_REMOVE
                                               : RESTRICT_FLAGS,
                                       &addr,
                                       &mask,
                                       ippeerlimit,
                                       mflags,
                                       rflags,
                                       0);
                       if (!success) {
                               msyslog(LOG_ERR,
                                       "unable to %s %s %s restriction",
                                       my_node->remove
                                               ? "delete"
                                               : "save",
                                       stoa(&addr), stoa(&mask));
                       }
                       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);
               }
       }

       /*
        * pollskewlist
        */
       atrv = HEAD_PFIFO(ptree->pollskewlist);
       if (NULL == atrv) {
               /* don't touch the poll skew list */
               return;
       }
       ZERO(psl);
       /*
        * First, find the last default pollskewlist item.
        */
       dflt_psl_atr = NULL;
       for ( ; atrv != NULL; atrv = atrv->link) {
               if (-1 == atrv->attr) { /* default */
                       dflt_psl_atr = atrv;
               } else if (   atrv->attr < NTP_MINPOLL
                          || atrv->attr > NTP_MAXPOLL) {
                       msyslog(LOG_ERR,
                               "Poll %d out of bounds [%d-%d] for pollskewlist",
                               atrv->attr, NTP_MINPOLL, NTP_MAXPOLL);
               }
       }

       /* If we have a nonzero default, put it in all entries */
       if (   dflt_psl_atr
           && (   0 != dflt_psl_atr->value.r.first
               || 0 != dflt_psl_atr->value.r.last)) {
               int i;

               for (i = NTP_MINPOLL; i <= NTP_MAXPOLL; ++i) {
                       attrtopsl(i, dflt_psl_atr);
               }
       }

       /* Finally, update the PSL with any explicit entries */
       atrv = HEAD_PFIFO(ptree->pollskewlist);
       for ( ; atrv != NULL; atrv = atrv->link) {
               if (atrv->attr >= NTP_MINPOLL && atrv->attr <= NTP_MAXPOLL) {
                       attrtopsl(atrv->attr, atrv);
               }
       }

#if 0
       int p;
       msyslog(LOG_INFO, "Dumping PSL:");
       for (p = NTP_MINPOLL; p <= NTP_MAXPOLL; ++p) {
               psl_item psi;

               if (0 == get_pollskew(p, &psi)) {
                       msyslog(LOG_INFO, "poll %d: sub %d, qty %d, msk %d",
                               p, psi.sub, psi.qty, psi.msk);
               } else {
                       msyslog(LOG_ERR, "Dumping PSL: get_pollskew(%d) failed!", p);
               }
       }
#endif
}


static void
attrtopsl(
       u_char          log2_poll,
       attr_val *      avp
       )
{
       int     pao   = log2_poll - NTP_MINPOLL;     /* poll array offset */
       u_int32 lower = (u_short)avp->value.r.first; /* ntp_parser.y ensures */
       u_int32 upper = (u_short)avp->value.r.last;  /* non-neg. first/last */
       u_int   psmax = 1 << (log2_poll - 1);
       u_int32 qmsk;

       DEBUG_INSIST((size_t)pao < COUNTOF(psl));

       if (lower > psmax) {
               msyslog(LOG_WARNING, "pollskewlist %d lower bound reduced from %d to %d",
                       log2_poll, lower, psmax);
               lower = psmax;
       }
       if (upper > psmax) {
               msyslog(LOG_WARNING, "pollskewlist %d upper bound reduced from %d to %d",
                       log2_poll, upper, psmax);
               upper = psmax;
       }
       psl[pao].sub = lower;
       psl[pao].qty = lower + upper;

       qmsk = 1;
       while (qmsk < (lower + upper)) {
               qmsk <<= 1;
               qmsk |=  1;
       }
       psl[pao].msk = qmsk;
}
#endif  /* !SIM */


int
get_pollskew(
       int p,
       psl_item *rv
       )
{
#ifdef DISABLE_BUG3767_FIX
       DEBUG_INSIST(NTP_MINPOLL <= p && NTP_MAXPOLL >= p);
#endif
       if (NTP_MINPOLL <= p && p <= NTP_MAXPOLL) {
               *rv = psl[p - NTP_MINPOLL];
               return 0;
       } else {
               msyslog(LOG_DEBUG, "get_pollskew(%d): out of range", p);
               return -1;
       }

       /* NOTREACHED */
}


#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:
                       fatal_error("config-rlimit: value-token=%d", rlimit_av->attr);

               case T_Memlock:
                       /* What if we HAVE_OPT(SAVECONFIGQUIT) ? */
                       if (HAVE_OPT( SAVECONFIGQUIT )) {
                               break;
                       }
                       if (rlimit_av->value.i == -1) {
# if defined(HAVE_MLOCKALL)
                               if (cur_memlock != 0) {
                                       if (-1 == munlockall()) {
                                               msyslog(LOG_ERR, "munlockall() failed: %m");
                                       }
                               }
                               cur_memlock = 0;
# endif /* HAVE_MLOCKALL */
                       } else if (rlimit_av->value.i >= 0) {
#if defined(RLIMIT_MEMLOCK)
# if defined(HAVE_MLOCKALL)
                               if (cur_memlock != 1) {
                                       if (-1 == mlockall(MCL_CURRENT|MCL_FUTURE)) {
                                               msyslog(LOG_ERR, "mlockall() failed: %m");
                                       }
                               }
# endif /* HAVE_MLOCKALL */
                               ntp_rlimit(RLIMIT_MEMLOCK,
                                          (rlim_t)(rlimit_av->value.i * 1024 * 1024),
                                          1024 * 1024,
                                          "MB");
                               cur_memlock = 1;
#else
                               /* STDERR as well would be fine... */
                               msyslog(LOG_WARNING, "'rlimit memlock' specified but is not available on this system.");
#endif /* RLIMIT_MEMLOCK */
                       } else {
                               msyslog(LOG_WARNING, "'rlimit memlock' value of %d is unexpected!", rlimit_av->value.i);
                       }
                       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;

       tinker = HEAD_PFIFO(ptree->tinker);
       for (; tinker != NULL; tinker = tinker->link) {
               switch (tinker->attr) {

               default:
                       fatal_error("config_tinker: attr-token=%d", tinker->attr);

               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_Stepback:
                       item = LOOP_MAX_BACK;
                       break;

               case T_Stepfwd:
                       item = LOOP_MAX_FWD;
                       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,
       int/*BOOL*/ input_from_file
       )
{
       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:
#ifdef DEBUG
                       fatal_error("config_nic_rules: match-class-token=%d", curr_node->match_class);
#endif
               case T_All:
                       match_type = MATCH_ALL;
                       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_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:
#ifdef DEBUG
                       fatal_error("config_nic_rules: action-token=%d", curr_node->action);
#endif
               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);
               if (!initializing && !scan_addrs_once) {
                       endpt_scan_timer = 1 + current_time;
               }
               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_tok_fifo;
       int option;
#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED
       bc_entry *pentry;
#endif

       for (curr_tok_fifo = HEAD_PFIFO(fifo);
            curr_tok_fifo != NULL;
            curr_tok_fifo = curr_tok_fifo->link) {

               option = curr_tok_fifo->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_Mode7:
                       proto_config(PROTO_MODE7, enable, 0., NULL);
                       break;

               case T_Ntp:
                       proto_config(PROTO_NTP, enable, 0., NULL);
                       break;

               case T_PCEdigest:
                       proto_config(PROTO_PCEDIGEST, enable, 0., NULL);
                       break;

               case T_Stats:
                       proto_config(PROTO_FILEGEN, enable, 0., NULL);
                       break;

               case T_UEcrypto:
                       proto_config(PROTO_UECRYPTO, enable, 0., NULL);
                       break;

               case T_UEcryptonak:
                       proto_config(PROTO_UECRYPTONAK, enable, 0., NULL);
                       break;

               case T_UEdigest:
                       proto_config(PROTO_UEDIGEST, 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:
                       fatal_error("config-logconfig: modifier='%c'", my_lc->attr);
               }
       }
}


#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
       )
{
       size_t          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 %zu. Ignoring phone %s...",
                               (COUNTOF(sys_phone) - 1), sn->s);
               }
       }
}

static void
config_mdnstries(
       config_tree *ptree
       )
{
#ifdef HAVE_DNSREGISTRATION
       extern int mdnstries;
       mdnstries = ptree->mdnstries;
#endif  /* HAVE_DNSREGISTRATION */
}
#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
       )
{
       size_t i = 0;
       int_node *curr_ttl;

       /* [Bug 3465] There is a built-in default for the TTLs. We must
        * overwrite 'sys_ttlmax' if we change that preset, and leave it
        * alone otherwise!
        */
       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 %zu. Ignoring TTL %d...",
                               COUNTOF(sys_ttl), curr_ttl->i);
       }
       if (0 != i) /* anything written back at all? */
               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;
       endpt *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;
       endpt *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;
       char refidstr[5];
       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",
                               addr_node->address);
               } else 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);
               /* some things are not necessarily cleared by ZERO...*/
               clock_stat.fudgeminjitter = 0.0;
               clock_stat.fudgetime1     = 0.0;
               clock_stat.fudgetime2     = 0.0;
               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;
                               /*
                                * strncpy() does exactly what we want
                                * here, do not be tempted to replace
                                * it with strlcpy(), we want it to
                                * zero-fill refid's less than 4 chars
                                * because we're going to stuff it
                                * into a u_int32.
                                */
                               strncpy(refidstr, curr_opt->value.s,
                                       sizeof refidstr - 1);
                               memcpy(&clock_stat.fudgeval2, refidstr,
                                      sizeof clock_stat.fudgeval2);
                               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;

                       case T_Minjitter:
                               clock_stat.haveflags |= CLK_HAVEMINJIT;
                               clock_stat.fudgeminjitter = curr_opt->value.d;
                               break;

                       default:
                               msyslog(LOG_ERR,
                                       "Unexpected fudge flag %s (%d) for %s",
                                       token_name(curr_opt->attr),
                                       curr_opt->attr, addr_node->address);
                               exit(curr_opt->attr ? curr_opt->attr : 1);
                       }
               }
# ifdef REFCLOCK
               if (!err_flag)
                       refclock_control(&addr_sock, &clock_stat, NULL);
# endif
       }
}
#endif  /* !SIM */

#ifndef SIM
static void
config_device(
       config_tree *ptree
       )
{
       addr_opts_node *curr_device;
       attr_val *curr_opt;
       sockaddr_u addr_sock;
       address_node *addr_node;
       char *ttyName, *ppsName;

       curr_device = HEAD_PFIFO(ptree->device);
       for (; curr_device != NULL; curr_device = curr_device->link) {
               /* Get the reference clock address and
                * ensure that it is sane
                */
               addr_node = curr_device->addr;
               ZERO_SOCK(&addr_sock);
               if (getnetnum(addr_node->address, &addr_sock, 1, t_REF)
                   != 1) {
                       msyslog(LOG_ERR,
                               "unrecognized device reference clock address %s, line ignored",
                               addr_node->address);
                       continue;
               }
               if (!ISREFCLOCKADR(&addr_sock)) {
                       msyslog(LOG_ERR,
                               "inappropriate address %s for the device command, line ignored",
                               stoa(&addr_sock));
                       continue;
               }

               ppsName = ttyName = NULL;
               curr_opt = HEAD_PFIFO(curr_device->options);
               for (; curr_opt != NULL; curr_opt = curr_opt->link) {
                       switch (curr_opt->attr) {

                       case T_TimeData:
                               ttyName = curr_opt->value.s;
                               break;

                       case T_PpsData:
                               ppsName = curr_opt->value.s;
                               break;

                       default:
                               msyslog(LOG_ERR,
                                       "Unexpected device spec %s (%d) for %s",
                                       token_name(curr_opt->attr),
                                       curr_opt->attr, addr_node->address);
                               exit(curr_opt->attr ? curr_opt->attr : 1);
                       }
               }
# ifdef REFCLOCK
               clockdev_update(&addr_sock, ttyName, ppsName);
# endif
       }
}
#endif  /* !SIM */


#ifdef FREE_CFG_T
static void
free_config_fudge(
       config_tree *ptree
       )
{
       FREE_ADDR_OPTS_FIFO(ptree->fudge);
}

static void
free_config_device(
       config_tree *ptree
       )
{
       FREE_ADDR_OPTS_FIFO(ptree->device);
}
#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])
                               msyslog(LOG_INFO, "config: driftfile disabled");
                       stats_config(STATS_FREQ_FILE, curr_var->value.s, TRUE);
                       break;

               case T_Dscp:
                       /* DSCP is in the upper 6 bits of the IP TOS/DS field */
                       qos = curr_var->value.i << 2;
                       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, curr_var->flag);
                       break;

#ifdef LEAP_SMEAR
               case T_Leapsmearinterval:
                       leap_smear_intv = curr_var->value.i;
                       msyslog(LOG_INFO, "config: leap smear interval %i s", leap_smear_intv);
                       break;
#endif

               case T_Pidfile:
                   stats_config(STATS_PID_FILE, curr_var->value.s, 0);
                       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
                       if (curr_var->value.i > 2 && curr_var->value.i < 32)
                               sys_automax = (u_char)curr_var->value.i;
                       else
                               msyslog(LOG_ERR,
                                       "'automax' value %d ignored",
                                       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 */


#ifndef SIM
/* 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!
        * - 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;
}


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;
       int     hmode;

       DEBUG_INSIST(pn);
       /* translate peerflags options to bits */
       peerflags = 0;
       hmode = pn->host_mode;
       option = HEAD_PFIFO(pn->peerflags);
       for (; option != NULL; option = option->link) {
               switch (option->value.i) {

               default:
                       fatal_error("peerflag_bits: option-token=%d", option->value.i);

               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;

               case T_Xmtnonce:
                       if (   MODE_CLIENT == hmode ) {
                               peerflags |= FLAG_LOOPNONCE;
                       }
                       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.
                *
                * Note that if we're told to add the peer here, we
                * do that regardless of ippeerlimit.
                */
               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,
                                       -1,
                                       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;
                       ctx->was_initializing = initializing;

                       ZERO(hints);
                       hints.ai_family = (u_short)ctx->family;
                       hints.ai_socktype = SOCK_DGRAM;
                       hints.ai_protocol = IPPROTO_UDP;

                       getaddrinfo_sometime_ex(*cmdline_servers,
                                            "ntp", &hints,
                                            INITIAL_DNS_RETRY,
                                            &peer_name_resolved,
                                            (void *)ctx, DNSFLAGS);
# 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,
                               -1,
                               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,
                                       -1,
                                       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;
                       ctx->was_initializing = initializing;

                       ZERO(hints);
                       hints.ai_family = ctx->family;
                       hints.ai_socktype = SOCK_DGRAM;
                       hints.ai_protocol = IPPROTO_UDP;

                       getaddrinfo_sometime_ex(curr_peer->addr->address,
                                            "ntp", &hints,
                                            INITIAL_DNS_RETRY,
                                            &peer_name_resolved, ctx,
                                            DNSFLAGS);
# else  /* !WORKER follows */
                       msyslog(LOG_ERR,
                               "hostname %s can not be used, please use IP address instead.",
                               curr_peer->addr->address);
# endif
               }
       }
}


/*
* 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) {
               free(ctx);
               msyslog(LOG_ERR,
                       "giving up resolving host %s: %s (%d)",
                       name, gai_strerror(rescode), rescode);
               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_clear needs to know if this association was specified
                        * in the startup configuration file to set the next poll time.
                        */
                       if (ctx->was_initializing) {
                               INSIST(!initializing);
                               initializing = TRUE;
                       }

                       peer_config(
                               &peeraddr,
                               NULL,
                               NULL,
                               -1,
                               ctx->hmode,
                               ctx->version,
                               ctx->minpoll,
                               ctx->maxpoll,
                               ctx->flags,
                               ctx->ttl,
                               ctx->keyid,
                               ctx->group);

                       if (ctx->was_initializing) {
                               initializing = FALSE;
                       }

                       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 */


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) {
               /*
                * If we have no address attached, assume we have to
                * unpeer by AssocID.
                */
               if (!curr_unpeer->addr) {
                       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, NULL);
                       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
       }
}


/*
* 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, NULL);
               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 */
#endif  /* !SIM */


#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 = eallocarray(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,
       int/*BOOL*/ input_from_files
       )
{
       /* [Bug 3435] check and esure clock sanity if configured from
        * file and clock sanity parameters (-> basedate) are given. Do
        * this ASAP, so we don't disturb the closed loop controller.
        */
       if (input_from_files) {
               if (config_tos_clock(ptree))
                       clamp_systime();
       }

       config_nic_rules(ptree, input_from_files);
       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_mdnstries(ptree);
       config_setvar(ptree);
       config_ttl(ptree);
       config_vars(ptree);

       io_open_sockets();      /* [bug 2837] dep. on config_vars() */

       config_trap(ptree);     /* [bug 2923] dep. on io_open_sockets() */
       config_other_modes(ptree);
       config_device(ptree);
       config_peers(ptree);
       config_unpeers(ptree);
       config_fudge(ptree);
       config_reset_counters(ptree);

#ifdef DEBUG
       if (debug > 1) {
               dump_restricts();
       }
#endif

#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
       )
{
       char origin[128];

       snprintf(origin, sizeof(origin), "remote config from %s",
                stoa(remote_addr));
       lex_init_stack(origin, NULL); /* no checking needed... */
       init_syntax_tree(&cfgt);
       yyparse();
       lex_drop_stack();

       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(FALSE);
}


/*
* 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);
       if (
               !lex_init_stack(FindConfig(config_file), "r")
#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>: %m", 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 (!lex_init_stack(FindConfig(alt_config_file), "r"))  {
                       /*
                        * Broadcast clients can sometimes run without
                        * a configuration file.
                        */
                       msyslog(LOG_INFO, "getconfig: Couldn't open <%s>: %m", 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 ***/
#if defined(DEBUG) && defined(YYDEBUG)
       yydebug = (debug >= 9);
#endif
       yyparse();
       lex_drop_stack();

       DPRINTF(1, ("Finished Parsing!!\n"));

       cfgt.source.attr = CONF_SOURCE_FILE;
       cfgt.timestamp = time(NULL);

       save_and_apply_config_tree(TRUE);

#ifdef HAVE_NETINFO
       if (config_netinfo)
               free_netinfo_config(config_netinfo);
#endif /* HAVE_NETINFO */
}


void
save_and_apply_config_tree(int/*BOOL*/ input_from_file)
{
       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, input_from_file);
#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
}

/* Hack to disambiguate 'server' statements for refclocks and network peers.
* Please note the qualification 'hack'. It's just that.
*/
static int/*BOOL*/
is_refclk_addr(
       const address_node * addr
       )
{
       return addr && addr->address && !strncmp(addr->address, "127.127.", 8);
}

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) {
               memmove(pch_e, pch_nz, 1 + strlen(pch_nz));
       }
       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 =
                                   eallocarray(
                                       (namelist.ni_namelist_len + 1),
                                       sizeof(char*));
                               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.
*/
static int
getnetnum(
       const char *num,
       sockaddr_u *addr,
       int complain,           /* ignored */
       enum gnn_type a_type    /* ignored */
       )
{
       REQUIRE(AF_UNSPEC == AF(addr) ||
               AF_INET == AF(addr) ||
               AF_INET6 == AF(addr));

       if (!is_ip_address(num, AF(addr), addr)) {
               return 0;
       }
# ifdef ISC_PLATFORM_HAVESALEN
       addr->sa.sa_len = SIZEOF_SOCKADDR(AF(addr));
# endif
       SET_PORT(addr, NTP_PORT);

       if (IS_IPV6(addr) && !ipv6_works) {
               return -1;
       } else {
               return 1;
       }
}


#if defined(HAVE_SETRLIMIT)
void
ntp_rlimit(
       int     rl_what,
       rlim_t  rl_value,
       int     rl_scale,
       const char *    rl_sstr
       )
{
       struct rlimit   rl;

       switch (rl_what) {
# ifdef RLIMIT_MEMLOCK
           case RLIMIT_MEMLOCK:
               if (HAVE_OPT( SAVECONFIGQUIT )) {
                       break;
               }
               /*
                * 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(RLIMIT_STACK) 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;
                       }
                       rl.rlim_cur = rl_value;
                       if (-1 == setrlimit(RLIMIT_STACK, &rl)) {
                               msyslog(LOG_DEBUG,
                                       "ntp_rlimit: Cannot set RLIMIT_STACK: %m");
                       }
               }
               break;
# endif /* RLIMIT_STACK */

           default:
                   fatal_error("ntp_rlimit: unexpected RLIMIT case: %d", rl_what);
       }
}
#endif  /* HAVE_SETRLIMIT */