1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup
36 * @ingroup
37 * @brief
38 *
39 *
40 *
41 * @{
42 *
43 *----------------------------------------------------------------------------*/
44
45 #include "server-config.h"
46
47 #define DYNAMIC_MODULE_HANDLER_C 1
48
49 #include <dlfcn.h>
50
51 #include <dnscore/logger.h>
52 #include <dnscore/parsing.h>
53 #include <dnscore/mutex.h>
54 #include <dnscore/ptr_set.h>
55
56 #include "confs.h"
57
58 struct dynamic_module_interface_chain;
59 struct dynamic_module_dnskey_interface_chain;
60
61 struct dynamic_module_interface_chain *g_dynamic_module_interface_chain = NULL;
62 struct dynamic_module_dnskey_interface_chain *g_dynamic_module_dnskey_interface_chain = NULL;
63 struct dynamic_module_statistics_interface_chain *g_dynamic_module_statistics_interface_chain = NULL;
64
65 #include "dynamic-module-handler.h"
66 #include "server.h"
67
68 extern logger_handle *g_server_logger;
69 #define MODULE_MSG_HANDLE g_server_logger
70
71 #define YDYNMOD_TAG 0x444f4d4e594459
72 #define YDYNMODA_TAG 0x41444f4d4e594459
73 #define YDYNMODI_TAG 0x49444f4d4e594459
74 #define YDYNMODK_TAG 0x4b444f4d4e594459
75 #define YDYNMODS_TAG 0x53444f4d4e594459
76
77 logger_handle *g_module_logger = LOGGER_HANDLE_SINK;
78 #define CALLER_NAME "yadifad"
79
80 struct dynamic_module
81 {
82 void *so;
83 dynamic_module_interface_init *entry_point;
84 char *path;
85 struct dynamic_module_settings_args args;
86 int rc;
87 };
88
89 struct dynamic_module_interface_chain
90 {
91 struct dynamic_module_interface_chain *next;
92 struct dynamic_module *module;
93 struct dynamic_module_interface itf;
94 };
95
96 struct dynamic_module_dnskey_interface_chain
97 {
98 struct dynamic_module_dnskey_interface_chain *next;
99 struct dynamic_module *module;
100 struct dynamic_module_dnskey_interface itf;
101 };
102
103
104 struct dynamic_module_statistics_interface_chain
105 {
106 struct dynamic_module_statistics_interface_chain *next;
107 struct dynamic_module *module;
108 struct dynamic_module_statistics_interface itf;
109 };
110
111 static ptr_set dynamic_module_set = PTR_SET_ASCIIZ_EMPTY;
112 static mutex_t dynamic_module_mtx = MUTEX_INITIALIZER;
113
114 /**
115 * Trivial.
116 */
117
118 ya_result
dynamic_module_handler_init()119 dynamic_module_handler_init()
120 {
121 return SUCCESS;
122 }
123
124 /**
125 * Trivial.
126 */
127
128 ya_result
dynamic_module_handler_finalize()129 dynamic_module_handler_finalize()
130 {
131 return SUCCESS;
132 }
133
134 /**
135 * Loads a module given its full path name (with luck relative may work too ... so yes: full path)
136 * Don't forget to take into account the choot.
137 *
138 * Modules should only be loaded AFTER the last fork.
139 */
140
141 ya_result
dynamic_module_handler_load(int argc,const char ** argv)142 dynamic_module_handler_load(int argc, const char **argv)
143 {
144 if(argc <= 0)
145 {
146 return INVALID_ARGUMENT_ERROR;
147 }
148
149 const char *shared_object_path = argv[0];
150
151 log_debug("module: checking module '%s'", shared_object_path);
152
153 ptr_node *node;
154 mutex_lock(&dynamic_module_mtx);
155 node = ptr_set_find(&dynamic_module_set, shared_object_path);
156
157 if(node != NULL)
158 {
159 // already known
160
161 mutex_unlock(&dynamic_module_mtx);
162 return SUCCESS;
163 }
164
165 void *so = dlopen(shared_object_path, RTLD_NOLOAD);
166
167 if(so == NULL)
168 {
169 so = dlopen(shared_object_path, RTLD_NOW/*|RTDL_LOCAL*/);
170
171 if(so != NULL)
172 {
173 log_info("module: '%s': loaded", shared_object_path);
174
175 // loaded
176
177 // try to find the entry point
178
179 void *f = dlsym(so, "module_interface_init");
180
181 if(f == NULL)
182 {
183 static const char suffix[] = "_interface_init";
184 char module_interface_init_name[48];
185
186 // get the last '/'
187 const char *start = parse_skip_until_chars(shared_object_path, "/", 1);
188 // get the first '.'
189 const char *stop = parse_skip_until_chars(stop, ".", 1);
190
191 if(stop == start)
192 {
193 stop = start + strlen(start);
194 }
195
196 size_t len = stop - start;
197
198 if(len <= sizeof(module_interface_init_name) - sizeof(suffix))
199 {
200 memcpy(module_interface_init_name, start, len);
201 memcpy(&module_interface_init_name[len], suffix, sizeof(suffix));
202
203 f = dlsym(so, module_interface_init_name);
204 }
205 }
206
207 if(f == NULL)
208 {
209 mutex_unlock(&dynamic_module_mtx);
210
211 log_err("module: '%s': failed to find the entry point", shared_object_path);
212 dlclose(so);
213 return INVALID_STATE_ERROR;
214 }
215
216 log_debug("module: '%s': got the entry point", shared_object_path);
217
218 struct dynamic_module *module;
219 MALLOC_OBJECT_OR_DIE(module, struct dynamic_module, YDYNMOD_TAG);
220 MALLOC_OBJECT_ARRAY_OR_DIE(module->args.argv, char*, argc, YDYNMODA_TAG);
221 module->so = so;
222 module->entry_point = (dynamic_module_interface_init*)f;
223 module->path = strdup(shared_object_path);
224 module->rc = 0;
225
226 struct dynamic_module_settings_args *args = &module->args;
227 args->sizeof_struct = sizeof(*args);
228 args->argc = argc;
229 memcpy(args->argv, argv, argc * sizeof(char*));
230 args->data_path = g_config->data_path;
231
232 union dynamic_module_interfaces itfu;
233
234 // DYNAMIC_MODULE_INTERFACE_ID
235 {
236 itfu.interface.sizeof_struct = sizeof(itfu.interface);
237
238 if(ISOK(module->entry_point(DYNAMIC_MODULE_INTERFACE_ID, &itfu)))
239 {
240 // interface is supported
241 log_debug("module: '%s': dynamic_module_interface is supported", shared_object_path);
242
243 if(itfu.interface.on_startup == NULL)
244 {
245 itfu.interface.on_startup = dynamic_module_startup_callback_nop;
246 }
247
248 if(itfu.interface.on_settings_updated == NULL)
249 {
250 itfu.interface.on_settings_updated = dynamic_module_settings_callback_nop;
251 }
252
253 if(itfu.interface.on_shutdown == NULL)
254 {
255 itfu.interface.on_shutdown = dynamic_module_shutdown_callback_nop;
256 }
257
258 struct dynamic_module_interface_chain *ic;
259 MALLOC_OBJECT_OR_DIE(ic, struct dynamic_module_interface_chain, YDYNMODI_TAG);
260 ic->next = g_dynamic_module_interface_chain;
261 ic->module = module;
262 ++module->rc;
263 memcpy(&ic->itf, &itfu.interface, sizeof(itfu.interface));
264 g_dynamic_module_interface_chain = ic;
265 }
266 else
267 {
268 log_debug("module: '%s': dynamic_module_interface is not supported", shared_object_path);
269 }
270 }
271
272 // DYNAMIC_MODULE_DNSKEY_INTERFACE_ID
273 {
274 itfu.dnskey_interface.sizeof_struct = sizeof(itfu.dnskey_interface);
275
276 if(ISOK(module->entry_point(DYNAMIC_MODULE_DNSKEY_INTERFACE_ID, &itfu)))
277 {
278 // interface is supported
279 log_debug("module: '%s': dynamic_module_dnskey_interface is supported", shared_object_path);
280
281 if(itfu.dnskey_interface.on_dnskey_created == NULL)
282 {
283 itfu.dnskey_interface.on_dnskey_created = dynamic_module_on_dnskey_callback_nop;
284 }
285
286 if(itfu.dnskey_interface.on_dnskey_publish == NULL)
287 {
288 itfu.dnskey_interface.on_dnskey_publish = dynamic_module_on_dnskey_callback_nop;
289 }
290
291 if(itfu.dnskey_interface.on_dnskey_activate == NULL)
292 {
293 itfu.dnskey_interface.on_dnskey_activate = dynamic_module_on_dnskey_callback_nop;
294 }
295
296 if(itfu.dnskey_interface.on_dnskey_revoke == NULL)
297 {
298 itfu.dnskey_interface.on_dnskey_revoke = dynamic_module_on_dnskey_callback_nop;
299 }
300
301 if(itfu.dnskey_interface.on_dnskey_inactive == NULL)
302 {
303 itfu.dnskey_interface.on_dnskey_inactive = dynamic_module_on_dnskey_callback_nop;
304 }
305
306 if(itfu.dnskey_interface.on_dnskey_delete == NULL)
307 {
308 itfu.dnskey_interface.on_dnskey_delete = dynamic_module_on_dnskey_callback_nop;
309 }
310
311 struct dynamic_module_dnskey_interface_chain *ic;
312 MALLOC_OBJECT_OR_DIE(ic, struct dynamic_module_dnskey_interface_chain, YDYNMODK_TAG);
313 ic->next = g_dynamic_module_dnskey_interface_chain;
314 ic->module = module;
315 ++module->rc;
316 memcpy(&ic->itf, &itfu.dnskey_interface, sizeof(itfu.dnskey_interface));
317 g_dynamic_module_dnskey_interface_chain = ic;
318 }
319 else
320 {
321 log_debug("module: '%s': dynamic_module_dnskey_interface is not supported", shared_object_path);
322 }
323 }
324
325 // DYNAMIC_MODULE_STATISTICS_ID
326 {
327 itfu.statistics_interface.sizeof_struct = sizeof(itfu.statistics_interface);
328
329 if(ISOK(module->entry_point(DYNAMIC_MODULE_STATISTICS_ID, &itfu)))
330 {
331 // interface is supported
332 log_debug("module: '%s': dynamic_module_statistics_interface is supported", shared_object_path);
333
334 if(itfu.statistics_interface.on_statistics_update == NULL)
335 {
336 itfu.statistics_interface.on_statistics_update = dynamic_module_statistics_callback_nop;
337 }
338
339 struct dynamic_module_statistics_interface_chain *ic;
340 MALLOC_OBJECT_OR_DIE(ic, struct dynamic_module_statistics_interface_chain, YDYNMODS_TAG);
341 ic->next = g_dynamic_module_statistics_interface_chain;
342 ic->module = module;
343 ++module->rc;
344 memcpy(&ic->itf, &itfu.statistics_interface, sizeof(itfu.statistics_interface));
345 g_dynamic_module_statistics_interface_chain = ic;
346 }
347 else
348 {
349 log_debug("module: '%s': dynamic_module_statistics_interface is not supported", shared_object_path);
350 }
351 }
352
353 node = ptr_set_insert(&dynamic_module_set, module->path);
354 node->key = module->path;
355 node->value = module;
356
357 mutex_unlock(&dynamic_module_mtx);
358
359 return SUCCESS;
360 }
361 else
362 {
363 // failed to load
364
365 ret = ERRNO_ERROR;
366
367 mutex_unlock(&dynamic_module_mtx);
368
369 log_err("module: '%s': failed to load: %s", shared_object_path, dlerror());
370
371 return ret;
372 }
373 }
374 else
375 {
376 // already loaded, somehow
377
378 ret = ERRNO_ERROR;
379
380 mutex_unlock(&dynamic_module_mtx);
381
382 log_err("module: '%s': already loaded (albeit not through this handler)", shared_object_path);
383
384 return ret;
385 }
386 }
387
388 ya_result
dynamic_module_handler_load_from_command(const char * command)389 dynamic_module_handler_load_from_command(const char *command)
390 {
391 //const char *command_limit = &command[strlen(command)];
392 int argc = 0;
393 char *argv[128];
394 char tmp[PATH_MAX];
395
396 s32 n;
397
398 for(;;)
399 {
400 if(argc == sizeof(argv) / sizeof(char*))
401 {
402 n = -1; // too many parameters
403 break;
404 }
405
406 n = parse_next_token(tmp, sizeof(tmp), command, " \t");
407
408 if(n <= 0)
409 {
410 break;
411 }
412
413 command += n;
414
415 command = parse_skip_spaces(command);
416
417 argv[argc++] = strdup(tmp);
418 }
419
420 ya_result ret;
421
422 if(n >= 0)
423 {
424 ret = dynamic_module_handler_load(argc, (const char**)argv);
425 }
426 else
427 {
428 ret = INVALID_ARGUMENT_ERROR; // too many parameters
429 }
430
431 for(int i = 0; i < argc; ++i)
432 {
433 free(argv[i]);
434 }
435
436 return ret;
437 }
438
439 // interface
440
441 void
dynamic_module_startup()442 dynamic_module_startup()
443 {
444 struct dynamic_module_interface_chain *dc = g_dynamic_module_interface_chain;
445 while(dc != NULL)
446 {
447 dc->itf.on_startup();
448 dc = dc->next;
449 }
450 }
451
452 void
dynamic_module_settings()453 dynamic_module_settings()
454 {
455 struct dynamic_module_interface_chain *dc = g_dynamic_module_interface_chain;
456 if(dc != NULL)
457 {
458 do
459 {
460 dc->itf.on_settings_updated(&dc->module->args);
461 dc = dc->next;
462 }
463 while(dc != NULL);
464 }
465 }
466
467 void
dynamic_module_shutdown()468 dynamic_module_shutdown()
469 {
470 struct dynamic_module_interface_chain *dc = g_dynamic_module_interface_chain;
471 while(dc != NULL)
472 {
473 dc->itf.on_shutdown();
474 dc = dc->next;
475 }
476 }
477
478 // dnskey_interface
479
480 ya_result
dynamic_module_on_dnskey_args_init(struct dynamic_module_on_dnskey_args * args,const dnssec_key * key,void * buffer,size_t buffer_size)481 dynamic_module_on_dnskey_args_init(struct dynamic_module_on_dnskey_args *args, const dnssec_key *key, void *buffer, size_t buffer_size)
482 {
483 args->sizeof_struct = sizeof(*args);
484 args->caller_name = CALLER_NAME;
485 args->origin = dnskey_get_domain(key);
486 args->rdata_size = key->vtbl->dnssec_key_rdatasize(key);
487
488 if(args->rdata_size > buffer_size)
489 {
490 return BUFFER_WOULD_OVERFLOW;
491 }
492
493 key->vtbl->dnssec_key_writerdata(key, buffer);
494 args->rdata = buffer;
495 args->epoch_created = dnskey_get_created_epoch(key);
496 args->epoch_publish = dnskey_get_publish_epoch(key);
497 args->epoch_activate = dnskey_get_activate_epoch(key);
498 args->epoch_revoke = 0;
499 args->epoch_inactive = dnskey_get_inactive_epoch(key);
500 args->epoch_delete = dnskey_get_delete_epoch(key);
501 args->flags = dnskey_get_flags(key);
502 args->tag = dnskey_get_tag_const(key);
503 args->algorithm = dnskey_get_algorithm(key);
504
505 return SUCCESS;
506 }
507
508 void
dynamic_module_on_dnskey_args_finalize(struct dynamic_module_on_dnskey_args * args)509 dynamic_module_on_dnskey_args_finalize(struct dynamic_module_on_dnskey_args *args)
510 {
511 #if DEBUG
512 ZEROMEMORY(args, sizeof(*args));
513 #else
514 (void)args;
515 #endif
516 }
517
518 void
dynamic_module_on_dnskey_created(const dnssec_key * key)519 dynamic_module_on_dnskey_created(const dnssec_key *key)
520 {
521 struct dynamic_module_dnskey_interface_chain *dc = g_dynamic_module_dnskey_interface_chain;
522 if(dc != NULL)
523 {
524 struct dynamic_module_on_dnskey_args args;
525 char buffer[2048];
526
527 if(ISOK(dynamic_module_on_dnskey_args_init(&args, key, buffer, sizeof(buffer))))
528 {
529 do
530 {
531 dc->itf.on_dnskey_created(&args);
532 dc = dc->next;
533 }
534 while(dc != NULL);
535
536 dynamic_module_on_dnskey_args_finalize(&args);
537 }
538 }
539 }
540
541 void
dynamic_module_on_dnskey_publish(const dnssec_key * key)542 dynamic_module_on_dnskey_publish(const dnssec_key *key)
543 {
544 struct dynamic_module_dnskey_interface_chain *dc = g_dynamic_module_dnskey_interface_chain;
545 if(dc != NULL)
546 {
547 struct dynamic_module_on_dnskey_args args;
548 char buffer[2048];
549
550 dynamic_module_on_dnskey_args_init(&args, key, buffer, sizeof(buffer));
551
552 do
553 {
554 dc->itf.on_dnskey_publish(&args);
555 dc = dc->next;
556 }
557 while(dc != NULL);
558
559 dynamic_module_on_dnskey_args_finalize(&args);
560 }
561 }
562
563 void
dynamic_module_on_dnskey_activate(const dnssec_key * key)564 dynamic_module_on_dnskey_activate(const dnssec_key *key)
565 {
566 struct dynamic_module_dnskey_interface_chain *dc = g_dynamic_module_dnskey_interface_chain;
567 if(dc != NULL)
568 {
569 struct dynamic_module_on_dnskey_args args;
570 char buffer[2048];
571
572 dynamic_module_on_dnskey_args_init(&args, key, buffer, sizeof(buffer));
573
574 do
575 {
576 dc->itf.on_dnskey_activate(&args);
577 dc = dc->next;
578 }
579 while(dc != NULL);
580
581 dynamic_module_on_dnskey_args_finalize(&args);
582 }
583 }
584
585 void
dynamic_module_on_dnskey_revoke(const dnssec_key * key)586 dynamic_module_on_dnskey_revoke(const dnssec_key *key)
587 {
588 struct dynamic_module_dnskey_interface_chain *dc = g_dynamic_module_dnskey_interface_chain;
589 if(dc != NULL)
590 {
591 struct dynamic_module_on_dnskey_args args;
592 char buffer[2048];
593
594 dynamic_module_on_dnskey_args_init(&args, key, buffer, sizeof(buffer));
595
596 do
597 {
598 dc->itf.on_dnskey_revoke(&args);
599 dc = dc->next;
600 }
601 while(dc != NULL);
602
603 dynamic_module_on_dnskey_args_finalize(&args);
604 }
605 }
606
607 void
dynamic_module_on_dnskey_inactive(const dnssec_key * key)608 dynamic_module_on_dnskey_inactive(const dnssec_key *key)
609 {
610 struct dynamic_module_dnskey_interface_chain *dc = g_dynamic_module_dnskey_interface_chain;
611 if(dc != NULL)
612 {
613 struct dynamic_module_on_dnskey_args args;
614 char buffer[2048];
615
616 dynamic_module_on_dnskey_args_init(&args, key, buffer, sizeof(buffer));
617
618 do
619 {
620 dc->itf.on_dnskey_inactive(&args);
621 dc = dc->next;
622 }
623 while(dc != NULL);
624
625 dynamic_module_on_dnskey_args_finalize(&args);
626 }
627 }
628
629 void
dynamic_module_on_dnskey_delete(const dnssec_key * key)630 dynamic_module_on_dnskey_delete(const dnssec_key *key)
631 {
632 struct dynamic_module_dnskey_interface_chain *dc = g_dynamic_module_dnskey_interface_chain;
633 if(dc != NULL)
634 {
635 struct dynamic_module_on_dnskey_args args;
636 char buffer[2048];
637
638 dynamic_module_on_dnskey_args_init(&args, key, buffer, sizeof(buffer));
639
640 do
641 {
642 dc->itf.on_dnskey_delete(&args);
643 dc = dc->next;
644 }
645 while(dc != NULL);
646
647 dynamic_module_on_dnskey_args_finalize(&args);
648 }
649 }
650
651 void
dynamic_module_on_statistics_update(server_statistics_t * st,u64 epoch)652 dynamic_module_on_statistics_update(server_statistics_t *st, u64 epoch)
653 {
654 struct dynamic_module_statistics_interface_chain *dc = g_dynamic_module_statistics_interface_chain;
655
656 if(dc != NULL)
657 {
658 struct dynamic_module_statistics_args args;
659 struct dynamic_module_statistics_args_buffers args_buffers;
660
661 args.sizeof_struct = sizeof(args);
662 args.epoch_us = epoch;
663
664 args.input_loop_count = st->input_loop_count;
665 args.input_timeout_count = st->input_timeout_count;
666 args.loop_rate_counter = st->loop_rate_counter;
667 args.loop_rate_elapsed = st->loop_rate_elapsed;
668
669 args.udp_input_count = st->udp_input_count;
670 args.udp_queries_count = st->udp_queries_count;
671 args.udp_notify_input_count = st->udp_notify_input_count;
672 args.udp_updates_count = st->udp_updates_count;
673 args.udp_dropped_count = st->udp_dropped_count;
674 args.udp_output_size_total = st->udp_output_size_total;
675 args.udp_undefined_count = st->udp_undefined_count;
676 args.udp_referrals_count = st->udp_referrals_count;
677
678 args.tcp_input_count = st->tcp_input_count;
679 args.tcp_queries_count = st->tcp_queries_count;
680 args.tcp_notify_input_count = st->tcp_notify_input_count;
681 args.tcp_updates_count = st->tcp_updates_count;
682 args.tcp_dropped_count = st->tcp_dropped_count;
683 args.tcp_output_size_total = st->tcp_output_size_total;
684 args.tcp_undefined_count = st->tcp_undefined_count;
685 args.tcp_referrals_count = st->tcp_referrals_count;
686
687 args.tcp_axfr_count = st->tcp_axfr_count;
688 args.tcp_ixfr_count = st->tcp_ixfr_count;
689 args.tcp_overflow_count = st->tcp_overflow_count;
690
691 args.rrl_slip = st->rrl_slip;
692 args.rrl_drop = st->rrl_drop;
693
694 args.udp_rcode_count = args_buffers.udp_rcode_buffer;
695 args.tcp_rcode_count = args_buffers.tcp_rcode_buffer;
696 args.udp_tsig_rcode_count = args_buffers.udp_tsig_rcode_buffer;
697 args.tcp_tsig_rcode_count = args_buffers.tcp_tsig_rcode_buffer;
698
699 args.rcode_count_size = DYNAMIC_MODULE_STATISTICS_RCODE_COUNT;
700 args.tsig_rcode_count_size = DYNAMIC_MODULE_STATISTICS_TSIG_RCODE_COUNT;
701
702 do
703 {
704 dc->itf.on_statistics_update(&args);
705 dc = dc->next;
706 }
707 while(dc != NULL);
708 }
709 }
710
711 /**
712 * @}
713 */
714