Coverage Report

Created: 2025-08-03 06:33

/src/bind9/lib/dns/ttl.c
Line
Count
Source (jump to first uncovered line)
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 <errno.h>
18
#include <inttypes.h>
19
#include <stdbool.h>
20
#include <stdio.h>
21
#include <stdlib.h>
22
23
#include <isc/ascii.h>
24
#include <isc/buffer.h>
25
#include <isc/parseint.h>
26
#include <isc/region.h>
27
#include <isc/result.h>
28
#include <isc/string.h>
29
#include <isc/util.h>
30
31
#include <dns/ttl.h>
32
33
#define RETERR(x)                        \
34
84.5k
  do {                             \
35
84.5k
    isc_result_t _r = (x);   \
36
84.5k
    if (_r != ISC_R_SUCCESS) \
37
84.5k
      return ((_r));   \
38
84.5k
  } while (0)
39
40
static isc_result_t
41
bind_ttl(isc_textregion_t *source, uint32_t *ttl);
42
43
/*
44
 * Helper for dns_ttl_totext().
45
 */
46
static isc_result_t
47
ttlfmt(unsigned int t, const char *s, bool verbose, bool space,
48
84.5k
       isc_buffer_t *target) {
49
84.5k
  char tmp[60];
50
84.5k
  unsigned int len;
51
84.5k
  isc_region_t region;
52
53
84.5k
  if (verbose) {
54
84.5k
    len = snprintf(tmp, sizeof(tmp), "%s%u %s%s", space ? " " : "",
55
84.5k
             t, s, t == 1 ? "" : "s");
56
84.5k
  } else {
57
0
    len = snprintf(tmp, sizeof(tmp), "%u%c", t, s[0]);
58
0
  }
59
60
84.5k
  INSIST(len + 1 <= sizeof(tmp));
61
84.5k
  isc_buffer_availableregion(target, &region);
62
84.5k
  if (len > region.length) {
63
0
    return ISC_R_NOSPACE;
64
0
  }
65
84.5k
  memmove(region.base, tmp, len);
66
84.5k
  isc_buffer_add(target, len);
67
68
84.5k
  return ISC_R_SUCCESS;
69
84.5k
}
70
71
/*
72
 * Derived from bind8 ns_format_ttl().
73
 */
74
isc_result_t
75
22.3k
dns_ttl_totext(uint32_t src, bool verbose, bool upcase, isc_buffer_t *target) {
76
22.3k
  unsigned int secs, mins, hours, days, weeks, x;
77
78
22.3k
  secs = src % 60;
79
22.3k
  src /= 60;
80
22.3k
  mins = src % 60;
81
22.3k
  src /= 60;
82
22.3k
  hours = src % 24;
83
22.3k
  src /= 24;
84
22.3k
  days = src % 7;
85
22.3k
  src /= 7;
86
22.3k
  weeks = src;
87
22.3k
  src = 0;
88
22.3k
  POST(src);
89
90
22.3k
  x = 0;
91
22.3k
  if (weeks != 0) {
92
14.1k
    RETERR(ttlfmt(weeks, "week", verbose, x > 0, target));
93
14.1k
    x++;
94
14.1k
  }
95
22.3k
  if (days != 0) {
96
14.6k
    RETERR(ttlfmt(days, "day", verbose, x > 0, target));
97
14.6k
    x++;
98
14.6k
  }
99
22.3k
  if (hours != 0) {
100
19.1k
    RETERR(ttlfmt(hours, "hour", verbose, x > 0, target));
101
19.1k
    x++;
102
19.1k
  }
103
22.3k
  if (mins != 0) {
104
19.6k
    RETERR(ttlfmt(mins, "minute", verbose, x > 0, target));
105
19.6k
    x++;
106
19.6k
  }
107
22.3k
  if (secs != 0 || (weeks == 0 && days == 0 && hours == 0 && mins == 0)) {
108
16.8k
    RETERR(ttlfmt(secs, "second", verbose, x > 0, target));
109
16.8k
    x++;
110
16.8k
  }
111
22.3k
  INSIST(x > 0);
112
  /*
113
   * If only a single unit letter is printed, print it
114
   * in upper case. (Why?  Because BIND 8 does that.
115
   * Presumably it has a reason.)
116
   */
117
22.3k
  if (x == 1 && upcase && !verbose) {
118
0
    isc_region_t region;
119
    /*
120
     * The unit letter is the last character in the
121
     * used region of the buffer.
122
     */
123
0
    isc_buffer_usedregion(target, &region);
124
0
    region.base[region.length - 1] =
125
0
      isc_ascii_toupper(region.base[region.length - 1]);
126
0
  }
127
22.3k
  return ISC_R_SUCCESS;
128
22.3k
}
129
130
isc_result_t
131
9.68k
dns_counter_fromtext(isc_textregion_t *source, uint32_t *ttl) {
132
9.68k
  return bind_ttl(source, ttl);
133
9.68k
}
134
135
isc_result_t
136
283k
dns_ttl_fromtext(isc_textregion_t *source, uint32_t *ttl) {
137
283k
  isc_result_t result;
138
139
283k
  result = bind_ttl(source, ttl);
140
283k
  if (result != ISC_R_SUCCESS && result != ISC_R_RANGE) {
141
247k
    result = DNS_R_BADTTL;
142
247k
  }
143
283k
  return result;
144
283k
}
145
146
static isc_result_t
147
293k
bind_ttl(isc_textregion_t *source, uint32_t *ttl) {
148
293k
  uint64_t tmp = 0ULL;
149
293k
  uint32_t n;
150
293k
  char *s;
151
293k
  char buf[64];
152
293k
  char nbuf[64]; /* Number buffer */
153
154
  /*
155
   * Copy the buffer as it may not be NULL terminated.
156
   * No legal counter / ttl is longer that 63 characters.
157
   */
158
293k
  if (source->length > sizeof(buf) - 1) {
159
393
    return DNS_R_SYNTAX;
160
393
  }
161
  /* Copy source->length bytes and NUL terminate. */
162
292k
  snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base);
163
292k
  s = buf;
164
165
293k
  do {
166
293k
    isc_result_t result;
167
168
293k
    char *np = nbuf;
169
402k
    while (*s != '\0' && isdigit((unsigned char)*s)) {
170
108k
      *np++ = *s++;
171
108k
    }
172
293k
    *np++ = '\0';
173
293k
    INSIST(np - nbuf <= (int)sizeof(nbuf));
174
293k
    result = isc_parse_uint32(&n, nbuf, 10);
175
293k
    if (result != ISC_R_SUCCESS) {
176
247k
      return DNS_R_SYNTAX;
177
247k
    }
178
46.6k
    switch (*s) {
179
359
    case 'w':
180
511
    case 'W':
181
511
      tmp += (uint64_t)n * 7 * 24 * 3600;
182
511
      s++;
183
511
      break;
184
415
    case 'd':
185
8.30k
    case 'D':
186
8.30k
      tmp += (uint64_t)n * 24 * 3600;
187
8.30k
      s++;
188
8.30k
      break;
189
128
    case 'h':
190
1.65k
    case 'H':
191
1.65k
      tmp += (uint64_t)n * 3600;
192
1.65k
      s++;
193
1.65k
      break;
194
370
    case 'm':
195
694
    case 'M':
196
694
      tmp += (uint64_t)n * 60;
197
694
      s++;
198
694
      break;
199
145
    case 's':
200
307
    case 'S':
201
307
      tmp += (uint64_t)n;
202
307
      s++;
203
307
      break;
204
35.0k
    case '\0':
205
      /* Plain number? */
206
35.0k
      if (tmp != 0ULL) {
207
82
        return DNS_R_SYNTAX;
208
82
      }
209
34.9k
      tmp = n;
210
34.9k
      break;
211
134
    default:
212
134
      return DNS_R_SYNTAX;
213
46.6k
    }
214
46.6k
  } while (*s != '\0');
215
216
45.2k
  if (tmp > 0xffffffffULL) {
217
19
    return ISC_R_RANGE;
218
19
  }
219
220
45.2k
  *ttl = (uint32_t)(tmp & 0xffffffffUL);
221
45.2k
  return ISC_R_SUCCESS;
222
45.2k
}