Fix a little redundancy from my last commit. The border cannot be outside
[dragonfly.git] / usr.sbin / rdate / ntpleaps.c
1 /*      $OpenBSD: src/usr.sbin/rdate/ntpleaps.c,v 1.7 2004/05/05 20:29:54 jakob Exp $   */
2 /*      $DragonFly: src/usr.sbin/rdate/ntpleaps.c,v 1.1 2004/12/01 15:04:43 joerg Exp $ */
3
4 /*
5  * Copyright (c) 2002 Thorsten Glaser. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  *    - Redistributions of source code must retain the above copyright
12  *      notice, this list of conditions and the following disclaimer.
13  *    - Redistributions in binary form must reproduce the above
14  *      copyright notice, this list of conditions and the following
15  *      disclaimer in the documentation and/or other materials provided
16  *      with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32
33 /* Leap second support for NTP clients (generic) */
34
35 /*
36  * I could include tzfile.h, but this would make the code unportable
37  * at no real benefit. Read tzfile.h for why.
38  */
39
40 #include <sys/types.h>
41 #include <netinet/in.h>
42
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #include "ntpleaps.h"
50
51 static uint32_t read_be_dword(uint8_t *ptr);
52
53 static uint64_t *leapsecs = NULL;
54 static unsigned int leapsecs_num = 0;
55 static int flaginit = -1;
56 static int flagwarn = 0;
57
58 int
59 ntpleaps_init(void)
60 {
61         if (!flaginit)
62                 return(0);
63
64         if (!ntpleaps_read()) {
65                 flaginit = 0;
66                 return(0);
67         }
68
69         /* This does not really hurt, but users will complain about
70          * off-by-22-seconds (at time of coding) errors if we don't warn.
71          */
72         if (!flagwarn) {
73                 fputs("Warning: error reading tzfile. You will NOT be\n"
74                     "able to get legal time or posix compliance!\n", stderr);
75                 flagwarn = 1;   /* put it only once */
76         }
77
78         return(-1);
79 }
80
81 int
82 ntpleaps_sub(uint64_t *t)
83 {
84         unsigned int i = 0;
85         u_int64_t u;
86         int r = 1;
87
88         if ((flaginit ? ntpleaps_init() : 0) == -1)
89                 return(-1);
90
91         u = *t;
92
93         while (i < leapsecs_num) {
94                 if (u < leapsecs[i])
95                         break;
96                 if (u == leapsecs[i++])
97                         goto do_sub;
98         }
99         --r;
100
101 do_sub:
102         *t = u - i;
103         return(r);
104 }
105
106 uint32_t
107 read_be_dword(uint8_t *ptr)
108 {
109         uint32_t res;
110
111         memcpy(&res, ptr, 4);
112         return(ntohl(res));
113 }
114
115 int
116 ntpleaps_read(void)
117 {
118         int fd;
119         unsigned int r;
120         uint8_t buf[32];
121         uint32_t m1, m2, m3;
122         uint64_t s;
123         uint64_t *l;
124
125         fd = open("/usr/share/zoneinfo/Etc/UTC", O_RDONLY | O_NDELAY);
126         if (fd == -1)
127                 return (-1);
128
129         /* Check signature */
130         read(fd, buf, 4);
131         buf[4] = 0;
132         if (strcmp((const char *)buf, "TZif")) {
133                 close(fd);
134                 return (-1);
135         }
136
137         /* Pre-initalize buf[24..27] so we need not check read(2) result */
138         buf[24] = 0;
139         buf[25] = 0;
140         buf[26] = 0;
141         buf[27] = 0;
142
143         /* Skip uninteresting parts of header */
144         read(fd, buf, 28);
145
146         /* Read number of leap second entries */
147         r = read_be_dword(&buf[24]);
148         /* Check for plausibility - arbitrary values */
149         if ((r < 20) || (r > 60000)) {
150                 close(fd);
151                 return (-1);
152         }
153         if ((l = (uint64_t *)malloc(r << 3)) == NULL) {
154                 close(fd);
155                 return (-1);
156         }
157
158         /* Skip further uninteresting stuff */
159         read(fd, buf, 12);
160         m1 = read_be_dword(buf);
161         m2 = read_be_dword(&buf[4]);
162         m3 = read_be_dword(&buf[8]);
163         m3 += (m1 << 2) + m1 + (m2 << 2) + (m2 << 1);
164         lseek(fd, (off_t)m3, SEEK_CUR);
165
166         /* Now go parse the tzfile leap second info */
167         for (m1 = 0; m1 < r; m1++) {
168                 if (read(fd, buf, 8) != 8) {
169                         free(l);
170                         close(fd);
171                         return(-1);
172                 }
173                 s = SEC_TO_TAI64(read_be_dword(buf));
174                 /*
175                  * Assume just _one_ leap second on each entry, and compensate
176                  * the lacking error checking by validating the first entry
177                  * against the known value
178                  */
179                 if (!m1 && s != 0x4000000004B2580AULL)
180                         return(-1);
181                 l[m1] = s;
182         }
183
184         /* Clean up and activate the table */
185         close(fd);
186         if (leapsecs != NULL)
187                 free(leapsecs);
188         leapsecs = l;
189         leapsecs_num = r;
190         return(0);
191 }