Coverage Report

Created: 2025-11-11 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/diff.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 <stddef.h>
19
#include <stdlib.h>
20
21
#include <isc/buffer.h>
22
#include <isc/file.h>
23
#include <isc/log.h>
24
#include <isc/mem.h>
25
#include <isc/result.h>
26
#include <isc/string.h>
27
#include <isc/util.h>
28
29
#include <dns/callbacks.h>
30
#include <dns/db.h>
31
#include <dns/diff.h>
32
#include <dns/rdataclass.h>
33
#include <dns/rdatalist.h>
34
#include <dns/rdataset.h>
35
#include <dns/rdatastruct.h>
36
#include <dns/rdatatype.h>
37
#include <dns/time.h>
38
39
#define CHECK(op)                            \
40
0
  do {                                 \
41
0
    result = (op);               \
42
0
    if (result != ISC_R_SUCCESS) \
43
0
      goto failure;        \
44
0
  } while (0)
45
46
static dns_rdatatype_t
47
0
rdata_covers(dns_rdata_t *rdata) {
48
0
  return rdata->type == dns_rdatatype_rrsig ? dns_rdata_covers(rdata) : 0;
49
0
}
50
51
void
52
dns_difftuple_create(isc_mem_t *mctx, dns_diffop_t op, const dns_name_t *name,
53
0
         dns_ttl_t ttl, dns_rdata_t *rdata, dns_difftuple_t **tp) {
54
0
  dns_difftuple_t *t;
55
0
  unsigned int size;
56
0
  unsigned char *datap;
57
58
0
  REQUIRE(tp != NULL && *tp == NULL);
59
60
  /*
61
   * Create a new tuple.  The variable-size wire-format name data and
62
   * rdata immediately follow the dns_difftuple_t structure
63
   * in memory.
64
   */
65
0
  size = sizeof(*t) + name->length + rdata->length;
66
0
  t = isc_mem_allocate(mctx, size);
67
0
  t->mctx = NULL;
68
0
  isc_mem_attach(mctx, &t->mctx);
69
0
  t->op = op;
70
71
0
  datap = (unsigned char *)(t + 1);
72
73
0
  memmove(datap, name->ndata, name->length);
74
0
  dns_name_init(&t->name);
75
0
  dns_name_clone(name, &t->name);
76
0
  t->name.ndata = datap;
77
0
  datap += name->length;
78
79
0
  t->ttl = ttl;
80
81
0
  dns_rdata_init(&t->rdata);
82
0
  dns_rdata_clone(rdata, &t->rdata);
83
0
  if (rdata->data != NULL) {
84
0
    memmove(datap, rdata->data, rdata->length);
85
0
    t->rdata.data = datap;
86
0
    datap += rdata->length;
87
0
  } else {
88
0
    t->rdata.data = NULL;
89
0
    INSIST(rdata->length == 0);
90
0
  }
91
92
0
  ISC_LINK_INIT(&t->rdata, link);
93
0
  ISC_LINK_INIT(t, link);
94
0
  t->magic = DNS_DIFFTUPLE_MAGIC;
95
96
0
  INSIST(datap == (unsigned char *)t + size);
97
98
0
  *tp = t;
99
0
}
100
101
void
102
0
dns_difftuple_free(dns_difftuple_t **tp) {
103
0
  dns_difftuple_t *t = *tp;
104
0
  *tp = NULL;
105
0
  isc_mem_t *mctx;
106
107
0
  REQUIRE(DNS_DIFFTUPLE_VALID(t));
108
109
0
  dns_name_invalidate(&t->name);
110
0
  t->magic = 0;
111
0
  mctx = t->mctx;
112
0
  isc_mem_free(mctx, t);
113
0
  isc_mem_detach(&mctx);
114
0
}
115
116
void
117
0
dns_difftuple_copy(dns_difftuple_t *orig, dns_difftuple_t **copyp) {
118
0
  dns_difftuple_create(orig->mctx, orig->op, &orig->name, orig->ttl,
119
0
           &orig->rdata, copyp);
120
0
}
121
122
void
123
0
dns_diff_init(isc_mem_t *mctx, dns_diff_t *diff) {
124
0
  diff->mctx = mctx;
125
0
  ISC_LIST_INIT(diff->tuples);
126
0
  diff->magic = DNS_DIFF_MAGIC;
127
0
  diff->size = 0;
128
0
}
129
130
void
131
0
dns_diff_clear(dns_diff_t *diff) {
132
0
  REQUIRE(DNS_DIFF_VALID(diff));
133
0
  ISC_LIST_FOREACH(diff->tuples, t, link) {
134
0
    ISC_LIST_UNLINK(diff->tuples, t, link);
135
0
    dns_difftuple_free(&t);
136
0
  }
137
0
  diff->size = 0;
138
0
  ENSURE(ISC_LIST_EMPTY(diff->tuples));
139
0
}
140
141
void
142
0
dns_diff_append(dns_diff_t *diff, dns_difftuple_t **tuplep) {
143
0
  REQUIRE(DNS_DIFF_VALID(diff));
144
0
  ISC_LIST_APPEND(diff->tuples, *tuplep, link);
145
0
  diff->size += 1;
146
0
  *tuplep = NULL;
147
0
}
148
149
bool
150
0
dns_diff_is_boundary(const dns_diff_t *diff, dns_name_t *new_name) {
151
0
  REQUIRE(DNS_DIFF_VALID(diff));
152
0
  REQUIRE(DNS_NAME_VALID(new_name));
153
154
0
  if (ISC_LIST_EMPTY(diff->tuples)) {
155
0
    return false;
156
0
  }
157
158
0
  dns_difftuple_t *tail = ISC_LIST_TAIL(diff->tuples);
159
0
  return !dns_name_caseequal(&tail->name, new_name);
160
0
}
161
162
size_t
163
0
dns_diff_size(const dns_diff_t *diff) {
164
0
  REQUIRE(DNS_DIFF_VALID(diff));
165
0
  return diff->size;
166
0
}
167
168
/* XXX this is O(N) */
169
170
void
171
0
dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuplep) {
172
0
  REQUIRE(DNS_DIFF_VALID(diff));
173
0
  REQUIRE(DNS_DIFFTUPLE_VALID(*tuplep));
174
175
  /*
176
   * Look for an existing tuple with the same owner name,
177
   * rdata, and TTL.   If we are doing an addition and find a
178
   * deletion or vice versa, remove both the old and the
179
   * new tuple since they cancel each other out (assuming
180
   * that we never delete nonexistent data or add existing
181
   * data).
182
   *
183
   * If we find an old update of the same kind as
184
   * the one we are doing, there must be a programming
185
   * error.  We report it but try to continue anyway.
186
   */
187
0
  ISC_LIST_FOREACH(diff->tuples, ot, link) {
188
0
    if (dns_name_caseequal(&ot->name, &(*tuplep)->name) &&
189
0
        dns_rdata_compare(&ot->rdata, &(*tuplep)->rdata) == 0 &&
190
0
        ot->ttl == (*tuplep)->ttl)
191
0
    {
192
0
      ISC_LIST_UNLINK(diff->tuples, ot, link);
193
0
      INSIST(diff->size > 0);
194
0
      diff->size -= 1;
195
196
0
      if ((*tuplep)->op == ot->op) {
197
0
        UNEXPECTED_ERROR("unexpected non-minimal diff");
198
0
      } else {
199
0
        dns_difftuple_free(tuplep);
200
0
      }
201
0
      dns_difftuple_free(&ot);
202
0
      break;
203
0
    }
204
0
  }
205
206
0
  if (*tuplep != NULL) {
207
0
    ISC_LIST_APPEND(diff->tuples, *tuplep, link);
208
0
    diff->size += 1;
209
0
    *tuplep = NULL;
210
0
  }
211
0
}
212
213
static isc_stdtime_t
214
0
setresign(dns_rdataset_t *modified) {
215
0
  dns_rdata_t rdata = DNS_RDATA_INIT;
216
0
  dns_rdata_rrsig_t sig;
217
0
  int64_t when;
218
0
  isc_result_t result;
219
220
0
  result = dns_rdataset_first(modified);
221
0
  INSIST(result == ISC_R_SUCCESS);
222
0
  dns_rdataset_current(modified, &rdata);
223
0
  (void)dns_rdata_tostruct(&rdata, &sig, NULL);
224
0
  if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
225
0
    when = 0;
226
0
  } else {
227
0
    when = dns_time64_from32(sig.timeexpire);
228
0
  }
229
0
  dns_rdata_reset(&rdata);
230
231
0
  result = dns_rdataset_next(modified);
232
0
  while (result == ISC_R_SUCCESS) {
233
0
    dns_rdataset_current(modified, &rdata);
234
0
    (void)dns_rdata_tostruct(&rdata, &sig, NULL);
235
0
    if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
236
0
      goto next_rr;
237
0
    }
238
0
    if (when == 0 || dns_time64_from32(sig.timeexpire) < when) {
239
0
      when = dns_time64_from32(sig.timeexpire);
240
0
    }
241
0
  next_rr:
242
0
    dns_rdata_reset(&rdata);
243
0
    result = dns_rdataset_next(modified);
244
0
  }
245
0
  INSIST(result == ISC_R_NOMORE);
246
0
  return (isc_stdtime_t)when;
247
0
}
248
249
static void
250
0
getownercase(dns_rdataset_t *rdataset, dns_name_t *name) {
251
0
  if (dns_rdataset_isassociated(rdataset)) {
252
0
    dns_rdataset_getownercase(rdataset, name);
253
0
  }
254
0
}
255
256
static const char *
257
0
optotext(dns_diffop_t op) {
258
0
  switch (op) {
259
0
  case DNS_DIFFOP_ADD:
260
0
    return "add";
261
0
  case DNS_DIFFOP_ADDRESIGN:
262
0
    return "add-resign";
263
0
  case DNS_DIFFOP_DEL:
264
0
    return "del";
265
0
  case DNS_DIFFOP_DELRESIGN:
266
0
    return "del-resign";
267
0
  default:
268
0
    return "unknown";
269
0
  }
270
0
}
271
272
static isc_result_t
273
diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
274
0
     bool warn) {
275
0
  dns_difftuple_t *t;
276
0
  dns_dbnode_t *node = NULL;
277
0
  isc_result_t result;
278
0
  char namebuf[DNS_NAME_FORMATSIZE];
279
0
  char typebuf[DNS_RDATATYPE_FORMATSIZE];
280
0
  char classbuf[DNS_RDATACLASS_FORMATSIZE];
281
282
0
  REQUIRE(DNS_DIFF_VALID(diff));
283
0
  REQUIRE(DNS_DB_VALID(db));
284
285
0
  t = ISC_LIST_HEAD(diff->tuples);
286
0
  while (t != NULL) {
287
0
    dns_name_t *name;
288
289
0
    INSIST(node == NULL);
290
0
    name = &t->name;
291
    /*
292
     * Find the node.
293
     * We create the node if it does not exist.
294
     * This will cause an empty node to be created if the diff
295
     * contains a deletion of an RR at a nonexistent name,
296
     * but such diffs should never be created in the first
297
     * place.
298
     */
299
300
0
    while (t != NULL && dns_name_equal(&t->name, name)) {
301
0
      dns_rdatatype_t type, covers;
302
0
      dns_rdataclass_t rdclass;
303
0
      dns_diffop_t op;
304
0
      dns_rdatalist_t rdl;
305
0
      dns_rdataset_t rds;
306
0
      dns_rdataset_t ardataset;
307
0
      unsigned int options;
308
309
0
      op = t->op;
310
0
      type = t->rdata.type;
311
0
      rdclass = t->rdata.rdclass;
312
0
      covers = rdata_covers(&t->rdata);
313
314
      /*
315
       * Collect a contiguous set of updates with
316
       * the same operation (add/delete) and RR type
317
       * into a single rdatalist so that the
318
       * database rrset merging/subtraction code
319
       * can work more efficiently than if each
320
       * RR were merged into / subtracted from
321
       * the database separately.
322
       *
323
       * This is done by linking rdata structures from the
324
       * diff into "rdatalist".  This uses the rdata link
325
       * field, not the diff link field, so the structure
326
       * of the diff itself is not affected.
327
       */
328
329
0
      dns_rdatalist_init(&rdl);
330
0
      rdl.type = type;
331
0
      rdl.covers = covers;
332
0
      rdl.rdclass = t->rdata.rdclass;
333
0
      rdl.ttl = t->ttl;
334
335
0
      node = NULL;
336
0
      if (type != dns_rdatatype_nsec3 &&
337
0
          covers != dns_rdatatype_nsec3)
338
0
      {
339
0
        CHECK(dns_db_findnode(db, name, true, &node));
340
0
      } else {
341
0
        CHECK(dns_db_findnsec3node(db, name, true,
342
0
                 &node));
343
0
      }
344
345
0
      while (t != NULL && dns_name_equal(&t->name, name) &&
346
0
             t->op == op && t->rdata.type == type &&
347
0
             rdata_covers(&t->rdata) == covers)
348
0
      {
349
        /*
350
         * Remember the add name for
351
         * dns_rdataset_setownercase.
352
         */
353
0
        name = &t->name;
354
0
        if (t->ttl != rdl.ttl && warn) {
355
0
          dns_name_format(name, namebuf,
356
0
              sizeof(namebuf));
357
0
          dns_rdatatype_format(t->rdata.type,
358
0
                   typebuf,
359
0
                   sizeof(typebuf));
360
0
          dns_rdataclass_format(t->rdata.rdclass,
361
0
                    classbuf,
362
0
                    sizeof(classbuf));
363
0
          isc_log_write(DNS_LOGCATEGORY_GENERAL,
364
0
                  DNS_LOGMODULE_DIFF,
365
0
                  ISC_LOG_WARNING,
366
0
                  "'%s/%s/%s': TTL differs "
367
0
                  "in "
368
0
                  "rdataset, adjusting "
369
0
                  "%lu -> %lu",
370
0
                  namebuf, typebuf,
371
0
                  classbuf,
372
0
                  (unsigned long)t->ttl,
373
0
                  (unsigned long)rdl.ttl);
374
0
        }
375
0
        ISC_LIST_APPEND(rdl.rdata, &t->rdata, link);
376
0
        t = ISC_LIST_NEXT(t, link);
377
0
      }
378
379
      /*
380
       * Convert the rdatalist into a rdataset.
381
       */
382
0
      dns_rdataset_init(&rds);
383
0
      dns_rdataset_init(&ardataset);
384
0
      dns_rdatalist_tordataset(&rdl, &rds);
385
0
      dns_rdataset_setownercase(&rds, name);
386
0
      rds.trust = dns_trust_ultimate;
387
388
      /*
389
       * Merge the rdataset into the database.
390
       */
391
0
      switch (op) {
392
0
      case DNS_DIFFOP_ADD:
393
0
      case DNS_DIFFOP_ADDRESIGN:
394
0
        options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
395
0
            DNS_DBADD_EXACTTTL;
396
0
        result = dns_db_addrdataset(db, node, ver, 0,
397
0
                  &rds, options,
398
0
                  &ardataset);
399
0
        break;
400
0
      case DNS_DIFFOP_DEL:
401
0
      case DNS_DIFFOP_DELRESIGN:
402
0
        options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
403
0
        result = dns_db_subtractrdataset(db, node, ver,
404
0
                 &rds, options,
405
0
                 &ardataset);
406
0
        break;
407
0
      default:
408
0
        UNREACHABLE();
409
0
      }
410
411
0
      if (result == ISC_R_SUCCESS) {
412
0
        if (rds.type == dns_rdatatype_rrsig &&
413
0
            (op == DNS_DIFFOP_DELRESIGN ||
414
0
             op == DNS_DIFFOP_ADDRESIGN))
415
0
        {
416
0
          isc_stdtime_t resign;
417
0
          resign = setresign(&ardataset);
418
0
          dns_db_setsigningtime(db, &ardataset,
419
0
                    resign);
420
0
        }
421
0
        if (op == DNS_DIFFOP_DEL ||
422
0
            op == DNS_DIFFOP_DELRESIGN)
423
0
        {
424
0
          getownercase(&ardataset, name);
425
0
        }
426
0
      } else if (result == DNS_R_UNCHANGED) {
427
        /*
428
         * This will not happen when executing a
429
         * dynamic update, because that code will
430
         * generate strictly minimal diffs.
431
         * It may happen when receiving an IXFR
432
         * from a server that is not as careful.
433
         * Issue a warning and continue.
434
         */
435
0
        if (warn) {
436
0
          dns_name_format(dns_db_origin(db),
437
0
              namebuf,
438
0
              sizeof(namebuf));
439
0
          dns_rdataclass_format(dns_db_class(db),
440
0
                    classbuf,
441
0
                    sizeof(classbuf));
442
0
          isc_log_write(DNS_LOGCATEGORY_GENERAL,
443
0
                  DNS_LOGMODULE_DIFF,
444
0
                  ISC_LOG_WARNING,
445
0
                  "%s/%s: dns_diff_apply: "
446
0
                  "update with no effect",
447
0
                  namebuf, classbuf);
448
0
        }
449
0
        if (op == DNS_DIFFOP_DEL ||
450
0
            op == DNS_DIFFOP_DELRESIGN)
451
0
        {
452
0
          getownercase(&ardataset, name);
453
0
        }
454
0
      } else if (result == DNS_R_NXRRSET) {
455
        /*
456
         * OK.
457
         */
458
0
        if (op == DNS_DIFFOP_DEL ||
459
0
            op == DNS_DIFFOP_DELRESIGN)
460
0
        {
461
0
          getownercase(&ardataset, name);
462
0
        }
463
0
        if (dns_rdataset_isassociated(&ardataset)) {
464
0
          dns_rdataset_disassociate(&ardataset);
465
0
        }
466
0
      } else {
467
0
        if (result == DNS_R_NOTEXACT) {
468
0
          dns_name_format(name, namebuf,
469
0
              sizeof(namebuf));
470
0
          dns_rdatatype_format(type, typebuf,
471
0
                   sizeof(typebuf));
472
0
          dns_rdataclass_format(rdclass, classbuf,
473
0
                    sizeof(classbuf));
474
0
          isc_log_write(
475
0
            DNS_LOGCATEGORY_GENERAL,
476
0
            DNS_LOGMODULE_DIFF,
477
0
            ISC_LOG_ERROR,
478
0
            "dns_diff_apply: %s/%s/%s: %s "
479
0
            "%s",
480
0
            namebuf, typebuf, classbuf,
481
0
            optotext(op),
482
0
            isc_result_totext(result));
483
0
        }
484
0
        if (dns_rdataset_isassociated(&ardataset)) {
485
0
          dns_rdataset_disassociate(&ardataset);
486
0
        }
487
0
        CHECK(result);
488
0
      }
489
0
      dns_db_detachnode(&node);
490
0
      if (dns_rdataset_isassociated(&ardataset)) {
491
0
        dns_rdataset_disassociate(&ardataset);
492
0
      }
493
0
    }
494
0
  }
495
0
  return ISC_R_SUCCESS;
496
497
0
failure:
498
0
  if (node != NULL) {
499
0
    dns_db_detachnode(&node);
500
0
  }
501
0
  return result;
502
0
}
503
504
isc_result_t
505
0
dns_diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver) {
506
0
  return diff_apply(diff, db, ver, true);
507
0
}
508
509
isc_result_t
510
dns_diff_applysilently(const dns_diff_t *diff, dns_db_t *db,
511
0
           dns_dbversion_t *ver) {
512
0
  return diff_apply(diff, db, ver, false);
513
0
}
514
515
/* XXX this duplicates lots of code in diff_apply(). */
516
517
isc_result_t
518
0
dns_diff_load(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks) {
519
0
  dns_difftuple_t *t;
520
0
  isc_result_t result;
521
522
0
  REQUIRE(DNS_DIFF_VALID(diff));
523
524
0
  if (callbacks->setup != NULL) {
525
0
    callbacks->setup(callbacks->add_private);
526
0
  }
527
528
0
  t = ISC_LIST_HEAD(diff->tuples);
529
0
  while (t != NULL) {
530
0
    dns_name_t *name;
531
532
0
    name = &t->name;
533
0
    while (t != NULL && dns_name_caseequal(&t->name, name)) {
534
0
      dns_rdatatype_t type, covers;
535
0
      dns_diffop_t op;
536
0
      dns_rdatalist_t rdl;
537
0
      dns_rdataset_t rds;
538
539
0
      op = t->op;
540
0
      type = t->rdata.type;
541
0
      covers = rdata_covers(&t->rdata);
542
543
0
      dns_rdatalist_init(&rdl);
544
0
      rdl.type = type;
545
0
      rdl.covers = covers;
546
0
      rdl.rdclass = t->rdata.rdclass;
547
0
      rdl.ttl = t->ttl;
548
549
0
      while (t != NULL &&
550
0
             dns_name_caseequal(&t->name, name) &&
551
0
             t->op == op && t->rdata.type == type &&
552
0
             rdata_covers(&t->rdata) == covers)
553
0
      {
554
0
        ISC_LIST_APPEND(rdl.rdata, &t->rdata, link);
555
0
        t = ISC_LIST_NEXT(t, link);
556
0
      }
557
558
      /*
559
       * Convert the rdatalist into a rdataset.
560
       */
561
0
      dns_rdataset_init(&rds);
562
0
      dns_rdatalist_tordataset(&rdl, &rds);
563
0
      rds.trust = dns_trust_ultimate;
564
565
0
      INSIST(op == DNS_DIFFOP_ADD);
566
0
      result = callbacks->add(callbacks->add_private, name,
567
0
            &rds DNS__DB_FILELINE);
568
0
      if (result == DNS_R_UNCHANGED) {
569
0
        isc_log_write(DNS_LOGCATEGORY_GENERAL,
570
0
                DNS_LOGMODULE_DIFF,
571
0
                ISC_LOG_WARNING,
572
0
                "dns_diff_load: "
573
0
                "update with no effect");
574
0
      } else if (result == ISC_R_SUCCESS ||
575
0
           result == DNS_R_NXRRSET)
576
0
      {
577
        /*
578
         * OK.
579
         */
580
0
      } else {
581
0
        CHECK(result);
582
0
      }
583
0
    }
584
0
  }
585
0
  result = ISC_R_SUCCESS;
586
587
0
failure:
588
0
  if (callbacks->commit != NULL) {
589
0
    callbacks->commit(callbacks->add_private);
590
0
  }
591
0
  return result;
592
0
}
593
594
/*
595
 * XXX uses qsort(); a merge sort would be more natural for lists,
596
 * and perhaps safer wrt thread stack overflow.
597
 */
598
isc_result_t
599
0
dns_diff_sort(dns_diff_t *diff, dns_diff_compare_func *compare) {
600
0
  unsigned int length = 0;
601
0
  unsigned int i;
602
0
  dns_difftuple_t **v = NULL;
603
0
  REQUIRE(DNS_DIFF_VALID(diff));
604
605
0
  ISC_LIST_FOREACH(diff->tuples, p, link) {
606
0
    length++;
607
0
  }
608
0
  if (length == 0) {
609
0
    return ISC_R_SUCCESS;
610
0
  }
611
0
  v = isc_mem_cget(diff->mctx, length, sizeof(dns_difftuple_t *));
612
0
  for (i = 0; i < length; i++) {
613
0
    dns_difftuple_t *p = ISC_LIST_HEAD(diff->tuples);
614
0
    v[i] = p;
615
0
    ISC_LIST_UNLINK(diff->tuples, p, link);
616
0
  }
617
0
  INSIST(ISC_LIST_HEAD(diff->tuples) == NULL);
618
0
  qsort(v, length, sizeof(v[0]), compare);
619
0
  for (i = 0; i < length; i++) {
620
0
    ISC_LIST_APPEND(diff->tuples, v[i], link);
621
0
  }
622
0
  isc_mem_cput(diff->mctx, v, length, sizeof(dns_difftuple_t *));
623
0
  return ISC_R_SUCCESS;
624
0
}
625
626
/*
627
 * Create an rdataset containing the single RR of the given
628
 * tuple.  The caller must allocate the rdata, rdataset and
629
 * an rdatalist structure for it to refer to.
630
 */
631
632
static void
633
diff_tuple_tordataset(dns_difftuple_t *t, dns_rdata_t *rdata,
634
0
          dns_rdatalist_t *rdl, dns_rdataset_t *rds) {
635
0
  REQUIRE(DNS_DIFFTUPLE_VALID(t));
636
0
  REQUIRE(rdl != NULL);
637
0
  REQUIRE(rds != NULL);
638
639
0
  dns_rdatalist_init(rdl);
640
0
  rdl->type = t->rdata.type;
641
0
  rdl->rdclass = t->rdata.rdclass;
642
0
  rdl->ttl = t->ttl;
643
0
  dns_rdataset_init(rds);
644
0
  ISC_LINK_INIT(rdata, link);
645
0
  dns_rdata_clone(&t->rdata, rdata);
646
0
  ISC_LIST_APPEND(rdl->rdata, rdata, link);
647
0
  dns_rdatalist_tordataset(rdl, rds);
648
0
}
649
650
isc_result_t
651
0
dns_diff_print(dns_diff_t *diff, FILE *file) {
652
0
  isc_result_t result;
653
0
  char *mem = NULL;
654
0
  unsigned int size = 2048;
655
0
  const char *op = NULL;
656
657
0
  REQUIRE(DNS_DIFF_VALID(diff));
658
659
0
  int required_log_level = ISC_LOG_DEBUG(7);
660
661
  /*
662
   * Logging requires allocating a buffer and some costly translation to
663
   * text. Avoid it if possible.
664
   */
665
0
  if (isc_log_wouldlog(required_log_level) || file != NULL) {
666
0
    mem = isc_mem_get(diff->mctx, size);
667
668
0
    ISC_LIST_FOREACH(diff->tuples, t, link) {
669
0
      isc_buffer_t buf;
670
0
      isc_region_t r;
671
672
0
      dns_rdatalist_t rdl;
673
0
      dns_rdataset_t rds;
674
0
      dns_rdata_t rd = DNS_RDATA_INIT;
675
676
0
      diff_tuple_tordataset(t, &rd, &rdl, &rds);
677
0
    again:
678
0
      isc_buffer_init(&buf, mem, size);
679
0
      result = dns_rdataset_totext(&rds, &t->name, false,
680
0
                 false, &buf);
681
682
0
      if (result == ISC_R_NOSPACE) {
683
0
        isc_mem_put(diff->mctx, mem, size);
684
0
        size += 1024;
685
0
        mem = isc_mem_get(diff->mctx, size);
686
0
        goto again;
687
0
      }
688
689
0
      if (result != ISC_R_SUCCESS) {
690
0
        goto cleanup;
691
0
      }
692
      /*
693
       * Get rid of final newline.
694
       */
695
0
      INSIST(buf.used >= 1 &&
696
0
             ((char *)buf.base)[buf.used - 1] == '\n');
697
0
      buf.used--;
698
699
0
      isc_buffer_usedregion(&buf, &r);
700
0
      switch (t->op) {
701
0
      case DNS_DIFFOP_EXISTS:
702
0
        op = "exists";
703
0
        break;
704
0
      case DNS_DIFFOP_ADD:
705
0
        op = "add";
706
0
        break;
707
0
      case DNS_DIFFOP_DEL:
708
0
        op = "del";
709
0
        break;
710
0
      case DNS_DIFFOP_ADDRESIGN:
711
0
        op = "add re-sign";
712
0
        break;
713
0
      case DNS_DIFFOP_DELRESIGN:
714
0
        op = "del re-sign";
715
0
        break;
716
0
      }
717
0
      if (file != NULL) {
718
0
        fprintf(file, "%s %.*s\n", op, (int)r.length,
719
0
          (char *)r.base);
720
0
      } else {
721
0
        isc_log_write(DNS_LOGCATEGORY_GENERAL,
722
0
                DNS_LOGMODULE_DIFF,
723
0
                required_log_level, "%s %.*s", op,
724
0
                (int)r.length, (char *)r.base);
725
0
      }
726
0
    }
727
0
  }
728
0
  result = ISC_R_SUCCESS;
729
0
cleanup:
730
0
  if (mem != NULL) {
731
    isc_mem_put(diff->mctx, mem, size);
732
0
  }
733
0
  return result;
734
0
}