diff options
author | Joshua Colp <jcolp@digium.com> | 2015-04-13 10:47:01 -0300 |
---|---|---|
committer | Joshua Colp <jcolp@digium.com> | 2015-04-15 10:47:53 -0300 |
commit | a3cec44a0a415e803057a11ab11d80e4f93e10cf (patch) | |
tree | 550c4a127eae12504f06ae3c7a96927173b0bbb6 /main/dns_query_set.c | |
parent | 60d1911482c1dcf44d34e30f252857d75f5d5d77 (diff) |
res_pjsip: Add external PJSIP resolver implementation using core DNS API.
This change adds the following:
1. A query set implementation. This is an API that allows queries to be executed in parallel and once all have completed a callback is invoked.
2. Unit tests for the query set implementation.
3. An external PJSIP resolver which uses the DNS core API to do NAPTR, SRV, AAAA, and A lookups.
For the resolver it will do NAPTR, SRV, and AAAA/A lookups in parallel. If NAPTR or SRV
are available it will then do more queries. And so on. Preference is NAPTR > SRV > AAAA/A,
with IPv6 preferred over IPv4. For transport it will prefer TLS > TCP > UDP if no explicit
transport has been provided. Configured transports on the system are taken into account to
eliminate resolved addresses which have no hope of completing.
ASTERISK-24947 #close
Reported by: Joshua Colp
Change-Id: I56cb03ce4f9d3d600776f36928e0b3e379b5d71e
Diffstat (limited to 'main/dns_query_set.c')
-rw-r--r-- | main/dns_query_set.c | 201 |
1 files changed, 182 insertions, 19 deletions
diff --git a/main/dns_query_set.c b/main/dns_query_set.c index 852fa3e53..c7a4eb18e 100644 --- a/main/dns_query_set.c +++ b/main/dns_query_set.c @@ -33,39 +33,117 @@ ASTERISK_REGISTER_FILE() #include "asterisk/vector.h" #include "asterisk/astobj2.h" +#include "asterisk/utils.h" +#include "asterisk/linkedlists.h" #include "asterisk/dns_core.h" #include "asterisk/dns_query_set.h" +#include "asterisk/dns_internal.h" +#include "asterisk/dns_resolver.h" -/*! \brief A set of DNS queries */ -struct ast_dns_query_set { - /*! \brief DNS queries */ - AST_VECTOR(, struct ast_dns_query *) queries; - /*! \brief The total number of completed queries */ - unsigned int queries_completed; - /*! \brief Callback to invoke upon completion */ - ast_dns_query_set_callback callback; - /*! \brief User-specific data */ - void *user_data; -}; +/*! \brief The default number of expected queries to be added to the query set */ +#define DNS_QUERY_SET_EXPECTED_QUERY_COUNT 5 + +/*! \brief Release all queries held in a query set */ +static void dns_query_set_release(struct ast_dns_query_set *query_set) +{ + int idx; + + for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) { + struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx); + + ao2_ref(query->query, -1); + } + + AST_VECTOR_FREE(&query_set->queries); +} + +/*! \brief Destructor for DNS query set */ +static void dns_query_set_destroy(void *data) +{ + struct ast_dns_query_set *query_set = data; + + dns_query_set_release(query_set); + ao2_cleanup(query_set->user_data); +} struct ast_dns_query_set *ast_dns_query_set_create(void) { - return NULL; + struct ast_dns_query_set *query_set; + + query_set = ao2_alloc_options(sizeof(*query_set), dns_query_set_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!query_set) { + return NULL; + } + + if (AST_VECTOR_INIT(&query_set->queries, DNS_QUERY_SET_EXPECTED_QUERY_COUNT)) { + ao2_ref(query_set, -1); + return NULL; + } + + return query_set; +} + +/*! \brief Callback invoked upon completion of a DNS query */ +static void dns_query_set_callback(const struct ast_dns_query *query) +{ + struct ast_dns_query_set *query_set = ast_dns_query_get_data(query); + + if (ast_atomic_fetchadd_int(&query_set->queries_completed, +1) != (AST_VECTOR_SIZE(&query_set->queries) - 1)) { + return; + } + + /* All queries have been completed, invoke final callback */ + if (query_set->queries_cancelled != AST_VECTOR_SIZE(&query_set->queries)) { + query_set->callback(query_set); + } + + ao2_cleanup(query_set->user_data); + query_set->user_data = NULL; + + dns_query_set_release(query_set); } int ast_dns_query_set_add(struct ast_dns_query_set *query_set, const char *name, int rr_type, int rr_class) { - return -1; + struct dns_query_set_query query = { + .started = 0, + }; + + ast_assert(!query_set->in_progress); + if (query_set->in_progress) { + ast_log(LOG_ERROR, "Attempted to add additional query to query set '%p' after resolution has started\n", + query_set); + return -1; + } + + query.query = dns_query_alloc(name, rr_type, rr_class, dns_query_set_callback, query_set); + if (!query.query) { + return -1; + } + + AST_VECTOR_APPEND(&query_set->queries, query); + + return 0; } size_t ast_dns_query_set_num_queries(const struct ast_dns_query_set *query_set) { - return 0; + return AST_VECTOR_SIZE(&query_set->queries); } struct ast_dns_query *ast_dns_query_set_get(const struct ast_dns_query_set *query_set, unsigned int index) { - return NULL; + /* Only once all queries have been completed can results be retrieved */ + if (query_set->queries_completed != AST_VECTOR_SIZE(&query_set->queries)) { + return NULL; + } + + /* If the index exceeds the number of queries... no query for you */ + if (index >= AST_VECTOR_SIZE(&query_set->queries)) { + return NULL; + } + + return AST_VECTOR_GET_ADDR(&query_set->queries, index)->query; } void *ast_dns_query_set_get_data(const struct ast_dns_query_set *query_set) @@ -75,19 +153,104 @@ void *ast_dns_query_set_get_data(const struct ast_dns_query_set *query_set) void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dns_query_set_callback callback, void *data) { + int idx; + + ast_assert(!query_set->in_progress); + if (query_set->in_progress) { + ast_log(LOG_ERROR, "Attempted to start asynchronous resolution of query set '%p' when it has already started\n", + query_set); + return; + } + + query_set->in_progress = 1; query_set->callback = callback; query_set->user_data = ao2_bump(data); + + for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) { + struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx); + + if (!query->query->resolver->resolve(query->query)) { + query->started = 1; + continue; + } + + dns_query_set_callback(query->query); + } } -void ast_query_set_resolve(struct ast_dns_query_set *query_set) +/*! \brief Structure used for signaling back for synchronous resolution completion */ +struct dns_synchronous_resolve { + /*! \brief Lock used for signaling */ + ast_mutex_t lock; + /*! \brief Condition used for signaling */ + ast_cond_t cond; + /*! \brief Whether the query has completed */ + unsigned int completed; +}; + +/*! \brief Destructor for synchronous resolution structure */ +static void dns_synchronous_resolve_destroy(void *data) { + struct dns_synchronous_resolve *synchronous = data; + + ast_mutex_destroy(&synchronous->lock); + ast_cond_destroy(&synchronous->cond); } -int ast_dns_query_set_resolve_cancel(struct ast_dns_query_set *query_set) +/*! \brief Callback used to implement synchronous resolution */ +static void dns_synchronous_resolve_callback(const struct ast_dns_query_set *query_set) { - return -1; + struct dns_synchronous_resolve *synchronous = ast_dns_query_set_get_data(query_set); + + ast_mutex_lock(&synchronous->lock); + synchronous->completed = 1; + ast_cond_signal(&synchronous->cond); + ast_mutex_unlock(&synchronous->lock); } -void ast_dns_query_set_free(struct ast_dns_query_set *query_set) +int ast_query_set_resolve(struct ast_dns_query_set *query_set) { + struct dns_synchronous_resolve *synchronous; + + synchronous = ao2_alloc_options(sizeof(*synchronous), dns_synchronous_resolve_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!synchronous) { + return -1; + } + + ast_mutex_init(&synchronous->lock); + ast_cond_init(&synchronous->cond, NULL); + + ast_dns_query_set_resolve_async(query_set, dns_synchronous_resolve_callback, synchronous); + + /* Wait for resolution to complete */ + ast_mutex_lock(&synchronous->lock); + while (!synchronous->completed) { + ast_cond_wait(&synchronous->cond, &synchronous->lock); + } + ast_mutex_unlock(&synchronous->lock); + + ao2_ref(synchronous, -1); + + return 0; } + +int ast_dns_query_set_resolve_cancel(struct ast_dns_query_set *query_set) +{ + int idx; + size_t query_count = AST_VECTOR_SIZE(&query_set->queries); + + for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) { + struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx); + + if (query->started) { + if (!query->query->resolver->cancel(query->query)) { + query_set->queries_cancelled++; + dns_query_set_callback(query->query); + } + } else { + query_set->queries_cancelled++; + } + } + + return (query_set->queries_cancelled == query_count) ? 0 : -1; +}
\ No newline at end of file |