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