diff options
Diffstat (limited to 'ntpdate/ntpdate.c')
-rw-r--r-- | ntpdate/ntpdate.c | 2246 |
1 files changed, 2246 insertions, 0 deletions
diff --git a/ntpdate/ntpdate.c b/ntpdate/ntpdate.c new file mode 100644 index 0000000..16569f3 --- /dev/null +++ b/ntpdate/ntpdate.c @@ -0,0 +1,2246 @@ +/* + * ntpdate - set the time of day by polling one or more NTP servers + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_NETINFO +#include <netinfo/ni.h> +#endif + +#include "ntp_machine.h" +#include "ntp_fp.h" +#include "ntp.h" +#include "ntp_io.h" +#include "timevalops.h" +#include "ntpdate.h" +#include "ntp_string.h" +#include "ntp_syslog.h" +#include "ntp_select.h" +#include "ntp_stdlib.h" +#include <ssl_applink.c> + +#include "isc/net.h" +#include "isc/result.h" +#include "isc/sockaddr.h" + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#ifdef HAVE_POLL_H +# include <poll.h> +#endif +#ifdef HAVE_SYS_SIGNAL_H +# include <sys/signal.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif + +#include <arpa/inet.h> + +#ifdef SYS_VXWORKS +# include "ioLib.h" +# include "sockLib.h" +# include "timers.h" + +/* select wants a zero structure ... */ +struct timeval timeout = {0,0}; +#elif defined(SYS_WINNT) +/* + * Windows does not abort a select select call if SIGALRM goes off + * so a 200 ms timeout is needed (TIMER_HZ is 5). + */ +struct sock_timeval timeout = {0,1000000/TIMER_HZ}; +#else +struct timeval timeout = {60,0}; +#endif + +#ifdef HAVE_NETINFO +#include <netinfo/ni.h> +#endif + +#include "recvbuff.h" + +#ifdef SYS_WINNT +#define TARGET_RESOLUTION 1 /* Try for 1-millisecond accuracy + on Windows NT timers. */ +#pragma comment(lib, "winmm") +isc_boolean_t ntp_port_inuse(int af, u_short port); +UINT wTimerRes; +#endif /* SYS_WINNT */ + +/* + * Scheduling priority we run at + */ +#ifndef SYS_VXWORKS +# define NTPDATE_PRIO (-12) +#else +# define NTPDATE_PRIO (100) +#endif + +#ifdef HAVE_TIMER_CREATE +/* POSIX TIMERS - vxWorks doesn't have itimer - casey */ +static timer_t ntpdate_timerid; +#endif + +/* + * Compatibility stuff for Version 2 + */ +#define NTP_MAXSKW 0x28f /* 0.01 sec in fp format */ +#define NTP_MINDIST 0x51f /* 0.02 sec in fp format */ +#define PEER_MAXDISP (64*FP_SECOND) /* maximum dispersion (fp 64) */ +#define NTP_INFIN 15 /* max stratum, infinity a la Bellman-Ford */ +#define NTP_MAXWGT (8*FP_SECOND) /* maximum select weight 8 seconds */ +#define NTP_MAXLIST 5 /* maximum select list size */ +#define PEER_SHIFT 8 /* 8 suitable for crystal time base */ + +/* + * for get_systime() + */ +s_char sys_precision; /* local clock precision (log2 s) */ + +/* + * File descriptor masks etc. for call to select + */ + +int ai_fam_templ; +int nbsock; /* the number of sockets used */ +SOCKET fd[MAX_AF]; +int fd_family[MAX_AF]; /* to remember the socket family */ +#ifdef HAVE_POLL_H +struct pollfd fdmask[MAX_AF]; +#else +fd_set fdmask; +SOCKET maxfd; +#endif +int polltest = 0; + +/* + * Initializing flag. All async routines watch this and only do their + * thing when it is clear. + */ +int initializing = 1; + +/* + * Alarm flag. Set when an alarm occurs + */ +volatile int alarm_flag = 0; + +/* + * Simple query flag. + */ +int simple_query = 0; + +/* + * Unprivileged port flag. + */ +int unpriv_port = 0; + +/* + * Program name. + */ +char *progname; + +/* + * Systemwide parameters and flags + */ +int sys_samples = DEFSAMPLES; /* number of samples/server */ +u_long sys_timeout = DEFTIMEOUT; /* timeout time, in TIMER_HZ units */ +struct server *sys_servers; /* the server list */ +int sys_numservers = 0; /* number of servers to poll */ +int sys_authenticate = 0; /* true when authenticating */ +u_int32 sys_authkey = 0; /* set to authentication key in use */ +u_long sys_authdelay = 0; /* authentication delay */ +int sys_version = NTP_VERSION; /* version to poll with */ + +/* + * The current internal time + */ +u_long current_time = 0; + +/* + * Counter for keeping track of completed servers + */ +int complete_servers = 0; + +/* + * File of encryption keys + */ + +#ifndef KEYFILE +# ifndef SYS_WINNT +#define KEYFILE "/etc/ntp.keys" +# else +#define KEYFILE "%windir%\\ntp.keys" +# endif /* SYS_WINNT */ +#endif /* KEYFILE */ + +#ifndef SYS_WINNT +const char *key_file = KEYFILE; +#else +char key_file_storage[MAX_PATH+1], *key_file ; +#endif /* SYS_WINNT */ + +/* + * Miscellaneous flags + */ +int verbose = 0; +int always_step = 0; +int never_step = 0; + +int ntpdatemain (int, char **); + +static void transmit (struct server *); +static void receive (struct recvbuf *); +static void server_data (struct server *, s_fp, l_fp *, u_fp); +static void clock_filter (struct server *); +static struct server *clock_select (void); +static int clock_adjust (void); +static void addserver (char *); +static struct server *findserver (sockaddr_u *); + void timer (void); +static void init_alarm (void); +#ifndef SYS_WINNT +static RETSIGTYPE alarming (int); +#endif /* SYS_WINNT */ +static void init_io (void); +static void sendpkt (sockaddr_u *, struct pkt *, int); +void input_handler (void); + +static int l_adj_systime (l_fp *); +static int l_step_systime (l_fp *); + +static void printserver (struct server *, FILE *); + +#ifdef SYS_WINNT +int on = 1; +WORD wVersionRequested; +WSADATA wsaData; +#endif /* SYS_WINNT */ + +#ifdef NO_MAIN_ALLOWED +CALL(ntpdate,"ntpdate",ntpdatemain); + +void clear_globals() +{ + /* + * Debugging flag + */ + debug = 0; + + ntp_optind = 0; + /* + * Initializing flag. All async routines watch this and only do their + * thing when it is clear. + */ + initializing = 1; + + /* + * Alarm flag. Set when an alarm occurs + */ + alarm_flag = 0; + + /* + * Simple query flag. + */ + simple_query = 0; + + /* + * Unprivileged port flag. + */ + unpriv_port = 0; + + /* + * Systemwide parameters and flags + */ + sys_numservers = 0; /* number of servers to poll */ + sys_authenticate = 0; /* true when authenticating */ + sys_authkey = 0; /* set to authentication key in use */ + sys_authdelay = 0; /* authentication delay */ + sys_version = NTP_VERSION; /* version to poll with */ + + /* + * The current internal time + */ + current_time = 0; + + /* + * Counter for keeping track of completed servers + */ + complete_servers = 0; + verbose = 0; + always_step = 0; + never_step = 0; +} +#endif + +#ifdef HAVE_NETINFO +static ni_namelist *getnetinfoservers (void); +#endif + +/* + * Main program. Initialize us and loop waiting for I/O and/or + * timer expiries. + */ +#ifndef NO_MAIN_ALLOWED +int +main( + int argc, + char *argv[] + ) +{ + return ntpdatemain (argc, argv); +} +#endif /* NO_MAIN_ALLOWED */ + +int +ntpdatemain ( + int argc, + char *argv[] + ) +{ + int was_alarmed; + int tot_recvbufs; + struct recvbuf *rbuf; + l_fp tmp; + int errflg; + int c; + int nfound; + +#ifdef HAVE_NETINFO + ni_namelist *netinfoservers; +#endif +#ifdef SYS_WINNT + key_file = key_file_storage; + + if (!ExpandEnvironmentStrings(KEYFILE, key_file, MAX_PATH)) + msyslog(LOG_ERR, "ExpandEnvironmentStrings(KEYFILE) failed: %m"); + + ssl_applink(); +#endif /* SYS_WINNT */ + +#ifdef NO_MAIN_ALLOWED + clear_globals(); +#endif + + init_lib(); /* sets up ipv4_works, ipv6_works */ + + /* Check to see if we have IPv6. Otherwise default to IPv4 */ + if (!ipv6_works) + ai_fam_templ = AF_INET; + + errflg = 0; + progname = argv[0]; + syslogit = 0; + + /* + * Decode argument list + */ + while ((c = ntp_getopt(argc, argv, "46a:bBde:k:o:p:qst:uv")) != EOF) + switch (c) + { + case '4': + ai_fam_templ = AF_INET; + break; + case '6': + ai_fam_templ = AF_INET6; + break; + case 'a': + c = atoi(ntp_optarg); + sys_authenticate = 1; + sys_authkey = c; + break; + case 'b': + always_step++; + never_step = 0; + break; + case 'B': + never_step++; + always_step = 0; + break; + case 'd': + ++debug; + break; + case 'e': + if (!atolfp(ntp_optarg, &tmp) + || tmp.l_ui != 0) { + (void) fprintf(stderr, + "%s: encryption delay %s is unlikely\n", + progname, ntp_optarg); + errflg++; + } else { + sys_authdelay = tmp.l_uf; + } + break; + case 'k': + key_file = ntp_optarg; + break; + case 'o': + sys_version = atoi(ntp_optarg); + break; + case 'p': + c = atoi(ntp_optarg); + if (c <= 0 || c > NTP_SHIFT) { + (void) fprintf(stderr, + "%s: number of samples (%d) is invalid\n", + progname, c); + errflg++; + } else { + sys_samples = c; + } + break; + case 'q': + simple_query = 1; + break; + case 's': + syslogit = 1; + break; + case 't': + if (!atolfp(ntp_optarg, &tmp)) { + (void) fprintf(stderr, + "%s: timeout %s is undecodeable\n", + progname, ntp_optarg); + errflg++; + } else { + sys_timeout = ((LFPTOFP(&tmp) * TIMER_HZ) + + 0x8000) >> 16; + sys_timeout = max(sys_timeout, MINTIMEOUT); + } + break; + case 'v': + verbose = 1; + break; + case 'u': + unpriv_port = 1; + break; + case '?': + ++errflg; + break; + default: + break; + } + + if (errflg) { + (void) fprintf(stderr, + "usage: %s [-46bBdqsuv] [-a key#] [-e delay] [-k file] [-p samples] [-o version#] [-t timeo] server ...\n", + progname); + exit(2); + } + + if (debug || simple_query) { +#ifdef HAVE_SETVBUF + static char buf[BUFSIZ]; + setvbuf(stdout, buf, _IOLBF, BUFSIZ); +#else + setlinebuf(stdout); +#endif + } + + /* + * Logging. Open the syslog if we have to + */ + if (syslogit) { +#if !defined (SYS_WINNT) && !defined (SYS_VXWORKS) && !defined SYS_CYGWIN32 +# ifndef LOG_DAEMON + openlog("ntpdate", LOG_PID); +# else + +# ifndef LOG_NTP +# define LOG_NTP LOG_DAEMON +# endif + openlog("ntpdate", LOG_PID | LOG_NDELAY, LOG_NTP); + if (debug) + setlogmask(LOG_UPTO(LOG_DEBUG)); + else + setlogmask(LOG_UPTO(LOG_INFO)); +# endif /* LOG_DAEMON */ +#endif /* SYS_WINNT */ + } + + if (debug || verbose) + msyslog(LOG_NOTICE, "%s", Version); + + /* + * Add servers we are going to be polling + */ +#ifdef HAVE_NETINFO + netinfoservers = getnetinfoservers(); +#endif + + for ( ; ntp_optind < argc; ntp_optind++) + addserver(argv[ntp_optind]); + +#ifdef HAVE_NETINFO + if (netinfoservers) { + if ( netinfoservers->ni_namelist_len && + *netinfoservers->ni_namelist_val ) { + u_int servercount = 0; + while (servercount < netinfoservers->ni_namelist_len) { + if (debug) msyslog(LOG_DEBUG, + "Adding time server %s from NetInfo configuration.", + netinfoservers->ni_namelist_val[servercount]); + addserver(netinfoservers->ni_namelist_val[servercount++]); + } + } + ni_namelist_free(netinfoservers); + free(netinfoservers); + } +#endif + + if (sys_numservers == 0) { + msyslog(LOG_ERR, "no servers can be used, exiting"); + exit(1); + } + + /* + * Initialize the time of day routines and the I/O subsystem + */ + if (sys_authenticate) { + init_auth(); + if (!authreadkeys(key_file)) { + msyslog(LOG_ERR, "no key file <%s>, exiting", key_file); + exit(1); + } + authtrust(sys_authkey, 1); + if (!authistrusted(sys_authkey)) { + msyslog(LOG_ERR, "authentication key %lu unknown", + (unsigned long) sys_authkey); + exit(1); + } + } + init_io(); + init_alarm(); + + /* + * Set the priority. + */ +#ifdef SYS_VXWORKS + taskPrioritySet( taskIdSelf(), NTPDATE_PRIO); +#endif +#if defined(HAVE_ATT_NICE) + nice (NTPDATE_PRIO); +#endif +#if defined(HAVE_BSD_NICE) + (void) setpriority(PRIO_PROCESS, 0, NTPDATE_PRIO); +#endif + + + initializing = 0; + was_alarmed = 0; + + while (complete_servers < sys_numservers) { +#ifdef HAVE_POLL_H + struct pollfd* rdfdes; + rdfdes = fdmask; +#else + fd_set rdfdes; + rdfdes = fdmask; +#endif + + if (alarm_flag) { /* alarmed? */ + was_alarmed = 1; + alarm_flag = 0; + } + tot_recvbufs = full_recvbuffs(); /* get received buffers */ + + if (!was_alarmed && tot_recvbufs == 0) { + /* + * Nothing to do. Wait for something. + */ +#ifdef HAVE_POLL_H + nfound = poll(rdfdes, (unsigned int)nbsock, timeout.tv_sec * 1000); + +#else + nfound = select(maxfd, &rdfdes, (fd_set *)0, + (fd_set *)0, &timeout); +#endif + if (nfound > 0) + input_handler(); + else if (nfound == SOCKET_ERROR) + { +#ifndef SYS_WINNT + if (errno != EINTR) +#else + if (WSAGetLastError() != WSAEINTR) +#endif + msyslog(LOG_ERR, +#ifdef HAVE_POLL_H + "poll() error: %m" +#else + "select() error: %m" +#endif + ); + } else if (errno != 0) { +#ifndef SYS_VXWORKS + msyslog(LOG_DEBUG, +#ifdef HAVE_POLL_H + "poll(): nfound = %d, error: %m", +#else + "select(): nfound = %d, error: %m", +#endif + nfound); +#endif + } + if (alarm_flag) { /* alarmed? */ + was_alarmed = 1; + alarm_flag = 0; + } + tot_recvbufs = full_recvbuffs(); /* get received buffers */ + } + + /* + * Out here, signals are unblocked. Call receive + * procedure for each incoming packet. + */ + rbuf = get_full_recv_buffer(); + while (rbuf != NULL) + { + receive(rbuf); + freerecvbuf(rbuf); + rbuf = get_full_recv_buffer(); + } + + /* + * Call timer to process any timeouts + */ + if (was_alarmed) { + timer(); + was_alarmed = 0; + } + + /* + * Go around again + */ + } + + /* + * When we get here we've completed the polling of all servers. + * Adjust the clock, then exit. + */ +#ifdef SYS_WINNT + WSACleanup(); +#endif +#ifdef SYS_VXWORKS + close (fd); + timer_delete(ntpdate_timerid); +#endif + + return clock_adjust(); +} + + +/* + * transmit - transmit a packet to the given server, or mark it completed. + * This is called by the timeout routine and by the receive + * procedure. + */ +static void +transmit( + register struct server *server + ) +{ + struct pkt xpkt; + + if (debug) + printf("transmit(%s)\n", stoa(&server->srcadr)); + + if (server->filter_nextpt < server->xmtcnt) { + l_fp ts; + /* + * Last message to this server timed out. Shift + * zeros into the filter. + */ + L_CLR(&ts); + server_data(server, 0, &ts, 0); + } + + if ((int)server->filter_nextpt >= sys_samples) { + /* + * Got all the data we need. Mark this guy + * completed and return. + */ + server->event_time = 0; + complete_servers++; + return; + } + + /* + * If we're here, send another message to the server. Fill in + * the packet and let 'er rip. + */ + xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC, + sys_version, MODE_CLIENT); + xpkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC); + xpkt.ppoll = NTP_MINPOLL; + xpkt.precision = NTPDATE_PRECISION; + xpkt.rootdelay = htonl(NTPDATE_DISTANCE); + xpkt.rootdisp = htonl(NTPDATE_DISP); + xpkt.refid = htonl(NTPDATE_REFID); + L_CLR(&xpkt.reftime); + L_CLR(&xpkt.org); + L_CLR(&xpkt.rec); + + /* + * Determine whether to authenticate or not. If so, + * fill in the extended part of the packet and do it. + * If not, just timestamp it and send it away. + */ + if (sys_authenticate) { + 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", + stoa(&server->srcadr)); + } else { + get_systime(&(server->xmt)); + HTONL_FP(&server->xmt, &xpkt.xmt); + sendpkt(&server->srcadr, &xpkt, LEN_PKT_NOMAC); + + if (debug > 1) + printf("transmit to %s\n", stoa(&server->srcadr)); + } + + /* + * Update the server timeout and transmit count + */ + server->event_time = current_time + sys_timeout; + server->xmtcnt++; +} + + +/* + * receive - receive and process an incoming frame + */ +static void +receive( + struct recvbuf *rbufp + ) +{ + register struct pkt *rpkt; + register struct server *server; + register s_fp di; + l_fp t10, t23, tmp; + l_fp org; + l_fp rec; + l_fp ci; + int has_mac; + int is_authentic; + + if (debug) + printf("receive(%s)\n", stoa(&rbufp->recv_srcadr)); + /* + * Check to see if the packet basically looks like something + * intended for us. + */ + if (rbufp->recv_length == LEN_PKT_NOMAC) + has_mac = 0; + else if (rbufp->recv_length >= LEN_PKT_NOMAC) + has_mac = 1; + else { + if (debug) + printf("receive: packet length %d\n", + rbufp->recv_length); + return; /* funny length packet */ + } + + rpkt = &(rbufp->recv_pkt); + if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION || + PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) { + return; + } + + if ((PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER + && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) + || rpkt->stratum >= STRATUM_UNSPEC) { + if (debug) + printf("receive: mode %d stratum %d\n", + PKT_MODE(rpkt->li_vn_mode), rpkt->stratum); + return; + } + + /* + * So far, so good. See if this is from a server we know. + */ + server = findserver(&(rbufp->recv_srcadr)); + if (server == NULL) { + if (debug) + printf("receive: server not found\n"); + return; + } + + /* + * Decode the org timestamp and make sure we're getting a response + * to our last request. + */ + NTOHL_FP(&rpkt->org, &org); + if (!L_ISEQU(&org, &server->xmt)) { + if (debug) + printf("receive: pkt.org and peer.xmt differ\n"); + return; + } + + /* + * Check out the authenticity if we're doing that. + */ + if (!sys_authenticate) + is_authentic = 1; + else { + is_authentic = 0; + + if (debug > 3) + printf("receive: rpkt keyid=%ld sys_authkey=%ld decrypt=%ld\n", + (long int)ntohl(rpkt->exten[0]), (long int)sys_authkey, + (long int)authdecrypt(sys_authkey, (u_int32 *)rpkt, + LEN_PKT_NOMAC, (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; + + /* + * Check for a KoD (rate limiting) response, cease and decist. + */ + if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode) && + STRATUM_PKT_UNSPEC == rpkt->stratum && + !memcmp("RATE", &rpkt->refid, 4)) { + msyslog(LOG_ERR, "%s rate limit response from server.", + stoa(&rbufp->recv_srcadr)); + server->event_time = 0; + complete_servers++; + return; + } + + /* + * Looks good. Record info from the packet. + */ + server->leap = PKT_LEAP(rpkt->li_vn_mode); + server->stratum = PKT_TO_STRATUM(rpkt->stratum); + server->precision = rpkt->precision; + server->rootdelay = ntohl(rpkt->rootdelay); + server->rootdisp = ntohl(rpkt->rootdisp); + server->refid = rpkt->refid; + NTOHL_FP(&rpkt->reftime, &server->reftime); + NTOHL_FP(&rpkt->rec, &rec); + NTOHL_FP(&rpkt->xmt, &server->org); + + /* + * Make sure the server is at least somewhat sane. If not, try + * again. + */ + if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) { + server->event_time = current_time + sys_timeout; + return; + } + + /* + * Calculate the round trip delay (di) and the clock offset (ci). + * We use the equations (reordered from those in the spec): + * + * d = (t2 - t3) - (t1 - t0) + * c = ((t2 - t3) + (t1 - t0)) / 2 + */ + t10 = server->org; /* pkt.xmt == t1 */ + L_SUB(&t10, &rbufp->recv_time); /* recv_time == t0*/ + + t23 = rec; /* pkt.rec == t2 */ + L_SUB(&t23, &org); /* pkt->org == t3 */ + + /* now have (t2 - t3) and (t0 - t1). Calculate (ci) and (di) */ + /* + * Calculate (ci) = ((t1 - t0) / 2) + ((t2 - t3) / 2) + * For large offsets this may prevent an overflow on '+' + */ + ci = t10; + L_RSHIFT(&ci); + tmp = t23; + L_RSHIFT(&tmp); + L_ADD(&ci, &tmp); + + /* + * Calculate di in t23 in full precision, then truncate + * to an s_fp. + */ + L_SUB(&t23, &t10); + di = LFPTOFP(&t23); + + if (debug > 3) + printf("offset: %s, delay %s\n", lfptoa(&ci, 6), fptoa(di, 5)); + + di += (FP_SECOND >> (-(int)NTPDATE_PRECISION)) + + (FP_SECOND >> (-(int)server->precision)) + NTP_MAXSKW; + + if (di <= 0) { /* value still too raunchy to use? */ + L_CLR(&ci); + di = 0; + } else { + di = max(di, NTP_MINDIST); + } + + /* + * Shift this data in, then schedule another transmit. + */ + server_data(server, (s_fp) di, &ci, 0); + + if ((int)server->filter_nextpt >= sys_samples) { + /* + * Got all the data we need. Mark this guy + * completed and return. + */ + server->event_time = 0; + complete_servers++; + return; + } + + server->event_time = current_time + sys_timeout; +} + + +/* + * server_data - add a sample to the server's filter registers + */ +static void +server_data( + register struct server *server, + s_fp d, + l_fp *c, + u_fp e + ) +{ + u_short i; + + i = server->filter_nextpt; + if (i < NTP_SHIFT) { + server->filter_delay[i] = d; + server->filter_offset[i] = *c; + server->filter_soffset[i] = LFPTOFP(c); + server->filter_error[i] = e; + server->filter_nextpt = (u_short)(i + 1); + } +} + + +/* + * clock_filter - determine a server's delay, dispersion and offset + */ +static void +clock_filter( + register struct server *server + ) +{ + register int i, j; + int ord[NTP_SHIFT]; + + /* + * Sort indices into increasing delay order + */ + for (i = 0; i < sys_samples; i++) + ord[i] = i; + + for (i = 0; i < (sys_samples-1); i++) { + for (j = i+1; j < sys_samples; j++) { + if (server->filter_delay[ord[j]] == 0) + continue; + if (server->filter_delay[ord[i]] == 0 + || (server->filter_delay[ord[i]] + > server->filter_delay[ord[j]])) { + register int tmp; + + tmp = ord[i]; + ord[i] = ord[j]; + ord[j] = tmp; + } + } + } + + /* + * Now compute the dispersion, and assign values to delay and + * offset. If there are no samples in the register, delay and + * offset go to zero and dispersion is set to the maximum. + */ + if (server->filter_delay[ord[0]] == 0) { + server->delay = 0; + L_CLR(&server->offset); + server->soffset = 0; + server->dispersion = PEER_MAXDISP; + } else { + register s_fp d; + + server->delay = server->filter_delay[ord[0]]; + server->offset = server->filter_offset[ord[0]]; + server->soffset = LFPTOFP(&server->offset); + server->dispersion = 0; + for (i = 1; i < sys_samples; i++) { + if (server->filter_delay[ord[i]] == 0) + d = PEER_MAXDISP; + else { + d = server->filter_soffset[ord[i]] + - server->filter_soffset[ord[0]]; + if (d < 0) + d = -d; + if (d > PEER_MAXDISP) + d = PEER_MAXDISP; + } + /* + * XXX This *knows* PEER_FILTER is 1/2 + */ + server->dispersion += (u_fp)(d) >> i; + } + } + /* + * We're done + */ +} + + +/* + * clock_select - select the pick-of-the-litter clock from the samples + * we've got. + */ +static struct server * +clock_select(void) +{ + struct server *server; + u_int nlist; + s_fp d; + u_int count; + u_int i; + u_int j; + u_int k; + int n; + s_fp local_threshold; + struct server *server_list[NTP_MAXCLOCK]; + u_fp server_badness[NTP_MAXCLOCK]; + struct server *sys_server; + + /* + * This first chunk of code is supposed to go through all + * servers we know about to find the NTP_MAXLIST servers which + * are most likely to succeed. We run through the list + * doing the sanity checks and trying to insert anyone who + * looks okay. We are at all times aware that we should + * only keep samples from the top two strata and we only need + * NTP_MAXLIST of them. + */ + nlist = 0; /* none yet */ + for (server = sys_servers; server != NULL; server = server->next_server) { + if (server->delay == 0) { + if (debug) + printf("%s: Server dropped: no data\n", ntoa(&server->srcadr)); + continue; /* no data */ + } + if (server->stratum > NTP_INFIN) { + if (debug) + printf("%s: Server dropped: strata too high\n", ntoa(&server->srcadr)); + continue; /* stratum no good */ + } + if (server->delay > NTP_MAXWGT) { + if (debug) + printf("%s: Server dropped: server too far away\n", + ntoa(&server->srcadr)); + continue; /* too far away */ + } + if (server->leap == LEAP_NOTINSYNC) { + if (debug) + printf("%s: Server dropped: Leap not in sync\n", ntoa(&server->srcadr)); + continue; /* he's in trouble */ + } + if (!L_ISHIS(&server->org, &server->reftime)) { + if (debug) + printf("%s: Server dropped: server is very broken\n", + ntoa(&server->srcadr)); + continue; /* very broken host */ + } + if ((server->org.l_ui - server->reftime.l_ui) + >= NTP_MAXAGE) { + if (debug) + printf("%s: Server dropped: Server has gone too long without sync\n", + ntoa(&server->srcadr)); + continue; /* too long without sync */ + } + if (server->trust != 0) { + if (debug) + printf("%s: Server dropped: Server is untrusted\n", + ntoa(&server->srcadr)); + continue; + } + + /* + * This one seems sane. Find where he belongs + * on the list. + */ + d = server->dispersion + server->dispersion; + for (i = 0; i < nlist; i++) + if (server->stratum <= server_list[i]->stratum) + break; + for ( ; i < nlist; i++) { + if (server->stratum < server_list[i]->stratum) + break; + if (d < (s_fp) server_badness[i]) + break; + } + + /* + * If i points past the end of the list, this + * guy is a loser, else stick him in. + */ + if (i >= NTP_MAXLIST) + continue; + for (j = nlist; j > i; j--) + if (j < NTP_MAXLIST) { + server_list[j] = server_list[j-1]; + server_badness[j] + = server_badness[j-1]; + } + + server_list[i] = server; + server_badness[i] = d; + if (nlist < NTP_MAXLIST) + nlist++; + } + + /* + * Got the five-or-less best. Cut the list where the number of + * strata exceeds two. + */ + count = 0; + for (i = 1; i < nlist; i++) + if (server_list[i]->stratum > server_list[i-1]->stratum) { + count++; + if (2 == count) { + nlist = i; + break; + } + } + + /* + * Whew! What we should have by now is 0 to 5 candidates for + * the job of syncing us. If we have none, we're out of luck. + * If we have one, he's a winner. If we have more, do falseticker + * detection. + */ + + if (0 == nlist) + sys_server = NULL; + else if (1 == nlist) { + sys_server = server_list[0]; + } else { + /* + * Re-sort by stratum, bdelay estimate quality and + * server.delay. + */ + for (i = 0; i < nlist-1; i++) + for (j = i+1; j < nlist; j++) { + if (server_list[i]->stratum < + server_list[j]->stratum) + /* already sorted by stratum */ + break; + if (server_list[i]->delay < + server_list[j]->delay) + continue; + server = server_list[i]; + server_list[i] = server_list[j]; + server_list[j] = server; + } + + /* + * Calculate the fixed part of the dispersion limit + */ + local_threshold = (FP_SECOND >> (-(int)NTPDATE_PRECISION)) + + NTP_MAXSKW; + + /* + * Now drop samples until we're down to one. + */ + while (nlist > 1) { + for (k = 0; k < nlist; k++) { + server_badness[k] = 0; + for (j = 0; j < nlist; j++) { + if (j == k) /* with self? */ + continue; + d = server_list[j]->soffset - + server_list[k]->soffset; + if (d < 0) /* abs value */ + d = -d; + /* + * XXX This code *knows* that + * NTP_SELECT is 3/4 + */ + for (i = 0; i < j; i++) + d = (d>>1) + (d>>2); + server_badness[k] += d; + } + } + + /* + * We now have an array of nlist badness + * coefficients. Find the badest. Find + * the minimum precision while we're at + * it. + */ + i = 0; + n = server_list[0]->precision;; + for (j = 1; j < nlist; j++) { + if (server_badness[j] >= server_badness[i]) + i = j; + if (n > server_list[j]->precision) + n = server_list[j]->precision; + } + + /* + * i is the index of the server with the worst + * dispersion. If his dispersion is less than + * the threshold, stop now, else delete him and + * continue around again. + */ + if ( (s_fp) server_badness[i] < (local_threshold + + (FP_SECOND >> (-n)))) + break; + for (j = i + 1; j < nlist; j++) + server_list[j-1] = server_list[j]; + nlist--; + } + + /* + * What remains is a list of less than 5 servers. Take + * the best. + */ + sys_server = server_list[0]; + } + + /* + * That's it. Return our server. + */ + return sys_server; +} + + +/* + * clock_adjust - process what we've received, and adjust the time + * if we got anything decent. + */ +static int +clock_adjust(void) +{ + register struct server *sp, *server; + 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 || absoffset < 0); + } + + if (dostep) { + if (simple_query || debug || l_step_systime(&server->offset)){ + msyslog(LOG_NOTICE, "step time server %s offset %s sec", + stoa(&server->srcadr), + lfptoa(&server->offset, 6)); + } + } else { +#ifndef SYS_WINNT + if (simple_query || l_adj_systime(&server->offset)) { + msyslog(LOG_NOTICE, "adjust time server %s offset %s sec", + stoa(&server->srcadr), + lfptoa(&server->offset, 6)); + } +#else + /* The NT SetSystemTimeAdjustment() call achieves slewing by + * changing the clock frequency. This means that we cannot specify + * it to slew the clock by a definite amount and then stop like + * the Unix adjtime() routine. We can technically adjust the clock + * frequency, have ntpdate sleep for a while, and then wake + * up and reset the clock frequency, but this might cause some + * grief if the user attempts to run ntpd immediately after + * ntpdate and the socket is in use. + */ + printf("\nThe -b option is required by ntpdate on Windows NT platforms\n"); + exit(1); +#endif /* SYS_WINNT */ + } + return(0); +} + + +/* + * is_unreachable - check to see if we have a route to given destination + * (non-blocking). + */ +static int +is_reachable (sockaddr_u *dst) +{ + SOCKET sockfd; + + sockfd = socket(AF(dst), SOCK_DGRAM, 0); + if (sockfd == -1) { + return 0; + } + + if (connect(sockfd, &dst->sa, SOCKLEN(dst))) { + closesocket(sockfd); + return 0; + } + closesocket(sockfd); + return 1; +} + + + +/* XXX ELIMINATE: merge BIG slew into adj_systime in lib/systime.c */ +/* + * addserver - determine a server's address and allocate a new structure + * for it. + */ +static void +addserver( + char *serv + ) +{ + register struct server *server; + /* Address infos structure to store result of getaddrinfo */ + struct addrinfo *addrResult, *ptr; + /* Address infos structure to store hints for getaddrinfo */ + struct addrinfo hints; + /* Error variable for getaddrinfo */ + int error; + /* Service name */ + char service[5]; + sockaddr_u addr; + + strlcpy(service, "ntp", sizeof(service)); + + /* Get host address. Looking for UDP datagram connection. */ + ZERO(hints); + hints.ai_family = ai_fam_templ; + hints.ai_socktype = SOCK_DGRAM; + +#ifdef DEBUG + if (debug) + printf("Looking for host %s and service %s\n", serv, service); +#endif + + error = getaddrinfo(serv, service, &hints, &addrResult); + if (error != 0) { + /* Conduct more refined error analysis */ + if (error == EAI_FAIL || error == EAI_AGAIN){ + /* Name server is unusable. Exit after failing on the + first server, in order to shorten the timeout caused + by waiting for resolution of several servers */ + fprintf(stderr, "Exiting, name server cannot be used: %s (%d)", + gai_strerror(error), error); + msyslog(LOG_ERR, "name server cannot be used: %s (%d)", + gai_strerror(error), error); + exit(1); + } + fprintf(stderr, "Error resolving %s: %s (%d)\n", serv, + gai_strerror(error), error); + msyslog(LOG_ERR, "Can't find host %s: %s (%d)", serv, + gai_strerror(error), error); + return; + } +#ifdef DEBUG + if (debug) { + ZERO(addr); + INSIST(addrResult->ai_addrlen <= sizeof(addr)); + memcpy(&addr, addrResult->ai_addr, addrResult->ai_addrlen); + fprintf(stderr, "host found : %s\n", stohost(&addr)); + } +#endif + + /* We must get all returned server in case the first one fails */ + for (ptr = addrResult; ptr != NULL; ptr = ptr->ai_next) { + ZERO(addr); + INSIST(ptr->ai_addrlen <= sizeof(addr)); + memcpy(&addr, ptr->ai_addr, ptr->ai_addrlen); + if (is_reachable(&addr)) { + server = emalloc_zero(sizeof(*server)); + memcpy(&server->srcadr, ptr->ai_addr, ptr->ai_addrlen); + server->event_time = ++sys_numservers; + if (sys_servers == NULL) + sys_servers = server; + else { + struct server *sp; + + for (sp = sys_servers; sp->next_server != NULL; + sp = sp->next_server) + /* empty */; + sp->next_server = server; + } + } + } + + freeaddrinfo(addrResult); +} + + +/* + * findserver - find a server in the list given its address + * ***(For now it isn't totally AF-Independant, to check later..) + */ +static struct server * +findserver( + sockaddr_u *addr + ) +{ + struct server *server; + struct server *mc_server; + + mc_server = NULL; + if (SRCPORT(addr) != NTP_PORT) + return 0; + + for (server = sys_servers; server != NULL; + server = server->next_server) { + if (SOCK_EQ(addr, &server->srcadr)) + return server; + + if (AF(addr) == AF(&server->srcadr)) { + if (IS_MCAST(&server->srcadr)) + mc_server = server; + } + } + + if (mc_server != NULL) { + + struct server *sp; + + if (mc_server->event_time != 0) { + mc_server->event_time = 0; + complete_servers++; + } + + server = emalloc_zero(sizeof(*server)); + + server->srcadr = *addr; + + server->event_time = ++sys_numservers; + + for (sp = sys_servers; sp->next_server != NULL; + sp = sp->next_server) + /* empty */; + sp->next_server = server; + transmit(server); + } + return NULL; +} + + +/* + * timer - process a timer interrupt + */ +void +timer(void) +{ + struct server *server; + + /* + * Bump the current idea of the time + */ + current_time++; + + /* + * Search through the server list looking for guys + * who's event timers have expired. Give these to + * the transmit routine. + */ + for (server = sys_servers; server != NULL; + server = server->next_server) { + if (server->event_time != 0 + && server->event_time <= current_time) + transmit(server); + } +} + + +/* + * The code duplication in the following subroutine sucks, but + * we need to appease ansi2knr. + */ + +#ifndef SYS_WINNT +/* + * alarming - record the occurance of an alarm interrupt + */ +static RETSIGTYPE +alarming( + int sig + ) +{ + alarm_flag++; +} +#else /* SYS_WINNT follows */ +void CALLBACK +alarming(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + UNUSED_ARG(uTimerID); UNUSED_ARG(uMsg); UNUSED_ARG(dwUser); + UNUSED_ARG(dw1); UNUSED_ARG(dw2); + + alarm_flag++; +} + +static void +callTimeEndPeriod(void) +{ + timeEndPeriod( wTimerRes ); + wTimerRes = 0; +} +#endif /* SYS_WINNT */ + + +/* + * init_alarm - set up the timer interrupt + */ +static void +init_alarm(void) +{ +#ifndef SYS_WINNT +# ifdef HAVE_TIMER_CREATE + struct itimerspec its; +# else + struct itimerval itv; +# endif +#else /* SYS_WINNT follows */ + TIMECAPS tc; + UINT wTimerID; + HANDLE hToken; + TOKEN_PRIVILEGES tkp; + DWORD dwUser = 0; +#endif /* SYS_WINNT */ + + alarm_flag = 0; + +#ifndef SYS_WINNT +# ifdef HAVE_TIMER_CREATE + alarm_flag = 0; + /* this code was put in as setitimer() is non existant this us the + * POSIX "equivalents" setup - casey + */ + /* ntpdate_timerid is global - so we can kill timer later */ + if (timer_create (CLOCK_REALTIME, NULL, &ntpdate_timerid) == +# ifdef SYS_VXWORKS + ERROR +# else + -1 +# endif + ) + { + fprintf (stderr, "init_alarm(): timer_create (...) FAILED\n"); + return; + } + + /* TIMER_HZ = (5) + * Set up the alarm interrupt. The first comes 1/(2*TIMER_HZ) + * seconds from now and they continue on every 1/TIMER_HZ seconds. + */ + signal_no_reset(SIGALRM, alarming); + its.it_interval.tv_sec = 0; + its.it_value.tv_sec = 0; + its.it_interval.tv_nsec = 1000000000/TIMER_HZ; + its.it_value.tv_nsec = 1000000000/(TIMER_HZ<<1); + timer_settime(ntpdate_timerid, 0 /* !TIMER_ABSTIME */, &its, NULL); +# else /* !HAVE_TIMER_CREATE follows */ + /* + * Set up the alarm interrupt. The first comes 1/(2*TIMER_HZ) + * seconds from now and they continue on every 1/TIMER_HZ seconds. + */ + signal_no_reset(SIGALRM, alarming); + itv.it_interval.tv_sec = 0; + itv.it_value.tv_sec = 0; + itv.it_interval.tv_usec = 1000000/TIMER_HZ; + itv.it_value.tv_usec = 1000000/(TIMER_HZ<<1); + + setitimer(ITIMER_REAL, &itv, NULL); +# endif /* !HAVE_TIMER_CREATE */ +#else /* SYS_WINNT follows */ + _tzset(); + + /* + * Get privileges needed for fiddling with the clock + */ + + /* get the current process token handle */ + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { + msyslog(LOG_ERR, "OpenProcessToken failed: %m"); + exit(1); + } + /* get the LUID for system-time privilege. */ + LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid); + tkp.PrivilegeCount = 1; /* one privilege to set */ + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + /* get set-time privilege for this process. */ + AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES) NULL, 0); + /* cannot test return value of AdjustTokenPrivileges. */ + if (GetLastError() != ERROR_SUCCESS) + msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m"); + + /* + * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds + * Under Win/NT, expiry of timer interval leads to invocation + * of a callback function (on a different thread) rather than + * generating an alarm signal + */ + + /* determine max and min resolution supported */ + if(timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + msyslog(LOG_ERR, "timeGetDevCaps failed: %m"); + exit(1); + } + wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); + /* establish the minimum timer resolution that we'll use */ + timeBeginPeriod(wTimerRes); + atexit(callTimeEndPeriod); + + /* start the timer event */ + wTimerID = timeSetEvent( + (UINT) (1000/TIMER_HZ), /* Delay */ + wTimerRes, /* Resolution */ + (LPTIMECALLBACK) alarming, /* Callback function */ + (DWORD) dwUser, /* User data */ + TIME_PERIODIC); /* Event type (periodic) */ + if (wTimerID == 0) { + msyslog(LOG_ERR, "timeSetEvent failed: %m"); + exit(1); + } +#endif /* SYS_WINNT */ +} + + + + +/* + * We do asynchronous input using the SIGIO facility. A number of + * recvbuf buffers are preallocated for input. In the signal + * handler we poll to see if the socket is ready and read the + * packets from it into the recvbuf's along with a time stamp and + * an indication of the source host and the interface it was received + * through. This allows us to get as accurate receive time stamps + * as possible independent of other processing going on. + * + * We allocate a number of recvbufs equal to the number of servers + * plus 2. This should be plenty. + */ + + +/* + * init_io - initialize I/O data and open socket + */ +static void +init_io(void) +{ + struct addrinfo *res, *ressave; + struct addrinfo hints; + sockaddr_u addr; + char service[5]; + int rc; + int optval = 1; + int check_ntp_port_in_use = !debug && !simple_query && !unpriv_port; + + /* + * Init buffer free list and stat counters + */ + init_recvbuff(sys_numservers + 2); + + /* + * Open the socket + */ + + strlcpy(service, "ntp", sizeof(service)); + + /* + * Init hints addrinfo structure + */ + ZERO(hints); + hints.ai_family = ai_fam_templ; + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_DGRAM; + + if (getaddrinfo(NULL, service, &hints, &res) != 0) { + msyslog(LOG_ERR, "getaddrinfo() failed: %m"); + exit(1); + /*NOTREACHED*/ + } + +#ifdef SYS_WINNT + if (check_ntp_port_in_use && ntp_port_inuse(AF_INET, NTP_PORT)){ + msyslog(LOG_ERR, "the NTP socket is in use, exiting: %m"); + exit(1); + } +#endif + + /* Remember the address of the addrinfo structure chain */ + ressave = res; + + /* + * For each structure returned, open and bind socket + */ + for(nbsock = 0; (nbsock < MAX_AF) && res ; res = res->ai_next) { + /* create a datagram (UDP) socket */ + fd[nbsock] = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd[nbsock] == SOCKET_ERROR) { +#ifndef SYS_WINNT + if (errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT || + errno == EPFNOSUPPORT) +#else + int err = WSAGetLastError(); + if (err == WSAEPROTONOSUPPORT || err == WSAEAFNOSUPPORT || + err == WSAEPFNOSUPPORT) +#endif + continue; + msyslog(LOG_ERR, "socket() failed: %m"); + exit(1); + /*NOTREACHED*/ + } + /* set socket to reuse address */ + if (setsockopt(fd[nbsock], SOL_SOCKET, SO_REUSEADDR, (void*) &optval, sizeof(optval)) < 0) { + msyslog(LOG_ERR, "setsockopt() SO_REUSEADDR failed: %m"); + exit(1); + /*NOTREACHED*/ + } +#ifdef IPV6_V6ONLY + /* Restricts AF_INET6 socket to IPv6 communications (see RFC 2553bis-03) */ + if (res->ai_family == AF_INET6) + if (setsockopt(fd[nbsock], IPPROTO_IPV6, IPV6_V6ONLY, (void*) &optval, sizeof(optval)) < 0) { + msyslog(LOG_ERR, "setsockopt() IPV6_V6ONLY failed: %m"); + exit(1); + /*NOTREACHED*/ + } +#endif + + /* Remember the socket family in fd_family structure */ + fd_family[nbsock] = res->ai_family; + + /* + * bind the socket to the NTP port + */ + if (check_ntp_port_in_use) { + ZERO(addr); + INSIST(res->ai_addrlen <= sizeof(addr)); + memcpy(&addr, res->ai_addr, res->ai_addrlen); + rc = bind(fd[nbsock], &addr.sa, SOCKLEN(&addr)); + if (rc < 0) { + if (EADDRINUSE == socket_errno()) + msyslog(LOG_ERR, "the NTP socket is in use, exiting"); + else + msyslog(LOG_ERR, "bind() fails: %m"); + exit(1); + } + } + +#ifdef HAVE_POLL_H + fdmask[nbsock].fd = fd[nbsock]; + fdmask[nbsock].events = POLLIN; +#else + FD_SET(fd[nbsock], &fdmask); + if (maxfd < fd[nbsock]+1) { + maxfd = fd[nbsock]+1; + } +#endif + + /* + * set non-blocking, + */ +#ifndef SYS_WINNT +# ifdef SYS_VXWORKS + { + int on = TRUE; + + if (ioctl(fd[nbsock],FIONBIO, &on) == ERROR) { + msyslog(LOG_ERR, "ioctl(FIONBIO) fails: %m"); + exit(1); + } + } +# else /* not SYS_VXWORKS */ +# if defined(O_NONBLOCK) + if (fcntl(fd[nbsock], F_SETFL, O_NONBLOCK) < 0) { + msyslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m"); + exit(1); + /*NOTREACHED*/ + } +# else /* not O_NONBLOCK */ +# if defined(FNDELAY) + if (fcntl(fd[nbsock], F_SETFL, FNDELAY) < 0) { + msyslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m"); + exit(1); + /*NOTREACHED*/ + } +# else /* FNDELAY */ +# include "Bletch: Need non blocking I/O" +# endif /* FNDELAY */ +# endif /* not O_NONBLOCK */ +# endif /* SYS_VXWORKS */ +#else /* SYS_WINNT */ + if (ioctlsocket(fd[nbsock], FIONBIO, (u_long *) &on) == SOCKET_ERROR) { + msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m"); + exit(1); + } +#endif /* SYS_WINNT */ + nbsock++; + } + freeaddrinfo(ressave); +} + +/* + * sendpkt - send a packet to the specified destination + */ +static void +sendpkt( + sockaddr_u *dest, + struct pkt *pkt, + int len + ) +{ + int i; + int cc; + SOCKET sock = INVALID_SOCKET; + +#ifdef SYS_WINNT + DWORD err; +#endif /* SYS_WINNT */ + + /* Find a local family compatible socket to send ntp packet to ntp server */ + for(i = 0; (i < MAX_AF); i++) { + if(AF(dest) == fd_family[i]) { + sock = fd[i]; + break; + } + } + + if (INVALID_SOCKET == sock) { + msyslog(LOG_ERR, "cannot find family compatible socket to send ntp packet"); + exit(1); + /*NOTREACHED*/ + } + + cc = sendto(sock, (char *)pkt, len, 0, (struct sockaddr *)dest, + SOCKLEN(dest)); + + if (SOCKET_ERROR == cc) { +#ifndef SYS_WINNT + if (errno != EWOULDBLOCK && errno != ENOBUFS) +#else + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAENOBUFS) +#endif /* SYS_WINNT */ + msyslog(LOG_ERR, "sendto(%s): %m", stohost(dest)); + } +} + + +/* + * input_handler - receive packets asynchronously + */ +void +input_handler(void) +{ + register int n; + register struct recvbuf *rb; + struct sock_timeval tvzero; + GETSOCKNAME_SOCKLEN_TYPE fromlen; + l_fp ts; + int i; +#ifdef HAVE_POLL_H + struct pollfd fds[MAX_AF]; +#else + fd_set fds; +#endif + int fdc = 0; + + /* + * Do a poll to see if we have data + */ + for (;;) { + tvzero.tv_sec = tvzero.tv_usec = 0; +#ifdef HAVE_POLL_H + memcpy(fds, fdmask, sizeof(fdmask)); + n = poll(fds, (unsigned int)nbsock, tvzero.tv_sec * 1000); + + /* + * Determine which socket received data + */ + + for(i=0; i < nbsock; i++) { + if(fds[i].revents & POLLIN) { + fdc = fd[i]; + break; + } + } + +#else + fds = fdmask; + n = select(maxfd, &fds, (fd_set *)0, (fd_set *)0, &tvzero); + + /* + * Determine which socket received data + */ + + for(i=0; i < nbsock; i++) { + if(FD_ISSET(fd[i], &fds)) { + fdc = fd[i]; + break; + } + } + +#endif + + /* + * If nothing to do, just return. If an error occurred, + * complain and return. If we've got some, freeze a + * timestamp. + */ + if (n == 0) + return; + else if (n == -1) { + if (errno != EINTR) + msyslog(LOG_ERR, +#ifdef HAVE_POLL_H + "poll() error: %m" +#else + "select() error: %m" +#endif + ); + return; + } + get_systime(&ts); + + /* + * Get a buffer and read the frame. If we + * haven't got a buffer, or this is received + * on the wild card socket, just dump the packet. + */ + if (initializing || free_recvbuffs() == 0) { + char buf[100]; + + +#ifndef SYS_WINNT + (void) read(fdc, buf, sizeof buf); +#else + /* NT's _read does not operate on nonblocking sockets + * either recvfrom or ReadFile() has to be used here. + * ReadFile is used in [ntpd]ntp_intres() and ntpdc, + * just to be different use recvfrom() here + */ + recvfrom(fdc, buf, sizeof(buf), 0, (struct sockaddr *)0, NULL); +#endif /* SYS_WINNT */ + continue; + } + + rb = get_free_recv_buffer(); + + fromlen = sizeof(rb->recv_srcadr); + rb->recv_length = recvfrom(fdc, (char *)&rb->recv_pkt, + sizeof(rb->recv_pkt), 0, + (struct sockaddr *)&rb->recv_srcadr, &fromlen); + if (rb->recv_length == -1) { + freerecvbuf(rb); + continue; + } + + /* + * Got one. Mark how and when it got here, + * put it on the full list. + */ + rb->recv_time = ts; + add_full_recv_buffer(rb); + } +} + + +#if !defined SYS_WINNT && !defined SYS_CYGWIN32 +/* + * adj_systime - do a big long slew of the system time + */ +static int +l_adj_systime( + l_fp *ts + ) +{ + struct timeval adjtv, oadjtv; + int isneg = 0; + l_fp offset; +#ifndef STEP_SLEW + l_fp overshoot; +#endif + + /* + * Take the absolute value of the offset + */ + offset = *ts; + if (L_ISNEG(&offset)) { + isneg = 1; + L_NEG(&offset); + } + +#ifndef STEP_SLEW + /* + * Calculate the overshoot. XXX N.B. This code *knows* + * ADJ_OVERSHOOT is 1/2. + */ + overshoot = offset; + L_RSHIFTU(&overshoot); + if (overshoot.l_ui != 0 || (overshoot.l_uf > ADJ_MAXOVERSHOOT)) { + overshoot.l_ui = 0; + overshoot.l_uf = ADJ_MAXOVERSHOOT; + } + L_ADD(&offset, &overshoot); +#endif + TSTOTV(&offset, &adjtv); + + if (isneg) { + adjtv.tv_sec = -adjtv.tv_sec; + adjtv.tv_usec = -adjtv.tv_usec; + } + + if (adjtv.tv_usec != 0 && !debug) { + if (adjtime(&adjtv, &oadjtv) < 0) { + msyslog(LOG_ERR, "Can't adjust the time of day: %m"); + exit(1); + } + } + return 1; +} +#endif /* SYS_WINNT */ + + +/* + * This fuction is not the same as lib/systime step_systime!!! + */ +static int +l_step_systime( + l_fp *ts + ) +{ + double dtemp; + +#ifdef SLEWALWAYS +#ifdef STEP_SLEW + l_fp ftmp; + int isneg; + int n; + + if (debug) return 1; + /* + * Take the absolute value of the offset + */ + ftmp = *ts; + if (L_ISNEG(&ftmp)) { + L_NEG(&ftmp); + isneg = 1; + } else + isneg = 0; + + if (ftmp.l_ui >= 3) { /* Step it and slew - we might win */ + LFPTOD(ts, dtemp); + n = step_systime(dtemp); + if (!n) + return n; + if (isneg) + ts->l_ui = ~0; + else + ts->l_ui = ~0; + } + /* + * Just add adjustment into the current offset. The update + * routine will take care of bringing the system clock into + * line. + */ +#endif + if (debug) + return 1; +#ifdef FORCE_NTPDATE_STEP + LFPTOD(ts, dtemp); + return step_systime(dtemp); +#else + l_adj_systime(ts); + return 1; +#endif +#else /* SLEWALWAYS */ + if (debug) + return 1; + LFPTOD(ts, dtemp); + return step_systime(dtemp); +#endif /* SLEWALWAYS */ +} + + +/* XXX ELIMINATE printserver similar in ntptrace.c, ntpdate.c */ +/* + * printserver - print detail information for a server + */ +static void +printserver( + register struct server *pp, + FILE *fp + ) +{ + register int i; + char junk[5]; + const char *str; + + if (!debug) { + (void) fprintf(fp, "server %s, stratum %d, offset %s, delay %s\n", + stoa(&pp->srcadr), pp->stratum, + lfptoa(&pp->offset, 6), fptoa((s_fp)pp->delay, 5)); + return; + } + + (void) fprintf(fp, "server %s, port %d\n", + stoa(&pp->srcadr), ntohs(((struct sockaddr_in*)&(pp->srcadr))->sin_port)); + + (void) fprintf(fp, "stratum %d, precision %d, leap %c%c, trust %03o\n", + pp->stratum, pp->precision, + pp->leap & 0x2 ? '1' : '0', + pp->leap & 0x1 ? '1' : '0', + pp->trust); + + if (pp->stratum == 1) { + junk[4] = 0; + memmove(junk, (char *)&pp->refid, 4); + str = junk; + } else { + str = stoa(&pp->srcadr); + } + (void) fprintf(fp, + "refid [%s], delay %s, dispersion %s\n", + str, fptoa((s_fp)pp->delay, 5), + ufptoa(pp->dispersion, 5)); + + (void) fprintf(fp, "transmitted %d, in filter %d\n", + pp->xmtcnt, pp->filter_nextpt); + + (void) fprintf(fp, "reference time: %s\n", + prettydate(&pp->reftime)); + (void) fprintf(fp, "originate timestamp: %s\n", + prettydate(&pp->org)); + (void) fprintf(fp, "transmit timestamp: %s\n", + prettydate(&pp->xmt)); + + (void) fprintf(fp, "filter delay: "); + for (i = 0; i < NTP_SHIFT; i++) { + (void) fprintf(fp, " %-8.8s", fptoa(pp->filter_delay[i], 5)); + if (i == (NTP_SHIFT>>1)-1) + (void) fprintf(fp, "\n "); + } + (void) fprintf(fp, "\n"); + + (void) fprintf(fp, "filter offset:"); + for (i = 0; i < PEER_SHIFT; i++) { + (void) fprintf(fp, " %-8.8s", lfptoa(&pp->filter_offset[i], 6)); + if (i == (PEER_SHIFT>>1)-1) + (void) fprintf(fp, "\n "); + } + (void) fprintf(fp, "\n"); + + (void) fprintf(fp, "delay %s, dispersion %s\n", + fptoa((s_fp)pp->delay, 5), ufptoa(pp->dispersion, 5)); + + (void) fprintf(fp, "offset %s\n\n", + lfptoa(&pp->offset, 6)); +} + + +#ifdef HAVE_NETINFO +static ni_namelist * +getnetinfoservers(void) +{ + ni_status status; + void *domain; + ni_id confdir; + ni_namelist *namelist = emalloc(sizeof(ni_namelist)); + + /* Find a time server in NetInfo */ + if ((status = ni_open(NULL, ".", &domain)) != NI_OK) return NULL; + + while (status = ni_pathsearch(domain, &confdir, NETINFO_CONFIG_DIR) == NI_NODIR) { + void *next_domain; + if (ni_open(domain, "..", &next_domain) != NI_OK) break; + ni_free(domain); + domain = next_domain; + } + if (status != NI_OK) return NULL; + + NI_INIT(namelist); + if (ni_lookupprop(domain, &confdir, "server", namelist) != NI_OK) { + ni_namelist_free(namelist); + free(namelist); + return NULL; + } + + return(namelist); +} +#endif + +#ifdef SYS_WINNT +isc_boolean_t ntp_port_inuse(int af, u_short port) +{ + /* + * Check if NTP socket is already in use on this system + * This is only for Windows Systems, as they tend not to fail on the real bind() below + */ + + SOCKET checksocket; + struct sockaddr_in checkservice; + checksocket = socket(af, SOCK_DGRAM, 0); + if (checksocket == INVALID_SOCKET) { + return (ISC_TRUE); + } + + checkservice.sin_family = (short) AF_INET; + checkservice.sin_addr.s_addr = INADDR_LOOPBACK; + checkservice.sin_port = htons(port); + + if (bind(checksocket, (struct sockaddr *)&checkservice, + sizeof(checkservice)) == SOCKET_ERROR) { + if ( WSAGetLastError() == WSAEADDRINUSE ){ + closesocket(checksocket); + return (ISC_TRUE); + } + } + closesocket(checksocket); + return (ISC_FALSE); +} +#endif |