Coverage Report

Created: 2025-12-12 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/stats.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
19
#include <isc/log.h>
20
#include <isc/magic.h>
21
#include <isc/mem.h>
22
#include <isc/refcount.h>
23
#include <isc/stats.h>
24
#include <isc/util.h>
25
26
#include <dns/opcode.h>
27
#include <dns/rdatatype.h>
28
#include <dns/stats.h>
29
30
0
#define DNS_STATS_MAGIC    ISC_MAGIC('D', 's', 't', 't')
31
#define DNS_STATS_VALID(x) ISC_MAGIC_VALID(x, DNS_STATS_MAGIC)
32
33
/*%
34
 * Statistics types.
35
 */
36
typedef enum {
37
  dns_statstype_general = 0,
38
  dns_statstype_rdtype = 1,
39
  dns_statstype_rdataset = 2,
40
  dns_statstype_opcode = 3,
41
  dns_statstype_rcode = 4,
42
  dns_statstype_dnssec = 5
43
} dns_statstype_t;
44
45
/*%
46
 * It doesn't make sense to have 2^16 counters for all possible types since
47
 * most of them won't be used.  We have counters for the first 256 types.
48
 *
49
 * A rdtypecounter is now 8 bits for RRtypes and 3 bits for flags:
50
 *
51
 *       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
52
 *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
53
 *     |  |  |  |  |  |  S  |NX|         RRType        |
54
 *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
55
 *
56
 * If the 8 bits for RRtype are all zero, this is an Other RRtype.
57
 */
58
0
#define RDTYPECOUNTER_MAXTYPE 0x00ff
59
60
/*
61
 *
62
 * Bit 7 is the NXRRSET (NX) flag and indicates whether this is a
63
 * positive (0) or a negative (1) RRset.
64
 */
65
0
#define RDTYPECOUNTER_NXRRSET 0x0100
66
67
/*
68
 * Then bit 5 and 6 mostly tell you if this counter is for an active,
69
 * stale, or ancient RRtype:
70
 *
71
 *     S = 0 (0b00) means Active
72
 *     S = 1 (0b01) means Stale
73
 *     S = 2 (0b10) means Ancient
74
 *
75
 * Since a counter cannot be stale and ancient at the same time, we
76
 * treat S = 0b11 as a special case to deal with NXDOMAIN counters.
77
 */
78
0
#define RDTYPECOUNTER_STALE    (1 << 9)
79
0
#define RDTYPECOUNTER_ANCIENT  (1 << 10)
80
0
#define RDTYPECOUNTER_NXDOMAIN ((1 << 9) | (1 << 10))
81
82
/*
83
 * S = 0b11 indicates an NXDOMAIN counter and in this case the RRtype
84
 * field signals the expiry of this cached item:
85
 *
86
 *     RRType = 0 (0b00) means Active
87
 *     RRType = 1 (0b01) means Stale
88
 *     RRType = 2 (0b02) means Ancient
89
 *
90
 */
91
0
#define RDTYPECOUNTER_NXDOMAIN_STALE   1
92
0
#define RDTYPECOUNTER_NXDOMAIN_ANCIENT 2
93
94
/*
95
 * The maximum value for rdtypecounter is for an ancient NXDOMAIN.
96
 */
97
0
#define RDTYPECOUNTER_MAXVAL 0x0602
98
99
/*
100
 * DNSSEC sign statistics.
101
 *
102
 * Per key we maintain 3 counters. The first is actually no counter but
103
 * a key id reference. The second is the number of signatures the key created.
104
 * The third is the number of signatures refreshed by the key.
105
 */
106
107
/* Maximum number of keys to keep track of for DNSSEC signing statistics. */
108
static int dnssecsign_num_keys = 4;
109
static int dnssecsign_block_size = 3;
110
111
struct dns_stats {
112
  unsigned int magic;
113
  dns_statstype_t type;
114
  isc_mem_t *mctx;
115
  isc_stats_t *counters;
116
  isc_refcount_t references;
117
};
118
119
typedef struct rdatadumparg {
120
  dns_rdatatypestats_dumper_t fn;
121
  void *arg;
122
} rdatadumparg_t;
123
124
typedef struct opcodedumparg {
125
  dns_opcodestats_dumper_t fn;
126
  void *arg;
127
} opcodedumparg_t;
128
129
typedef struct rcodedumparg {
130
  dns_rcodestats_dumper_t fn;
131
  void *arg;
132
} rcodedumparg_t;
133
typedef struct dnssecsigndumparg {
134
  dns_dnssecsignstats_dumper_t fn;
135
  void *arg;
136
} dnssecsigndumparg_t;
137
138
void
139
0
dns_stats_attach(dns_stats_t *stats, dns_stats_t **statsp) {
140
0
  REQUIRE(DNS_STATS_VALID(stats));
141
0
  REQUIRE(statsp != NULL && *statsp == NULL);
142
143
0
  isc_refcount_increment(&stats->references);
144
145
0
  *statsp = stats;
146
0
}
147
148
void
149
0
dns_stats_detach(dns_stats_t **statsp) {
150
0
  dns_stats_t *stats;
151
152
0
  REQUIRE(statsp != NULL && DNS_STATS_VALID(*statsp));
153
154
0
  stats = *statsp;
155
0
  *statsp = NULL;
156
157
0
  if (isc_refcount_decrement(&stats->references) == 1) {
158
0
    isc_refcount_destroy(&stats->references);
159
0
    isc_stats_detach(&stats->counters);
160
0
    isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
161
0
  }
162
0
}
163
164
/*%
165
 * Create methods
166
 */
167
static void
168
create_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters,
169
0
       dns_stats_t **statsp) {
170
0
  dns_stats_t *stats = isc_mem_get(mctx, sizeof(*stats));
171
172
0
  stats->counters = NULL;
173
0
  isc_refcount_init(&stats->references, 1);
174
175
0
  isc_stats_create(mctx, &stats->counters, ncounters);
176
177
0
  stats->magic = DNS_STATS_MAGIC;
178
0
  stats->type = type;
179
0
  stats->mctx = NULL;
180
0
  isc_mem_attach(mctx, &stats->mctx);
181
0
  *statsp = stats;
182
0
}
183
184
void
185
0
dns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters) {
186
0
  REQUIRE(statsp != NULL && *statsp == NULL);
187
188
0
  create_stats(mctx, dns_statstype_general, ncounters, statsp);
189
0
}
190
191
void
192
0
dns_rdatatypestats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
193
0
  REQUIRE(statsp != NULL && *statsp == NULL);
194
195
  /*
196
   * Create rdtype statistics for the first 255 RRtypes,
197
   * plus one additional for other RRtypes.
198
   */
199
0
  create_stats(mctx, dns_statstype_rdtype, RDTYPECOUNTER_MAXTYPE + 1,
200
0
         statsp);
201
0
}
202
203
void
204
0
dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
205
0
  REQUIRE(statsp != NULL && *statsp == NULL);
206
207
0
  create_stats(mctx, dns_statstype_rdataset, RDTYPECOUNTER_MAXVAL + 1,
208
0
         statsp);
209
0
}
210
211
void
212
0
dns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
213
0
  REQUIRE(statsp != NULL && *statsp == NULL);
214
215
0
  create_stats(mctx, dns_statstype_opcode, 16, statsp);
216
0
}
217
218
void
219
0
dns_rcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
220
0
  REQUIRE(statsp != NULL && *statsp == NULL);
221
222
0
  create_stats(mctx, dns_statstype_rcode, dns_rcode_badcookie + 1,
223
0
         statsp);
224
0
}
225
226
void
227
0
dns_dnssecsignstats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
228
0
  REQUIRE(statsp != NULL && *statsp == NULL);
229
230
  /*
231
   * Create two counters per key, one is the key id, the other two are
232
   * the actual counters for creating and refreshing signatures.
233
   */
234
0
  create_stats(mctx, dns_statstype_dnssec,
235
0
         dnssecsign_num_keys * dnssecsign_block_size, statsp);
236
0
}
237
238
/*%
239
 * Increment/Decrement methods
240
 */
241
void
242
0
dns_generalstats_increment(dns_stats_t *stats, isc_statscounter_t counter) {
243
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general);
244
245
0
  isc_stats_increment(stats->counters, counter);
246
0
}
247
248
static isc_statscounter_t
249
0
rdatatype2counter(dns_rdatatype_t type) {
250
0
  if (type > (dns_rdatatype_t)RDTYPECOUNTER_MAXTYPE) {
251
0
    return 0;
252
0
  }
253
0
  return (isc_statscounter_t)type;
254
0
}
255
256
void
257
0
dns_rdatatypestats_increment(dns_stats_t *stats, dns_rdatatype_t type) {
258
0
  isc_statscounter_t counter;
259
260
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype);
261
262
0
  counter = rdatatype2counter(type);
263
0
  isc_stats_increment(stats->counters, counter);
264
0
}
265
266
static void
267
update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype,
268
0
         bool increment) {
269
0
  isc_statscounter_t counter;
270
271
0
  if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
272
0
       DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0)
273
0
  {
274
0
    counter = RDTYPECOUNTER_NXDOMAIN;
275
276
    /*
277
     * This is an NXDOMAIN counter, save the expiry value
278
     * (active, stale, or ancient) value in the RRtype part.
279
     */
280
0
    if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
281
0
         DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0)
282
0
    {
283
0
      counter |= RDTYPECOUNTER_NXDOMAIN_ANCIENT;
284
0
    } else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
285
0
          DNS_RDATASTATSTYPE_ATTR_STALE) != 0)
286
0
    {
287
0
      counter += RDTYPECOUNTER_NXDOMAIN_STALE;
288
0
    }
289
0
  } else {
290
0
    counter = rdatatype2counter(DNS_RDATASTATSTYPE_BASE(rrsettype));
291
292
0
    if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
293
0
         DNS_RDATASTATSTYPE_ATTR_NXRRSET) != 0)
294
0
    {
295
0
      counter |= RDTYPECOUNTER_NXRRSET;
296
0
    }
297
298
0
    if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
299
0
         DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0)
300
0
    {
301
0
      counter |= RDTYPECOUNTER_ANCIENT;
302
0
    } else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
303
0
          DNS_RDATASTATSTYPE_ATTR_STALE) != 0)
304
0
    {
305
0
      counter |= RDTYPECOUNTER_STALE;
306
0
    }
307
0
  }
308
309
0
  if (increment) {
310
0
    isc_stats_increment(stats->counters, counter);
311
0
  } else {
312
0
    isc_stats_decrement(stats->counters, counter);
313
0
  }
314
0
}
315
316
void
317
dns_rdatasetstats_increment(dns_stats_t *stats,
318
0
          dns_rdatastatstype_t rrsettype) {
319
0
  REQUIRE(DNS_STATS_VALID(stats) &&
320
0
    stats->type == dns_statstype_rdataset);
321
322
0
  update_rdatasetstats(stats, rrsettype, true);
323
0
}
324
325
void
326
dns_rdatasetstats_decrement(dns_stats_t *stats,
327
0
          dns_rdatastatstype_t rrsettype) {
328
0
  REQUIRE(DNS_STATS_VALID(stats) &&
329
0
    stats->type == dns_statstype_rdataset);
330
331
0
  update_rdatasetstats(stats, rrsettype, false);
332
0
}
333
334
void
335
0
dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code) {
336
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode);
337
338
0
  isc_stats_increment(stats->counters, (isc_statscounter_t)code);
339
0
}
340
341
void
342
0
dns_rcodestats_increment(dns_stats_t *stats, dns_rcode_t code) {
343
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode);
344
345
0
  if (code <= dns_rcode_badcookie) {
346
0
    isc_stats_increment(stats->counters, (isc_statscounter_t)code);
347
0
  }
348
0
}
349
350
void
351
dns_dnssecsignstats_increment(dns_stats_t *stats, dns_keytag_t id, uint8_t alg,
352
0
            dnssecsignstats_type_t operation) {
353
0
  uint32_t kval;
354
355
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
356
357
0
  int num_keys = isc_stats_ncounters(stats->counters) /
358
0
           dnssecsign_block_size;
359
360
  /* Shift algorithm in front of key tag, which is 16 bits */
361
0
  kval = (uint32_t)(alg << 16 | id);
362
363
  /* Look up correct counter. */
364
0
  for (int i = 0; i < num_keys; i++) {
365
0
    int idx = i * dnssecsign_block_size;
366
0
    uint32_t counter = isc_stats_get_counter(stats->counters, idx);
367
0
    if (counter == kval) {
368
      /* Match */
369
0
      isc_stats_increment(stats->counters, idx + operation);
370
0
      return;
371
0
    }
372
0
  }
373
374
  /* No match found. Store key in unused slot. */
375
0
  for (int i = 0; i < num_keys; i++) {
376
0
    int idx = i * dnssecsign_block_size;
377
0
    uint32_t counter = isc_stats_get_counter(stats->counters, idx);
378
0
    if (counter == 0) {
379
0
      isc_stats_set(stats->counters, kval, idx);
380
0
      isc_stats_increment(stats->counters, idx + operation);
381
0
      return;
382
0
    }
383
0
  }
384
385
  /* No room, grow stats storage. */
386
0
  isc_stats_resize(&stats->counters,
387
0
       num_keys * dnssecsign_block_size * 2);
388
389
  /* Reset counters for new key (new index, nidx). */
390
0
  int nidx = num_keys * dnssecsign_block_size;
391
0
  isc_stats_set(stats->counters, kval, nidx);
392
0
  isc_stats_set(stats->counters, 0, nidx + dns_dnssecsignstats_sign);
393
0
  isc_stats_set(stats->counters, 0, nidx + dns_dnssecsignstats_refresh);
394
395
  /* And increment the counter for the given operation. */
396
0
  isc_stats_increment(stats->counters, nidx + operation);
397
0
}
398
399
void
400
0
dns_dnssecsignstats_clear(dns_stats_t *stats, dns_keytag_t id, uint8_t alg) {
401
0
  uint32_t kval;
402
403
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
404
405
0
  int num_keys = isc_stats_ncounters(stats->counters) /
406
0
           dnssecsign_block_size;
407
408
  /* Shift algorithm in front of key tag, which is 16 bits */
409
0
  kval = (uint32_t)(alg << 16 | id);
410
411
  /* Look up correct counter. */
412
0
  for (int i = 0; i < num_keys; i++) {
413
0
    int idx = i * dnssecsign_block_size;
414
0
    uint32_t counter = isc_stats_get_counter(stats->counters, idx);
415
0
    if (counter == kval) {
416
      /* Match */
417
0
      isc_stats_set(stats->counters, 0, idx);
418
0
      isc_stats_set(stats->counters, 0,
419
0
              idx + dns_dnssecsignstats_sign);
420
0
      isc_stats_set(stats->counters, 0,
421
0
              idx + dns_dnssecsignstats_refresh);
422
0
      return;
423
0
    }
424
0
  }
425
0
}
426
427
/*%
428
 * Dump methods
429
 */
430
void
431
dns_generalstats_dump(dns_stats_t *stats, dns_generalstats_dumper_t dump_fn,
432
0
          void *arg, unsigned int options) {
433
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general);
434
435
0
  isc_stats_dump(stats->counters, (isc_stats_dumper_t)dump_fn, arg,
436
0
           options);
437
0
}
438
439
static void
440
dump_rdentry(int rdcounter, uint64_t value, dns_rdatastatstype_t attributes,
441
0
       dns_rdatatypestats_dumper_t dump_fn, void *arg) {
442
0
  dns_rdatatype_t rdtype = dns_rdatatype_none; /* sentinel */
443
0
  dns_rdatastatstype_t type;
444
445
0
  if ((rdcounter & RDTYPECOUNTER_MAXTYPE) == 0) {
446
0
    attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE;
447
0
  } else {
448
0
    rdtype = (dns_rdatatype_t)(rdcounter & RDTYPECOUNTER_MAXTYPE);
449
0
  }
450
0
  type = DNS_RDATASTATSTYPE_VALUE((dns_rdatastatstype_t)rdtype,
451
0
          attributes);
452
0
  dump_fn(type, value, arg);
453
0
}
454
455
static void
456
0
rdatatype_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
457
0
  rdatadumparg_t *rdatadumparg = arg;
458
459
0
  dump_rdentry(counter, value, 0, rdatadumparg->fn, rdatadumparg->arg);
460
0
}
461
462
void
463
dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn,
464
0
      void *arg0, unsigned int options) {
465
0
  rdatadumparg_t arg;
466
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype);
467
468
0
  arg.fn = dump_fn;
469
0
  arg.arg = arg0;
470
0
  isc_stats_dump(stats->counters, rdatatype_dumpcb, &arg, options);
471
0
}
472
473
static void
474
0
rdataset_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
475
0
  rdatadumparg_t *rdatadumparg = arg;
476
0
  unsigned int attributes = 0;
477
478
0
  if ((counter & RDTYPECOUNTER_NXDOMAIN) == RDTYPECOUNTER_NXDOMAIN) {
479
0
    attributes |= DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
480
481
    /*
482
     * This is an NXDOMAIN counter, check the RRtype part for the
483
     * expiry value (active, stale, or ancient).
484
     */
485
0
    if ((counter & RDTYPECOUNTER_MAXTYPE) ==
486
0
        RDTYPECOUNTER_NXDOMAIN_STALE)
487
0
    {
488
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
489
0
    } else if ((counter & RDTYPECOUNTER_MAXTYPE) ==
490
0
         RDTYPECOUNTER_NXDOMAIN_ANCIENT)
491
0
    {
492
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT;
493
0
    }
494
0
  } else {
495
0
    if ((counter & RDTYPECOUNTER_MAXTYPE) == 0) {
496
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE;
497
0
    }
498
0
    if ((counter & RDTYPECOUNTER_NXRRSET) != 0) {
499
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_NXRRSET;
500
0
    }
501
502
0
    if ((counter & RDTYPECOUNTER_STALE) != 0) {
503
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
504
0
    } else if ((counter & RDTYPECOUNTER_ANCIENT) != 0) {
505
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT;
506
0
    }
507
0
  }
508
509
0
  dump_rdentry(counter, value, attributes, rdatadumparg->fn,
510
0
         rdatadumparg->arg);
511
0
}
512
513
void
514
dns_rdatasetstats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn,
515
0
           void *arg0, unsigned int options) {
516
0
  rdatadumparg_t arg;
517
518
0
  REQUIRE(DNS_STATS_VALID(stats) &&
519
0
    stats->type == dns_statstype_rdataset);
520
521
0
  arg.fn = dump_fn;
522
0
  arg.arg = arg0;
523
0
  isc_stats_dump(stats->counters, rdataset_dumpcb, &arg, options);
524
0
}
525
526
static void
527
0
dnssec_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
528
0
  dnssecsigndumparg_t *dnssecarg = arg;
529
530
0
  dnssecarg->fn((uint32_t)counter, value, dnssecarg->arg);
531
0
}
532
533
static void
534
dnssec_statsdump(isc_stats_t *stats, dnssecsignstats_type_t operation,
535
0
     isc_stats_dumper_t dump_fn, void *arg, unsigned int options) {
536
0
  int i, num_keys;
537
538
0
  num_keys = isc_stats_ncounters(stats) / dnssecsign_block_size;
539
0
  for (i = 0; i < num_keys; i++) {
540
0
    int idx = dnssecsign_block_size * i;
541
0
    uint32_t kval, val;
542
543
0
    kval = isc_stats_get_counter(stats, idx);
544
0
    if (kval == 0) {
545
0
      continue;
546
0
    }
547
548
0
    val = isc_stats_get_counter(stats, idx + operation);
549
0
    if ((options & ISC_STATSDUMP_VERBOSE) == 0 && val == 0) {
550
0
      continue;
551
0
    }
552
553
0
    dump_fn(kval, val, arg);
554
0
  }
555
0
}
556
557
void
558
dns_dnssecsignstats_dump(dns_stats_t *stats, dnssecsignstats_type_t operation,
559
       dns_dnssecsignstats_dumper_t dump_fn, void *arg0,
560
0
       unsigned int options) {
561
0
  dnssecsigndumparg_t arg;
562
563
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
564
565
0
  arg.fn = dump_fn;
566
0
  arg.arg = arg0;
567
568
0
  dnssec_statsdump(stats->counters, operation, dnssec_dumpcb, &arg,
569
0
       options);
570
0
}
571
572
static void
573
0
opcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
574
0
  opcodedumparg_t *opcodearg = arg;
575
576
0
  opcodearg->fn((dns_opcode_t)counter, value, opcodearg->arg);
577
0
}
578
579
static void
580
0
rcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
581
0
  rcodedumparg_t *rcodearg = arg;
582
583
0
  rcodearg->fn((dns_rcode_t)counter, value, rcodearg->arg);
584
0
}
585
586
void
587
dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn,
588
0
         void *arg0, unsigned int options) {
589
0
  opcodedumparg_t arg;
590
591
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode);
592
593
0
  arg.fn = dump_fn;
594
0
  arg.arg = arg0;
595
0
  isc_stats_dump(stats->counters, opcode_dumpcb, &arg, options);
596
0
}
597
598
void
599
dns_rcodestats_dump(dns_stats_t *stats, dns_rcodestats_dumper_t dump_fn,
600
0
        void *arg0, unsigned int options) {
601
0
  rcodedumparg_t arg;
602
603
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode);
604
605
0
  arg.fn = dump_fn;
606
0
  arg.arg = arg0;
607
0
  isc_stats_dump(stats->counters, rcode_dumpcb, &arg, options);
608
0
}