/* ntp_config.c
 *
 * This file contains the ntpd configuration code.
 *
 * Written By:	Sachin Kamboj
 *		University of Delaware
 *		Newark, DE 19711
 * Some parts borrowed from the older ntp_config.c
 * Copyright (c) 2006
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef HAVE_NETINFO
# include <netinfo/ni.h>
#endif

#include <stdio.h>
#include <ctype.h>
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#include <signal.h>
#ifndef SIGCHLD
# define SIGCHLD SIGCLD
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif

#include <isc/net.h>
#include <isc/result.h>

#include "ntp.h"
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_unixtime.h"
#include "ntp_refclock.h"
#include "ntp_filegen.h"
#include "ntp_stdlib.h"
#include "lib_strbuf.h"
#include "ntp_assert.h"
#include "ntp_random.h"
/*
 * [Bug 467]: Some linux headers collide with CONFIG_PHONE and CONFIG_KEYS
 * so #include these later.
 */
#include "ntp_config.h"
#include "ntp_cmdargs.h"
#include "ntp_scanner.h"
#include "ntp_parser.h"
#include "ntpd-opts.h"

extern int yyparse(void);

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

/* 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;
} peer_resolved_ctx;

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

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

#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 */
struct config_tree_tag *cfg_tree_history;	/* History of configs */
char	*sys_phone[MAXPHONE] = {NULL};	/* ACTS phone numbers */
char	default_keysdir[] = NTP_KEYSDIR;
char	*keysdir = default_keysdir;	/* crypto keys directory */
char *	saveconfigdir;
#if defined(HAVE_SCHED_SETSCHEDULER)
int	config_priority_override = 0;
int	config_priority;
#endif

const char *config_file;
static char default_ntp_signd_socket[] =
#ifdef NTP_SIGND_PATH
					NTP_SIGND_PATH;
#else
					"";
#endif
char *ntp_signd_socket = default_ntp_signd_socket;
#ifdef HAVE_NETINFO
struct netinfo_config_state *config_netinfo = NULL;
int check_netinfo = 1;
#endif /* HAVE_NETINFO */
#ifdef SYS_WINNT
char *alt_config_file;
LPTSTR temp;
char config_file_storage[MAX_PATH];
char alt_config_file_storage[MAX_PATH];
#endif /* SYS_WINNT */

#ifdef HAVE_NETINFO
/*
 * NetInfo configuration state
 */
struct netinfo_config_state {
	void *domain;		/* domain with config */
	ni_id config_dir;	/* ID config dir      */
	int prop_index;		/* current property   */
	int val_index;		/* current value      */
	char **val_list;	/* value list         */
};
#endif

struct REMOTE_CONFIG_INFO remote_config;  /* Remote configuration buffer and
					     pointer info */
int old_config_style = 1;    /* A boolean flag, which when set,
			      * indicates that the old configuration
			      * format with a newline at the end of
			      * every command is being used
			      */
int	cryptosw;		/* crypto command called */

extern char *stats_drift_file;	/* name of the driftfile */

#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED
/*
 * backwards compatibility flags
 */
bc_entry bc_list[] = {
	{ T_Bc_bugXXXX,		1	}	/* default enabled */
};

/*
 * declare an int pointer for each flag for quick testing without
 * walking bc_list.  If the pointer is consumed by libntp rather
 * than ntpd, declare it in a libntp source file pointing to storage
 * initialized with the appropriate value for other libntp clients, and
 * redirect it to point into bc_list during ntpd startup.
 */
int *p_bcXXXX_enabled = &bc_list[0].enabled;
#endif

/* FUNCTION PROTOTYPES */

static void init_syntax_tree(config_tree *);
static void apply_enable_disable(attr_val_fifo *q, int enable);

#ifdef FREE_CFG_T
static void free_auth_node(config_tree *);
static void free_all_config_trees(void);

static void free_config_access(config_tree *);
static void free_config_auth(config_tree *);
static void free_config_fudge(config_tree *);
static void free_config_logconfig(config_tree *);
static void free_config_monitor(config_tree *);
static void free_config_nic_rules(config_tree *);
static void free_config_other_modes(config_tree *);
static void free_config_peers(config_tree *);
static void free_config_phone(config_tree *);
static void free_config_reset_counters(config_tree *);
static void free_config_rlimit(config_tree *);
static void free_config_setvar(config_tree *);
static void free_config_system_opts(config_tree *);
static void free_config_tinker(config_tree *);
static void free_config_tos(config_tree *);
static void free_config_trap(config_tree *);
static void free_config_ttl(config_tree *);
static void free_config_unpeers(config_tree *);
static void free_config_vars(config_tree *);

#ifdef SIM
static void free_config_sim(config_tree *);
#endif
static void destroy_address_fifo(address_fifo *);
#define FREE_ADDRESS_FIFO(pf)			\
	do {					\
		destroy_address_fifo(pf);	\
		(pf) = NULL;			\
	} while (0)
       void free_all_config_trees(void);	/* atexit() */
static void free_config_tree(config_tree *ptree);
#endif	/* FREE_CFG_T */

static void destroy_restrict_node(restrict_node *my_node);
static int is_sane_resolved_address(sockaddr_u *peeraddr, int hmode);
static void save_and_apply_config_tree(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 config_access(config_tree *);
static void config_mdnstries(config_tree *);
static void config_phone(config_tree *);
static void config_setvar(config_tree *);
static void config_ttl(config_tree *);
static void config_trap(config_tree *);
static void config_fudge(config_tree *);
static void config_peers(config_tree *);
static void config_unpeers(config_tree *);
static void config_nic_rules(config_tree *, 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 *);
#endif	/* !SIM */

#ifdef WORKER
static void peer_name_resolved(int, int, void *, const char *, const char *,
			const struct addrinfo *,
			const struct addrinfo *);
static void unpeer_name_resolved(int, int, void *, const char *, const char *,
			  const struct addrinfo *,
			  const struct addrinfo *);
static void trap_name_resolved(int, int, void *, const char *, const char *,
			const struct addrinfo *,
			const struct addrinfo *);
#endif

enum gnn_type {
	t_UNK,		/* Unknown */
	t_REF,		/* Refclock */
	t_MSK		/* Network Mask */
};

static void ntpd_set_tod_using(const char *);
static char * normal_dtoa(double);
static u_int32 get_pfxmatch(const char **, struct masks *);
static u_int32 get_match(const char *, struct masks *);
static u_int32 get_logmask(const char *);
#ifndef SIM
static int getnetnum(const char *num, sockaddr_u *addr, int complain,
		     enum gnn_type a_type);

#endif


/* FUNCTIONS FOR INITIALIZATION
 * ----------------------------
 */

#ifdef FREE_CFG_T
static void
free_auth_node(
	config_tree *ptree
	)
{
	if (ptree->auth.keys) {
		free(ptree->auth.keys);
		ptree->auth.keys = NULL;
	}

	if (ptree->auth.keysdir) {
		free(ptree->auth.keysdir);
		ptree->auth.keysdir = NULL;
	}

	if (ptree->auth.ntp_signd_socket) {
		free(ptree->auth.ntp_signd_socket);
		ptree->auth.ntp_signd_socket = NULL;
	}
}
#endif /* DEBUG */


static void
init_syntax_tree(
	config_tree *ptree
	)
{
	ZERO(*ptree);
	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_vars(ptree);
	free_config_peers(ptree);
	free_config_unpeers(ptree);
	free_config_nic_rules(ptree);
	free_config_reset_counters(ptree);
#ifdef SIM
	free_config_sim(ptree);
#endif
	free_auth_node(ptree);

	free(ptree);

#if defined(_MSC_VER) && defined (_DEBUG)
	_CrtCheckMemory();
#endif
}
#endif /* FREE_CFG_T */


#ifdef SAVECONFIG
/* Dump all trees */
int
dump_all_config_trees(
	FILE *df,
	int comment
	)
{
	config_tree *	cfg_ptr;
	int		return_value;

	return_value = 0;
	for (cfg_ptr = cfg_tree_history;
	     cfg_ptr != NULL;
	     cfg_ptr = cfg_ptr->link)
		return_value |= dump_config_tree(cfg_ptr, df, comment);

	return return_value;
}


/* The config dumper */
int
dump_config_tree(
	config_tree *ptree,
	FILE *df,
	int comment
	)
{
	peer_node *peern;
	unpeer_node *unpeern;
	attr_val *atrv;
	address_node *addr;
	address_node *peer_addr;
	address_node *fudge_addr;
	filegen_node *fgen_node;
	restrict_node *rest_node;
	addr_opts_node *addr_opts;
	setvar_node *setv_node;
	nic_rule_node *rule_node;
	int_node *i_n;
	int_node *flags;
	int_node *counter_set;
	string_node *str_node;

	const char *s = 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 I didn't find documentation I'll just output its name and the cor. value */
	atrv = HEAD_PFIFO(ptree->vars);
	for ( ; atrv != NULL; atrv = atrv->link) {
		switch (atrv->type) {
#ifdef DEBUG
		default:
			fprintf(df, "\n# dump error:\n"
				"# unknown vars type %d (%s) for %s\n",
				atrv->type, token_name(atrv->type),
				token_name(atrv->attr));
			break;
#endif
		case T_Double:
			fprintf(df, "%s %s\n", keyword(atrv->attr),
				normal_dtoa(atrv->value.d));
			break;

		case T_Integer:
			fprintf(df, "%s %d\n", keyword(atrv->attr),
				atrv->value.i);
			break;

		case T_String:
			fprintf(df, "%s \"%s\"", keyword(atrv->attr),
				atrv->value.s);
			if (T_Driftfile == atrv->attr &&
			    atrv->link != NULL &&
			    T_WanderThreshold == atrv->link->attr) {
				atrv = atrv->link;
				fprintf(df, " %s\n",
					normal_dtoa(atrv->value.d));
			} else {
				fprintf(df, "\n");
			}
			break;
		}
	}

	atrv = HEAD_PFIFO(ptree->logconfig);
	if (atrv != NULL) {
		fprintf(df, "logconfig");
		for ( ; atrv != NULL; atrv = atrv->link)
			fprintf(df, " %c%s", atrv->attr, atrv->value.s);
		fprintf(df, "\n");
	}

	if (ptree->stats_dir)
		fprintf(df, "statsdir \"%s\"\n", ptree->stats_dir);

	i_n = HEAD_PFIFO(ptree->stats_list);
	if (i_n != NULL) {
		fprintf(df, "statistics");
		for ( ; i_n != NULL; i_n = i_n->link)
			fprintf(df, " %s", keyword(i_n->i));
		fprintf(df, "\n");
	}

	fgen_node = HEAD_PFIFO(ptree->filegen_opts);
	for ( ; fgen_node != NULL; fgen_node = fgen_node->link) {
		atrv = HEAD_PFIFO(fgen_node->options);
		if (atrv != NULL) {
			fprintf(df, "filegen %s",
				keyword(fgen_node->filegen_token));
			for ( ; atrv != NULL; atrv = atrv->link) {
				switch (atrv->attr) {
#ifdef DEBUG
				default:
					fprintf(df, "\n# dump error:\n"
						"# unknown filegen option token %s\n"
						"filegen %s",
						token_name(atrv->attr),
						keyword(fgen_node->filegen_token));
					break;
#endif
				case T_File:
					fprintf(df, " file %s",
						atrv->value.s);
					break;

				case T_Type:
					fprintf(df, " type %s",
						keyword(atrv->value.i));
					break;

				case T_Flag:
					fprintf(df, " %s",
						keyword(atrv->value.i));
					break;
				}
			}
			fprintf(df, "\n");
		}
	}

	atrv = HEAD_PFIFO(ptree->auth.crypto_cmd_list);
	if (atrv != NULL) {
		fprintf(df, "crypto");
		for ( ; atrv != NULL; atrv = atrv->link) {
			fprintf(df, " %s %s", keyword(atrv->attr),
				atrv->value.s);
		}
		fprintf(df, "\n");
	}

	if (ptree->auth.revoke != 0)
		fprintf(df, "revoke %d\n", ptree->auth.revoke);

	if (ptree->auth.keysdir != NULL)
		fprintf(df, "keysdir \"%s\"\n", ptree->auth.keysdir);

	if (ptree->auth.keys != NULL)
		fprintf(df, "keys \"%s\"\n", ptree->auth.keys);

	atrv = HEAD_PFIFO(ptree->auth.trusted_key_list);
	if (atrv != NULL) {
		fprintf(df, "trustedkey");
		for ( ; atrv != NULL; atrv = atrv->link) {
			if (T_Integer == atrv->type)
				fprintf(df, " %d", atrv->value.i);
			else if (T_Intrange == atrv->type)
				fprintf(df, " (%d ... %d)",
					atrv->value.r.first,
					atrv->value.r.last);
#ifdef DEBUG
			else
				fprintf(df, "\n# dump error:\n"
					"# unknown trustedkey attr type %d\n"
					"trustedkey", atrv->type);
#endif
		}
		fprintf(df, "\n");
	}

	if (ptree->auth.control_key)
		fprintf(df, "controlkey %d\n", ptree->auth.control_key);

	if (ptree->auth.request_key)
		fprintf(df, "requestkey %d\n", ptree->auth.request_key);

	/* dump enable list, then disable list */
	for (enable = 1; enable >= 0; enable--) {
		atrv = (enable)
			   ? HEAD_PFIFO(ptree->enable_opts)
			   : HEAD_PFIFO(ptree->disable_opts);
		if (atrv != NULL) {
			fprintf(df, "%s", (enable)
					? "enable"
					: "disable");
			for ( ; atrv != NULL; atrv = atrv->link)
				fprintf(df, " %s",
					keyword(atrv->value.i));
			fprintf(df, "\n");
		}
	}

	atrv = HEAD_PFIFO(ptree->orphan_cmds);
	if (atrv != NULL) {
		fprintf(df, "tos");
		for ( ; atrv != NULL; atrv = atrv->link) {
			switch (atrv->type) {
#ifdef DEBUG
			default:
				fprintf(df, "\n# dump error:\n"
					"# unknown tos attr type %d %s\n"
					"tos", atrv->type,
					token_name(atrv->type));
				break;
#endif
			case T_Double:
				fprintf(df, " %s %s",
					keyword(atrv->attr),
					normal_dtoa(atrv->value.d));
				break;
			}
		}
		fprintf(df, "\n");
	}

	atrv = HEAD_PFIFO(ptree->rlimit);
	if (atrv != NULL) {
		fprintf(df, "rlimit");
		for ( ; atrv != NULL; atrv = atrv->link) {
			INSIST(T_Integer == atrv->type);
			fprintf(df, " %s %d", keyword(atrv->attr),
				atrv->value.i);
		}
		fprintf(df, "\n");
	}

	atrv = HEAD_PFIFO(ptree->tinker);
	if (atrv != NULL) {
		fprintf(df, "tinker");
		for ( ; atrv != NULL; atrv = atrv->link) {
			INSIST(T_Double == atrv->type);
			fprintf(df, " %s %s", keyword(atrv->attr),
				normal_dtoa(atrv->value.d));
		}
		fprintf(df, "\n");
	}

	if (ptree->broadcastclient)
		fprintf(df, "broadcastclient\n");

	peern = HEAD_PFIFO(ptree->peers);
	for ( ; peern != NULL; peern = peern->link) {
		addr = peern->addr;
		fprintf(df, "%s", keyword(peern->host_mode));
		switch (addr->type) {
#ifdef DEBUG
		default:
			fprintf(df, "# dump error:\n"
				"# unknown peer family %d for:\n"
				"%s", addr->type,
				keyword(peern->host_mode));
			break;
#endif
		case AF_UNSPEC:
			break;

		case AF_INET:
			fprintf(df, " -4");
			break;

		case AF_INET6:
			fprintf(df, " -6");
			break;
		}
		fprintf(df, " %s", addr->address);

		if (peern->minpoll != 0)
			fprintf(df, " minpoll %u", peern->minpoll);

		if (peern->maxpoll != 0)
			fprintf(df, " maxpoll %u", peern->maxpoll);

		if (peern->ttl != 0) {
			if (strlen(addr->address) > 8
			    && !memcmp(addr->address, "127.127.", 8))
				fprintf(df, " mode %u", peern->ttl);
			else
				fprintf(df, " ttl %u", peern->ttl);
		}

		if (peern->peerversion != NTP_VERSION)
			fprintf(df, " version %u", peern->peerversion);

		if (peern->peerkey != 0)
			fprintf(df, " key %u", peern->peerkey);

		if (peern->group != NULL)
			fprintf(df, " ident \"%s\"", peern->group);

		atrv = HEAD_PFIFO(peern->peerflags);
		for ( ; atrv != NULL; atrv = atrv->link) {
			INSIST(T_Flag == atrv->attr);
			INSIST(T_Integer == atrv->type);
			fprintf(df, " %s", keyword(atrv->value.i));
		}

		fprintf(df, "\n");

		addr_opts = HEAD_PFIFO(ptree->fudge);
		for ( ; addr_opts != NULL; addr_opts = addr_opts->link) {
			peer_addr = peern->addr;
			fudge_addr = addr_opts->addr;

			s1 = peer_addr->address;
			s2 = fudge_addr->address;

			if (strcmp(s1, s2))
				continue;

			fprintf(df, "fudge %s", s1);

			for (atrv = HEAD_PFIFO(addr_opts->options);
			     atrv != NULL;
			     atrv = atrv->link) {

				switch (atrv->type) {
#ifdef DEBUG
				default:
					fprintf(df, "\n# dump error:\n"
						"# unknown fudge atrv->type %d\n"
						"fudge %s", atrv->type,
						s1);
					break;
#endif
				case T_Double:
					fprintf(df, " %s %s",
						keyword(atrv->attr),
						normal_dtoa(atrv->value.d));
					break;

				case T_Integer:
					fprintf(df, " %s %d",
						keyword(atrv->attr),
						atrv->value.i);
					break;

				case T_String:
					fprintf(df, " %s %s",
						keyword(atrv->attr),
						atrv->value.s);
					break;
				}
			}
			fprintf(df, "\n");
		}
	}

	addr = HEAD_PFIFO(ptree->manycastserver);
	if (addr != NULL) {
		fprintf(df, "manycastserver");
		for ( ; addr != NULL; addr = addr->link)
			fprintf(df, " %s", addr->address);
		fprintf(df, "\n");
	}

	addr = HEAD_PFIFO(ptree->multicastclient);
	if (addr != NULL) {
		fprintf(df, "multicastclient");
		for ( ; addr != NULL; addr = addr->link)
			fprintf(df, " %s", addr->address);
		fprintf(df, "\n");
	}


	for (unpeern = HEAD_PFIFO(ptree->unpeers);
	     unpeern != NULL;
	     unpeern = unpeern->link)
		fprintf(df, "unpeer %s\n", unpeern->addr->address);

	atrv = HEAD_PFIFO(ptree->mru_opts);
	if (atrv != NULL) {
		fprintf(df, "mru");
		for ( ;	atrv != NULL; atrv = atrv->link)
			fprintf(df, " %s %d", keyword(atrv->attr),
				atrv->value.i);
		fprintf(df, "\n");
	}

	atrv = HEAD_PFIFO(ptree->discard_opts);
	if (atrv != NULL) {
		fprintf(df, "discard");
		for ( ;	atrv != NULL; atrv = atrv->link)
			fprintf(df, " %s %d", keyword(atrv->attr),
				atrv->value.i);
		fprintf(df, "\n");
	}


	for (rest_node = HEAD_PFIFO(ptree->restrict_opts);
	     rest_node != NULL;
	     rest_node = rest_node->link) {

		if (NULL == rest_node->addr) {
			s = "default";
			flags = HEAD_PFIFO(rest_node->flags);
			for ( ; flags != NULL; flags = flags->link)
				if (T_Source == flags->i) {
					s = "source";
					break;
				}
		} else {
			s = rest_node->addr->address;
		}
		fprintf(df, "restrict %s", s);
		if (rest_node->mask != NULL)
			fprintf(df, " mask %s",
				rest_node->mask->address);
		flags = HEAD_PFIFO(rest_node->flags);
		for ( ; flags != NULL; flags = flags->link)
			if (T_Source != flags->i)
				fprintf(df, " %s", keyword(flags->i));
		fprintf(df, "\n");
	}

	rule_node = HEAD_PFIFO(ptree->nic_rules);
	for ( ; rule_node != NULL; rule_node = rule_node->link) {
		fprintf(df, "interface %s %s\n",
			keyword(rule_node->action),
			(rule_node->match_class)
			    ? keyword(rule_node->match_class)
			    : rule_node->if_name);
	}

	str_node = HEAD_PFIFO(ptree->phone);
	if (str_node != NULL) {
		fprintf(df, "phone");
		for ( ; str_node != NULL; str_node = str_node->link)
			fprintf(df, " \"%s\"", str_node->s);
		fprintf(df, "\n");
	}

	setv_node = HEAD_PFIFO(ptree->setvar);
	for ( ; setv_node != NULL; setv_node = setv_node->link) {
		s1 = quote_if_needed(setv_node->var);
		s2 = quote_if_needed(setv_node->val);
		fprintf(df, "setvar %s = %s", s1, s2);
		free(s1);
		free(s2);
		if (setv_node->isdefault)
			fprintf(df, " default");
		fprintf(df, "\n");
	}

	i_n = HEAD_PFIFO(ptree->ttl);
	if (i_n != NULL) {
		fprintf(df, "ttl");
		for( ; i_n != NULL; i_n = i_n->link)
			fprintf(df, " %d", i_n->i);
		fprintf(df, "\n");
	}

	addr_opts = HEAD_PFIFO(ptree->trap);
	for ( ; addr_opts != NULL; addr_opts = addr_opts->link) {
		addr = addr_opts->addr;
		fprintf(df, "trap %s", addr->address);
		atrv = HEAD_PFIFO(addr_opts->options);
		for ( ; atrv != NULL; atrv = atrv->link) {
			switch (atrv->attr) {
#ifdef DEBUG
			default:
				fprintf(df, "\n# dump error:\n"
					"# unknown trap token %d\n"
					"trap %s", atrv->attr,
					addr->address);
				break;
#endif
			case T_Port:
				fprintf(df, " port %d", atrv->value.i);
				break;

			case T_Interface:
				fprintf(df, " interface %s",
					atrv->value.s);
				break;
			}
		}
		fprintf(df, "\n");
	}

	counter_set = HEAD_PFIFO(ptree->reset_counters);
	if (counter_set != NULL) {
		fprintf(df, "reset");
		for ( ; counter_set != NULL;
		     counter_set = counter_set->link)
			fprintf(df, " %s", keyword(counter_set->i));
		fprintf(df, "\n");
	}

	return 0;
}
#endif	/* SAVECONFIG */



/* generic fifo routines for structs linked by 1st member */
void *
append_gen_fifo(
	void *fifo,
	void *entry
	)
{
	gen_fifo *pf;
	gen_node *pe;

	pf = fifo;
	pe = entry;
	if (NULL == pf)
		pf = emalloc_zero(sizeof(*pf));
	else
		CHECK_FIFO_CONSISTENCY(*pf);
	if (pe != NULL)
		LINK_FIFO(*pf, pe, link);
	CHECK_FIFO_CONSISTENCY(*pf);

	return pf;
}


void *
concat_gen_fifos(
	void *first,
	void *second
	)
{
	gen_fifo *pf1;
	gen_fifo *pf2;

	pf1 = first;
	pf2 = second;
	if (NULL == pf1)
		return pf2;
	if (NULL == pf2)
		return pf1;

	CONCAT_FIFO(*pf1, *pf2, link);
	free(pf2);

	return pf1;
}


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

attr_val *
create_attr_dval(
	int attr,
	double value
	)
{
	attr_val *my_val;

	my_val = emalloc_zero(sizeof(*my_val));
	my_val->attr = attr;
	my_val->value.d = value;
	my_val->type = T_Double;

	return my_val;
}


attr_val *
create_attr_ival(
	int attr,
	int value
	)
{
	attr_val *my_val;

	my_val = emalloc_zero(sizeof(*my_val));
	my_val->attr = attr;
	my_val->value.i = value;
	my_val->type = T_Integer;

	return my_val;
}


attr_val *
create_attr_uval(
	int	attr,
	u_int	value
	)
{
	attr_val *my_val;

	my_val = emalloc_zero(sizeof(*my_val));
	my_val->attr = attr;
	my_val->value.u = value;
	my_val->type = T_U_int;

	return my_val;
}


attr_val *
create_attr_rangeval(
	int	attr,
	int	first,
	int	last
	)
{
	attr_val *my_val;

	my_val = emalloc_zero(sizeof(*my_val));
	my_val->attr = attr;
	my_val->value.r.first = first;
	my_val->value.r.last = last;
	my_val->type = T_Intrange;

	return my_val;
}


attr_val *
create_attr_sval(
	int attr,
	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 (option->value.u >= MAX_TTL) {
				msyslog(LOG_ERR, "ttl: invalid argument");
				errflag = 1;
			} else {
				my_node->ttl = (u_char)option->value.u;
			}
			break;

		case T_Mode:
			my_node->ttl = option->value.u;
			break;

		case T_Key:
			if (option->value.u >= KEYID_T_MAX) {
				msyslog(LOG_ERR, "key: invalid argument");
				errflag = 1;
			} else {
				my_node->peerkey =
					(keyid_t)option->value.u;
			}
			break;

		case T_Version:
			if (option->value.u >= UCHAR_MAX) {
				msyslog(LOG_ERR, "version: invalid argument");
				errflag = 1;
			} else {
				my_node->peerversion =
					(u_char)option->value.u;
			}
			break;

		case T_Ident:
			my_node->group = option->value.s;
			break;

		default:
			msyslog(LOG_ERR,
				"Unknown peer/server option token %s",
				token_name(option->attr));
			errflag = 1;
		}
		if (freenode)
			free(option);
	}

	/* Check if errors were reported. If yes, ignore the node */
	if (errflag) {
		free(my_node);
		my_node = NULL;
	}

	return my_node;
}


unpeer_node *
create_unpeer_node(
	address_node *addr
	)
{
	unpeer_node *	my_node;
	u_int		u;
	char *		pch;

	my_node = emalloc_zero(sizeof(*my_node));

	/*
	 * From the parser's perspective an association ID fits into
	 * its generic T_String definition of a name/address "address".
	 * We treat all valid 16-bit numbers as association IDs.
	 */
	pch = addr->address;
	while (*pch && isdigit((unsigned char)*pch))
		pch++;

	if (!*pch
	    && 1 == sscanf(addr->address, "%u", &u)
	    && u <= ASSOCID_MAX) {
		my_node->assocID = (associd_t)u;
		destroy_address_node(addr);
		my_node->addr = NULL;
	} else {
		my_node->assocID = 0;
		my_node->addr = addr;
	}

	return my_node;
}

filegen_node *
create_filegen_node(
	int		filegen_token,
	attr_val_fifo *	options
	)
{
	filegen_node *my_node;

	my_node = emalloc_zero(sizeof(*my_node));
	my_node->filegen_token = filegen_token;
	my_node->options = options;

	return my_node;
}


restrict_node *
create_restrict_node(
	address_node *	addr,
	address_node *	mask,
	int_fifo *	flags,
	int		line_no
	)
{
	restrict_node *my_node;

	my_node = emalloc_zero(sizeof(*my_node));
	my_node->addr = addr;
	my_node->mask = mask;
	my_node->flags = flags;
	my_node->line_no = line_no;

	return my_node;
}


static void
destroy_restrict_node(
	restrict_node *my_node
	)
{
	/* With great care, free all the memory occupied by
	 * the restrict node
	 */
	destroy_address_node(my_node->addr);
	destroy_address_node(my_node->mask);
	destroy_int_fifo(my_node->flags);
	free(my_node);
}


static void
destroy_int_fifo(
	int_fifo *	fifo
	)
{
	int_node *	i_n;

	if (fifo != NULL) {
		for (;;) {
			UNLINK_FIFO(i_n, *fifo, link);
			if (i_n == NULL)
				break;
			free(i_n);
		}
		free(fifo);
	}
}


static void
destroy_string_fifo(
	string_fifo *	fifo
	)
{
	string_node *	sn;

	if (fifo != NULL) {
		for (;;) {
			UNLINK_FIFO(sn, *fifo, link);
			if (sn == NULL)
				break;
			free(sn->s);
			free(sn);
		}
		free(fifo);
	}
}


static void
destroy_attr_val_fifo(
	attr_val_fifo *	av_fifo
	)
{
	attr_val *	av;

	if (av_fifo != NULL) {
		for (;;) {
			UNLINK_FIFO(av, *av_fifo, link);
			if (av == NULL)
				break;
			if (T_String == av->type)
				free(av->value.s);
			free(av);
		}
		free(av_fifo);
	}
}


static void
destroy_filegen_fifo(
	filegen_fifo *	fifo
	)
{
	filegen_node *	fg;

	if (fifo != NULL) {
		for (;;) {
			UNLINK_FIFO(fg, *fifo, link);
			if (fg == NULL)
				break;
			destroy_attr_val_fifo(fg->options);
			free(fg);
		}
		free(fifo);
	}
}


static void
destroy_restrict_fifo(
	restrict_fifo *	fifo
	)
{
	restrict_node *	rn;

	if (fifo != NULL) {
		for (;;) {
			UNLINK_FIFO(rn, *fifo, link);
			if (rn == NULL)
				break;
			destroy_restrict_node(rn);
		}
		free(fifo);
	}
}


static void
destroy_setvar_fifo(
	setvar_fifo *	fifo
	)
{
	setvar_node *	sv;

	if (fifo != NULL) {
		for (;;) {
			UNLINK_FIFO(sv, *fifo, link);
			if (sv == NULL)
				break;
			free(sv->var);
			free(sv->val);
			free(sv);
		}
		free(fifo);
	}
}


static void
destroy_addr_opts_fifo(
	addr_opts_fifo *	fifo
	)
{
	addr_opts_node *	aon;

	if (fifo != NULL) {
		for (;;) {
			UNLINK_FIFO(aon, *fifo, link);
			if (aon == NULL)
				break;
			destroy_address_node(aon->addr);
			destroy_attr_val_fifo(aon->options);
			free(aon);
		}
		free(fifo);
	}
}


setvar_node *
create_setvar_node(
	char *	var,
	char *	val,
	int	isdefault
	)
{
	setvar_node *	my_node;
	char *		pch;

	/* do not allow = in the variable name */
	pch = strchr(var, '=');
	if (NULL != pch)
		*pch = '\0';

	/* Now store the string into a setvar_node */
	my_node = emalloc_zero(sizeof(*my_node));
	my_node->var = var;
	my_node->val = val;
	my_node->isdefault = isdefault;

	return my_node;
}


nic_rule_node *
create_nic_rule_node(
	int match_class,
	char *if_name,	/* interface name or numeric address */
	int action
	)
{
	nic_rule_node *my_node;

	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
# ifdef __GNUC__
	item = -1;	/* quiet warning */
# endif
	my_val = HEAD_PFIFO(ptree->auth.crypto_cmd_list);
	for (; my_val != NULL; my_val = my_val->link) {
		switch (my_val->attr) {

		default:
			INSIST(0);
			break;

		case T_Host:
			item = CRYPTO_CONF_PRIV;
			break;

		case T_Ident:
			item = CRYPTO_CONF_IDENT;
			break;

		case T_Pw:
			item = CRYPTO_CONF_PW;
			break;

		case T_Randfile:
			item = CRYPTO_CONF_RAND;
			break;

		case T_Digest:
			item = CRYPTO_CONF_NID;
			break;
		}
		crypto_config(item, my_val->value.s);
	}
#endif	/* AUTOKEY */

	/* Keysdir Command */
	if (ptree->auth.keysdir) {
		if (keysdir != default_keysdir)
			free(keysdir);
		keysdir = estrdup(ptree->auth.keysdir);
	}


	/* ntp_signd_socket Command */
	if (ptree->auth.ntp_signd_socket) {
		if (ntp_signd_socket != default_ntp_signd_socket)
			free(ntp_signd_socket);
		ntp_signd_socket = estrdup(ptree->auth.ntp_signd_socket);
	}

#ifdef AUTOKEY
	if (ptree->auth.cryptosw && !cryptosw) {
		crypto_setup();
		cryptosw = 1;
	}
#endif	/* AUTOKEY */

	/*
	 * Count the number of trusted keys to preallocate storage and
	 * size the hash table.
	 */
	count = 0;
	my_val = HEAD_PFIFO(ptree->auth.trusted_key_list);
	for (; my_val != NULL; my_val = my_val->link) {
		if (T_Integer == my_val->type) {
			first = my_val->value.i;
			if (first > 1 && first <= NTP_MAXKEY)
				count++;
		} else {
			REQUIRE(T_Intrange == my_val->type);
			first = my_val->value.r.first;
			last = my_val->value.r.last;
			if (!(first > last || first < 1 ||
			    last > NTP_MAXKEY)) {
				count += 1 + last - first;
			}
		}
	}
	auth_prealloc_symkeys(count);

	/* Keys Command */
	if (ptree->auth.keys)
		getauthkeys(ptree->auth.keys);

	/* Control Key Command */
	if (ptree->auth.control_key)
		ctl_auth_keyid = (keyid_t)ptree->auth.control_key;

	/* Requested Key Command */
	if (ptree->auth.request_key) {
		DPRINTF(4, ("set info_auth_keyid to %08lx\n",
			    (u_long) ptree->auth.request_key));
		info_auth_keyid = (keyid_t)ptree->auth.request_key;
	}

	/* Trusted Key Command */
	my_val = HEAD_PFIFO(ptree->auth.trusted_key_list);
	for (; my_val != NULL; my_val = my_val->link) {
		if (T_Integer == my_val->type) {
			first = my_val->value.i;
			if (first >= 1 && first <= NTP_MAXKEY) {
				authtrust(first, TRUE);
			} else {
				msyslog(LOG_NOTICE,
					"Ignoring invalid trustedkey %d, min 1 max %d.",
					first, NTP_MAXKEY);
			}
		} else {
			first = my_val->value.r.first;
			last = my_val->value.r.last;
			if (first > last || first < 1 ||
			    last > NTP_MAXKEY) {
				msyslog(LOG_NOTICE,
					"Ignoring invalid trustedkey range %d ... %d, min 1 max %d.",
					first, last, NTP_MAXKEY);
			} else {
				for (i = first; i <= last; i++) {
					authtrust(i, TRUE);
				}
			}
		}
	}

#ifdef AUTOKEY
	/* crypto revoke command */
	if (ptree->auth.revoke)
		sys_revoke = 1UL << ptree->auth.revoke;
#endif	/* AUTOKEY */
}
#endif	/* !SIM */


#ifdef FREE_CFG_T
static void
free_config_auth(
	config_tree *ptree
	)
{
	destroy_attr_val_fifo(ptree->auth.crypto_cmd_list);
	ptree->auth.crypto_cmd_list = NULL;
	destroy_attr_val_fifo(ptree->auth.trusted_key_list);
	ptree->auth.trusted_key_list = NULL;
}
#endif	/* FREE_CFG_T */


static void
config_tos(
	config_tree *ptree
	)
{
	attr_val *	tos;
	int		item;
	double		val;

#ifdef __GNUC__
	item = -1;	/* quiet warning */
#endif
	tos = HEAD_PFIFO(ptree->orphan_cmds);
	for (; tos != NULL; tos = tos->link) {
		val = tos->value.d;
		switch(tos->attr) {

		default:
			INSIST(0);
			break;

		case T_Ceiling:
			if (val > STRATUM_UNSPEC - 1) {
				msyslog(LOG_WARNING,
					"Using maximum tos ceiling %d, %g requested",
					STRATUM_UNSPEC - 1, val);
				val = STRATUM_UNSPEC - 1;
			}
			item = PROTO_CEILING;
			break;

		case T_Floor:
			item = PROTO_FLOOR;
			break;

		case T_Cohort:
			item = PROTO_COHORT;
			break;

		case T_Orphan:
			item = PROTO_ORPHAN;
			break;

		case T_Orphanwait:
			item = PROTO_ORPHWAIT;
			break;

		case T_Mindist:
			item = PROTO_MINDISP;
			break;

		case T_Maxdist:
			item = PROTO_MAXDIST;
			break;

		case T_Minclock:
			item = PROTO_MINCLOCK;
			break;

		case T_Maxclock:
			item = PROTO_MAXCLOCK;
			break;

		case T_Minsane:
			item = PROTO_MINSANE;
			break;

		case T_Beacon:
			item = PROTO_BEACON;
			break;
		}
		proto_config(item, 0, val, NULL);
	}
}


#ifdef FREE_CFG_T
static void
free_config_tos(
	config_tree *ptree
	)
{
	FREE_ATTR_VAL_FIFO(ptree->orphan_cmds);
}
#endif	/* FREE_CFG_T */


static void
config_monitor(
	config_tree *ptree
	)
{
	int_node *pfilegen_token;
	const char *filegen_string;
	const char *filegen_file;
	FILEGEN *filegen;
	filegen_node *my_node;
	attr_val *my_opts;
	int filegen_type;
	int filegen_flag;

	/* Set the statistics directory */
	if (ptree->stats_dir)
		stats_config(STATS_STATSDIR, ptree->stats_dir);

	/* NOTE:
	 * Calling filegen_get is brain dead. Doing a string
	 * comparison to find the relavant filegen structure is
	 * expensive.
	 *
	 * Through the parser, we already know which filegen is
	 * being specified. Hence, we should either store a
	 * pointer to the specified structure in the syntax tree
	 * or an index into a filegen array.
	 *
	 * Need to change the filegen code to reflect the above.
	 */

	/* Turn on the specified statistics */
	pfilegen_token = HEAD_PFIFO(ptree->stats_list);
	for (; pfilegen_token != NULL; pfilegen_token = pfilegen_token->link) {
		filegen_string = keyword(pfilegen_token->i);
		filegen = filegen_get(filegen_string);
		if (NULL == filegen) {
			msyslog(LOG_ERR,
				"stats %s unrecognized",
				filegen_string);
			continue;
		}
		DPRINTF(4, ("enabling filegen for %s statistics '%s%s'\n",
			    filegen_string, filegen->dir,
			    filegen->fname));
		filegen_flag = filegen->flag;
		filegen_flag |= FGEN_FLAG_ENABLED;
		filegen_config(filegen, statsdir, filegen_string,
			       filegen->type, filegen_flag);
	}

	/* Configure the statistics with the options */
	my_node = HEAD_PFIFO(ptree->filegen_opts);
	for (; my_node != NULL; my_node = my_node->link) {
		filegen_string = keyword(my_node->filegen_token);
		filegen = filegen_get(filegen_string);
		if (NULL == filegen) {
			msyslog(LOG_ERR,
				"filegen category '%s' unrecognized",
				filegen_string);
			continue;
		}
		filegen_file = filegen_string;

		/* Initialize the filegen variables to their pre-configuration states */
		filegen_flag = filegen->flag;
		filegen_type = filegen->type;

		/* "filegen ... enabled" is the default (when filegen is used) */
		filegen_flag |= FGEN_FLAG_ENABLED;

		my_opts = HEAD_PFIFO(my_node->options);
		for (; my_opts != NULL; my_opts = my_opts->link) {
			switch (my_opts->attr) {

			case T_File:
				filegen_file = my_opts->value.s;
				break;

			case T_Type:
				switch (my_opts->value.i) {

				default:
					INSIST(0);
					break;

				case T_None:
					filegen_type = FILEGEN_NONE;
					break;

				case T_Pid:
					filegen_type = FILEGEN_PID;
					break;

				case T_Day:
					filegen_type = FILEGEN_DAY;
					break;

				case T_Week:
					filegen_type = FILEGEN_WEEK;
					break;

				case T_Month:
					filegen_type = FILEGEN_MONTH;
					break;

				case T_Year:
					filegen_type = FILEGEN_YEAR;
					break;

				case T_Age:
					filegen_type = FILEGEN_AGE;
					break;
				}
				break;

			case T_Flag:
				switch (my_opts->value.i) {

				case T_Link:
					filegen_flag |= FGEN_FLAG_LINK;
					break;

				case T_Nolink:
					filegen_flag &= ~FGEN_FLAG_LINK;
					break;

				case T_Enable:
					filegen_flag |= FGEN_FLAG_ENABLED;
					break;

				case T_Disable:
					filegen_flag &= ~FGEN_FLAG_ENABLED;
					break;

				default:
					msyslog(LOG_ERR,
						"Unknown filegen flag token %d",
						my_opts->value.i);
					exit(1);
				}
				break;

			default:
				msyslog(LOG_ERR,
					"Unknown filegen option token %d",
					my_opts->attr);
				exit(1);
			}
		}
		filegen_config(filegen, statsdir, filegen_file,
			       filegen_type, filegen_flag);
	}
}


#ifdef FREE_CFG_T
static void
free_config_monitor(
	config_tree *ptree
	)
{
	if (ptree->stats_dir) {
		free(ptree->stats_dir);
		ptree->stats_dir = NULL;
	}

	FREE_INT_FIFO(ptree->stats_list);
	FREE_FILEGEN_FIFO(ptree->filegen_opts);
}
#endif	/* FREE_CFG_T */


#ifndef SIM
static void
config_access(
	config_tree *ptree
	)
{
	static int		warned_signd;
	attr_val *		my_opt;
	restrict_node *		my_node;
	int_node *		curr_flag;
	sockaddr_u		addr;
	sockaddr_u		mask;
	struct addrinfo		hints;
	struct addrinfo *	ai_list;
	struct addrinfo *	pai;
	int			rc;
	int			restrict_default;
	u_short			flags;
	u_short			mflags;
	int			range_err;
	const char *		signd_warning =
#ifdef HAVE_NTP_SIGND
	    "MS-SNTP signd operations currently block ntpd degrading service to all clients.";
#else
	    "mssntp restrict bit ignored, this ntpd was configured without --enable-ntp-signd.";
#endif

	/* Configure the mru options */
	my_opt = HEAD_PFIFO(ptree->mru_opts);
	for (; my_opt != NULL; my_opt = my_opt->link) {

		range_err = FALSE;

		switch (my_opt->attr) {

		case T_Incalloc:
			if (0 <= my_opt->value.i)
				mru_incalloc = my_opt->value.u;
			else
				range_err = TRUE;
			break;

		case T_Incmem:
			if (0 <= my_opt->value.i)
				mru_incalloc = (my_opt->value.u * 1024U)
						/ sizeof(mon_entry);
			else
				range_err = TRUE;
			break;

		case T_Initalloc:
			if (0 <= my_opt->value.i)
				mru_initalloc = my_opt->value.u;
			else
				range_err = TRUE;
			break;

		case T_Initmem:
			if (0 <= my_opt->value.i)
				mru_initalloc = (my_opt->value.u * 1024U)
						 / sizeof(mon_entry);
			else
				range_err = TRUE;
			break;

		case T_Mindepth:
			if (0 <= my_opt->value.i)
				mru_mindepth = my_opt->value.u;
			else
				range_err = TRUE;
			break;

		case T_Maxage:
			mru_maxage = my_opt->value.i;
			break;

		case T_Maxdepth:
			if (0 <= my_opt->value.i)
				mru_maxdepth = my_opt->value.u;
			else
				mru_maxdepth = UINT_MAX;
			break;

		case T_Maxmem:
			if (0 <= my_opt->value.i)
				mru_maxdepth = (my_opt->value.u * 1024U) /
					       sizeof(mon_entry);
			else
				mru_maxdepth = UINT_MAX;
			break;

		default:
			msyslog(LOG_ERR,
				"Unknown mru option %s (%d)",
				keyword(my_opt->attr), my_opt->attr);
			exit(1);
		}
		if (range_err)
			msyslog(LOG_ERR,
				"mru %s %d out of range, ignored.",
				keyword(my_opt->attr), my_opt->value.i);
	}

	/* Configure the discard options */
	my_opt = HEAD_PFIFO(ptree->discard_opts);
	for (; my_opt != NULL; my_opt = my_opt->link) {

		switch (my_opt->attr) {

		case T_Average:
			if (0 <= my_opt->value.i &&
			    my_opt->value.i <= UCHAR_MAX)
				ntp_minpoll = (u_char)my_opt->value.u;
			else
				msyslog(LOG_ERR,
					"discard average %d out of range, ignored.",
					my_opt->value.i);
			break;

		case T_Minimum:
			ntp_minpkt = my_opt->value.i;
			break;

		case T_Monitor:
			mon_age = my_opt->value.i;
			break;

		default:
			msyslog(LOG_ERR,
				"Unknown discard option %s (%d)",
				keyword(my_opt->attr), my_opt->attr);
			exit(1);
		}
	}

	/* Configure the restrict options */
	my_node = HEAD_PFIFO(ptree->restrict_opts);
	for (; my_node != NULL; my_node = my_node->link) {
		/* Parse the flags */
		flags = 0;
		mflags = 0;

		curr_flag = HEAD_PFIFO(my_node->flags);
		for (; curr_flag != NULL; curr_flag = curr_flag->link) {
			switch (curr_flag->i) {

			default:
				INSIST(0);
				break;

			case T_Ntpport:
				mflags |= RESM_NTPONLY;
				break;

			case T_Source:
				mflags |= RESM_SOURCE;
				break;

			case T_Flake:
				flags |= RES_FLAKE;
				break;

			case T_Ignore:
				flags |= RES_IGNORE;
				break;

			case T_Kod:
				flags |= RES_KOD;
				break;

			case T_Mssntp:
				flags |= RES_MSSNTP;
				break;

			case T_Limited:
				flags |= RES_LIMITED;
				break;

			case T_Lowpriotrap:
				flags |= RES_LPTRAP;
				break;

			case T_Nomodify:
				flags |= RES_NOMODIFY;
				break;

			case T_Nomrulist:
				flags |= RES_NOMRULIST;
				break;

			case T_Nopeer:
				flags |= RES_NOPEER;
				break;

			case T_Noquery:
				flags |= RES_NOQUERY;
				break;

			case T_Noserve:
				flags |= RES_DONTSERVE;
				break;

			case T_Notrap:
				flags |= RES_NOTRAP;
				break;

			case T_Notrust:
				flags |= RES_DONTTRUST;
				break;

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

		if ((RES_MSSNTP & flags) && !warned_signd) {
			warned_signd = 1;
			fprintf(stderr, "%s\n", signd_warning);
			msyslog(LOG_WARNING, "%s", signd_warning);
		}

		/* It would be swell if we could identify the line number */
		if ((RES_KOD & flags) && !(RES_LIMITED & flags)) {
			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.";

			fprintf(stderr, "restrict %s: %s\n", kod_where, kod_warn);
			msyslog(LOG_WARNING, "restrict %s: %s", kod_where, kod_warn);
		}

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

		if (NULL == my_node->addr) {
			ZERO_SOCK(&mask);
			if (!(RESM_SOURCE & mflags)) {
				/*
				 * The user specified a default rule
				 * without a -4 / -6 qualifier, add to
				 * both lists
				 */
				restrict_default = 1;
			} else {
				/* apply "restrict source ..." */
				DPRINTF(1, ("restrict source template mflags %x flags %x\n",
					mflags, flags));
				hack_restrict(RESTRICT_FLAGS, NULL,
					      NULL, mflags, flags, 0);
				continue;
			}
		} else {
			/* Resolve the specified address */
			AF(&addr) = (u_short)my_node->addr->type;

			if (getnetnum(my_node->addr->address,
				      &addr, 1, t_UNK) != 1) {
				/*
				 * Attempt a blocking lookup.  This
				 * is in violation of the nonblocking
				 * design of ntpd's mainline code.  The
				 * alternative of running without the
				 * restriction until the name resolved
				 * seems worse.
				 * Ideally some scheme could be used for
				 * restrict directives in the startup
				 * ntp.conf to delay starting up the
				 * protocol machinery until after all
				 * restrict hosts have been resolved.
				 */
				ai_list = NULL;
				ZERO(hints);
				hints.ai_protocol = IPPROTO_UDP;
				hints.ai_socktype = SOCK_DGRAM;
				hints.ai_family = my_node->addr->type;
				rc = getaddrinfo(my_node->addr->address,
						 "ntp", &hints,
						 &ai_list);
				if (rc) {
					msyslog(LOG_ERR,
						"restrict: ignoring line %d, address/host '%s' unusable.",
						my_node->line_no,
						my_node->addr->address);
					continue;
				}
				INSIST(ai_list != NULL);
				pai = ai_list;
				INSIST(pai->ai_addr != NULL);
				INSIST(sizeof(addr) >=
					   pai->ai_addrlen);
				memcpy(&addr, pai->ai_addr,
				       pai->ai_addrlen);
				INSIST(AF_INET == AF(&addr) ||
					   AF_INET6 == AF(&addr));
			}

			SET_HOSTMASK(&mask, AF(&addr));

			/* Resolve the mask */
			if (my_node->mask) {
				ZERO_SOCK(&mask);
				AF(&mask) = my_node->mask->type;
				if (getnetnum(my_node->mask->address,
					      &mask, 1, t_MSK) != 1) {
					msyslog(LOG_ERR,
						"restrict: ignoring line %d, mask '%s' unusable.",
						my_node->line_no,
						my_node->mask->address);
					continue;
				}
			}
		}

		/* Set the flags */
		if (restrict_default) {
			AF(&addr) = AF_INET;
			AF(&mask) = AF_INET;
			hack_restrict(RESTRICT_FLAGS, &addr,
				      &mask, mflags, flags, 0);
			AF(&addr) = AF_INET6;
			AF(&mask) = AF_INET6;
		}

		do {
			hack_restrict(RESTRICT_FLAGS, &addr,
				      &mask, mflags, flags, 0);
			if (pai != NULL &&
			    NULL != (pai = pai->ai_next)) {
				INSIST(pai->ai_addr != NULL);
				INSIST(sizeof(addr) >=
					   pai->ai_addrlen);
				ZERO_SOCK(&addr);
				memcpy(&addr, pai->ai_addr,
				       pai->ai_addrlen);
				INSIST(AF_INET == AF(&addr) ||
					   AF_INET6 == AF(&addr));
				SET_HOSTMASK(&mask, AF(&addr));
			}
		} while (pai != NULL);

		if (ai_list != NULL)
			freeaddrinfo(ai_list);
	}
}
#endif	/* !SIM */


#ifdef FREE_CFG_T
static void
free_config_access(
	config_tree *ptree
	)
{
	FREE_ATTR_VAL_FIFO(ptree->mru_opts);
	FREE_ATTR_VAL_FIFO(ptree->discard_opts);
	FREE_RESTRICT_FIFO(ptree->restrict_opts);
}
#endif	/* FREE_CFG_T */


static void
config_rlimit(
	config_tree *ptree
	)
{
	attr_val *	rlimit_av;

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

		default:
			INSIST(0);
			break;

		case T_Memlock:
			/* What if we HAVE_OPT(SAVECONFIGQUIT) ? */
			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;

#ifdef __GNUC__
	item = -1;	/* quiet warning */
#endif
	tinker = HEAD_PFIFO(ptree->tinker);
	for (; tinker != NULL; tinker = tinker->link) {
		switch (tinker->attr) {

		default:
			INSIST(0);
			break;

		case T_Allan:
			item = LOOP_ALLAN;
			break;

		case T_Dispersion:
			item = LOOP_PHI;
			break;

		case T_Freq:
			item = LOOP_FREQ;
			break;

		case T_Huffpuff:
			item = LOOP_HUFFPUFF;
			break;

		case T_Panic:
			item = LOOP_PANIC;
			break;

		case T_Step:
			item = LOOP_MAX;
			break;

		case T_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 __GNUC__
			/*
			 * this assignment quiets a gcc "may be used
			 * uninitialized" warning and is here for no
			 * other reason.
			 */
			match_type = MATCH_ALL;
#endif
			INSIST(FALSE);
			break;

		case 0:
			/*
			 * 0 is out of range for valid token T_...
			 * and in a nic_rules_node indicates the
			 * interface descriptor is either a name or
			 * address, stored in if_name in either case.
			 */
			INSIST(if_name != NULL);
			pchSlash = strchr(if_name, '/');
			if (pchSlash != NULL)
				*pchSlash = '\0';
			if (is_ip_address(if_name, AF_UNSPEC, &addr)) {
				match_type = MATCH_IFADDR;
				if (pchSlash != NULL
				    && 1 == sscanf(pchSlash + 1, "%d",
					    &prefixlen)) {
					addrbits = 8 *
					    SIZEOF_INADDR(AF(&addr));
					prefixlen = max(-1, prefixlen);
					prefixlen = min(prefixlen,
							addrbits);
				}
			} else {
				match_type = MATCH_IFNAME;
				if (pchSlash != NULL)
					*pchSlash = '/';
			}
			break;

		case T_All:
			match_type = MATCH_ALL;
			break;

		case T_Ipv4:
			match_type = MATCH_IPV4;
			break;

		case T_Ipv6:
			match_type = MATCH_IPV6;
			break;

		case T_Wildcard:
			match_type = MATCH_WILDCARD;
			break;
		}

		switch (curr_node->action) {

		default:
#ifdef __GNUC__
			/*
			 * this assignment quiets a gcc "may be used
			 * uninitialized" warning and is here for no
			 * other reason.
			 */
			action = ACTION_LISTEN;
#endif
			INSIST(FALSE);
			break;

		case T_Listen:
			action = ACTION_LISTEN;
			break;

		case T_Ignore:
			action = ACTION_IGNORE;
			break;

		case T_Drop:
			action = ACTION_DROP;
			break;
		}

		add_nic_rule(match_type, if_name, prefixlen,
			     action);
		timer_interfacetimeout(current_time + 2);
		if (if_name != NULL)
			free(if_name);
	}
}
#endif	/* !SIM */


#ifdef FREE_CFG_T
static void
free_config_nic_rules(
	config_tree *ptree
	)
{
	nic_rule_node *curr_node;

	if (ptree->nic_rules != NULL) {
		for (;;) {
			UNLINK_FIFO(curr_node, *ptree->nic_rules, link);
			if (NULL == curr_node)
				break;
			free(curr_node->if_name);
			free(curr_node);
		}
		free(ptree->nic_rules);
		ptree->nic_rules = NULL;
	}
}
#endif	/* FREE_CFG_T */


static void
apply_enable_disable(
	attr_val_fifo *	fifo,
	int		enable
	)
{
	attr_val *curr_flag;
	int option;
#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED
	bc_entry *pentry;
#endif

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

		option = curr_flag->value.i;
		switch (option) {

		default:
			msyslog(LOG_ERR,
				"can not apply enable/disable token %d, unknown",
				option);
			break;

		case T_Auth:
			proto_config(PROTO_AUTHENTICATE, enable, 0., NULL);
			break;

		case T_Bclient:
			proto_config(PROTO_BROADCLIENT, enable, 0., NULL);
			break;

		case T_Calibrate:
			proto_config(PROTO_CAL, enable, 0., NULL);
			break;

		case T_Kernel:
			proto_config(PROTO_KERNEL, enable, 0., NULL);
			break;

		case T_Monitor:
			proto_config(PROTO_MONITOR, enable, 0., NULL);
			break;

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

		case T_Mode7:
			proto_config(PROTO_MODE7, enable, 0., NULL);
			break;

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

		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:
			INSIST(0);
			break;
		}
	}
}


#ifdef FREE_CFG_T
static void
free_config_logconfig(
	config_tree *ptree
	)
{
	FREE_ATTR_VAL_FIFO(ptree->logconfig);
}
#endif	/* FREE_CFG_T */


#ifndef SIM
static void
config_phone(
	config_tree *ptree
	)
{
	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);
		}
	}
}
#endif	/* !SIM */

static void
config_mdnstries(
	config_tree *ptree
	)
{
#ifdef HAVE_DNSREGISTRATION
	extern int mdnstries;
	mdnstries = ptree->mdnstries;
#endif  /* HAVE_DNSREGISTRATION */
}

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

	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);
	}
	sys_ttlmax = i - 1;
}
#endif	/* !SIM */


#ifdef FREE_CFG_T
static void
free_config_ttl(
	config_tree *ptree
	)
{
	FREE_INT_FIFO(ptree->ttl);
}
#endif	/* FREE_CFG_T */


#ifndef SIM
static void
config_trap(
	config_tree *ptree
	)
{
	addr_opts_node *curr_trap;
	attr_val *curr_opt;
	sockaddr_u addr_sock;
	sockaddr_u peeraddr;
	struct interface *localaddr;
	struct addrinfo hints;
	char port_text[8];
	settrap_parms *pstp;
	u_short port;
	int err_flag;
	int rc;

	/* silence warning about addr_sock potentially uninitialized */
	AF(&addr_sock) = AF_UNSPEC;

	curr_trap = HEAD_PFIFO(ptree->trap);
	for (; curr_trap != NULL; curr_trap = curr_trap->link) {
		err_flag = 0;
		port = 0;
		localaddr = NULL;

		curr_opt = HEAD_PFIFO(curr_trap->options);
		for (; curr_opt != NULL; curr_opt = curr_opt->link) {
			if (T_Port == curr_opt->attr) {
				if (curr_opt->value.i < 1
				    || curr_opt->value.i > USHRT_MAX) {
					msyslog(LOG_ERR,
						"invalid port number "
						"%d, trap ignored",
						curr_opt->value.i);
					err_flag = 1;
				}
				port = (u_short)curr_opt->value.i;
			}
			else if (T_Interface == curr_opt->attr) {
				/* Resolve the interface address */
				ZERO_SOCK(&addr_sock);
				if (getnetnum(curr_opt->value.s,
					      &addr_sock, 1, t_UNK) != 1) {
					err_flag = 1;
					break;
				}

				localaddr = findinterface(&addr_sock);

				if (NULL == localaddr) {
					msyslog(LOG_ERR,
						"can't find interface with address %s",
						stoa(&addr_sock));
					err_flag = 1;
				}
			}
		}

		/* Now process the trap for the specified interface
		 * and port number
		 */
		if (!err_flag) {
			if (!port)
				port = TRAPPORT;
			ZERO_SOCK(&peeraddr);
			rc = getnetnum(curr_trap->addr->address,
				       &peeraddr, 1, t_UNK);
			if (1 != rc) {
#ifndef WORKER
				msyslog(LOG_ERR,
					"trap: unable to use IP address %s.",
					curr_trap->addr->address);
#else	/* WORKER follows */
				/*
				 * save context and hand it off
				 * for name resolution.
				 */
				ZERO(hints);
				hints.ai_protocol = IPPROTO_UDP;
				hints.ai_socktype = SOCK_DGRAM;
				snprintf(port_text, sizeof(port_text),
					 "%u", port);
				hints.ai_flags = Z_AI_NUMERICSERV;
				pstp = emalloc_zero(sizeof(*pstp));
				if (localaddr != NULL) {
					hints.ai_family = localaddr->family;
					pstp->ifaddr_nonnull = 1;
					memcpy(&pstp->ifaddr,
					       &localaddr->sin,
					       sizeof(pstp->ifaddr));
				}
				rc = getaddrinfo_sometime(
					curr_trap->addr->address,
					port_text, &hints,
					INITIAL_DNS_RETRY,
					&trap_name_resolved,
					pstp);
				if (!rc)
					msyslog(LOG_ERR,
						"config_trap: getaddrinfo_sometime(%s,%s): %m",
						curr_trap->addr->address,
						port_text);
#endif	/* WORKER */
				continue;
			}
			/* port is at same location for v4 and v6 */
			SET_PORT(&peeraddr, port);

			if (NULL == localaddr)
				localaddr = ANY_INTERFACE_CHOOSE(&peeraddr);
			else
				AF(&peeraddr) = AF(&addr_sock);

			if (!ctlsettrap(&peeraddr, localaddr, 0,
					NTP_VERSION))
				msyslog(LOG_ERR,
					"set trap %s -> %s failed.",
					latoa(localaddr),
					stoa(&peeraddr));
		}
	}
}


/*
 * trap_name_resolved()
 *
 * Callback invoked when config_trap()'s DNS lookup completes.
 */
# ifdef WORKER
static void
trap_name_resolved(
	int			rescode,
	int			gai_errno,
	void *			context,
	const char *		name,
	const char *		service,
	const struct addrinfo *	hints,
	const struct addrinfo *	res
	)
{
	settrap_parms *pstp;
	struct interface *localaddr;
	sockaddr_u peeraddr;

	(void)gai_errno;
	(void)service;
	(void)hints;
	pstp = context;
	if (rescode) {
		msyslog(LOG_ERR,
			"giving up resolving trap host %s: %s (%d)",
			name, gai_strerror(rescode), rescode);
		free(pstp);
		return;
	}
	INSIST(sizeof(peeraddr) >= res->ai_addrlen);
	ZERO(peeraddr);
	memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
	localaddr = NULL;
	if (pstp->ifaddr_nonnull)
		localaddr = findinterface(&pstp->ifaddr);
	if (NULL == localaddr)
		localaddr = ANY_INTERFACE_CHOOSE(&peeraddr);
	if (!ctlsettrap(&peeraddr, localaddr, 0, NTP_VERSION))
		msyslog(LOG_ERR, "set trap %s -> %s failed.",
			latoa(localaddr), stoa(&peeraddr));
	free(pstp);
}
# endif	/* WORKER */
#endif	/* !SIM */


#ifdef FREE_CFG_T
static void
free_config_trap(
	config_tree *ptree
	)
{
	FREE_ADDR_OPTS_FIFO(ptree->trap);
}
#endif	/* FREE_CFG_T */


#ifndef SIM
static void
config_fudge(
	config_tree *ptree
	)
{
	addr_opts_node *curr_fudge;
	attr_val *curr_opt;
	sockaddr_u addr_sock;
	address_node *addr_node;
	struct refclockstat clock_stat;
	int err_flag;

	curr_fudge = HEAD_PFIFO(ptree->fudge);
	for (; curr_fudge != NULL; curr_fudge = curr_fudge->link) {
		err_flag = 0;

		/* Get the reference clock address and
		 * ensure that it is sane
		 */
		addr_node = curr_fudge->addr;
		ZERO_SOCK(&addr_sock);
		if (getnetnum(addr_node->address, &addr_sock, 1, t_REF)
		    != 1) {
			err_flag = 1;
			msyslog(LOG_ERR,
				"unrecognized fudge reference clock address %s, line ignored",
				stoa(&addr_sock));
		}

		if (!ISREFCLOCKADR(&addr_sock)) {
			err_flag = 1;
			msyslog(LOG_ERR,
				"inappropriate address %s for the fudge command, line ignored",
				stoa(&addr_sock));
		}

		/* Parse all the options to the fudge command */
		ZERO(clock_stat);
		curr_opt = HEAD_PFIFO(curr_fudge->options);
		for (; curr_opt != NULL; curr_opt = curr_opt->link) {
			switch (curr_opt->attr) {

			case T_Time1:
				clock_stat.haveflags |= CLK_HAVETIME1;
				clock_stat.fudgetime1 = curr_opt->value.d;
				break;

			case T_Time2:
				clock_stat.haveflags |= CLK_HAVETIME2;
				clock_stat.fudgetime2 = curr_opt->value.d;
				break;

			case T_Stratum:
				clock_stat.haveflags |= CLK_HAVEVAL1;
				clock_stat.fudgeval1 = curr_opt->value.i;
				break;

			case T_Refid:
				clock_stat.haveflags |= CLK_HAVEVAL2;
				clock_stat.fudgeval2 = 0;
				memcpy(&clock_stat.fudgeval2,
				       curr_opt->value.s,
				       min(strlen(curr_opt->value.s), 4));
				break;

			case T_Flag1:
				clock_stat.haveflags |= CLK_HAVEFLAG1;
				if (curr_opt->value.i)
					clock_stat.flags |= CLK_FLAG1;
				else
					clock_stat.flags &= ~CLK_FLAG1;
				break;

			case T_Flag2:
				clock_stat.haveflags |= CLK_HAVEFLAG2;
				if (curr_opt->value.i)
					clock_stat.flags |= CLK_FLAG2;
				else
					clock_stat.flags &= ~CLK_FLAG2;
				break;

			case T_Flag3:
				clock_stat.haveflags |= CLK_HAVEFLAG3;
				if (curr_opt->value.i)
					clock_stat.flags |= CLK_FLAG3;
				else
					clock_stat.flags &= ~CLK_FLAG3;
				break;

			case T_Flag4:
				clock_stat.haveflags |= CLK_HAVEFLAG4;
				if (curr_opt->value.i)
					clock_stat.flags |= CLK_FLAG4;
				else
					clock_stat.flags &= ~CLK_FLAG4;
				break;

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


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


static void
config_vars(
	config_tree *ptree
	)
{
	attr_val *curr_var;
	int len;

	curr_var = HEAD_PFIFO(ptree->vars);
	for (; curr_var != NULL; curr_var = curr_var->link) {
		/* Determine which variable to set and set it */
		switch (curr_var->attr) {

		case T_Broadcastdelay:
			proto_config(PROTO_BROADDELAY, 0, curr_var->value.d, NULL);
			break;

		case T_Tick:
			loop_config(LOOP_TICK, curr_var->value.d);
			break;

		case T_Driftfile:
			if ('\0' == curr_var->value.s[0]) {
				stats_drift_file = 0;
				msyslog(LOG_INFO, "config: driftfile disabled");
			} else
				stats_config(STATS_FREQ_FILE, curr_var->value.s);
			break;

		case T_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);
			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);
			break;

		case T_Logfile:
			if (-1 == change_logfile(curr_var->value.s, TRUE))
				msyslog(LOG_ERR,
					"Cannot open logfile %s: %m",
					curr_var->value.s);
			break;

		case T_Saveconfigdir:
			if (saveconfigdir != NULL)
				free(saveconfigdir);
			len = strlen(curr_var->value.s);
			if (0 == len) {
				saveconfigdir = NULL;
			} else if (DIR_SEP != curr_var->value.s[len - 1]
#ifdef SYS_WINNT	/* slash is also a dir. sep. on Windows */
				   && '/' != curr_var->value.s[len - 1]
#endif
				 ) {
					len++;
					saveconfigdir = emalloc(len + 1);
					snprintf(saveconfigdir, len + 1,
						 "%s%c",
						 curr_var->value.s,
						 DIR_SEP);
			} else {
					saveconfigdir = estrdup(
					    curr_var->value.s);
			}
			break;

		case T_Automax:
#ifdef AUTOKEY
			sys_automax = curr_var->value.i;
#endif
			break;

		default:
			msyslog(LOG_ERR,
				"config_vars(): unexpected token %d",
				curr_var->attr);
		}
	}
}


#ifdef FREE_CFG_T
static void
free_config_vars(
	config_tree *ptree
	)
{
	FREE_ATTR_VAL_FIFO(ptree->vars);
}
#endif	/* FREE_CFG_T */


/* Define a function to check if a resolved address is sane.
 * If yes, return 1, else return 0;
 */
static int
is_sane_resolved_address(
	sockaddr_u *	peeraddr,
	int		hmode
	)
{
	if (!ISREFCLOCKADR(peeraddr) && ISBADADR(peeraddr)) {
		msyslog(LOG_ERR,
			"attempt to configure invalid address %s",
			stoa(peeraddr));
		return 0;
	}
	/*
	 * Shouldn't be able to specify multicast
	 * address for server/peer!
	 * and unicast address for manycastclient!
	 */
	if ((T_Server == hmode || T_Peer == hmode || T_Pool == hmode)
	    && IS_MCAST(peeraddr)) {
		msyslog(LOG_ERR,
			"attempt to configure invalid address %s",
			stoa(peeraddr));
		return 0;
	}
	if (T_Manycastclient == hmode && !IS_MCAST(peeraddr)) {
		msyslog(LOG_ERR,
			"attempt to configure invalid address %s",
			stoa(peeraddr));
		return 0;
	}

	if (IS_IPV6(peeraddr) && !ipv6_works)
		return 0;

	/* Ok, all tests succeeded, now we can return 1 */
	return 1;
}


#ifndef SIM
static u_char
get_correct_host_mode(
	int token
	)
{
	switch (token) {

	case T_Server:
	case T_Pool:
	case T_Manycastclient:
		return MODE_CLIENT;

	case T_Peer:
		return MODE_ACTIVE;

	case T_Broadcast:
		return MODE_BROADCAST;

	default:
		return 0;
	}
}


/*
 * peerflag_bits()	get config_peers() peerflags value from a
 *			peer_node's queue of flag attr_val entries.
 */
static int
peerflag_bits(
	peer_node *pn
	)
{
	int peerflags;
	attr_val *option;

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

		default:
			INSIST(0);
			break;

		case T_Autokey:
			peerflags |= FLAG_SKEY;
			break;

		case T_Burst:
			peerflags |= FLAG_BURST;
			break;

		case T_Iburst:
			peerflags |= FLAG_IBURST;
			break;

		case T_Noselect:
			peerflags |= FLAG_NOSELECT;
			break;

		case T_Preempt:
			peerflags |= FLAG_PREEMPT;
			break;

		case T_Prefer:
			peerflags |= FLAG_PREFER;
			break;

		case T_True:
			peerflags |= FLAG_TRUE;
			break;

		case T_Xleave:
			peerflags |= FLAG_XLEAVE;
			break;
		}
	}

	return peerflags;
}


static void
config_peers(
	config_tree *ptree
	)
{
	sockaddr_u		peeraddr;
	struct addrinfo		hints;
	peer_node *		curr_peer;
	peer_resolved_ctx *	ctx;
	u_char			hmode;

	/* add servers named on the command line with iburst implied */
	for (;
	     cmdline_server_count > 0;
	     cmdline_server_count--, cmdline_servers++) {

		ZERO_SOCK(&peeraddr);
		/*
		 * If we have a numeric address, we can safely
		 * proceed in the mainline with it.  Otherwise, hand
		 * the hostname off to the blocking child.
		 */
		if (is_ip_address(*cmdline_servers, AF_UNSPEC,
				  &peeraddr)) {

			SET_PORT(&peeraddr, NTP_PORT);
			if (is_sane_resolved_address(&peeraddr,
						     T_Server))
				peer_config(
					&peeraddr,
					NULL,
					NULL,
					MODE_CLIENT,
					NTP_VERSION,
					0,
					0,
					FLAG_IBURST,
					0,
					0,
					NULL);
		} else {
			/* we have a hostname to resolve */
# ifdef WORKER
			ctx = emalloc_zero(sizeof(*ctx));
			ctx->family = AF_UNSPEC;
			ctx->host_mode = T_Server;
			ctx->hmode = MODE_CLIENT;
			ctx->version = NTP_VERSION;
			ctx->flags = FLAG_IBURST;

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

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

	/* add associations from the configuration file */
	curr_peer = HEAD_PFIFO(ptree->peers);
	for (; curr_peer != NULL; curr_peer = curr_peer->link) {
		ZERO_SOCK(&peeraddr);
		/* Find the correct host-mode */
		hmode = get_correct_host_mode(curr_peer->host_mode);
		INSIST(hmode != 0);

		if (T_Pool == curr_peer->host_mode) {
			AF(&peeraddr) = curr_peer->addr->type;
			peer_config(
				&peeraddr,
				curr_peer->addr->address,
				NULL,
				hmode,
				curr_peer->peerversion,
				curr_peer->minpoll,
				curr_peer->maxpoll,
				peerflag_bits(curr_peer),
				curr_peer->ttl,
				curr_peer->peerkey,
				curr_peer->group);
		/*
		 * If we have a numeric address, we can safely
		 * proceed in the mainline with it.  Otherwise, hand
		 * the hostname off to the blocking child.
		 */
		} else if (is_ip_address(curr_peer->addr->address,
				  curr_peer->addr->type, &peeraddr)) {

			SET_PORT(&peeraddr, NTP_PORT);
			if (is_sane_resolved_address(&peeraddr,
			    curr_peer->host_mode))
				peer_config(
					&peeraddr,
					NULL,
					NULL,
					hmode,
					curr_peer->peerversion,
					curr_peer->minpoll,
					curr_peer->maxpoll,
					peerflag_bits(curr_peer),
					curr_peer->ttl,
					curr_peer->peerkey,
					curr_peer->group);
		} else {
			/* we have a hostname to resolve */
# ifdef WORKER
			ctx = emalloc_zero(sizeof(*ctx));
			ctx->family = curr_peer->addr->type;
			ctx->host_mode = curr_peer->host_mode;
			ctx->hmode = hmode;
			ctx->version = curr_peer->peerversion;
			ctx->minpoll = curr_peer->minpoll;
			ctx->maxpoll = curr_peer->maxpoll;
			ctx->flags = peerflag_bits(curr_peer);
			ctx->ttl = curr_peer->ttl;
			ctx->keyid = curr_peer->peerkey;
			ctx->group = curr_peer->group;

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

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

/*
 * peer_name_resolved()
 *
 * Callback invoked when config_peers()'s DNS lookup completes.
 */
#ifdef WORKER
static void
peer_name_resolved(
	int			rescode,
	int			gai_errno,
	void *			context,
	const char *		name,
	const char *		service,
	const struct addrinfo *	hints,
	const struct addrinfo *	res
	)
{
	sockaddr_u		peeraddr;
	peer_resolved_ctx *	ctx;
	u_short			af;
	const char *		fam_spec;

	(void)gai_errno;
	(void)service;
	(void)hints;
	ctx = context;

	DPRINTF(1, ("peer_name_resolved(%s) rescode %d\n", name, rescode));

	if (rescode) {
#ifndef IGNORE_DNS_ERRORS
		free(ctx);
		msyslog(LOG_ERR,
			"giving up resolving host %s: %s (%d)",
			name, gai_strerror(rescode), rescode);
#else	/* IGNORE_DNS_ERRORS follows */
		getaddrinfo_sometime(name, service, hints,
				     INITIAL_DNS_RETRY,
				     &peer_name_resolved, context);
#endif
		return;
	}

	/* Loop to configure a single association */
	for (; res != NULL; res = res->ai_next) {
		memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
		if (is_sane_resolved_address(&peeraddr,
					     ctx->host_mode)) {
			NLOG(NLOG_SYSINFO) {
				af = ctx->family;
				fam_spec = (AF_INET6 == af)
					       ? "(AAAA) "
					       : (AF_INET == af)
						     ? "(A) "
						     : "";
				msyslog(LOG_INFO, "DNS %s %s-> %s",
					name, fam_spec,
					stoa(&peeraddr));
			}
			peer_config(
				&peeraddr,
				NULL,
				NULL,
				ctx->hmode,
				ctx->version,
				ctx->minpoll,
				ctx->maxpoll,
				ctx->flags,
				ctx->ttl,
				ctx->keyid,
				ctx->group);
			break;
		}
	}
	free(ctx);
}
#endif	/* WORKER */


#ifdef FREE_CFG_T
static void
free_config_peers(
	config_tree *ptree
	)
{
	peer_node *curr_peer;

	if (ptree->peers != NULL) {
		for (;;) {
			UNLINK_FIFO(curr_peer, *ptree->peers, link);
			if (NULL == curr_peer)
				break;
			destroy_address_node(curr_peer->addr);
			destroy_attr_val_fifo(curr_peer->peerflags);
			free(curr_peer);
		}
		free(ptree->peers);
		ptree->peers = NULL;
	}
}
#endif	/* FREE_CFG_T */


#ifndef SIM
static void
config_unpeers(
	config_tree *ptree
	)
{
	sockaddr_u		peeraddr;
	struct addrinfo		hints;
	unpeer_node *		curr_unpeer;
	struct peer *		p;
	const char *		name;
	int			rc;

	curr_unpeer = HEAD_PFIFO(ptree->unpeers);
	for (; curr_unpeer != NULL; curr_unpeer = curr_unpeer->link) {
		/*
		 * Either AssocID will be zero, and we unpeer by name/
		 * address addr, or it is nonzero and addr NULL.
		 */
		if (curr_unpeer->assocID) {
			p = findpeerbyassoc(curr_unpeer->assocID);
			if (p != NULL) {
				msyslog(LOG_NOTICE, "unpeered %s",
					stoa(&p->srcadr));
				peer_clear(p, "GONE");
				unpeer(p);
			}

			continue;
		}

		ZERO(peeraddr);
		AF(&peeraddr) = curr_unpeer->addr->type;
		name = curr_unpeer->addr->address;
		rc = getnetnum(name, &peeraddr, 0, t_UNK);
		/* Do we have a numeric address? */
		if (rc > 0) {
			DPRINTF(1, ("unpeer: searching for %s\n",
				    stoa(&peeraddr)));
			p = findexistingpeer(&peeraddr, NULL, NULL, -1, 0);
			if (p != NULL) {
				msyslog(LOG_NOTICE, "unpeered %s",
					stoa(&peeraddr));
				peer_clear(p, "GONE");
				unpeer(p);
			}

			continue;
		}
		/*
		 * It's not a numeric IP address, it's a hostname.
		 * Check for associations with a matching hostname.
		 */
		for (p = peer_list; p != NULL; p = p->p_link)
			if (p->hostname != NULL)
				if (!strcasecmp(p->hostname, name))
					break;
		if (p != NULL) {
			msyslog(LOG_NOTICE, "unpeered %s", name);
			peer_clear(p, "GONE");
			unpeer(p);
		}
		/* Resolve the hostname to address(es). */
# ifdef WORKER
		ZERO(hints);
		hints.ai_family = curr_unpeer->addr->type;
		hints.ai_socktype = SOCK_DGRAM;
		hints.ai_protocol = IPPROTO_UDP;
		getaddrinfo_sometime(name, "ntp", &hints,
				     INITIAL_DNS_RETRY,
				     &unpeer_name_resolved, NULL);
# else	/* !WORKER follows */
		msyslog(LOG_ERR,
			"hostname %s can not be used, please use IP address instead.",
			name);
# endif
	}
}
#endif	/* !SIM */


/*
 * unpeer_name_resolved()
 *
 * Callback invoked when config_unpeers()'s DNS lookup completes.
 */
#ifdef WORKER
static void
unpeer_name_resolved(
	int			rescode,
	int			gai_errno,
	void *			context,
	const char *		name,
	const char *		service,
	const struct addrinfo *	hints,
	const struct addrinfo *	res
	)
{
	sockaddr_u	peeraddr;
	struct peer *	peer;
	u_short		af;
	const char *	fam_spec;

	(void)context;
	(void)hints;
	DPRINTF(1, ("unpeer_name_resolved(%s) rescode %d\n", name, rescode));

	if (rescode) {
		msyslog(LOG_ERR, "giving up resolving unpeer %s: %s (%d)",
			name, gai_strerror(rescode), rescode);
		return;
	}
	/*
	 * Loop through the addresses found
	 */
	for (; res != NULL; res = res->ai_next) {
		INSIST(res->ai_addrlen <= sizeof(peeraddr));
		memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
		DPRINTF(1, ("unpeer: searching for peer %s\n",
			    stoa(&peeraddr)));
		peer = findexistingpeer(&peeraddr, NULL, NULL, -1, 0);
		if (peer != NULL) {
			af = AF(&peeraddr);
			fam_spec = (AF_INET6 == af)
				       ? "(AAAA) "
				       : (AF_INET == af)
					     ? "(A) "
					     : "";
			msyslog(LOG_NOTICE, "unpeered %s %s-> %s", name,
				fam_spec, stoa(&peeraddr));
			peer_clear(peer, "GONE");
			unpeer(peer);
		}
	}
}
#endif	/* WORKER */


#ifdef FREE_CFG_T
static void
free_config_unpeers(
	config_tree *ptree
	)
{
	unpeer_node *curr_unpeer;

	if (ptree->unpeers != NULL) {
		for (;;) {
			UNLINK_FIFO(curr_unpeer, *ptree->unpeers, link);
			if (NULL == curr_unpeer)
				break;
			destroy_address_node(curr_unpeer->addr);
			free(curr_unpeer);
		}
		free(ptree->unpeers);
	}
}
#endif	/* FREE_CFG_T */


#ifndef SIM
static void
config_reset_counters(
	config_tree *ptree
	)
{
	int_node *counter_set;

	for (counter_set = HEAD_PFIFO(ptree->reset_counters);
	     counter_set != NULL;
	     counter_set = counter_set->link) {
		switch (counter_set->i) {
		default:
			DPRINTF(1, ("config_reset_counters %s (%d) invalid\n",
				    keyword(counter_set->i), counter_set->i));
			break;

		case T_Allpeers:
			peer_all_reset();
			break;

		case T_Auth:
			reset_auth_stats();
			break;

		case T_Ctl:
			ctl_clr_stats();
			break;

		case T_Io:
			io_clr_stats();
			break;

		case T_Mem:
			peer_clr_stats();
			break;

		case T_Sys:
			proto_clr_stats();
			break;

		case T_Timer:
			timer_clr_stats();
			break;
		}
	}
}
#endif	/* !SIM */


#ifdef FREE_CFG_T
static void
free_config_reset_counters(
	config_tree *ptree
	)
{
	FREE_INT_FIFO(ptree->reset_counters);
}
#endif	/* FREE_CFG_T */


#ifdef SIM
static void
config_sim(
	config_tree *ptree
	)
{
	int i;
	server_info *serv_info;
	attr_val *init_stmt;
	sim_node *sim_n;

	/* Check if a simulate block was found in the configuration code.
	 * If not, return an error and exit
	 */
	sim_n = HEAD_PFIFO(ptree->sim_details);
	if (NULL == sim_n) {
		fprintf(stderr, "ERROR!! I couldn't find a \"simulate\" block for configuring the simulator.\n");
		fprintf(stderr, "\tCheck your configuration file.\n");
		exit(1);
	}

	/* Process the initialization statements
	 * -------------------------------------
	 */
	init_stmt = HEAD_PFIFO(sim_n->init_opts);
	for (; init_stmt != NULL; init_stmt = init_stmt->link) {
		switch(init_stmt->attr) {

		case T_Beep_Delay:
			simulation.beep_delay = init_stmt->value.d;
			break;

		case T_Sim_Duration:
			simulation.end_time = init_stmt->value.d;
			break;

		default:
			fprintf(stderr,
				"Unknown simulator init token %d\n",
				init_stmt->attr);
			exit(1);
		}
	}

	/* Process the server list
	 * -----------------------
	 */
	simulation.num_of_servers = 0;
	serv_info = HEAD_PFIFO(sim_n->servers);
	for (; serv_info != NULL; serv_info = serv_info->link)
		simulation.num_of_servers++;
	simulation.servers = 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
	)
{
	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_trap(ptree);
	config_vars(ptree);

	io_open_sockets();

	config_other_modes(ptree);
	config_peers(ptree);
	config_unpeers(ptree);
	config_fudge(ptree);
	config_reset_counters(ptree);

#ifdef TEST_BLOCKING_WORKER
	{
		struct addrinfo hints;

		ZERO(hints);
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;
		getaddrinfo_sometime("www.cnn.com", "ntp", &hints,
				     INITIAL_DNS_RETRY,
				     gai_test_callback, (void *)1);
		hints.ai_family = AF_INET6;
		getaddrinfo_sometime("ipv6.google.com", "ntp", &hints,
				     INITIAL_DNS_RETRY,
				     gai_test_callback, (void *)0x600);
	}
#endif
}
#endif	/* !SIM */


#ifdef SIM
static void
config_ntpdsim(
	config_tree *ptree
	)
{
	printf("Configuring Simulator...\n");
	printf("Some ntpd-specific commands in the configuration file will be ignored.\n");

	config_tos(ptree);
	config_monitor(ptree);
	config_tinker(ptree);
	if (0)
		config_rlimit(ptree);	/* not needed for the simulator */
	config_system_opts(ptree);
	config_logconfig(ptree);
	config_vars(ptree);
	config_sim(ptree);
}
#endif /* SIM */


/*
 * config_remotely() - implements ntpd side of ntpq :config
 */
void
config_remotely(
	sockaddr_u *	remote_addr
	)
{
	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 ***/
#ifdef DEBUG
	yydebug = !!(debug >= 5);
#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
}


static void
ntpd_set_tod_using(
	const char *which
	)
{
	char line[128];

	snprintf(line, sizeof(line), "settimeofday=\"%s\"", which);
	set_sys_var(line, strlen(line) + 1, RO);
}


static char *
normal_dtoa(
	double d
	)
{
	char *	buf;
	char *	pch_e;
	char *	pch_nz;

	LIB_GETBUF(buf);
	snprintf(buf, LIB_BUFLENGTH, "%g", d);

	/* use lowercase 'e', strip any leading zeroes in exponent */
	pch_e = strchr(buf, 'e');
	if (NULL == pch_e) {
		pch_e = strchr(buf, 'E');
		if (NULL == pch_e)
			return buf;
		*pch_e = 'e';
	}
	pch_e++;
	if ('-' == *pch_e)
		pch_e++;
	pch_nz = pch_e;
	while ('0' == *pch_nz)
		pch_nz++;
	if (pch_nz == pch_e)
		return buf;
	strlcpy(pch_e, pch_nz, LIB_BUFLENGTH - (pch_e - buf));

	return buf;
}


/* FUNCTIONS COPIED FROM THE OLDER ntp_config.c
 * --------------------------------------------
 */


/*
 * get_pfxmatch - find value for prefixmatch
 * and update char * accordingly
 */
static u_int32
get_pfxmatch(
	const char **	pstr,
	struct masks *	m
	)
{
	while (m->name != NULL) {
		if (strncmp(*pstr, m->name, strlen(m->name)) == 0) {
			*pstr += strlen(m->name);
			return m->mask;
		} else {
			m++;
		}
	}
	return 0;
}

/*
 * get_match - find logmask value
 */
static u_int32
get_match(
	const char *	str,
	struct masks *	m
	)
{
	while (m->name != NULL) {
		if (strcmp(str, m->name) == 0)
			return m->mask;
		else
			m++;
	}
	return 0;
}

/*
 * get_logmask - build bitmask for ntp_syslogmask
 */
static u_int32
get_logmask(
	const char *	str
	)
{
	const char *	t;
	u_int32		offset;
	u_int32		mask;

	mask = get_match(str, logcfg_noclass_items);
	if (mask != 0)
		return mask;

	t = str;
	offset = get_pfxmatch(&t, logcfg_class);
	mask   = get_match(t, logcfg_class_items);

	if (mask)
		return mask << offset;
	else
		msyslog(LOG_ERR, "logconfig: '%s' not recognized - ignored",
			str);

	return 0;
}


#ifdef HAVE_NETINFO

/*
 * get_netinfo_config - find the nearest NetInfo domain with an ntp
 * configuration and initialize the configuration state.
 */
static struct netinfo_config_state *
get_netinfo_config(void)
{
	ni_status status;
	void *domain;
	ni_id config_dir;
	struct netinfo_config_state *config;

	if (ni_open(NULL, ".", &domain) != NI_OK) return NULL;

	while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) {
		void *next_domain;
		if (ni_open(domain, "..", &next_domain) != NI_OK) {
			ni_free(next_domain);
			break;
		}
		ni_free(domain);
		domain = next_domain;
	}
	if (status != NI_OK) {
		ni_free(domain);
		return NULL;
	}

	config = emalloc(sizeof(*config));
	config->domain = domain;
	config->config_dir = config_dir;
	config->prop_index = 0;
	config->val_index = 0;
	config->val_list = NULL;

	return config;
}


/*
 * free_netinfo_config - release NetInfo configuration state
 */
static void
free_netinfo_config(
	struct netinfo_config_state *config
	)
{
	ni_free(config->domain);
	free(config);
}


/*
 * gettokens_netinfo - return tokens from NetInfo
 */
static int
gettokens_netinfo (
	struct netinfo_config_state *config,
	char **tokenlist,
	int *ntokens
	)
{
	int prop_index = config->prop_index;
	int val_index = config->val_index;
	char **val_list = config->val_list;

	/*
	 * Iterate through each keyword and look for a property that matches it.
	 */
  again:
	if (!val_list) {
		for (; prop_index < COUNTOF(keywords); prop_index++)
		{
			ni_namelist namelist;
			struct keyword current_prop = keywords[prop_index];
			ni_index index;

			/*
			 * For each value associated in the property, we're going to return
			 * a separate line. We squirrel away the values in the config state
			 * so the next time through, we don't need to do this lookup.
			 */
			NI_INIT(&namelist);
			if (NI_OK == ni_lookupprop(config->domain,
			    &config->config_dir, current_prop.text,
			    &namelist)) {

				/* Found the property, but it has no values */
				if (namelist.ni_namelist_len == 0) continue;

				config->val_list =
				    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.
 */
#ifndef SIM
static int
getnetnum(
	const char *num,
	sockaddr_u *addr,
	int complain,
	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;

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

# ifdef ISC_PLATFORM_HAVESALEN
	addr->sa.sa_len = SIZEOF_SOCKADDR(AF(addr));
# endif
	SET_PORT(addr, NTP_PORT);

	DPRINTF(2, ("getnetnum given %s, got %s\n", num, stoa(addr)));

	return 1;
}
#endif	/* !SIM */

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

	switch (rl_what) {
# ifdef RLIMIT_MEMLOCK
	    case RLIMIT_MEMLOCK:
		/*
		 * The default RLIMIT_MEMLOCK is very low on Linux systems.
		 * Unless we increase this limit malloc calls are likely to
		 * fail if we drop root privilege.  To be useful the value
		 * has to be larger than the largest ntpd resident set size.
		 */
		DPRINTF(2, ("ntp_rlimit: MEMLOCK: %d %s\n",
			(int)(rl_value / rl_scale), rl_sstr));
		rl.rlim_cur = rl.rlim_max = rl_value;
		if (setrlimit(RLIMIT_MEMLOCK, &rl) == -1)
			msyslog(LOG_ERR, "Cannot set RLIMIT_MEMLOCK: %m");
		break;
# endif /* RLIMIT_MEMLOCK */

# ifdef RLIMIT_NOFILE
	    case RLIMIT_NOFILE:
		/*
		 * For large systems the default file descriptor limit may
		 * not be enough.
		 */
		DPRINTF(2, ("ntp_rlimit: NOFILE: %d %s\n",
			(int)(rl_value / rl_scale), rl_sstr));
		rl.rlim_cur = rl.rlim_max = rl_value;
		if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
			msyslog(LOG_ERR, "Cannot set RLIMIT_NOFILE: %m");
		break;
# endif /* RLIMIT_NOFILE */

# ifdef RLIMIT_STACK
	    case RLIMIT_STACK:
		/*
		 * Provide a way to set the stack limit to something
		 * smaller, so that we don't lock a lot of unused
		 * stack memory.
		 */
		DPRINTF(2, ("ntp_rlimit: STACK: %d %s pages\n",
			    (int)(rl_value / rl_scale), rl_sstr));
		if (-1 == getrlimit(RLIMIT_STACK, &rl)) {
			msyslog(LOG_ERR, "getrlimit(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_ERR,
					"ntp_rlimit: Cannot set RLIMIT_STACK: %m");
			}
		}
		break;
# endif /* RLIMIT_STACK */

	    default:
		INSIST(!"Unexpected setrlimit() case!");
		break;
	}
}
#endif	/* HAVE_SETRLIMIT */
