Coverage Report

Created: 2026-07-16 07:04

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