Asterisk - The Open Source Telephony Project GIT-master-7e7a603
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 (!ast_dns_resolver_set_result(query, ub_result->secure, ub_result->bogus, ub_result->rcode,
261 S_OR(ub_result->canonname, ast_dns_query_get_name(query)), ub_result->answer_packet, ub_result->answer_len)) {
262 int i;
263 char *result_data;
264
265 for (i = 0; (result_data = ub_result->data[i]); i++) {
266 if (ast_dns_resolver_add_record(query, ub_result->qtype, ub_result->qclass, ub_result->ttl,
267 result_data, ub_result->len[i])) {
268 break;
269 }
270 }
271 }
272
274 ao2_ref(query, -1);
275 ub_resolve_free(ub_result);
276}
277
278static void unbound_resolver_data_dtor(void *vdoomed)
279{
280 struct unbound_resolver_data *doomed = vdoomed;
281
282 ao2_cleanup(doomed->resolver);
283}
284
286{
288 struct unbound_resolver_data *data;
289 int res;
290
291 data = ao2_alloc_options(sizeof(*data), unbound_resolver_data_dtor,
293 if (!data) {
294 ast_log(LOG_ERROR, "Failed to allocate resolver data for resolution of '%s'\n",
296 return -1;
297 }
298 data->resolver = ao2_bump(cfg->global->state->resolver);
299 ast_dns_resolver_set_data(query, data);
300
301 res = ub_resolve_async(data->resolver->context, ast_dns_query_get_name(query),
303 ao2_bump(query), unbound_resolver_callback, &data->id);
304
305 if (res) {
306 ast_log(LOG_ERROR, "Failed to perform async DNS resolution of '%s'\n",
308 ao2_ref(query, -1);
309 }
310
311 ao2_ref(data, -1);
312 ao2_ref(cfg, -1);
313 return res;
314}
315
316static int unbound_resolver_cancel(struct ast_dns_query *query)
317{
319 int res;
320
321 res = ub_cancel(data->resolver->context, data->id);
322 if (!res) {
323 /* When this query was started we bumped the ref, now that it has been cancelled we have ownership and
324 * need to drop it
325 */
326 ao2_ref(query, -1);
327 }
328
329 return res;
330}
331
333 .name = "unbound",
334 .priority = 100,
335 .resolve = unbound_resolver_resolve,
336 .cancel = unbound_resolver_cancel,
337};
338
339static void unbound_config_destructor(void *obj)
340{
341 struct unbound_config *cfg = obj;
342
343 ao2_cleanup(cfg->global);
344}
345
347{
348 struct unbound_global_config *global = obj;
349
351 ao2_cleanup(global->nameservers);
352 ao2_cleanup(global->state);
353}
354
356{
357 struct unbound_config_state *state = obj;
358
359 if (state->resolver) {
360 unbound_resolver_stop(state->resolver);
361 ao2_ref(state->resolver, -1);
362 }
363}
364
365static void *unbound_config_alloc(void)
366{
367 struct unbound_config *cfg;
368
370 if (!cfg) {
371 return NULL;
372 }
373
374 /* Allocate/initialize memory */
377 if (!cfg->global) {
378 goto error;
379 }
380
381 if (ast_string_field_init(cfg->global, 128)) {
382 goto error;
383 }
384
385 return cfg;
386error:
387 ao2_ref(cfg, -1);
388 return NULL;
389}
390
392{
393 int res = 0;
394
397 if (!cfg->global->state) {
398 ast_log(LOG_ERROR, "Could not allocate unbound resolver state structure\n");
399 return -1;
400 }
401
403 if (!cfg->global->state->resolver) {
404 ast_log(LOG_ERROR, "Could not create an unbound resolver\n");
405 return -1;
406 }
407
408 ub_ctx_debuglevel(cfg->global->state->resolver->context, cfg->global->debug);
409
410 if (!strcmp(cfg->global->hosts, "system")) {
411 res = ub_ctx_hosts(cfg->global->state->resolver->context, NULL);
412 } else if (!ast_strlen_zero(cfg->global->hosts)) {
413 res = ub_ctx_hosts(cfg->global->state->resolver->context, cfg->global->hosts);
414 }
415
416 if (res) {
417 ast_log(LOG_ERROR, "Failed to set hosts file to '%s' in unbound resolver: %s\n",
418 cfg->global->hosts, ub_strerror(res));
419 return -1;
420 }
421
422 if (cfg->global->nameservers) {
423 struct ao2_iterator it_nameservers;
424 char *nameserver;
425
426 it_nameservers = ao2_iterator_init(cfg->global->nameservers, 0);
427 while (!res && (nameserver = ao2_iterator_next(&it_nameservers))) {
428 res = ub_ctx_set_fwd(cfg->global->state->resolver->context, nameserver);
429
430 if (res) {
431 ast_log(LOG_ERROR, "Failed to add nameserver '%s' to unbound resolver: %s\n",
432 nameserver, ub_strerror(res));
433 }
434 ao2_ref(nameserver, -1);
435 }
436 ao2_iterator_destroy(&it_nameservers);
437 if (res) {
438 return -1;
439 }
440 }
441
442 if (!strcmp(cfg->global->resolv, "system")) {
443 res = ub_ctx_resolvconf(cfg->global->state->resolver->context, NULL);
444 } else if (!ast_strlen_zero(cfg->global->resolv)) {
445 res = ub_ctx_resolvconf(cfg->global->state->resolver->context, cfg->global->resolv);
446 }
447
448 if (res) {
449 ast_log(LOG_ERROR, "Failed to set resolv.conf file to '%s' in unbound resolver: %s\n",
450 cfg->global->resolv, ub_strerror(res));
451 return -1;
452 }
453
454 if (!ast_strlen_zero(cfg->global->ta_file)) {
455 res = ub_ctx_add_ta_file(cfg->global->state->resolver->context, cfg->global->ta_file);
456
457 if (res) {
458 ast_log(LOG_ERROR, "Failed to set trusted anchor file to '%s' in unbound resolver: %s\n",
459 cfg->global->ta_file, ub_strerror(res));
460 return -1;
461 }
462 }
463
465 ast_log(LOG_ERROR, "Could not start unbound resolver thread\n");
466 return -1;
467 }
468
469 return 0;
470}
471
473{
474 struct unbound_config *cfg;
475
476 cfg = unbound_config_alloc();
477 if (!cfg) {
478 ast_log(LOG_ERROR, "Could not create default configuration for unbound resolver\n");
479 return -1;
480 }
481
482 aco_set_defaults(&global_option, "general", cfg->global);
483
484 if (unbound_config_preapply(cfg)) {
485 ao2_ref(cfg, -1);
486 return -1;
487 }
488
489 ast_verb(1, "Starting unbound resolver using default configuration\n");
490
492 ao2_ref(cfg, -1);
493
494 return 0;
495}
496
498{
500}
501
502#ifdef TEST_FRAMEWORK
503
504#include "asterisk/dns_naptr.h"
505
506/*!
507 * \brief A DNS record to be used during a test
508 */
509struct dns_record {
510 /*! String representation of the record, as would be found in a file */
511 const char *as_string;
512 /*! The domain this record belongs to */
513 const char *domain;
514 /*! The type of the record */
515 int rr_type;
516 /*! The class of the record */
517 int rr_class;
518 /*! The TTL of the record, in seconds */
519 int ttl;
520 /*! The RDATA of the DNS record */
521 const char *buf;
522 /*! The size of the RDATA */
523 const size_t bufsize;
524 /*! Whether a record checker has visited this record */
525 int visited;
526};
527
528/*!
529 * \brief Resolution function for tests.
530 *
531 * Several tests will have similar setups but will want to make use of a different
532 * means of actually making queries and checking their results. This pluggable
533 * function pointer allows for similar tests to be operated in different ways.
534 *
535 * \param test The test being run
536 * \param domain The domain to look up
537 * \param rr_type The record type to look up
538 * \param rr_class The class of record to look up
539 * \param records All records that exist for the test.
540 * \param num_records Number of records in the records array.
541 *
542 * \retval 0 The test has passed thus far.
543 * \retval -1 The test has failed.
544 */
545typedef int (*resolve_fn)(struct ast_test *test, const char *domain, int rr_type,
546 int rr_class, struct dns_record *records, size_t num_records);
547
548/*!
549 * \brief Pluggable function for running a synchronous query and checking its results
550 */
551static int nominal_sync_run(struct ast_test *test, const char *domain, int rr_type,
552 int rr_class, struct dns_record *records, size_t num_records)
553{
555 const struct ast_dns_record *record;
556 int i;
557
558 /* Start by making sure no records have been visited */
559 for (i = 0; i < num_records; ++i) {
560 records[i].visited = 0;
561 }
562
563 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
564
565 if (ast_dns_resolve(domain, rr_type, rr_class, &result)) {
566 ast_test_status_update(test, "Failed to perform synchronous resolution of domain %s\n", domain);
567 return -1;
568 }
569
570 if (!result) {
571 ast_test_status_update(test, "Successful synchronous resolution of domain %s gave NULL result\n", domain);
572 return -1;
573 }
574
575 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
576 int match = 0;
577
578 /* Let's make sure this matches one of our known records */
579 for (i = 0; i < num_records; ++i) {
580 if (ast_dns_record_get_rr_type(record) == records[i].rr_type &&
581 ast_dns_record_get_rr_class(record) == records[i].rr_class &&
582 ast_dns_record_get_ttl(record) == records[i].ttl &&
583 !memcmp(ast_dns_record_get_data(record), records[i].buf, records[i].bufsize)) {
584 match = 1;
585 records[i].visited = 1;
586 break;
587 }
588 }
589
590 if (!match) {
591 ast_test_status_update(test, "Unknown DNS record returned from domain %s\n", domain);
592 return -1;
593 }
594 }
595
596 return 0;
597}
598
599/*!
600 * \brief Data required for an asynchronous callback
601 */
602struct async_data {
603 /*! The set of DNS records on a test */
604 struct dns_record *records;
605 /*! The number of DNS records on the test */
606 size_t num_records;
607 /*! Whether an asynchronous query failed */
608 int failed;
609 /*! Indicates the asynchronous query is complete */
610 int complete;
613};
614
615static void async_data_destructor(void *obj)
616{
617 struct async_data *adata = obj;
618
619 ast_mutex_destroy(&adata->lock);
620 ast_cond_destroy(&adata->cond);
621}
622
623static struct async_data *async_data_alloc(struct dns_record *records, size_t num_records)
624{
625 struct async_data *adata;
626
627 adata = ao2_alloc(sizeof(*adata), async_data_destructor);
628 if (!adata) {
629 return NULL;
630 }
631
632 ast_mutex_init(&adata->lock);
633 ast_cond_init(&adata->cond, NULL);
634 adata->records = records;
635 adata->num_records = num_records;
636
637 return adata;
638}
639
640/*!
641 * \brief Callback for asynchronous queries
642 *
643 * This query will check that the records in the DNS result match
644 * records that the test has created. The success or failure of the
645 * query is indicated through the async_data failed field.
646 *
647 * \param query The DNS query that has been resolved
648 */
649static void async_callback(const struct ast_dns_query *query)
650{
651 struct async_data *adata = ast_dns_query_get_data(query);
653 const struct ast_dns_record *record;
654 int i;
655
656 if (!result) {
657 adata->failed = -1;
658 goto end;
659 }
660
661 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
662 int match = 0;
663
664 /* Let's make sure this matches one of our known records */
665 for (i = 0; i < adata->num_records; ++i) {
666 if (ast_dns_record_get_rr_type(record) == adata->records[i].rr_type &&
667 ast_dns_record_get_rr_class(record) == adata->records[i].rr_class &&
668 ast_dns_record_get_ttl(record) == adata->records[i].ttl &&
669 !memcmp(ast_dns_record_get_data(record), adata->records[i].buf, adata->records[i].bufsize)) {
670 match = 1;
671 adata->records[i].visited = 1;
672 break;
673 }
674 }
675
676 if (!match) {
677 adata->failed = -1;
678 goto end;
679 }
680 }
681
682end:
683 ast_mutex_lock(&adata->lock);
684 adata->complete = 1;
685 ast_cond_signal(&adata->cond);
686 ast_mutex_unlock(&adata->lock);
687}
688
689/*!
690 * \brief Pluggable function for performing an asynchronous query during a test
691 *
692 * Unlike the synchronous version, this does not check the records, instead leaving
693 * that to be done in the asynchronous callback.
694 */
695static int nominal_async_run(struct ast_test *test, const char *domain, int rr_type,
696 int rr_class, struct dns_record *records, size_t num_records)
697{
698 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
699 RAII_VAR(struct async_data *, adata, NULL, ao2_cleanup);
700 int i;
701
702 adata = async_data_alloc(records, num_records);
703 if (!adata) {
704 ast_test_status_update(test, "Unable to allocate data for async query\n");
705 return -1;
706 }
707
708 /* Start by making sure no records have been visited */
709 for (i = 0; i < num_records; ++i) {
710 records[i].visited = 0;
711 }
712
713 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
714
715 active = ast_dns_resolve_async(domain, rr_type, rr_class, async_callback, adata);
716 if (!active) {
717 ast_test_status_update(test, "Failed to perform asynchronous resolution of domain %s\n", domain);
718 return -1;
719 }
720
721 ast_mutex_lock(&adata->lock);
722 while (!adata->complete) {
723 ast_cond_wait(&adata->cond, &adata->lock);
724 }
725 ast_mutex_unlock(&adata->lock);
726
727 if (adata->failed) {
728 ast_test_status_update(test, "Unknown DNS record returned from domain %s\n", domain);
729 }
730 return adata->failed;
731}
732
733/*!
734 * \brief Framework for running a nominal DNS test
735 *
736 * Synchronous and asynchronous tests mostly have the same setup, so this function
737 * serves as a common way to set up both types of tests by accepting a pluggable
738 * function to determine which type of lookup is used
739 *
740 * \param test The test being run
741 * \param runner The method for resolving queries on this test
742 */
743static enum ast_test_result_state nominal_test(struct ast_test *test, resolve_fn runner)
744{
745 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
746 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
747
748 static const size_t V4_SIZE = sizeof(struct in_addr);
749 static const size_t V6_SIZE = sizeof(struct in6_addr);
750
751 static const char *DOMAIN1 = "goose.feathers";
752 static const char *DOMAIN2 = "duck.feathers";
753
754 static const char *ADDR1 = "127.0.0.2";
755 static const char *ADDR2 = "127.0.0.3";
756 static const char *ADDR3 = "::1";
757 static const char *ADDR4 = "127.0.0.4";
758
759 char addr1_buf[V4_SIZE];
760 char addr2_buf[V4_SIZE];
761 char addr3_buf[V6_SIZE];
762 char addr4_buf[V4_SIZE];
763
764 struct dns_record records [] = {
765 { "goose.feathers 12345 IN A 127.0.0.2", DOMAIN1, ns_t_a, ns_c_in, 12345, addr1_buf, V4_SIZE, 0 },
766 { "goose.feathers 12345 IN A 127.0.0.3", DOMAIN1, ns_t_a, ns_c_in, 12345, addr2_buf, V4_SIZE, 0 },
767 { "goose.feathers 12345 IN AAAA ::1", DOMAIN1, ns_t_aaaa, ns_c_in, 12345, addr3_buf, V6_SIZE, 0 },
768 { "duck.feathers 12345 IN A 127.0.0.4", DOMAIN2, ns_t_a, ns_c_in, 12345, addr4_buf, V4_SIZE, 0 },
769 };
770
771 struct {
772 const char *domain;
773 int rr_type;
774 int rr_class;
775 int visited[ARRAY_LEN(records)];
776 } runs [] = {
777 { DOMAIN1, ns_t_a, ns_c_in, { 1, 1, 0, 0 } },
778 { DOMAIN1, ns_t_aaaa, ns_c_in, { 0, 0, 1, 0 } },
779 { DOMAIN2, ns_t_a, ns_c_in, { 0, 0, 0, 1 } },
780 };
781
782 int i;
784
785 inet_pton(AF_INET, ADDR1, addr1_buf);
786 inet_pton(AF_INET, ADDR2, addr2_buf);
787 inet_pton(AF_INET6, ADDR3, addr3_buf);
788 inet_pton(AF_INET, ADDR4, addr4_buf);
789
791 resolver = ao2_bump(cfg->global->state->resolver);
792
793 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
794 ub_ctx_zone_add(resolver->context, DOMAIN2, "static");
795
796 for (i = 0; i < ARRAY_LEN(records); ++i) {
797 ub_ctx_data_add(resolver->context, records[i].as_string);
798 }
799
800 for (i = 0; i < ARRAY_LEN(runs); ++i) {
801 int j;
802
803 if (runner(test, runs[i].domain, runs[i].rr_type, runs[i].rr_class, records, ARRAY_LEN(records))) {
804 res = AST_TEST_FAIL;
805 goto cleanup;
806 }
807
808 for (j = 0; j < ARRAY_LEN(records); ++j) {
809 if (records[j].visited != runs[i].visited[j]) {
810 ast_test_status_update(test, "DNS results match unexpected records\n");
811 res = AST_TEST_FAIL;
812 goto cleanup;
813 }
814 }
815 }
816
817cleanup:
818 for (i = 0; i < ARRAY_LEN(records); ++i) {
819 ub_ctx_data_remove(resolver->context, records[i].as_string);
820 }
821 ub_ctx_zone_remove(resolver->context, DOMAIN1);
822 ub_ctx_zone_remove(resolver->context, DOMAIN2);
823
824 return res;
825}
826
827AST_TEST_DEFINE(resolve_sync)
828{
829
830 switch (cmd) {
831 case TEST_INIT:
832 info->name = "resolve_sync";
833 info->category = "/res/res_resolver_unbound/";
834 info->summary = "Test nominal synchronous resolution using libunbound";
835 info->description = "This test performs the following:\n"
836 "\t* Set two static A records and one static AAAA record on one domain\n"
837 "\t* Set an A record for a second domain\n"
838 "\t* Perform an A record lookup on the first domain\n"
839 "\t* Ensure that both A records are returned and no AAAA record is returned\n"
840 "\t* Perform an AAAA record lookup on the first domain\n"
841 "\t* Ensure that the AAAA record is returned and no A record is returned\n"
842 "\t* Perform an A record lookup on the second domain\n"
843 "\t* Ensure that the A record from the second domain is returned";
844 return AST_TEST_NOT_RUN;
845 case TEST_EXECUTE:
846 break;
847 }
848
849 return nominal_test(test, nominal_sync_run);
850}
851
852AST_TEST_DEFINE(resolve_async)
853{
854 switch (cmd) {
855 case TEST_INIT:
856 info->name = "resolve_async";
857 info->category = "/res/res_resolver_unbound/";
858 info->summary = "Test nominal asynchronous resolution using libunbound";
859 info->description = "This test performs the following:\n"
860 "\t* Set two static A records and one static AAAA record on one domain\n"
861 "\t* Set an A record for a second domain\n"
862 "\t* Perform an A record lookup on the first domain\n"
863 "\t* Ensure that both A records are returned and no AAAA record is returned\n"
864 "\t* Perform an AAAA record lookup on the first domain\n"
865 "\t* Ensure that the AAAA record is returned and no A record is returned\n"
866 "\t* Perform an A record lookup on the second domain\n"
867 "\t* Ensure that the A record from the second domain is returned";
868 return AST_TEST_NOT_RUN;
869 case TEST_EXECUTE:
870 break;
871 }
872
873 return nominal_test(test, nominal_async_run);
874}
875
876typedef int (*off_nominal_resolve_fn)(struct ast_test *test, const char *domain, int rr_type,
877 int rr_class, int expected_rcode);
878
879static int off_nominal_sync_run(struct ast_test *test, const char *domain, int rr_type,
880 int rr_class, int expected_rcode)
881{
882 struct ast_dns_result *result;
883 int res = 0;
884
885 if (ast_dns_resolve(domain, rr_type, rr_class, &result)) {
886 ast_test_status_update(test, "Failed to perform resolution :(\n");
887 return -1;
888 }
889
890 if (!result) {
891 ast_test_status_update(test, "Resolution returned no result\n");
892 return -1;
893 }
894
895 if (ast_dns_result_get_rcode(result) != expected_rcode) {
896 ast_test_status_update(test, "Unexpected rcode from DNS resolution\n");
897 res = -1;
898 }
899
901 ast_test_status_update(test, "DNS resolution returned records unexpectedly\n");
902 res = -1;
903 }
904
906 return res;
907}
908
909/*!
910 * \brief User data for off-nominal async resolution test
911 */
912struct off_nominal_async_data {
913 /*! The DNS result's expected rcode */
914 int expected_rcode;
915 /*! Whether an asynchronous query failed */
916 int failed;
917 /*! Indicates the asynchronous query is complete */
918 int complete;
921};
922
923static void off_nominal_async_data_destructor(void *obj)
924{
925 struct off_nominal_async_data *adata = obj;
926
927 ast_mutex_destroy(&adata->lock);
928 ast_cond_destroy(&adata->cond);
929}
930
931static struct off_nominal_async_data *off_nominal_async_data_alloc(int expected_rcode)
932{
933 struct off_nominal_async_data *adata;
934
935 adata = ao2_alloc(sizeof(*adata), off_nominal_async_data_destructor);
936 if (!adata) {
937 return NULL;
938 }
939
940 ast_mutex_init(&adata->lock);
941 ast_cond_init(&adata->cond, NULL);
942
943 adata->expected_rcode = expected_rcode;
944
945 return adata;
946}
947
948/*!
949 * \brief Async callback for off-nominal async test
950 *
951 * This test ensures that there is a result present on the query, then it checks
952 * that the rcode on the result is the expected value and that there are no
953 * records on the result.
954 *
955 * Once completed, the testing thread is signaled that the async query has
956 * completed.
957 */
958static void off_nominal_async_callback(const struct ast_dns_query *query)
959{
960 struct off_nominal_async_data *adata = ast_dns_query_get_data(query);
962
963 if (!result) {
964 adata->failed = -1;
965 goto end;
966 }
967
968 if (ast_dns_result_get_rcode(result) != adata->expected_rcode) {
969 adata->failed = -1;
970 }
971
973 adata->failed = -1;
974 }
975
976end:
977 ast_mutex_lock(&adata->lock);
978 adata->complete = 1;
979 ast_cond_signal(&adata->cond);
980 ast_mutex_unlock(&adata->lock);
981}
982
983static int off_nominal_async_run(struct ast_test *test, const char *domain, int rr_type,
984 int rr_class, int expected_rcode)
985{
986 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
987 RAII_VAR(struct off_nominal_async_data *, adata, NULL, ao2_cleanup);
988
989 adata = off_nominal_async_data_alloc(expected_rcode);
990 if (!adata) {
991 ast_test_status_update(test, "Unable to allocate data for async query\n");
992 return -1;
993 }
994
995 ast_test_status_update(test, "Performing DNS query '%s', type %d\n", domain, rr_type);
996
997 active = ast_dns_resolve_async(domain, rr_type, rr_class, off_nominal_async_callback, adata);
998 if (!active) {
999 ast_test_status_update(test, "Failed to perform asynchronous resolution of domain %s\n", domain);
1000 return -1;
1001 }
1002
1003 ast_mutex_lock(&adata->lock);
1004 while (!adata->complete) {
1005 ast_cond_wait(&adata->cond, &adata->lock);
1006 }
1007 ast_mutex_unlock(&adata->lock);
1008
1009 if (adata->failed) {
1010 ast_test_status_update(test, "Asynchronous resolution failure %s\n", domain);
1011 }
1012 return adata->failed;
1013}
1014
1015static enum ast_test_result_state off_nominal_test(struct ast_test *test,
1016 off_nominal_resolve_fn runner)
1017{
1018 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1019 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1020
1021 static const size_t V4_SIZE = sizeof(struct in_addr);
1022
1023 static const char *DOMAIN1 = "goose.feathers";
1024 static const char *DOMAIN2 = "duck.feathers";
1025
1026 static const char *ADDR1 = "127.0.0.2";
1027
1028 char addr1_buf[V4_SIZE];
1029
1030 struct dns_record records [] = {
1031 { "goose.feathers 12345 IN A 127.0.0.2", DOMAIN1, ns_t_a, ns_c_in, 12345, addr1_buf, V4_SIZE, 0, },
1032 };
1033
1034 int i;
1036
1037 struct {
1038 const char *domain;
1039 int rr_type;
1040 int rr_class;
1041 int rcode;
1042 } runs [] = {
1043 { DOMAIN2, ns_t_a, ns_c_in, ns_r_nxdomain },
1044 { DOMAIN1, ns_t_aaaa, ns_c_in, ns_r_noerror },
1045 { DOMAIN1, ns_t_a, ns_c_chaos, ns_r_refused },
1046 };
1047
1048 inet_pton(AF_INET, ADDR1, addr1_buf);
1049
1051 resolver = ao2_bump(cfg->global->state->resolver);
1052
1053 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1054 ub_ctx_zone_add(resolver->context, DOMAIN2, "static");
1055
1056 for (i = 0; i < ARRAY_LEN(records); ++i) {
1057 ub_ctx_data_add(resolver->context, records[i].as_string);
1058 }
1059
1060 for (i = 0; i < ARRAY_LEN(runs); ++i) {
1061 if (runner(test, runs[i].domain, runs[i].rr_type, runs[i].rr_class, runs[i].rcode)) {
1062 res = AST_TEST_FAIL;
1063 }
1064 }
1065
1066 return res;
1067}
1068
1069AST_TEST_DEFINE(resolve_sync_off_nominal)
1070{
1071 switch (cmd) {
1072 case TEST_INIT:
1073 info->name = "resolve_sync_off_nominal";
1074 info->category = "/res/res_resolver_unbound/";
1075 info->summary = "Test off-nominal synchronous resolution using libunbound";
1076 info->description = "This test performs the following:\n"
1077 "\t* Attempt a lookup of a non-existent domain\n"
1078 "\t* Attempt a lookup of a AAAA record on a domain that contains only A records\n"
1079 "\t* Attempt a lookup of an A record on Chaos-net";
1080 return AST_TEST_NOT_RUN;
1081 case TEST_EXECUTE:
1082 break;
1083 }
1084
1085 return off_nominal_test(test, off_nominal_sync_run);
1086}
1087
1088AST_TEST_DEFINE(resolve_async_off_nominal)
1089{
1090 switch (cmd) {
1091 case TEST_INIT:
1092 info->name = "resolve_async_off_nominal";
1093 info->category = "/res/res_resolver_unbound/";
1094 info->summary = "Test off-nominal synchronous resolution using libunbound";
1095 info->description = "This test performs the following:\n"
1096 "\t* Attempt a lookup of a non-existent domain\n"
1097 "\t* Attempt a lookup of a AAAA record on a domain that contains only A records\n"
1098 "\t* Attempt a lookup of an A record on Chaos-net";
1099 return AST_TEST_NOT_RUN;
1100 case TEST_EXECUTE:
1101 break;
1102 }
1103
1104 return off_nominal_test(test, off_nominal_async_run);
1105}
1106
1107/*!
1108 * \brief Minimal data required to signal the completion of an async resolve
1109 */
1110struct async_minimal_data {
1111 int complete;
1114};
1115
1116static void async_minimal_data_destructor(void *obj)
1117{
1118 struct async_minimal_data *adata = obj;
1119
1120 ast_mutex_destroy(&adata->lock);
1121 ast_cond_destroy(&adata->cond);
1122}
1123
1124static struct async_minimal_data *async_minimal_data_alloc(void)
1125{
1126 struct async_minimal_data *adata;
1127
1128 adata = ao2_alloc(sizeof(*adata), async_minimal_data_destructor);
1129 if (!adata) {
1130 return NULL;
1131 }
1132
1133 ast_mutex_init(&adata->lock);
1134 ast_cond_init(&adata->cond, NULL);
1135
1136 return adata;
1137}
1138
1139/*!
1140 * \brief Async callback for off-nominal cancellation test.
1141 *
1142 * This simply signals the testing thread that the query completed
1143 */
1144static void minimal_callback(const struct ast_dns_query *query)
1145{
1146 struct async_minimal_data *adata = ast_dns_query_get_data(query);
1147
1148 ast_mutex_lock(&adata->lock);
1149 adata->complete = 1;
1150 ast_cond_signal(&adata->cond);
1151 ast_mutex_unlock(&adata->lock);
1152}
1153
1154AST_TEST_DEFINE(resolve_cancel_off_nominal)
1155{
1156 RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
1157 RAII_VAR(struct async_minimal_data *, adata, NULL, ao2_cleanup);
1158
1159 switch (cmd) {
1160 case TEST_INIT:
1161 info->name = "resolve_cancel_off_nominal";
1162 info->category = "/res/res_resolver_unbound/";
1163 info->summary = "Off nominal cancellation test using libunbound";
1164 info->description = "This test does the following:\n"
1165 "\t* Perform an asynchronous query\n"
1166 "\t* Once the query has completed, attempt to cancel it";
1167 return AST_TEST_NOT_RUN;
1168 case TEST_EXECUTE:
1169 break;
1170 }
1171
1172 adata = async_minimal_data_alloc();
1173 if (!adata) {
1174 ast_test_status_update(test, "Failed to allocate necessary data for test\n");
1175 return AST_TEST_FAIL;
1176 }
1177
1178 active = ast_dns_resolve_async("crunchy.peanut.butter", ns_t_a, ns_c_in, minimal_callback, adata);
1179 if (!active) {
1180 ast_test_status_update(test, "Failed to perform asynchronous query\n");
1181 return AST_TEST_FAIL;
1182 }
1183
1184 /* Wait for async query to complete */
1185 ast_mutex_lock(&adata->lock);
1186 while (!adata->complete) {
1187 ast_cond_wait(&adata->cond, &adata->lock);
1188 }
1189 ast_mutex_unlock(&adata->lock);
1190
1191 if (!ast_dns_resolve_cancel(active)) {
1192 ast_test_status_update(test, "Successfully canceled completed query\n");
1193 return AST_TEST_FAIL;
1194 }
1195
1196 return AST_TEST_PASS;
1197}
1198
1199AST_TEST_DEFINE(resolve_naptr)
1200{
1201 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1202 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1204
1205 const struct ast_dns_record *record;
1206
1207 static char * DOMAIN1 = "goose.feathers";
1208 int i;
1210
1211 struct naptr_record {
1212 const char *zone_entry;
1213 uint16_t order;
1214 uint16_t preference;
1215 const char *flags;
1216 const char *services;
1217 const char *regexp;
1218 const char *replacement;
1219 int visited;
1220 } records [] = {
1221 { "goose.feathers 12345 IN NAPTR 100 100 A SIP+D2U \"\" goose.down", 100, 100, "A", "SIP+D2U", "", "goose.down", 0},
1222 { "goose.feathers 12345 IN NAPTR 100 200 A SIP+D2T \"\" duck.down", 100, 200, "A", "SIP+D2T", "", "duck.down", 0},
1223 { "goose.feathers 12345 IN NAPTR 200 100 A SIPS+D2U \"\" pheasant.down", 200, 100, "A", "SIPS+D2U", "", "pheasant.down", 0},
1224 { "goose.feathers 12345 IN NAPTR 200 200 A SIPS+D2T \"\" platypus.fur", 200, 200, "A", "SIPS+D2T", "", "platypus.fur", 0},
1225 };
1226
1227 switch (cmd) {
1228 case TEST_INIT:
1229 info->name = "resolve_naptr";
1230 info->category = "/res/res_resolver_unbound/";
1231 info->summary = "Attempt resolution of NAPTR record";
1232 info->description = "This test performs a NAPTR lookup and ensures that\n"
1233 "the returned record has the appropriate values set";
1234 return AST_TEST_NOT_RUN;
1235 case TEST_EXECUTE:
1236 break;
1237 }
1238
1240 resolver = ao2_bump(cfg->global->state->resolver);
1241
1242 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1243
1244 for (i = 0; i < ARRAY_LEN(records); ++i) {
1245 ub_ctx_data_add(resolver->context, records[i].zone_entry);
1246 }
1247
1248 if (ast_dns_resolve(DOMAIN1, ns_t_naptr, ns_c_in, &result)) {
1249 ast_test_status_update(test, "Failed to resolve domain\n");
1250 return AST_TEST_FAIL;
1251 }
1252
1253 if (!result) {
1254 ast_test_status_update(test, "Successful resolution set a NULL result\n");
1255 return AST_TEST_FAIL;
1256 }
1257
1259 if (!record) {
1260 ast_test_status_update(test, "Failed to get any DNS records from the result\n");
1261 return AST_TEST_FAIL;
1262 }
1263
1264 i = 0;
1265 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
1266 if (ast_dns_naptr_get_order(record) != records[i].order) {
1267 ast_test_status_update(test, "Expected order %hu, got order %hu from NAPTR record\n",
1269 res = AST_TEST_FAIL;
1270 }
1271 if (ast_dns_naptr_get_preference(record) != records[i].preference) {
1272 ast_test_status_update(test, "Expected preference %hu, got preference %hu from NAPTR record\n",
1273 records[i].preference, ast_dns_naptr_get_preference(record));
1274 res = AST_TEST_FAIL;
1275 }
1276 if (strcmp(ast_dns_naptr_get_flags(record), records[i].flags)) {
1277 ast_test_status_update(test, "Expected flags %s, got flags %s from NAPTR record\n",
1278 records[i].flags, ast_dns_naptr_get_flags(record));
1279 res = AST_TEST_FAIL;
1280 }
1281 if (strcmp(ast_dns_naptr_get_service(record), records[i].services)) {
1282 ast_test_status_update(test, "Expected services %s, got services %s from NAPTR record\n",
1283 records[i].services, ast_dns_naptr_get_service(record));
1284 res = AST_TEST_FAIL;
1285 }
1286 if (strcmp(ast_dns_naptr_get_regexp(record), records[i].regexp)) {
1287 ast_test_status_update(test, "Expected regexp %s, got regexp %s from NAPTR record\n",
1288 records[i].regexp, ast_dns_naptr_get_regexp(record));
1289 res = AST_TEST_FAIL;
1290 }
1291 if (strcmp(ast_dns_naptr_get_replacement(record), records[i].replacement)) {
1292 ast_test_status_update(test, "Expected replacement %s, got replacement %s from NAPTR record\n",
1293 records[i].replacement, ast_dns_naptr_get_replacement(record));
1294 res = AST_TEST_FAIL;
1295 }
1296 records[i].visited = 1;
1297 ++i;
1298 }
1299
1300 if (i != ARRAY_LEN(records)) {
1301 ast_test_status_update(test, "Unexpected number of records visited\n");
1302 res = AST_TEST_FAIL;
1303 }
1304
1305 for (i = 0; i < ARRAY_LEN(records); ++i) {
1306 if (!records[i].visited) {
1307 ast_test_status_update(test, "Did not visit all expected NAPTR records\n");
1308 res = AST_TEST_FAIL;
1309 }
1310 }
1311
1312 return res;
1313
1314}
1315
1316AST_TEST_DEFINE(resolve_srv)
1317{
1318 RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
1319 RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
1321 const struct ast_dns_record *record;
1322 static const char *DOMAIN1 = "taco.bananas";
1323 static const char *DOMAIN1_SRV = "taco.bananas 12345 IN SRV 10 20 5060 sip.taco.bananas";
1325
1326 switch (cmd) {
1327 case TEST_INIT:
1328 info->name = "resolve_srv";
1329 info->category = "/res/res_resolver_unbound/";
1330 info->summary = "Test synchronous SRV resolution using libunbound";
1331 info->description = "This test performs the following:\n"
1332 "\t* Set one SRV record on one domain\n"
1333 "\t* Perform an SRV lookup on the domain\n"
1334 "\t* Ensure that the SRV record returned matches the expected value";
1335 return AST_TEST_NOT_RUN;
1336 case TEST_EXECUTE:
1337 break;
1338 }
1339
1341 resolver = ao2_bump(cfg->global->state->resolver);
1342
1343 ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
1344 ub_ctx_data_add(resolver->context, DOMAIN1_SRV);
1345
1346 if (ast_dns_resolve(DOMAIN1, ns_t_srv, ns_c_in, &result)) {
1347 ast_test_status_update(test, "Failed to synchronously resolve SRV for domain '%s'\n", DOMAIN1);
1348 res = AST_TEST_FAIL;
1349 goto cleanup;
1350 }
1351
1353 if (ast_dns_srv_get_priority(record) != 10) {
1354 ast_test_status_update(test, "SRV Record returned priority '%d' when we expected 10\n", ast_dns_srv_get_priority(record));
1355 res = AST_TEST_FAIL;
1356 goto cleanup;
1357 }
1358
1359 if (ast_dns_srv_get_weight(record) != 20) {
1360 ast_test_status_update(test, "SRV Record returned weight '%d' when we expected 20\n", ast_dns_srv_get_weight(record));
1361 res = AST_TEST_FAIL;
1362 goto cleanup;
1363 }
1364
1365 if (ast_dns_srv_get_port(record) != 5060) {
1366 ast_test_status_update(test, "SRV Record returned port '%d' when we expected 5060\n", ast_dns_srv_get_port(record));
1367 res = AST_TEST_FAIL;
1368 goto cleanup;
1369 }
1370
1371 if (strcmp(ast_dns_srv_get_host(record), "sip.taco.bananas")) {
1372 ast_test_status_update(test, "SRV Record returned host '%s' when we expected sip.taco.bananas\n", ast_dns_srv_get_host(record));
1373 res = AST_TEST_FAIL;
1374 goto cleanup;
1375 }
1376
1377cleanup:
1378 ub_ctx_data_remove(resolver->context, DOMAIN1_SRV);
1379 ub_ctx_zone_remove(resolver->context, DOMAIN1);
1380
1381 return res;
1382}
1383#endif
1384
1385static int reload_module(void)
1386{
1387 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1389 }
1390
1391 return 0;
1392}
1393
1394static int unload_module(void)
1395{
1396 aco_info_destroy(&cfg_info);
1398
1399 AST_TEST_UNREGISTER(resolve_sync);
1400 AST_TEST_UNREGISTER(resolve_async);
1401 AST_TEST_UNREGISTER(resolve_sync_off_nominal);
1402 AST_TEST_UNREGISTER(resolve_sync_off_nominal);
1403 AST_TEST_UNREGISTER(resolve_cancel_off_nominal);
1404 AST_TEST_UNREGISTER(resolve_naptr);
1405 AST_TEST_UNREGISTER(resolve_srv);
1406 return 0;
1407}
1408
1409static int custom_nameserver_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1410{
1411 struct unbound_global_config *global = obj;
1412
1413 if (!global->nameservers) {
1415 if (!global->nameservers) {
1416 return -1;
1417 }
1418 }
1419
1420 return ast_str_container_add(global->nameservers, var->value);
1421}
1422
1423static int load_module(void)
1424{
1425 struct ast_config *cfg;
1426 struct ast_flags cfg_flags = { 0, };
1427
1428 if (aco_info_init(&cfg_info)) {
1430 }
1431
1432 aco_option_register(&cfg_info, "hosts", ACO_EXACT, global_options, "system", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, hosts));
1433 aco_option_register(&cfg_info, "resolv", ACO_EXACT, global_options, "system", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, resolv));
1436 aco_option_register(&cfg_info, "ta_file", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, ta_file));
1437
1438 /* This purposely checks for a configuration file so we don't output an error message in ACO if one is not present */
1440 if (!cfg) {
1442 unload_module();
1444 }
1445 } else {
1446 ast_config_destroy(cfg);
1447 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1448 unload_module();
1450 }
1451 }
1452
1454
1456
1457 AST_TEST_REGISTER(resolve_sync);
1458 AST_TEST_REGISTER(resolve_async);
1459 AST_TEST_REGISTER(resolve_sync_off_nominal);
1460 AST_TEST_REGISTER(resolve_async_off_nominal);
1461 AST_TEST_REGISTER(resolve_cancel_off_nominal);
1462 AST_TEST_REGISTER(resolve_naptr);
1463 AST_TEST_REGISTER(resolve_srv);
1464
1466}
1467
1468AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Unbound DNS Resolver Support",
1469 .support_level = AST_MODULE_SUPPORT_CORE,
1470 .load = load_module,
1471 .unload = unload_module,
1473 .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:630
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:317
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:464
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ 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:342
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