xref: /netbsd-src/external/mpl/bind/dist/lib/dns/ttl.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1*bcda20f6Schristos /*	$NetBSD: ttl.c,v 1.11 2025/01/26 16:25:25 christos Exp $	*/
2d68c78b8Schristos 
3d68c78b8Schristos /*
4d68c78b8Schristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5d68c78b8Schristos  *
68596601aSchristos  * SPDX-License-Identifier: MPL-2.0
78596601aSchristos  *
8d68c78b8Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
9d68c78b8Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
10fce770bdSchristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11d68c78b8Schristos  *
12d68c78b8Schristos  * See the COPYRIGHT file distributed with this work for additional
13d68c78b8Schristos  * information regarding copyright ownership.
14d68c78b8Schristos  */
15d68c78b8Schristos 
16d68c78b8Schristos /*! \file */
17d68c78b8Schristos 
18d68c78b8Schristos #include <ctype.h>
19d68c78b8Schristos #include <errno.h>
205606745fSchristos #include <inttypes.h>
21d4a20c3eSchristos #include <stdbool.h>
22d68c78b8Schristos #include <stdio.h>
23d68c78b8Schristos #include <stdlib.h>
24d68c78b8Schristos 
25*bcda20f6Schristos #include <isc/ascii.h>
26d68c78b8Schristos #include <isc/buffer.h>
27d68c78b8Schristos #include <isc/parseint.h>
28d68c78b8Schristos #include <isc/region.h>
29bb5aa156Schristos #include <isc/result.h>
30d68c78b8Schristos #include <isc/string.h>
31d68c78b8Schristos #include <isc/util.h>
32d68c78b8Schristos 
33d68c78b8Schristos #include <dns/ttl.h>
34d68c78b8Schristos 
355606745fSchristos #define RETERR(x)                        \
365606745fSchristos 	do {                             \
37d68c78b8Schristos 		isc_result_t _r = (x);   \
38d68c78b8Schristos 		if (_r != ISC_R_SUCCESS) \
395606745fSchristos 			return ((_r));   \
4053cc4e50Srillig 	} while (0)
41d68c78b8Schristos 
425606745fSchristos static isc_result_t
435606745fSchristos bind_ttl(isc_textregion_t *source, uint32_t *ttl);
44d68c78b8Schristos 
45d68c78b8Schristos /*
46d68c78b8Schristos  * Helper for dns_ttl_totext().
47d68c78b8Schristos  */
48d68c78b8Schristos static isc_result_t
495606745fSchristos ttlfmt(unsigned int t, const char *s, bool verbose, bool space,
505606745fSchristos        isc_buffer_t *target) {
51d68c78b8Schristos 	char tmp[60];
52d68c78b8Schristos 	unsigned int len;
53d68c78b8Schristos 	isc_region_t region;
54d68c78b8Schristos 
555606745fSchristos 	if (verbose) {
565606745fSchristos 		len = snprintf(tmp, sizeof(tmp), "%s%u %s%s", space ? " " : "",
575606745fSchristos 			       t, s, t == 1 ? "" : "s");
585606745fSchristos 	} else {
59d68c78b8Schristos 		len = snprintf(tmp, sizeof(tmp), "%u%c", t, s[0]);
605606745fSchristos 	}
61d68c78b8Schristos 
62d68c78b8Schristos 	INSIST(len + 1 <= sizeof(tmp));
63d68c78b8Schristos 	isc_buffer_availableregion(target, &region);
645606745fSchristos 	if (len > region.length) {
65*bcda20f6Schristos 		return ISC_R_NOSPACE;
665606745fSchristos 	}
67d68c78b8Schristos 	memmove(region.base, tmp, len);
68d68c78b8Schristos 	isc_buffer_add(target, len);
69d68c78b8Schristos 
70*bcda20f6Schristos 	return ISC_R_SUCCESS;
71d68c78b8Schristos }
72d68c78b8Schristos 
73d68c78b8Schristos /*
74d68c78b8Schristos  * Derived from bind8 ns_format_ttl().
75d68c78b8Schristos  */
76d68c78b8Schristos isc_result_t
775606745fSchristos dns_ttl_totext(uint32_t src, bool verbose, bool upcase, isc_buffer_t *target) {
78*bcda20f6Schristos 	unsigned int secs, mins, hours, days, weeks, x;
79d68c78b8Schristos 
805606745fSchristos 	secs = src % 60;
815606745fSchristos 	src /= 60;
825606745fSchristos 	mins = src % 60;
835606745fSchristos 	src /= 60;
845606745fSchristos 	hours = src % 24;
855606745fSchristos 	src /= 24;
865606745fSchristos 	days = src % 7;
875606745fSchristos 	src /= 7;
885606745fSchristos 	weeks = src;
895606745fSchristos 	src = 0;
90d68c78b8Schristos 	POST(src);
91d68c78b8Schristos 
92d68c78b8Schristos 	x = 0;
93d68c78b8Schristos 	if (weeks != 0) {
94d4a20c3eSchristos 		RETERR(ttlfmt(weeks, "week", verbose, (x > 0), target));
95d68c78b8Schristos 		x++;
96d68c78b8Schristos 	}
97d68c78b8Schristos 	if (days != 0) {
98d4a20c3eSchristos 		RETERR(ttlfmt(days, "day", verbose, (x > 0), target));
99d68c78b8Schristos 		x++;
100d68c78b8Schristos 	}
101d68c78b8Schristos 	if (hours != 0) {
102d4a20c3eSchristos 		RETERR(ttlfmt(hours, "hour", verbose, (x > 0), target));
103d68c78b8Schristos 		x++;
104d68c78b8Schristos 	}
105d68c78b8Schristos 	if (mins != 0) {
106d4a20c3eSchristos 		RETERR(ttlfmt(mins, "minute", verbose, (x > 0), target));
107d68c78b8Schristos 		x++;
108d68c78b8Schristos 	}
1095606745fSchristos 	if (secs != 0 || (weeks == 0 && days == 0 && hours == 0 && mins == 0)) {
110d4a20c3eSchristos 		RETERR(ttlfmt(secs, "second", verbose, (x > 0), target));
111d68c78b8Schristos 		x++;
112d68c78b8Schristos 	}
113d68c78b8Schristos 	INSIST(x > 0);
114d68c78b8Schristos 	/*
115d68c78b8Schristos 	 * If only a single unit letter is printed, print it
116d68c78b8Schristos 	 * in upper case. (Why?  Because BIND 8 does that.
117d68c78b8Schristos 	 * Presumably it has a reason.)
118d68c78b8Schristos 	 */
119d68c78b8Schristos 	if (x == 1 && upcase && !verbose) {
120d68c78b8Schristos 		isc_region_t region;
121d68c78b8Schristos 		/*
122d68c78b8Schristos 		 * The unit letter is the last character in the
123d68c78b8Schristos 		 * used region of the buffer.
124d68c78b8Schristos 		 */
125d68c78b8Schristos 		isc_buffer_usedregion(target, &region);
126d68c78b8Schristos 		region.base[region.length - 1] =
127*bcda20f6Schristos 			isc_ascii_toupper(region.base[region.length - 1]);
128d68c78b8Schristos 	}
129*bcda20f6Schristos 	return ISC_R_SUCCESS;
130d68c78b8Schristos }
131d68c78b8Schristos 
132d68c78b8Schristos isc_result_t
133d4a20c3eSchristos dns_counter_fromtext(isc_textregion_t *source, uint32_t *ttl) {
134*bcda20f6Schristos 	return bind_ttl(source, ttl);
135d68c78b8Schristos }
136d68c78b8Schristos 
137d68c78b8Schristos isc_result_t
138d4a20c3eSchristos dns_ttl_fromtext(isc_textregion_t *source, uint32_t *ttl) {
139d68c78b8Schristos 	isc_result_t result;
140d68c78b8Schristos 
141d68c78b8Schristos 	result = bind_ttl(source, ttl);
1425606745fSchristos 	if (result != ISC_R_SUCCESS && result != ISC_R_RANGE) {
143d68c78b8Schristos 		result = DNS_R_BADTTL;
1445606745fSchristos 	}
145*bcda20f6Schristos 	return result;
146d68c78b8Schristos }
147d68c78b8Schristos 
148d68c78b8Schristos static isc_result_t
149d4a20c3eSchristos bind_ttl(isc_textregion_t *source, uint32_t *ttl) {
150d4a20c3eSchristos 	uint64_t tmp = 0ULL;
151d4a20c3eSchristos 	uint32_t n;
152d68c78b8Schristos 	char *s;
153d68c78b8Schristos 	char buf[64];
154d68c78b8Schristos 	char nbuf[64]; /* Number buffer */
155d68c78b8Schristos 
156d68c78b8Schristos 	/*
157d68c78b8Schristos 	 * Copy the buffer as it may not be NULL terminated.
158d68c78b8Schristos 	 * No legal counter / ttl is longer that 63 characters.
159d68c78b8Schristos 	 */
1605606745fSchristos 	if (source->length > sizeof(buf) - 1) {
161*bcda20f6Schristos 		return DNS_R_SYNTAX;
1625606745fSchristos 	}
163d68c78b8Schristos 	/* Copy source->length bytes and NUL terminate. */
164d68c78b8Schristos 	snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base);
165d68c78b8Schristos 	s = buf;
166d68c78b8Schristos 
167d68c78b8Schristos 	do {
168d68c78b8Schristos 		isc_result_t result;
169d68c78b8Schristos 
170d68c78b8Schristos 		char *np = nbuf;
1715606745fSchristos 		while (*s != '\0' && isdigit((unsigned char)*s)) {
172d68c78b8Schristos 			*np++ = *s++;
1735606745fSchristos 		}
174d68c78b8Schristos 		*np++ = '\0';
175d68c78b8Schristos 		INSIST(np - nbuf <= (int)sizeof(nbuf));
176d68c78b8Schristos 		result = isc_parse_uint32(&n, nbuf, 10);
1775606745fSchristos 		if (result != ISC_R_SUCCESS) {
178*bcda20f6Schristos 			return DNS_R_SYNTAX;
1795606745fSchristos 		}
180d68c78b8Schristos 		switch (*s) {
181d68c78b8Schristos 		case 'w':
182d68c78b8Schristos 		case 'W':
183d4a20c3eSchristos 			tmp += (uint64_t)n * 7 * 24 * 3600;
184d68c78b8Schristos 			s++;
185d68c78b8Schristos 			break;
186d68c78b8Schristos 		case 'd':
187d68c78b8Schristos 		case 'D':
188d4a20c3eSchristos 			tmp += (uint64_t)n * 24 * 3600;
189d68c78b8Schristos 			s++;
190d68c78b8Schristos 			break;
191d68c78b8Schristos 		case 'h':
192d68c78b8Schristos 		case 'H':
193d4a20c3eSchristos 			tmp += (uint64_t)n * 3600;
194d68c78b8Schristos 			s++;
195d68c78b8Schristos 			break;
196d68c78b8Schristos 		case 'm':
197d68c78b8Schristos 		case 'M':
198d4a20c3eSchristos 			tmp += (uint64_t)n * 60;
199d68c78b8Schristos 			s++;
200d68c78b8Schristos 			break;
201d68c78b8Schristos 		case 's':
202d68c78b8Schristos 		case 'S':
203d4a20c3eSchristos 			tmp += (uint64_t)n;
204d68c78b8Schristos 			s++;
205d68c78b8Schristos 			break;
206d68c78b8Schristos 		case '\0':
207d68c78b8Schristos 			/* Plain number? */
2085606745fSchristos 			if (tmp != 0ULL) {
209*bcda20f6Schristos 				return DNS_R_SYNTAX;
2105606745fSchristos 			}
211d68c78b8Schristos 			tmp = n;
212d68c78b8Schristos 			break;
213d68c78b8Schristos 		default:
214*bcda20f6Schristos 			return DNS_R_SYNTAX;
215d68c78b8Schristos 		}
216d68c78b8Schristos 	} while (*s != '\0');
217d68c78b8Schristos 
2185606745fSchristos 	if (tmp > 0xffffffffULL) {
219*bcda20f6Schristos 		return ISC_R_RANGE;
2205606745fSchristos 	}
221d68c78b8Schristos 
222d4a20c3eSchristos 	*ttl = (uint32_t)(tmp & 0xffffffffUL);
223*bcda20f6Schristos 	return ISC_R_SUCCESS;
224d68c78b8Schristos }
225