2055 lines
44 KiB
C
2055 lines
44 KiB
C
/* $NetBSD: ntpdate.c,v 1.2 2001/02/02 03:35:24 hubertf Exp $ */
|
|
|
|
/*
|
|
* ntpdate - set the time of day by polling one or more NTP servers
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
#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
|
|
#ifndef SYS_WINNT
|
|
# include <netdb.h>
|
|
# include <sys/signal.h>
|
|
# include <sys/ioctl.h>
|
|
#endif /* SYS_WINNT */
|
|
#include <sys/time.h>
|
|
#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"
|
|
|
|
/* select wants a zero structure ... */
|
|
struct timeval timeout = {0,0};
|
|
#else
|
|
struct timeval timeout = {60,0};
|
|
#endif
|
|
|
|
|
|
#if defined(SYS_HPUX)
|
|
# include <utmp.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINFO
|
|
#include <netinfo/ni.h>
|
|
#endif
|
|
|
|
#include "ntp_fp.h"
|
|
#include "ntp.h"
|
|
#include "ntp_io.h"
|
|
#include "ntp_unixtime.h"
|
|
#include "ntpdate.h"
|
|
#include "ntp_string.h"
|
|
#include "ntp_syslog.h"
|
|
#include "ntp_select.h"
|
|
#include "ntp_stdlib.h"
|
|
#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 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 */
|
|
|
|
/*
|
|
* Debugging flag
|
|
*/
|
|
volatile int debug = 0;
|
|
|
|
/*
|
|
* File descriptor masks etc. for call to select
|
|
*/
|
|
int fd;
|
|
#ifdef HAVE_POLL_H
|
|
struct pollfd fdmask;
|
|
#else
|
|
fd_set fdmask;
|
|
#endif
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/*
|
|
* Unpriviledged port flag.
|
|
*/
|
|
int unpriv_port = 0;
|
|
|
|
/*
|
|
* Time to spend measuring drift rate
|
|
*/
|
|
int rate = 0;
|
|
|
|
/*
|
|
* Program name.
|
|
*/
|
|
char *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 P((int, char **));
|
|
static void transmit P((struct server *));
|
|
static void receive P((struct recvbuf *));
|
|
static void server_data P((struct server *, s_fp, l_fp *, u_fp));
|
|
static void clock_filter P((struct server *));
|
|
static struct server *clock_select P((void));
|
|
static int clock_adjust P((void));
|
|
static void addserver P((char *));
|
|
static struct server *findserver P((struct sockaddr_in *));
|
|
void timer P((void));
|
|
static void init_alarm P((void));
|
|
#ifndef SYS_WINNT
|
|
static RETSIGTYPE alarming P((int));
|
|
#endif /* SYS_WINNT */
|
|
static void init_io P((void));
|
|
static void sendpkt P((struct sockaddr_in *, struct pkt *, int));
|
|
void input_handler P((void));
|
|
|
|
static int l_adj_systime P((l_fp *));
|
|
static int l_step_systime P((l_fp *));
|
|
|
|
static int getnetnum P((const char *, u_int32 *));
|
|
static void printserver P((struct server *, FILE *));
|
|
|
|
#ifdef SYS_WINNT
|
|
int on = 1;
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
HANDLE TimerThreadHandle = NULL;
|
|
#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;
|
|
|
|
/*
|
|
* Unpriviledged port flag.
|
|
*/
|
|
unpriv_port = 0;
|
|
|
|
/*
|
|
* Time to spend measuring drift rate
|
|
*/
|
|
rate = 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 P((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;
|
|
struct recvbuf *rbuflist;
|
|
struct recvbuf *rbuf;
|
|
l_fp tmp;
|
|
int errflg;
|
|
int c;
|
|
#ifdef HAVE_NETINFO
|
|
ni_namelist *netinfoservers;
|
|
#endif
|
|
#ifdef SYS_WINNT
|
|
HANDLE process_handle;
|
|
|
|
wVersionRequested = MAKEWORD(1,1);
|
|
if (WSAStartup(wVersionRequested, &wsaData)) {
|
|
msyslog(LOG_ERR, "No useable winsock.dll: %m");
|
|
exit(1);
|
|
}
|
|
|
|
key_file = key_file_storage;
|
|
|
|
if (!ExpandEnvironmentStrings(KEYFILE, key_file, MAX_PATH))
|
|
{
|
|
msyslog(LOG_ERR, "ExpandEnvironmentStrings(KEYFILE) failed: %m\n");
|
|
}
|
|
#endif /* SYS_WINNT */
|
|
|
|
#ifdef NO_MAIN_ALLOWED
|
|
clear_globals();
|
|
#endif
|
|
|
|
errflg = 0;
|
|
progname = argv[0];
|
|
syslogit = 0;
|
|
|
|
/*
|
|
* Decode argument list
|
|
*/
|
|
while ((c = ntp_getopt(argc, argv, "a:bBde:k:o:p:qr:st:uv")) != EOF)
|
|
switch (c)
|
|
{
|
|
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 'r':
|
|
c = atoi(ntp_optarg);
|
|
if (c <= 0 || c > (60 * 60)) {
|
|
(void) fprintf(stderr,
|
|
"%s: rate (%d) is invalid: 0 - %d\n",
|
|
progname, c, (60 * 60));
|
|
errflg++;
|
|
} else {
|
|
rate = c;
|
|
}
|
|
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;
|
|
if (sys_timeout == 0)
|
|
sys_timeout = 1;
|
|
}
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'u':
|
|
unpriv_port = 1;
|
|
break;
|
|
case '?':
|
|
++errflg;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (errflg) {
|
|
(void) fprintf(stderr,
|
|
"usage: %s [-bBdqsuv] [-a key#] [-e delay] [-k file] [-p samples] [-o version#] [-r rate] [-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, exitting");
|
|
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;
|
|
|
|
was_alarmed = 0;
|
|
rbuflist = (struct recvbuf *)0;
|
|
while (complete_servers < sys_numservers) {
|
|
#ifdef HAVE_POLL_H
|
|
struct pollfd rdfdes;
|
|
#else
|
|
fd_set rdfdes;
|
|
#endif
|
|
int nfound;
|
|
|
|
if (alarm_flag) { /* alarmed? */
|
|
was_alarmed = 1;
|
|
alarm_flag = 0;
|
|
}
|
|
rbuflist = getrecvbufs(); /* get received buffers */
|
|
|
|
if (!was_alarmed && rbuflist == (struct recvbuf *)0) {
|
|
/*
|
|
* Nothing to do. Wait for something.
|
|
*/
|
|
rdfdes = fdmask;
|
|
#ifdef HAVE_POLL_H
|
|
nfound = poll(&rdfdes, 1, timeout.tv_sec * 1000);
|
|
#else
|
|
nfound = select(fd+1, &rdfdes, (fd_set *)0,
|
|
(fd_set *)0, &timeout);
|
|
#endif
|
|
if (nfound > 0)
|
|
input_handler();
|
|
else if (
|
|
#ifndef SYS_WINNT
|
|
nfound == -1
|
|
#else
|
|
nfound == SOCKET_ERROR
|
|
#endif /* SYS_WINNT */
|
|
) {
|
|
#ifndef SYS_WINNT
|
|
if (errno != EINTR)
|
|
#endif
|
|
msyslog(LOG_ERR,
|
|
#ifdef HAVE_POLL_H
|
|
"poll() error: %m"
|
|
#else
|
|
"select() error: %m"
|
|
#endif
|
|
);
|
|
} else {
|
|
#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;
|
|
}
|
|
rbuflist = getrecvbufs(); /* get received buffers */
|
|
}
|
|
|
|
/*
|
|
* Out here, signals are unblocked. Call receive
|
|
* procedure for each incoming packet.
|
|
*/
|
|
while (rbuflist != (struct recvbuf *)0) {
|
|
rbuf = rbuflist;
|
|
rbuflist = rbuf->next;
|
|
receive(rbuf);
|
|
freerecvbuf(rbuf);
|
|
}
|
|
|
|
/*
|
|
* 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", ntoa(&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.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);
|
|
sendpkt(&(server->srcadr), &xpkt, (int)(LEN_PKT_NOMAC + len));
|
|
|
|
if (debug > 1)
|
|
printf("transmit auth to %s\n",
|
|
ntoa(&(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", ntoa(&(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;
|
|
l_fp org;
|
|
l_fp rec;
|
|
l_fp ci;
|
|
int has_mac;
|
|
int is_authentic;
|
|
|
|
if (debug)
|
|
printf("receive(%s)\n", ntoa(&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 >= 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 > NTP_MAXSTRATUM) {
|
|
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, (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);
|
|
|
|
/*
|
|
* Make sure the server is at least somewhat sane. If not, try
|
|
* again.
|
|
*/
|
|
if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) {
|
|
transmit(server);
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Shift this data in, then transmit again.
|
|
*/
|
|
server_data(server, (s_fp) di, &ci, 0);
|
|
transmit(server);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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
|
|
)
|
|
{
|
|
register int 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 = 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)
|
|
{
|
|
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 (server = sys_servers; server != NULL; server = server->next_server) {
|
|
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 ( (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;
|
|
s_fp absoffset;
|
|
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 {
|
|
absoffset = server->soffset;
|
|
if (absoffset < 0)
|
|
absoffset = -absoffset;
|
|
dostep = (absoffset >= NTPDATE_THRESHOLD);
|
|
}
|
|
|
|
if (dostep) {
|
|
if (simple_query || l_step_systime(&server->offset)) {
|
|
msyslog(LOG_NOTICE, "step time server %s offset %s sec",
|
|
ntoa(&server->srcadr),
|
|
lfptoa(&server->offset, 6));
|
|
}
|
|
} else {
|
|
#if !defined SYS_WINNT && !defined SYS_CYGWIN32
|
|
if (simple_query || l_adj_systime(&server->offset)) {
|
|
msyslog(LOG_NOTICE, "adjust time server %s offset %s sec",
|
|
ntoa(&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);
|
|
}
|
|
|
|
|
|
/* 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;
|
|
u_int32 netnum;
|
|
|
|
if (!getnetnum(serv, &netnum)) {
|
|
msyslog(LOG_ERR, "can't find host %s\n", serv);
|
|
return;
|
|
}
|
|
|
|
server = (struct server *)emalloc(sizeof(struct server));
|
|
memset((char *)server, 0, sizeof(struct server));
|
|
|
|
server->srcadr.sin_family = AF_INET;
|
|
server->srcadr.sin_addr.s_addr = netnum;
|
|
server->srcadr.sin_port = htons(NTP_PORT);
|
|
|
|
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) ;
|
|
sp->next_server = server;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* findserver - find a server in the list given its address
|
|
*/
|
|
static struct server *
|
|
findserver(
|
|
struct sockaddr_in *addr
|
|
)
|
|
{
|
|
register u_int32 netnum;
|
|
struct server *server;
|
|
struct server *mc_server;
|
|
|
|
mc_server = NULL;
|
|
if (htons(addr->sin_port) != NTP_PORT)
|
|
return 0;
|
|
netnum = addr->sin_addr.s_addr;
|
|
|
|
for (server = sys_servers; server != NULL;
|
|
server = server->next_server) {
|
|
register u_int32 servnum;
|
|
|
|
servnum = server->srcadr.sin_addr.s_addr;
|
|
if (netnum == servnum)
|
|
return server;
|
|
if (IN_MULTICAST(ntohl(servnum)))
|
|
mc_server = server;
|
|
}
|
|
|
|
if (mc_server != NULL) {
|
|
struct server *sp;
|
|
|
|
if (mc_server->event_time != 0) {
|
|
mc_server->event_time = 0;
|
|
complete_servers++;
|
|
}
|
|
server = (struct server *)emalloc(sizeof(struct server));
|
|
memset((char *)server, 0, sizeof(struct server));
|
|
|
|
server->srcadr.sin_family = AF_INET;
|
|
server->srcadr.sin_addr.s_addr = netnum;
|
|
server->srcadr.sin_port = htons(NTP_PORT);
|
|
|
|
server->event_time = ++sys_numservers;
|
|
for (sp = sys_servers; sp->next_server != NULL;
|
|
sp = sp->next_server) ;
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
#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 */
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
/*
|
|
* Init buffer free list and stat counters
|
|
*/
|
|
init_recvbuff(sys_numservers + 2);
|
|
/*
|
|
* Open the socket
|
|
*/
|
|
|
|
/* 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 && !simple_query && !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 /* SYS_WINNT */
|
|
msyslog(LOG_ERR,
|
|
"the NTP socket is in use, exiting");
|
|
else
|
|
msyslog(LOG_ERR, "bind() fails: %m");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_POLL_H
|
|
fdmask.fd = fd;
|
|
fdmask.events = POLLIN;
|
|
#else
|
|
FD_ZERO(&fdmask);
|
|
FD_SET(fd, &fdmask);
|
|
#endif
|
|
|
|
/*
|
|
* set non-blocking,
|
|
*/
|
|
#ifndef SYS_WINNT
|
|
# ifdef SYS_VXWORKS
|
|
{
|
|
int on = TRUE;
|
|
|
|
if (ioctl(fd,FIONBIO, &on) == ERROR) {
|
|
msyslog(LOG_ERR, "ioctl(FIONBIO) fails: %m");
|
|
exit(1);
|
|
}
|
|
}
|
|
# else /* not SYS_VXWORKS */
|
|
# if defined(O_NONBLOCK)
|
|
if (fcntl(fd, 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, 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, FIONBIO, (u_long *) &on) == SOCKET_ERROR) {
|
|
msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
|
|
exit(1);
|
|
}
|
|
#endif /* SYS_WINNT */
|
|
}
|
|
|
|
|
|
/*
|
|
* sendpkt - send a packet to the specified destination
|
|
*/
|
|
static void
|
|
sendpkt(
|
|
struct sockaddr_in *dest,
|
|
struct pkt *pkt,
|
|
int len
|
|
)
|
|
{
|
|
int cc;
|
|
|
|
#ifdef SYS_WINNT
|
|
DWORD err;
|
|
#endif /* SYS_WINNT */
|
|
|
|
cc = sendto(fd, (char *)pkt, 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));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* input_handler - receive packets asynchronously
|
|
*/
|
|
void
|
|
input_handler(void)
|
|
{
|
|
register int n;
|
|
register struct recvbuf *rb;
|
|
struct timeval tvzero;
|
|
int fromlen;
|
|
l_fp ts;
|
|
#ifdef HAVE_POLL_H
|
|
struct pollfd fds;
|
|
#else
|
|
fd_set fds;
|
|
#endif
|
|
|
|
/*
|
|
* Do a poll to see if we have data
|
|
*/
|
|
for (;;) {
|
|
fds = fdmask;
|
|
tvzero.tv_sec = tvzero.tv_usec = 0;
|
|
#ifdef HAVE_POLL_H
|
|
n = poll(&fds, 1, tvzero.tv_sec * 1000);
|
|
#else
|
|
n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
|
|
#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(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->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");
|
|
return 0;
|
|
}
|
|
}
|
|
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 */
|
|
}
|
|
|
|
/*
|
|
* getnetnum - given a host name, return its net number
|
|
*/
|
|
static int
|
|
getnetnum(
|
|
const char *host,
|
|
u_int32 *num
|
|
)
|
|
{
|
|
struct hostent *hp;
|
|
|
|
if (decodenetnum(host, num)) {
|
|
return 1;
|
|
} else if ((hp = gethostbyname(host)) != 0) {
|
|
memmove((char *)num, hp->h_addr, sizeof(u_int32));
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* 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, "server %s, stratum %d, offset %s, delay %s\n",
|
|
ntoa(&pp->srcadr), pp->stratum,
|
|
lfptoa(&pp->offset, 6), fptoa((s_fp)pp->delay, 5));
|
|
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, 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));
|
|
}
|
|
|
|
#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
|
|
|
|
#if 0
|
|
/* override function in library since SA_RESTART makes ALL syscalls restart */
|
|
#ifdef SA_RESTART
|
|
void
|
|
signal_no_reset(
|
|
int sig,
|
|
void (*func)()
|
|
)
|
|
{
|
|
int n;
|
|
struct sigaction vec;
|
|
|
|
vec.sa_handler = func;
|
|
sigemptyset(&vec.sa_mask);
|
|
vec.sa_flags = 0;
|
|
|
|
while (1)
|
|
{
|
|
n = sigaction(sig, &vec, NULL);
|
|
if (n == -1 && errno == EINTR)
|
|
continue;
|
|
break;
|
|
}
|
|
if (n == -1)
|
|
{
|
|
perror("sigaction");
|
|
exit(1);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINFO
|
|
static ni_namelist *
|
|
getnetinfoservers(void)
|
|
{
|
|
ni_status status;
|
|
void *domain;
|
|
ni_id confdir;
|
|
ni_namelist *namelist = (ni_namelist*)malloc(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
|