ntptimeset.c   [plain text]


/*
 * ntptimeset - get/set the time via ntp
 *
 * GOAL:
 * The goal of ntptime is to set the current time on system startup
 * to the best possible time using the network very wisely. It is assumed
 * that after a resonable time has been sett then ntp daemon will
 * maintain it.
 *
 * PROBLEM DOMAIN:
 * We have three sets of issues related to acheiving the goal. The first
 * issue is using the network when normal traffic is happening or when
 * the entire network world is recovering from a campus wide power failure
 * and is restarting. The second issue is the class of machine whether it
 * is a user's office workstation being handled by an uneducated user or
 * a server computer being handled by a trained operations staff. The third
 * issue is whether the ratio of people to computers and whether the 
 * environment is stable and viable or not.
 *
 * NETWORK USAGE:
 * The first issue of using the network wisely is a question of whether
 * the network load and time server load and state are normal. If things
 * are normal ntptime can do what ntpdate does of sending out 4 packets
 * quickly to each server (new transmit done with each ack). However
 * if network or time load is high then this scheme will simply contribute
 * to problems. Given we have minimal state, we simply weight lost packets
 * significantly and make sure we throttle output as much as possible
 * without performance lost for quick startups.
 *
 * TRAINING AND KNOWLEDGE:
 * The second issue of uneducated user of a office workstation versus a
 * trained operation staff of a server machine translates into simply an
 * issue of untrained and trained users.
 * 
 * The training issue implies that for the sake of the users involved in the
 * handling of their office workstation, problems and options should be
 * communicated simply and effectively and not in terse expert related
 * descriptions without possible options to be taken. The operator's training
 * and education enables them to deal with either type of communication and
 * control.
 *
 * AUTOMATION AND MANUAL CONTROL:
 * The last issue boils down to a design problem. If the design tends to go
 * into a manual mode when the environment is non-viable then one person
 * handling many computers all at the same time will be heavily impacted. On
 * the other hand, if the design tends to be automatic and does not indicate
 * a way for the user to take over control then the computer will be
 * unavailable for the user until the proble is resolved by someone else or
 * the user.
 *
 * NOTE: Please do not have this program print out every minute some line,
 *       of output. If this happens and the environment is in trouble then
 *       many pages of paper on many different machines will be filled up.
 *       Save some tress in your lifetime.
 * 
 * CONCLUSION:
 * The behavior of the program derived from these three issues should be
 * that during normal situations it quickly sets the time and allow the
 * system to startup.
 *
 * However during abnormal conditions as detected by unresponsive servers,
 * out-of-sync or bad responses and other detections, it should print out
 * a simple but clear message and continue in a mellow way to get the best
 * possible time. It may never get the time and if so should also indicate
 * this.
 *
 * Rudy Nedved
 * 18-May-1993
 *
 ****************************************************************
 *
 * Much of the above is confusing or no longer relevant.  For example,
 * it is rare these days for a machine's console to be a printing terminal,
 * so the comment about saving trees doesn't mean much.  Nonetheless,
 * the basic principles still stand:
 *
 * - Work automatically, without human control or intervention.  To
 *   this end, we use the same configuration file as ntpd itself, so
 *   you don't have to specify servers or other information on the
 *   command line.  We also recognize that sometimes we won't be able
 *   to contact any servers, and give up in that event instead of
 *   hanging forever.
 *
 * - Behave in a sane way, both internally and externally, even in the
 *   face of insane conditions.  That means we back off quickly when
 *   we don't hear a response, to avoid network congestion.  Like
 *   ntpd, we verify responses from several servers before accepting
 *   the new time data.
 *
 *   However, we don't assume that the local clock is right, or even
 *   close, because it might not be at boot time, and we want to catch
 *   and correct that situation.  This behaviour has saved us in several
 *   instances.  On HP-UX 9.0x, there used to be a bug in adjtimed which
 *   would cause the time to be set to some wild value, making the machine
 *   essentially unusable (we use Kerberos authentication pervasively,
 *   and it requires workstations and servers to have a time within five
 *   minutes of the Kerberos server).  We also have problems on PC's
 *   running both Linux and some Microsoft OS -- they tend to disagree
 *   on what the BIOS clock should say, and who should update it, and
 *   when.  On those systems, we not only run ntptimeset at boot, we
 *   also reset the BIOS clock based on the result, so the correct
 *   time will be retained across reboots.
 *
 * For these reasons, and others, we have continued to use this tool
 * rather than ntpdate.  It is run automatically at boot time on every
 * workstation and server in our facility.
 *
 * In the past, we called this program 'ntptime'.  Unfortunately, the
 * ntp v4 distribution also includes a program with that name.  In
 * order to avoid confusion, we have renamed our program 'ntptimeset',
 * which more accurately describes what it does.
 *
 * Jeffrey T. Hutzelman (N3NHS) <jhutz+@cmu.edu>
 * School of Computer Science - Research Computing Facility
 * Carnegie Mellon University - Pittsburgh, PA
 * 16-Aug-1999
 *
 */

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

#include "ntp_machine.h"
#include "ntp_fp.h"
#include "ntp.h"
#include "ntp_io.h"
#include "iosignal.h"
#include "ntp_unixtime.h"
#include "ntpdate.h"
#include "ntp_string.h"
#include "ntp_syslog.h"
#include "ntp_select.h"
#include "ntp_stdlib.h"

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

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#ifndef SYS_WINNT
# ifdef HAVE_SYS_SIGNAL_H
#  include <sys/signal.h>
# else
#  include <signal.h>
# endif
# include <sys/ioctl.h>
#endif /* SYS_WINNT */

#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif /* HAVE_SYS_RESOURCE_H */

#ifdef SYS_VXWORKS
# include "ioLib.h"
# include "sockLib.h"
# include "timers.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")
#endif /* SYS_WINNT */

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

#if defined(HAVE_TIMER_SETTIME) || defined (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 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 */

/*
 * Debugging flag
 */
volatile int debug = 0;

/*
 * File descriptor masks etc. for call to select
 */
int fd;
fd_set fdmask;

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

/*
 * Set the time if valid time determined
 */
int set_time = 0;

/*
 * transmission rate control
 */
#define	MINTRANSMITS	(3)	/* minimum total packets per server */
#define	MAXXMITCOUNT	(2)	/* maximum packets per time interrupt */

/*
 * time setting constraints
 */
#define	DESIREDDISP	(4*FP_SECOND)	/* desired dispersion, (fp 4) */
int max_period = DEFMAXPERIOD;
int min_servers = DEFMINSERVERS;
int min_valid = DEFMINVALID;

/*
 * counters related to time setting constraints
 */
int contacted = 0;		/* # of servers we have sent to */
int responding = 0;		/* servers responding */
int validcount = 0;		/* servers with valid time */
int valid_n_low = 0;		/* valid time servers with low dispersion */

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

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

/*
 * Systemwide parameters and flags
 */
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 */

/*
 * The current internal time
 */
u_long current_time = 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 */

/*
 * total packet counts
 */
u_long total_xmit = 0;
u_long total_recv = 0;

/*
 * Miscellaneous flags
 */
int verbose = 0;
#define	HORRIBLEOK	3	/* how many packets to let out */
int horrible = 0;	/* how many packets we drop for testing */
int secondhalf = 0;	/* second half of timeout period */
int printmsg = 0;	/* print time response analysis */

/*
 * The half time and finish time in internal time
 */
u_long half_time = 0;
u_long finish_time = 0;


int	ntptimesetmain	P((int argc, char *argv[]));
static	void	analysis	P((int final));
static	int	have_enough	P((void));
static	void	transmit	P((register struct server *server));
static	void	receive		P((struct recvbuf *rbufp));
static	void	clock_filter P((register struct server *server, s_fp d, l_fp *c));
static	void	clock_count	P((void));
static	struct server *clock_select P((void));
static	void	set_local_clock	P((void));
static	struct server *findserver P((struct sockaddr_in *addr));
static	void	timer		P((void));
#ifndef SYS_WINNT
static	RETSIGTYPE 	alarming	P((int sig));
#endif /* SYS_WINNT */
static	void	init_alarm	P((void));
static	void	init_io		P((void));
static	int	sendpkt		P((struct sockaddr_in *dest, struct pkt *pkt, int len));
	void 	input_handler	P((l_fp *xts));
static	void	printserver	P((register struct server *pp, FILE *fp));
#if !defined(HAVE_VSPRINTF)
int	vsprintf	P((char *str, const char *fmt, va_list ap));
#endif

#ifdef HAVE_SIGNALED_IO
extern  void    wait_for_signal P((void));
extern  void    unblock_io_and_alarm P((void));
extern  void    block_io_and_alarm P((void));
#endif


#ifdef NO_MAIN_ALLOWED
CALL(ntptimeset,"ntptimeset",ntptimesetmain);

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;

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

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

  verbose = 0;
}
#endif /* NO_MAIN_ALLOWED */

/*
 * 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 ntptimesetmain(argc, argv);
}
#endif /* NO_MAIN_ALLOWED */
	   

int
ntptimesetmain(
	int argc,
	char *argv[]
	)
{
	int was_alarmed;
	int tot_recvbufs;
	struct recvbuf *rbuf;
	l_fp tmp;
	int errflg;
	int c;
	extern char *ntp_optarg;
	extern int ntp_optind;
	int ltmp;
	char *cfgpath;

#ifdef SYS_WINNT
	HANDLE process_handle;

	wVersionRequested = MAKEWORD(1,1);
	if (WSAStartup(wVersionRequested, &wsaData)) {
		msyslog(LOG_ERR, "No useable winsock.dll: %m");
		exit(1);
	}
#endif /* SYS_WINNT */

#ifdef NO_MAIN_ALLOWED
	clear_globals();
#endif

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

	/*
	 * Decode argument list
	 */
	while ((c = ntp_getopt(argc, argv, "a:c:de:slt:uvHS:V:")) != EOF)
		switch (c)
		{
		case 'a':
			c = atoi(ntp_optarg);
			sys_authenticate = 1;
			sys_authkey = c;
			break;
		case 'c':
			cfgpath = ntp_optarg;
			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 's':
			set_time = 1;
			break;
		case 'l':
			syslogit = 1;
			break;
		case 't':
			ltmp = atoi(ntp_optarg);
			if (ltmp <= 0) {
			    (void) fprintf(stderr,
				"%s: maximum time period (%d) is invalid\n",
				progname, ltmp);
			    errflg++;
			}
			else
			    max_period = ltmp;
			break;
		case 'u':
			unpriv_port = 1;
			break;
		case 'v':
			++verbose;
			break;
		case 'H':
			horrible++;
			break;
		case 'S':
			ltmp = atoi(ntp_optarg);
			if (ltmp <= 0) {
			    (void) fprintf(stderr,
				"%s: minimum responding (%d) is invalid\n",
				progname, ltmp);
			    errflg++;
			}
			else
			    min_servers = ltmp;
			break;
		case 'V':
			ltmp = atoi(ntp_optarg);
			if (ltmp <= 0) {
			    (void) fprintf(stderr,
				"%s: minimum valid (%d) is invalid\n",
				progname, ltmp);
			    errflg++;
			}
			else
			    min_valid = ltmp;
			break;
		case '?':
			++errflg;
			break;
		default:
			break;
		}

	
	if (errflg || ntp_optind < argc) {
		fprintf(stderr,"usage: %s [switches...]\n",progname);
		fprintf(stderr,"  -v       (verbose)\n");
		fprintf(stderr,"  -c path  (set config file path)\n");
		fprintf(stderr,"  -a key   (authenticate using key)\n");
		fprintf(stderr,"  -e delay (authentication delay)\n");
		fprintf(stderr,"  -S num   (# of servers that must respond)\n");
		fprintf(stderr,"  -V num   (# of servers that must valid)\n");
		fprintf(stderr,"  -s       (set the time based if okay)\n");
		fprintf(stderr,"  -t secs  (time period before ending)\n");
		fprintf(stderr,"  -l       (use syslog facility)\n");
		fprintf(stderr,"  -u       (use unprivileged port)\n");
		fprintf(stderr,"  -H       (drop packets for debugging)\n");
		fprintf(stderr,"  -d       (debug output)\n");
		exit(2);
	}

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

#  ifndef	LOG_NTP
#	define	LOG_NTP LOG_DAEMON
#  endif
		openlog("ntptimeset", 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_INFO, "%s", Version);

	if (horrible)
		msyslog(LOG_INFO, "Dropping %d out of %d packets",
			horrible,horrible+HORRIBLEOK);
	/*
	 * Add servers we are going to be polling
	 */
	loadservers(cfgpath);

	if (sys_numservers < min_servers) {
		msyslog(LOG_ERR, "Found %d servers, require %d servers",
			sys_numservers,min_servers);
		exit(2);
	}

	/*
	 * determine when we will end at least
 	 */
	finish_time = max_period * TIMER_HZ;
	half_time = finish_time >> 1;

	/*
	 * Initialize the time of day routines and the I/O subsystem
	 */
	if (sys_authenticate) {
		init_auth();
#ifdef SYS_WINNT
		if (!key_file) key_file = KEYFILE;
		if (!ExpandEnvironmentStrings(key_file, key_file_storage, MAX_PATH))
		{
			msyslog(LOG_ERR, "ExpandEnvironmentStrings(%s) failed: %m\n",
				key_file);
		} else {
			key_file = key_file_storage;
		}
#endif /* SYS_WINNT */

		if (!authreadkeys(key_file)) {
			msyslog(LOG_ERR, "no key file, exiting");
			exit(1);
		}
		if (!authistrusted(sys_authkey)) {
			char buf[10];

			(void) sprintf(buf, "%lu", (unsigned long)sys_authkey);
			msyslog(LOG_ERR, "authentication key %s unknown", buf);
			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
#ifdef SYS_WINNT
	process_handle = GetCurrentProcess();
	if (!SetPriorityClass(process_handle, (DWORD) REALTIME_PRIORITY_CLASS)) {
		msyslog(LOG_ERR, "SetPriorityClass failed: %m");
	}
#endif /* SYS_WINNT */

	initializing = 0;

	/*
	 * Use select() on all on all input fd's for unlimited
	 * time.  select() will terminate on SIGALARM or on the
	 * reception of input.	Using select() means we can't do
	 * robust signal handling and we get a potential race
	 * between checking for alarms and doing the select().
	 * Mostly harmless, I think.
	 * Keep going until we have enough information, or time is up.
	 */
	/* On VMS, I suspect that select() can't be interrupted
	 * by a "signal" either, so I take the easy way out and
	 * have select() time out after one second.
	 * System clock updates really aren't time-critical,
	 * and - lacking a hardware reference clock - I have
	 * yet to learn about anything else that is.
	 */
	was_alarmed = 0;
	while (finish_time > current_time) {
#if !defined(HAVE_SIGNALED_IO) 
		fd_set rdfdes;
		int nfound;
#elif defined(HAVE_SIGNALED_IO)
		block_io_and_alarm();
#endif

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

		if (!was_alarmed && tot_recvbufs > 0) {
			/*
			 * Nothing to do.  Wait for something.
			 */
#ifndef HAVE_SIGNALED_IO
			rdfdes = fdmask;
# if defined(VMS) || defined(SYS_VXWORKS)
			/* make select() wake up after one second */
			{
				struct timeval t1;

				t1.tv_sec = 1; t1.tv_usec = 0;
				nfound = select(fd+1, &rdfdes, (fd_set *)0,
						(fd_set *)0, &t1);
			}
# else
			nfound = select(fd+1, &rdfdes, (fd_set *)0,
					(fd_set *)0, (struct timeval *)0);
# endif /* VMS */
			if (nfound > 0) {
				l_fp ts;
				get_systime(&ts);
				(void)input_handler(&ts);
			}
			else if (nfound == -1 && errno != EINTR)
				msyslog(LOG_ERR, "select() error: %m");
			else if (debug) {
# if !defined SYS_VXWORKS && !defined SYS_CYGWIN32 /* to unclutter log */
				msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound);
# endif
			}
#else /* HAVE_SIGNALED_IO */
                        
			wait_for_signal();
#endif /* HAVE_SIGNALED_IO */
			if (alarm_flag) 	/* alarmed? */
			{
				was_alarmed = 1;
				alarm_flag = 0;
			}
			tot_recvbufs = full_recvbuffs();  /* get received buffers */
		}
#ifdef HAVE_SIGNALED_IO
		unblock_io_and_alarm();
#endif /* HAVE_SIGNALED_IO */

		/*
		 * Out here, signals are unblocked.  Call timer routine
		 * to process expiry.
		 */
		if (was_alarmed)
		{
			timer();
			was_alarmed = 0;
		}

		/*
		 * Call the data procedure to handle each received
		 * packet.
		 */
		rbuf = get_full_recv_buffer();
		while (rbuf != NULL)
		{
			receive(rbuf);
			freerecvbuf(rbuf);
			rbuf = get_full_recv_buffer();
		}

		/*
		 * Do we have enough information to stop now?
		 */
		if (have_enough())
			break;	/* time to end */

		/*
		 * Go around again
		 */
	}

	/*
	 * adjust the clock and exit accordingly
	 */
	set_local_clock();

	/*
	 * if we get here then we are in trouble
	 */
	return(1);
}


/*
 * analysis - print a message indicating what is happening with time service
 *	      must mimic have_enough() procedure.
 */
static void
analysis(
	int final
	)
{
	if (contacted < sys_numservers) {
		printf("%d servers of %d have been probed with %d packets\n",
		       contacted,sys_numservers,MINTRANSMITS);
		return;
	}
	if (!responding) {
		printf("No response from any of %d servers, network problem?\n",
		       sys_numservers);
		return;
	}
	else if (responding < min_servers) {
		printf("%d servers out of %d responding, need at least %d.\n",
		       responding, sys_numservers, min_servers);
		return;
	}
	if (!validcount) {
		printf("%d servers responding but none have valid time\n",
		       responding);
		return;
	}
	else if (validcount < min_valid) {
		printf("%d servers responding, %d are valid, need %d valid\n",
		       responding,validcount,min_valid);
		return;
	}
	if (!final && valid_n_low != validcount) {
		printf("%d valid servers but only %d have low dispersion\n",
		       validcount,valid_n_low);
		return;
	}
}


/* have_enough - see if we have enough information to terminate probing
 */
static int
have_enough(void)
{
	/* have we contacted all servers yet? */
	if (contacted < sys_numservers)
		return 0;	/* no...try some more */

	/* have we got at least minimum servers responding? */
	if (responding < min_servers)
		return 0;	/* no...try some more */

	/* count the clocks */
	(void) clock_count();

	/* have we got at least minimum valid clocks? */
	if (validcount <= 0 || validcount < min_valid)
		return 0;	/* no...try some more */

	/* do we have all valid servers with low dispersion */
	if (!secondhalf && valid_n_low != validcount)
		return 0;

	/* if we get into the secondhalf then we ignore dispersion */

	/* all conditions have been met...end */
	return 1;
}


/*
 * 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;
	int timeout;

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

	if ((server->reach & 01) == 0) {
		l_fp ts;
		/*
		 * Last message to this server timed out.  Shift
		 * zeros into the filter.
		 */
		L_CLR(&ts);
		clock_filter(server, 0, &ts);
	}

	/*
	 * shift reachable register over
	 */
	server->reach <<= 1;

	/*
	 * 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,
		server->version, MODE_CLIENT);
	xpkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
	xpkt.ppoll = NTP_MINPOLL;
	xpkt.precision = NTPDATE_PRECISION;
	xpkt.rootdelay = htonl(NTPDATE_DISTANCE);
	xpkt.rootdispersion = 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) {
		int 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);
		if (sendpkt(&(server->srcadr), &xpkt, (int)(LEN_PKT_NOMAC + len))) {
			if (debug > 1)
				printf("failed transmit auth to %s\n",
				    ntoa(&(server->srcadr)));
			return;
		}

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

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

	/*
	 * count transmits, record contacted count and set transmit time
	 */
	if (++server->xmtcnt == MINTRANSMITS)
	    contacted++;
	server->last_xmit = current_time;

	/*
	 * determine timeout for this packet. The more packets we send
	 * to the host, the slower we get. If the host indicates that
	 * it is not "sane" then we expect even less.
	 */
	if (server->xmtcnt < MINTRANSMITS) {
	    /* we have not sent enough */
	    timeout = TIMER_HZ;		/* 1 second probe */
	}
	else if (server->rcvcnt <= 0) {
	    /* we have heard nothing */
	    if (secondhalf)
		timeout = TIMER_HZ<<4;	/* 16 second probe */
	    else
		timeout = TIMER_HZ<<3;	/* 8 second probe */
	}
	else {
	    /* if we have low dispersion then probe infrequently */
	    if (server->dispersion <= DESIREDDISP)
		timeout = TIMER_HZ<<4;	/* 16 second probe */
	    /* if the server is not in sync then let it alone */
	    else if (server->leap == LEAP_NOTINSYNC)
		timeout = TIMER_HZ<<4;	/* 16 second probe */
	    /* if the server looks broken ignore it */
	    else if (server->org.l_ui < server->reftime.l_ui)
		timeout = TIMER_HZ<<5;	/* 32 second probe */
	    else if (secondhalf)
		timeout = TIMER_HZ<<2;	/* 4 second probe */
	    else
		timeout = TIMER_HZ<<1;	/* 2 second probe */
	}

	/*
	 * set next transmit time based on timeout
	 */
	server->event_time = current_time + timeout;
}


/*
 * 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;
	l_fp org;
	l_fp rec;
	l_fp ci;
	int has_mac;
	int is_authentic;

	if (debug > 2)
		printf("receive(%s)\n", ntoa(&rbufp->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 >= LEN_PKT_NOMAC)
		has_mac = 1;
	else {
		if (debug > 2)
			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) {
		if (debug > 1)
			printf("receive: bad version %d\n",
			       PKT_VERSION(rpkt->li_vn_mode));
		return;
	}

	if ((PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER
	    && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE)
	    || rpkt->stratum >=STRATUM_UNSPEC) {
		if (debug > 1)
			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->srcadr));
	if (server == NULL) {
		if (debug > 1)
			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 > 1)
			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, (int)(rbufp->recv_length - LEN_PKT_NOMAC)));

		if (has_mac && ntohl(rpkt->exten[0]) == sys_authkey &&
			authdecrypt(sys_authkey, (u_int32 *)rpkt, LEN_PKT_NOMAC,
			(int)(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;
	
	/*
	 * 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->rootdispersion = ntohl(rpkt->rootdispersion);
	server->refid = rpkt->refid;
	NTOHL_FP(&rpkt->reftime, &server->reftime);
	NTOHL_FP(&rpkt->rec, &rec);
	NTOHL_FP(&rpkt->xmt, &server->org);

	/*
	 * count this guy as responding
	 */
	server->reach |= 1;
	if (server->rcvcnt++ == 0)
		responding++;

	/*
	 * Make sure the server is at least somewhat sane.  If not, ignore
	 * it for later.
	 */
	if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) {
		if (debug > 1)
			printf("receive: pkt insane\n");
		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) */
	ci = t10;
	L_ADD(&ci, &t23);
	L_RSHIFT(&ci);

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


	/*
	 * This one is valid.  Give it to clock_filter(),
	 */
	clock_filter(server, di, &ci);
	if (debug > 1)
		printf("receive from %s\n", ntoa(&rbufp->srcadr));

	/*
	 * See if we should goes the transmission. If not return now
	 * otherwise have the next event time be shortened
	 */
	if (server->stratum <= NTP_INFIN)
	    return;	/* server does not have a stratum */
	if (server->leap == LEAP_NOTINSYNC)
	    return;	/* just booted server or out of sync */
	if (!L_ISHIS(&server->org, &server->reftime))
	    return;	/* broken host */
	if (server->trust != 0)
	    return;	/* can not trust it */

	if (server->dispersion < DESIREDDISP)
	    return;	/* we have the desired dispersion */

	server->event_time -= (TIMER_HZ+1);
}


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

	/*
	 * Insert sample and increment nextpt
	 */

	i = server->filter_nextpt;
	server->filter_delay[i] = di;
	server->filter_offset[i] = *c;
	server->filter_soffset[i] = LFPTOFP(c);
	server->filter_nextpt++;
	if (server->filter_nextpt >= NTP_SHIFT)
		server->filter_nextpt = 0;

	/*
	 * Sort indices into increasing delay order
	 */
	for (i = 0; i < NTP_SHIFT; i++)
		ord[i] = i;
	
	for (i = 0; i < (NTP_SHIFT-1); i++) {
		for (j = i+1; j < NTP_SHIFT; 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 < NTP_SHIFT; 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_count - count the clock sources we have
 */
static void
clock_count(void)
{
	register struct server *server;
	register int n;

	/* reset counts */
	validcount = valid_n_low = 0;

	/* go through the list of servers and count the clocks we believe
	 * and that have low dispersion
	 */
	for (n = 0; n < sys_numservers; n++) {
		server = sys_servers[n];
		if (server->delay == 0) {
			continue;	/* no data */
		}
		if (server->stratum > NTP_INFIN) {
			continue;	/* stratum no good */
		}
		if (server->delay > NTP_MAXWGT) {
			continue;	/* too far away */
		}
		if (server->leap == LEAP_NOTINSYNC)
			continue;	/* he's in trouble */
		if (!L_ISHIS(&server->org, &server->reftime)) {
			continue;	/* very broken host */
		}
		if ((server->org.l_ui - server->reftime.l_ui) >= NTP_MAXAGE) {
			continue;	/* too long without sync */
		}
		if (server->trust != 0) {
			continue;
		}

		/*
		 * This one is a valid time source..
		 */
		validcount++;

		/*
		 * See if this one has a okay low dispersion
		 */
		if (server->dispersion <= DESIREDDISP)
		    valid_n_low++;
	}

	if (debug > 1)
		printf("have %d, valid %d, low %d\n",
			responding, validcount, valid_n_low);
}


/*
 * clock_select - select the pick-of-the-litter clock from the samples
 *		  we've got.
 */
static struct server *
clock_select(void)
{
	register struct server *server;
	register int i;
	register int nlist;
	register s_fp d;
	register int j;
	register 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 (n = 0; n < sys_numservers; n++) {
		server = sys_servers[n];
		if (server->delay == 0)
			continue;	/* no data */
		if (server->stratum > NTP_INFIN)
			continue;	/* stratum no good */
		if (server->delay > NTP_MAXWGT) {
			continue;	/* too far away */
		}
		if (server->leap == LEAP_NOTINSYNC)
			continue;	/* he's in trouble */
		if (!L_ISHIS(&server->org, &server->reftime)) {
			continue;	/* very broken host */
		}
		if ((server->org.l_ui - server->reftime.l_ui)
		    >= NTP_MAXAGE) {
			continue;	/* too long without sync */
		}
		if (server->trust != 0) {
			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.
	 */
	j = 0;
	for (i = 1; i < nlist; i++)
		if (server_list[i]->stratum > server_list[i-1]->stratum)
			if (++j == 2) {
				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 (nlist == 0)
		sys_server = 0;
	else if (nlist == 1) {
		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)
					break;	/* already sorted by stratum */
				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 (n = 0; n < nlist; n++) {
				server_badness[n] = 0;
				for (j = 0; j < nlist; j++) {
					if (j == n)	/* with self? */
						continue;
					d = server_list[j]->soffset
					    - server_list[n]->soffset;
					if (d < 0)	/* absolute 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[n] += 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 (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;
}


/*
 * set_local_clock -- handle setting the local clock or displaying info.
 */
static void
set_local_clock(void)
{
	register int i;
	register struct server *server;
	time_t tmp;
	double dtemp;

	/*
	 * if setting time then print final analysis
	 */
	if (set_time)
	    analysis(1);

	/*
	 * pick a clock
	 */
	server = clock_select();

	/*
	 * do some display of information
	 */
	if (debug || verbose) {
		for (i = 0; i < sys_numservers; i++)
			printserver(sys_servers[i], stdout);
		if (debug)
			printf("packets sent %ld, received %ld\n",
				total_xmit, total_recv);
	}

	/*
	 * see if we have a server to set the time with
	 */
	if (server == 0) {
	    if (!set_time || verbose)
		fprintf(stdout,"No servers available to sync time with\n");
	    exit(1);
	}

	/*
	 * we have a valid and selected time to use!!!!!
	 */

	/*
	 * if we are not setting the time then display offset and exit
	 */
	if (!set_time) {
		fprintf(stdout,
			"Your clock is off by %s seconds. (%s) [%ld/%ld]\n",
			lfptoa(&server->offset, 7),
			ntoa(&server->srcadr),
			total_xmit, total_recv);
		exit(0);
	}

	/*
	 * set the clock
	 * XXX: Examine the more flexible approach used by ntpdate.
	 * Note that a design consideration here is that we sometimes
	 * _want_ to step the clock by a _huge_ amount in either
	 * direction, because the local clock is completely bogus.
	 * This condition must be recognized and dealt with, so
	 * that we always get a good time when this completes.
	 * -- jhutz+@cmu.edu, 16-Aug-1999
	 */
	LFPTOD(&server->offset, dtemp);
	step_systime(dtemp);
	time(&tmp);
	fprintf(stdout,"Time set to %.20s [%s %s %ld/%ld]\n",
		ctime(&tmp)+4,
		ntoa(&server->srcadr),
		lfptoa(&server->offset, 7),
		total_xmit, total_recv);
	exit(0);
}


/*
 * findserver - find a server in the list given its address
 */
static struct server *
findserver(
	struct sockaddr_in *addr
	)
{
	register int i;
	register u_int32 netnum;

	if (htons(addr->sin_port) != NTP_PORT)
		return 0;
	netnum = addr->sin_addr.s_addr;

	for (i = 0; i < sys_numservers; i++) {
		if (netnum == sys_servers[i]->srcadr.sin_addr.s_addr)
			return sys_servers[i];
	}
	return 0;
}


/*
 * timer - process a timer interrupt
 */
static void
timer(void)
{
	register int k;

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

	/*
	 * see if we have reached half time
	 */
	if (current_time >= half_time && !secondhalf) {
	    secondhalf++;
	    if (debug)
		printf("\nSecond Half of Timeout!\n");
	    printmsg++;
	}

	/*
	 * We only want to send a few packets per transmit interrupt
	 * to throttle things
	 */
	for(k = 0;k < MAXXMITCOUNT;k++) {
	    register int i, oldi;
	    register u_long oldxtime;

	    /*
	     * We want to send a packet out for a server that has an
	     * expired event time. However to be mellow about this, we only
	     * use one expired event timer and to avoid starvation we use
	     * the one with the oldest last transmit time.
	     */
	    oldi = -1;
	    oldxtime = 0;
	    for (i = 0; i < sys_numservers; i++) {
		if (sys_servers[i]->event_time <= current_time) {
		    if (oldi < 0 || oldxtime > sys_servers[i]->last_xmit) {
			oldxtime = sys_servers[i]->last_xmit;
			oldi = i;
		    }
		}
	    }
	    if (oldi >= 0)
		transmit(sys_servers[oldi]);
	    else
		break;	/* no expired event */
	} /* end of transmit loop */
}


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


/*
 * init_alarm - set up the timer interrupt
 */
static void
init_alarm(void)
{
#ifndef SYS_WINNT
# ifndef HAVE_TIMER_SETTIME
	struct itimerval itimer;
# else
	struct itimerspec ntpdate_itimer;
# endif
#else
	TIMECAPS tc;
	UINT wTimerRes, wTimerID;
# endif /* SYS_WINNT */
#if defined SYS_CYGWIN32 || defined SYS_WINNT
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;
	DWORD dwUser = 0;
#endif /* SYS_WINNT */

	alarm_flag = 0;

#ifndef SYS_WINNT
# if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
	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.
	 */
	(void) signal_no_reset(SIGALRM, alarming);
	ntpdate_itimer.it_interval.tv_sec = ntpdate_itimer.it_value.tv_sec = 0;
	ntpdate_itimer.it_interval.tv_nsec = 1000000000/TIMER_HZ;
	ntpdate_itimer.it_value.tv_nsec = 1000000000/(TIMER_HZ<<1);
	timer_settime(ntpdate_timerid, 0 /* !TIMER_ABSTIME */, &ntpdate_itimer, NULL);
# else
	/*
	 * Set up the alarm interrupt.	The first comes 1/(2*TIMER_HZ)
	 * seconds from now and they continue on every 1/TIMER_HZ seconds.
	 */
	(void) signal_no_reset(SIGALRM, alarming);
	itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
	itimer.it_interval.tv_usec = 1000000/TIMER_HZ;
	itimer.it_value.tv_usec = 1000000/(TIMER_HZ<<1);
	setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
# endif
#if defined SYS_CYGWIN32
	/*
	 * Get previleges 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");
#endif
#else	/* SYS_WINNT */
	_tzset();

	/*
	 * Get previleges 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);

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


/*
 * init_io - initialize I/O data and open socket
 */
static void
init_io(void)
{
#ifdef SYS_WINNT
    	WORD wVersionRequested;
	WSADATA wsaData;
	init_transmitbuff();
#endif /* SYS_WINNT */

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

#if defined(HAVE_SIGNALED_IO)
	set_signal();
#endif

#ifdef SYS_WINNT
	wVersionRequested = MAKEWORD(1,1);
	if (WSAStartup(wVersionRequested, &wsaData))
	{
		msyslog(LOG_ERR, "No useable winsock.dll: %m");
		exit(1);
	}
#endif /* SYS_WINNT */

	BLOCKIO();

	/* create a datagram (UDP) socket */
	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		msyslog(LOG_ERR, "socket() failed: %m");
		exit(1);
		/*NOTREACHED*/
	}

	/*
	 * bind the socket to the NTP port
	 */
	if (!debug && set_time && !unpriv_port) {
		struct sockaddr_in addr;

		memset((char *)&addr, 0, sizeof addr);
		addr.sin_family = AF_INET;
		addr.sin_port = htons(NTP_PORT);
		addr.sin_addr.s_addr = htonl(INADDR_ANY);
		if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
#ifndef SYS_WINNT
			if (errno == EADDRINUSE)
#else
			if (WSAGetLastError() == WSAEADDRINUSE)
#endif
				msyslog(LOG_ERR,
				    "the NTP socket is in use, exiting");
			else
				msyslog(LOG_ERR, "bind() fails: %m");
			exit(1);
		}
	}

	FD_ZERO(&fdmask);
	FD_SET(fd, &fdmask);

	/*
	 * set non-blocking,
	 */

#ifdef USE_FIONBIO
	/* in vxWorks we use FIONBIO, but the others are defined for old systems, so
	 * all hell breaks loose if we leave them defined
	 */
#undef O_NONBLOCK
#undef FNDELAY
#undef O_NDELAY
#endif

#if defined(O_NONBLOCK) /* POSIX */
	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
	{
		msyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails: %m");
		exit(1);
		/*NOTREACHED*/
	}
#elif defined(FNDELAY)
	if (fcntl(fd, F_SETFL, FNDELAY) < 0)
	{
		msyslog(LOG_ERR, "fcntl(FNDELAY) fails: %m");
		exit(1);
		/*NOTREACHED*/
	}
#elif defined(O_NDELAY) /* generally the same as FNDELAY */
	if (fcntl(fd, F_SETFL, O_NDELAY) < 0)
	{
		msyslog(LOG_ERR, "fcntl(O_NDELAY) fails: %m");
		exit(1);
		/*NOTREACHED*/
	}
#elif defined(FIONBIO)
	if (
# if defined(VMS)
		(ioctl(fd,FIONBIO,&1) < 0)
# elif defined(SYS_WINNT)
		(ioctlsocket(fd,FIONBIO,(u_long *) &on) == SOCKET_ERROR)
# else
		(ioctl(fd,FIONBIO,&on) < 0)
# endif
	   )
	{
		msyslog(LOG_ERR, "ioctl(FIONBIO) fails: %m");
		exit(1);
		/*NOTREACHED*/
	}
#elif defined(FIOSNBIO)
	if (ioctl(fd,FIOSNBIO,&on) < 0)
	{
		msyslog(LOG_ERR, "ioctl(FIOSNBIO) fails: %m");
		exit(1);
		/*NOTREACHED*/
	}
#else
# include "Bletch: Need non-blocking I/O!"
#endif

#ifdef HAVE_SIGNALED_IO
	init_socket_sig(fd);
#endif /* not HAVE_SIGNALED_IO */

	UNBLOCKIO();
}


/*
 * sendpkt - send a packet to the specified destination
 */
static int
sendpkt(
	struct sockaddr_in *dest,
	struct pkt *pkt,
	int len
	)
{
	int cc;
	static int horriblecnt = 0;
#ifdef SYS_WINNT
	DWORD err;
#endif /* SYS_WINNT */

	total_xmit++;	/* count it */

	if (horrible) {
	    if (++horriblecnt > HORRIBLEOK) {
		if (debug > 3)
			printf("dropping send (%s)\n", ntoa(dest));
		if (horriblecnt >= HORRIBLEOK+horrible)
		    horriblecnt = 0;
		return 0;
	    }
	}


	cc = sendto(fd, (char *)pkt, (size_t)len, 0, (struct sockaddr *)dest,
	    sizeof(struct sockaddr_in));
#ifndef SYS_WINNT
	if (cc == -1) {
		if (errno != EWOULDBLOCK && errno != ENOBUFS)
#else
	if (cc == SOCKET_ERROR) {
		err = WSAGetLastError();
		if (err != WSAEWOULDBLOCK && err != WSAENOBUFS)
#endif /* SYS_WINNT */
			msyslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
		return -1;
	}
	return 0;
}


/*
 * input_handler - receive packets asynchronously
 */
void
input_handler(l_fp *xts)
{
	register int n;
	register struct recvbuf *rb;
	struct timeval tvzero;
	int fromlen;
	fd_set fds;
	l_fp ts;
	ts = *xts; /* we ignore xts, but make the compiler happy */

	/*
	 * Do a poll to see if we have data
	 */
	for (;;) {
		fds = fdmask;
		tvzero.tv_sec = tvzero.tv_usec = 0;
		n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);

		/*
		 * 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, "select() error: %m");
			}
			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(fd, 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(fd, buf, sizeof(buf), 0, (struct sockaddr *)0, NULL);
#endif /* SYS_WINNT */
			continue;
		}

		rb = get_free_recv_buffer();

		fromlen = sizeof(struct sockaddr_in);
		rb->recv_length = recvfrom(fd, (char *)&rb->recv_pkt,
		    sizeof(rb->recv_pkt), 0,
		    (struct sockaddr *)&rb->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);
		total_recv++;	/* count it */
	}
}


/* 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];
	char *str;

	if (!debug) {
	    (void) fprintf(fp,
		"%-15s %d/%d %03o v%d s%d offset %9s delay %s disp %s\n",
		ntoa(&pp->srcadr),
		pp->xmtcnt,pp->rcvcnt,pp->reach,
		pp->version,pp->stratum,
		lfptoa(&pp->offset, 6), ufptoa(pp->delay, 5),
		ufptoa(pp->dispersion, 4));
	    return;
	}

	(void) fprintf(fp, "server %s, port %d\n",
			   ntoa(&pp->srcadr), ntohs(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 = numtoa(pp->refid);
	}
	(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, received %d, reachable %03o\n",
	    pp->xmtcnt, pp->rcvcnt, pp->reach);

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

#if !defined(HAVE_VSPRINTF)
int
vsprintf(
	char *str,
	const char *fmt,
	va_list ap
	)
{
	FILE f;
	int len;

	f._flag = _IOWRT+_IOSTRG;
	f._ptr = str;
	f._cnt = 32767;
	len = _doprnt(fmt, ap, &f);
	*f._ptr = 0;
	return (len);
}
#endif