libm: Import nearbyint{,f}() functions from FreeBSD.
[dragonfly.git] / usr.sbin / rdate / ntp.c
blob2efc1ffbf5e7a8ea033435dc389a3a64cb96d372
1 /* $OpenBSD: src/usr.sbin/rdate/ntp.c,v 1.27 2004/10/26 09:48:59 henning Exp $ */
2 /* $DragonFly: src/usr.sbin/rdate/ntp.c,v 1.1 2004/12/01 15:04:43 joerg Exp $ */
4 /*
5 * Copyright (c) 1996, 1997 by N.M. Maclaren. All rights reserved.
6 * Copyright (c) 1996, 1997 by University of Cambridge. All rights reserved.
7 * Copyright (c) 2002 by Thorsten "mirabile" Glaser.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the author nor the university may be used to
18 * endorse or promote products derived from this software without
19 * specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/param.h>
34 #include <sys/socket.h>
35 #include <sys/time.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <float.h>
44 #include <limits.h>
45 #include <math.h>
46 #include <netdb.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <time.h>
51 #include <unistd.h>
53 #include "ntpleaps.h"
54 #include "rdate.h"
57 * NTP definitions. Note that these assume 8-bit bytes - sigh. There
58 * is little point in parameterising everything, as it is neither
59 * feasible nor useful. It would be very useful if more fields could
60 * be defined as unspecified. The NTP packet-handling routines
61 * contain a lot of extra assumptions.
64 #define JAN_1970 2208988800.0 /* 1970 - 1900 in seconds */
65 #define NTP_SCALE 4294967296.0 /* 2^32, of course! */
67 #define NTP_MODE_CLIENT 3 /* NTP client mode */
68 #define NTP_MODE_SERVER 4 /* NTP server mode */
69 #define NTP_VERSION 4 /* The current version */
70 #define NTP_VERSION_MIN 1 /* The minum valid version */
71 #define NTP_VERSION_MAX 4 /* The maximum valid version */
72 #define NTP_STRATUM_MAX 14 /* The maximum valid stratum */
73 #define NTP_INSANITY 3600.0 /* Errors beyond this are hopeless */
75 #define NTP_PACKET_MIN 48 /* Without authentication */
76 #define NTP_PACKET_MAX 68 /* With authentication (ignored) */
78 #define NTP_DISP_FIELD 8 /* Offset of dispersion field */
79 #define NTP_REFERENCE 16 /* Offset of reference timestamp */
80 #define NTP_ORIGINATE 24 /* Offset of originate timestamp */
81 #define NTP_RECEIVE 32 /* Offset of receive timestamp */
82 #define NTP_TRANSMIT 40 /* Offset of transmit timestamp */
84 #define STATUS_NOWARNING 0 /* No Leap Indicator */
85 #define STATUS_LEAPHIGH 1 /* Last Minute Has 61 Seconds */
86 #define STATUS_LEAPLOW 2 /* Last Minute Has 59 Seconds */
87 #define STATUS_ALARM 3 /* Server Clock Not Synchronized */
89 #define MAX_QUERIES 25
90 #define MAX_DELAY 15
92 #define MILLION_L 1000000l /* For conversion to/from timeval */
93 #define MILLION_D 1.0e6 /* Must be equal to MILLION_L */
95 struct ntp_data {
96 uint8_t status;
97 uint8_t version;
98 uint8_t mode;
99 uint8_t stratum;
100 double receive;
101 double transmit;
102 double current;
103 uint64_t recvck;
105 /* Local State */
106 double originate;
107 uint64_t xmitck;
110 static int sync_ntp(int, const struct sockaddr *, double *, double *);
111 static int write_packet(int, struct ntp_data *);
112 static int read_packet(int, struct ntp_data *, double *, double *);
113 static void unpack_ntp(struct ntp_data *, u_char *);
114 static double current_time(double);
115 static void create_timeval(double, struct timeval *, struct timeval *);
117 #ifdef DEBUG
118 void print_packet(const struct ntp_data *);
119 #endif
121 static int corrleaps;
123 void
124 ntp_client(const char *hostname, int family, struct timeval *new,
125 struct timeval *adjust, int leapflag)
127 struct addrinfo hints, *res0, *res;
128 double offset, error;
129 int accepted = 0, ret, s, ierror;
131 memset(&hints, 0, sizeof(hints));
132 hints.ai_family = family;
133 hints.ai_socktype = SOCK_DGRAM;
134 ierror = getaddrinfo(hostname, "ntp", &hints, &res0);
135 if (ierror)
136 errx(1, "%s: %s", hostname, gai_strerror(ierror));
138 corrleaps = leapflag;
139 if (corrleaps)
140 ntpleaps_init();
142 s = -1;
143 for (res = res0; res; res = res->ai_next) {
144 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
145 if (s < 0)
146 continue;
148 ret = sync_ntp(s, res->ai_addr, &offset, &error);
149 if (ret < 0) {
150 #ifdef DEBUG
151 fprintf(stderr, "try the next address\n");
152 #endif
153 close(s);
154 s = -1;
155 continue;
158 accepted++;
159 break;
161 freeaddrinfo(res0);
163 #ifdef DEBUG
164 fprintf(stderr, "Correction: %.6f +/- %.6f\n", offset, error);
165 #endif
167 if (accepted < 1)
168 errx(1, "Unable to get a reasonable time estimate");
170 create_timeval(offset, new, adjust);
173 static int
174 sync_ntp(int fd, const struct sockaddr *peer, double *offset, double *error)
176 int attempts = 0, accepts = 0, rejects = 0;
177 int delay = MAX_DELAY, ret;
178 double deadline;
179 double a, b, x, y;
180 double minerr = 0.1; /* Maximum ignorable variation */
181 struct ntp_data data;
183 deadline = current_time(JAN_1970) + delay;
184 *offset = 0.0;
185 *error = NTP_INSANITY;
187 if (connect(fd, peer, peer->sa_len) < 0) {
188 warn("Failed to connect to server");
189 return(-1);
192 while (accepts < MAX_QUERIES && attempts < 2 * MAX_QUERIES) {
193 memset(&data, 0, sizeof(data));
195 if (current_time(JAN_1970) > deadline) {
196 warnx("Not enough valid responses received in time");
197 return(-1);
200 if (write_packet(fd, &data) < 0)
201 return(-1);
203 ret = read_packet(fd, &data, &x, &y);
205 if (ret < 0)
206 return(-1);
207 else if (ret > 0) {
208 #ifdef DEBUG
209 print_packet(&data);
210 #endif
212 if (++rejects > MAX_QUERIES) {
213 warnx("Too many bad or lost packets");
214 return(-1);
215 } else
216 continue;
217 } else
218 ++accepts;
220 #ifdef DEBUG
221 fprintf(stderr, "Offset: %.6f +/- %.6f\n", x, y);
222 #endif
224 if ((a = x - *offset) < 0.0)
225 a = -a;
226 if (accepts <= 1)
227 a = 0.0;
228 b = *error + y;
229 if (y < *error) {
230 *offset = x;
231 *error = y;
234 #ifdef DEBUG
235 fprintf(stderr, "Best: %.6f +/- %.6f\n", *offset, *error);
236 #endif
238 if (a > b) {
239 warnx("Inconsistent times received from NTP server");
240 return(-1);
243 if (*error <= minerr)
244 break;
247 return(accepts);
250 /* Send out NTP packet. */
252 write_packet(int fd, struct ntp_data *data)
254 uint8_t packet[NTP_PACKET_MIN];
255 ssize_t length;
257 memset(packet, 0, sizeof(packet));
259 packet[0] = (NTP_VERSION << 3) | (NTP_MODE_CLIENT);
261 data->xmitck = (uint64_t)arc4random() << 32 | arc4random();
264 * Send out a random 64-bit number as our transmit time. The NTP
265 * server will copy said number into the originate field on the
266 * response that it sends us. This is totally legal per the SNTP spec.
268 * The impact of this is two fold: we no longer send out the current
269 * system time for the world to see (which may aid an attacker), and
270 * it gives us a (not very secure) way of knowing that we're not
271 * getting spoofed by an attacker that can't capture our traffic
272 * but can spoof packets from the NTP server we're communicating with.
274 * No endian concerns here. Since we're running as a strict
275 * unicast client, we don't have to worry about anyone else finding
276 * the transmit field intelligible.
279 *(uint64_t *)(packet + NTP_TRANSMIT) = data->xmitck;
281 data->originate = current_time(JAN_1970);
283 length = write(fd, packet, sizeof(packet));
285 if (length != sizeof(packet)) {
286 warn("Unable to send NTP packet to server");
287 return (-1);
290 return (0);
294 * Check the packet and work out the offset and optionally the error.
295 * Note that this contains more checking than xntp does. Return 0 for
296 * success, 1 for failure. Note that it must not change its arguments
297 * if it fails.
300 read_packet(int fd, struct ntp_data *data, double *off, double *error)
302 uint8_t receive[NTP_PACKET_MAX];
303 struct timeval tv;
304 double x, y;
305 int length, r;
306 fd_set *rfds;
308 rfds = calloc(howmany(fd + 1, NFDBITS), sizeof(fd_mask));
309 if (rfds == NULL)
310 err(1, "calloc");
312 FD_SET(fd, rfds);
314 retry:
315 tv.tv_sec = 0;
316 tv.tv_usec = 1000000 * MAX_DELAY / MAX_QUERIES;
318 r = select(fd + 1, rfds, NULL, NULL, &tv);
320 if (r < 0) {
321 if (errno == EINTR)
322 goto retry;
323 else
324 warn("select");
326 free(rfds);
327 return(r);
330 if (r != 1 || !FD_ISSET(fd, rfds)) {
331 free(rfds);
332 return(1);
335 free(rfds);
337 length = read(fd, receive, NTP_PACKET_MAX);
339 if (length < 0) {
340 warn("Unable to receive NTP packet from server");
341 return(-1);
344 if (length < NTP_PACKET_MIN || length > NTP_PACKET_MAX) {
345 warnx("Invalid NTP packet size, packet rejected");
346 return(1);
349 unpack_ntp(data, receive);
351 if (data->recvck != data->xmitck) {
352 warnx("Invalid cookie received, packet rejected");
353 return(1);
356 if (data->version < NTP_VERSION_MIN ||
357 data->version > NTP_VERSION_MAX) {
358 warnx("Received NTP version %u, need %u or lower",
359 data->version, NTP_VERSION);
360 return(1);
363 if (data->mode != NTP_MODE_SERVER) {
364 warnx("Invalid NTP server mode, packet rejected");
365 return(1);
368 if (data->stratum > NTP_STRATUM_MAX) {
369 warnx("Invalid stratum received, packet rejected");
370 return(1);
373 if (data->transmit == 0.0) {
374 warnx("Server clock invalid, packet rejected");
375 return(1);
378 x = data->receive - data->originate;
379 y = data->transmit - data->current;
381 *off = (x + y) / 2;
382 *error = x - y;
384 x = (data->current - data->originate) / 2;
386 if (x > *error)
387 *error = x;
389 return(0);
393 * Unpack the essential data from an NTP packet, bypassing struct
394 * layout and endian problems. Note that it ignores fields irrelevant
395 * to SNTP.
397 void
398 unpack_ntp(struct ntp_data *data, u_char *packet)
400 int i;
401 double d;
403 data->current = current_time(JAN_1970);
405 data->status = (packet[0] >> 6);
406 data->version = (packet[0] >> 3) & 0x07;
407 data->mode = packet[0] & 0x07;
408 data->stratum = packet[1];
410 for (i = 0, d = 0.0; i < 8; ++i)
411 d = 256.0 * d + packet[NTP_RECEIVE+i];
413 data->receive = d / NTP_SCALE;
415 for (i = 0, d = 0.0; i < 8; ++i)
416 d = 256.0 * d + packet[NTP_TRANSMIT+i];
418 data->transmit = d / NTP_SCALE;
420 /* See write_packet for why this isn't an endian problem. */
421 data->recvck = *(uint64_t *)(packet + NTP_ORIGINATE);
425 * Get the current UTC time in seconds since the Epoch plus an offset
426 * (usually the time from the beginning of the century to the Epoch)
428 double
429 current_time(double offset)
431 struct timeval current;
432 uint64_t t;
434 if (gettimeofday(&current, NULL))
435 err(1, "Could not get local time of day");
438 * At this point, current has the current TAI time.
439 * Now subtract leap seconds to set the posix tick.
442 t = SEC_TO_TAI64(current.tv_sec);
443 if (corrleaps)
444 ntpleaps_sub(&t);
446 return (offset + TAI64_TO_SEC(t) + 1.0e-6 * current.tv_usec);
450 * Change offset into current UTC time. This is portable, even if
451 * struct timeval uses an unsigned long for tv_sec.
453 void
454 create_timeval(double difference, struct timeval *new, struct timeval *adjust)
456 struct timeval old;
457 long n;
459 /* Start by converting to timeval format. Note that we have to
460 * cater for negative, unsigned values. */
461 if ((n = (long) difference) > difference)
462 --n;
463 adjust->tv_sec = n;
464 adjust->tv_usec = (long) (MILLION_D * (difference-n));
465 errno = 0;
466 if (gettimeofday(&old, NULL))
467 err(1, "Could not get local time of day");
468 new->tv_sec = old.tv_sec + adjust->tv_sec;
469 new->tv_usec = (n = (long) old.tv_usec + (long) adjust->tv_usec);
471 if (n < 0) {
472 new->tv_usec += MILLION_L;
473 --new->tv_sec;
474 } else if (n >= MILLION_L) {
475 new->tv_usec -= MILLION_L;
476 ++new->tv_sec;
480 #ifdef DEBUG
481 void
482 print_packet(const struct ntp_data *data)
484 printf("status: %u\n", data->status);
485 printf("version: %u\n", data->version);
486 printf("mode: %u\n", data->mode);
487 printf("stratum: %u\n", data->stratum);
488 printf("originate: %f\n", data->originate);
489 printf("receive: %f\n", data->receive);
490 printf("transmit: %f\n", data->transmit);
491 printf("current: %f\n", data->current);
492 printf("xmitck: 0x%0llX\n", data->xmitck);
493 printf("recvck: 0x%0llX\n", data->recvck);
495 #endif