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