Merge branch 'vendor/TCSH'
[dragonfly.git] / usr.sbin / rdate / ntp.c
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 $ */
3
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.
8  *
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.
20  *
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.
31  */
32
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>
38
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>
52
53 #include "ntpleaps.h"
54 #include "rdate.h"
55
56 /*
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.
62  */
63
64 #define JAN_1970        2208988800.0    /* 1970 - 1900 in seconds */
65 #define NTP_SCALE       4294967296.0    /* 2^32, of course! */
66
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 */
74
75 #define NTP_PACKET_MIN        48        /* Without authentication */
76 #define NTP_PACKET_MAX        68        /* With authentication (ignored) */
77
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 */
83
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 */
88
89 #define MAX_QUERIES           25
90 #define MAX_DELAY             15
91
92 #define MILLION_L       1000000l        /* For conversion to/from timeval */
93 #define MILLION_D          1.0e6        /* Must be equal to MILLION_L */
94
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;
104
105         /* Local State */
106         double          originate;
107         uint64_t        xmitck;
108 };
109
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 *);
116
117 #ifdef DEBUG
118 void    print_packet(const struct ntp_data *);
119 #endif
120
121 static int      corrleaps;
122
123 void
124 ntp_client(const char *hostname, int family, struct timeval *new,
125            struct timeval *adjust, int leapflag)
126 {
127         struct addrinfo hints, *res0, *res;
128         double offset, error;
129         int accepted = 0, ret, s, ierror;
130
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));
137
138         corrleaps = leapflag;
139         if (corrleaps)
140                 ntpleaps_init();
141
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;
147
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;
156                 }
157
158                 accepted++;
159                 break;
160         }
161         freeaddrinfo(res0);
162
163 #ifdef DEBUG
164         fprintf(stderr, "Correction: %.6f +/- %.6f\n", offset, error);
165 #endif
166
167         if (accepted < 1)
168                 errx(1, "Unable to get a reasonable time estimate");
169
170         create_timeval(offset, new, adjust);
171 }
172
173 static int
174 sync_ntp(int fd, const struct sockaddr *peer, double *offset, double *error)
175 {
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;
182
183         deadline = current_time(JAN_1970) + delay;
184         *offset = 0.0;
185         *error = NTP_INSANITY;
186
187         if (connect(fd, peer, peer->sa_len) < 0) {
188                 warn("Failed to connect to server");
189                 return(-1);
190         }
191
192         while (accepts < MAX_QUERIES && attempts < 2 * MAX_QUERIES) {
193                 memset(&data, 0, sizeof(data));
194
195                 if (current_time(JAN_1970) > deadline) {
196                         warnx("Not enough valid responses received in time");
197                         return(-1);
198                 }
199
200                 if (write_packet(fd, &data) < 0)
201                         return(-1);
202
203                 ret = read_packet(fd, &data, &x, &y);
204
205                 if (ret < 0)
206                         return(-1);
207                 else if (ret > 0) {
208 #ifdef DEBUG
209                         print_packet(&data);
210 #endif
211
212                         if (++rejects > MAX_QUERIES) {
213                                 warnx("Too many bad or lost packets");
214                                 return(-1);
215                         } else
216                                 continue;
217                 } else
218                         ++accepts;
219
220 #ifdef DEBUG
221                 fprintf(stderr, "Offset: %.6f +/- %.6f\n", x, y);
222 #endif
223
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;
232                 }
233
234 #ifdef DEBUG
235                 fprintf(stderr, "Best: %.6f +/- %.6f\n", *offset, *error);
236 #endif
237
238                 if (a > b) {
239                         warnx("Inconsistent times received from NTP server");
240                         return(-1);
241                 }
242
243                 if (*error <= minerr)
244                         break;
245         }
246
247         return(accepts);
248 }
249
250 /* Send out NTP packet. */
251 int
252 write_packet(int fd, struct ntp_data *data)
253 {
254         uint8_t packet[NTP_PACKET_MIN];
255         ssize_t length;
256
257         memset(packet, 0, sizeof(packet));
258
259         packet[0] = (NTP_VERSION << 3) | (NTP_MODE_CLIENT);
260
261         data->xmitck = (uint64_t)arc4random() << 32 | arc4random();
262
263         /*
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.
267          *
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.
273          *
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.
277          */
278
279         *(uint64_t *)(packet + NTP_TRANSMIT) = data->xmitck;
280
281         data->originate = current_time(JAN_1970);
282
283         length = write(fd, packet, sizeof(packet));
284
285         if (length != sizeof(packet)) {
286                 warn("Unable to send NTP packet to server");
287                 return (-1);
288         }
289
290         return (0);
291 }
292
293 /*
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.
298  */
299 int
300 read_packet(int fd, struct ntp_data *data, double *off, double *error)
301 {
302         uint8_t receive[NTP_PACKET_MAX];
303         struct timeval tv;
304         double x, y;
305         int length, r;
306         fd_set *rfds;
307
308         rfds = calloc(howmany(fd + 1, NFDBITS), sizeof(fd_mask));
309         if (rfds == NULL)
310                 err(1, "calloc");
311
312         FD_SET(fd, rfds);
313
314 retry:
315         tv.tv_sec = 0;
316         tv.tv_usec = 1000000 * MAX_DELAY / MAX_QUERIES;
317
318         r = select(fd + 1, rfds, NULL, NULL, &tv);
319
320         if (r < 0) {
321                 if (errno == EINTR)
322                         goto retry;
323                 else
324                         warn("select");
325
326                 free(rfds);
327                 return(r);
328         }
329
330         if (r != 1 || !FD_ISSET(fd, rfds)) {
331                 free(rfds);
332                 return(1);
333         }
334
335         free(rfds);
336
337         length = read(fd, receive, NTP_PACKET_MAX);
338
339         if (length < 0) {
340                 warn("Unable to receive NTP packet from server");
341                 return(-1);
342         }
343
344         if (length < NTP_PACKET_MIN || length > NTP_PACKET_MAX) {
345                 warnx("Invalid NTP packet size, packet rejected");
346                 return(1);
347         }
348
349         unpack_ntp(data, receive);
350
351         if (data->recvck != data->xmitck) {
352                 warnx("Invalid cookie received, packet rejected");
353                 return(1);
354         }
355
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);
361         }
362
363         if (data->mode != NTP_MODE_SERVER) {
364                 warnx("Invalid NTP server mode, packet rejected");
365                 return(1);
366         }
367
368         if (data->stratum > NTP_STRATUM_MAX) {
369                 warnx("Invalid stratum received, packet rejected");
370                 return(1);
371         }
372
373         if (data->transmit == 0.0) {
374                 warnx("Server clock invalid, packet rejected");
375                 return(1);
376         }
377
378         x = data->receive - data->originate;
379         y = data->transmit - data->current;
380
381         *off = (x + y) / 2;
382         *error = x - y;
383
384         x = (data->current - data->originate) / 2;
385
386         if (x > *error)
387                 *error = x;
388
389         return(0);
390 }
391
392 /*
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.
396  */
397 void
398 unpack_ntp(struct ntp_data *data, u_char *packet)
399 {
400         int i;
401         double d;
402
403         data->current = current_time(JAN_1970);
404
405         data->status = (packet[0] >> 6);
406         data->version = (packet[0] >> 3) & 0x07;
407         data->mode = packet[0] & 0x07;
408         data->stratum = packet[1];
409
410         for (i = 0, d = 0.0; i < 8; ++i)
411                 d = 256.0 * d + packet[NTP_RECEIVE+i];
412
413         data->receive = d / NTP_SCALE;
414
415         for (i = 0, d = 0.0; i < 8; ++i)
416                 d = 256.0 * d + packet[NTP_TRANSMIT+i];
417
418         data->transmit = d / NTP_SCALE;
419
420         /* See write_packet for why this isn't an endian problem. */
421         data->recvck = *(uint64_t *)(packet + NTP_ORIGINATE);
422 }
423
424 /*
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)
427  */
428 double
429 current_time(double offset)
430 {
431         struct timeval current;
432         uint64_t t;
433
434         if (gettimeofday(&current, NULL))
435                 err(1, "Could not get local time of day");
436
437         /*
438          * At this point, current has the current TAI time.
439          * Now subtract leap seconds to set the posix tick.
440          */
441
442         t = SEC_TO_TAI64(current.tv_sec);
443         if (corrleaps)
444                 ntpleaps_sub(&t);
445
446         return (offset + TAI64_TO_SEC(t) + 1.0e-6 * current.tv_usec);
447 }
448
449 /*
450  * Change offset into current UTC time. This is portable, even if
451  * struct timeval uses an unsigned long for tv_sec.
452  */
453 void
454 create_timeval(double difference, struct timeval *new, struct timeval *adjust)
455 {
456         struct timeval old;
457         long n;
458
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);
470
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;
477         }
478 }
479
480 #ifdef DEBUG
481 void
482 print_packet(const struct ntp_data *data)
483 {
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);
494 };
495 #endif