Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
47 <version>14.1.0</version>
48 </since>
49 <synopsis>General options for res_resolver_unbound</synopsis>
50 <configOption name="hosts">
51 <since>
52 <version>14.0.0</version>
53 </since>
54 <synopsis>Full path to an optional hosts file</synopsis>
55 <description><para>Hosts specified in a hosts file will be resolved within the resolver itself. If a value
56 of system is provided the system-specific file will be used.</para></description>
57 </configOption>
58 <configOption name="resolv">
59 <since>
60 <version>14.0.0</version>
61 </since>
62 <synopsis>Full path to an optional resolv.conf file</synopsis>
63 <description><para>The resolv.conf file specifies the nameservers to contact when resolving queries. If a
64 value of system is provided the system-specific file will be used. If provided alongside explicit nameservers the
65 nameservers contained within the resolv.conf file will be used after all others.</para></description>
66 </configOption>
67 <configOption name="nameserver">
68 <since>
69 <version>14.0.0</version>
70 </since>
71 <synopsis>Nameserver to use for queries</synopsis>
72 <description><para>An explicit nameserver can be specified which is used for resolving queries. If multiple
73 nameserver lines are specified the first will be the primary with failover occurring, in order, to the other
74 nameservers as backups. If provided alongside a resolv.conf file the nameservers explicitly specified will be
75 used before all others.</para></description>
76 </configOption>
77 <configOption name="debug">
78 <since>
79 <version>14.0.0</version>
80 </since>
81 <synopsis>Unbound debug level</synopsis>
82 <description><para>The debugging level for the unbound resolver. While there is no explicit range generally
83 the higher the number the more debug is output.</para></description>
84 </configOption>
85 <configOption name="ta_file">
86 <since>
87 <version>14.0.0</version>
88 </since>
89 <synopsis>Trust anchor file</synopsis>
90 <description><para>Full path to a file with DS and DNSKEY records in zone file format. This file is provided
91 to unbound and is used as a source for trust anchors.</para></description>
92 </configOption>
93 </configObject>
94 </configFile>
95 </configInfo>
96 ***/
97
98/*! \brief Structure for an unbound resolver */
100 /*! \brief Resolver context itself */
101 struct ub_ctx *context;
102 /*! \brief Thread handling the resolver */
103 pthread_t thread;
104};
105
106/*! \brief Structure for query resolver data */
108 /*! \brief ID for the specific query */
109 int id;
110 /*! \brief The resolver in use for the query */
112};
113
114/*! \brief Unbound configuration state information */
116 /*! \brief The configured resolver */
118};
119
120/*! \brief A structure to hold global configuration-related options */
123 AST_STRING_FIELD(hosts); /*!< Optional hosts file */
124 AST_STRING_FIELD(resolv); /*!< Optional resolv.conf file */
125 AST_STRING_FIELD(ta_file); /*!< Optional trust anchor file */
126 );
127 /*! \brief List of nameservers (in order) to use for queries */
129 /*! \brief Debug level for the resolver */
130 unsigned int debug;
131 /*! \brief State information */
133};
134
135/*! \brief A container for config related information */
138};
139
140/*!
141 * \brief Allocate a unbound_config to hold a snapshot of the complete results of parsing a config
142 * \internal
143 * \returns A void pointer to a newly allocated unbound_config
144 */
145static void *unbound_config_alloc(void);
146
147/*! \brief An aco_type structure to link the "general" category to the unbound_global_config type */
148static struct aco_type global_option = {
149 .type = ACO_GLOBAL,
150 .name = "general",
151 .item_offset = offsetof(struct unbound_config, global),
152 .category_match = ACO_WHITELIST_EXACT,
153 .category = "general",
154};
155
157
159 .filename = "resolver_unbound.conf",
160 .types = ACO_TYPES(&global_option),
161};
162
163/*! \brief A global object container that will contain the global_config that gets swapped out on reloads */
165
166/*!
167 * \brief Finish initializing new configuration
168 * \internal
169 */
170static int unbound_config_preapply_callback(void);
171
172/*! \brief Register information about the configs being processed by this module */
175 .pre_apply_config = unbound_config_preapply_callback,
176);
177
178/*! \brief Destructor for unbound resolver */
179static void unbound_resolver_destroy(void *obj)
180{
181 struct unbound_resolver *resolver = obj;
182
183 if (resolver->context) {
184 ub_ctx_delete(resolver->context);
185 }
186}
187
188/*! \brief Allocator for unbound resolver */
190{
191 struct unbound_resolver *resolver;
192
194 if (!resolver) {
195 return NULL;
196 }
197
198 resolver->thread = AST_PTHREADT_NULL;
199
200 resolver->context = ub_ctx_create();
201 if (!resolver->context) {
202 ao2_ref(resolver, -1);
203 return NULL;
204 }
205
206 /* Each async result should be invoked in a separate thread so others are not blocked */
207 ub_ctx_async(resolver->context, 1);
208
209 return resolver;
210}
211
212/*! \brief Resolver thread which waits and handles results */
213static void *unbound_resolver_thread(void *data)
214{
215 struct unbound_resolver *resolver = data;
216
217 ast_debug(1, "Starting processing for unbound resolver\n");
218
219 while (resolver->thread != AST_PTHREADT_STOP) {
220 /* Wait for any results to come in */
221 ast_wait_for_input(ub_fd(resolver->context), -1);
222
223 /* Finally process any results */
224 ub_process(resolver->context);
225 }
226
227 ast_debug(1, "Terminating processing for unbound resolver\n");
228
229 ao2_ref(resolver, -1);
230
231 return NULL;
232}
233
234/*! \brief Start function for the unbound resolver */
235static int unbound_resolver_start(struct unbound_resolver *resolver)
236{
237 int res;
238
239 if (resolver->thread != AST_PTHREADT_NULL) {
240 return 0;
241 }
242
243 ast_debug(1, "Starting thread for unbound resolver\n");
244
245 res = ast_pthread_create(&resolver->thread, NULL, unbound_resolver_thread, ao2_bump(resolver));
246 if (res) {
247 ast_debug(1, "Could not start thread for unbound resolver\n");
248 ao2_ref(resolver, -1);
249 }
250
251 return res;
252}
253
254/*! \brief Stop function for the unbound resolver */
255static void unbound_resolver_stop(struct unbound_resolver *resolver)
256{
257 pthread_t thread;
258
259 if (resolver->thread == AST_PTHREADT_NULL) {
260 return;
261 }
262
263 ast_debug(1, "Stopping processing thread for unbound resolver\n");
264
265 thread = resolver->thread;
266 resolver->thread = AST_PTHREADT_STOP;
267 pthread_kill(thread, SIGURG);
268 pthread_join(thread, NULL);
269
270 ast_debug(1, "Stopped processing thread for unbound resolver\n");
271}
272
273/*! \brief Callback invoked when resolution completes on a query */
274static void unbound_resolver_callback(void *data, int err, struct ub_result *ub_result)
275{
276 struct ast_dns_query *query = data;
277
278 if (!ub_result) {
279 ast_debug(3, "Badly formatted DNS query '%s'\n", ast_dns_query_get_name(query));
280 ast_dns_resolver_set_result(query, 0, 0, ns_r_formerr, ast_dns_query_get_name(query), "", 0);
282 ao2_ref(query, -1);
283 return;
284 }
285
286 if (!ast_dns_resolver_set_result(query, ub_result->secure, ub_result->bogus, ub_result->rcode,
287 S_OR(ub_result->canonname, ast_dns_query_get_name(query)), ub_result->answer_packet, ub_result->answer_len)) {
288 int i;
289 char *result_data;
290
291 for (i = 0; (result_data = ub_result->data[i]); i++) {
292 if (ast_dns_resolver_add_record(query, ub_result->qtype, ub_result->qclass, ub_result->ttl,
293 result_data, ub_result->len[i])) {
294 break;
295 }
296 }
297 }
298
300 ao2_ref(query, -1);
301 ub_resolve_free(ub_result);
302}
303
304static void unbound_resolver_data_dtor(void *vdoomed)
305{
306 struct unbound_resolver_data *doomed = vdoomed;
307
308 ao2_cleanup(doomed->resolver);
309}
310
312{
314 struct unbound_resolver_data *data;
315 int res;
316
317 data = ao2_alloc_options(sizeof(*data), unbound_resolver_data_dtor,
319 if (!data) {
320 ast_log(LOG_ERROR, "Failed to allocate resolver data for resolution of '%s'\n",
322 return -1;
323 }
324 data->resolver = ao2_bump(cfg->global->state->resolver);
325 ast_dns_resolver_set_data(query, data);
326
327 res = ub_resolve_async(data->resolver->context, ast_dns_query_get_name(query),
329 ao2_bump(query), unbound_resolver_callback, &data->id);
330
331 if (res) {
332 ast_log(LOG_ERROR, "Failed to perform async DNS resolution of '%s'\n",
334 ao2_ref(query, -1);
335 }
336
337 ao2_ref(data, -1);
338 ao2_ref(cfg, -1);
339 return res;
340}
341
342static int unbound_resolver_cancel(struct ast_dns_query *query)
343{
345 int res;
346
347 res = ub_cancel(data->resolver->context, data->id);
348 if (!res) {
349 /* When this query was started we bumped the ref, now that it has been cancelled we have ownership and
350 * need to drop it
351 */
352 ao2_ref(query, -1);
353 }
354
355 return res;
356}
357
359 .name = "unbound",
360 .priority = 100,
361 .resolve = unbound_resolver_resolve,
362 .cancel = unbound_resolver_cancel,
363};
364
365static void unbound_config_destructor(void *obj)
366{
367 struct unbound_config *cfg = obj;
368
369 ao2_cleanup(cfg->global);
370}
371
373{
374 struct unbound_global_config *global = obj;
375
377 ao2_cleanup(global->nameservers);
378 ao2_cleanup(global->state);
379}
380
382{
383 struct unbound_config_state *state = obj;
384
385 if (state->resolver) {
386 unbound_resolver_stop(state->resolver);
387 ao2_ref(state->resolver, -1);
388 }
389}
390
391static void *unbound_config_alloc(void)
392{
393 struct unbound_config *cfg;
394
396 if (!cfg) {
397 return NULL;
398 }
399
400 /* Allocate/initialize memory */
403 if (!cfg->global) {
404 goto error;
405 }
406
407 if (ast_string_field_init(cfg->global, 128)) {
408 goto error;
409 }
410
411 return cfg;
412error:
413 ao2_ref(cfg, -1);
414 return NULL;
415}
416
418{
419 int res = 0;
420
423 if (!cfg->global->state) {
424 ast_log(LOG_ERROR, "Could not allocate unbound resolver state structure\n");
425 return -1;
426 }
427
429 if (!cfg->global->state->resolver) {
430 ast_log(LOG_ERROR, "Could not create an unbound resolver\n");
431 return -1;
432 }
433
434 ub_ctx_debuglevel(cfg->global->state->resolver->context, cfg->global->debug);
435
436 if (!strcmp(cfg->global->hosts, "system")) {
437 res = ub_ctx_hosts(cfg->global->state->resolver->context, NULL);
438 } else if (!ast_strlen_zero(cfg->global->hosts)) {
439 res = ub_ctx_hosts(cfg->global->state->resolver->context, cfg->global->hosts);
440 }
441
442 if (res) {
443 ast_log(LOG_ERROR, "Failed to set hosts file to '%s' in unbound resolver: %s\n",
444 cfg->global->hosts, ub_strerror(res));
445 return -1;
446 }
447
448 if (cfg->global->nameservers) {
449 struct ao2_iterator it_nameservers;
450 char *nameserver;
451
452 it_nameservers = ao2_iterator_init(cfg->global->nameservers, 0);
453 while (!res && (nameserver = ao2_iterator_next(&it_nameservers))) {
454 res = ub_ctx_set_fwd(cfg->global->state->resolver->context, nameserver);
455
456 if (res) {
457 ast_log(LOG_ERROR, "Failed to add nameserver '%s' to unbound resolver: %s\n",
458 nameserver, ub_strerror(res));
459 }
460 ao2_ref(nameserver, -1);
461 }
462 ao2_iterator_destroy(&it_nameservers);
463 if (res) {
464 return -1;
465 }
466 }
467
468 if (!strcmp(cfg->global->resolv, "system")) {
469 res = ub_ctx_resolvconf(cfg->global->state->resolver->context, NULL);
470 } else if (!ast_strlen_zero(cfg->global->resolv)) {
471 res = ub_ctx_resolvconf(cfg->global->state->resolver->context, cfg->global->resolv);
472 }
473
474 if (res) {
475 ast_log(LOG_ERROR, "Failed to set resolv.conf file to '%s' in unbound resolver: %s\n",
476 cfg->global->resolv, ub_strerror(res));
477 return -1;
478 }
479
480 if (!ast_strlen_zero(cfg->global->ta_file)) {
481 res = ub_ctx_add_ta_file(cfg->global->state->resolver->context, cfg->global->ta_file);
482
483 if (res) {
484 ast_log(LOG_ERROR, "Failed to set trusted anchor file to '%s' in unbound resolver: %s\n",
485 cfg->global->ta_file, ub_strerror(res));
486 return -1;
487 }
488 }
489
491 ast_log(LOG_ERROR, "Could not start unbound resolver thread\n");
492 return -1;
493 }
494
495 return 0;
496}
497
499{
500 struct unbound_config *cfg;
501
502 cfg = unbound_config_alloc();
503 if (!cfg) {
504 ast_log(LOG_ERROR, "Could not create default configuration for unbound resolver\n");
505 return -1;
506 }
507
508 aco_set_defaults(&global_option, "general", cfg->global);
509
510 if (unbound_config_preapply(cfg)) {
511 ao2_ref(cfg, -1);
512 return -1;
513 }
514
515 ast_verb(1, "Starting unbound resolver using default configuration\n");
516
518 ao2_ref(cfg, -1);
519
520 return 0;
521}
522
524{
526}
527
528#ifdef TEST_FRAMEWORK
529
530#include "asterisk/dns_naptr.h"
531
532/*!
533 * \brief A DNS record to be used during a test
534 */
535struct dns_record {
536 /*! String representation of the record, as would be found in a file */
537 const char *as_string;
538 /*! The domain this record belongs to */
539 const char *domain;
540 /*! The type of the record */
541 int rr_type;
542 /*! The class of the record */
543 int rr_class;
544 /*! The TTL of the record, in seconds */
545 int ttl;
546 /*! The RDATA of the DNS record */
547 const char *buf;
548 /*! The size of the RDATA */
549 const size_t bufsize;
550 /*! Whether a record checker has visited this record */
551 int visited;
552};
553
554/*!
555 * \brief Resolution function for tests.
556 *
557 * Several tests will have similar setups but will want to make use of a different
558 * means of actually making queries and checking their results. This pluggable
559 * function pointer allows for similar tests to be operated in different ways.
560 *
561 * \param test The test being run
562 * \param domain The domain to look up
563 * \param rr_type The record type to look up
564 * \param rr_class The class of record to look up
565 * \param records All records that exist for the test.
566 * \param num_records Number of records in the records array.
567 *
568 * \retval 0 The test has passed thus far.
569 * \retval -1 The test has failed.
570 */
571typedef int (*resolve_fn)(struct ast_test *test, const char *domain, int rr_type,
572 int rr_class, struct dns_record *records, size_t num_records);
573
574/*!
575 * \brief Pluggable function for running a synchronous query and checking its results
576 */
577static int nominal_sync_run(struct ast_test *test, const char *domain, int rr_type,
578 int rr_class, struct dns_record *records, size_t num_records)
579{
581 const struct ast_dns_record *record;
582 int i;
583
584 /* Start by making sure no records have been visited */
585 for (i = 0; i < num_records; ++i) {
586 records[i].visited = 0;
587 }
588
589 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
590
591 if (ast_dns_resolve(domain, rr_type, rr_class, &result)) {
592 ast_test_status_update(test, "Failed to perform synchronous resolution of domain %s\n", domain);
593 return -1;
594 }
595
596 if (!result) {
597 ast_test_status_update(test, "Successful synchronous resolution of domain %s gave NULL result\n", domain);
598 return -1;
599 }
600
601 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
602 int match = 0;
603
604 /* Let's make sure this matches one of our known records */
605 for (i = 0; i < num_records; ++i) {
606 if (ast_dns_record_get_rr_type(record) == records[i].rr_type &&
607 ast_dns_record_get_rr_class(record) == records[i].rr_class &&
608 ast_dns_record_get_ttl(record) == records[i].ttl &&
609 !memcmp(ast_dns_record_get_data(record), records[i].buf, records[i].bufsize)) {
610 match = 1;
611 records[i].visited = 1;
612 break;
613 }
614 }
615
616 if (!match) {
617 ast_test_status_update(test, "Unknown DNS record returned from domain %s\n", domain);
618 return -1;
619 }
620 }
621
622 return 0;
623}
624
625/*!
626 * \brief Data required for an asynchronous callback
627 */
628struct async_data {
629 /*! The set of DNS records on a test */
630 struct dns_record *records;
631 /*! The number of DNS records on the test */
632 size_t num_records;
633 /*! Whether an asynchronous query failed */
634 int failed;
635 /*! Indicates the asynchronous query is complete */
636 int complete;
639};
640
641static void async_data_destructor(void *obj)
642{
643 struct async_data *adata = obj;
644
645 ast_mutex_destroy(&adata->lock);
646 ast_cond_destroy(&adata->cond);
647}
648
649static struct async_data *async_data_alloc(struct dns_record *records, size_t num_records)
650{
651 struct async_data *adata;
652
653 adata = ao2_alloc(sizeof(*adata), async_data_destructor);
654 if (!adata) {
655 return NULL;
656 }
657
658 ast_mutex_init(&adata->lock);
659 ast_cond_init(&adata->cond, NULL);
660 adata->records = records;
661 adata->num_records = num_records;
662
663 return adata;
664}
665
666/*!
667 * \brief Callback for asynchronous queries
668 *
669 * This query will check that the records in the DNS result match
670 * records that the test has created. The success or failure of the
671 * query is indicated through the async_data failed field.
672 *
673 * \param query The DNS query that has been resolved
674 */
675static void async_callback(const struct ast_dns_query *query)
676{
677 struct async_data *adata = ast_dns_query_get_data(query);
679 const struct ast_dns_record *record;
680 int i;
681
682 if (!result) {
683 adata->failed = -1;
684 goto end;
685 }
686
687 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
688 int match = 0;
689
690 /* Let's make sure this matches one of our known records */
691 for (i = 0; i < adata->num_records; ++i) {
692 if (ast_dns_record_get_rr_type(record) == adata->records[i].rr_type &&
693 ast_dns_record_get_rr_class(record) == adata->records[i].rr_class &&
694 ast_dns_record_get_ttl(record) == adata->records[i].ttl &&
695 !memcmp(ast_dns_record_get_data(record), adata->records[i].buf, adata->records[i].bufsize)) {
696 match = 1;
697 adata->records[i].visited = 1;
698 break;
699 }
700 }
701
702 if (!match) {
703 adata->failed = -1;
704 goto end;
705 }
706 }
707
708end:
709 ast_mutex_lock(&adata->lock);
710 adata->complete = 1;
711 ast_cond_signal(&adata->cond);
712 ast_mutex_unlock(&adata->lock);
713}
714
715/*!
716 * \brief Pluggable function for performing an asynchronous query during a test
717 *
718 * Unlike the synchronous version, this does not check the records, instead leaving
719 * that to be done in the asynchronous callback.
720 */
721static int nominal_async_run(struct ast_test *test, const char *domain, int rr_type,
722 int rr_class, struct dns_record *records, size_t num_records)
723{
724 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
725 RAII_VAR(struct async_data *, adata, NULL, ao2_cleanup);
726 int i;
727
728 adata = async_data_alloc(records, num_records);
729 if (!adata) {
730 ast_test_status_update(test, "Unable to allocate data for async query\n");
731 return -1;
732 }
733
734 /* Start by making sure no records have been visited */
735 for (i = 0; i < num_records; ++i) {
736 records[i].visited = 0;
737 }
738
739 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
740
741 active = ast_dns_resolve_async(domain, rr_type, rr_class, async_callback, adata);
742 if (!active) {
743 ast_test_status_update(test, "Failed to perform asynchronous resolution of domain %s\n", domain);
744 return -1;
745 }
746
747 ast_mutex_lock(&adata->lock);
748 while (!adata->complete) {
749 ast_cond_wait(&adata->cond, &adata->lock);
750 }
751 ast_mutex_unlock(&adata->lock);
752
753 if (adata->failed) {
754 ast_test_status_update(test, "Unknown DNS record returned from domain %s\n", domain);
755 }
756 return adata->failed;
757}
758
759/*!
760 * \brief Framework for running a nominal DNS test
761 *
762 * Synchronous and asynchronous tests mostly have the same setup, so this function
763 * serves as a common way to set up both types of tests by accepting a pluggable
764 * function to determine which type of lookup is used
765 *
766 * \param test The test being run
767 * \param runner The method for resolving queries on this test
768 */
769static enum ast_test_result_state nominal_test(struct ast_test *test, resolve_fn runner)
770{
771 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
772 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
773
774 static const size_t V4_SIZE = sizeof(struct in_addr);
775 static const size_t V6_SIZE = sizeof(struct in6_addr);
776
777 static const char *DOMAIN1 = "goose.feathers";
778 static const char *DOMAIN2 = "duck.feathers";
779
780 static const char *ADDR1 = "127.0.0.2";
781 static const char *ADDR2 = "127.0.0.3";
782 static const char *ADDR3 = "::1";
783 static const char *ADDR4 = "127.0.0.4";
784
785 char addr1_buf[V4_SIZE];
786 char addr2_buf[V4_SIZE];
787 char addr3_buf[V6_SIZE];
788 char addr4_buf[V4_SIZE];
789
790 struct dns_record records [] = {
791 { "goose.feathers 12345 IN A 127.0.0.2", DOMAIN1, ns_t_a, ns_c_in, 12345, addr1_buf, V4_SIZE, 0 },
792 { "goose.feathers 12345 IN A 127.0.0.3", DOMAIN1, ns_t_a, ns_c_in, 12345, addr2_buf, V4_SIZE, 0 },
793 { "goose.feathers 12345 IN AAAA ::1", DOMAIN1, ns_t_aaaa, ns_c_in, 12345, addr3_buf, V6_SIZE, 0 },
794 { "duck.feathers 12345 IN A 127.0.0.4", DOMAIN2, ns_t_a, ns_c_in, 12345, addr4_buf, V4_SIZE, 0 },
795 };
796
797 struct {
798 const char *domain;
799 int rr_type;
800 int rr_class;
801 int visited[ARRAY_LEN(records)];
802 } runs [] = {
803 { DOMAIN1, ns_t_a, ns_c_in, { 1, 1, 0, 0 } },
804 { DOMAIN1, ns_t_aaaa, ns_c_in, { 0, 0, 1, 0 } },
805 { DOMAIN2, ns_t_a, ns_c_in, { 0, 0, 0, 1 } },
806 };
807
808 int i;
810
811 inet_pton(AF_INET, ADDR1, addr1_buf);
812 inet_pton(AF_INET, ADDR2, addr2_buf);
813 inet_pton(AF_INET6, ADDR3, addr3_buf);
814 inet_pton(AF_INET, ADDR4, addr4_buf);
815
817 resolver = ao2_bump(cfg->global->state->resolver);
818
819 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
820 ub_ctx_zone_add(resolver->context, DOMAIN2, "static");
821
822 for (i = 0; i < ARRAY_LEN(records); ++i) {
823 ub_ctx_data_add(resolver->context, records[i].as_string);
824 }
825
826 for (i = 0; i < ARRAY_LEN(runs); ++i) {
827 int j;
828
829 if (runner(test, runs[i].domain, runs[i].rr_type, runs[i].rr_class, records, ARRAY_LEN(records))) {
830 res = AST_TEST_FAIL;
831 goto cleanup;
832 }
833
834 for (j = 0; j < ARRAY_LEN(records); ++j) {
835 if (records[j].visited != runs[i].visited[j]) {
836 ast_test_status_update(test, "DNS results match unexpected records\n");
837 res = AST_TEST_FAIL;
838 goto cleanup;
839 }
840 }
841 }
842
843cleanup:
844 for (i = 0; i < ARRAY_LEN(records); ++i) {
845 ub_ctx_data_remove(resolver->context, records[i].as_string);
846 }
847 ub_ctx_zone_remove(resolver->context, DOMAIN1);
848 ub_ctx_zone_remove(resolver->context, DOMAIN2);
849
850 return res;
851}
852
853AST_TEST_DEFINE(resolve_sync)
854{
855
856 switch (cmd) {
857 case TEST_INIT:
858 info->name = "resolve_sync";
859 info->category = "/res/res_resolver_unbound/";
860 info->summary = "Test nominal synchronous resolution using libunbound";
861 info->description = "This test performs the following:\n"
862 "\t* Set two static A records and one static AAAA record on one domain\n"
863 "\t* Set an A record for a second domain\n"
864 "\t* Perform an A record lookup on the first domain\n"
865 "\t* Ensure that both A records are returned and no AAAA record is returned\n"
866 "\t* Perform an AAAA record lookup on the first domain\n"
867 "\t* Ensure that the AAAA record is returned and no A record is returned\n"
868 "\t* Perform an A record lookup on the second domain\n"
869 "\t* Ensure that the A record from the second domain is returned";
870 return AST_TEST_NOT_RUN;
871 case TEST_EXECUTE:
872 break;
873 }
874
875 return nominal_test(test, nominal_sync_run);
876}
877
878AST_TEST_DEFINE(resolve_async)
879{
880 switch (cmd) {
881 case TEST_INIT:
882 info->name = "resolve_async";
883 info->category = "/res/res_resolver_unbound/";
884 info->summary = "Test nominal asynchronous resolution using libunbound";
885 info->description = "This test performs the following:\n"
886 "\t* Set two static A records and one static AAAA record on one domain\n"
887 "\t* Set an A record for a second domain\n"
888 "\t* Perform an A record lookup on the first domain\n"
889 "\t* Ensure that both A records are returned and no AAAA record is returned\n"
890 "\t* Perform an AAAA record lookup on the first domain\n"
891 "\t* Ensure that the AAAA record is returned and no A record is returned\n"
892 "\t* Perform an A record lookup on the second domain\n"
893 "\t* Ensure that the A record from the second domain is returned";
894 return AST_TEST_NOT_RUN;
895 case TEST_EXECUTE:
896 break;
897 }
898
899 return nominal_test(test, nominal_async_run);
900}
901
902typedef int (*off_nominal_resolve_fn)(struct ast_test *test, const char *domain, int rr_type,
903 int rr_class, int expected_rcode);
904
905static int off_nominal_sync_run(struct ast_test *test, const char *domain, int rr_type,
906 int rr_class, int expected_rcode)
907{
908 struct ast_dns_result *result;
909 int res = 0;
910
911 if (ast_dns_resolve(domain, rr_type, rr_class, &result)) {
912 ast_test_status_update(test, "Failed to perform resolution :(\n");
913 return -1;
914 }
915
916 if (!result) {
917 ast_test_status_update(test, "Resolution returned no result\n");
918 return -1;
919 }
920
921 if (ast_dns_result_get_rcode(result) != expected_rcode) {
922 ast_test_status_update(test, "Unexpected rcode '%d' (expected '%d') from DNS resolution of '%s' class: '%d' type: '%d'\n",
923 ast_dns_result_get_rcode(result), expected_rcode, domain, rr_class, rr_type);
924 res = -1;
925 }
926
928 ast_test_status_update(test, "DNS resolution returned records unexpectedly\n");
929 res = -1;
930 }
931
933 return res;
934}
935
936/*!
937 * \brief User data for off-nominal async resolution test
938 */
939struct off_nominal_async_data {
940 /*! The DNS result's expected rcode */
941 int expected_rcode;
942 /*! Whether an asynchronous query failed */
943 int failed;
944 /*! Indicates the asynchronous query is complete */
945 int complete;
948};
949
950static void off_nominal_async_data_destructor(void *obj)
951{
952 struct off_nominal_async_data *adata = obj;
953
954 ast_mutex_destroy(&adata->lock);
955 ast_cond_destroy(&adata->cond);
956}
957
958static struct off_nominal_async_data *off_nominal_async_data_alloc(int expected_rcode)
959{
960 struct off_nominal_async_data *adata;
961
962 adata = ao2_alloc(sizeof(*adata), off_nominal_async_data_destructor);
963 if (!adata) {
964 return NULL;
965 }
966
967 ast_mutex_init(&adata->lock);
968 ast_cond_init(&adata->cond, NULL);
969
970 adata->expected_rcode = expected_rcode;
971
972 return adata;
973}
974
975/*!
976 * \brief Async callback for off-nominal async test
977 *
978 * This test ensures that there is a result present on the query, then it checks
979 * that the rcode on the result is the expected value and that there are no
980 * records on the result.
981 *
982 * Once completed, the testing thread is signaled that the async query has
983 * completed.
984 */
985static void off_nominal_async_callback(const struct ast_dns_query *query)
986{
987 struct off_nominal_async_data *adata = ast_dns_query_get_data(query);
989
990 if (!result) {
991 adata->failed = -1;
992 goto end;
993 }
994
995 if (ast_dns_result_get_rcode(result) != adata->expected_rcode) {
996 adata->failed = -1;
997 }
998
1000 adata->failed = -1;
1001 }
1002
1003end:
1004 ast_mutex_lock(&adata->lock);
1005 adata->complete = 1;
1006 ast_cond_signal(&adata->cond);
1007 ast_mutex_unlock(&adata->lock);
1008}
1009
1010static int off_nominal_async_run(struct ast_test *test, const char *domain, int rr_type,
1011 int rr_class, int expected_rcode)
1012{
1013 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
1014 RAII_VAR(struct off_nominal_async_data *, adata, NULL, ao2_cleanup);
1015
1016 adata = off_nominal_async_data_alloc(expected_rcode);
1017 if (!adata) {
1018 ast_test_status_update(test, "Unable to allocate data for async query\n");
1019 return -1;
1020 }
1021
1022 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
1023
1024 active = ast_dns_resolve_async(domain, rr_type, rr_class, off_nominal_async_callback, adata);
1025 if (!active) {
1026 ast_test_status_update(test, "Failed to perform asynchronous resolution of domain %s\n", domain);
1027 return -1;
1028 }
1029
1030 ast_mutex_lock(&adata->lock);
1031 while (!adata->complete) {
1032 ast_cond_wait(&adata->cond, &adata->lock);
1033 }
1034 ast_mutex_unlock(&adata->lock);
1035
1036 if (adata->failed) {
1037 ast_test_status_update(test, "Asynchronous resolution failure %s\n", domain);
1038 }
1039 return adata->failed;
1040}
1041
1042static enum ast_test_result_state off_nominal_test(struct ast_test *test,
1043 off_nominal_resolve_fn runner)
1044{
1045 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1046 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1047
1048 static const size_t V4_SIZE = sizeof(struct in_addr);
1049
1050 static const char *DOMAIN1 = "goose.feathers";
1051 static const char *DOMAIN2 = "duck.feathers";
1052 static const char *BADFORMAT1 = ".1";
1053 static const char *BADFORMAT2 = ".www";
1054
1055 static const char *ADDR1 = "127.0.0.2";
1056
1057 char addr1_buf[V4_SIZE];
1058
1059 struct dns_record records [] = {
1060 { "goose.feathers 12345 IN A 127.0.0.2", DOMAIN1, ns_t_a, ns_c_in, 12345, addr1_buf, V4_SIZE, 0, },
1061 };
1062
1063 int i;
1065
1066 struct {
1067 const char *domain;
1068 int rr_type;
1069 int rr_class;
1070 int rcode;
1071 } runs [] = {
1072 { DOMAIN2, ns_t_a, ns_c_in, ns_r_nxdomain },
1073 { DOMAIN1, ns_t_aaaa, ns_c_in, ns_r_noerror },
1074 { DOMAIN1, ns_t_a, ns_c_chaos, ns_r_refused },
1075 { BADFORMAT1, ns_t_a, ns_c_in, ns_r_formerr },
1076 { BADFORMAT2, ns_t_a, ns_c_in, ns_r_formerr },
1077 { BADFORMAT1, ns_t_ptr, ns_c_in, ns_r_formerr },
1078 { BADFORMAT2, ns_t_ptr, ns_c_in, ns_r_formerr },
1079 };
1080
1081 inet_pton(AF_INET, ADDR1, addr1_buf);
1082
1084 resolver = ao2_bump(cfg->global->state->resolver);
1085
1086 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1087 ub_ctx_zone_add(resolver->context, DOMAIN2, "static");
1088
1089 for (i = 0; i < ARRAY_LEN(records); ++i) {
1090 ub_ctx_data_add(resolver->context, records[i].as_string);
1091 }
1092
1093 for (i = 0; i < ARRAY_LEN(runs); ++i) {
1094 if (runner(test, runs[i].domain, runs[i].rr_type, runs[i].rr_class, runs[i].rcode)) {
1095 res = AST_TEST_FAIL;
1096 }
1097 }
1098
1099 return res;
1100}
1101
1102AST_TEST_DEFINE(resolve_sync_off_nominal)
1103{
1104 switch (cmd) {
1105 case TEST_INIT:
1106 info->name = "resolve_sync_off_nominal";
1107 info->category = "/res/res_resolver_unbound/";
1108 info->summary = "Test off-nominal synchronous resolution using libunbound";
1109 info->description = "This test performs the following:\n"
1110 "\t* Attempt a lookup of a non-existent domain\n"
1111 "\t* Attempt a lookup of a AAAA record on a domain that contains only A records\n"
1112 "\t* Attempt a lookup of an A record on Chaos-net";
1113 return AST_TEST_NOT_RUN;
1114 case TEST_EXECUTE:
1115 break;
1116 }
1117
1118 return off_nominal_test(test, off_nominal_sync_run);
1119}
1120
1121AST_TEST_DEFINE(resolve_async_off_nominal)
1122{
1123 switch (cmd) {
1124 case TEST_INIT:
1125 info->name = "resolve_async_off_nominal";
1126 info->category = "/res/res_resolver_unbound/";
1127 info->summary = "Test off-nominal synchronous resolution using libunbound";
1128 info->description = "This test performs the following:\n"
1129 "\t* Attempt a lookup of a non-existent domain\n"
1130 "\t* Attempt a lookup of a AAAA record on a domain that contains only A records\n"
1131 "\t* Attempt a lookup of an A record on Chaos-net";
1132 return AST_TEST_NOT_RUN;
1133 case TEST_EXECUTE:
1134 break;
1135 }
1136
1137 return off_nominal_test(test, off_nominal_async_run);
1138}
1139
1140/*!
1141 * \brief Minimal data required to signal the completion of an async resolve
1142 */
1143struct async_minimal_data {
1144 int complete;
1147};
1148
1149static void async_minimal_data_destructor(void *obj)
1150{
1151 struct async_minimal_data *adata = obj;
1152
1153 ast_mutex_destroy(&adata->lock);
1154 ast_cond_destroy(&adata->cond);
1155}
1156
1157static struct async_minimal_data *async_minimal_data_alloc(void)
1158{
1159 struct async_minimal_data *adata;
1160
1161 adata = ao2_alloc(sizeof(*adata), async_minimal_data_destructor);
1162 if (!adata) {
1163 return NULL;
1164 }
1165
1166 ast_mutex_init(&adata->lock);
1167 ast_cond_init(&adata->cond, NULL);
1168
1169 return adata;
1170}
1171
1172/*!
1173 * \brief Async callback for off-nominal cancellation test.
1174 *
1175 * This simply signals the testing thread that the query completed
1176 */
1177static void minimal_callback(const struct ast_dns_query *query)
1178{
1179 struct async_minimal_data *adata = ast_dns_query_get_data(query);
1180
1181 ast_mutex_lock(&adata->lock);
1182 adata->complete = 1;
1183 ast_cond_signal(&adata->cond);
1184 ast_mutex_unlock(&adata->lock);
1185}
1186
1187AST_TEST_DEFINE(resolve_cancel_off_nominal)
1188{
1189 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
1190 RAII_VAR(struct async_minimal_data *, adata, NULL, ao2_cleanup);
1191
1192 switch (cmd) {
1193 case TEST_INIT:
1194 info->name = "resolve_cancel_off_nominal";
1195 info->category = "/res/res_resolver_unbound/";
1196 info->summary = "Off nominal cancellation test using libunbound";
1197 info->description = "This test does the following:\n"
1198 "\t* Perform an asynchronous query\n"
1199 "\t* Once the query has completed, attempt to cancel it";
1200 return AST_TEST_NOT_RUN;
1201 case TEST_EXECUTE:
1202 break;
1203 }
1204
1205 adata = async_minimal_data_alloc();
1206 if (!adata) {
1207 ast_test_status_update(test, "Failed to allocate necessary data for test\n");
1208 return AST_TEST_FAIL;
1209 }
1210
1211 active = ast_dns_resolve_async("crunchy.peanut.butter", ns_t_a, ns_c_in, minimal_callback, adata);
1212 if (!active) {
1213 ast_test_status_update(test, "Failed to perform asynchronous query\n");
1214 return AST_TEST_FAIL;
1215 }
1216
1217 /* Wait for async query to complete */
1218 ast_mutex_lock(&adata->lock);
1219 while (!adata->complete) {
1220 ast_cond_wait(&adata->cond, &adata->lock);
1221 }
1222 ast_mutex_unlock(&adata->lock);
1223
1224 if (!ast_dns_resolve_cancel(active)) {
1225 ast_test_status_update(test, "Successfully canceled completed query\n");
1226 return AST_TEST_FAIL;
1227 }
1228
1229 return AST_TEST_PASS;
1230}
1231
1232AST_TEST_DEFINE(resolve_naptr)
1233{
1234 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1235 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1237
1238 const struct ast_dns_record *record;
1239
1240 static char * DOMAIN1 = "goose.feathers";
1241 int i;
1243
1244 struct naptr_record {
1245 const char *zone_entry;
1246 uint16_t order;
1247 uint16_t preference;
1248 const char *flags;
1249 const char *services;
1250 const char *regexp;
1251 const char *replacement;
1252 int visited;
1253 } records [] = {
1254 { "goose.feathers 12345 IN NAPTR 100 100 A SIP+D2U \"\" goose.down", 100, 100, "A", "SIP+D2U", "", "goose.down", 0},
1255 { "goose.feathers 12345 IN NAPTR 100 200 A SIP+D2T \"\" duck.down", 100, 200, "A", "SIP+D2T", "", "duck.down", 0},
1256 { "goose.feathers 12345 IN NAPTR 200 100 A SIPS+D2U \"\" pheasant.down", 200, 100, "A", "SIPS+D2U", "", "pheasant.down", 0},
1257 { "goose.feathers 12345 IN NAPTR 200 200 A SIPS+D2T \"\" platypus.fur", 200, 200, "A", "SIPS+D2T", "", "platypus.fur", 0},
1258 };
1259
1260 switch (cmd) {
1261 case TEST_INIT:
1262 info->name = "resolve_naptr";
1263 info->category = "/res/res_resolver_unbound/";
1264 info->summary = "Attempt resolution of NAPTR record";
1265 info->description = "This test performs a NAPTR lookup and ensures that\n"
1266 "the returned record has the appropriate values set";
1267 return AST_TEST_NOT_RUN;
1268 case TEST_EXECUTE:
1269 break;
1270 }
1271
1273 resolver = ao2_bump(cfg->global->state->resolver);
1274
1275 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1276
1277 for (i = 0; i < ARRAY_LEN(records); ++i) {
1278 ub_ctx_data_add(resolver->context, records[i].zone_entry);
1279 }
1280
1281 if (ast_dns_resolve(DOMAIN1, ns_t_naptr, ns_c_in, &result)) {
1282 ast_test_status_update(test, "Failed to resolve domain\n");
1283 return AST_TEST_FAIL;
1284 }
1285
1286 if (!result) {
1287 ast_test_status_update(test, "Successful resolution set a NULL result\n");
1288 return AST_TEST_FAIL;
1289 }
1290
1292 if (!record) {
1293 ast_test_status_update(test, "Failed to get any DNS records from the result\n");
1294 return AST_TEST_FAIL;
1295 }
1296
1297 i = 0;
1298 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
1299 if (ast_dns_naptr_get_order(record) != records[i].order) {
1300 ast_test_status_update(test, "Expected order %hu, got order %hu from NAPTR record\n",
1302 res = AST_TEST_FAIL;
1303 }
1304 if (ast_dns_naptr_get_preference(record) != records[i].preference) {
1305 ast_test_status_update(test, "Expected preference %hu, got preference %hu from NAPTR record\n",
1306 records[i].preference, ast_dns_naptr_get_preference(record));
1307 res = AST_TEST_FAIL;
1308 }
1309 if (strcmp(ast_dns_naptr_get_flags(record), records[i].flags)) {
1310 ast_test_status_update(test, "Expected flags %s, got flags %s from NAPTR record\n",
1311 records[i].flags, ast_dns_naptr_get_flags(record));
1312 res = AST_TEST_FAIL;
1313 }
1314 if (strcmp(ast_dns_naptr_get_service(record), records[i].services)) {
1315 ast_test_status_update(test, "Expected services %s, got services %s from NAPTR record\n",
1316 records[i].services, ast_dns_naptr_get_service(record));
1317 res = AST_TEST_FAIL;
1318 }
1319 if (strcmp(ast_dns_naptr_get_regexp(record), records[i].regexp)) {
1320 ast_test_status_update(test, "Expected regexp %s, got regexp %s from NAPTR record\n",
1321 records[i].regexp, ast_dns_naptr_get_regexp(record));
1322 res = AST_TEST_FAIL;
1323 }
1324 if (strcmp(ast_dns_naptr_get_replacement(record), records[i].replacement)) {
1325 ast_test_status_update(test, "Expected replacement %s, got replacement %s from NAPTR record\n",
1326 records[i].replacement, ast_dns_naptr_get_replacement(record));
1327 res = AST_TEST_FAIL;
1328 }
1329 records[i].visited = 1;
1330 ++i;
1331 }
1332
1333 if (i != ARRAY_LEN(records)) {
1334 ast_test_status_update(test, "Unexpected number of records visited\n");
1335 res = AST_TEST_FAIL;
1336 }
1337
1338 for (i = 0; i < ARRAY_LEN(records); ++i) {
1339 if (!records[i].visited) {
1340 ast_test_status_update(test, "Did not visit all expected NAPTR records\n");
1341 res = AST_TEST_FAIL;
1342 }
1343 }
1344
1345 return res;
1346
1347}
1348
1349AST_TEST_DEFINE(resolve_srv)
1350{
1351 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1352 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1354 const struct ast_dns_record *record;
1355 static const char *DOMAIN1 = "taco.bananas";
1356 static const char *DOMAIN1_SRV = "taco.bananas 12345 IN SRV 10 20 5060 sip.taco.bananas";
1358
1359 switch (cmd) {
1360 case TEST_INIT:
1361 info->name = "resolve_srv";
1362 info->category = "/res/res_resolver_unbound/";
1363 info->summary = "Test synchronous SRV resolution using libunbound";
1364 info->description = "This test performs the following:\n"
1365 "\t* Set one SRV record on one domain\n"
1366 "\t* Perform an SRV lookup on the domain\n"
1367 "\t* Ensure that the SRV record returned matches the expected value";
1368 return AST_TEST_NOT_RUN;
1369 case TEST_EXECUTE:
1370 break;
1371 }
1372
1374 resolver = ao2_bump(cfg->global->state->resolver);
1375
1376 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1377 ub_ctx_data_add(resolver->context, DOMAIN1_SRV);
1378
1379 if (ast_dns_resolve(DOMAIN1, ns_t_srv, ns_c_in, &result)) {
1380 ast_test_status_update(test, "Failed to synchronously resolve SRV for domain '%s'\n", DOMAIN1);
1381 res = AST_TEST_FAIL;
1382 goto cleanup;
1383 }
1384
1386 if (ast_dns_srv_get_priority(record) != 10) {
1387 ast_test_status_update(test, "SRV Record returned priority '%d' when we expected 10\n", ast_dns_srv_get_priority(record));
1388 res = AST_TEST_FAIL;
1389 goto cleanup;
1390 }
1391
1392 if (ast_dns_srv_get_weight(record) != 20) {
1393 ast_test_status_update(test, "SRV Record returned weight '%d' when we expected 20\n", ast_dns_srv_get_weight(record));
1394 res = AST_TEST_FAIL;
1395 goto cleanup;
1396 }
1397
1398 if (ast_dns_srv_get_port(record) != 5060) {
1399 ast_test_status_update(test, "SRV Record returned port '%d' when we expected 5060\n", ast_dns_srv_get_port(record));
1400 res = AST_TEST_FAIL;
1401 goto cleanup;
1402 }
1403
1404 if (strcmp(ast_dns_srv_get_host(record), "sip.taco.bananas")) {
1405 ast_test_status_update(test, "SRV Record returned host '%s' when we expected sip.taco.bananas\n", ast_dns_srv_get_host(record));
1406 res = AST_TEST_FAIL;
1407 goto cleanup;
1408 }
1409
1410cleanup:
1411 ub_ctx_data_remove(resolver->context, DOMAIN1_SRV);
1412 ub_ctx_zone_remove(resolver->context, DOMAIN1);
1413
1414 return res;
1415}
1416#endif
1417
1418static int reload_module(void)
1419{
1420 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1422 }
1423
1424 return 0;
1425}
1426
1427static int unload_module(void)
1428{
1429 aco_info_destroy(&cfg_info);
1431
1432 AST_TEST_UNREGISTER(resolve_sync);
1433 AST_TEST_UNREGISTER(resolve_async);
1434 AST_TEST_UNREGISTER(resolve_sync_off_nominal);
1435 AST_TEST_UNREGISTER(resolve_sync_off_nominal);
1436 AST_TEST_UNREGISTER(resolve_cancel_off_nominal);
1437 AST_TEST_UNREGISTER(resolve_naptr);
1438 AST_TEST_UNREGISTER(resolve_srv);
1439 return 0;
1440}
1441
1442static int custom_nameserver_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1443{
1444 struct unbound_global_config *global = obj;
1445
1446 if (!global->nameservers) {
1448 if (!global->nameservers) {
1449 return -1;
1450 }
1451 }
1452
1453 return ast_str_container_add(global->nameservers, var->value);
1454}
1455
1456static int load_module(void)
1457{
1458 struct ast_config *cfg;
1459 struct ast_flags cfg_flags = { 0, };
1460
1461 if (aco_info_init(&cfg_info)) {
1463 }
1464
1465 aco_option_register(&cfg_info, "hosts", ACO_EXACT, global_options, "system", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, hosts));
1466 aco_option_register(&cfg_info, "resolv", ACO_EXACT, global_options, "system", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, resolv));
1469 aco_option_register(&cfg_info, "ta_file", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, ta_file));
1470
1471 /* This purposely checks for a configuration file so we don't output an error message in ACO if one is not present */
1473 if (!cfg) {
1475 unload_module();
1477 }
1478 } else {
1479 ast_config_destroy(cfg);
1480 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1481 unload_module();
1483 }
1484 }
1485
1487
1489
1490 AST_TEST_REGISTER(resolve_sync);
1491 AST_TEST_REGISTER(resolve_async);
1492 AST_TEST_REGISTER(resolve_sync_off_nominal);
1493 AST_TEST_REGISTER(resolve_async_off_nominal);
1494 AST_TEST_REGISTER(resolve_cancel_off_nominal);
1495 AST_TEST_REGISTER(resolve_naptr);
1496 AST_TEST_REGISTER(resolve_srv);
1497
1499}
1500
1501AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Unbound DNS Resolver Support",
1502 .support_level = AST_MODULE_SUPPORT_CORE,
1503 .load = load_module,
1504 .unload = unload_module,
1506 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
integer order
Definition: analys.c:66
pthread_t thread
Definition: app_sla.c:335
ast_cond_t cond
Definition: app_sla.c:336
ast_mutex_t lock
Definition: app_sla.c:337
#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:2387
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:206
#define ast_cond_wait(cond, mutex)
Definition: lock.h:209
#define AST_PTHREADT_NULL
Definition: lock.h:70
#define ast_cond_init(cond, attr)
Definition: lock.h:205
#define ast_mutex_init(pmutex)
Definition: lock.h:190
#define AST_PTHREADT_STOP
Definition: lock.h:71
#define ast_mutex_unlock(a)
Definition: lock.h:194
pthread_cond_t ast_cond_t
Definition: lock.h:182
#define ast_mutex_destroy(a)
Definition: lock.h:192
#define ast_mutex_lock(a)
Definition: lock.h:193
#define ast_cond_signal(cond)
Definition: lock.h:207
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:570
#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:139
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