Coverage Report

Created: 2025-12-14 06:30

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