Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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"
31 #include "asterisk/linkedlists.h"
32 #include "asterisk/dns_core.h"
33 #include "asterisk/dns_resolver.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  */
127 static void *unbound_config_alloc(void);
128 
129 /*! \brief An aco_type structure to link the "general" category to the unbound_global_config type */
130 static 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 
138 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
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  */
152 static int unbound_config_preapply_callback(void);
153 
154 /*! \brief Register information about the configs being processed by this module */
156  .files = ACO_FILES(&resolver_unbound_conf),
157  .pre_apply_config = unbound_config_preapply_callback,
158 );
159 
160 /*! \brief Destructor for unbound resolver */
161 static 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 */
195 static 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 */
217 static 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 */
237 static 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 */
256 static 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 
278 static void unbound_resolver_data_dtor(void *vdoomed)
279 {
280  struct unbound_resolver_data *doomed = vdoomed;
281 
282  ao2_cleanup(doomed->resolver);
283 }
284 
285 static int unbound_resolver_resolve(struct ast_dns_query *query)
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",
295  ast_dns_query_get_name(query));
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",
307  ast_dns_query_get_name(query));
308  ao2_ref(query, -1);
309  }
310 
311  ao2_ref(data, -1);
312  ao2_ref(cfg, -1);
313  return res;
314 }
315 
316 static int unbound_resolver_cancel(struct ast_dns_query *query)
317 {
318  struct unbound_resolver_data *data = ast_dns_resolver_get_data(query);
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 
339 static void unbound_config_destructor(void *obj)
340 {
341  struct unbound_config *cfg = obj;
342 
343  ao2_cleanup(cfg->global);
344 }
345 
346 static void unbound_global_config_destructor(void *obj)
347 {
348  struct unbound_global_config *global = obj;
349 
351  ao2_cleanup(global->nameservers);
352  ao2_cleanup(global->state);
353 }
354 
355 static void unbound_config_state_destructor(void *obj)
356 {
357  struct unbound_config_state *state = obj;
358 
359  if (state->resolver) {
361  ao2_ref(state->resolver, -1);
362  }
363 }
364 
365 static 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;
386 error:
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 {
499  return unbound_config_preapply(aco_pending_config(&cfg_info));
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  */
509 struct 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  */
545 typedef 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  */
551 static 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  */
602 struct async_data {
603  /*! The set of DNS records on a test */
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 
615 static 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 
623 static 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  */
649 static 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 
682 end:
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  */
695 static 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  */
743 static 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 
817 cleanup:
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 
827 AST_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 
852 AST_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 
876 typedef int (*off_nominal_resolve_fn)(struct ast_test *test, const char *domain, int rr_type,
877  int rr_class, int expected_rcode);
878 
879 static 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 
900  if (ast_dns_result_get_records(result)) {
901  ast_test_status_update(test, "DNS resolution returned records unexpectedly\n");
902  res = -1;
903  }
904 
905  ast_dns_result_free(result);
906  return res;
907 }
908 
909 /*!
910  * \brief User data for off-nominal async resolution test
911  */
913  /*! The DNS result's expected rcode */
915  /*! Whether an asynchronous query failed */
916  int failed;
917  /*! Indicates the asynchronous query is complete */
918  int complete;
921 };
922 
923 static 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 
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 
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  */
958 static 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 
972  if (ast_dns_result_get_records(result)) {
973  adata->failed = -1;
974  }
975 
976 end:
977  ast_mutex_lock(&adata->lock);
978  adata->complete = 1;
979  ast_cond_signal(&adata->cond);
980  ast_mutex_unlock(&adata->lock);
981 }
982 
983 static 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 
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 
1050  cfg = ao2_global_obj_ref(globals);
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 
1069 AST_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 
1088 AST_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 
1105 }
1106 
1107 /*!
1108  * \brief Minimal data required to signal the completion of an async resolve
1109  */
1114 };
1115 
1116 static 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 
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  */
1144 static 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 
1154 AST_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 
1199 AST_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 
1239  cfg = ao2_global_obj_ref(globals);
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",
1268  records[i].order, ast_dns_naptr_get_order(record));
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 
1316 AST_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 
1340  cfg = ao2_global_obj_ref(globals);
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 
1377 cleanup:
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 
1385 static int reload_module(void)
1386 {
1387  if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1388  return AST_MODULE_RELOAD_ERROR;
1389  }
1390 
1391  return 0;
1392 }
1393 
1394 static 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 
1409 static 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 
1423 static 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)) {
1429  return AST_MODULE_LOAD_DECLINE;
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));
1434  aco_option_register_custom(&cfg_info, "nameserver", ACO_EXACT, global_options, "", custom_nameserver_handler, 0);
1435  aco_option_register(&cfg_info, "debug", ACO_EXACT, global_options, "0", OPT_UINT_T, 0, FLDSET(struct unbound_global_config, debug));
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 */
1439  cfg = ast_config_load(resolver_unbound_conf.filename, cfg_flags);
1440  if (!cfg) {
1442  unload_module();
1443  return AST_MODULE_LOAD_DECLINE;
1444  }
1445  } else {
1446  ast_config_destroy(cfg);
1447  if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1448  unload_module();
1449  return AST_MODULE_LOAD_DECLINE;
1450  }
1451  }
1452 
1453  ast_dns_resolver_register(&unbound_resolver);
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 
1465  return AST_MODULE_LOAD_SUCCESS;
1466 }
1467 
1468 AST_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,
1472  .reload = reload_module,
1473  .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
1474 );
int(* off_nominal_resolve_fn)(struct ast_test *test, const char *domain, int rr_type, int rr_class, int expected_rcode)
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
int ast_dns_record_get_ttl(const struct ast_dns_record *record)
Get the TTL of a DNS record.
Definition: dns_core.c:155
Data required for an asynchronous callback.
static void * unbound_config_alloc(void)
Allocate a unbound_config to hold a snapshot of the complete results of parsing a config...
static int records
Definition: cdr_mysql.c:84
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
const ast_string_field hosts
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_dns_record_get_data(const struct ast_dns_record *record)
Retrieve the raw DNS record.
Definition: dns_core.c:160
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
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static void unbound_resolver_destroy(void *obj)
Destructor for unbound resolver.
void ast_dns_result_free(struct ast_dns_result *result)
Free the DNS result information.
Definition: dns_core.c:130
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
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
static void unbound_resolver_data_dtor(void *vdoomed)
static struct aco_type global
Definition: test_config.c:1445
static struct off_nominal_async_data * off_nominal_async_data_alloc(int expected_rcode)
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
static int unload_module(void)
A container for config related information.
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
Unbound configuration state information.
const ast_string_field resolv
const ast_string_field ta_file
ast_mutex_t lock
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
const size_t bufsize
static int debug
Global debug status.
Definition: res_xmpp.c:435
For AST_LIST.
Definition: dns_internal.h:39
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
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
static enum ast_test_result_state nominal_test(struct ast_test *test, resolve_fn runner)
Framework for running a nominal DNS test.
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
Test Framework API.
A DNS record to be used during a test.
const char * as_string
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
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
#define ast_cond_wait(cond, mutex)
Definition: lock.h:203
#define ast_cond_init(cond, attr)
Definition: lock.h:199
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 ao2_global_obj_ref(holder)
Definition: astobj2.h:925
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
struct unbound_resolver * resolver
The configured resolver.
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:406
#define ast_mutex_lock(a)
Definition: lock.h:187
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
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:2315
static int off_nominal_async_run(struct ast_test *test, const char *domain, int rr_type, int rr_class, int expected_rcode)
#define NULL
Definition: resample.c:96
const char * domain
Domain data structure.
Definition: sip.h:888
static AO2_GLOBAL_OBJ_STATIC(globals)
A global object container that will contain the global_config that gets swapped out on reloads...
char * end
Definition: eagi_proxy.c:73
struct unbound_config_state * state
State information.
The representation of a single configuration file to be processed.
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
enum aco_type_t type
void * ast_dns_resolver_get_data(const struct ast_dns_query *query)
Retrieve resolver specific data.
Definition: dns_core.c:451
#define ast_cond_signal(cond)
Definition: lock.h:201
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
struct unbound_global_config * global
#define ast_verb(level,...)
Definition: logger.h:463
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
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
Structure for query resolver data.
int(* resolve_fn)(struct ast_test *test, const char *domain, int rr_type, int rr_class, struct dns_record *records, size_t num_records)
Resolution function for tests.
pthread_cond_t ast_cond_t
Definition: lock.h:176
#define ast_strlen_zero(foo)
Definition: strings.h:52
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
#define ao2_bump(obj)
Definition: astobj2.h:491
static int nominal_sync_run(struct ast_test *test, const char *domain, int rr_type, int rr_class, struct dns_record *records, size_t num_records)
Pluggable function for running a synchronous query and checking its results.
static int custom_nameserver_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static int nominal_async_run(struct ast_test *test, const char *domain, int rr_type, int rr_class, struct dns_record *records, size_t num_records)
Pluggable function for performing an asynchronous query during a test.
Configuration File Parser.
A structure to hold global configuration-related options.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define ast_config_load(filename, flags)
Load a config file.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
struct ast_module * self
Definition: module.h:342
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
pthread_t thread
Thread handling the resolver.
static void off_nominal_async_data_destructor(void *obj)
User data for off-nominal async resolution test.
void * aco_pending_config(struct aco_info *info)
Get pending config changes.
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:201
#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:911
Type for default option handler for unsigned integers.
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
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
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
integer order
Definition: analys.c:66
#define AST_PTHREADT_NULL
Definition: lock.h:66
static int reload_module(void)
int ast_dns_resolver_register(struct ast_dns_resolver *resolver)
Register a DNS resolver.
Definition: dns_core.c:630
static int load_module(void)
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
#define ao2_ref(o, delta)
Definition: astobj2.h:464
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
static void unbound_global_config_destructor(void *obj)
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_resolve(struct ast_dns_query *query)
A set of macros to manage forward-linked lists.
static struct console_pvt globals
static void * unbound_resolver_thread(void *data)
Resolver thread which waits and handles results.
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.
Their was an error and no changes were applied.
The result of a DNS query.
Definition: dns_internal.h:117
void ast_dns_resolver_completed(struct ast_dns_query *query)
Mark a DNS query as having been completed.
Definition: dns_core.c:599
static void async_callback(const struct ast_dns_query *query)
Callback for asynchronous queries.
static int unbound_resolver_cancel(struct ast_dns_query *query)
Configuration option-handling.
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
static void unbound_config_state_destructor(void *obj)
#define LOG_ERROR
Definition: logger.h:285
static struct unbound_resolver * unbound_resolver_alloc(void)
Allocator for unbound resolver.
static int unbound_resolver_start(struct unbound_resolver *resolver)
Start function for the unbound resolver.
An active DNS query.
Definition: dns_internal.h:201
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define ao2_global_obj_release(holder)
Definition: astobj2.h:865
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
static void async_minimal_data_destructor(void *obj)
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
static void unbound_config_destructor(void *obj)
def info(msg)
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:464
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
#define ast_cond_destroy(cond)
Definition: lock.h:200
Minimal data required to signal the completion of an async resolve.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static int unbound_config_preapply(struct unbound_config *cfg)
static int off_nominal_sync_run(struct ast_test *test, const char *domain, int rr_type, int rr_class, int expected_rcode)
DNS resolver implementation.
Definition: dns_resolver.h:32
static int unbound_config_apply_default(void)
static void off_nominal_async_callback(const struct ast_dns_query *query)
Async callback for off-nominal async test.
A DNS query.
Definition: dns_internal.h:137
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:559
static int reload(void)
Definition: cdr_mysql.c:741
int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
Set all default options of obj.
static enum ast_test_result_state off_nominal_test(struct ast_test *test, off_nominal_resolve_fn runner)
struct dns_record * records
AST_TEST_DEFINE(resolve_sync)
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
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
const char * buf
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
Structure used to handle boolean flags.
Definition: utils.h:199
static int unbound_config_preapply_callback(void)
Finish initializing new configuration.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
struct ub_ctx * context
Resolver context itself.
int id
ID for the specific query.
struct ao2_container * nameservers
List of nameservers (in order) to use for queries.
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
#define ao2_global_obj_replace_unref(holder, obj)
Definition: astobj2.h:908
#define ACO_FILES(...)
DNS NAPTR Record Parsing API.
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
Definition: test.c:65
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Type information about a category-level configurable object.
static void async_data_destructor(void *obj)
int ast_dns_resolve_cancel(struct ast_dns_query_active *active)
Cancel an asynchronous DNS resolution.
Definition: dns_core.c:272
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
static struct async_data * async_data_alloc(struct dns_record *records, size_t num_records)
const char * filename
#define AST_PTHREADT_STOP
Definition: lock.h:67
static void minimal_callback(const struct ast_dns_query *query)
Async callback for off-nominal cancellation test.
static struct async_minimal_data * async_minimal_data_alloc(void)
static PGresult * result
Definition: cel_pgsql.c:88
static void unbound_resolver_stop(struct unbound_resolver *resolver)
Stop function for the unbound resolver.
Structure for an unbound resolver.
static struct aco_type * global_options[]
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
Type for default option handler for stringfields.
static struct aco_file resolver_unbound_conf
int error(const char *format,...)
Definition: utils/frame.c:999
int ast_wait_for_input(int fd, int ms)
Definition: main/utils.c:1519
DNS SRV Record Parsing API.
#define ast_mutex_init(pmutex)
Definition: lock.h:184
Generic container type.
static struct aco_type global_option
An aco_type structure to link the "general" category to the unbound_global_config type...
#define ast_mutex_destroy(a)
Definition: lock.h:186
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
struct unbound_resolver * resolver
The resolver in use for the query.
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
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
unsigned int debug
Debug level for the resolver.
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
Asterisk module definitions.
int rr_type
Resource record type.
Definition: dns_internal.h:41
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
DNS Resolver API.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
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:206
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
Structure for mutex and tracking information.
Definition: lock.h:135
Core DNS API.
ast_test_result_state
Definition: test.h:200
static struct ast_test * test
Definition: localtime.c:115
#define ast_mutex_unlock(a)
Definition: lock.h:188
int rr_class
Resource record class.
Definition: dns_internal.h:43