Coverage Report

Created: 2026-01-17 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/rdataset.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 <inttypes.h>
17
#include <stdbool.h>
18
#include <stdlib.h>
19
20
#include <isc/buffer.h>
21
#include <isc/mem.h>
22
#include <isc/random.h>
23
#include <isc/result.h>
24
#include <isc/serial.h>
25
#include <isc/util.h>
26
27
#include <dns/compress.h>
28
#include <dns/fixedname.h>
29
#include <dns/name.h>
30
#include <dns/ncache.h>
31
#include <dns/rdata.h>
32
#include <dns/rdataset.h>
33
#include <dns/time.h>
34
#include <dns/types.h>
35
36
#define MAX_SHUFFLE 100
37
thread_local dns_rdata_t dns__rdataset_rdatas[MAX_SHUFFLE];
38
39
static const char *trustnames[] = {
40
  "none",     "pending-additional",
41
  "pending-answer", "additional",
42
  "glue",     "answer",
43
  "authauthority",  "authanswer",
44
  "secure",   "local" /* aka ultimate */
45
};
46
47
const char *
48
0
dns_trust_totext(dns_trust_t trust) {
49
0
  if (trust >= sizeof(trustnames) / sizeof(*trustnames)) {
50
0
    return "bad";
51
0
  }
52
0
  return trustnames[trust];
53
0
}
54
55
void
56
0
dns_rdataset_init(dns_rdataset_t *rdataset) {
57
  /*
58
   * Make 'rdataset' a valid, disassociated rdataset.
59
   */
60
61
0
  REQUIRE(rdataset != NULL);
62
63
0
  *rdataset = (dns_rdataset_t){
64
0
    .magic = DNS_RDATASET_MAGIC,
65
0
    .link = ISC_LINK_INITIALIZER,
66
0
  };
67
0
}
68
69
void
70
0
dns_rdataset_invalidate(dns_rdataset_t *rdataset) {
71
  /*
72
   * Invalidate 'rdataset'.
73
   */
74
75
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
76
0
  REQUIRE(rdataset->methods == NULL);
77
78
0
  *rdataset = (dns_rdataset_t){
79
0
    .magic = 0,
80
0
    .link = ISC_LINK_INITIALIZER,
81
0
  };
82
0
}
83
84
void
85
0
dns__rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) {
86
  /*
87
   * Disassociate 'rdataset' from its rdata, allowing it to be reused.
88
   */
89
90
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
91
0
  REQUIRE(rdataset->methods != NULL);
92
93
0
  if (rdataset->methods->disassociate != NULL) {
94
0
    (rdataset->methods->disassociate)(rdataset DNS__DB_FLARG_PASS);
95
0
  }
96
0
  *rdataset = (dns_rdataset_t){
97
0
    .magic = DNS_RDATASET_MAGIC,
98
0
    .link = ISC_LINK_INITIALIZER,
99
0
  };
100
0
}
101
102
bool
103
0
dns_rdataset_isassociated(dns_rdataset_t *rdataset) {
104
  /*
105
   * Is 'rdataset' associated?
106
   */
107
108
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
109
110
0
  if (rdataset->methods != NULL) {
111
0
    return true;
112
0
  }
113
114
0
  return false;
115
0
}
116
117
static isc_result_t
118
0
question_cursor(dns_rdataset_t *rdataset ISC_ATTR_UNUSED) {
119
0
  return ISC_R_NOMORE;
120
0
}
121
122
static void
123
0
question_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) {
124
0
  *target = *source;
125
0
}
126
127
static dns_rdatasetmethods_t question_methods = {
128
  .first = question_cursor,
129
  .next = question_cursor,
130
  .clone = question_clone,
131
};
132
133
void
134
dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass,
135
0
        dns_rdatatype_t type) {
136
  /*
137
   * Make 'rdataset' a valid, associated, question rdataset, with a
138
   * question class of 'rdclass' and type 'type'.
139
   */
140
141
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
142
0
  REQUIRE(rdataset->methods == NULL);
143
144
0
  rdataset->methods = &question_methods;
145
0
  rdataset->rdclass = rdclass;
146
0
  rdataset->type = type;
147
0
  rdataset->attributes.question = true;
148
0
}
149
150
unsigned int
151
0
dns_rdataset_count(dns_rdataset_t *rdataset) {
152
  /*
153
   * Return the number of records in 'rdataset'.
154
   */
155
156
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
157
0
  REQUIRE(rdataset->methods != NULL);
158
0
  REQUIRE(rdataset->methods->count != NULL);
159
160
0
  return (rdataset->methods->count)(rdataset);
161
0
}
162
163
void
164
dns__rdataset_clone(dns_rdataset_t *source,
165
0
        dns_rdataset_t *target DNS__DB_FLARG) {
166
  /*
167
   * Make 'target' refer to the same rdataset as 'source'.
168
   */
169
170
0
  REQUIRE(DNS_RDATASET_VALID(source));
171
0
  REQUIRE(source->methods != NULL);
172
0
  REQUIRE(DNS_RDATASET_VALID(target));
173
0
  REQUIRE(target->methods == NULL);
174
175
0
  (source->methods->clone)(source, target DNS__DB_FLARG_PASS);
176
0
}
177
178
isc_result_t
179
0
dns_rdataset_first(dns_rdataset_t *rdataset) {
180
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
181
0
  REQUIRE(rdataset->methods != NULL);
182
0
  REQUIRE(rdataset->methods->first != NULL);
183
184
0
  isc_result_t result = rdataset->methods->first(rdataset);
185
0
  ENSURE(result == ISC_R_SUCCESS || result == ISC_R_NOMORE);
186
0
  return result;
187
0
}
188
189
isc_result_t
190
0
dns_rdataset_next(dns_rdataset_t *rdataset) {
191
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
192
0
  REQUIRE(rdataset->methods != NULL);
193
0
  REQUIRE(rdataset->methods->next != NULL);
194
195
0
  isc_result_t result = rdataset->methods->next(rdataset);
196
0
  ENSURE(result == ISC_R_SUCCESS || result == ISC_R_NOMORE);
197
0
  return result;
198
0
}
199
200
void
201
0
dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
202
  /*
203
   * Make 'rdata' refer to the current rdata.
204
   */
205
206
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
207
0
  REQUIRE(rdataset->methods != NULL);
208
0
  REQUIRE(rdataset->methods->current != NULL);
209
210
0
  (rdataset->methods->current)(rdataset, rdata);
211
0
}
212
213
0
#define WANT_CYCLIC(r) (((r)->attributes.order == dns_order_cyclic))
214
215
static isc_result_t
216
towire_addtypeclass(dns_rdataset_t *rdataset, const dns_name_t *name,
217
        dns_compress_t *cctx, isc_buffer_t *target,
218
0
        isc_buffer_t *rrbuffer, size_t extralen) {
219
0
  isc_region_t r;
220
0
  isc_result_t result;
221
0
  size_t headlen;
222
223
0
  *rrbuffer = *target;
224
0
  dns_compress_setpermitted(cctx, true);
225
0
  result = dns_name_towire(name, cctx, target);
226
0
  if (result != ISC_R_SUCCESS) {
227
0
    return result;
228
0
  }
229
0
  headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t) + extralen;
230
0
  isc_buffer_availableregion(target, &r);
231
0
  if (r.length < headlen) {
232
0
    return ISC_R_NOSPACE;
233
0
  }
234
0
  isc_buffer_putuint16(target, rdataset->type);
235
0
  isc_buffer_putuint16(target, rdataset->rdclass);
236
0
  return ISC_R_SUCCESS;
237
0
}
238
239
static void
240
towire_addttl(dns_rdataset_t *rdataset, isc_buffer_t *target,
241
0
        isc_buffer_t *rdlen) {
242
0
  isc_buffer_putuint32(target, rdataset->ttl);
243
244
  /* Save space for rdlen. */
245
0
  *rdlen = *target;
246
0
  isc_buffer_add(target, 2);
247
0
}
248
249
static isc_result_t
250
towire_addrdata(dns_rdata_t *rdata, dns_compress_t *cctx, isc_buffer_t *target,
251
0
    isc_buffer_t *rdlen) {
252
0
  isc_result_t result = dns_rdata_towire(rdata, cctx, target);
253
0
  if (result != ISC_R_SUCCESS) {
254
0
    return result;
255
0
  }
256
0
  INSIST((target->used >= rdlen->used + 2) &&
257
0
         (target->used - rdlen->used - 2 < 65536));
258
0
  isc_buffer_putuint16(rdlen, (uint16_t)(target->used - rdlen->used - 2));
259
0
  return ISC_R_SUCCESS;
260
0
}
261
262
static isc_result_t
263
towire_question(dns_rdataset_t *rdataset, const dns_name_t *name,
264
    dns_compress_t *cctx, isc_buffer_t *target,
265
0
    isc_buffer_t *rrbuffer, unsigned int *countp) {
266
0
  isc_result_t result;
267
268
0
  result = dns_rdataset_first(rdataset);
269
0
  INSIST(result == ISC_R_NOMORE);
270
271
0
  result = towire_addtypeclass(rdataset, name, cctx, target, rrbuffer, 0);
272
0
  if (result != ISC_R_SUCCESS) {
273
0
    return ISC_R_SUCCESS;
274
0
  }
275
276
0
  *countp += 1;
277
278
0
  return ISC_R_SUCCESS;
279
0
}
280
281
static isc_result_t
282
towire_answer(dns_rdataset_t *rdataset, const dns_name_t *name,
283
        dns_compress_t *cctx, isc_buffer_t *target,
284
0
        isc_buffer_t *rrbuffer, uint16_t id, unsigned int *countp) {
285
0
  isc_result_t result;
286
0
  size_t start = 0, count = 0, added = 0;
287
0
  isc_buffer_t rdlen;
288
0
  dns_rdata_t *rdatas = dns__rdataset_rdatas;
289
290
0
  count = dns_rdataset_count(rdataset);
291
0
  result = dns_rdataset_first(rdataset);
292
0
  if (result == ISC_R_NOMORE) {
293
0
    return ISC_R_SUCCESS;
294
0
  } else if (result != ISC_R_SUCCESS) {
295
0
    return result;
296
0
  }
297
298
0
  if (WANT_CYCLIC(rdataset) && rdataset->type != dns_rdatatype_rrsig) {
299
0
    start = id % count;
300
301
    /* Do we need larger buffer? */
302
0
    if (start > ARRAY_SIZE(dns__rdataset_rdatas)) {
303
0
      rdatas = isc_mem_cget(cctx->mctx, start,
304
0
                sizeof(rdatas[0]));
305
0
    }
306
0
  }
307
308
  /*
309
   * Save the rdata up until the start.  If we are not
310
   * doing cyclic, the start == 0, so this is no-op.
311
   */
312
0
  for (size_t i = 0; i < start; i++) {
313
0
    dns_rdata_init(&rdatas[i]);
314
0
    dns_rdataset_current(rdataset, &rdatas[i]);
315
316
0
    result = dns_rdataset_next(rdataset);
317
0
    if (result == ISC_R_NOMORE) {
318
0
      result = ISC_R_SUCCESS;
319
0
      break;
320
0
    } else if (result != ISC_R_SUCCESS) {
321
0
      goto cleanup;
322
0
    }
323
0
  }
324
325
0
  for (size_t i = start; i < count; i++) {
326
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
327
328
0
    CHECK(towire_addtypeclass(rdataset, name, cctx, target,
329
0
            rrbuffer, sizeof(dns_ttl_t) + 2));
330
0
    towire_addttl(rdataset, target, &rdlen);
331
332
0
    dns_rdataset_current(rdataset, &rdata);
333
0
    CHECK(towire_addrdata(&rdata, cctx, target, &rdlen));
334
0
    added++;
335
336
0
    result = dns_rdataset_next(rdataset);
337
0
    if (result == ISC_R_NOMORE) {
338
0
      result = ISC_R_SUCCESS;
339
0
      break;
340
0
    } else if (result != ISC_R_SUCCESS) {
341
0
      goto cleanup;
342
0
    }
343
0
  }
344
345
0
  for (size_t i = 0; i < start; i++) {
346
0
    CHECK(towire_addtypeclass(rdataset, name, cctx, target,
347
0
            rrbuffer, sizeof(dns_ttl_t) + 2));
348
0
    towire_addttl(rdataset, target, &rdlen);
349
350
0
    CHECK(towire_addrdata(&rdatas[i], cctx, target, &rdlen));
351
0
    added++;
352
0
  }
353
354
0
  INSIST(added == count);
355
356
0
cleanup:
357
0
  *countp += added;
358
0
  if (rdatas != dns__rdataset_rdatas) {
359
0
    isc_mem_cput(cctx->mctx, rdatas, start, sizeof(rdatas[0]));
360
0
  }
361
362
0
  return result;
363
0
}
364
365
isc_result_t
366
dns_rdataset_towire(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
367
        uint16_t id, dns_compress_t *cctx, isc_buffer_t *target,
368
0
        bool partial, unsigned int options, unsigned int *countp) {
369
0
  isc_result_t result;
370
0
  isc_buffer_t savedbuffer = *target;
371
0
  isc_buffer_t rrbuffer;
372
0
  dns_fixedname_t fixed;
373
0
  dns_name_t *name = NULL;
374
375
  /*
376
   * Convert 'rdataset' to wire format, compressing names as specified
377
   * in cctx, and storing the result in 'target'.
378
   */
379
380
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
381
0
  REQUIRE(rdataset->methods != NULL);
382
0
  REQUIRE(countp != NULL);
383
0
  REQUIRE(cctx != NULL && cctx->mctx != NULL);
384
385
0
  if (rdataset->attributes.negative) {
386
    /*
387
     * This is a negative caching rdataset.
388
     */
389
0
    unsigned int ncache_opts = 0;
390
0
    if ((options & DNS_RDATASETTOWIRE_OMITDNSSEC) != 0) {
391
0
      ncache_opts |= DNS_NCACHETOWIRE_OMITDNSSEC;
392
0
    }
393
0
    return dns_ncache_towire(rdataset, cctx, target, ncache_opts,
394
0
           countp);
395
0
  }
396
397
0
  name = dns_fixedname_initname(&fixed);
398
0
  dns_name_copy(owner_name, name);
399
0
  dns_rdataset_getownercase(rdataset, name);
400
0
  dns_compress_setmultiuse(cctx, true);
401
402
0
  name->attributes.nocompress |= owner_name->attributes.nocompress;
403
404
0
  if (rdataset->attributes.question) {
405
0
    result = towire_question(rdataset, name, cctx, target,
406
0
           &rrbuffer, countp);
407
0
    if (result != ISC_R_SUCCESS) {
408
0
      goto rollback;
409
0
    }
410
0
  } else {
411
0
    result = towire_answer(rdataset, name, cctx, target, &rrbuffer,
412
0
               id, countp);
413
0
    if (result != ISC_R_SUCCESS) {
414
0
      goto rollback;
415
0
    }
416
0
  }
417
418
0
  return ISC_R_SUCCESS;
419
420
0
rollback:
421
0
  if (partial && result == ISC_R_NOSPACE) {
422
0
    dns_compress_rollback(cctx, rrbuffer.used);
423
0
    *target = rrbuffer;
424
0
    return result;
425
0
  }
426
0
  dns_compress_rollback(cctx, savedbuffer.used);
427
0
  *countp = 0;
428
0
  *target = savedbuffer;
429
430
0
  return result;
431
0
}
432
433
isc_result_t
434
dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
435
          const dns_name_t *owner_name,
436
          dns_additionaldatafunc_t add, void *arg,
437
0
          size_t limit) {
438
  /*
439
   * For each rdata in rdataset, call 'add' for each name and type in the
440
   * rdata which is subject to additional section processing.
441
   */
442
443
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
444
0
  REQUIRE(!rdataset->attributes.question);
445
446
0
  if (limit != 0 && dns_rdataset_count(rdataset) > limit) {
447
0
    return DNS_R_TOOMANYRECORDS;
448
0
  }
449
450
0
  DNS_RDATASET_FOREACH(rdataset) {
451
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
452
0
    dns_rdataset_current(rdataset, &rdata);
453
0
    RETERR(dns_rdata_additionaldata(&rdata, owner_name, add, arg));
454
0
  }
455
456
0
  return ISC_R_SUCCESS;
457
0
}
458
459
isc_result_t
460
0
dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) {
461
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
462
0
  REQUIRE(rdataset->methods != NULL);
463
0
  if (rdataset->methods->addnoqname == NULL) {
464
0
    return ISC_R_NOTIMPLEMENTED;
465
0
  }
466
0
  return (rdataset->methods->addnoqname)(rdataset, name);
467
0
}
468
469
isc_result_t
470
dns__rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
471
       dns_rdataset_t *neg,
472
0
       dns_rdataset_t *negsig DNS__DB_FLARG) {
473
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
474
0
  REQUIRE(rdataset->methods != NULL);
475
476
0
  if (rdataset->methods->getnoqname == NULL) {
477
0
    return ISC_R_NOTIMPLEMENTED;
478
0
  }
479
0
  return (rdataset->methods->getnoqname)(rdataset, name, neg,
480
0
                 negsig DNS__DB_FLARG_PASS);
481
0
}
482
483
isc_result_t
484
0
dns_rdataset_addclosest(dns_rdataset_t *rdataset, dns_name_t *name) {
485
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
486
0
  REQUIRE(rdataset->methods != NULL);
487
0
  if (rdataset->methods->addclosest == NULL) {
488
0
    return ISC_R_NOTIMPLEMENTED;
489
0
  }
490
0
  return (rdataset->methods->addclosest)(rdataset, name);
491
0
}
492
493
isc_result_t
494
dns__rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
495
       dns_rdataset_t *neg,
496
0
       dns_rdataset_t *negsig DNS__DB_FLARG) {
497
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
498
0
  REQUIRE(rdataset->methods != NULL);
499
500
0
  if (rdataset->methods->getclosest == NULL) {
501
0
    return ISC_R_NOTIMPLEMENTED;
502
0
  }
503
0
  return (rdataset->methods->getclosest)(rdataset, name, neg,
504
0
                 negsig DNS__DB_FLARG_PASS);
505
0
}
506
507
void
508
0
dns_rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
509
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
510
0
  REQUIRE(rdataset->methods != NULL);
511
512
0
  if (rdataset->methods->settrust != NULL) {
513
0
    (rdataset->methods->settrust)(rdataset, trust);
514
0
  } else {
515
0
    rdataset->trust = trust;
516
0
  }
517
0
}
518
519
void
520
0
dns__rdataset_expire(dns_rdataset_t *rdataset DNS__DB_FLARG) {
521
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
522
0
  REQUIRE(rdataset->methods != NULL);
523
524
0
  if (rdataset->methods->expire != NULL) {
525
0
    (rdataset->methods->expire)(rdataset DNS__DB_FLARG_PASS);
526
0
  }
527
0
}
528
529
void
530
0
dns_rdataset_clearprefetch(dns_rdataset_t *rdataset) {
531
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
532
0
  REQUIRE(rdataset->methods != NULL);
533
534
0
  if (rdataset->methods->clearprefetch != NULL) {
535
0
    (rdataset->methods->clearprefetch)(rdataset);
536
0
  }
537
0
}
538
539
void
540
0
dns_rdataset_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) {
541
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
542
0
  REQUIRE(rdataset->methods != NULL);
543
544
0
  if (rdataset->methods->setownercase != NULL &&
545
0
      !rdataset->attributes.keepcase)
546
0
  {
547
0
    (rdataset->methods->setownercase)(rdataset, name);
548
0
  }
549
0
}
550
551
void
552
0
dns_rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
553
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
554
0
  REQUIRE(rdataset->methods != NULL);
555
556
0
  if (rdataset->methods->getownercase != NULL &&
557
0
      !rdataset->attributes.keepcase)
558
0
  {
559
0
    (rdataset->methods->getownercase)(rdataset, name);
560
0
  }
561
0
}
562
563
void
564
dns_rdataset_trimttl(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
565
         dns_rdata_rrsig_t *rrsig, isc_stdtime_t now,
566
0
         bool acceptexpired) {
567
0
  uint32_t ttl = 0;
568
569
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
570
0
  REQUIRE(DNS_RDATASET_VALID(sigrdataset));
571
0
  REQUIRE(rrsig != NULL);
572
573
  /*
574
   * If we accept expired RRsets keep them for no more than 120 seconds.
575
   */
576
0
  if (acceptexpired &&
577
0
      (isc_serial_le(rrsig->timeexpire, (now + 120) & 0xffffffff) ||
578
0
       isc_serial_le(rrsig->timeexpire, now)))
579
0
  {
580
0
    ttl = 120;
581
0
  } else if (isc_serial_ge(rrsig->timeexpire, now)) {
582
0
    ttl = rrsig->timeexpire - now;
583
0
  }
584
585
0
  ttl = ISC_MIN(ISC_MIN(rdataset->ttl, sigrdataset->ttl),
586
0
          ISC_MIN(rrsig->originalttl, ttl));
587
0
  rdataset->ttl = ttl;
588
0
  sigrdataset->ttl = ttl;
589
0
}
590
591
isc_stdtime_t
592
0
dns_rdataset_minresign(dns_rdataset_t *rdataset) {
593
0
  dns_rdata_t rdata = DNS_RDATA_INIT;
594
0
  dns_rdata_rrsig_t sig;
595
0
  int64_t when;
596
0
  isc_result_t result;
597
598
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
599
600
0
  result = dns_rdataset_first(rdataset);
601
0
  INSIST(result == ISC_R_SUCCESS);
602
0
  dns_rdataset_current(rdataset, &rdata);
603
0
  (void)dns_rdata_tostruct(&rdata, &sig, NULL);
604
0
  if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
605
0
    when = 0;
606
0
  } else {
607
0
    when = dns_time64_from32(sig.timeexpire);
608
0
  }
609
0
  dns_rdata_reset(&rdata);
610
611
0
  result = dns_rdataset_next(rdataset);
612
0
  while (result == ISC_R_SUCCESS) {
613
0
    dns_rdataset_current(rdataset, &rdata);
614
0
    (void)dns_rdata_tostruct(&rdata, &sig, NULL);
615
0
    if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
616
0
      goto next_rr;
617
0
    }
618
0
    if (when == 0 || dns_time64_from32(sig.timeexpire) < when) {
619
0
      when = dns_time64_from32(sig.timeexpire);
620
0
    }
621
0
  next_rr:
622
0
    dns_rdata_reset(&rdata);
623
0
    result = dns_rdataset_next(rdataset);
624
0
  }
625
0
  INSIST(result == ISC_R_NOMORE);
626
0
  return (isc_stdtime_t)when;
627
0
}