#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
# include <netdb.h>
# ifdef HAVE_SYS_SIGNAL_H
# include <sys/signal.h>
# else
# include <signal.h>
# endif
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
#ifdef SYS_VXWORKS
# include "ioLib.h"
# include "sockLib.h"
# include "timers.h"
#endif
#include "recvbuff.h"
#ifdef SYS_WINNT
# define TARGET_RESOLUTION 1
#pragma comment(lib, "winmm")
#endif
#ifndef SYS_VXWORKS
# define NTPDATE_PRIO (-12)
#else
# define NTPDATE_PRIO (100)
#endif
#if defined(HAVE_TIMER_SETTIME) || defined (HAVE_TIMER_CREATE)
static timer_t ntpdate_timerid;
#endif
#define NTP_MAXSKW 0x28f
#define NTP_MINDIST 0x51f
#define NTP_INFIN 15
#define NTP_MAXWGT (8*FP_SECOND)
#define NTP_MAXLIST 5
#define PEER_SHIFT 8
volatile int debug = 0;
int fd;
fd_set fdmask;
int initializing = 1;
volatile int alarm_flag = 0;
int set_time = 0;
#define MINTRANSMITS (3)
#define MAXXMITCOUNT (2)
#define DESIREDDISP (4*FP_SECOND)
int max_period = DEFMAXPERIOD;
int min_servers = DEFMINSERVERS;
int min_valid = DEFMINVALID;
int contacted = 0;
int responding = 0;
int validcount = 0;
int valid_n_low = 0;
int unpriv_port = 0;
char *progname;
struct server **sys_servers;
int sys_numservers = 0;
int sys_authenticate = 0;
u_int32 sys_authkey = 0;
u_long sys_authdelay = 0;
u_long current_time = 0;
#ifndef KEYFILE
# ifndef SYS_WINNT
#define KEYFILE "/etc/ntp.keys"
# else
#define KEYFILE "%windir%\\ntp.keys"
# endif
#endif
#ifndef SYS_WINNT
const char *key_file = KEYFILE;
#else
char key_file_storage[MAX_PATH+1], *key_file ;
#endif
u_long total_xmit = 0;
u_long total_recv = 0;
int verbose = 0;
#define HORRIBLEOK 3
int horrible = 0;
int secondhalf = 0;
int printmsg = 0;
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
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()
{
debug = 0;
ntp_optind = 0;
initializing = 1;
alarm_flag = 0;
unpriv_port = 0;
sys_numservers = 0;
sys_authenticate = 0;
sys_authkey = 0;
sys_authdelay = 0;
current_time = 0;
verbose = 0;
}
#endif
#ifndef NO_MAIN_ALLOWED
int
main(
int argc,
char *argv[]
)
{
return ntptimesetmain(argc, argv);
}
#endif
int
ntptimesetmain(
int argc,
char *argv[]
)
{
int was_alarmed;
struct recvbuf *rbuflist;
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
#ifdef NO_MAIN_ALLOWED
clear_globals();
#endif
errflg = 0;
cfgpath = 0;
progname = argv[0];
syslogit = 0;
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);
}
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
#endif
}
if (debug || verbose)
msyslog(LOG_INFO, "%s", Version);
if (horrible)
msyslog(LOG_INFO, "Dropping %d out of %d packets",
horrible,horrible+HORRIBLEOK);
loadservers(cfgpath);
if (sys_numservers < min_servers) {
msyslog(LOG_ERR, "Found %d servers, require %d servers",
sys_numservers,min_servers);
exit(2);
}
finish_time = max_period * TIMER_HZ;
half_time = finish_time >> 1;
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
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();
#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
initializing = 0;
was_alarmed = 0;
rbuflist = (struct recvbuf *)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
rbuflist = getrecvbufs();
if (printmsg) {
printmsg = 0;
analysis(0);
}
if (alarm_flag) {
was_alarmed = 1;
alarm_flag = 0;
}
if (!was_alarmed && rbuflist == (struct recvbuf *)0) {
#ifndef HAVE_SIGNALED_IO
rdfdes = fdmask;
# if defined(VMS) || defined(SYS_VXWORKS)
{
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
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
msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound);
# endif
}
#else
wait_for_signal();
#endif
if (alarm_flag)
{
was_alarmed = 1;
alarm_flag = 0;
}
rbuflist = getrecvbufs();
}
#ifdef HAVE_SIGNALED_IO
unblock_io_and_alarm();
#endif
if (was_alarmed)
{
timer();
was_alarmed = 0;
}
while (rbuflist != (struct recvbuf *)0)
{
rbuf = rbuflist;
rbuflist = rbuf->next;
receive(rbuf);
freerecvbuf(rbuf);
}
#if defined DEBUG && defined SYS_WINNT
if (debug > 4)
printf("getrecvbufs: %ld handler interrupts, %ld frames\n",
handler_calls, handler_pkts);
#endif
if (have_enough())
break;
}
set_local_clock();
return(1);
}
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;
}
}
static int
have_enough(void)
{
if (contacted < sys_numservers)
return 0;
if (responding < min_servers)
return 0;
(void) clock_count();
if (validcount <= 0 || validcount < min_valid)
return 0;
if (!secondhalf && valid_n_low != validcount)
return 0;
return 1;
}
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;
L_CLR(&ts);
clock_filter(server, 0, &ts);
}
server->reach <<= 1;
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);
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)));
}
if (++server->xmtcnt == MINTRANSMITS)
contacted++;
server->last_xmit = current_time;
if (server->xmtcnt < MINTRANSMITS) {
timeout = TIMER_HZ;
}
else if (server->rcvcnt <= 0) {
if (secondhalf)
timeout = TIMER_HZ<<4;
else
timeout = TIMER_HZ<<3;
}
else {
if (server->dispersion <= DESIREDDISP)
timeout = TIMER_HZ<<4;
else if (server->leap == LEAP_NOTINSYNC)
timeout = TIMER_HZ<<4;
else if (server->org.l_ui < server->reftime.l_ui)
timeout = TIMER_HZ<<5;
else if (secondhalf)
timeout = TIMER_HZ<<2;
else
timeout = TIMER_HZ<<1;
}
server->event_time = current_time + timeout;
}
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));
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;
}
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;
}
server = findserver(&(rbufp->srcadr));
if (server == NULL) {
if (debug > 1)
printf("receive: server not found\n");
return;
}
NTOHL_FP(&rpkt->org, &org);
if (!L_ISEQU(&org, &server->xmt)) {
if (debug > 1)
printf("receive: pkt.org and peer.xmt differ\n");
return;
}
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;
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);
server->reach |= 1;
if (server->rcvcnt++ == 0)
responding++;
if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) {
if (debug > 1)
printf("receive: pkt insane\n");
return;
}
t10 = server->org;
L_SUB(&t10, &rbufp->recv_time);
t23 = rec;
L_SUB(&t23, &org);
ci = t10;
L_ADD(&ci, &t23);
L_RSHIFT(&ci);
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) {
L_CLR(&ci);
di = 0;
} else {
di = max(di, NTP_MINDIST);
}
clock_filter(server, di, &ci);
if (debug > 1)
printf("receive from %s\n", ntoa(&rbufp->srcadr));
if (server->stratum <= NTP_INFIN)
return;
if (server->leap == LEAP_NOTINSYNC)
return;
if (!L_ISHIS(&server->org, &server->reftime))
return;
if (server->trust != 0)
return;
if (server->dispersion < DESIREDDISP)
return;
server->event_time -= (TIMER_HZ+1);
}
static void
clock_filter(
register struct server *server,
s_fp di,
l_fp *c
)
{
register int i, j;
int ord[NTP_SHIFT];
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;
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;
}
}
}
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;
}
server->dispersion += (u_fp)(d) >> i;
}
}
}
static void
clock_count(void)
{
register struct server *server;
register int n;
validcount = valid_n_low = 0;
for (n = 0; n < sys_numservers; n++) {
server = sys_servers[n];
if (server->delay == 0) {
continue;
}
if (server->stratum > NTP_INFIN) {
continue;
}
if (server->delay > NTP_MAXWGT) {
continue;
}
if (server->leap == LEAP_NOTINSYNC)
continue;
if (!L_ISHIS(&server->org, &server->reftime)) {
continue;
}
if ((server->org.l_ui - server->reftime.l_ui) >= NTP_MAXAGE) {
continue;
}
if (server->trust != 0) {
continue;
}
validcount++;
if (server->dispersion <= DESIREDDISP)
valid_n_low++;
}
if (debug > 1)
printf("have %d, valid %d, low %d\n",
responding, validcount, valid_n_low);
}
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;
nlist = 0;
for (n = 0; n < sys_numservers; n++) {
server = sys_servers[n];
if (server->delay == 0)
continue;
if (server->stratum > NTP_INFIN)
continue;
if (server->delay > NTP_MAXWGT) {
continue;
}
if (server->leap == LEAP_NOTINSYNC)
continue;
if (!L_ISHIS(&server->org, &server->reftime)) {
continue;
}
if ((server->org.l_ui - server->reftime.l_ui)
>= NTP_MAXAGE) {
continue;
}
if (server->trust != 0) {
continue;
}
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 >= 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++;
}
j = 0;
for (i = 1; i < nlist; i++)
if (server_list[i]->stratum > server_list[i-1]->stratum)
if (++j == 2) {
nlist = i;
break;
}
if (nlist == 0)
sys_server = 0;
else if (nlist == 1) {
sys_server = server_list[0];
} else {
for (i = 0; i < nlist-1; i++)
for (j = i+1; j < nlist; j++) {
if (server_list[i]->stratum
< server_list[j]->stratum)
break;
if (server_list[i]->delay
< server_list[j]->delay)
continue;
server = server_list[i];
server_list[i] = server_list[j];
server_list[j] = server;
}
local_threshold = (FP_SECOND >> (-(int)NTPDATE_PRECISION))
+ NTP_MAXSKW;
while (nlist > 1) {
for (n = 0; n < nlist; n++) {
server_badness[n] = 0;
for (j = 0; j < nlist; j++) {
if (j == n)
continue;
d = server_list[j]->soffset
- server_list[n]->soffset;
if (d < 0)
d = -d;
for (i = 0; i < j; i++)
d = (d>>1) + (d>>2);
server_badness[n] += d;
}
}
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;
}
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--;
}
sys_server = server_list[0];
}
return sys_server;
}
static void
set_local_clock(void)
{
register int i;
register struct server *server;
time_t tmp;
double dtemp;
if (set_time)
analysis(1);
server = clock_select();
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);
}
if (server == 0) {
if (!set_time || verbose)
fprintf(stdout,"No servers available to sync time with\n");
exit(1);
}
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);
}
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);
}
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;
}
static void
timer(void)
{
register int k;
current_time++;
if (current_time >= half_time && !secondhalf) {
secondhalf++;
if (debug)
printf("\nSecond Half of Timeout!\n");
printmsg++;
}
for(k = 0;k < MAXXMITCOUNT;k++) {
register int i, oldi;
register u_long oldxtime;
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;
}
}
#ifndef SYS_WINNT
static RETSIGTYPE
alarming(
int sig
)
#else
void CALLBACK
alarming(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
#endif
{
alarm_flag++;
}
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
#if defined SYS_CYGWIN32 || defined SYS_WINNT
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
DWORD dwUser = 0;
#endif
alarm_flag = 0;
#ifndef SYS_WINNT
# if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
alarm_flag = 0;
if (timer_create (CLOCK_REALTIME, NULL, &ntpdate_timerid) ==
# ifdef SYS_VXWORKS
ERROR
# else
-1
# endif
)
{
fprintf (stderr, "init_alarm(): timer_create (...) FAILED\n");
return;
}
(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 , &ntpdate_itimer, NULL);
# else
(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
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
msyslog(LOG_ERR, "OpenProcessToken failed: %m");
exit(1);
}
LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES) NULL, 0);
if (GetLastError() != ERROR_SUCCESS)
msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
#endif
#else
_tzset();
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
msyslog(LOG_ERR, "OpenProcessToken failed: %m");
exit(1);
}
LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES) NULL, 0);
if (GetLastError() != ERROR_SUCCESS)
msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
if(timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
msyslog(LOG_ERR, "timeGetDevCaps failed: %m");
exit(1);
}
wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
timeBeginPeriod(wTimerRes);
wTimerID = timeSetEvent(
(UINT) (1000/TIMER_HZ),
wTimerRes,
(LPTIMECALLBACK) alarming,
(DWORD) dwUser,
TIME_PERIODIC);
if (wTimerID == 0) {
msyslog(LOG_ERR, "timeSetEvent failed: %m");
exit(1);
}
#endif
}
static void
init_io(void)
{
#ifdef SYS_WINNT
WORD wVersionRequested;
WSADATA wsaData;
init_transmitbuff();
#endif
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
BLOCKIO();
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
msyslog(LOG_ERR, "socket() failed: %m");
exit(1);
}
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);
#ifdef USE_FIONBIO
#undef O_NONBLOCK
#undef FNDELAY
#undef O_NDELAY
#endif
#if defined(O_NONBLOCK)
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
{
msyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails: %m");
exit(1);
}
#elif defined(FNDELAY)
if (fcntl(fd, F_SETFL, FNDELAY) < 0)
{
msyslog(LOG_ERR, "fcntl(FNDELAY) fails: %m");
exit(1);
}
#elif defined(O_NDELAY)
if (fcntl(fd, F_SETFL, O_NDELAY) < 0)
{
msyslog(LOG_ERR, "fcntl(O_NDELAY) fails: %m");
exit(1);
}
#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);
}
#elif defined(FIOSNBIO)
if (ioctl(fd,FIOSNBIO,&on) < 0)
{
msyslog(LOG_ERR, "ioctl(FIOSNBIO) fails: %m");
exit(1);
}
#else
# include "Bletch: Need non-blocking I/O!"
#endif
#ifdef HAVE_SIGNALED_IO
init_socket_sig(fd);
#endif
UNBLOCKIO();
}
static int
sendpkt(
struct sockaddr_in *dest,
struct pkt *pkt,
int len
)
{
int cc;
static int horriblecnt = 0;
#ifdef SYS_WINNT
DWORD err;
#endif
total_xmit++;
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
msyslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
return -1;
}
return 0;
}
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;
for (;;) {
fds = fdmask;
tvzero.tv_sec = tvzero.tv_usec = 0;
n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
if (n == 0)
return;
else if (n == -1) {
if (errno != EINTR) {
msyslog(LOG_ERR, "select() error: %m");
}
return;
}
get_systime(&ts);
if (initializing || free_recvbuffs == 0) {
char buf[100];
#ifndef SYS_WINNT
(void) read(fd, buf, sizeof buf);
#else
recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)0, NULL);
#endif
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;
}
rb->recv_time = ts;
add_full_recv_buffer(rb);
total_recv++;
}
}
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