mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +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:
@@ -32,7 +32,6 @@
|
||||
ASTERISK_REGISTER_FILE()
|
||||
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/vector.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/strings.h"
|
||||
#include "asterisk/sched.h"
|
||||
@@ -163,6 +162,11 @@ const char *ast_dns_record_get_data(const struct ast_dns_record *record)
|
||||
return record->data_ptr;
|
||||
}
|
||||
|
||||
size_t ast_dns_record_get_data_size(const struct ast_dns_record *record)
|
||||
{
|
||||
return record->data_len;
|
||||
}
|
||||
|
||||
const struct ast_dns_record *ast_dns_record_get_next(const struct ast_dns_record *record)
|
||||
{
|
||||
return AST_LIST_NEXT(record, list);
|
||||
@@ -186,9 +190,9 @@ static void dns_query_destroy(void *data)
|
||||
ast_dns_result_free(query->result);
|
||||
}
|
||||
|
||||
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)
|
||||
struct ast_dns_query *dns_query_alloc(const char *name, int rr_type, int rr_class, ast_dns_resolve_callback callback, void *data)
|
||||
{
|
||||
struct ast_dns_query_active *active;
|
||||
struct ast_dns_query *query;
|
||||
|
||||
if (ast_strlen_zero(name)) {
|
||||
ast_log(LOG_WARNING, "Could not perform asynchronous resolution, no name provided\n");
|
||||
@@ -215,34 +219,46 @@ struct ast_dns_query_active *ast_dns_resolve_async(const char *name, int rr_type
|
||||
return NULL;
|
||||
}
|
||||
|
||||
query = ao2_alloc_options(sizeof(*query) + strlen(name) + 1, dns_query_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
|
||||
if (!query) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
query->callback = callback;
|
||||
query->user_data = ao2_bump(data);
|
||||
query->rr_type = rr_type;
|
||||
query->rr_class = rr_class;
|
||||
strcpy(query->name, name); /* SAFE */
|
||||
|
||||
AST_RWLIST_RDLOCK(&resolvers);
|
||||
query->resolver = AST_RWLIST_FIRST(&resolvers);
|
||||
AST_RWLIST_UNLOCK(&resolvers);
|
||||
|
||||
if (!query->resolver) {
|
||||
ast_log(LOG_ERROR, "Attempted to do a DNS query for '%s' of class '%d' and type '%d' but no resolver is available\n",
|
||||
name, rr_class, rr_type);
|
||||
ao2_ref(query, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct ast_dns_query_active *active;
|
||||
|
||||
active = ao2_alloc_options(sizeof(*active), dns_query_active_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
|
||||
if (!active) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
active->query = ao2_alloc_options(sizeof(*active->query) + strlen(name) + 1, dns_query_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
|
||||
active->query = dns_query_alloc(name, rr_type, rr_class, callback, data);
|
||||
if (!active->query) {
|
||||
ao2_ref(active, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
active->query->callback = callback;
|
||||
active->query->user_data = ao2_bump(data);
|
||||
active->query->rr_type = rr_type;
|
||||
active->query->rr_class = rr_class;
|
||||
strcpy(active->query->name, name); /* SAFE */
|
||||
|
||||
AST_RWLIST_RDLOCK(&resolvers);
|
||||
active->query->resolver = AST_RWLIST_FIRST(&resolvers);
|
||||
AST_RWLIST_UNLOCK(&resolvers);
|
||||
|
||||
if (!active->query->resolver) {
|
||||
ast_log(LOG_ERROR, "Attempted to do a DNS query for '%s' of class '%d' and type '%d' but no resolver is available\n",
|
||||
name, rr_class, rr_type);
|
||||
ao2_ref(active, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (active->query->resolver->resolve(active->query)) {
|
||||
ast_log(LOG_ERROR, "Resolver '%s' returned an error when resolving '%s' of class '%d' and type '%d'\n",
|
||||
active->query->resolver->name, name, rr_class, rr_type);
|
||||
|
@@ -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