xref: /netbsd-src/external/mpl/bind/dist/lib/isccfg/duration.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1*bcda20f6Schristos /*	$NetBSD: duration.c,v 1.4 2025/01/26 16:25:45 christos Exp $	*/
28aaca124Schristos 
38aaca124Schristos /*
48aaca124Schristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
58aaca124Schristos  *
68aaca124Schristos  * SPDX-License-Identifier: MPL-2.0
78aaca124Schristos  *
88aaca124Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
98aaca124Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
108aaca124Schristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
118aaca124Schristos  *
128aaca124Schristos  * See the COPYRIGHT file distributed with this work for additional
138aaca124Schristos  * information regarding copyright ownership.
148aaca124Schristos  */
158aaca124Schristos 
168aaca124Schristos /*! \file */
178aaca124Schristos 
188aaca124Schristos #include <ctype.h>
198aaca124Schristos #include <errno.h>
208aaca124Schristos #include <inttypes.h>
218aaca124Schristos #include <stdbool.h>
228aaca124Schristos #include <stdint.h>
238aaca124Schristos #include <stdio.h>
248aaca124Schristos #include <stdlib.h>
258aaca124Schristos 
268aaca124Schristos #include <isc/buffer.h>
278aaca124Schristos #include <isc/parseint.h>
288aaca124Schristos #include <isc/region.h>
298aaca124Schristos #include <isc/result.h>
308aaca124Schristos #include <isc/string.h>
318aaca124Schristos #include <isc/util.h>
328aaca124Schristos 
338aaca124Schristos #include <dns/ttl.h>
348aaca124Schristos 
358aaca124Schristos #include <isccfg/duration.h>
368aaca124Schristos 
378aaca124Schristos /*
388aaca124Schristos  * isccfg_duration_fromtext initially taken from OpenDNSSEC code base.
398aaca124Schristos  * Modified to fit the BIND 9 code.
408aaca124Schristos  */
418aaca124Schristos isc_result_t
428aaca124Schristos isccfg_duration_fromtext(isc_textregion_t *source,
438aaca124Schristos 			 isccfg_duration_t *duration) {
448aaca124Schristos 	char buf[CFG_DURATION_MAXLEN] = { 0 };
458aaca124Schristos 	char *P, *X, *T, *W, *str;
468aaca124Schristos 	bool not_weeks = false;
478aaca124Schristos 	int i;
488aaca124Schristos 	long long int lli;
49dff692fcSchristos 	char *endptr;
508aaca124Schristos 
518aaca124Schristos 	/*
528aaca124Schristos 	 * Copy the buffer as it may not be NULL terminated.
538aaca124Schristos 	 */
548aaca124Schristos 	if (source->length > sizeof(buf) - 1) {
55*bcda20f6Schristos 		return ISC_R_BADNUMBER;
568aaca124Schristos 	}
578aaca124Schristos 	/* Copy source->length bytes and NULL terminate. */
588aaca124Schristos 	snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base);
598aaca124Schristos 	str = buf;
608aaca124Schristos 
618aaca124Schristos 	/* Clear out duration. */
628aaca124Schristos 	for (i = 0; i < 7; i++) {
638aaca124Schristos 		duration->parts[i] = 0;
648aaca124Schristos 	}
658aaca124Schristos 	duration->iso8601 = false;
668aaca124Schristos 	duration->unlimited = false;
678aaca124Schristos 
688aaca124Schristos 	/* Every duration starts with 'P' */
698aaca124Schristos 	if (toupper((unsigned char)str[0]) != 'P') {
70*bcda20f6Schristos 		return ISC_R_BADNUMBER;
718aaca124Schristos 	}
728aaca124Schristos 	P = str;
738aaca124Schristos 
748aaca124Schristos 	/* Record the time indicator. */
758aaca124Schristos 	T = strpbrk(str, "Tt");
768aaca124Schristos 
778aaca124Schristos 	/* Record years. */
788aaca124Schristos 	X = strpbrk(str, "Yy");
798aaca124Schristos 	if (X != NULL) {
808aaca124Schristos 		errno = 0;
81dff692fcSchristos 		endptr = NULL;
82dff692fcSchristos 		lli = strtoll(str + 1, &endptr, 10);
83dff692fcSchristos 		if (*endptr != *X) {
84*bcda20f6Schristos 			return ISC_R_BADNUMBER;
85dff692fcSchristos 		}
868aaca124Schristos 		if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
87*bcda20f6Schristos 			return ISC_R_BADNUMBER;
888aaca124Schristos 		}
898aaca124Schristos 		duration->parts[0] = (uint32_t)lli;
908aaca124Schristos 		str = X;
918aaca124Schristos 		not_weeks = true;
928aaca124Schristos 	}
938aaca124Schristos 
948aaca124Schristos 	/* Record months. */
958aaca124Schristos 	X = strpbrk(str, "Mm");
968aaca124Schristos 
978aaca124Schristos 	/*
988aaca124Schristos 	 * M could be months or minutes. This is months if there is no time
998aaca124Schristos 	 * part, or this M indicator is before the time indicator.
1008aaca124Schristos 	 */
1018aaca124Schristos 	if (X != NULL && (T == NULL || (size_t)(X - P) < (size_t)(T - P))) {
1028aaca124Schristos 		errno = 0;
103dff692fcSchristos 		lli = strtoll(str + 1, &endptr, 10);
104dff692fcSchristos 		if (*endptr != *X) {
105*bcda20f6Schristos 			return ISC_R_BADNUMBER;
106dff692fcSchristos 		}
1078aaca124Schristos 		if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
108*bcda20f6Schristos 			return ISC_R_BADNUMBER;
1098aaca124Schristos 		}
1108aaca124Schristos 		duration->parts[1] = (uint32_t)lli;
1118aaca124Schristos 		str = X;
1128aaca124Schristos 		not_weeks = true;
1138aaca124Schristos 	}
1148aaca124Schristos 
1158aaca124Schristos 	/* Record days. */
1168aaca124Schristos 	X = strpbrk(str, "Dd");
1178aaca124Schristos 	if (X != NULL) {
1188aaca124Schristos 		errno = 0;
119dff692fcSchristos 		lli = strtoll(str + 1, &endptr, 10);
120dff692fcSchristos 		if (*endptr != *X) {
121*bcda20f6Schristos 			return ISC_R_BADNUMBER;
122dff692fcSchristos 		}
1238aaca124Schristos 		if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
124*bcda20f6Schristos 			return ISC_R_BADNUMBER;
1258aaca124Schristos 		}
1268aaca124Schristos 		duration->parts[3] = (uint32_t)lli;
1278aaca124Schristos 		str = X;
1288aaca124Schristos 		not_weeks = true;
1298aaca124Schristos 	}
1308aaca124Schristos 
1318aaca124Schristos 	/* Time part? */
1328aaca124Schristos 	if (T != NULL) {
1338aaca124Schristos 		str = T;
1348aaca124Schristos 		not_weeks = true;
1358aaca124Schristos 	}
1368aaca124Schristos 
1378aaca124Schristos 	/* Record hours. */
1388aaca124Schristos 	X = strpbrk(str, "Hh");
1398aaca124Schristos 	if (X != NULL && T != NULL) {
1408aaca124Schristos 		errno = 0;
141dff692fcSchristos 		lli = strtoll(str + 1, &endptr, 10);
142dff692fcSchristos 		if (*endptr != *X) {
143*bcda20f6Schristos 			return ISC_R_BADNUMBER;
144dff692fcSchristos 		}
1458aaca124Schristos 		if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
146*bcda20f6Schristos 			return ISC_R_BADNUMBER;
1478aaca124Schristos 		}
1488aaca124Schristos 		duration->parts[4] = (uint32_t)lli;
1498aaca124Schristos 		str = X;
1508aaca124Schristos 		not_weeks = true;
1518aaca124Schristos 	}
1528aaca124Schristos 
1538aaca124Schristos 	/* Record minutes. */
1548aaca124Schristos 	X = strpbrk(str, "Mm");
1558aaca124Schristos 
1568aaca124Schristos 	/*
1578aaca124Schristos 	 * M could be months or minutes. This is minutes if there is a time
1588aaca124Schristos 	 * part and the M indicator is behind the time indicator.
1598aaca124Schristos 	 */
1608aaca124Schristos 	if (X != NULL && T != NULL && (size_t)(X - P) > (size_t)(T - P)) {
1618aaca124Schristos 		errno = 0;
162dff692fcSchristos 		lli = strtoll(str + 1, &endptr, 10);
163dff692fcSchristos 		if (*endptr != *X) {
164*bcda20f6Schristos 			return ISC_R_BADNUMBER;
165dff692fcSchristos 		}
1668aaca124Schristos 		if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
167*bcda20f6Schristos 			return ISC_R_BADNUMBER;
1688aaca124Schristos 		}
1698aaca124Schristos 		duration->parts[5] = (uint32_t)lli;
1708aaca124Schristos 		str = X;
1718aaca124Schristos 		not_weeks = true;
1728aaca124Schristos 	}
1738aaca124Schristos 
1748aaca124Schristos 	/* Record seconds. */
1758aaca124Schristos 	X = strpbrk(str, "Ss");
1768aaca124Schristos 	if (X != NULL && T != NULL) {
1778aaca124Schristos 		errno = 0;
178dff692fcSchristos 		lli = strtoll(str + 1, &endptr, 10);
179dff692fcSchristos 		if (*endptr != *X) {
180*bcda20f6Schristos 			return ISC_R_BADNUMBER;
181dff692fcSchristos 		}
1828aaca124Schristos 		if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
183*bcda20f6Schristos 			return ISC_R_BADNUMBER;
1848aaca124Schristos 		}
1858aaca124Schristos 		duration->parts[6] = (uint32_t)lli;
1868aaca124Schristos 		str = X;
1878aaca124Schristos 		not_weeks = true;
1888aaca124Schristos 	}
1898aaca124Schristos 
1908aaca124Schristos 	/* Or is the duration configured in weeks? */
1918aaca124Schristos 	W = strpbrk(buf, "Ww");
1928aaca124Schristos 	if (W != NULL) {
1938aaca124Schristos 		if (not_weeks) {
1948aaca124Schristos 			/* Mix of weeks and other indicators is not allowed */
195*bcda20f6Schristos 			return ISC_R_BADNUMBER;
1968aaca124Schristos 		} else {
1978aaca124Schristos 			errno = 0;
198dff692fcSchristos 			lli = strtoll(str + 1, &endptr, 10);
199dff692fcSchristos 			if (*endptr != *W) {
200*bcda20f6Schristos 				return ISC_R_BADNUMBER;
201dff692fcSchristos 			}
2028aaca124Schristos 			if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
203*bcda20f6Schristos 				return ISC_R_BADNUMBER;
2048aaca124Schristos 			}
2058aaca124Schristos 			duration->parts[2] = (uint32_t)lli;
2068aaca124Schristos 			str = W;
2078aaca124Schristos 		}
2088aaca124Schristos 	}
2098aaca124Schristos 
2108aaca124Schristos 	/* Deal with trailing garbage. */
2118aaca124Schristos 	if (str[1] != '\0') {
212*bcda20f6Schristos 		return ISC_R_BADNUMBER;
2138aaca124Schristos 	}
2148aaca124Schristos 
2158aaca124Schristos 	duration->iso8601 = true;
216*bcda20f6Schristos 	return ISC_R_SUCCESS;
2178aaca124Schristos }
2188aaca124Schristos 
2198aaca124Schristos isc_result_t
2208aaca124Schristos isccfg_parse_duration(isc_textregion_t *source, isccfg_duration_t *duration) {
2218aaca124Schristos 	isc_result_t result;
2228aaca124Schristos 
2238aaca124Schristos 	REQUIRE(duration != NULL);
2248aaca124Schristos 
2258aaca124Schristos 	duration->unlimited = false;
2268aaca124Schristos 	result = isccfg_duration_fromtext(source, duration);
2278aaca124Schristos 	if (result == ISC_R_BADNUMBER) {
2288aaca124Schristos 		/* Fallback to dns_ttl_fromtext. */
2298aaca124Schristos 		uint32_t ttl;
2308aaca124Schristos 		result = dns_ttl_fromtext(source, &ttl);
2318aaca124Schristos 		if (result == ISC_R_SUCCESS) {
2328aaca124Schristos 			/*
2338aaca124Schristos 			 * With dns_ttl_fromtext() the information on optional
2348aaca124Schristos 			 * units is lost, and is treated as seconds from now on.
2358aaca124Schristos 			 */
2368aaca124Schristos 			duration->iso8601 = false;
2378aaca124Schristos 			duration->parts[6] = ttl;
2388aaca124Schristos 		}
2398aaca124Schristos 	}
2408aaca124Schristos 
241*bcda20f6Schristos 	return result;
2428aaca124Schristos }
2438aaca124Schristos 
2448aaca124Schristos uint32_t
2458aaca124Schristos isccfg_duration_toseconds(const isccfg_duration_t *duration) {
2468aaca124Schristos 	uint64_t seconds = 0;
2478aaca124Schristos 
2488aaca124Schristos 	REQUIRE(duration != NULL);
2498aaca124Schristos 
2508aaca124Schristos 	seconds += (uint64_t)duration->parts[6];	     /* Seconds */
2518aaca124Schristos 	seconds += (uint64_t)duration->parts[5] * 60;	     /* Minutes */
2528aaca124Schristos 	seconds += (uint64_t)duration->parts[4] * 3600;	     /* Hours */
2538aaca124Schristos 	seconds += (uint64_t)duration->parts[3] * 86400;     /* Days */
2548aaca124Schristos 	seconds += (uint64_t)duration->parts[2] * 86400 * 7; /* Weeks */
2558aaca124Schristos 	/*
2568aaca124Schristos 	 * The below additions are not entirely correct
2578aaca124Schristos 	 * because days may vary per month and per year.
2588aaca124Schristos 	 */
2598aaca124Schristos 	seconds += (uint64_t)duration->parts[1] * 86400 * 31;  /* Months */
2608aaca124Schristos 	seconds += (uint64_t)duration->parts[0] * 86400 * 365; /* Years */
2618aaca124Schristos 
262*bcda20f6Schristos 	return seconds > UINT32_MAX ? UINT32_MAX : (uint32_t)seconds;
2638aaca124Schristos }
264