Coverage Report

Created: 2023-06-07 06:23

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