mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-02 19:16:15 +00:00
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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
/*! \brief Callback used to implement synchronous resolution */
|
||||
static void dns_synchronous_resolve_callback(const struct ast_dns_query_set *query_set)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
int idx;
|
||||
size_t query_count = AST_VECTOR_SIZE(&query_set->queries);
|
||||
|
||||
void ast_dns_query_set_free(struct ast_dns_query_set *query_set)
|
||||
{
|
||||
}
|
||||
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;
|
||||
}
|
Reference in New Issue
Block a user