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