Asterisk - The Open Source Telephony Project GIT-master-f36a736
res_resolver_unbound.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2015, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <depend>unbound</depend>
21 <support_level>core</support_level>
22 ***/
23
24#include "asterisk.h"
25
26#include <signal.h>
27#include <unbound.h>
28#include <arpa/nameser.h>
29
30#include "asterisk/module.h"
32#include "asterisk/dns_core.h"
34#include "asterisk/config.h"
36#include "asterisk/test.h"
37
38#ifdef TEST_FRAMEWORK
39#include "asterisk/dns_srv.h"
40#endif
41
42/*** DOCUMENTATION
43 <configInfo name="res_resolver_unbound" language="en_US">
44 <configFile name="resolver_unbound.conf">
45 <configObject name="general">
46 <synopsis>General options for res_resolver_unbound</synopsis>
47 <configOption name="hosts">
48 <synopsis>Full path to an optional hosts file</synopsis>
49 <description><para>Hosts specified in a hosts file will be resolved within the resolver itself. If a value
50 of system is provided the system-specific file will be used.</para></description>
51 </configOption>
52 <configOption name="resolv">
53 <synopsis>Full path to an optional resolv.conf file</synopsis>
54 <description><para>The resolv.conf file specifies the nameservers to contact when resolving queries. If a
55 value of system is provided the system-specific file will be used. If provided alongside explicit nameservers the
56 nameservers contained within the resolv.conf file will be used after all others.</para></description>
57 </configOption>
58 <configOption name="nameserver">
59 <synopsis>Nameserver to use for queries</synopsis>
60 <description><para>An explicit nameserver can be specified which is used for resolving queries. If multiple
61 nameserver lines are specified the first will be the primary with failover occurring, in order, to the other
62 nameservers as backups. If provided alongside a resolv.conf file the nameservers explicitly specified will be
63 used before all others.</para></description>
64 </configOption>
65 <configOption name="debug">
66 <synopsis>Unbound debug level</synopsis>
67 <description><para>The debugging level for the unbound resolver. While there is no explicit range generally
68 the higher the number the more debug is output.</para></description>
69 </configOption>
70 <configOption name="ta_file">
71 <synopsis>Trust anchor file</synopsis>
72 <description><para>Full path to a file with DS and DNSKEY records in zone file format. This file is provided
73 to unbound and is used as a source for trust anchors.</para></description>
74 </configOption>
75 </configObject>
76 </configFile>
77 </configInfo>
78 ***/
79
80/*! \brief Structure for an unbound resolver */
82 /*! \brief Resolver context itself */
83 struct ub_ctx *context;
84 /*! \brief Thread handling the resolver */
85 pthread_t thread;
86};
87
88/*! \brief Structure for query resolver data */
90 /*! \brief ID for the specific query */
91 int id;
92 /*! \brief The resolver in use for the query */
94};
95
96/*! \brief Unbound configuration state information */
98 /*! \brief The configured resolver */
100};
101
102/*! \brief A structure to hold global configuration-related options */
105 AST_STRING_FIELD(hosts); /*!< Optional hosts file */
106 AST_STRING_FIELD(resolv); /*!< Optional resolv.conf file */
107 AST_STRING_FIELD(ta_file); /*!< Optional trust anchor file */
108 );
109 /*! \brief List of nameservers (in order) to use for queries */
111 /*! \brief Debug level for the resolver */
112 unsigned int debug;
113 /*! \brief State information */
115};
116
117/*! \brief A container for config related information */
120};
121
122/*!
123 * \brief Allocate a unbound_config to hold a snapshot of the complete results of parsing a config
124 * \internal
125 * \returns A void pointer to a newly allocated unbound_config
126 */
127static void *unbound_config_alloc(void);
128
129/*! \brief An aco_type structure to link the "general" category to the unbound_global_config type */
130static struct aco_type global_option = {
131 .type = ACO_GLOBAL,
132 .name = "general",
133 .item_offset = offsetof(struct unbound_config, global),
134 .category_match = ACO_WHITELIST_EXACT,
135 .category = "general",
136};
137
139
141 .filename = "resolver_unbound.conf",
142 .types = ACO_TYPES(&global_option),
143};
144
145/*! \brief A global object container that will contain the global_config that gets swapped out on reloads */
147
148/*!
149 * \brief Finish initializing new configuration
150 * \internal
151 */
152static int unbound_config_preapply_callback(void);
153
154/*! \brief Register information about the configs being processed by this module */
157 .pre_apply_config = unbound_config_preapply_callback,
158);
159
160/*! \brief Destructor for unbound resolver */
161static void unbound_resolver_destroy(void *obj)
162{
163 struct unbound_resolver *resolver = obj;
164
165 if (resolver->context) {
166 ub_ctx_delete(resolver->context);
167 }
168}
169
170/*! \brief Allocator for unbound resolver */
172{
173 struct unbound_resolver *resolver;
174
176 if (!resolver) {
177 return NULL;
178 }
179
180 resolver->thread = AST_PTHREADT_NULL;
181
182 resolver->context = ub_ctx_create();
183 if (!resolver->context) {
184 ao2_ref(resolver, -1);
185 return NULL;
186 }
187
188 /* Each async result should be invoked in a separate thread so others are not blocked */
189 ub_ctx_async(resolver->context, 1);
190
191 return resolver;
192}
193
194/*! \brief Resolver thread which waits and handles results */
195static void *unbound_resolver_thread(void *data)
196{
197 struct unbound_resolver *resolver = data;
198
199 ast_debug(1, "Starting processing for unbound resolver\n");
200
201 while (resolver->thread != AST_PTHREADT_STOP) {
202 /* Wait for any results to come in */
203 ast_wait_for_input(ub_fd(resolver->context), -1);
204
205 /* Finally process any results */
206 ub_process(resolver->context);
207 }
208
209 ast_debug(1, "Terminating processing for unbound resolver\n");
210
211 ao2_ref(resolver, -1);
212
213 return NULL;
214}
215
216/*! \brief Start function for the unbound resolver */
217static int unbound_resolver_start(struct unbound_resolver *resolver)
218{
219 int res;
220
221 if (resolver->thread != AST_PTHREADT_NULL) {
222 return 0;
223 }
224
225 ast_debug(1, "Starting thread for unbound resolver\n");
226
227 res = ast_pthread_create(&resolver->thread, NULL, unbound_resolver_thread, ao2_bump(resolver));
228 if (res) {
229 ast_debug(1, "Could not start thread for unbound resolver\n");
230 ao2_ref(resolver, -1);
231 }
232
233 return res;
234}
235
236/*! \brief Stop function for the unbound resolver */
237static void unbound_resolver_stop(struct unbound_resolver *resolver)
238{
239 pthread_t thread;
240
241 if (resolver->thread == AST_PTHREADT_NULL) {
242 return;
243 }
244
245 ast_debug(1, "Stopping processing thread for unbound resolver\n");
246
247 thread = resolver->thread;
248 resolver->thread = AST_PTHREADT_STOP;
249 pthread_kill(thread, SIGURG);
250 pthread_join(thread, NULL);
251
252 ast_debug(1, "Stopped processing thread for unbound resolver\n");
253}
254
255/*! \brief Callback invoked when resolution completes on a query */
256static void unbound_resolver_callback(void *data, int err, struct ub_result *ub_result)
257{
258 struct ast_dns_query *query = data;
259
260 if (!ub_result) {
261 ast_debug(3, "Badly formatted DNS query '%s'\n", ast_dns_query_get_name(query));
262 ast_dns_resolver_set_result(query, 0, 0, ns_r_formerr, ast_dns_query_get_name(query), "", 0);
264 ao2_ref(query, -1);
265 return;
266 }
267
268 if (!ast_dns_resolver_set_result(query, ub_result->secure, ub_result->bogus, ub_result->rcode,
269 S_OR(ub_result->canonname, ast_dns_query_get_name(query)), ub_result->answer_packet, ub_result->answer_len)) {
270 int i;
271 char *result_data;
272
273 for (i = 0; (result_data = ub_result->data[i]); i++) {
274 if (ast_dns_resolver_add_record(query, ub_result->qtype, ub_result->qclass, ub_result->ttl,
275 result_data, ub_result->len[i])) {
276 break;
277 }
278 }
279 }
280
282 ao2_ref(query, -1);
283 ub_resolve_free(ub_result);
284}
285
286static void unbound_resolver_data_dtor(void *vdoomed)
287{
288 struct unbound_resolver_data *doomed = vdoomed;
289
290 ao2_cleanup(doomed->resolver);
291}
292
294{
296 struct unbound_resolver_data *data;
297 int res;
298
299 data = ao2_alloc_options(sizeof(*data), unbound_resolver_data_dtor,
301 if (!data) {
302 ast_log(LOG_ERROR, "Failed to allocate resolver data for resolution of '%s'\n",
304 return -1;
305 }
306 data->resolver = ao2_bump(cfg->global->state->resolver);
307 ast_dns_resolver_set_data(query, data);
308
309 res = ub_resolve_async(data->resolver->context, ast_dns_query_get_name(query),
311 ao2_bump(query), unbound_resolver_callback, &data->id);
312
313 if (res) {
314 ast_log(LOG_ERROR, "Failed to perform async DNS resolution of '%s'\n",
316 ao2_ref(query, -1);
317 }
318
319 ao2_ref(data, -1);
320 ao2_ref(cfg, -1);
321 return res;
322}
323
324static int unbound_resolver_cancel(struct ast_dns_query *query)
325{
327 int res;
328
329 res = ub_cancel(data->resolver->context, data->id);
330 if (!res) {
331 /* When this query was started we bumped the ref, now that it has been cancelled we have ownership and
332 * need to drop it
333 */
334 ao2_ref(query, -1);
335 }
336
337 return res;
338}
339
341 .name = "unbound",
342 .priority = 100,
343 .resolve = unbound_resolver_resolve,
344 .cancel = unbound_resolver_cancel,
345};
346
347static void unbound_config_destructor(void *obj)
348{
349 struct unbound_config *cfg = obj;
350
351 ao2_cleanup(cfg->global);
352}
353
355{
356 struct unbound_global_config *global = obj;
357
359 ao2_cleanup(global->nameservers);
360 ao2_cleanup(global->state);
361}
362
364{
365 struct unbound_config_state *state = obj;
366
367 if (state->resolver) {
368 unbound_resolver_stop(state->resolver);
369 ao2_ref(state->resolver, -1);
370 }
371}
372
373static void *unbound_config_alloc(void)
374{
375 struct unbound_config *cfg;
376
378 if (!cfg) {
379 return NULL;
380 }
381
382 /* Allocate/initialize memory */
385 if (!cfg->global) {
386 goto error;
387 }
388
389 if (ast_string_field_init(cfg->global, 128)) {
390 goto error;
391 }
392
393 return cfg;
394error:
395 ao2_ref(cfg, -1);
396 return NULL;
397}
398
400{
401 int res = 0;
402
405 if (!cfg->global->state) {
406 ast_log(LOG_ERROR, "Could not allocate unbound resolver state structure\n");
407 return -1;
408 }
409
411 if (!cfg->global->state->resolver) {
412 ast_log(LOG_ERROR, "Could not create an unbound resolver\n");
413 return -1;
414 }
415
416 ub_ctx_debuglevel(cfg->global->state->resolver->context, cfg->global->debug);
417
418 if (!strcmp(cfg->global->hosts, "system")) {
419 res = ub_ctx_hosts(cfg->global->state->resolver->context, NULL);
420 } else if (!ast_strlen_zero(cfg->global->hosts)) {
421 res = ub_ctx_hosts(cfg->global->state->resolver->context, cfg->global->hosts);
422 }
423
424 if (res) {
425 ast_log(LOG_ERROR, "Failed to set hosts file to '%s' in unbound resolver: %s\n",
426 cfg->global->hosts, ub_strerror(res));
427 return -1;
428 }
429
430 if (cfg->global->nameservers) {
431 struct ao2_iterator it_nameservers;
432 char *nameserver;
433
434 it_nameservers = ao2_iterator_init(cfg->global->nameservers, 0);
435 while (!res && (nameserver = ao2_iterator_next(&it_nameservers))) {
436 res = ub_ctx_set_fwd(cfg->global->state->resolver->context, nameserver);
437
438 if (res) {
439 ast_log(LOG_ERROR, "Failed to add nameserver '%s' to unbound resolver: %s\n",
440 nameserver, ub_strerror(res));
441 }
442 ao2_ref(nameserver, -1);
443 }
444 ao2_iterator_destroy(&it_nameservers);
445 if (res) {
446 return -1;
447 }
448 }
449
450 if (!strcmp(cfg->global->resolv, "system")) {
451 res = ub_ctx_resolvconf(cfg->global->state->resolver->context, NULL);
452 } else if (!ast_strlen_zero(cfg->global->resolv)) {
453 res = ub_ctx_resolvconf(cfg->global->state->resolver->context, cfg->global->resolv);
454 }
455
456 if (res) {
457 ast_log(LOG_ERROR, "Failed to set resolv.conf file to '%s' in unbound resolver: %s\n",
458 cfg->global->resolv, ub_strerror(res));
459 return -1;
460 }
461
462 if (!ast_strlen_zero(cfg->global->ta_file)) {
463 res = ub_ctx_add_ta_file(cfg->global->state->resolver->context, cfg->global->ta_file);
464
465 if (res) {
466 ast_log(LOG_ERROR, "Failed to set trusted anchor file to '%s' in unbound resolver: %s\n",
467 cfg->global->ta_file, ub_strerror(res));
468 return -1;
469 }
470 }
471
473 ast_log(LOG_ERROR, "Could not start unbound resolver thread\n");
474 return -1;
475 }
476
477 return 0;
478}
479
481{
482 struct unbound_config *cfg;
483
484 cfg = unbound_config_alloc();
485 if (!cfg) {
486 ast_log(LOG_ERROR, "Could not create default configuration for unbound resolver\n");
487 return -1;
488 }
489
490 aco_set_defaults(&global_option, "general", cfg->global);
491
492 if (unbound_config_preapply(cfg)) {
493 ao2_ref(cfg, -1);
494 return -1;
495 }
496
497 ast_verb(1, "Starting unbound resolver using default configuration\n");
498
500 ao2_ref(cfg, -1);
501
502 return 0;
503}
504
506{
508}
509
510#ifdef TEST_FRAMEWORK
511
512#include "asterisk/dns_naptr.h"
513
514/*!
515 * \brief A DNS record to be used during a test
516 */
517struct dns_record {
518 /*! String representation of the record, as would be found in a file */
519 const char *as_string;
520 /*! The domain this record belongs to */
521 const char *domain;
522 /*! The type of the record */
523 int rr_type;
524 /*! The class of the record */
525 int rr_class;
526 /*! The TTL of the record, in seconds */
527 int ttl;
528 /*! The RDATA of the DNS record */
529 const char *buf;
530 /*! The size of the RDATA */
531 const size_t bufsize;
532 /*! Whether a record checker has visited this record */
533 int visited;
534};
535
536/*!
537 * \brief Resolution function for tests.
538 *
539 * Several tests will have similar setups but will want to make use of a different
540 * means of actually making queries and checking their results. This pluggable
541 * function pointer allows for similar tests to be operated in different ways.
542 *
543 * \param test The test being run
544 * \param domain The domain to look up
545 * \param rr_type The record type to look up
546 * \param rr_class The class of record to look up
547 * \param records All records that exist for the test.
548 * \param num_records Number of records in the records array.
549 *
550 * \retval 0 The test has passed thus far.
551 * \retval -1 The test has failed.
552 */
553typedef int (*resolve_fn)(struct ast_test *test, const char *domain, int rr_type,
554 int rr_class, struct dns_record *records, size_t num_records);
555
556/*!
557 * \brief Pluggable function for running a synchronous query and checking its results
558 */
559static int nominal_sync_run(struct ast_test *test, const char *domain, int rr_type,
560 int rr_class, struct dns_record *records, size_t num_records)
561{
563 const struct ast_dns_record *record;
564 int i;
565
566 /* Start by making sure no records have been visited */
567 for (i = 0; i < num_records; ++i) {
568 records[i].visited = 0;
569 }
570
571 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
572
573 if (ast_dns_resolve(domain, rr_type, rr_class, &result)) {
574 ast_test_status_update(test, "Failed to perform synchronous resolution of domain %s\n", domain);
575 return -1;
576 }
577
578 if (!result) {
579 ast_test_status_update(test, "Successful synchronous resolution of domain %s gave NULL result\n", domain);
580 return -1;
581 }
582
583 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
584 int match = 0;
585
586 /* Let's make sure this matches one of our known records */
587 for (i = 0; i < num_records; ++i) {
588 if (ast_dns_record_get_rr_type(record) == records[i].rr_type &&
589 ast_dns_record_get_rr_class(record) == records[i].rr_class &&
590 ast_dns_record_get_ttl(record) == records[i].ttl &&
591 !memcmp(ast_dns_record_get_data(record), records[i].buf, records[i].bufsize)) {
592 match = 1;
593 records[i].visited = 1;
594 break;
595 }
596 }
597
598 if (!match) {
599 ast_test_status_update(test, "Unknown DNS record returned from domain %s\n", domain);
600 return -1;
601 }
602 }
603
604 return 0;
605}
606
607/*!
608 * \brief Data required for an asynchronous callback
609 */
610struct async_data {
611 /*! The set of DNS records on a test */
612 struct dns_record *records;
613 /*! The number of DNS records on the test */
614 size_t num_records;
615 /*! Whether an asynchronous query failed */
616 int failed;
617 /*! Indicates the asynchronous query is complete */
618 int complete;
621};
622
623static void async_data_destructor(void *obj)
624{
625 struct async_data *adata = obj;
626
627 ast_mutex_destroy(&adata->lock);
628 ast_cond_destroy(&adata->cond);
629}
630
631static struct async_data *async_data_alloc(struct dns_record *records, size_t num_records)
632{
633 struct async_data *adata;
634
635 adata = ao2_alloc(sizeof(*adata), async_data_destructor);
636 if (!adata) {
637 return NULL;
638 }
639
640 ast_mutex_init(&adata->lock);
641 ast_cond_init(&adata->cond, NULL);
642 adata->records = records;
643 adata->num_records = num_records;
644
645 return adata;
646}
647
648/*!
649 * \brief Callback for asynchronous queries
650 *
651 * This query will check that the records in the DNS result match
652 * records that the test has created. The success or failure of the
653 * query is indicated through the async_data failed field.
654 *
655 * \param query The DNS query that has been resolved
656 */
657static void async_callback(const struct ast_dns_query *query)
658{
659 struct async_data *adata = ast_dns_query_get_data(query);
661 const struct ast_dns_record *record;
662 int i;
663
664 if (!result) {
665 adata->failed = -1;
666 goto end;
667 }
668
669 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
670 int match = 0;
671
672 /* Let's make sure this matches one of our known records */
673 for (i = 0; i < adata->num_records; ++i) {
674 if (ast_dns_record_get_rr_type(record) == adata->records[i].rr_type &&
675 ast_dns_record_get_rr_class(record) == adata->records[i].rr_class &&
676 ast_dns_record_get_ttl(record) == adata->records[i].ttl &&
677 !memcmp(ast_dns_record_get_data(record), adata->records[i].buf, adata->records[i].bufsize)) {
678 match = 1;
679 adata->records[i].visited = 1;
680 break;
681 }
682 }
683
684 if (!match) {
685 adata->failed = -1;
686 goto end;
687 }
688 }
689
690end:
691 ast_mutex_lock(&adata->lock);
692 adata->complete = 1;
693 ast_cond_signal(&adata->cond);
694 ast_mutex_unlock(&adata->lock);
695}
696
697/*!
698 * \brief Pluggable function for performing an asynchronous query during a test
699 *
700 * Unlike the synchronous version, this does not check the records, instead leaving
701 * that to be done in the asynchronous callback.
702 */
703static int nominal_async_run(struct ast_test *test, const char *domain, int rr_type,
704 int rr_class, struct dns_record *records, size_t num_records)
705{
706 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
707 RAII_VAR(struct async_data *, adata, NULL, ao2_cleanup);
708 int i;
709
710 adata = async_data_alloc(records, num_records);
711 if (!adata) {
712 ast_test_status_update(test, "Unable to allocate data for async query\n");
713 return -1;
714 }
715
716 /* Start by making sure no records have been visited */
717 for (i = 0; i < num_records; ++i) {
718 records[i].visited = 0;
719 }
720
721 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
722
723 active = ast_dns_resolve_async(domain, rr_type, rr_class, async_callback, adata);
724 if (!active) {
725 ast_test_status_update(test, "Failed to perform asynchronous resolution of domain %s\n", domain);
726 return -1;
727 }
728
729 ast_mutex_lock(&adata->lock);
730 while (!adata->complete) {
731 ast_cond_wait(&adata->cond, &adata->lock);
732 }
733 ast_mutex_unlock(&adata->lock);
734
735 if (adata->failed) {
736 ast_test_status_update(test, "Unknown DNS record returned from domain %s\n", domain);
737 }
738 return adata->failed;
739}
740
741/*!
742 * \brief Framework for running a nominal DNS test
743 *
744 * Synchronous and asynchronous tests mostly have the same setup, so this function
745 * serves as a common way to set up both types of tests by accepting a pluggable
746 * function to determine which type of lookup is used
747 *
748 * \param test The test being run
749 * \param runner The method for resolving queries on this test
750 */
751static enum ast_test_result_state nominal_test(struct ast_test *test, resolve_fn runner)
752{
753 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
754 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
755
756 static const size_t V4_SIZE = sizeof(struct in_addr);
757 static const size_t V6_SIZE = sizeof(struct in6_addr);
758
759 static const char *DOMAIN1 = "goose.feathers";
760 static const char *DOMAIN2 = "duck.feathers";
761
762 static const char *ADDR1 = "127.0.0.2";
763 static const char *ADDR2 = "127.0.0.3";
764 static const char *ADDR3 = "::1";
765 static const char *ADDR4 = "127.0.0.4";
766
767 char addr1_buf[V4_SIZE];
768 char addr2_buf[V4_SIZE];
769 char addr3_buf[V6_SIZE];
770 char addr4_buf[V4_SIZE];
771
772 struct dns_record records [] = {
773 { "goose.feathers 12345 IN A 127.0.0.2", DOMAIN1, ns_t_a, ns_c_in, 12345, addr1_buf, V4_SIZE, 0 },
774 { "goose.feathers 12345 IN A 127.0.0.3", DOMAIN1, ns_t_a, ns_c_in, 12345, addr2_buf, V4_SIZE, 0 },
775 { "goose.feathers 12345 IN AAAA ::1", DOMAIN1, ns_t_aaaa, ns_c_in, 12345, addr3_buf, V6_SIZE, 0 },
776 { "duck.feathers 12345 IN A 127.0.0.4", DOMAIN2, ns_t_a, ns_c_in, 12345, addr4_buf, V4_SIZE, 0 },
777 };
778
779 struct {
780 const char *domain;
781 int rr_type;
782 int rr_class;
783 int visited[ARRAY_LEN(records)];
784 } runs [] = {
785 { DOMAIN1, ns_t_a, ns_c_in, { 1, 1, 0, 0 } },
786 { DOMAIN1, ns_t_aaaa, ns_c_in, { 0, 0, 1, 0 } },
787 { DOMAIN2, ns_t_a, ns_c_in, { 0, 0, 0, 1 } },
788 };
789
790 int i;
792
793 inet_pton(AF_INET, ADDR1, addr1_buf);
794 inet_pton(AF_INET, ADDR2, addr2_buf);
795 inet_pton(AF_INET6, ADDR3, addr3_buf);
796 inet_pton(AF_INET, ADDR4, addr4_buf);
797
799 resolver = ao2_bump(cfg->global->state->resolver);
800
801 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
802 ub_ctx_zone_add(resolver->context, DOMAIN2, "static");
803
804 for (i = 0; i < ARRAY_LEN(records); ++i) {
805 ub_ctx_data_add(resolver->context, records[i].as_string);
806 }
807
808 for (i = 0; i < ARRAY_LEN(runs); ++i) {
809 int j;
810
811 if (runner(test, runs[i].domain, runs[i].rr_type, runs[i].rr_class, records, ARRAY_LEN(records))) {
812 res = AST_TEST_FAIL;
813 goto cleanup;
814 }
815
816 for (j = 0; j < ARRAY_LEN(records); ++j) {
817 if (records[j].visited != runs[i].visited[j]) {
818 ast_test_status_update(test, "DNS results match unexpected records\n");
819 res = AST_TEST_FAIL;
820 goto cleanup;
821 }
822 }
823 }
824
825cleanup:
826 for (i = 0; i < ARRAY_LEN(records); ++i) {
827 ub_ctx_data_remove(resolver->context, records[i].as_string);
828 }
829 ub_ctx_zone_remove(resolver->context, DOMAIN1);
830 ub_ctx_zone_remove(resolver->context, DOMAIN2);
831
832 return res;
833}
834
835AST_TEST_DEFINE(resolve_sync)
836{
837
838 switch (cmd) {
839 case TEST_INIT:
840 info->name = "resolve_sync";
841 info->category = "/res/res_resolver_unbound/";
842 info->summary = "Test nominal synchronous resolution using libunbound";
843 info->description = "This test performs the following:\n"
844 "\t* Set two static A records and one static AAAA record on one domain\n"
845 "\t* Set an A record for a second domain\n"
846 "\t* Perform an A record lookup on the first domain\n"
847 "\t* Ensure that both A records are returned and no AAAA record is returned\n"
848 "\t* Perform an AAAA record lookup on the first domain\n"
849 "\t* Ensure that the AAAA record is returned and no A record is returned\n"
850 "\t* Perform an A record lookup on the second domain\n"
851 "\t* Ensure that the A record from the second domain is returned";
852 return AST_TEST_NOT_RUN;
853 case TEST_EXECUTE:
854 break;
855 }
856
857 return nominal_test(test, nominal_sync_run);
858}
859
860AST_TEST_DEFINE(resolve_async)
861{
862 switch (cmd) {
863 case TEST_INIT:
864 info->name = "resolve_async";
865 info->category = "/res/res_resolver_unbound/";
866 info->summary = "Test nominal asynchronous resolution using libunbound";
867 info->description = "This test performs the following:\n"
868 "\t* Set two static A records and one static AAAA record on one domain\n"
869 "\t* Set an A record for a second domain\n"
870 "\t* Perform an A record lookup on the first domain\n"
871 "\t* Ensure that both A records are returned and no AAAA record is returned\n"
872 "\t* Perform an AAAA record lookup on the first domain\n"
873 "\t* Ensure that the AAAA record is returned and no A record is returned\n"
874 "\t* Perform an A record lookup on the second domain\n"
875 "\t* Ensure that the A record from the second domain is returned";
876 return AST_TEST_NOT_RUN;
877 case TEST_EXECUTE:
878 break;
879 }
880
881 return nominal_test(test, nominal_async_run);
882}
883
884typedef int (*off_nominal_resolve_fn)(struct ast_test *test, const char *domain, int rr_type,
885 int rr_class, int expected_rcode);
886
887static int off_nominal_sync_run(struct ast_test *test, const char *domain, int rr_type,
888 int rr_class, int expected_rcode)
889{
890 struct ast_dns_result *result;
891 int res = 0;
892
893 if (ast_dns_resolve(domain, rr_type, rr_class, &result)) {
894 ast_test_status_update(test, "Failed to perform resolution :(\n");
895 return -1;
896 }
897
898 if (!result) {
899 ast_test_status_update(test, "Resolution returned no result\n");
900 return -1;
901 }
902
903 if (ast_dns_result_get_rcode(result) != expected_rcode) {
904 ast_test_status_update(test, "Unexpected rcode '%d' (expected '%d') from DNS resolution of '%s' class: '%d' type: '%d'\n",
905 ast_dns_result_get_rcode(result), expected_rcode, domain, rr_class, rr_type);
906 res = -1;
907 }
908
910 ast_test_status_update(test, "DNS resolution returned records unexpectedly\n");
911 res = -1;
912 }
913
915 return res;
916}
917
918/*!
919 * \brief User data for off-nominal async resolution test
920 */
921struct off_nominal_async_data {
922 /*! The DNS result's expected rcode */
923 int expected_rcode;
924 /*! Whether an asynchronous query failed */
925 int failed;
926 /*! Indicates the asynchronous query is complete */
927 int complete;
930};
931
932static void off_nominal_async_data_destructor(void *obj)
933{
934 struct off_nominal_async_data *adata = obj;
935
936 ast_mutex_destroy(&adata->lock);
937 ast_cond_destroy(&adata->cond);
938}
939
940static struct off_nominal_async_data *off_nominal_async_data_alloc(int expected_rcode)
941{
942 struct off_nominal_async_data *adata;
943
944 adata = ao2_alloc(sizeof(*adata), off_nominal_async_data_destructor);
945 if (!adata) {
946 return NULL;
947 }
948
949 ast_mutex_init(&adata->lock);
950 ast_cond_init(&adata->cond, NULL);
951
952 adata->expected_rcode = expected_rcode;
953
954 return adata;
955}
956
957/*!
958 * \brief Async callback for off-nominal async test
959 *
960 * This test ensures that there is a result present on the query, then it checks
961 * that the rcode on the result is the expected value and that there are no
962 * records on the result.
963 *
964 * Once completed, the testing thread is signaled that the async query has
965 * completed.
966 */
967static void off_nominal_async_callback(const struct ast_dns_query *query)
968{
969 struct off_nominal_async_data *adata = ast_dns_query_get_data(query);
971
972 if (!result) {
973 adata->failed = -1;
974 goto end;
975 }
976
977 if (ast_dns_result_get_rcode(result) != adata->expected_rcode) {
978 adata->failed = -1;
979 }
980
982 adata->failed = -1;
983 }
984
985end:
986 ast_mutex_lock(&adata->lock);
987 adata->complete = 1;
988 ast_cond_signal(&adata->cond);
989 ast_mutex_unlock(&adata->lock);
990}
991
992static int off_nominal_async_run(struct ast_test *test, const char *domain, int rr_type,
993 int rr_class, int expected_rcode)
994{
995 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
996 RAII_VAR(struct off_nominal_async_data *, adata, NULL, ao2_cleanup);
997
998 adata = off_nominal_async_data_alloc(expected_rcode);
999 if (!adata) {
1000 ast_test_status_update(test, "Unable to allocate data for async query\n");
1001 return -1;
1002 }
1003
1004 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
1005
1006 active = ast_dns_resolve_async(domain, rr_type, rr_class, off_nominal_async_callback, adata);
1007 if (!active) {
1008 ast_test_status_update(test, "Failed to perform asynchronous resolution of domain %s\n", domain);
1009 return -1;
1010 }
1011
1012 ast_mutex_lock(&adata->lock);
1013 while (!adata->complete) {
1014 ast_cond_wait(&adata->cond, &adata->lock);
1015 }
1016 ast_mutex_unlock(&adata->lock);
1017
1018 if (adata->failed) {
1019 ast_test_status_update(test, "Asynchronous resolution failure %s\n", domain);
1020 }
1021 return adata->failed;
1022}
1023
1024static enum ast_test_result_state off_nominal_test(struct ast_test *test,
1025 off_nominal_resolve_fn runner)
1026{
1027 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1028 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1029
1030 static const size_t V4_SIZE = sizeof(struct in_addr);
1031
1032 static const char *DOMAIN1 = "goose.feathers";
1033 static const char *DOMAIN2 = "duck.feathers";
1034 static const char *BADFORMAT1 = ".1";
1035 static const char *BADFORMAT2 = ".www";
1036
1037 static const char *ADDR1 = "127.0.0.2";
1038
1039 char addr1_buf[V4_SIZE];
1040
1041 struct dns_record records [] = {
1042 { "goose.feathers 12345 IN A 127.0.0.2", DOMAIN1, ns_t_a, ns_c_in, 12345, addr1_buf, V4_SIZE, 0, },
1043 };
1044
1045 int i;
1047
1048 struct {
1049 const char *domain;
1050 int rr_type;
1051 int rr_class;
1052 int rcode;
1053 } runs [] = {
1054 { DOMAIN2, ns_t_a, ns_c_in, ns_r_nxdomain },
1055 { DOMAIN1, ns_t_aaaa, ns_c_in, ns_r_noerror },
1056 { DOMAIN1, ns_t_a, ns_c_chaos, ns_r_refused },
1057 { BADFORMAT1, ns_t_a, ns_c_in, ns_r_formerr },
1058 { BADFORMAT2, ns_t_a, ns_c_in, ns_r_formerr },
1059 { BADFORMAT1, ns_t_ptr, ns_c_in, ns_r_formerr },
1060 { BADFORMAT2, ns_t_ptr, ns_c_in, ns_r_formerr },
1061 };
1062
1063 inet_pton(AF_INET, ADDR1, addr1_buf);
1064
1066 resolver = ao2_bump(cfg->global->state->resolver);
1067
1068 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1069 ub_ctx_zone_add(resolver->context, DOMAIN2, "static");
1070
1071 for (i = 0; i < ARRAY_LEN(records); ++i) {
1072 ub_ctx_data_add(resolver->context, records[i].as_string);
1073 }
1074
1075 for (i = 0; i < ARRAY_LEN(runs); ++i) {
1076 if (runner(test, runs[i].domain, runs[i].rr_type, runs[i].rr_class, runs[i].rcode)) {
1077 res = AST_TEST_FAIL;
1078 }
1079 }
1080
1081 return res;
1082}
1083
1084AST_TEST_DEFINE(resolve_sync_off_nominal)
1085{
1086 switch (cmd) {
1087 case TEST_INIT:
1088 info->name = "resolve_sync_off_nominal";
1089 info->category = "/res/res_resolver_unbound/";
1090 info->summary = "Test off-nominal synchronous resolution using libunbound";
1091 info->description = "This test performs the following:\n"
1092 "\t* Attempt a lookup of a non-existent domain\n"
1093 "\t* Attempt a lookup of a AAAA record on a domain that contains only A records\n"
1094 "\t* Attempt a lookup of an A record on Chaos-net";
1095 return AST_TEST_NOT_RUN;
1096 case TEST_EXECUTE:
1097 break;
1098 }
1099
1100 return off_nominal_test(test, off_nominal_sync_run);
1101}
1102
1103AST_TEST_DEFINE(resolve_async_off_nominal)
1104{
1105 switch (cmd) {
1106 case TEST_INIT:
1107 info->name = "resolve_async_off_nominal";
1108 info->category = "/res/res_resolver_unbound/";
1109 info->summary = "Test off-nominal synchronous resolution using libunbound";
1110 info->description = "This test performs the following:\n"
1111 "\t* Attempt a lookup of a non-existent domain\n"
1112 "\t* Attempt a lookup of a AAAA record on a domain that contains only A records\n"
1113 "\t* Attempt a lookup of an A record on Chaos-net";
1114 return AST_TEST_NOT_RUN;
1115 case TEST_EXECUTE:
1116 break;
1117 }
1118
1119 return off_nominal_test(test, off_nominal_async_run);
1120}
1121
1122/*!
1123 * \brief Minimal data required to signal the completion of an async resolve
1124 */
1125struct async_minimal_data {
1126 int complete;
1129};
1130
1131static void async_minimal_data_destructor(void *obj)
1132{
1133 struct async_minimal_data *adata = obj;
1134
1135 ast_mutex_destroy(&adata->lock);
1136 ast_cond_destroy(&adata->cond);
1137}
1138
1139static struct async_minimal_data *async_minimal_data_alloc(void)
1140{
1141 struct async_minimal_data *adata;
1142
1143 adata = ao2_alloc(sizeof(*adata), async_minimal_data_destructor);
1144 if (!adata) {
1145 return NULL;
1146 }
1147
1148 ast_mutex_init(&adata->lock);
1149 ast_cond_init(&adata->cond, NULL);
1150
1151 return adata;
1152}
1153
1154/*!
1155 * \brief Async callback for off-nominal cancellation test.
1156 *
1157 * This simply signals the testing thread that the query completed
1158 */
1159static void minimal_callback(const struct ast_dns_query *query)
1160{
1161 struct async_minimal_data *adata = ast_dns_query_get_data(query);
1162
1163 ast_mutex_lock(&adata->lock);
1164 adata->complete = 1;
1165 ast_cond_signal(&adata->cond);
1166 ast_mutex_unlock(&adata->lock);
1167}
1168
1169AST_TEST_DEFINE(resolve_cancel_off_nominal)
1170{
1171 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
1172 RAII_VAR(struct async_minimal_data *, adata, NULL, ao2_cleanup);
1173
1174 switch (cmd) {
1175 case TEST_INIT:
1176 info->name = "resolve_cancel_off_nominal";
1177 info->category = "/res/res_resolver_unbound/";
1178 info->summary = "Off nominal cancellation test using libunbound";
1179 info->description = "This test does the following:\n"
1180 "\t* Perform an asynchronous query\n"
1181 "\t* Once the query has completed, attempt to cancel it";
1182 return AST_TEST_NOT_RUN;
1183 case TEST_EXECUTE:
1184 break;
1185 }
1186
1187 adata = async_minimal_data_alloc();
1188 if (!adata) {
1189 ast_test_status_update(test, "Failed to allocate necessary data for test\n");
1190 return AST_TEST_FAIL;
1191 }
1192
1193 active = ast_dns_resolve_async("crunchy.peanut.butter", ns_t_a, ns_c_in, minimal_callback, adata);
1194 if (!active) {
1195 ast_test_status_update(test, "Failed to perform asynchronous query\n");
1196 return AST_TEST_FAIL;
1197 }
1198
1199 /* Wait for async query to complete */
1200 ast_mutex_lock(&adata->lock);
1201 while (!adata->complete) {
1202 ast_cond_wait(&adata->cond, &adata->lock);
1203 }
1204 ast_mutex_unlock(&adata->lock);
1205
1206 if (!ast_dns_resolve_cancel(active)) {
1207 ast_test_status_update(test, "Successfully canceled completed query\n");
1208 return AST_TEST_FAIL;
1209 }
1210
1211 return AST_TEST_PASS;
1212}
1213
1214AST_TEST_DEFINE(resolve_naptr)
1215{
1216 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1217 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1219
1220 const struct ast_dns_record *record;
1221
1222 static char * DOMAIN1 = "goose.feathers";
1223 int i;
1225
1226 struct naptr_record {
1227 const char *zone_entry;
1228 uint16_t order;
1229 uint16_t preference;
1230 const char *flags;
1231 const char *services;
1232 const char *regexp;
1233 const char *replacement;
1234 int visited;
1235 } records [] = {
1236 { "goose.feathers 12345 IN NAPTR 100 100 A SIP+D2U \"\" goose.down", 100, 100, "A", "SIP+D2U", "", "goose.down", 0},
1237 { "goose.feathers 12345 IN NAPTR 100 200 A SIP+D2T \"\" duck.down", 100, 200, "A", "SIP+D2T", "", "duck.down", 0},
1238 { "goose.feathers 12345 IN NAPTR 200 100 A SIPS+D2U \"\" pheasant.down", 200, 100, "A", "SIPS+D2U", "", "pheasant.down", 0},
1239 { "goose.feathers 12345 IN NAPTR 200 200 A SIPS+D2T \"\" platypus.fur", 200, 200, "A", "SIPS+D2T", "", "platypus.fur", 0},
1240 };
1241
1242 switch (cmd) {
1243 case TEST_INIT:
1244 info->name = "resolve_naptr";
1245 info->category = "/res/res_resolver_unbound/";
1246 info->summary = "Attempt resolution of NAPTR record";
1247 info->description = "This test performs a NAPTR lookup and ensures that\n"
1248 "the returned record has the appropriate values set";
1249 return AST_TEST_NOT_RUN;
1250 case TEST_EXECUTE:
1251 break;
1252 }
1253
1255 resolver = ao2_bump(cfg->global->state->resolver);
1256
1257 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1258
1259 for (i = 0; i < ARRAY_LEN(records); ++i) {
1260 ub_ctx_data_add(resolver->context, records[i].zone_entry);
1261 }
1262
1263 if (ast_dns_resolve(DOMAIN1, ns_t_naptr, ns_c_in, &result)) {
1264 ast_test_status_update(test, "Failed to resolve domain\n");
1265 return AST_TEST_FAIL;
1266 }
1267
1268 if (!result) {
1269 ast_test_status_update(test, "Successful resolution set a NULL result\n");
1270 return AST_TEST_FAIL;
1271 }
1272
1274 if (!record) {
1275 ast_test_status_update(test, "Failed to get any DNS records from the result\n");
1276 return AST_TEST_FAIL;
1277 }
1278
1279 i = 0;
1280 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
1281 if (ast_dns_naptr_get_order(record) != records[i].order) {
1282 ast_test_status_update(test, "Expected order %hu, got order %hu from NAPTR record\n",
1284 res = AST_TEST_FAIL;
1285 }
1286 if (ast_dns_naptr_get_preference(record) != records[i].preference) {
1287 ast_test_status_update(test, "Expected preference %hu, got preference %hu from NAPTR record\n",
1288 records[i].preference, ast_dns_naptr_get_preference(record));
1289 res = AST_TEST_FAIL;
1290 }
1291 if (strcmp(ast_dns_naptr_get_flags(record), records[i].flags)) {
1292 ast_test_status_update(test, "Expected flags %s, got flags %s from NAPTR record\n",
1293 records[i].flags, ast_dns_naptr_get_flags(record));
1294 res = AST_TEST_FAIL;
1295 }
1296 if (strcmp(ast_dns_naptr_get_service(record), records[i].services)) {
1297 ast_test_status_update(test, "Expected services %s, got services %s from NAPTR record\n",
1298 records[i].services, ast_dns_naptr_get_service(record));
1299 res = AST_TEST_FAIL;
1300 }
1301 if (strcmp(ast_dns_naptr_get_regexp(record), records[i].regexp)) {
1302 ast_test_status_update(test, "Expected regexp %s, got regexp %s from NAPTR record\n",
1303 records[i].regexp, ast_dns_naptr_get_regexp(record));
1304 res = AST_TEST_FAIL;
1305 }
1306 if (strcmp(ast_dns_naptr_get_replacement(record), records[i].replacement)) {
1307 ast_test_status_update(test, "Expected replacement %s, got replacement %s from NAPTR record\n",
1308 records[i].replacement, ast_dns_naptr_get_replacement(record));
1309 res = AST_TEST_FAIL;
1310 }
1311 records[i].visited = 1;
1312 ++i;
1313 }
1314
1315 if (i != ARRAY_LEN(records)) {
1316 ast_test_status_update(test, "Unexpected number of records visited\n");
1317 res = AST_TEST_FAIL;
1318 }
1319
1320 for (i = 0; i < ARRAY_LEN(records); ++i) {
1321 if (!records[i].visited) {
1322 ast_test_status_update(test, "Did not visit all expected NAPTR records\n");
1323 res = AST_TEST_FAIL;
1324 }
1325 }
1326
1327 return res;
1328
1329}
1330
1331AST_TEST_DEFINE(resolve_srv)
1332{
1333 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1334 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1336 const struct ast_dns_record *record;
1337 static const char *DOMAIN1 = "taco.bananas";
1338 static const char *DOMAIN1_SRV = "taco.bananas 12345 IN SRV 10 20 5060 sip.taco.bananas";
1340
1341 switch (cmd) {
1342 case TEST_INIT:
1343 info->name = "resolve_srv";
1344 info->category = "/res/res_resolver_unbound/";
1345 info->summary = "Test synchronous SRV resolution using libunbound";
1346 info->description = "This test performs the following:\n"
1347 "\t* Set one SRV record on one domain\n"
1348 "\t* Perform an SRV lookup on the domain\n"
1349 "\t* Ensure that the SRV record returned matches the expected value";
1350 return AST_TEST_NOT_RUN;
1351 case TEST_EXECUTE:
1352 break;
1353 }
1354
1356 resolver = ao2_bump(cfg->global->state->resolver);
1357
1358 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1359 ub_ctx_data_add(resolver->context, DOMAIN1_SRV);
1360
1361 if (ast_dns_resolve(DOMAIN1, ns_t_srv, ns_c_in, &result)) {
1362 ast_test_status_update(test, "Failed to synchronously resolve SRV for domain '%s'\n", DOMAIN1);
1363 res = AST_TEST_FAIL;
1364 goto cleanup;
1365 }
1366
1368 if (ast_dns_srv_get_priority(record) != 10) {
1369 ast_test_status_update(test, "SRV Record returned priority '%d' when we expected 10\n", ast_dns_srv_get_priority(record));
1370 res = AST_TEST_FAIL;
1371 goto cleanup;
1372 }
1373
1374 if (ast_dns_srv_get_weight(record) != 20) {
1375 ast_test_status_update(test, "SRV Record returned weight '%d' when we expected 20\n", ast_dns_srv_get_weight(record));
1376 res = AST_TEST_FAIL;
1377 goto cleanup;
1378 }
1379
1380 if (ast_dns_srv_get_port(record) != 5060) {
1381 ast_test_status_update(test, "SRV Record returned port '%d' when we expected 5060\n", ast_dns_srv_get_port(record));
1382 res = AST_TEST_FAIL;
1383 goto cleanup;
1384 }
1385
1386 if (strcmp(ast_dns_srv_get_host(record), "sip.taco.bananas")) {
1387 ast_test_status_update(test, "SRV Record returned host '%s' when we expected sip.taco.bananas\n", ast_dns_srv_get_host(record));
1388 res = AST_TEST_FAIL;
1389 goto cleanup;
1390 }
1391
1392cleanup:
1393 ub_ctx_data_remove(resolver->context, DOMAIN1_SRV);
1394 ub_ctx_zone_remove(resolver->context, DOMAIN1);
1395
1396 return res;
1397}
1398#endif
1399
1400static int reload_module(void)
1401{
1402 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1404 }
1405
1406 return 0;
1407}
1408
1409static int unload_module(void)
1410{
1411 aco_info_destroy(&cfg_info);
1413
1414 AST_TEST_UNREGISTER(resolve_sync);
1415 AST_TEST_UNREGISTER(resolve_async);
1416 AST_TEST_UNREGISTER(resolve_sync_off_nominal);
1417 AST_TEST_UNREGISTER(resolve_sync_off_nominal);
1418 AST_TEST_UNREGISTER(resolve_cancel_off_nominal);
1419 AST_TEST_UNREGISTER(resolve_naptr);
1420 AST_TEST_UNREGISTER(resolve_srv);
1421 return 0;
1422}
1423
1424static int custom_nameserver_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1425{
1426 struct unbound_global_config *global = obj;
1427
1428 if (!global->nameservers) {
1430 if (!global->nameservers) {
1431 return -1;
1432 }
1433 }
1434
1435 return ast_str_container_add(global->nameservers, var->value);
1436}
1437
1438static int load_module(void)
1439{
1440 struct ast_config *cfg;
1441 struct ast_flags cfg_flags = { 0, };
1442
1443 if (aco_info_init(&cfg_info)) {
1445 }
1446
1447 aco_option_register(&cfg_info, "hosts", ACO_EXACT, global_options, "system", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, hosts));
1448 aco_option_register(&cfg_info, "resolv", ACO_EXACT, global_options, "system", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, resolv));
1451 aco_option_register(&cfg_info, "ta_file", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, ta_file));
1452
1453 /* This purposely checks for a configuration file so we don't output an error message in ACO if one is not present */
1455 if (!cfg) {
1457 unload_module();
1459 }
1460 } else {
1461 ast_config_destroy(cfg);
1462 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1463 unload_module();
1465 }
1466 }
1467
1469
1471
1472 AST_TEST_REGISTER(resolve_sync);
1473 AST_TEST_REGISTER(resolve_async);
1474 AST_TEST_REGISTER(resolve_sync_off_nominal);
1475 AST_TEST_REGISTER(resolve_async_off_nominal);
1476 AST_TEST_REGISTER(resolve_cancel_off_nominal);
1477 AST_TEST_REGISTER(resolve_naptr);
1478 AST_TEST_REGISTER(resolve_srv);
1479
1481}
1482
1483AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Unbound DNS Resolver Support",
1484 .support_level = AST_MODULE_SUPPORT_CORE,
1485 .load = load_module,
1486 .unload = unload_module,
1488 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
integer order
Definition: analys.c:66
pthread_t thread
Definition: app_sla.c:329
ast_cond_t cond
Definition: app_sla.c:330
ast_mutex_t lock
Definition: app_sla.c:331
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
Definition: astobj2.h:901
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:404
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
static int records
Definition: cdr_pgsql.c:78
static PGresult * result
Definition: cel_pgsql.c:84
static struct console_pvt globals
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2362
Configuration option-handling.
@ ACO_EXACT
int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
Set all default options of obj.
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
@ ACO_PROCESS_ERROR
Their was an error and no changes were applied.
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
#define ACO_FILES(...)
@ OPT_UINT_T
Type for default option handler for unsigned integers.
@ OPT_STRINGFIELD_T
Type for default option handler for stringfields.
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
@ ACO_GLOBAL
@ ACO_WHITELIST_EXACT
void * aco_pending_config(struct aco_info *info)
Get pending config changes.
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
Core DNS API.
int ast_dns_query_get_rr_type(const struct ast_dns_query *query)
Get the record resource type of a DNS query.
Definition: dns_core.c:62
int ast_dns_record_get_rr_class(const struct ast_dns_record *record)
Get the resource record class of a DNS record.
Definition: dns_core.c:150
int ast_dns_record_get_ttl(const struct ast_dns_record *record)
Get the TTL of a DNS record.
Definition: dns_core.c:155
const struct ast_dns_record * ast_dns_record_get_next(const struct ast_dns_record *record)
Get the next DNS record.
Definition: dns_core.c:170
struct ast_dns_query_active * ast_dns_resolve_async(const char *name, int rr_type, int rr_class, ast_dns_resolve_callback callback, void *data)
Asynchronously resolve a DNS query.
Definition: dns_core.c:247
int ast_dns_resolve_cancel(struct ast_dns_query_active *active)
Cancel an asynchronous DNS resolution.
Definition: dns_core.c:272
int ast_dns_query_get_rr_class(const struct ast_dns_query *query)
Get the record resource class of a DNS query.
Definition: dns_core.c:67
unsigned int ast_dns_result_get_rcode(const struct ast_dns_result *result)
Get the error rcode of a DN result.
Definition: dns_core.c:92
const char * ast_dns_record_get_data(const struct ast_dns_record *record)
Retrieve the raw DNS record.
Definition: dns_core.c:160
const struct ast_dns_record * ast_dns_result_get_records(const struct ast_dns_result *result)
Get the first record of a DNS Result.
Definition: dns_core.c:102
void * ast_dns_query_get_data(const struct ast_dns_query *query)
Get the user specific data of a DNS query.
Definition: dns_core.c:72
void ast_dns_result_free(struct ast_dns_result *result)
Free the DNS result information.
Definition: dns_core.c:130
struct ast_dns_result * ast_dns_query_get_result(const struct ast_dns_query *query)
Get the result information for a DNS query.
Definition: dns_core.c:77
int ast_dns_record_get_rr_type(const struct ast_dns_record *record)
Get the resource record type of a DNS record.
Definition: dns_core.c:145
int ast_dns_resolve(const char *name, int rr_type, int rr_class, struct ast_dns_result **result)
Synchronously resolve a DNS query.
Definition: dns_core.c:314
const char * ast_dns_query_get_name(const struct ast_dns_query *query)
Get the name queried in a DNS query.
Definition: dns_core.c:57
DNS NAPTR Record Parsing API.
const char * ast_dns_naptr_get_replacement(const struct ast_dns_record *record)
Get the replacement value from a NAPTR record.
Definition: dns_naptr.c:624
unsigned short ast_dns_naptr_get_preference(const struct ast_dns_record *record)
Get the preference from a NAPTR record.
Definition: dns_naptr.c:640
const char * ast_dns_naptr_get_flags(const struct ast_dns_record *record)
Get the flags from a NAPTR record.
Definition: dns_naptr.c:600
const char * ast_dns_naptr_get_service(const struct ast_dns_record *record)
Get the service from a NAPTR record.
Definition: dns_naptr.c:608
const char * ast_dns_naptr_get_regexp(const struct ast_dns_record *record)
Get the regular expression from a NAPTR record.
Definition: dns_naptr.c:616
unsigned short ast_dns_naptr_get_order(const struct ast_dns_record *record)
Get the order from a NAPTR record.
Definition: dns_naptr.c:632
DNS Resolver API.
int ast_dns_resolver_set_data(struct ast_dns_query *query, void *data)
Set resolver specific data on a query.
Definition: dns_core.c:440
int ast_dns_resolver_set_result(struct ast_dns_query *query, unsigned int secure, unsigned int bogus, unsigned int rcode, const char *canonical, const char *answer, size_t answer_size)
Set result information for a DNS query.
Definition: dns_core.c:456
void ast_dns_resolver_completed(struct ast_dns_query *query)
Mark a DNS query as having been completed.
Definition: dns_core.c:599
void * ast_dns_resolver_get_data(const struct ast_dns_query *query)
Retrieve resolver specific data.
Definition: dns_core.c:451
int ast_dns_resolver_register(struct ast_dns_resolver *resolver)
Register a DNS resolver.
Definition: dns_core.c:632
int ast_dns_resolver_add_record(struct ast_dns_query *query, int rr_type, int rr_class, int ttl, const char *data, const size_t size)
Add a DNS record to the result of a DNS query.
Definition: dns_core.c:535
DNS SRV Record Parsing API.
const char * ast_dns_srv_get_host(const struct ast_dns_record *record)
Get the hostname from an SRV record.
Definition: dns_srv.c:188
unsigned short ast_dns_srv_get_priority(const struct ast_dns_record *record)
Get the priority from an SRV record.
Definition: dns_srv.c:196
unsigned short ast_dns_srv_get_weight(const struct ast_dns_record *record)
Get the weight from an SRV record.
Definition: dns_srv.c:204
unsigned short ast_dns_srv_get_port(const struct ast_dns_record *record)
Get the port from an SRV record.
Definition: dns_srv.c:212
char * end
Definition: eagi_proxy.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
A set of macros to manage forward-linked lists.
#define ast_cond_destroy(cond)
Definition: lock.h:202
#define ast_cond_wait(cond, mutex)
Definition: lock.h:205
#define AST_PTHREADT_NULL
Definition: lock.h:66
#define ast_cond_init(cond, attr)
Definition: lock.h:201
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define AST_PTHREADT_STOP
Definition: lock.h:67
#define ast_mutex_unlock(a)
Definition: lock.h:190
pthread_cond_t ast_cond_t
Definition: lock.h:178
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
#define ast_cond_signal(cond)
Definition: lock.h:203
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:478
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_RELOAD_ERROR
Definition: module.h:113
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
def info(msg)
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
static int reload(void)
static struct aco_type * global_options[]
static struct unbound_resolver * unbound_resolver_alloc(void)
Allocator for unbound resolver.
static void unbound_resolver_callback(void *data, int err, struct ub_result *ub_result)
Callback invoked when resolution completes on a query.
static int unbound_resolver_cancel(struct ast_dns_query *query)
static void unbound_resolver_destroy(void *obj)
Destructor for unbound resolver.
static void * unbound_resolver_thread(void *data)
Resolver thread which waits and handles results.
static void * unbound_config_alloc(void)
Allocate a unbound_config to hold a snapshot of the complete results of parsing a config.
static AO2_GLOBAL_OBJ_STATIC(globals)
A global object container that will contain the global_config that gets swapped out on reloads.
static struct aco_file resolver_unbound_conf
static int reload_module(void)
static int unbound_config_preapply(struct unbound_config *cfg)
static int unbound_config_preapply_callback(void)
Finish initializing new configuration.
static int unbound_config_apply_default(void)
static void unbound_config_destructor(void *obj)
static void unbound_resolver_data_dtor(void *vdoomed)
static int custom_nameserver_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static void unbound_global_config_destructor(void *obj)
static int unbound_resolver_start(struct unbound_resolver *resolver)
Start function for the unbound resolver.
static int load_module(void)
static void unbound_resolver_stop(struct unbound_resolver *resolver)
Stop function for the unbound resolver.
static int unload_module(void)
CONFIG_INFO_STANDARD(cfg_info, globals, unbound_config_alloc,.files=ACO_FILES(&resolver_unbound_conf),.pre_apply_config=unbound_config_preapply_callback,)
Register information about the configs being processed by this module.
static struct aco_type global_option
An aco_type structure to link the "general" category to the unbound_global_config type.
static int unbound_resolver_resolve(struct ast_dns_query *query)
static void unbound_config_state_destructor(void *obj)
static int debug
Global debug status.
Definition: res_xmpp.c:441
#define NULL
Definition: resample.c:96
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
struct ao2_container * ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
Allocates a hash container for bare strings.
Definition: strings.c:200
int ast_str_container_add(struct ao2_container *str_container, const char *add)
Adds a string to a string container allocated by ast_str_container_alloc.
Definition: strings.c:205
The representation of a single configuration file to be processed.
const char * filename
Type information about a category-level configurable object.
enum aco_type_t type
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
An active DNS query.
Definition: dns_internal.h:201
A DNS query.
Definition: dns_internal.h:137
For AST_LIST.
Definition: dns_internal.h:39
int rr_class
Resource record class.
Definition: dns_internal.h:43
int rr_type
Resource record type.
Definition: dns_internal.h:41
DNS resolver implementation.
Definition: dns_resolver.h:32
The result of a DNS query.
Definition: dns_internal.h:117
Structure used to handle boolean flags.
Definition: utils.h:199
struct ast_module * self
Definition: module.h:356
Structure for mutex and tracking information.
Definition: lock.h:135
Structure for variables, used for configurations and for channel variables.
struct ast_dns_test_string services
uint16_t preference
struct ast_dns_test_string flags
struct ast_dns_test_string regexp
const char * replacement
uint16_t order
Unbound configuration state information.
struct unbound_resolver * resolver
The configured resolver.
A container for config related information.
struct unbound_global_config * global
A structure to hold global configuration-related options.
struct unbound_config_state * state
State information.
const ast_string_field resolv
unsigned int debug
Debug level for the resolver.
struct ao2_container * nameservers
List of nameservers (in order) to use for queries.
const ast_string_field hosts
const ast_string_field ta_file
Structure for query resolver data.
int id
ID for the specific query.
struct unbound_resolver * resolver
The resolver in use for the query.
Structure for an unbound resolver.
pthread_t thread
Thread handling the resolver.
struct ub_ctx * context
Resolver context itself.
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
static struct aco_type global
Definition: test_config.c:1445
static struct async_resolution_data * async_data_alloc(void)
Allocation/initialization for async_resolution_data.
Definition: test_dns.c:983
static void async_data_destructor(void *obj)
Destructor for async_resolution_data.
Definition: test_dns.c:966
static void async_callback(const struct ast_dns_query *query)
Async DNS callback.
Definition: test_dns.c:1008
static enum ast_test_result_state off_nominal_test(struct ast_test *test, struct naptr_record *records, int num_records)
static enum ast_test_result_state nominal_test(struct ast_test *test, struct srv_record *records, int *srv_record_order, int num_records)
Definition: test_dns_srv.c:125
int error(const char *format,...)
Definition: utils/frame.c:999
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:584
int ast_wait_for_input(int fd, int ms)
Definition: utils.c:1698
#define ARRAY_LEN(a)
Definition: utils.h:666