Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: time.c,v 1.1 2024/02/18 20:57:34 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 <inttypes.h>
     20 #include <stdio.h>
     21 #include <time.h>
     22 
     23 #include <isc/print.h>
     24 #include <isc/region.h>
     25 #include <isc/serial.h>
     26 #include <isc/stdtime.h>
     27 #include <isc/string.h> /* Required for HP/UX (and others?) */
     28 #include <isc/util.h>
     29 
     30 #include <dns/result.h>
     31 #include <dns/time.h>
     32 
     33 static const int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
     34 
     35 isc_result_t
     36 dns_time64_totext(int64_t t, isc_buffer_t *target) {
     37 	struct tm tm;
     38 	char buf[sizeof("!!!!!!YYYY!!!!!!!!MM!!!!!!!!DD!!!!!!!!HH!!!!!!!!MM!!!!"
     39 			"!!!!SS")];
     40 	int secs;
     41 	unsigned int l;
     42 	isc_region_t region;
     43 
     44 /*
     45  * Warning. Do NOT use arguments with side effects with these macros.
     46  */
     47 #define is_leap(y)	 ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
     48 #define year_secs(y)	 ((is_leap(y) ? 366 : 365) * 86400)
     49 #define month_secs(m, y) ((days[m] + ((m == 1 && is_leap(y)) ? 1 : 0)) * 86400)
     50 
     51 	tm.tm_year = 70;
     52 	while (t < 0) {
     53 		if (tm.tm_year == 0) {
     54 			return (ISC_R_RANGE);
     55 		}
     56 		tm.tm_year--;
     57 		secs = year_secs(tm.tm_year + 1900);
     58 		t += secs;
     59 	}
     60 	while ((secs = year_secs(tm.tm_year + 1900)) <= t) {
     61 		t -= secs;
     62 		tm.tm_year++;
     63 		if (tm.tm_year + 1900 > 9999) {
     64 			return (ISC_R_RANGE);
     65 		}
     66 	}
     67 	tm.tm_mon = 0;
     68 	while ((secs = month_secs(tm.tm_mon, tm.tm_year + 1900)) <= t) {
     69 		t -= secs;
     70 		tm.tm_mon++;
     71 	}
     72 	tm.tm_mday = 1;
     73 	while (86400 <= t) {
     74 		t -= 86400;
     75 		tm.tm_mday++;
     76 	}
     77 	tm.tm_hour = 0;
     78 	while (3600 <= t) {
     79 		t -= 3600;
     80 		tm.tm_hour++;
     81 	}
     82 	tm.tm_min = 0;
     83 	while (60 <= t) {
     84 		t -= 60;
     85 		tm.tm_min++;
     86 	}
     87 	tm.tm_sec = (int)t;
     88 	/* yyyy  mm  dd  HH  MM  SS */
     89 	snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
     90 		 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
     91 		 tm.tm_min, tm.tm_sec);
     92 
     93 	isc_buffer_availableregion(target, &region);
     94 	l = strlen(buf);
     95 
     96 	if (l > region.length) {
     97 		return (ISC_R_NOSPACE);
     98 	}
     99 
    100 	memmove(region.base, buf, l);
    101 	isc_buffer_add(target, l);
    102 	return (ISC_R_SUCCESS);
    103 }
    104 
    105 int64_t
    106 dns_time64_from32(uint32_t value) {
    107 	isc_stdtime_t now;
    108 	int64_t start;
    109 	int64_t t;
    110 
    111 	/*
    112 	 * Adjust the time to the closest epoch.  This should be changed
    113 	 * to use a 64-bit counterpart to isc_stdtime_get() if one ever
    114 	 * is defined, but even the current code is good until the year
    115 	 * 2106.
    116 	 */
    117 	isc_stdtime_get(&now);
    118 	start = (int64_t)now;
    119 	if (isc_serial_gt(value, now)) {
    120 		t = start + (value - now);
    121 	} else {
    122 		t = start - (now - value);
    123 	}
    124 
    125 	return (t);
    126 }
    127 
    128 isc_result_t
    129 dns_time32_totext(uint32_t value, isc_buffer_t *target) {
    130 	return (dns_time64_totext(dns_time64_from32(value), target));
    131 }
    132 
    133 isc_result_t
    134 dns_time64_fromtext(const char *source, int64_t *target) {
    135 	int year, month, day, hour, minute, second;
    136 	int64_t value;
    137 	int secs;
    138 	int i;
    139 
    140 #define RANGE(min, max, value)                      \
    141 	do {                                        \
    142 		if (value < (min) || value > (max)) \
    143 			return ((ISC_R_RANGE));     \
    144 	} while (0)
    145 
    146 	if (strlen(source) != 14U) {
    147 		return (DNS_R_SYNTAX);
    148 	}
    149 	/*
    150 	 * Confirm the source only consists digits.  sscanf() allows some
    151 	 * minor exceptions.
    152 	 */
    153 	for (i = 0; i < 14; i++) {
    154 		if (!isdigit((unsigned char)source[i])) {
    155 			return (DNS_R_SYNTAX);
    156 		}
    157 	}
    158 	if (sscanf(source, "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour,
    159 		   &minute, &second) != 6)
    160 	{
    161 		return (DNS_R_SYNTAX);
    162 	}
    163 
    164 	RANGE(0, 9999, year);
    165 	RANGE(1, 12, month);
    166 	RANGE(1, days[month - 1] + ((month == 2 && is_leap(year)) ? 1 : 0),
    167 	      day);
    168 #ifdef __COVERITY__
    169 	/*
    170 	 * Use a simplified range to silence Coverity warning (in
    171 	 * arithmetic with day below).
    172 	 */
    173 	RANGE(1, 31, day);
    174 #endif /* __COVERITY__ */
    175 
    176 	RANGE(0, 23, hour);
    177 	RANGE(0, 59, minute);
    178 	RANGE(0, 60, second); /* 60 == leap second. */
    179 
    180 	/*
    181 	 * Calculate seconds from epoch.
    182 	 * Note: this uses a idealized calendar.
    183 	 */
    184 	value = second + (60 * minute) + (3600 * hour) + ((day - 1) * 86400);
    185 	for (i = 0; i < (month - 1); i++) {
    186 		value += days[i] * 86400;
    187 	}
    188 	if (is_leap(year) && month > 2) {
    189 		value += 86400;
    190 	}
    191 	if (year < 1970) {
    192 		for (i = 1969; i >= year; i--) {
    193 			secs = (is_leap(i) ? 366 : 365) * 86400;
    194 			value -= secs;
    195 		}
    196 	} else {
    197 		for (i = 1970; i < year; i++) {
    198 			secs = (is_leap(i) ? 366 : 365) * 86400;
    199 			value += secs;
    200 		}
    201 	}
    202 
    203 	*target = value;
    204 	return (ISC_R_SUCCESS);
    205 }
    206 
    207 isc_result_t
    208 dns_time32_fromtext(const char *source, uint32_t *target) {
    209 	int64_t value64;
    210 	isc_result_t result;
    211 	result = dns_time64_fromtext(source, &value64);
    212 	if (result != ISC_R_SUCCESS) {
    213 		return (result);
    214 	}
    215 	*target = (uint32_t)value64;
    216 
    217 	return (ISC_R_SUCCESS);
    218 }
    219