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, ®ion); 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, ®ion); 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