/*
 * ntpdate - set the time of day by polling one or more NTP servers
 */

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

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

#include "ntp_machine.h"
#include "ntp_fp.h"
#include "ntp.h"
#include "ntp_io.h"
#include "timevalops.h"
#include "ntpdate.h"
#include "ntp_string.h"
#include "ntp_syslog.h"
#include "ntp_select.h"
#include "ntp_stdlib.h"
#include <ssl_applink.c>

#include "isc/net.h"
#include "isc/result.h"
#include "isc/sockaddr.h"

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#ifdef HAVE_POLL_H
# include <poll.h>
#endif
#ifdef HAVE_SYS_SIGNAL_H
# include <sys/signal.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif

#include <arpa/inet.h>

#ifdef SYS_VXWORKS
# include "ioLib.h"
# include "sockLib.h"
# include "timers.h"

/* select wants a zero structure ... */
struct timeval timeout = {0,0};
#elif defined(SYS_WINNT)
/*
 * Windows does not abort a select select call if SIGALRM goes off
 * so a 200 ms timeout is needed (TIMER_HZ is 5).
 */
struct sock_timeval timeout = {0,1000000/TIMER_HZ};
#else
struct timeval timeout = {60,0};
#endif

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

#include "recvbuff.h"

#ifdef SYS_WINNT
#define TARGET_RESOLUTION 1  /* Try for 1-millisecond accuracy
				on Windows NT timers. */
#pragma comment(lib, "winmm")
isc_boolean_t ntp_port_inuse(int af, u_short port);
UINT wTimerRes;
#endif /* SYS_WINNT */

/*
 * Scheduling priority we run at
 */
#ifndef SYS_VXWORKS
# define	NTPDATE_PRIO	(-12)
#else
# define	NTPDATE_PRIO	(100)
#endif

#ifdef HAVE_TIMER_CREATE
/* POSIX TIMERS - vxWorks doesn't have itimer - casey */
static timer_t ntpdate_timerid;
#endif

/*
 * Compatibility stuff for Version 2
 */
#define NTP_MAXSKW	0x28f	/* 0.01 sec in fp format */
#define NTP_MINDIST	0x51f	/* 0.02 sec in fp format */
#define PEER_MAXDISP	(64*FP_SECOND)	/* maximum dispersion (fp 64) */
#define NTP_INFIN	15	/* max stratum, infinity a la Bellman-Ford */
#define NTP_MAXWGT	(8*FP_SECOND)	/* maximum select weight 8 seconds */
#define NTP_MAXLIST	5	/* maximum select list size */
#define PEER_SHIFT	8	/* 8 suitable for crystal time base */

/*
 * for get_systime()
 */
s_char	sys_precision;		/* local clock precision (log2 s) */

/*
 * File descriptor masks etc. for call to select
 */

int ai_fam_templ;
int nbsock;			/* the number of sockets used */
SOCKET fd[MAX_AF];
int fd_family[MAX_AF];		/* to remember the socket family */
#ifdef HAVE_POLL_H
struct pollfd fdmask[MAX_AF];
#else
fd_set fdmask;
SOCKET maxfd;
#endif
int polltest = 0;

/*
 * Initializing flag.  All async routines watch this and only do their
 * thing when it is clear.
 */
int initializing = 1;

/*
 * Alarm flag.	Set when an alarm occurs
 */
volatile int alarm_flag = 0;

/*
 * Simple query flag.
 */
int simple_query = 0;

/*
 * Unprivileged port flag.
 */
int unpriv_port = 0;

/*
 * Program name.
 */
char const *progname;

/*
 * Systemwide parameters and flags
 */
int sys_samples = DEFSAMPLES;	/* number of samples/server */
u_long sys_timeout = DEFTIMEOUT; /* timeout time, in TIMER_HZ units */
struct server *sys_servers;	/* the server list */
int sys_numservers = 0; 	/* number of servers to poll */
int sys_authenticate = 0;	/* true when authenticating */
u_int32 sys_authkey = 0;	/* set to authentication key in use */
u_long sys_authdelay = 0;	/* authentication delay */
int sys_version = NTP_VERSION;	/* version to poll with */

/*
 * The current internal time
 */
u_long current_time = 0;

/*
 * Counter for keeping track of completed servers
 */
int complete_servers = 0;

/*
 * File of encryption keys
 */

#ifndef KEYFILE
# ifndef SYS_WINNT
#define KEYFILE 	"/etc/ntp.keys"
# else
#define KEYFILE 	"%windir%\\ntp.keys"
# endif /* SYS_WINNT */
#endif /* KEYFILE */

#ifndef SYS_WINNT
const char *key_file = KEYFILE;
#else
char key_file_storage[MAX_PATH+1], *key_file ;
#endif	 /* SYS_WINNT */

/*
 * Miscellaneous flags
 */
int verbose = 0;
int always_step = 0;
int never_step = 0;

int 	ntpdatemain (int, char **);

static	void	transmit	(struct server *);
static	void	receive 	(struct recvbuf *);
static	void	server_data (struct server *, s_fp, l_fp *, u_fp);
static	void	clock_filter	(struct server *);
static	struct server *clock_select (void);
static	int clock_adjust	(void);
static	void	addserver	(char *);
static	struct server *findserver (sockaddr_u *);
		void	timer		(void);
static	void	init_alarm	(void);
#ifndef SYS_WINNT
static	RETSIGTYPE alarming (int);
#endif /* SYS_WINNT */
static	void	init_io 	(void);
static	void	sendpkt 	(sockaddr_u *, struct pkt *, int);
void	input_handler	(void);

static	int l_adj_systime	(l_fp *);
static	int l_step_systime	(l_fp *);

static	void	printserver (struct server *, FILE *);

#ifdef SYS_WINNT
int 	on = 1;
WORD	wVersionRequested;
WSADATA	wsaData;
#endif /* SYS_WINNT */

#ifdef NO_MAIN_ALLOWED
CALL(ntpdate,"ntpdate",ntpdatemain);

void clear_globals()
{
  /*
   * Debugging flag
   */
  debug = 0;

  ntp_optind = 0;
  /*
   * Initializing flag.  All async routines watch this and only do their
   * thing when it is clear.
   */
  initializing = 1;

  /*
   * Alarm flag.  Set when an alarm occurs
   */
  alarm_flag = 0;

  /*
   * Simple query flag.
   */
  simple_query = 0;

  /*
   * Unprivileged port flag.
   */
  unpriv_port = 0;

  /*
   * Systemwide parameters and flags
   */
  sys_numservers = 0;	  /* number of servers to poll */
  sys_authenticate = 0;   /* true when authenticating */
  sys_authkey = 0;	   /* set to authentication key in use */
  sys_authdelay = 0;   /* authentication delay */
  sys_version = NTP_VERSION;  /* version to poll with */

  /*
   * The current internal time
   */
  current_time = 0;

  /*
   * Counter for keeping track of completed servers
   */
  complete_servers = 0;
  verbose = 0;
  always_step = 0;
  never_step = 0;
}
#endif

#ifdef HAVE_NETINFO
static ni_namelist *getnetinfoservers (void);
#endif

/*
 * Main program.  Initialize us and loop waiting for I/O and/or
 * timer expiries.
 */
#ifndef NO_MAIN_ALLOWED
int
main(
	int argc,
	char *argv[]
	)
{
	return ntpdatemain (argc, argv);
}
#endif /* NO_MAIN_ALLOWED */

int
ntpdatemain (
	int argc,
	char *argv[]
	)
{
	int was_alarmed;
	int tot_recvbufs;
	struct recvbuf *rbuf;
	l_fp tmp;
	int errflg;
	int c;
	int nfound;

#ifdef HAVE_NETINFO
	ni_namelist *netinfoservers;
#endif
#ifdef SYS_WINNT
	key_file = key_file_storage;

	if (!ExpandEnvironmentStrings(KEYFILE, key_file, MAX_PATH))
		msyslog(LOG_ERR, "ExpandEnvironmentStrings(KEYFILE) failed: %m");

	ssl_applink();
#endif /* SYS_WINNT */

#ifdef NO_MAIN_ALLOWED
	clear_globals();
#endif

	init_lib();	/* sets up ipv4_works, ipv6_works */

	/* Check to see if we have IPv6. Otherwise default to IPv4 */
	if (!ipv6_works)
		ai_fam_templ = AF_INET;

	errflg = 0;
	progname = argv[0];
	syslogit = 0;

	/*
	 * Decode argument list
	 */
	while ((c = ntp_getopt(argc, argv, "46a:bBde:k:o:p:qst:uv")) != EOF)
		switch (c)
		{
		case '4':
			ai_fam_templ = AF_INET;
			break;
		case '6':
			ai_fam_templ = AF_INET6;
			break;
		case 'a':
			c = atoi(ntp_optarg);
			sys_authenticate = 1;
			sys_authkey = c;
			break;
		case 'b':
			always_step++;
			never_step = 0;
			break;
		case 'B':
			never_step++;
			always_step = 0;
			break;
		case 'd':
			++debug;
			break;
		case 'e':
			if (!atolfp(ntp_optarg, &tmp)
			|| tmp.l_ui != 0) {
				(void) fprintf(stderr,
					   "%s: encryption delay %s is unlikely\n",
					   progname, ntp_optarg);
				errflg++;
			} else {
				sys_authdelay = tmp.l_uf;
			}
			break;
		case 'k':
			key_file = ntp_optarg;
			break;
		case 'o':
			sys_version = atoi(ntp_optarg);
			break;
		case 'p':
			c = atoi(ntp_optarg);
			if (c <= 0 || c > NTP_SHIFT) {
				(void) fprintf(stderr,
					   "%s: number of samples (%d) is invalid\n",
					   progname, c);
				errflg++;
			} else {
				sys_samples = c;
			}
			break;
		case 'q':
			simple_query = 1;
			break;
		case 's':
			syslogit = 1;
			break;
		case 't':
			if (!atolfp(ntp_optarg, &tmp)) {
				(void) fprintf(stderr,
					   "%s: timeout %s is undecodeable\n",
					   progname, ntp_optarg);
				errflg++;
			} else {
				sys_timeout = ((LFPTOFP(&tmp) * TIMER_HZ)
					   + 0x8000) >> 16;
				sys_timeout = max(sys_timeout, MINTIMEOUT);
			}
			break;
		case 'v':
			verbose = 1;
			break;
		case 'u':
			unpriv_port = 1;
			break;
		case '?':
			++errflg;
			break;
		default:
			break;
	    }
	
	if (errflg) {
		(void) fprintf(stderr,
		    "usage: %s [-46bBdqsuv] [-a key#] [-e delay] [-k file] [-p samples] [-o version#] [-t timeo] server ...\n",
		    progname);
		exit(2);
	}

	if (debug || simple_query) {
#ifdef HAVE_SETVBUF
		static char buf[BUFSIZ];
		setvbuf(stdout, buf, _IOLBF, BUFSIZ);
#else
		setlinebuf(stdout);
#endif
	}

	/*
	 * Logging.  Open the syslog if we have to
	 */
	if (syslogit) {
#if !defined (SYS_WINNT) && !defined (SYS_VXWORKS) && !defined SYS_CYGWIN32
# ifndef	LOG_DAEMON
		openlog("ntpdate", LOG_PID);
# else

#  ifndef	LOG_NTP
#	define	LOG_NTP LOG_DAEMON
#  endif
		openlog("ntpdate", LOG_PID | LOG_NDELAY, LOG_NTP);
		if (debug)
			setlogmask(LOG_UPTO(LOG_DEBUG));
		else
			setlogmask(LOG_UPTO(LOG_INFO));
# endif /* LOG_DAEMON */
#endif	/* SYS_WINNT */
	}

	if (debug || verbose)
		msyslog(LOG_NOTICE, "%s", Version);

	/*
	 * Add servers we are going to be polling
	 */
#ifdef HAVE_NETINFO
	netinfoservers = getnetinfoservers();
#endif

	for ( ; ntp_optind < argc; ntp_optind++)
		addserver(argv[ntp_optind]);

#ifdef HAVE_NETINFO
	if (netinfoservers) {
		if ( netinfoservers->ni_namelist_len &&
		    *netinfoservers->ni_namelist_val ) {
			u_int servercount = 0;
			while (servercount < netinfoservers->ni_namelist_len) {
				if (debug) msyslog(LOG_DEBUG,
						   "Adding time server %s from NetInfo configuration.",
						   netinfoservers->ni_namelist_val[servercount]);
				addserver(netinfoservers->ni_namelist_val[servercount++]);
			}
		}
		ni_namelist_free(netinfoservers);
		free(netinfoservers);
	}
#endif

	if (sys_numservers == 0) {
		msyslog(LOG_ERR, "no servers can be used, exiting");
		exit(1);
	}

	/*
	 * Initialize the time of day routines and the I/O subsystem
	 */
	if (sys_authenticate) {
		init_auth();
		if (!authreadkeys(key_file)) {
			msyslog(LOG_ERR, "no key file <%s>, exiting", key_file);
			exit(1);
		}
		authtrust(sys_authkey, 1);
		if (!authistrusted(sys_authkey)) {
			msyslog(LOG_ERR, "authentication key %lu unknown",
				(unsigned long) sys_authkey);
			exit(1);
		}
	}
	init_io();
	init_alarm();

	/*
	 * Set the priority.
	 */
#ifdef SYS_VXWORKS
	taskPrioritySet( taskIdSelf(), NTPDATE_PRIO);
#endif
#if defined(HAVE_ATT_NICE)
	nice (NTPDATE_PRIO);
#endif
#if defined(HAVE_BSD_NICE)
	(void) setpriority(PRIO_PROCESS, 0, NTPDATE_PRIO);
#endif


	initializing = 0;
	was_alarmed = 0;

	while (complete_servers < sys_numservers) {
#ifdef HAVE_POLL_H
		struct pollfd* rdfdes;
		rdfdes = fdmask;
#else
		fd_set rdfdes;
		rdfdes = fdmask;
#endif

		if (alarm_flag) {		/* alarmed? */
			was_alarmed = 1;
			alarm_flag = 0;
		}
		tot_recvbufs = full_recvbuffs();	/* get received buffers */

		if (!was_alarmed && tot_recvbufs == 0) {
			/*
			 * Nothing to do.	 Wait for something.
			 */
#ifdef HAVE_POLL_H
			nfound = poll(rdfdes, (unsigned int)nbsock, timeout.tv_sec * 1000);

#else
			nfound = select(maxfd, &rdfdes, NULL, NULL,
					&timeout);
#endif
			if (nfound > 0)
				input_handler();
			else if (nfound == SOCKET_ERROR)
			{
#ifndef SYS_WINNT
				if (errno != EINTR)
#else
				if (WSAGetLastError() != WSAEINTR)
#endif
					msyslog(LOG_ERR,
#ifdef HAVE_POLL_H
						"poll() error: %m"
#else
						"select() error: %m"
#endif
						);
			} else if (errno != 0) {
#ifndef SYS_VXWORKS
				msyslog(LOG_DEBUG,
#ifdef HAVE_POLL_H
					"poll(): nfound = %d, error: %m",
#else
					"select(): nfound = %d, error: %m",
#endif
					nfound);
#endif
			}
			if (alarm_flag) {		/* alarmed? */
				was_alarmed = 1;
				alarm_flag = 0;
			}
			tot_recvbufs = full_recvbuffs();	/* get received buffers */
		}

		/*
		 * Out here, signals are unblocked.  Call receive
		 * procedure for each incoming packet.
		 */
		rbuf = get_full_recv_buffer();
		while (rbuf != NULL)
		{
			receive(rbuf);
			freerecvbuf(rbuf);
			rbuf = get_full_recv_buffer();
		}

		/*
		 * Call timer to process any timeouts
		 */
		if (was_alarmed) {
			timer();
			was_alarmed = 0;
		}

		/*
		 * Go around again
		 */
	}

	/*
	 * When we get here we've completed the polling of all servers.
	 * Adjust the clock, then exit.
	 */
#ifdef SYS_WINNT
	WSACleanup();
#endif
#ifdef SYS_VXWORKS
	close (fd);
	timer_delete(ntpdate_timerid);
#endif

	return clock_adjust();
}


/*
 * transmit - transmit a packet to the given server, or mark it completed.
 *		This is called by the timeout routine and by the receive
 *		procedure.
 */
static void
transmit(
	register struct server *server
	)
{
	struct pkt xpkt;

	if (debug)
		printf("transmit(%s)\n", stoa(&server->srcadr));

	if (server->filter_nextpt < server->xmtcnt) {
		l_fp ts;
		/*
		 * Last message to this server timed out.  Shift
		 * zeros into the filter.
		 */
		L_CLR(&ts);
		server_data(server, 0, &ts, 0);
	}

	if ((int)server->filter_nextpt >= sys_samples) {
		/*
		 * Got all the data we need.  Mark this guy
		 * completed and return.
		 */
		server->event_time = 0;
		complete_servers++;
		return;
	}

	/*
	 * If we're here, send another message to the server.  Fill in
	 * the packet and let 'er rip.
	 */
	xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
					 sys_version, MODE_CLIENT);
	xpkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
	xpkt.ppoll = NTP_MINPOLL;
	xpkt.precision = NTPDATE_PRECISION;
	xpkt.rootdelay = htonl(NTPDATE_DISTANCE);
	xpkt.rootdisp = htonl(NTPDATE_DISP);
	xpkt.refid = htonl(NTPDATE_REFID);
	L_CLR(&xpkt.reftime);
	L_CLR(&xpkt.org);
	L_CLR(&xpkt.rec);

	/*
	 * Determine whether to authenticate or not.	If so,
	 * fill in the extended part of the packet and do it.
	 * If not, just timestamp it and send it away.
	 */
	if (sys_authenticate) {
		size_t len;

		xpkt.exten[0] = htonl(sys_authkey);
		get_systime(&server->xmt);
		L_ADDUF(&server->xmt, sys_authdelay);
		HTONL_FP(&server->xmt, &xpkt.xmt);
		len = authencrypt(sys_authkey, (u_int32 *)&xpkt, LEN_PKT_NOMAC);
		sendpkt(&server->srcadr, &xpkt, (int)(LEN_PKT_NOMAC + len));

		if (debug > 1)
			printf("transmit auth to %s\n",
			   stoa(&server->srcadr));
	} else {
		get_systime(&(server->xmt));
		HTONL_FP(&server->xmt, &xpkt.xmt);
		sendpkt(&server->srcadr, &xpkt, LEN_PKT_NOMAC);

		if (debug > 1)
			printf("transmit to %s\n", stoa(&server->srcadr));
	}

	/*
	 * Update the server timeout and transmit count
	 */
	server->event_time = current_time + sys_timeout;
	server->xmtcnt++;
}


/*
 * receive - receive and process an incoming frame
 */
static void
receive(
	struct recvbuf *rbufp
	)
{
	register struct pkt *rpkt;
	register struct server *server;
	register s_fp di;
	l_fp t10, t23, tmp;
	l_fp org;
	l_fp rec;
	l_fp ci;
	int has_mac;
	int is_authentic;

	if (debug)
		printf("receive(%s)\n", stoa(&rbufp->recv_srcadr));
	/*
	 * Check to see if the packet basically looks like something
	 * intended for us.
	 */
	if (rbufp->recv_length == LEN_PKT_NOMAC)
		has_mac = 0;
	else if (rbufp->recv_length >= (int)LEN_PKT_NOMAC)
		has_mac = 1;
	else {
		if (debug)
			printf("receive: packet length %d\n",
			   rbufp->recv_length);
		return; 		/* funny length packet */
	}

	rpkt = &(rbufp->recv_pkt);
	if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
		PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
		return;
	}

	if ((PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER
		 && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE)
		|| rpkt->stratum >= STRATUM_UNSPEC) {
		if (debug)
			printf("receive: mode %d stratum %d\n",
			   PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
		return;
	}

	/*
	 * So far, so good.  See if this is from a server we know.
	 */
	server = findserver(&(rbufp->recv_srcadr));
	if (server == NULL) {
		if (debug)
			printf("receive: server not found\n");
		return;
	}

	/*
	 * Decode the org timestamp and make sure we're getting a response
	 * to our last request.
	 */
	NTOHL_FP(&rpkt->org, &org);
	if (!L_ISEQU(&org, &server->xmt)) {
		if (debug)
			printf("receive: pkt.org and peer.xmt differ\n");
		return;
	}

	/*
	 * Check out the authenticity if we're doing that.
	 */
	if (!sys_authenticate)
		is_authentic = 1;
	else {
		is_authentic = 0;

		if (debug > 3)
			printf("receive: rpkt keyid=%ld sys_authkey=%ld decrypt=%ld\n",
			   (long int)ntohl(rpkt->exten[0]), (long int)sys_authkey,
			   (long int)authdecrypt(sys_authkey, (u_int32 *)rpkt,
				LEN_PKT_NOMAC, (size_t)(rbufp->recv_length - LEN_PKT_NOMAC)));

		if (has_mac && ntohl(rpkt->exten[0]) == sys_authkey &&
			authdecrypt(sys_authkey, (u_int32 *)rpkt, LEN_PKT_NOMAC,
			(size_t)(rbufp->recv_length - LEN_PKT_NOMAC)))
			is_authentic = 1;
		if (debug)
			printf("receive: authentication %s\n",
			   is_authentic ? "passed" : "failed");
	}
	server->trust <<= 1;
	if (!is_authentic)
		server->trust |= 1;

	/*
	 * Check for a KoD (rate limiting) response, cease and decist.
	 */
	if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode) &&
	    STRATUM_PKT_UNSPEC == rpkt->stratum &&
	    !memcmp("RATE", &rpkt->refid, 4)) {
		msyslog(LOG_ERR, "%s rate limit response from server.",
			stoa(&rbufp->recv_srcadr));
		server->event_time = 0;
		complete_servers++;
		return;
	}

	/*
	 * Looks good.	Record info from the packet.
	 */
	server->leap = PKT_LEAP(rpkt->li_vn_mode);
	server->stratum = PKT_TO_STRATUM(rpkt->stratum);
	server->precision = rpkt->precision;
	server->rootdelay = ntohl(rpkt->rootdelay);
	server->rootdisp = ntohl(rpkt->rootdisp);
	server->refid = rpkt->refid;
	NTOHL_FP(&rpkt->reftime, &server->reftime);
	NTOHL_FP(&rpkt->rec, &rec);
	NTOHL_FP(&rpkt->xmt, &server->org);

	/*
	 * Make sure the server is at least somewhat sane.	If not, try
	 * again.
	 */
	if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) {
		server->event_time = current_time + sys_timeout;
		return;
	}

	/*
	 * Calculate the round trip delay (di) and the clock offset (ci).
	 * We use the equations (reordered from those in the spec):
	 *
	 * d = (t2 - t3) - (t1 - t0)
	 * c = ((t2 - t3) + (t1 - t0)) / 2
	 */
	t10 = server->org;		/* pkt.xmt == t1 */
	L_SUB(&t10, &rbufp->recv_time); /* recv_time == t0*/

	t23 = rec;			/* pkt.rec == t2 */
	L_SUB(&t23, &org);		/* pkt->org == t3 */

	/* now have (t2 - t3) and (t0 - t1).	Calculate (ci) and (di) */
	/*
	 * Calculate (ci) = ((t1 - t0) / 2) + ((t2 - t3) / 2)
	 * For large offsets this may prevent an overflow on '+'
	 */
	ci = t10;
	L_RSHIFT(&ci);
	tmp = t23;
	L_RSHIFT(&tmp);
	L_ADD(&ci, &tmp);

	/*
	 * Calculate di in t23 in full precision, then truncate
	 * to an s_fp.
	 */
	L_SUB(&t23, &t10);
	di = LFPTOFP(&t23);

	if (debug > 3)
		printf("offset: %s, delay %s\n", lfptoa(&ci, 6), fptoa(di, 5));

	di += (FP_SECOND >> (-(int)NTPDATE_PRECISION))
		+ (FP_SECOND >> (-(int)server->precision)) + NTP_MAXSKW;

	if (di <= 0) {		/* value still too raunchy to use? */
		L_CLR(&ci);
		di = 0;
	} else {
		di = max(di, NTP_MINDIST);
	}

	/*
	 * Shift this data in, then schedule another transmit.
	 */
	server_data(server, (s_fp) di, &ci, 0);

	if ((int)server->filter_nextpt >= sys_samples) {
		/*
		 * Got all the data we need.  Mark this guy
		 * completed and return.
		 */
		server->event_time = 0;
		complete_servers++;
		return;
	}

	server->event_time = current_time + sys_timeout;
}


/*
 * server_data - add a sample to the server's filter registers
 */
static void
server_data(
	register struct server *server,
	s_fp d,
	l_fp *c,
	u_fp e
	)
{
	u_short i;

	i = server->filter_nextpt;
	if (i < NTP_SHIFT) {
		server->filter_delay[i] = d;
		server->filter_offset[i] = *c;
		server->filter_soffset[i] = LFPTOFP(c);
		server->filter_error[i] = e;
		server->filter_nextpt = (u_short)(i + 1);
	}
}


/*
 * clock_filter - determine a server's delay, dispersion and offset
 */
static void
clock_filter(
	register struct server *server
	)
{
	register int i, j;
	int ord[NTP_SHIFT];

	/*
	 * Sort indices into increasing delay order
	 */
	for (i = 0; i < sys_samples; i++)
		ord[i] = i;

	for (i = 0; i < (sys_samples-1); i++) {
		for (j = i+1; j < sys_samples; j++) {
			if (server->filter_delay[ord[j]] == 0)
				continue;
			if (server->filter_delay[ord[i]] == 0
				|| (server->filter_delay[ord[i]]
				> server->filter_delay[ord[j]])) {
				register int tmp;

				tmp = ord[i];
				ord[i] = ord[j];
				ord[j] = tmp;
			}
		}
	}

	/*
	 * Now compute the dispersion, and assign values to delay and
	 * offset.	If there are no samples in the register, delay and
	 * offset go to zero and dispersion is set to the maximum.
	 */
	if (server->filter_delay[ord[0]] == 0) {
		server->delay = 0;
		L_CLR(&server->offset);
		server->soffset = 0;
		server->dispersion = PEER_MAXDISP;
	} else {
		register s_fp d;

		server->delay = server->filter_delay[ord[0]];
		server->offset = server->filter_offset[ord[0]];
		server->soffset = LFPTOFP(&server->offset);
		server->dispersion = 0;
		for (i = 1; i < sys_samples; i++) {
			if (server->filter_delay[ord[i]] == 0)
				d = PEER_MAXDISP;
			else {
				d = server->filter_soffset[ord[i]]
					- server->filter_soffset[ord[0]];
				if (d < 0)
					d = -d;
				if (d > PEER_MAXDISP)
					d = PEER_MAXDISP;
			}
			/*
			 * XXX This *knows* PEER_FILTER is 1/2
			 */
			server->dispersion += (u_fp)(d) >> i;
		}
	}
	/*
	 * We're done
	 */
}


/*
 * clock_select - select the pick-of-the-litter clock from the samples
 *		  we've got.
 */
static struct server *
clock_select(void)
{
	struct server *server;
	u_int nlist;
	s_fp d;
	u_int count;
	u_int i;
	u_int j;
	u_int k;
	int n;
	s_fp local_threshold;
	struct server *server_list[NTP_MAXCLOCK];
	u_fp server_badness[NTP_MAXCLOCK];
	struct server *sys_server;

	/*
	 * This first chunk of code is supposed to go through all
	 * servers we know about to find the NTP_MAXLIST servers which
	 * are most likely to succeed.	We run through the list
	 * doing the sanity checks and trying to insert anyone who
	 * looks okay.	We are at all times aware that we should
	 * only keep samples from the top two strata and we only need
	 * NTP_MAXLIST of them.
	 */
	nlist = 0;	/* none yet */
	for (server = sys_servers; server != NULL; server = server->next_server) {
		if (server->delay == 0) {
			if (debug)
				printf("%s: Server dropped: no data\n", ntoa(&server->srcadr));
			continue;	/* no data */
		}
		if (server->stratum > NTP_INFIN) {
			if (debug)
				printf("%s: Server dropped: strata too high\n", ntoa(&server->srcadr));
			continue;	/* stratum no good */
		}
		if (server->delay > NTP_MAXWGT) {
			if (debug)
				printf("%s: Server dropped: server too far away\n", 
					ntoa(&server->srcadr));
			continue;	/* too far away */
		}
		if (server->leap == LEAP_NOTINSYNC) {
			if (debug)
				printf("%s: Server dropped: Leap not in sync\n", ntoa(&server->srcadr));
			continue;	/* he's in trouble */
		}
		if (!L_ISHIS(&server->org, &server->reftime)) {
			if (debug)
				printf("%s: Server dropped: server is very broken\n", 
				       ntoa(&server->srcadr));
			continue;	/* very broken host */
		}
		if ((server->org.l_ui - server->reftime.l_ui)
		    >= NTP_MAXAGE) {
			if (debug)
				printf("%s: Server dropped: Server has gone too long without sync\n", 
				       ntoa(&server->srcadr));
			continue;	/* too long without sync */
		}
		if (server->trust != 0) {
			if (debug)
				printf("%s: Server dropped: Server is untrusted\n",
				       ntoa(&server->srcadr));
			continue;
		}

		/*
		 * This one seems sane.  Find where he belongs
		 * on the list.
		 */
		d = server->dispersion + server->dispersion;
		for (i = 0; i < nlist; i++)
			if (server->stratum <= server_list[i]->stratum)
			break;
		for ( ; i < nlist; i++) {
			if (server->stratum < server_list[i]->stratum)
				break;
			if (d < (s_fp) server_badness[i])
				break;
		}

		/*
		 * If i points past the end of the list, this
		 * guy is a loser, else stick him in.
		 */
		if (i >= NTP_MAXLIST)
			continue;
		for (j = nlist; j > i; j--)
			if (j < NTP_MAXLIST) {
				server_list[j] = server_list[j-1];
				server_badness[j]
					= server_badness[j-1];
			}

		server_list[i] = server;
		server_badness[i] = d;
		if (nlist < NTP_MAXLIST)
			nlist++;
	}

	/*
	 * Got the five-or-less best.	 Cut the list where the number of
	 * strata exceeds two.
	 */
	count = 0;
	for (i = 1; i < nlist; i++)
		if (server_list[i]->stratum > server_list[i-1]->stratum) {
			count++;
			if (2 == count) {
				nlist = i;
				break;
			}
		}

	/*
	 * Whew!  What we should have by now is 0 to 5 candidates for
	 * the job of syncing us.  If we have none, we're out of luck.
	 * If we have one, he's a winner.  If we have more, do falseticker
	 * detection.
	 */

	if (0 == nlist)
		sys_server = NULL;
	else if (1 == nlist) {
		sys_server = server_list[0];
	} else {
		/*
		 * Re-sort by stratum, bdelay estimate quality and
		 * server.delay.
		 */
		for (i = 0; i < nlist-1; i++)
			for (j = i+1; j < nlist; j++) {
				if (server_list[i]->stratum <
				    server_list[j]->stratum)
					/* already sorted by stratum */
					break;
				if (server_list[i]->delay <
				    server_list[j]->delay)
					continue;
				server = server_list[i];
				server_list[i] = server_list[j];
				server_list[j] = server;
			}

		/*
		 * Calculate the fixed part of the dispersion limit
		 */
		local_threshold = (FP_SECOND >> (-(int)NTPDATE_PRECISION))
			+ NTP_MAXSKW;

		/*
		 * Now drop samples until we're down to one.
		 */
		while (nlist > 1) {
			for (k = 0; k < nlist; k++) {
				server_badness[k] = 0;
				for (j = 0; j < nlist; j++) {
					if (j == k) /* with self? */
						continue;
					d = server_list[j]->soffset -
					    server_list[k]->soffset;
					if (d < 0)	/* abs value */
						d = -d;
					/*
					 * XXX This code *knows* that
					 * NTP_SELECT is 3/4
					 */
					for (i = 0; i < j; i++)
						d = (d>>1) + (d>>2);
					server_badness[k] += d;
				}
			}

			/*
			 * We now have an array of nlist badness
			 * coefficients.	Find the badest.  Find
			 * the minimum precision while we're at
			 * it.
			 */
			i = 0;
			n = server_list[0]->precision;;
			for (j = 1; j < nlist; j++) {
				if (server_badness[j] >= server_badness[i])
					i = j;
				if (n > server_list[j]->precision)
					n = server_list[j]->precision;
			}

			/*
			 * i is the index of the server with the worst
			 * dispersion.	If his dispersion is less than
			 * the threshold, stop now, else delete him and
			 * continue around again.
			 */
			if ( (s_fp) server_badness[i] < (local_threshold
							 + (FP_SECOND >> (-n))))
				break;
			for (j = i + 1; j < nlist; j++)
				server_list[j-1] = server_list[j];
			nlist--;
		}

		/*
		 * What remains is a list of less than 5 servers.  Take
		 * the best.
		 */
		sys_server = server_list[0];
	}

	/*
	 * That's it.  Return our server.
	 */
	return sys_server;
}


/*
 * clock_adjust - process what we've received, and adjust the time
 *		 if we got anything decent.
 */
static int
clock_adjust(void)
{
	register struct server *sp, *server;
	int dostep;

	for (sp = sys_servers; sp != NULL; sp = sp->next_server)
		clock_filter(sp);
	server = clock_select();

	if (debug || simple_query) {
		for (sp = sys_servers; sp != NULL; sp = sp->next_server)
			printserver(sp, stdout);
	}

	if (server == 0) {
		msyslog(LOG_ERR,
			"no server suitable for synchronization found");
		return(1);
	}

	if (always_step) {
		dostep = 1;
	} else if (never_step) {
		dostep = 0;
	} else {
		/* [Bug 3023] get absolute difference, avoiding signed
		 * integer overflow like hell.
		 */
		u_fp absoffset;
		if (server->soffset < 0)
			absoffset = 1u + (u_fp)(-(server->soffset + 1));
		else
			absoffset = (u_fp)server->soffset;
		dostep = (absoffset >= NTPDATE_THRESHOLD);
	}

	if (dostep) {
		if (simple_query || debug || l_step_systime(&server->offset)){
			msyslog(LOG_NOTICE, "step time server %s offset %s sec",
				stoa(&server->srcadr),
				lfptoa(&server->offset, 6));
		}
	} else {
#ifndef SYS_WINNT
		if (simple_query || l_adj_systime(&server->offset)) {
			msyslog(LOG_NOTICE, "adjust time server %s offset %s sec",
				stoa(&server->srcadr),
				lfptoa(&server->offset, 6));
		}
#else
		/* The NT SetSystemTimeAdjustment() call achieves slewing by
		 * changing the clock frequency. This means that we cannot specify
		 * it to slew the clock by a definite amount and then stop like
		 * the Unix adjtime() routine. We can technically adjust the clock
		 * frequency, have ntpdate sleep for a while, and then wake
		 * up and reset the clock frequency, but this might cause some
		 * grief if the user attempts to run ntpd immediately after
		 * ntpdate and the socket is in use.
		 */
		printf("\nThe -b option is required by ntpdate on Windows NT platforms\n");
		exit(1);
#endif /* SYS_WINNT */
	}
	return(0);
}


/*
 * is_unreachable - check to see if we have a route to given destination
 *		    (non-blocking).
 */
static int
is_reachable (sockaddr_u *dst)
{
	SOCKET sockfd;

	sockfd = socket(AF(dst), SOCK_DGRAM, 0);
	if (sockfd == -1) {
		return 0;
	}

	if (connect(sockfd, &dst->sa, SOCKLEN(dst))) {
		closesocket(sockfd);
		return 0;
	}
	closesocket(sockfd);
	return 1;
}



/* XXX ELIMINATE: merge BIG slew into adj_systime in lib/systime.c */
/*
 * addserver - determine a server's address and allocate a new structure
 *		for it.
 */
static void
addserver(
	char *serv
	)
{
	register struct server *server;
	/* Address infos structure to store result of getaddrinfo */
	struct addrinfo *addrResult, *ptr;
	/* Address infos structure to store hints for getaddrinfo */
	struct addrinfo hints;
	/* Error variable for getaddrinfo */
	int error;
	/* Service name */
	char service[5];
	sockaddr_u addr;

	strlcpy(service, "ntp", sizeof(service));

	/* Get host address. Looking for UDP datagram connection. */
	ZERO(hints);
	hints.ai_family = ai_fam_templ;
	hints.ai_socktype = SOCK_DGRAM;

#ifdef DEBUG
	if (debug)
		printf("Looking for host %s and service %s\n", serv, service);
#endif

	error = getaddrinfo(serv, service, &hints, &addrResult);
	if (error != 0) {
		/* Conduct more refined error analysis */
		if (error == EAI_FAIL || error == EAI_AGAIN){
			/* Name server is unusable. Exit after failing on the
			   first server, in order to shorten the timeout caused
			   by waiting for resolution of several servers */
			fprintf(stderr, "Exiting, name server cannot be used: %s (%d)",
				gai_strerror(error), error);
			msyslog(LOG_ERR, "name server cannot be used: %s (%d)",
				gai_strerror(error), error);
			exit(1);
		}
		fprintf(stderr, "Error resolving %s: %s (%d)\n", serv,
			gai_strerror(error), error);
		msyslog(LOG_ERR, "Can't find host %s: %s (%d)", serv,
			gai_strerror(error), error);
		return;
	}
#ifdef DEBUG
	if (debug) {
		ZERO(addr);
		INSIST(addrResult->ai_addrlen <= sizeof(addr));
		memcpy(&addr, addrResult->ai_addr, addrResult->ai_addrlen);
		fprintf(stderr, "host found : %s\n", stohost(&addr));
	}
#endif

	/* We must get all returned server in case the first one fails */
	for (ptr = addrResult; ptr != NULL; ptr = ptr->ai_next) {
		ZERO(addr);
		INSIST(ptr->ai_addrlen <= sizeof(addr));
		memcpy(&addr, ptr->ai_addr, ptr->ai_addrlen);
		if (is_reachable(&addr)) {
			server = emalloc_zero(sizeof(*server));
			memcpy(&server->srcadr, ptr->ai_addr, ptr->ai_addrlen);
			server->event_time = ++sys_numservers;
			if (sys_servers == NULL)
				sys_servers = server;
			else {
				struct server *sp;

				for (sp = sys_servers; sp->next_server != NULL;
				     sp = sp->next_server)
					/* empty */;
				sp->next_server = server;
			}
		}
	}

	freeaddrinfo(addrResult);
}


/*
 * findserver - find a server in the list given its address
 * ***(For now it isn't totally AF-Independant, to check later..)
 */
static struct server *
findserver(
	sockaddr_u *addr
	)
{
	struct server *server;
	struct server *mc_server;

	mc_server = NULL;
	if (SRCPORT(addr) != NTP_PORT)
		return 0;

	for (server = sys_servers; server != NULL; 
	     server = server->next_server) {
		if (SOCK_EQ(addr, &server->srcadr))
			return server;

		if (AF(addr) == AF(&server->srcadr)) {
			if (IS_MCAST(&server->srcadr))
				mc_server = server;
		}
	}

	if (mc_server != NULL) {	

		struct server *sp;

		if (mc_server->event_time != 0) {
			mc_server->event_time = 0;
			complete_servers++;
		}

		server = emalloc_zero(sizeof(*server));

		server->srcadr = *addr;

		server->event_time = ++sys_numservers;

		for (sp = sys_servers; sp->next_server != NULL;
		     sp = sp->next_server)
			/* empty */;
		sp->next_server = server;
		transmit(server);
	}
	return NULL;
}


/*
 * timer - process a timer interrupt
 */
void
timer(void)
{
	struct server *server;

	/*
	 * Bump the current idea of the time
	 */
	current_time++;

	/*
	 * Search through the server list looking for guys
	 * who's event timers have expired.  Give these to
	 * the transmit routine.
	 */
	for (server = sys_servers; server != NULL; 
	     server = server->next_server) {
		if (server->event_time != 0
		    && server->event_time <= current_time)
			transmit(server);
	}
}


/*
 * The code duplication in the following subroutine sucks, but
 * we need to appease ansi2knr.
 */

#ifndef SYS_WINNT
/*
 * alarming - record the occurance of an alarm interrupt
 */
static RETSIGTYPE
alarming(
	int sig
	)
{
	alarm_flag++;
}
#else	/* SYS_WINNT follows */
void CALLBACK 
alarming(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
	UNUSED_ARG(uTimerID); UNUSED_ARG(uMsg); UNUSED_ARG(dwUser);
	UNUSED_ARG(dw1); UNUSED_ARG(dw2);

	alarm_flag++;
}

static void
callTimeEndPeriod(void)
{
	timeEndPeriod( wTimerRes );
	wTimerRes = 0;
}
#endif /* SYS_WINNT */


/*
 * init_alarm - set up the timer interrupt
 */
static void
init_alarm(void)
{
#ifndef SYS_WINNT
# ifdef HAVE_TIMER_CREATE
	struct itimerspec its;
# else
	struct itimerval itv;
# endif
#else	/* SYS_WINNT follows */
	TIMECAPS tc;
	UINT wTimerID;
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;
	DWORD dwUser = 0;
#endif /* SYS_WINNT */

	alarm_flag = 0;

#ifndef SYS_WINNT
# ifdef HAVE_TIMER_CREATE
	alarm_flag = 0;
	/* this code was put in as setitimer() is non existant this us the
	 * POSIX "equivalents" setup - casey
	 */
	/* ntpdate_timerid is global - so we can kill timer later */
	if (timer_create (CLOCK_REALTIME, NULL, &ntpdate_timerid) ==
#  ifdef SYS_VXWORKS
		ERROR
#  else
		-1
#  endif
		)
	{
		fprintf (stderr, "init_alarm(): timer_create (...) FAILED\n");
		return;
	}

	/*	TIMER_HZ = (5)
	 * Set up the alarm interrupt.	The first comes 1/(2*TIMER_HZ)
	 * seconds from now and they continue on every 1/TIMER_HZ seconds.
	 */
	signal_no_reset(SIGALRM, alarming);
	its.it_interval.tv_sec = 0;
	its.it_value.tv_sec = 0;
	its.it_interval.tv_nsec = 1000000000/TIMER_HZ;
	its.it_value.tv_nsec = 1000000000/(TIMER_HZ<<1);
	timer_settime(ntpdate_timerid, 0 /* !TIMER_ABSTIME */, &its, NULL);
# else	/* !HAVE_TIMER_CREATE follows */
	/*
	 * Set up the alarm interrupt.	The first comes 1/(2*TIMER_HZ)
	 * seconds from now and they continue on every 1/TIMER_HZ seconds.
	 */
	signal_no_reset(SIGALRM, alarming);
	itv.it_interval.tv_sec = 0;
	itv.it_value.tv_sec = 0;
	itv.it_interval.tv_usec = 1000000/TIMER_HZ;
	itv.it_value.tv_usec = 1000000/(TIMER_HZ<<1);

	setitimer(ITIMER_REAL, &itv, NULL);
# endif	/* !HAVE_TIMER_CREATE */
#else	/* SYS_WINNT follows */
	_tzset();

	/*
	 * Get privileges needed for fiddling with the clock
	 */

	/* get the current process token handle */
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
		msyslog(LOG_ERR, "OpenProcessToken failed: %m");
		exit(1);
	}
	/* get the LUID for system-time privilege. */
	LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
	tkp.PrivilegeCount = 1;		/* one privilege to set */
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	/* get set-time privilege for this process. */
	AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES) NULL, 0);
	/* cannot test return value of AdjustTokenPrivileges. */
	if (GetLastError() != ERROR_SUCCESS)
		msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");

	/*
	 * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds
	 * Under Win/NT, expiry of timer interval leads to invocation
	 * of a callback function (on a different thread) rather than
	 * generating an alarm signal
	 */

	/* determine max and min resolution supported */
	if(timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
		msyslog(LOG_ERR, "timeGetDevCaps failed: %m");
		exit(1);
	}
	wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
	/* establish the minimum timer resolution that we'll use */
	timeBeginPeriod(wTimerRes);
	atexit(callTimeEndPeriod);

	/* start the timer event */
	wTimerID = timeSetEvent(
		(UINT) (1000/TIMER_HZ),		/* Delay */
		wTimerRes,			/* Resolution */
		(LPTIMECALLBACK) alarming,	/* Callback function */
		(DWORD) dwUser,			/* User data */
		TIME_PERIODIC);			/* Event type (periodic) */
	if (wTimerID == 0) {
		msyslog(LOG_ERR, "timeSetEvent failed: %m");
		exit(1);
	}
#endif /* SYS_WINNT */
}




/*
 * We do asynchronous input using the SIGIO facility.  A number of
 * recvbuf buffers are preallocated for input.	In the signal
 * handler we poll to see if the socket is ready and read the
 * packets from it into the recvbuf's along with a time stamp and
 * an indication of the source host and the interface it was received
 * through.  This allows us to get as accurate receive time stamps
 * as possible independent of other processing going on.
 *
 * We allocate a number of recvbufs equal to the number of servers
 * plus 2.	This should be plenty.
 */


/*
 * init_io - initialize I/O data and open socket
 */
static void
init_io(void)
{
	struct addrinfo *res, *ressave;
	struct addrinfo hints;
	sockaddr_u addr;
	char service[5];
	int rc;
	int optval = 1;
	int check_ntp_port_in_use = !debug && !simple_query && !unpriv_port;

	/*
	 * Init buffer free list and stat counters
	 */
	init_recvbuff(sys_numservers + 2);

	/*
	 * Open the socket
	 */

	strlcpy(service, "ntp", sizeof(service));

	/*
	 * Init hints addrinfo structure
	 */
	ZERO(hints);
	hints.ai_family = ai_fam_templ;
	hints.ai_flags = AI_PASSIVE;
	hints.ai_socktype = SOCK_DGRAM;

	if (getaddrinfo(NULL, service, &hints, &res) != 0) {
		msyslog(LOG_ERR, "getaddrinfo() failed: %m");
		exit(1);
		/*NOTREACHED*/
	}

#ifdef SYS_WINNT
	if (check_ntp_port_in_use && ntp_port_inuse(AF_INET, NTP_PORT)){
		msyslog(LOG_ERR, "the NTP socket is in use, exiting: %m");
		exit(1);
	}
#endif

	/* Remember the address of the addrinfo structure chain */
	ressave = res;

	/*
	 * For each structure returned, open and bind socket
	 */
	for(nbsock = 0; (nbsock < MAX_AF) && res ; res = res->ai_next) {
	/* create a datagram (UDP) socket */
		fd[nbsock] = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if (fd[nbsock] == SOCKET_ERROR) {
#ifndef SYS_WINNT
		if (errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT ||
		    errno == EPFNOSUPPORT)
#else
		int err = WSAGetLastError();
		if (err == WSAEPROTONOSUPPORT || err == WSAEAFNOSUPPORT ||
		    err == WSAEPFNOSUPPORT)
#endif
			continue;
		msyslog(LOG_ERR, "socket() failed: %m");
		exit(1);
		/*NOTREACHED*/
		}
		/* set socket to reuse address */
		if (setsockopt(fd[nbsock], SOL_SOCKET, SO_REUSEADDR, (void*) &optval, sizeof(optval)) < 0) {
				msyslog(LOG_ERR, "setsockopt() SO_REUSEADDR failed: %m");
				exit(1);
				/*NOTREACHED*/
		}
#ifdef IPV6_V6ONLY
		/* Restricts AF_INET6 socket to IPv6 communications (see RFC 2553bis-03) */
		if (res->ai_family == AF_INET6)
			if (setsockopt(fd[nbsock], IPPROTO_IPV6, IPV6_V6ONLY, (void*) &optval, sizeof(optval)) < 0) {
				   msyslog(LOG_ERR, "setsockopt() IPV6_V6ONLY failed: %m");
					exit(1);
					/*NOTREACHED*/
		}
#endif

		/* Remember the socket family in fd_family structure */
		fd_family[nbsock] = res->ai_family;

		/*
		 * bind the socket to the NTP port
		 */
		if (check_ntp_port_in_use) {
			ZERO(addr);
			INSIST(res->ai_addrlen <= sizeof(addr));
			memcpy(&addr, res->ai_addr, res->ai_addrlen);
			rc = bind(fd[nbsock], &addr.sa, SOCKLEN(&addr));
			if (rc < 0) {
				if (EADDRINUSE == socket_errno())
					msyslog(LOG_ERR, "the NTP socket is in use, exiting");
				else
					msyslog(LOG_ERR, "bind() fails: %m");
				exit(1);
			}
		}

#ifdef HAVE_POLL_H
		fdmask[nbsock].fd = fd[nbsock];
		fdmask[nbsock].events = POLLIN;
#else
		FD_SET(fd[nbsock], &fdmask);
		if (maxfd < fd[nbsock]+1) {
			maxfd = fd[nbsock]+1;
		}
#endif

		/*
		 * set non-blocking,
		 */
#ifndef SYS_WINNT
# ifdef SYS_VXWORKS
		{
			int on = TRUE;

			if (ioctl(fd[nbsock],FIONBIO, &on) == ERROR) {
				msyslog(LOG_ERR, "ioctl(FIONBIO) fails: %m");
				exit(1);
			}
		}
# else /* not SYS_VXWORKS */
#  if defined(O_NONBLOCK)
		if (fcntl(fd[nbsock], F_SETFL, O_NONBLOCK) < 0) {
			msyslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
			exit(1);
			/*NOTREACHED*/
		}
#  else /* not O_NONBLOCK */
#	if defined(FNDELAY)
		if (fcntl(fd[nbsock], F_SETFL, FNDELAY) < 0) {
			msyslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
			exit(1);
			/*NOTREACHED*/
		}
#	else /* FNDELAY */
#	 include "Bletch: Need non blocking I/O"
#	endif /* FNDELAY */
#  endif /* not O_NONBLOCK */
# endif /* SYS_VXWORKS */
#else /* SYS_WINNT */
		if (ioctlsocket(fd[nbsock], FIONBIO, (u_long *) &on) == SOCKET_ERROR) {
			msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
			exit(1);
		}
#endif /* SYS_WINNT */
		nbsock++;
	}
	freeaddrinfo(ressave);
}

/*
 * sendpkt - send a packet to the specified destination
 */
static void
sendpkt(
	sockaddr_u *dest,
	struct pkt *pkt,
	int len
	)
{
	int i;
	int cc;
	SOCKET sock = INVALID_SOCKET;

#ifdef SYS_WINNT
	DWORD err;
#endif /* SYS_WINNT */

	/* Find a local family compatible socket to send ntp packet to ntp server */
	for(i = 0; (i < MAX_AF); i++) {
		if(AF(dest) == fd_family[i]) {
			sock = fd[i];
		break;
		}
	}

	if (INVALID_SOCKET == sock) {
		msyslog(LOG_ERR, "cannot find family compatible socket to send ntp packet");
		exit(1);
		/*NOTREACHED*/
	}

	cc = sendto(sock, (char *)pkt, len, 0, (struct sockaddr *)dest,
			SOCKLEN(dest));

	if (SOCKET_ERROR == cc) {
#ifndef SYS_WINNT
		if (errno != EWOULDBLOCK && errno != ENOBUFS)
#else
		err = WSAGetLastError();
		if (err != WSAEWOULDBLOCK && err != WSAENOBUFS)
#endif /* SYS_WINNT */
			msyslog(LOG_ERR, "sendto(%s): %m", stohost(dest));
	}
}


/*
 * input_handler - receive packets asynchronously
 */
void
input_handler(void)
{
	register int n;
	register struct recvbuf *rb;
	struct sock_timeval tvzero;
	GETSOCKNAME_SOCKLEN_TYPE fromlen;
	l_fp ts;
	int i;
#ifdef HAVE_POLL_H
	struct pollfd fds[MAX_AF];
#else
	fd_set fds;
#endif
	SOCKET fdc = 0;

	/*
	 * Do a poll to see if we have data
	 */
	for (;;) {
		tvzero.tv_sec = tvzero.tv_usec = 0;
#ifdef HAVE_POLL_H
		memcpy(fds, fdmask, sizeof(fdmask));
		n = poll(fds, (unsigned int)nbsock, tvzero.tv_sec * 1000);

		/*
		 * Determine which socket received data
		 */

		for(i=0; i < nbsock; i++) {
			if(fds[i].revents & POLLIN) {
				fdc = fd[i];
				break;
			}
		}

#else
		fds = fdmask;
		n = select(maxfd, &fds, NULL, NULL, &tvzero);

		/*
		 * Determine which socket received data
		 */

		for(i=0; i < nbsock; i++) {
			if(FD_ISSET(fd[i], &fds)) {
				 fdc = fd[i];
				 break;
			}
		}

#endif

		/*
		 * If nothing to do, just return.  If an error occurred,
		 * complain and return.  If we've got some, freeze a
		 * timestamp.
		 */
		if (n == 0)
			return;
		else if (n == -1) {
			if (errno != EINTR)
				msyslog(LOG_ERR,
#ifdef HAVE_POLL_H
					"poll() error: %m"
#else
					"select() error: %m"
#endif
					);
			return;
		}
		get_systime(&ts);

		/*
		 * Get a buffer and read the frame.  If we
		 * haven't got a buffer, or this is received
		 * on the wild card socket, just dump the packet.
		 */
		if (initializing || free_recvbuffs() == 0) {
			char buf[100];


#ifndef SYS_WINNT
			(void) read(fdc, buf, sizeof buf);
#else
			/* NT's _read does not operate on nonblocking sockets
			 * either recvfrom or ReadFile() has to be used here.
			 * ReadFile is used in [ntpd]ntp_intres() and ntpdc,
			 * just to be different use recvfrom() here
			 */
			recvfrom(fdc, buf, sizeof(buf), 0, (struct sockaddr *)0, NULL);
#endif /* SYS_WINNT */
			continue;
		}

		rb = get_free_recv_buffer();

		fromlen = sizeof(rb->recv_srcadr);
		rb->recv_length = recvfrom(fdc, (char *)&rb->recv_pkt,
		   sizeof(rb->recv_pkt), 0,
		   (struct sockaddr *)&rb->recv_srcadr, &fromlen);
		if (rb->recv_length == -1) {
			freerecvbuf(rb);
			continue;
		}

		/*
		 * Got one.  Mark how and when it got here,
		 * put it on the full list.
		 */
		rb->recv_time = ts;
		add_full_recv_buffer(rb);
	}
}


#if !defined SYS_WINNT && !defined SYS_CYGWIN32
/*
 * adj_systime - do a big long slew of the system time
 */
static int
l_adj_systime(
	l_fp *ts
	)
{
	struct timeval adjtv, oadjtv;
	int isneg = 0;
	l_fp offset;
#ifndef STEP_SLEW
	l_fp overshoot;
#endif

	/*
	 * Take the absolute value of the offset
	 */
	offset = *ts;
	if (L_ISNEG(&offset)) {
		isneg = 1;
		L_NEG(&offset);
	}

#ifndef STEP_SLEW
	/*
	 * Calculate the overshoot.  XXX N.B. This code *knows*
	 * ADJ_OVERSHOOT is 1/2.
	 */
	overshoot = offset;
	L_RSHIFTU(&overshoot);
	if (overshoot.l_ui != 0 || (overshoot.l_uf > ADJ_MAXOVERSHOOT)) {
		overshoot.l_ui = 0;
		overshoot.l_uf = ADJ_MAXOVERSHOOT;
	}
	L_ADD(&offset, &overshoot);
#endif
	TSTOTV(&offset, &adjtv);

	if (isneg) {
		adjtv.tv_sec = -adjtv.tv_sec;
		adjtv.tv_usec = -adjtv.tv_usec;
	}

	if (adjtv.tv_usec != 0 && !debug) {
		if (adjtime(&adjtv, &oadjtv) < 0) {
			msyslog(LOG_ERR, "Can't adjust the time of day: %m");
			exit(1);
		}
	}
	return 1;
}
#endif /* SYS_WINNT */


/*
 * This fuction is not the same as lib/systime step_systime!!!
 */
static int
l_step_systime(
	l_fp *ts
	)
{
	double dtemp;

#ifdef SLEWALWAYS
#ifdef STEP_SLEW
	l_fp ftmp;
	int isneg;
	int n;

	if (debug) return 1;
	/*
	 * Take the absolute value of the offset
	 */
	ftmp = *ts;
	if (L_ISNEG(&ftmp)) {
		L_NEG(&ftmp);
		isneg = 1;
	} else
		isneg = 0;

	if (ftmp.l_ui >= 3) {		/* Step it and slew - we might win */
		LFPTOD(ts, dtemp);
		n = step_systime(dtemp);
		if (!n)
			return n;
		if (isneg)
			ts->l_ui = ~0;
		else
			ts->l_ui = ~0;
	}
	/*
	 * Just add adjustment into the current offset.  The update
	 * routine will take care of bringing the system clock into
	 * line.
	 */
#endif
	if (debug)
		return 1;
#ifdef FORCE_NTPDATE_STEP
	LFPTOD(ts, dtemp);
	return step_systime(dtemp);
#else
	l_adj_systime(ts);
	return 1;
#endif
#else /* SLEWALWAYS */
	if (debug)
		return 1;
	LFPTOD(ts, dtemp);
	return step_systime(dtemp);
#endif	/* SLEWALWAYS */
}


/* XXX ELIMINATE printserver similar in ntptrace.c, ntpdate.c */
/*
 * printserver - print detail information for a server
 */
static void
printserver(
	register struct server *pp,
	FILE *fp
	)
{
	register int i;
	char junk[5];
	const char *str;

	if (!debug) {
		(void) fprintf(fp, "server %s, stratum %d, offset %s, delay %s\n",
				   stoa(&pp->srcadr), pp->stratum,
				   lfptoa(&pp->offset, 6), fptoa((s_fp)pp->delay, 5));
		return;
	}

	(void) fprintf(fp, "server %s, port %d\n",
			   stoa(&pp->srcadr), ntohs(((struct sockaddr_in*)&(pp->srcadr))->sin_port));

	(void) fprintf(fp, "stratum %d, precision %d, leap %c%c, trust %03o\n",
			   pp->stratum, pp->precision,
			   pp->leap & 0x2 ? '1' : '0',
			   pp->leap & 0x1 ? '1' : '0',
			   pp->trust);

	if (pp->stratum == 1) {
		junk[4] = 0;
		memmove(junk, (char *)&pp->refid, 4);
		str = junk;
	} else {
		str = stoa(&pp->srcadr);
	}
	(void) fprintf(fp,
			   "refid [%s], delay %s, dispersion %s\n",
			   str, fptoa((s_fp)pp->delay, 5),
			   ufptoa(pp->dispersion, 5));

	(void) fprintf(fp, "transmitted %d, in filter %d\n",
			   pp->xmtcnt, pp->filter_nextpt);

	(void) fprintf(fp, "reference time:    %s\n",
			   prettydate(&pp->reftime));
	(void) fprintf(fp, "originate timestamp: %s\n",
			   prettydate(&pp->org));
	(void) fprintf(fp, "transmit timestamp:  %s\n",
			   prettydate(&pp->xmt));

	(void) fprintf(fp, "filter delay: ");
	for (i = 0; i < NTP_SHIFT; i++) {
		(void) fprintf(fp, " %-8.8s", fptoa(pp->filter_delay[i], 5));
		if (i == (NTP_SHIFT>>1)-1)
			(void) fprintf(fp, "\n        ");
	}
	(void) fprintf(fp, "\n");

	(void) fprintf(fp, "filter offset:");
	for (i = 0; i < PEER_SHIFT; i++) {
		(void) fprintf(fp, " %-8.8s", lfptoa(&pp->filter_offset[i], 6));
		if (i == (PEER_SHIFT>>1)-1)
			(void) fprintf(fp, "\n        ");
	}
	(void) fprintf(fp, "\n");

	(void) fprintf(fp, "delay %s, dispersion %s\n",
			   fptoa((s_fp)pp->delay, 5), ufptoa(pp->dispersion, 5));

	(void) fprintf(fp, "offset %s\n\n",
			   lfptoa(&pp->offset, 6));
}


#ifdef HAVE_NETINFO
static ni_namelist *
getnetinfoservers(void)
{
	ni_status status;
	void *domain;
	ni_id confdir;
	ni_namelist *namelist = emalloc(sizeof(ni_namelist));

	/* Find a time server in NetInfo */
	if ((status = ni_open(NULL, ".", &domain)) != NI_OK) return NULL;

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

	NI_INIT(namelist);
	if (ni_lookupprop(domain, &confdir, "server", namelist) != NI_OK) {
		ni_namelist_free(namelist);
		free(namelist);
		return NULL;
	}

	return(namelist);
}
#endif

#ifdef SYS_WINNT
isc_boolean_t ntp_port_inuse(int af, u_short port)
{
	/*
	 * Check if NTP socket is already in use on this system
	 * This is only for Windows Systems, as they tend not to fail on the real bind() below
	 */
	
	SOCKET checksocket;
	struct sockaddr_in checkservice;
	checksocket = socket(af, SOCK_DGRAM, 0);
	if (checksocket == INVALID_SOCKET) {
		return (ISC_TRUE);
	}

	checkservice.sin_family = (short) AF_INET;
	checkservice.sin_addr.s_addr = INADDR_LOOPBACK;
	checkservice.sin_port = htons(port);

	if (bind(checksocket, (struct sockaddr *)&checkservice,
		sizeof(checkservice)) == SOCKET_ERROR) {
		if ( WSAGetLastError() == WSAEADDRINUSE ){
			closesocket(checksocket);
			return (ISC_TRUE);
		}
	}
	closesocket(checksocket);
	return (ISC_FALSE);
}
#endif
