xref: /netbsd/external/mpl/bind/dist/lib/dns/ttl.c (revision c0b5d9fb)
1 /*	$NetBSD: ttl.c,v 1.9 2022/09/23 12:15:30 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <ctype.h>
19 #include <errno.h>
20 #include <inttypes.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 
25 #include <isc/buffer.h>
26 #include <isc/parseint.h>
27 #include <isc/print.h>
28 #include <isc/region.h>
29 #include <isc/string.h>
30 #include <isc/util.h>
31 
32 #include <dns/result.h>
33 #include <dns/ttl.h>
34 
35 #define RETERR(x)                        \
36 	do {                             \
37 		isc_result_t _r = (x);   \
38 		if (_r != ISC_R_SUCCESS) \
39 			return ((_r));   \
40 	} while (0)
41 
42 static isc_result_t
43 bind_ttl(isc_textregion_t *source, uint32_t *ttl);
44 
45 /*
46  * Helper for dns_ttl_totext().
47  */
48 static isc_result_t
ttlfmt(unsigned int t,const char * s,bool verbose,bool space,isc_buffer_t * target)49 ttlfmt(unsigned int t, const char *s, bool verbose, bool space,
50        isc_buffer_t *target) {
51 	char tmp[60];
52 	unsigned int len;
53 	isc_region_t region;
54 
55 	if (verbose) {
56 		len = snprintf(tmp, sizeof(tmp), "%s%u %s%s", space ? " " : "",
57 			       t, s, t == 1 ? "" : "s");
58 	} else {
59 		len = snprintf(tmp, sizeof(tmp), "%u%c", t, s[0]);
60 	}
61 
62 	INSIST(len + 1 <= sizeof(tmp));
63 	isc_buffer_availableregion(target, &region);
64 	if (len > region.length) {
65 		return (ISC_R_NOSPACE);
66 	}
67 	memmove(region.base, tmp, len);
68 	isc_buffer_add(target, len);
69 
70 	return (ISC_R_SUCCESS);
71 }
72 
73 /*
74  * Derived from bind8 ns_format_ttl().
75  */
76 isc_result_t
dns_ttl_totext(uint32_t src,bool verbose,bool upcase,isc_buffer_t * target)77 dns_ttl_totext(uint32_t src, bool verbose, bool upcase, isc_buffer_t *target) {
78 	unsigned secs, mins, hours, days, weeks, x;
79 
80 	secs = src % 60;
81 	src /= 60;
82 	mins = src % 60;
83 	src /= 60;
84 	hours = src % 24;
85 	src /= 24;
86 	days = src % 7;
87 	src /= 7;
88 	weeks = src;
89 	src = 0;
90 	POST(src);
91 
92 	x = 0;
93 	if (weeks != 0) {
94 		RETERR(ttlfmt(weeks, "week", verbose, (x > 0), target));
95 		x++;
96 	}
97 	if (days != 0) {
98 		RETERR(ttlfmt(days, "day", verbose, (x > 0), target));
99 		x++;
100 	}
101 	if (hours != 0) {
102 		RETERR(ttlfmt(hours, "hour", verbose, (x > 0), target));
103 		x++;
104 	}
105 	if (mins != 0) {
106 		RETERR(ttlfmt(mins, "minute", verbose, (x > 0), target));
107 		x++;
108 	}
109 	if (secs != 0 || (weeks == 0 && days == 0 && hours == 0 && mins == 0)) {
110 		RETERR(ttlfmt(secs, "second", verbose, (x > 0), target));
111 		x++;
112 	}
113 	INSIST(x > 0);
114 	/*
115 	 * If only a single unit letter is printed, print it
116 	 * in upper case. (Why?  Because BIND 8 does that.
117 	 * Presumably it has a reason.)
118 	 */
119 	if (x == 1 && upcase && !verbose) {
120 		isc_region_t region;
121 		/*
122 		 * The unit letter is the last character in the
123 		 * used region of the buffer.
124 		 *
125 		 * toupper() does not need its argument to be masked of cast
126 		 * here because region.base is type unsigned char *.
127 		 */
128 		isc_buffer_usedregion(target, &region);
129 		region.base[region.length - 1] =
130 			toupper(region.base[region.length - 1]);
131 	}
132 	return (ISC_R_SUCCESS);
133 }
134 
135 isc_result_t
dns_counter_fromtext(isc_textregion_t * source,uint32_t * ttl)136 dns_counter_fromtext(isc_textregion_t *source, uint32_t *ttl) {
137 	return (bind_ttl(source, ttl));
138 }
139 
140 isc_result_t
dns_ttl_fromtext(isc_textregion_t * source,uint32_t * ttl)141 dns_ttl_fromtext(isc_textregion_t *source, uint32_t *ttl) {
142 	isc_result_t result;
143 
144 	result = bind_ttl(source, ttl);
145 	if (result != ISC_R_SUCCESS && result != ISC_R_RANGE) {
146 		result = DNS_R_BADTTL;
147 	}
148 	return (result);
149 }
150 
151 static isc_result_t
bind_ttl(isc_textregion_t * source,uint32_t * ttl)152 bind_ttl(isc_textregion_t *source, uint32_t *ttl) {
153 	uint64_t tmp = 0ULL;
154 	uint32_t n;
155 	char *s;
156 	char buf[64];
157 	char nbuf[64]; /* Number buffer */
158 
159 	/*
160 	 * Copy the buffer as it may not be NULL terminated.
161 	 * No legal counter / ttl is longer that 63 characters.
162 	 */
163 	if (source->length > sizeof(buf) - 1) {
164 		return (DNS_R_SYNTAX);
165 	}
166 	/* Copy source->length bytes and NUL terminate. */
167 	snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base);
168 	s = buf;
169 
170 	do {
171 		isc_result_t result;
172 
173 		char *np = nbuf;
174 		while (*s != '\0' && isdigit((unsigned char)*s)) {
175 			*np++ = *s++;
176 		}
177 		*np++ = '\0';
178 		INSIST(np - nbuf <= (int)sizeof(nbuf));
179 		result = isc_parse_uint32(&n, nbuf, 10);
180 		if (result != ISC_R_SUCCESS) {
181 			return (DNS_R_SYNTAX);
182 		}
183 		switch (*s) {
184 		case 'w':
185 		case 'W':
186 			tmp += (uint64_t)n * 7 * 24 * 3600;
187 			s++;
188 			break;
189 		case 'd':
190 		case 'D':
191 			tmp += (uint64_t)n * 24 * 3600;
192 			s++;
193 			break;
194 		case 'h':
195 		case 'H':
196 			tmp += (uint64_t)n * 3600;
197 			s++;
198 			break;
199 		case 'm':
200 		case 'M':
201 			tmp += (uint64_t)n * 60;
202 			s++;
203 			break;
204 		case 's':
205 		case 'S':
206 			tmp += (uint64_t)n;
207 			s++;
208 			break;
209 		case '\0':
210 			/* Plain number? */
211 			if (tmp != 0ULL) {
212 				return (DNS_R_SYNTAX);
213 			}
214 			tmp = n;
215 			break;
216 		default:
217 			return (DNS_R_SYNTAX);
218 		}
219 	} while (*s != '\0');
220 
221 	if (tmp > 0xffffffffULL) {
222 		return (ISC_R_RANGE);
223 	}
224 
225 	*ttl = (uint32_t)(tmp & 0xffffffffUL);
226 	return (ISC_R_SUCCESS);
227 }
228