xref: /openbsd-src/usr.bin/dig/lib/dns/dns_time.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /* $Id: dns_time.c,v 1.6 2020/02/24 13:49:38 jsg Exp $ */
18 
19 /*! \file */
20 
21 #include <stdio.h>
22 #include <string.h>		/* Required for HP/UX (and others?) */
23 #include <time.h>
24 
25 #include <isc/region.h>
26 #include <isc/serial.h>
27 #include <isc/result.h>
28 
29 #include <dns/time.h>
30 
31 static const int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
32 
33 isc_result_t
34 dns_time64_totext(int64_t t, isc_buffer_t *target) {
35 	struct tm tm;
36 	char buf[sizeof("!!!!!!YYYY!!!!!!!!MM!!!!!!!!DD!!!!!!!!HH!!!!!!!!MM!!!!!!!!SS")];
37 	int secs;
38 	unsigned int l;
39 	isc_region_t region;
40 
41 /*
42  * Warning. Do NOT use arguments with side effects with these macros.
43  */
44 #define is_leap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
45 #define year_secs(y) ((is_leap(y) ? 366 : 365 ) * 86400)
46 #define month_secs(m,y) ((days[m] + ((m == 1 && is_leap(y)) ? 1 : 0 )) * 86400)
47 
48 	tm.tm_year = 70;
49 	while (t < 0) {
50 		if (tm.tm_year == 0)
51 			return (ISC_R_RANGE);
52 		tm.tm_year--;
53 		secs = year_secs(tm.tm_year + 1900);
54 		t += secs;
55 	}
56 	while ((secs = year_secs(tm.tm_year + 1900)) <= t) {
57 		t -= secs;
58 		tm.tm_year++;
59 		if (tm.tm_year + 1900 > 9999)
60 			return (ISC_R_RANGE);
61 	}
62 	tm.tm_mon = 0;
63 	while ((secs = month_secs(tm.tm_mon, tm.tm_year + 1900)) <= t) {
64 		t -= secs;
65 		tm.tm_mon++;
66 	}
67 	tm.tm_mday = 1;
68 	while (86400 <= t) {
69 		t -= 86400;
70 		tm.tm_mday++;
71 	}
72 	tm.tm_hour = 0;
73 	while (3600 <= t) {
74 		t -= 3600;
75 		tm.tm_hour++;
76 	}
77 	tm.tm_min = 0;
78 	while (60 <= t) {
79 		t -= 60;
80 		tm.tm_min++;
81 	}
82 	tm.tm_sec = (int)t;
83 				 /* yyyy  mm  dd  HH  MM  SS */
84 	snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
85 		 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
86 		 tm.tm_hour, tm.tm_min, tm.tm_sec);
87 
88 	isc_buffer_availableregion(target, &region);
89 	l = strlen(buf);
90 
91 	if (l > region.length)
92 		return (ISC_R_NOSPACE);
93 
94 	memmove(region.base, buf, l);
95 	isc_buffer_add(target, l);
96 	return (ISC_R_SUCCESS);
97 }
98 
99 int64_t
100 dns_time64_from32(uint32_t value) {
101 	time_t now;
102 	int64_t start;
103 	int64_t t;
104 
105 	/*
106 	 * Adjust the time to the closest epoch.  This should be changed
107 	 * to use a 64-bit counterpart to time() if one ever
108 	 * is defined, but even the current code is good until the year
109 	 * 2106.
110 	 */
111 	time(&now);
112 	start = (int64_t) now;
113 	if (isc_serial_gt(value, now))
114 		t = start + (value - now);
115 	else
116 		t = start - (now - value);
117 
118 	return (t);
119 }
120 
121 isc_result_t
122 dns_time32_totext(uint32_t value, isc_buffer_t *target) {
123 	return (dns_time64_totext(dns_time64_from32(value), target));
124 }
125