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 <errno.h> |
18 | | #include <inttypes.h> |
19 | | #include <stdbool.h> |
20 | | #include <stdio.h> |
21 | | #include <stdlib.h> |
22 | | |
23 | | #include <isc/ascii.h> |
24 | | #include <isc/buffer.h> |
25 | | #include <isc/parseint.h> |
26 | | #include <isc/region.h> |
27 | | #include <isc/result.h> |
28 | | #include <isc/string.h> |
29 | | #include <isc/util.h> |
30 | | |
31 | | #include <dns/ttl.h> |
32 | | |
33 | | #define RETERR(x) \ |
34 | 84.5k | do { \ |
35 | 84.5k | isc_result_t _r = (x); \ |
36 | 84.5k | if (_r != ISC_R_SUCCESS) \ |
37 | 84.5k | return ((_r)); \ |
38 | 84.5k | } while (0) |
39 | | |
40 | | static isc_result_t |
41 | | bind_ttl(isc_textregion_t *source, uint32_t *ttl); |
42 | | |
43 | | /* |
44 | | * Helper for dns_ttl_totext(). |
45 | | */ |
46 | | static isc_result_t |
47 | | ttlfmt(unsigned int t, const char *s, bool verbose, bool space, |
48 | 84.5k | isc_buffer_t *target) { |
49 | 84.5k | char tmp[60]; |
50 | 84.5k | unsigned int len; |
51 | 84.5k | isc_region_t region; |
52 | | |
53 | 84.5k | if (verbose) { |
54 | 84.5k | len = snprintf(tmp, sizeof(tmp), "%s%u %s%s", space ? " " : "", |
55 | 84.5k | t, s, t == 1 ? "" : "s"); |
56 | 84.5k | } else { |
57 | 0 | len = snprintf(tmp, sizeof(tmp), "%u%c", t, s[0]); |
58 | 0 | } |
59 | | |
60 | 84.5k | INSIST(len + 1 <= sizeof(tmp)); |
61 | 84.5k | isc_buffer_availableregion(target, ®ion); |
62 | 84.5k | if (len > region.length) { |
63 | 0 | return ISC_R_NOSPACE; |
64 | 0 | } |
65 | 84.5k | memmove(region.base, tmp, len); |
66 | 84.5k | isc_buffer_add(target, len); |
67 | | |
68 | 84.5k | return ISC_R_SUCCESS; |
69 | 84.5k | } |
70 | | |
71 | | /* |
72 | | * Derived from bind8 ns_format_ttl(). |
73 | | */ |
74 | | isc_result_t |
75 | 22.3k | dns_ttl_totext(uint32_t src, bool verbose, bool upcase, isc_buffer_t *target) { |
76 | 22.3k | unsigned int secs, mins, hours, days, weeks, x; |
77 | | |
78 | 22.3k | secs = src % 60; |
79 | 22.3k | src /= 60; |
80 | 22.3k | mins = src % 60; |
81 | 22.3k | src /= 60; |
82 | 22.3k | hours = src % 24; |
83 | 22.3k | src /= 24; |
84 | 22.3k | days = src % 7; |
85 | 22.3k | src /= 7; |
86 | 22.3k | weeks = src; |
87 | 22.3k | src = 0; |
88 | 22.3k | POST(src); |
89 | | |
90 | 22.3k | x = 0; |
91 | 22.3k | if (weeks != 0) { |
92 | 14.1k | RETERR(ttlfmt(weeks, "week", verbose, x > 0, target)); |
93 | 14.1k | x++; |
94 | 14.1k | } |
95 | 22.3k | if (days != 0) { |
96 | 14.6k | RETERR(ttlfmt(days, "day", verbose, x > 0, target)); |
97 | 14.6k | x++; |
98 | 14.6k | } |
99 | 22.3k | if (hours != 0) { |
100 | 19.1k | RETERR(ttlfmt(hours, "hour", verbose, x > 0, target)); |
101 | 19.1k | x++; |
102 | 19.1k | } |
103 | 22.3k | if (mins != 0) { |
104 | 19.6k | RETERR(ttlfmt(mins, "minute", verbose, x > 0, target)); |
105 | 19.6k | x++; |
106 | 19.6k | } |
107 | 22.3k | if (secs != 0 || (weeks == 0 && days == 0 && hours == 0 && mins == 0)) { |
108 | 16.8k | RETERR(ttlfmt(secs, "second", verbose, x > 0, target)); |
109 | 16.8k | x++; |
110 | 16.8k | } |
111 | 22.3k | INSIST(x > 0); |
112 | | /* |
113 | | * If only a single unit letter is printed, print it |
114 | | * in upper case. (Why? Because BIND 8 does that. |
115 | | * Presumably it has a reason.) |
116 | | */ |
117 | 22.3k | if (x == 1 && upcase && !verbose) { |
118 | 0 | isc_region_t region; |
119 | | /* |
120 | | * The unit letter is the last character in the |
121 | | * used region of the buffer. |
122 | | */ |
123 | 0 | isc_buffer_usedregion(target, ®ion); |
124 | 0 | region.base[region.length - 1] = |
125 | 0 | isc_ascii_toupper(region.base[region.length - 1]); |
126 | 0 | } |
127 | 22.3k | return ISC_R_SUCCESS; |
128 | 22.3k | } |
129 | | |
130 | | isc_result_t |
131 | 9.68k | dns_counter_fromtext(isc_textregion_t *source, uint32_t *ttl) { |
132 | 9.68k | return bind_ttl(source, ttl); |
133 | 9.68k | } |
134 | | |
135 | | isc_result_t |
136 | 283k | dns_ttl_fromtext(isc_textregion_t *source, uint32_t *ttl) { |
137 | 283k | isc_result_t result; |
138 | | |
139 | 283k | result = bind_ttl(source, ttl); |
140 | 283k | if (result != ISC_R_SUCCESS && result != ISC_R_RANGE) { |
141 | 247k | result = DNS_R_BADTTL; |
142 | 247k | } |
143 | 283k | return result; |
144 | 283k | } |
145 | | |
146 | | static isc_result_t |
147 | 293k | bind_ttl(isc_textregion_t *source, uint32_t *ttl) { |
148 | 293k | uint64_t tmp = 0ULL; |
149 | 293k | uint32_t n; |
150 | 293k | char *s; |
151 | 293k | char buf[64]; |
152 | 293k | char nbuf[64]; /* Number buffer */ |
153 | | |
154 | | /* |
155 | | * Copy the buffer as it may not be NULL terminated. |
156 | | * No legal counter / ttl is longer that 63 characters. |
157 | | */ |
158 | 293k | if (source->length > sizeof(buf) - 1) { |
159 | 393 | return DNS_R_SYNTAX; |
160 | 393 | } |
161 | | /* Copy source->length bytes and NUL terminate. */ |
162 | 292k | snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base); |
163 | 292k | s = buf; |
164 | | |
165 | 293k | do { |
166 | 293k | isc_result_t result; |
167 | | |
168 | 293k | char *np = nbuf; |
169 | 402k | while (*s != '\0' && isdigit((unsigned char)*s)) { |
170 | 108k | *np++ = *s++; |
171 | 108k | } |
172 | 293k | *np++ = '\0'; |
173 | 293k | INSIST(np - nbuf <= (int)sizeof(nbuf)); |
174 | 293k | result = isc_parse_uint32(&n, nbuf, 10); |
175 | 293k | if (result != ISC_R_SUCCESS) { |
176 | 247k | return DNS_R_SYNTAX; |
177 | 247k | } |
178 | 46.6k | switch (*s) { |
179 | 359 | case 'w': |
180 | 511 | case 'W': |
181 | 511 | tmp += (uint64_t)n * 7 * 24 * 3600; |
182 | 511 | s++; |
183 | 511 | break; |
184 | 415 | case 'd': |
185 | 8.30k | case 'D': |
186 | 8.30k | tmp += (uint64_t)n * 24 * 3600; |
187 | 8.30k | s++; |
188 | 8.30k | break; |
189 | 128 | case 'h': |
190 | 1.65k | case 'H': |
191 | 1.65k | tmp += (uint64_t)n * 3600; |
192 | 1.65k | s++; |
193 | 1.65k | break; |
194 | 370 | case 'm': |
195 | 694 | case 'M': |
196 | 694 | tmp += (uint64_t)n * 60; |
197 | 694 | s++; |
198 | 694 | break; |
199 | 145 | case 's': |
200 | 307 | case 'S': |
201 | 307 | tmp += (uint64_t)n; |
202 | 307 | s++; |
203 | 307 | break; |
204 | 35.0k | case '\0': |
205 | | /* Plain number? */ |
206 | 35.0k | if (tmp != 0ULL) { |
207 | 82 | return DNS_R_SYNTAX; |
208 | 82 | } |
209 | 34.9k | tmp = n; |
210 | 34.9k | break; |
211 | 134 | default: |
212 | 134 | return DNS_R_SYNTAX; |
213 | 46.6k | } |
214 | 46.6k | } while (*s != '\0'); |
215 | | |
216 | 45.2k | if (tmp > 0xffffffffULL) { |
217 | 19 | return ISC_R_RANGE; |
218 | 19 | } |
219 | | |
220 | 45.2k | *ttl = (uint32_t)(tmp & 0xffffffffUL); |
221 | 45.2k | return ISC_R_SUCCESS; |
222 | 45.2k | } |