/src/bind9/lib/dns/time.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | /*! \file */ |
15 | | |
16 | | #include <ctype.h> |
17 | | #include <inttypes.h> |
18 | | #include <stdio.h> |
19 | | #include <time.h> |
20 | | |
21 | | #include <isc/region.h> |
22 | | #include <isc/result.h> |
23 | | #include <isc/serial.h> |
24 | | #include <isc/stdtime.h> |
25 | | #include <isc/string.h> |
26 | | #include <isc/util.h> |
27 | | |
28 | | #include <dns/time.h> |
29 | | |
30 | | static const int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
31 | | |
32 | | isc_result_t |
33 | 23.1k | dns_time64_totext(int64_t t, isc_buffer_t *target) { |
34 | 23.1k | struct tm tm; |
35 | 23.1k | char buf[sizeof("!!!!!!YYYY!!!!!!!!MM!!!!!!!!DD!!!!!!!!HH!!!!!!!!MM!!!!" |
36 | 23.1k | "!!!!SS")]; |
37 | 23.1k | int secs; |
38 | 23.1k | unsigned int l; |
39 | 23.1k | isc_region_t region; |
40 | | |
41 | | /* |
42 | | * Warning. Do NOT use arguments with side effects with these macros. |
43 | | */ |
44 | 1.28M | #define is_leap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) |
45 | 513k | #define year_secs(y) ((is_leap(y) ? 366 : 365) * 86400) |
46 | 126k | #define month_secs(m, y) ((days[m] + ((m == 1 && is_leap(y)) ? 1 : 0)) * 86400) |
47 | | |
48 | 23.1k | tm.tm_year = 70; |
49 | 31.0k | while (t < 0) { |
50 | 7.82k | if (tm.tm_year == 0) { |
51 | 0 | return ISC_R_RANGE; |
52 | 0 | } |
53 | 7.82k | tm.tm_year--; |
54 | 7.82k | secs = year_secs(tm.tm_year + 1900); |
55 | 7.82k | t += secs; |
56 | 7.82k | } |
57 | 505k | while ((secs = year_secs(tm.tm_year + 1900)) <= t) { |
58 | 482k | t -= secs; |
59 | 482k | tm.tm_year++; |
60 | 482k | if (tm.tm_year + 1900 > 9999) { |
61 | 0 | return ISC_R_RANGE; |
62 | 0 | } |
63 | 482k | } |
64 | 23.1k | tm.tm_mon = 0; |
65 | 126k | while ((secs = month_secs(tm.tm_mon, tm.tm_year + 1900)) <= t) { |
66 | 103k | t -= secs; |
67 | 103k | tm.tm_mon++; |
68 | 103k | } |
69 | 23.1k | tm.tm_mday = 1; |
70 | 304k | while (86400 <= t) { |
71 | 281k | t -= 86400; |
72 | 281k | tm.tm_mday++; |
73 | 281k | } |
74 | 23.1k | tm.tm_hour = 0; |
75 | 276k | while (3600 <= t) { |
76 | 253k | t -= 3600; |
77 | 253k | tm.tm_hour++; |
78 | 253k | } |
79 | 23.1k | tm.tm_min = 0; |
80 | 597k | while (60 <= t) { |
81 | 574k | t -= 60; |
82 | 574k | tm.tm_min++; |
83 | 574k | } |
84 | 23.1k | tm.tm_sec = (int)t; |
85 | | /* yyyy mm dd HH MM SS */ |
86 | 23.1k | snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d", |
87 | 23.1k | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, |
88 | 23.1k | tm.tm_min, tm.tm_sec); |
89 | | |
90 | 23.1k | isc_buffer_availableregion(target, ®ion); |
91 | 23.1k | l = strlen(buf); |
92 | | |
93 | 23.1k | if (l > region.length) { |
94 | 0 | return ISC_R_NOSPACE; |
95 | 0 | } |
96 | | |
97 | 23.1k | memmove(region.base, buf, l); |
98 | 23.1k | isc_buffer_add(target, l); |
99 | 23.1k | return ISC_R_SUCCESS; |
100 | 23.1k | } |
101 | | |
102 | | int64_t |
103 | 23.1k | dns_time64_from32(uint32_t value) { |
104 | 23.1k | isc_stdtime_t now = isc_stdtime_now(); |
105 | 23.1k | int64_t start; |
106 | 23.1k | int64_t t; |
107 | | |
108 | | /* |
109 | | * Adjust the time to the closest epoch. This should be changed |
110 | | * to use a 64-bit counterpart to isc_stdtime_now() if one ever |
111 | | * is defined, but even the current code is good until the year |
112 | | * 2106. |
113 | | */ |
114 | | |
115 | 23.1k | start = (int64_t)now; |
116 | 23.1k | if (isc_serial_gt(value, now)) { |
117 | 3.02k | t = start + (value - now); |
118 | 20.1k | } else { |
119 | 20.1k | t = start - (now - value); |
120 | 20.1k | } |
121 | | |
122 | 23.1k | return t; |
123 | 23.1k | } |
124 | | |
125 | | isc_result_t |
126 | 23.1k | dns_time32_totext(uint32_t value, isc_buffer_t *target) { |
127 | 23.1k | return dns_time64_totext(dns_time64_from32(value), target); |
128 | 23.1k | } |
129 | | |
130 | | isc_result_t |
131 | 3.69k | dns_time64_fromtext(const char *source, int64_t *target) { |
132 | 3.69k | int year, month, day, hour, minute, second; |
133 | 3.69k | int64_t value; |
134 | 3.69k | int secs; |
135 | 3.69k | int i; |
136 | | |
137 | 3.69k | #define RANGE(min, max, value) \ |
138 | 21.5k | do { \ |
139 | 21.5k | if (value < (min) || value > (max)) \ |
140 | 21.5k | return ((ISC_R_RANGE)); \ |
141 | 21.5k | } while (0) |
142 | | |
143 | 3.69k | if (strlen(source) != 14U) { |
144 | 77 | return DNS_R_SYNTAX; |
145 | 77 | } |
146 | | /* |
147 | | * Confirm the source only consists digits. sscanf() allows some |
148 | | * minor exceptions. |
149 | | */ |
150 | 54.2k | for (i = 0; i < 14; i++) { |
151 | 50.5k | if (!isdigit((unsigned char)source[i])) { |
152 | 9 | return DNS_R_SYNTAX; |
153 | 9 | } |
154 | 50.5k | } |
155 | 3.61k | if (sscanf(source, "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, |
156 | 3.61k | &minute, &second) != 6) |
157 | 0 | { |
158 | 0 | return DNS_R_SYNTAX; |
159 | 0 | } |
160 | | |
161 | 3.61k | RANGE(0, 9999, year); |
162 | 3.61k | RANGE(1, 12, month); |
163 | 3.59k | RANGE(1, days[month - 1] + ((month == 2 && is_leap(year)) ? 1 : 0), |
164 | 3.59k | day); |
165 | | #ifdef __COVERITY__ |
166 | | /* |
167 | | * Use a simplified range to silence Coverity warning (in |
168 | | * arithmetic with day below). |
169 | | */ |
170 | | RANGE(1, 31, day); |
171 | | #endif /* __COVERITY__ */ |
172 | | |
173 | 3.58k | RANGE(0, 23, hour); |
174 | 3.57k | RANGE(0, 59, minute); |
175 | 3.57k | RANGE(0, 60, second); /* 60 == leap second. */ |
176 | | |
177 | | /* |
178 | | * Calculate seconds from epoch. |
179 | | * Note: this uses a idealized calendar. |
180 | | */ |
181 | 3.56k | value = second + (60 * minute) + (3600 * hour) + ((day - 1) * 86400); |
182 | 16.6k | for (i = 0; i < (month - 1); i++) { |
183 | 13.1k | value += days[i] * 86400; |
184 | 13.1k | } |
185 | 3.56k | if (is_leap(year) && month > 2) { |
186 | 701 | value += 86400; |
187 | 701 | } |
188 | 3.56k | if (year < 1970) { |
189 | 126k | for (i = 1969; i >= year; i--) { |
190 | 126k | secs = (is_leap(i) ? 366 : 365) * 86400; |
191 | 126k | value -= secs; |
192 | 126k | } |
193 | 3.20k | } else { |
194 | 620k | for (i = 1970; i < year; i++) { |
195 | 617k | secs = (is_leap(i) ? 366 : 365) * 86400; |
196 | 617k | value += secs; |
197 | 617k | } |
198 | 3.20k | } |
199 | | |
200 | 3.56k | *target = value; |
201 | 3.56k | return ISC_R_SUCCESS; |
202 | 3.57k | } |
203 | | |
204 | | isc_result_t |
205 | 1.14k | dns_time32_fromtext(const char *source, uint32_t *target) { |
206 | 1.14k | int64_t value64; |
207 | 1.14k | isc_result_t result; |
208 | 1.14k | result = dns_time64_fromtext(source, &value64); |
209 | 1.14k | if (result != ISC_R_SUCCESS) { |
210 | 90 | return result; |
211 | 90 | } |
212 | 1.05k | *target = (uint32_t)value64; |
213 | | |
214 | 1.05k | return ISC_R_SUCCESS; |
215 | 1.14k | } |