Coverage Report

Created: 2025-12-14 06:30

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