xref: /openbsd/usr.sbin/rdate/ntpleaps.c (revision 404b540a)
1 /*	$OpenBSD: ntpleaps.c,v 1.9 2007/11/26 09:28:34 martynas 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 static const char RCSId[] = "$OpenBSD: ntpleaps.c,v 1.9 2007/11/26 09:28:34 martynas Exp $";
35 
36 
37 /*
38  * I could include tzfile.h, but this would make the code unportable
39  * at no real benefit. Read tzfile.h for why.
40  */
41 
42 #include <sys/types.h>
43 #include <netinet/in.h>
44 
45 #include <fcntl.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #include "ntpleaps.h"
52 
53 static u_int64_t *leapsecs;
54 static unsigned int leapsecs_num;
55 
56 
57 int
58 ntpleaps_init(void)
59 {
60 	static int doneinit;
61 	static int donewarn;
62 
63 	if (doneinit)
64 		return (0);
65 
66 	if (ntpleaps_read() == 0) {
67 		doneinit = 1;
68 		return (0);
69 	}
70 
71 	/* This does not really hurt, but users will complain about
72 	 * off-by-22-seconds (at time of coding) errors if we don't warn.
73 	 */
74 	if (!donewarn) {
75 		fputs("Warning: error reading tzfile. You will NOT be\n"
76 		    "able to get legal time or posix compliance!\n", stderr);
77 		donewarn = 1;	/* put it only once */
78 	}
79 
80 	return (-1);
81 }
82 
83 int
84 ntpleaps_sub(u_int64_t *t)
85 {
86 	unsigned int i = 0;
87 	u_int64_t u;
88 	int r = 1;
89 
90 	if (ntpleaps_init() == -1)
91 		return (-1);
92 
93 	u = *t;
94 
95 	while (i < leapsecs_num) {
96 		if (u < leapsecs[i]) {
97 			r--;
98 			break;
99 		}
100 		if (u == leapsecs[i++])
101 			break;
102 	}
103 
104 	*t = u - i;
105 	return (r);
106 }
107 
108 u_int32_t
109 read_be_dword(u_int8_t *ptr)
110 {
111 	u_int32_t res;
112 
113 	memcpy(&res, ptr, 4);
114 	return (ntohl(res));
115 }
116 
117 
118 int
119 ntpleaps_read(void)
120 {
121 	int fd;
122 	unsigned int r;
123 	u_int8_t buf[32];
124 	u_int32_t m1, m2, m3;
125 	u_int64_t s;
126 	u_int64_t *l;
127 
128 	fd = open("/usr/share/zoneinfo/right/UTC", O_RDONLY | O_NDELAY);
129 	if (fd == -1)
130 		return (-1);
131 
132 	/* Check signature */
133 	read(fd, buf, 4);
134 	buf[4] = 0;
135 	if (strcmp((const char *)buf, "TZif")) {
136 		close(fd);
137 		return (-1);
138 	}
139 
140 	/* Pre-initialize buf[24..27] so we need not check read(2) result */
141 	buf[24] = 0;
142 	buf[25] = 0;
143 	buf[26] = 0;
144 	buf[27] = 0;
145 
146 	/* Skip uninteresting parts of header */
147 	read(fd, buf, 28);
148 
149 	/* Read number of leap second entries */
150 	r = read_be_dword(&buf[24]);
151 	/* Check for plausibility - arbitrary values */
152 	if ((r < 20) || (r > 60000)) {
153 		close(fd);
154 		return (-1);
155 	}
156 	if ((l = (u_int64_t *)malloc(r << 3)) == NULL) {
157 		close(fd);
158 		return (-1);
159 	}
160 
161 	/* Skip further uninteresting stuff */
162 	read(fd, buf, 12);
163 	m1 = read_be_dword(buf);
164 	m2 = read_be_dword(&buf[4]);
165 	m3 = read_be_dword(&buf[8]);
166 	m3 += (m1 << 2)+m1+(m2 << 2)+(m2 << 1);
167 	lseek(fd, (off_t)m3, SEEK_CUR);
168 
169 	/* Now go parse the tzfile leap second info */
170 	for (m1 = 0; m1 < r; m1++) {
171 		if (read(fd, buf, 8) != 8) {
172 			free(l);
173 			close(fd);
174 			return (-1);
175 		}
176 		s = SEC_TO_TAI64(read_be_dword(buf));
177 		/*
178 		 * Assume just _one_ leap second on each entry, and compensate
179 		 * the lacking error checking by validating the first entry
180 		 * against the known value
181 		 */
182 		if (!m1 && s != 0x4000000004B2580AULL) {
183 			free(l);
184 			close(fd);
185 			return (-1);
186 		}
187 		l[m1] = s;
188 	}
189 
190 	/* Clean up and activate the table */
191 	close(fd);
192 	if (leapsecs != NULL)
193 		free(leapsecs);
194 	leapsecs = l;
195 	leapsecs_num = r;
196 	return (0);
197 }
198