mirror of https://github.com/asterisk/asterisk
This change adds an abstracted core DNS API which resembles the API described here[1]. The API provides a pluggable mechanism for resolvers and also a consistent view for records. Both synchronous and asynchronous queries are supported. This change also adds a res_resolver_unbound module which uses the libunbound library to provide resolution. Unit tests have also been written for all of the above to confirm the API and functionality. ASTERISK-24834 #close Reported by: Matt Jordan ASTERISK-24836 #close Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/4474/ Review: https://reviewboard.asterisk.org/r/4512/ [1] https://wiki.asterisk.org/wiki/display/AST/Asterisk+DNS+API git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@433370 65c4cc65-6c06-0410-ace0-fbb531ad65f3changes/42/42/1
parent
4c2fc5b811
commit
abf3e40902
@ -0,0 +1,24 @@
|
||||
; Unbound DNS Resolver Configuration
|
||||
;
|
||||
; This file serves as a reference for the configurable options within the
|
||||
; unbound DNS resolver.
|
||||
|
||||
[general]
|
||||
;hosts = /etc/hosts ; Full path to a hosts file which contains a mapping of
|
||||
; ; hostnames to addresses. If "system" is specified then
|
||||
; ; the system specific hosts file will be used. (default: system)
|
||||
;resolv = /etc/resolv.conf ; Full path to a resolv.conf which contains the nameservers
|
||||
; ; to use for resolution. If "system" is specified then the
|
||||
; ; system specific resolv.conf file will be used. (default: system)
|
||||
;nameserver = 127.0.0.1 ; An explicit nameserver to use for queries. If this option
|
||||
; ; is specified multiple times the first configured one will
|
||||
; ; be treated as the primary with each subsequent one being
|
||||
; ; a backup. If the resolv options is also specified the
|
||||
; ; nameservers from it will be tried after all nameserver
|
||||
; ; options.
|
||||
;debug = 99 ; The debug level to run the unbound resolver at. While
|
||||
; ; there is no explicit range the higher the number the more
|
||||
; ; debug is output.
|
||||
;ta_file = /etc/asterisk/dnssec_keys ; Full path to a trusted anchors key file. These keys are
|
||||
; ; used to verify DNSSEC signed results.
|
||||
|
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief Core DNS API
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_DNS_CORE_H
|
||||
#define _ASTERISK_DNS_CORE_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*! \brief Opaque structure for an active DNS query */
|
||||
struct ast_dns_query_active;
|
||||
|
||||
/*! \brief Opaque structure for a DNS query */
|
||||
struct ast_dns_query;
|
||||
|
||||
/*!
|
||||
* \brief Get the name queried in a DNS query
|
||||
*
|
||||
* \param query The DNS query
|
||||
*
|
||||
* \return the name queried
|
||||
*/
|
||||
const char *ast_dns_query_get_name(const struct ast_dns_query *query);
|
||||
|
||||
/*!
|
||||
* \brief Get the record resource type of a DNS query
|
||||
*
|
||||
* \param query The DNS query
|
||||
*
|
||||
* \return the record resource type
|
||||
*/
|
||||
int ast_dns_query_get_rr_type(const struct ast_dns_query *query);
|
||||
|
||||
/*!
|
||||
* \brief Get the record resource class of a DNS query
|
||||
*
|
||||
* \param query The DNS query
|
||||
*
|
||||
* \return the record resource class
|
||||
*/
|
||||
int ast_dns_query_get_rr_class(const struct ast_dns_query *query);
|
||||
|
||||
/*!
|
||||
* \brief Get the user specific data of a DNS query
|
||||
*
|
||||
* \param query The DNS query
|
||||
*
|
||||
* \return the user specific data
|
||||
*
|
||||
* \note The reference count of the data is NOT incremented on return
|
||||
*/
|
||||
void *ast_dns_query_get_data(const struct ast_dns_query *query);
|
||||
|
||||
/*! \brief Opaque structure for a DNS query result, guaranteed to be immutable */
|
||||
struct ast_dns_result;
|
||||
|
||||
/*!
|
||||
* \brief Get the result information for a DNS query
|
||||
*
|
||||
* \param query The DNS query
|
||||
*
|
||||
* \return the DNS result information
|
||||
*
|
||||
* \note The result is NOT ao2 allocated
|
||||
*/
|
||||
struct ast_dns_result *ast_dns_query_get_result(const struct ast_dns_query *query);
|
||||
|
||||
/*!
|
||||
* \brief Get whether the result is secure or not
|
||||
*
|
||||
* \param result The DNS result
|
||||
*
|
||||
* \return whether the result is secure or not
|
||||
*/
|
||||
unsigned int ast_dns_result_get_secure(const struct ast_dns_result *result);
|
||||
|
||||
/*!
|
||||
* \brief Get whether the result is bogus or not
|
||||
*
|
||||
* \param result The DNS result
|
||||
*
|
||||
* \return whether the result is bogus or not
|
||||
*/
|
||||
unsigned int ast_dns_result_get_bogus(const struct ast_dns_result *result);
|
||||
|
||||
/*!
|
||||
* \brief Get the error rcode of a DN result
|
||||
*
|
||||
* \param query The DNS result
|
||||
*
|
||||
* \return the DNS rcode
|
||||
*/
|
||||
unsigned int ast_dns_result_get_rcode(const struct ast_dns_result *result);
|
||||
|
||||
/*!
|
||||
* \brief Get the canonical name of the result
|
||||
*
|
||||
* \param result The DNS result
|
||||
*
|
||||
* \return the canonical name
|
||||
*/
|
||||
const char *ast_dns_result_get_canonical(const struct ast_dns_result *result);
|
||||
|
||||
/*!
|
||||
* \brief Get the first record of a DNS Result
|
||||
*
|
||||
* \param result The DNS result
|
||||
*
|
||||
* \return first DNS record
|
||||
*/
|
||||
const struct ast_dns_record *ast_dns_result_get_records(const struct ast_dns_result *result);
|
||||
|
||||
/*!
|
||||
* \brief Get the raw DNS answer from a DNS result
|
||||
*
|
||||
* \param result The DNS result
|
||||
*
|
||||
* \return The DNS result
|
||||
*/
|
||||
const char *ast_dns_result_get_answer(const struct ast_dns_result *result);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the lowest TTL from a result
|
||||
*
|
||||
* \param result The DNS result
|
||||
*
|
||||
* \return the lowest TTL
|
||||
*
|
||||
* \note If no records exist this function will return a TTL of 0
|
||||
*/
|
||||
int ast_dns_result_get_lowest_ttl(const struct ast_dns_result *result);
|
||||
|
||||
/*!
|
||||
* \brief Free the DNS result information
|
||||
*
|
||||
* \param result The DNS result
|
||||
*/
|
||||
void ast_dns_result_free(struct ast_dns_result *result);
|
||||
|
||||
/*! \brief Opaque structure for a DNS record */
|
||||
struct ast_dns_record;
|
||||
|
||||
/*!
|
||||
* \brief Callback invoked when a query completes
|
||||
*
|
||||
* \param query The DNS query that was invoked
|
||||
*/
|
||||
typedef void (*ast_dns_resolve_callback)(const struct ast_dns_query *query);
|
||||
|
||||
/*!
|
||||
* \brief Get the resource record type of a DNS record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the resource record type
|
||||
*/
|
||||
int ast_dns_record_get_rr_type(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the resource record class of a DNS record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the resource record class
|
||||
*/
|
||||
int ast_dns_record_get_rr_class(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the TTL of a DNS record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the TTL
|
||||
*/
|
||||
int ast_dns_record_get_ttl(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the raw DNS record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the raw DNS record
|
||||
*/
|
||||
const char *ast_dns_record_get_data(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the next DNS record
|
||||
*
|
||||
* \param record The current DNS record
|
||||
*
|
||||
* \return the next DNS record
|
||||
*/
|
||||
const struct ast_dns_record *ast_dns_record_get_next(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Asynchronously resolve a DNS query
|
||||
*
|
||||
* \param name The name of what to resolve
|
||||
* \param rr_type Resource record type
|
||||
* \param rr_class Resource record class
|
||||
* \param callback The callback to invoke upon completion
|
||||
* \param data User data to make available on the query
|
||||
*
|
||||
* \retval non-NULL success - query has been sent for resolution
|
||||
* \retval NULL failure
|
||||
*
|
||||
* \note The result passed to the callback does not need to be freed
|
||||
*
|
||||
* \note The user data MUST be an ao2 object
|
||||
*
|
||||
* \note This function increments the reference count of the user data, it does NOT steal
|
||||
*
|
||||
* \note The active query must be released upon completion or cancellation using ao2_ref
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Cancel an asynchronous DNS resolution
|
||||
*
|
||||
* \param active The active DNS query returned from ast_dns_resolve_async
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*
|
||||
* \note If successfully cancelled the callback will not be invoked
|
||||
*/
|
||||
int ast_dns_resolve_cancel(struct ast_dns_query_active *active);
|
||||
|
||||
/*!
|
||||
* \brief Synchronously resolve a DNS query
|
||||
*
|
||||
* \param name The name of what to resolve
|
||||
* \param rr_type Resource record type
|
||||
* \param rr_class Resource record class
|
||||
* \param result A pointer to hold the DNS result
|
||||
*
|
||||
* \retval 0 success - query was completed and result is available
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_dns_resolve(const char *name, int rr_type, int rr_class, struct ast_dns_result **result);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ASTERISK_DNS_CORE_H */
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Internal DNS structure definitions
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
/*! \brief Generic DNS record information */
|
||||
struct ast_dns_record {
|
||||
/*! \brief Resource record type */
|
||||
int rr_type;
|
||||
/*! \brief Resource record class */
|
||||
int rr_class;
|
||||
/*! \brief Time-to-live of the record */
|
||||
int ttl;
|
||||
/*! \brief The size of the raw DNS record */
|
||||
size_t data_len;
|
||||
/*! \brief Linked list information */
|
||||
AST_LIST_ENTRY(ast_dns_record) list;
|
||||
/*! \brief The raw DNS record */
|
||||
char data[0];
|
||||
};
|
||||
|
||||
/*! \brief An SRV record */
|
||||
struct ast_dns_srv_record {
|
||||
/*! \brief Generic DNS record information */
|
||||
struct ast_dns_record generic;
|
||||
/*! \brief The hostname in the SRV record */
|
||||
const char *host;
|
||||
/*! \brief The priority of the SRV record */
|
||||
unsigned short priority;
|
||||
/*! \brief The weight of the SRV record */
|
||||
unsigned short weight;
|
||||
/*! \brief The port in the SRV record */
|
||||
unsigned short port;
|
||||
};
|
||||
|
||||
/*! \brief A NAPTR record */
|
||||
struct ast_dns_naptr_record {
|
||||
/*! \brief Generic DNS record information */
|
||||
struct ast_dns_record generic;
|
||||
/*! \brief The flags from the NAPTR record */
|
||||
const char *flags;
|
||||
/*! \brief The service from the NAPTR record */
|
||||
const char *service;
|
||||
/*! \brief The regular expression from the NAPTR record */
|
||||
const char *regexp;
|
||||
/*! \brief The replacement from the NAPTR record */
|
||||
const char *replacement;
|
||||
/*! \brief The order for the NAPTR record */
|
||||
unsigned short order;
|
||||
/*! \brief The preference of the NAPTR record */
|
||||
unsigned short preference;
|
||||
};
|
||||
|
||||
/*! \brief The result of a DNS query */
|
||||
struct ast_dns_result {
|
||||
/*! \brief Whether the result is secure */
|
||||
unsigned int secure;
|
||||
/*! \brief Whether the result is bogus */
|
||||
unsigned int bogus;
|
||||
/*! \brief Optional rcode, set if an error occurred */
|
||||
unsigned int rcode;
|
||||
/*! \brief Records returned */
|
||||
AST_LIST_HEAD_NOLOCK(, ast_dns_record) records;
|
||||
/*! \brief The canonical name */
|
||||
const char *canonical;
|
||||
/*! \brief The raw DNS answer */
|
||||
const char *answer;
|
||||
/*! \brief Buffer for dynamic data */
|
||||
char buf[0];
|
||||
};
|
||||
|
||||
/*! \brief A DNS query */
|
||||
struct ast_dns_query {
|
||||
/*! \brief Callback to invoke upon completion */
|
||||
ast_dns_resolve_callback callback;
|
||||
/*! \brief User-specific data */
|
||||
void *user_data;
|
||||
/*! \brief The resolver in use for this query */
|
||||
struct ast_dns_resolver *resolver;
|
||||
/*! \brief Resolver-specific data */
|
||||
void *resolver_data;
|
||||
/*! \brief Result of the DNS query */
|
||||
struct ast_dns_result *result;
|
||||
/*! \brief Resource record type */
|
||||
int rr_type;
|
||||
/*! \brief Resource record class */
|
||||
int rr_class;
|
||||
/*! \brief The name of what is being resolved */
|
||||
char name[0];
|
||||
};
|
||||
|
||||
/*! \brief A recurring DNS query */
|
||||
struct ast_dns_query_recurring {
|
||||
/*! \brief Callback to invoke upon completion */
|
||||
ast_dns_resolve_callback callback;
|
||||
/*! \brief User-specific data */
|
||||
void *user_data;
|
||||
/*! \brief Current active query */
|
||||
struct ast_dns_query_active *active;
|
||||
/*! \brief The recurring query has been cancelled */
|
||||
unsigned int cancelled;
|
||||
/*! \brief Scheduled timer for next resolution */
|
||||
int timer;
|
||||
/*! \brief Resource record type */
|
||||
int rr_type;
|
||||
/*! \brief Resource record class */
|
||||
int rr_class;
|
||||
/*! \brief The name of what is being resolved */
|
||||
char name[0];
|
||||
};
|
||||
|
||||
/*! \brief An active DNS query */
|
||||
struct ast_dns_query_active {
|
||||
/*! \brief The underlying DNS query */
|
||||
struct ast_dns_query *query;
|
||||
};
|
||||
|
||||
struct ast_sched_context;
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the DNS scheduler context
|
||||
*
|
||||
* \return scheduler context
|
||||
*/
|
||||
struct ast_sched_context *ast_dns_get_sched(void);
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief DNS NAPTR Record Parsing API
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_DNS_NAPTR_H
|
||||
#define _ASTERISK_DNS_NAPTR_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Get the flags from a NAPTR record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the flags
|
||||
*/
|
||||
const char *ast_dns_naptr_get_flags(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the service from a NAPTR record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the service
|
||||
*/
|
||||
const char *ast_dns_naptr_get_service(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the regular expression from a NAPTR record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the regular expression
|
||||
*/
|
||||
const char *ast_dns_naptr_get_regexp(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the replacement value from a NAPTR record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the replacement value
|
||||
*/
|
||||
const char *ast_dns_naptr_get_replacement(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the order from a NAPTR record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the order
|
||||
*/
|
||||
unsigned short ast_dns_naptr_get_order(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the preference from a NAPTR record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the preference
|
||||
*/
|
||||
unsigned short ast_dns_naptr_get_preference(const struct ast_dns_record *record);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ASTERISK_DNS_NAPTR_H */
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief DNS Query Set API
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_DNS_QUERY_SET_H
|
||||
#define _ASTERISK_DNS_QUERY_SET_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*! \brief Opaque structure for a set of DNS queries */
|
||||
struct ast_dns_query_set;
|
||||
|
||||
/*!
|
||||
* \brief Callback invoked when a query set completes
|
||||
*
|
||||
* \param query_set The DNS query set that was invoked
|
||||
*/
|
||||
typedef void (*ast_dns_query_set_callback)(const struct ast_dns_query_set *query_set);
|
||||
|
||||
/*!
|
||||
* \brief Create a query set to hold queries
|
||||
*
|
||||
* \retval non-NULL success
|
||||
* \retval NULL failure
|
||||
*/
|
||||
struct ast_dns_query_set *ast_dns_query_set_create(void);
|
||||
|
||||
/*!
|
||||
* \brief Add a query to a query set
|
||||
*
|
||||
* \param query_set A DNS query set
|
||||
* \param name The name of what to resolve
|
||||
* \param rr_type Resource record type
|
||||
* \param rr_class Resource record class
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_dns_query_set_add(struct ast_dns_query_set *query_set, const char *name, int rr_type, int rr_class);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the number of queries in a query set
|
||||
*
|
||||
* \param query_set A DNS query set
|
||||
*
|
||||
* \return the number of queries
|
||||
*/
|
||||
size_t ast_dns_query_set_num_queries(const struct ast_dns_query_set *query_set);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve a query from a query set
|
||||
*
|
||||
* \param query_set A DNS query set
|
||||
* \param index The index of the query to retrieve
|
||||
*
|
||||
* \retval non-NULL success
|
||||
* \retval NULL failure
|
||||
*/
|
||||
struct ast_dns_query *ast_dns_query_set_get(const struct ast_dns_query_set *query_set, unsigned int index);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve user specific data from a query set
|
||||
*
|
||||
* \param query_set A DNS query set
|
||||
*
|
||||
* \return user specific data
|
||||
*/
|
||||
void *ast_dns_query_set_get_data(const struct ast_dns_query_set *query_set);
|
||||
|
||||
/*!
|
||||
* \brief Asynchronously resolve queries in a query set
|
||||
*
|
||||
* \param query_set The query set
|
||||
* \param callback The callback to invoke upon completion
|
||||
* \param data User data to make available on the query set
|
||||
*
|
||||
* \note The callback will be invoked when all queries have completed
|
||||
*
|
||||
* \note The user data passed in to this function must be ao2 allocated
|
||||
*/
|
||||
void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dns_query_set_callback callback, void *data);
|
||||
|
||||
/*!
|
||||
* \brief Synchronously resolve queries in a query set
|
||||
*
|
||||
* \param query_set The query set
|
||||
*
|
||||
* \note This function will return when all queries have been completed
|
||||
*/
|
||||
void ast_query_set_resolve(struct ast_dns_query_set *query_set);
|
||||
|
||||
/*!
|
||||
* \brief Cancel an asynchronous DNS query set resolution
|
||||
*
|
||||
* \param query_set The DNS query set
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*
|
||||
* \note If successfully cancelled the callback will not be invoked
|
||||
*/
|
||||
int ast_dns_query_set_resolve_cancel(struct ast_dns_query_set *query_set);
|
||||
|
||||
/*!
|
||||
* \brief Free a query set
|
||||
*
|
||||
* \param query_set A DNS query set
|
||||
*/
|
||||
void ast_dns_query_set_free(struct ast_dns_query_set *query_set);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ASTERISK_DNS_QUERY_SET_H */
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief DNS Recurring Resolution API
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_DNS_RECURRING_H
|
||||
#define _ASTERISK_DNS_RECURRING_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*! \brief Opaque structure for a recurring DNS query */
|
||||
struct ast_dns_query_recurring;
|
||||
|
||||
/*!
|
||||
* \brief Asynchronously resolve a DNS query, and continue resolving it according to the lowest TTL available
|
||||
*
|
||||
* \param name The name of what to resolve
|
||||
* \param rr_type Resource record type
|
||||
* \param rr_class Resource record class
|
||||
* \param callback The callback to invoke upon completion
|
||||
* \param data User data to make available on the query
|
||||
*
|
||||
* \retval non-NULL success - query has been sent for resolution
|
||||
* \retval NULL failure
|
||||
*
|
||||
* \note The user data passed in to this function must be ao2 allocated
|
||||
*
|
||||
* \note This query will continue to happen according to the lowest TTL unless cancelled using ast_dns_resolve_recurring_cancel
|
||||
*
|
||||
* \note It is NOT possible for the callback to be invoked concurrently for the query multiple times
|
||||
*
|
||||
* \note The query will occur when the TTL expires, not before. This means that there is a period of time where the previous
|
||||
* information can be considered stale.
|
||||
*
|
||||
* \note If the TTL is determined to be 0 (the record specifies 0, or no records exist) this will cease doing a recurring query.
|
||||
* It is the responsibility of the caller to resume querying at an interval they determine.
|
||||
*/
|
||||
struct ast_dns_query_recurring *ast_dns_resolve_recurring(const char *name, int rr_type, int rr_class, ast_dns_resolve_callback callback, void *data);
|
||||
|
||||
/*!
|
||||
* \brief Cancel an asynchronous recurring DNS resolution
|
||||
*
|
||||
* \param query The DNS query returned from ast_dns_resolve_recurring
|
||||
*
|
||||
* \retval 0 success - any active query has been cancelled and the query will no longer occur
|
||||
* \retval -1 failure - an active query was in progress and could not be cancelled
|
||||
*
|
||||
* \note If successfully cancelled the callback will not be invoked
|
||||
*
|
||||
* \note This function does NOT drop your reference to the recurring query, this should be dropped using ao2_ref
|
||||
*/
|
||||
int ast_dns_resolve_recurring_cancel(struct ast_dns_query_recurring *recurring);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ASTERISK_DNS_RECURRING_H */
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief DNS Resolver API
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_DNS_RESOLVER_H
|
||||
#define _ASTERISK_DNS_RESOLVER_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*! \brief DNS resolver implementation */
|
||||
struct ast_dns_resolver {
|
||||
/*! \brief The name of the resolver implementation */
|
||||
const char *name;
|
||||
|
||||
/*! \brief Priority for this resolver if multiple exist, lower being higher priority */
|
||||
unsigned int priority;
|
||||
|
||||
/*!
|
||||
* \brief Perform resolution of a DNS query
|
||||
*
|
||||
* \note The reference count of the query should be increased and released
|
||||
* upon the query completing or being successfully cancelled
|
||||
*/
|
||||
int (*resolve)(struct ast_dns_query *query);
|
||||
|
||||
/*! \brief Cancel resolution of a DNS query */
|
||||
int (*cancel)(struct ast_dns_query *query);
|
||||
|
||||
/*! \brief Linked list information */
|
||||
AST_RWLIST_ENTRY(ast_dns_resolver) next;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Set resolver specific data on a query
|
||||
*
|
||||
* \param query The DNS query
|
||||
* \param data The resolver specific data
|
||||
*
|
||||
* \note The resolver data MUST be an ao2 object
|
||||
*
|
||||
* \note This function increments the reference count of the resolver data, it does NOT steal
|
||||
*
|
||||
* \note Once resolver specific data has been set it can not be changed
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure, resolver data is already set
|
||||
*/
|
||||
int ast_dns_resolver_set_data(struct ast_dns_query *query, void *data);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve resolver specific data
|
||||
*
|
||||
* \param query The DNS query
|
||||
*
|
||||
* \return the resolver specific data
|
||||
*
|
||||
* \note The reference count of the resolver data is NOT incremented on return
|
||||
*/
|
||||
void *ast_dns_resolver_get_data(const struct ast_dns_query *query);
|
||||
|
||||
/*!
|
||||
* \brief Set result information for a DNS query
|
||||
*
|
||||
* \param query The DNS query
|
||||
* \param result Whether the result is secured or not
|
||||
* \param bogus Whether the result is bogus or not
|
||||
* \param rcode Optional response code
|
||||
* \param canonical The canonical name
|
||||
* \param answer The raw DNS answer
|
||||
* \param answer_size The size of the raw DNS answer
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Add a DNS record to the result of a DNS query
|
||||
*
|
||||
* \param query The DNS query
|
||||
* \param rr_type Resource record type
|
||||
* \param rr_class Resource record class
|
||||
* \param ttl TTL of the record
|
||||
* \param data The raw DNS record
|
||||
* \param size The size of the raw DNS record
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Mark a DNS query as having been completed
|
||||
*
|
||||
* \param query The DNS query
|
||||
*/
|
||||
void ast_dns_resolver_completed(struct ast_dns_query *query);
|
||||
|
||||
/*!
|
||||
* \brief Register a DNS resolver
|
||||
*
|
||||
* \param resolver A DNS resolver implementation
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_dns_resolver_register(struct ast_dns_resolver *resolver);
|
||||
|
||||
/*!
|
||||
* \brief Unregister a DNS resolver
|
||||
*
|
||||
* \param resolver A DNS resolver implementation
|
||||
*/
|
||||
void ast_dns_resolver_unregister(struct ast_dns_resolver *resolver);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ASTERISK_DNS_RESOLVER_H */
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief DNS SRV Record Parsing API
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_DNS_SRV_H
|
||||
#define _ASTERISK_DNS_SRV_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Get the hostname from an SRV record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the hostname
|
||||
*/
|
||||
const char *ast_dns_srv_get_host(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the priority from an SRV record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the priority
|
||||
*/
|
||||
unsigned short ast_dns_srv_get_priority(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the weight from an SRV record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the weight
|
||||
*/
|
||||
unsigned short ast_dns_srv_get_weight(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the port from an SRV record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the port
|
||||
*/
|
||||
unsigned short ast_dns_srv_get_port(const struct ast_dns_record *record);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ASTERISK_DNS_SRV_H */
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief DNS TLSA Record Parsing API
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_DNS_TLSA_H
|
||||
#define _ASTERISK_DNS_TLSA_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Get the certificate usage field from a TLSA record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the certificate usage field
|
||||
*/
|
||||
|
||||
unsigned int ast_dns_tlsa_get_usage(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the selector field from a TLSA record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the selector field
|
||||
*/
|
||||
unsigned int ast_dns_tlsa_get_selector(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the matching type field from a TLSA record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the matching type field
|
||||
*/
|
||||
unsigned int ast_dns_tlsa_get_matching_type(const struct ast_dns_record *record);
|
||||
|
||||
/*!
|
||||
* \brief Get the certificate association data from a TLSA record
|
||||
*
|
||||
* \param record The DNS record
|
||||
*
|
||||
* \return the certificate association data
|
||||
*/
|
||||
const char *ast_dns_tlsa_get_association_data(const struct ast_dns_record *record);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ASTERISK_DNS_TLSA_H */
|
@ -0,0 +1,566 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Core DNS Functionality
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/vector.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/strings.h"
|
||||
#include "asterisk/sched.h"
|
||||
#include "asterisk/dns_core.h"
|
||||
#include "asterisk/dns_srv.h"
|
||||
#include "asterisk/dns_tlsa.h"
|
||||
#include "asterisk/dns_recurring.h"
|
||||
#include "asterisk/dns_resolver.h"
|
||||
#include "asterisk/dns_internal.h"
|
||||
|
||||
#include <arpa/nameser.h>
|
||||
|
||||
AST_RWLIST_HEAD_STATIC(resolvers, ast_dns_resolver);
|
||||
|
||||
static struct ast_sched_context *sched;
|
||||
|
||||
struct ast_sched_context *ast_dns_get_sched(void)
|
||||
{
|
||||
return sched;
|
||||
}
|
||||
|
||||
const char *ast_dns_query_get_name(const struct ast_dns_query *query)
|
||||
{
|
||||
return query->name;
|
||||
}
|
||||
|
||||
int ast_dns_query_get_rr_type(const struct ast_dns_query *query)
|
||||
{
|
||||
return query->rr_type;
|
||||
}
|
||||
|
||||
int ast_dns_query_get_rr_class(const struct ast_dns_query *query)
|
||||
{
|
||||
return query->rr_class;
|
||||
}
|
||||
|
||||
void *ast_dns_query_get_data(const struct ast_dns_query *query)
|
||||
{
|
||||
return query->user_data;
|
||||
}
|
||||
|
||||
struct ast_dns_result *ast_dns_query_get_result(const struct ast_dns_query *query)
|
||||
{
|
||||
return query->result;
|
||||
}
|
||||
|
||||
unsigned int ast_dns_result_get_secure(const struct ast_dns_result *result)
|
||||
{
|
||||
return result->secure;
|
||||
}
|
||||
|
||||
unsigned int ast_dns_result_get_bogus(const struct ast_dns_result *result)
|
||||
{
|
||||
return result->bogus;
|
||||
}
|
||||
|
||||
unsigned int ast_dns_result_get_rcode(const struct ast_dns_result *result)
|
||||
{
|
||||
return result->rcode;
|
||||
}
|
||||
|
||||
const char *ast_dns_result_get_canonical(const struct ast_dns_result *result)
|
||||
{
|
||||
return result->canonical;
|
||||
}
|
||||
|
||||
const struct ast_dns_record *ast_dns_result_get_records(const struct ast_dns_result *result)
|
||||
{
|
||||
return AST_LIST_FIRST(&result->records);
|
||||
}
|
||||
|
||||
const char *ast_dns_result_get_answer(const struct ast_dns_result *result)
|
||||
{
|
||||
return result->answer;
|
||||
}
|
||||
|
||||
int ast_dns_result_get_lowest_ttl(const struct ast_dns_result *result)
|
||||
{
|
||||
int ttl = 0;
|
||||
const struct ast_dns_record *record;
|
||||
|
||||
if (ast_dns_result_get_rcode(result) == ns_r_nxdomain) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
|
||||
if (!ttl || (ast_dns_record_get_ttl(record) && (ast_dns_record_get_ttl(record) < ttl))) {
|
||||
ttl = ast_dns_record_get_ttl(record);
|
||||
}
|
||||
}
|
||||
|
||||
return ttl;
|
||||
}
|
||||
|
||||
void ast_dns_result_free(struct ast_dns_result *result)
|
||||
{
|
||||
struct ast_dns_record *record;
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ((record = AST_LIST_REMOVE_HEAD(&result->records, list))) {
|
||||
ast_free(record);
|
||||
}
|
||||
|
||||
ast_free(result);
|
||||
}
|
||||
|
||||
int ast_dns_record_get_rr_type(const struct ast_dns_record *record)
|
||||
{
|
||||
return record->rr_type;
|
||||
}
|
||||
|
||||
int ast_dns_record_get_rr_class(const struct ast_dns_record *record)
|
||||
{
|
||||
return record->rr_class;
|
||||
}
|
||||
|
||||
int ast_dns_record_get_ttl(const struct ast_dns_record *record)
|
||||
{
|
||||
return record->ttl;
|
||||
}
|
||||
|
||||
const char *ast_dns_record_get_data(const struct ast_dns_record *record)
|
||||
{
|
||||
return record->data;
|
||||
}
|
||||
|
||||
const struct ast_dns_record *ast_dns_record_get_next(const struct ast_dns_record *record)
|
||||
{
|
||||
return AST_LIST_NEXT(record, list);
|
||||
}
|
||||
|
||||
/*! \brief Destructor for an active DNS query */
|
||||
static void dns_query_active_destroy(void *data)
|
||||
{
|
||||
struct ast_dns_query_active *active = data;
|
||||
|
||||
ao2_cleanup(active->query);
|
||||
}
|
||||
|
||||
/*! \brief \brief Destructor for a DNS query */
|
||||
static void dns_query_destroy(void *data)
|
||||
{
|
||||
struct ast_dns_query *query = data;
|
||||
|
||||
ao2_cleanup(query->user_data);
|
||||
ao2_cleanup(query->resolver_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_active *active;
|
||||
|
||||
if (ast_strlen_zero(name)) {
|
||||
ast_log(LOG_WARNING, "Could not perform asynchronous resolution, no name provided\n");
|
||||
return NULL;
|
||||
} else if (rr_type > ns_t_max) {
|
||||
ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', resource record type '%d' exceeds maximum\n",
|
||||
name, rr_type);
|
||||
return NULL;
|
||||
} else if (rr_type < 0) {
|
||||
ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', invalid resource record type '%d'\n",
|
||||
name, rr_type);
|
||||
return NULL;
|
||||
} else if (rr_class > ns_c_max) {
|
||||
ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', resource record class '%d' exceeds maximum\n",
|
||||
name, rr_class);
|
||||
return NULL;
|
||||
} else if (rr_class < 0) {
|
||||
ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', invalid resource class '%d'\n",
|
||||
name, rr_class);
|
||||
return NULL;
|
||||
} else if (!callback) {
|
||||
ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', no callback provided\n",
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
ao2_ref(active, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
int ast_dns_resolve_cancel(struct ast_dns_query_active *active)
|
||||
{
|
||||
return active->query->resolver->cancel(active->query);
|
||||
}
|
||||
|
||||
/*! \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 The result from the query */
|
||||
struct ast_dns_result *result;
|
||||
};
|
||||
|
||||
/*! \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);
|
||||
|
||||
/* This purposely does not unref result as it has been passed to the caller */
|
||||
}
|
||||
|
||||
/*! \brief Callback used to implement synchronous resolution */
|
||||
static void dns_synchronous_resolve_callback(const struct ast_dns_query *query)
|
||||
{
|
||||
struct dns_synchronous_resolve *synchronous = ast_dns_query_get_data(query);
|
||||
|
||||
synchronous->result = query->result;
|
||||
((struct ast_dns_query *)query)->result = NULL;
|
||||
|
||||
ast_mutex_lock(&synchronous->lock);
|
||||
synchronous->completed = 1;
|
||||
ast_cond_signal(&synchronous->cond);
|
||||
ast_mutex_unlock(&synchronous->lock);
|
||||
}
|
||||
|
||||
int ast_dns_resolve(const char *name, int rr_type, int rr_class, struct ast_dns_result **result)
|
||||
{
|
||||
struct dns_synchronous_resolve *synchronous;
|
||||
struct ast_dns_query_active *active;
|
||||
|
||||
if (ast_strlen_zero(name)) {
|
||||
ast_log(LOG_WARNING, "Could not perform synchronous resolution, no name provided\n");
|
||||
return -1;
|
||||
} else if (rr_type > ns_t_max) {
|
||||
ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', resource record type '%d' exceeds maximum\n",
|
||||
name, rr_type);
|
||||
return -1;
|
||||
} else if (rr_type < 0) {
|
||||
ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', invalid resource record type '%d'\n",
|
||||
name, rr_type);
|
||||
return -1;
|
||||
} else if (rr_class > ns_c_max) {
|
||||
ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', resource record class '%d' exceeds maximum\n",
|
||||
name, rr_class);
|
||||
return -1;
|
||||
} else if (rr_class < 0) {
|
||||
ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', invalid resource class '%d'\n",
|
||||
name, rr_class);
|
||||
return -1;
|
||||
} else if (!result) {
|
||||
ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', no result pointer provided for storing results\n",
|
||||
name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
active = ast_dns_resolve_async(name, rr_type, rr_class, dns_synchronous_resolve_callback, synchronous);
|
||||
if (active) {
|
||||
/* 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(active, -1);
|
||||
}
|
||||
|
||||
*result = synchronous->result;
|
||||
ao2_ref(synchronous, -1);
|
||||
|
||||
return *result ? 0 : -1;
|
||||
}
|
||||
|
||||
int ast_dns_resolver_set_data(struct ast_dns_query *query, void *data)
|
||||
{
|
||||
if (query->resolver_data) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
query->resolver_data = ao2_bump(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ast_dns_resolver_get_data(const struct ast_dns_query *query)
|
||||
{
|
||||
return query->resolver_data;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char *buf_ptr;
|
||||
|
||||
if (secure && bogus) {
|
||||
ast_debug(2, "Query '%p': Could not set result information, it can not be both secure and bogus\n",
|
||||
query);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(canonical)) {
|
||||
ast_debug(2, "Query '%p': Could not set result information since no canonical name was provided\n",
|
||||
query);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!answer || answer_size == 0) {
|
||||
ast_debug(2, "Query '%p': Could not set result information since no DNS answer was provided\n",
|
||||
query);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_dns_result_free(query->result);
|
||||
|
||||
query->result = ast_calloc(1, sizeof(*query->result) + strlen(canonical) + 1 + answer_size);
|
||||
if (!query->result) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
query->result->secure = secure;
|
||||
query->result->bogus = bogus;
|
||||
query->result->rcode = rcode;
|
||||
|
||||
buf_ptr = query->result->buf;
|
||||
strcpy(buf_ptr, canonical); /* SAFE */
|
||||
query->result->canonical = buf_ptr;
|
||||
|
||||
buf_ptr += strlen(canonical) + 1;
|
||||
memcpy(buf_ptr, answer, answer_size); /* SAFE */
|
||||
query->result->answer = buf_ptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct ast_dns_record *record;
|
||||
|
||||
if (rr_type < 0) {
|
||||
ast_debug(2, "Query '%p': Could not add record, invalid resource record type '%d'\n",
|
||||
query, rr_type);
|
||||
return -1;
|
||||
} else if (rr_type > ns_t_max) {
|
||||
ast_debug(2, "Query '%p': Could not add record, resource record type '%d' exceeds maximum\n",
|
||||
query, rr_type);
|
||||
return -1;
|
||||
} else if (rr_class < 0) {
|
||||
ast_debug(2, "Query '%p': Could not add record, invalid resource record class '%d'\n",
|
||||
query, rr_class);
|
||||
return -1;
|
||||
} else if (rr_class > ns_c_max) {
|
||||
ast_debug(2, "Query '%p': Could not add record, resource record class '%d' exceeds maximum\n",
|
||||
query, rr_class);
|
||||
return -1;
|
||||
} else if (ttl < 0) {
|
||||
ast_debug(2, "Query '%p': Could not add record, invalid TTL '%d'\n",
|
||||
query, ttl);
|
||||
return -1;
|
||||
} else if (!data || !size) {
|
||||
ast_debug(2, "Query '%p': Could not add record, no data specified\n",
|
||||
query);
|
||||
return -1;
|
||||
} else if (!query->result) {
|
||||
ast_debug(2, "Query '%p': No result was set on the query, thus records can not be added\n",
|
||||
query);
|
||||
return -1;
|
||||
}
|
||||
|
||||
record = ast_calloc(1, sizeof(*record) + size);
|
||||
if (!record) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
record->rr_type = rr_type;
|
||||
record->rr_class = rr_class;
|
||||
record->ttl = ttl;
|
||||
memcpy(record->data, data, size);
|
||||
record->data_len = size;
|
||||
|
||||
AST_LIST_INSERT_TAIL(&query->result->records, record, list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ast_dns_resolver_completed(struct ast_dns_query *query)
|
||||
{
|
||||
query->callback(query);
|
||||
}
|
||||
|
||||
static void dns_shutdown(void)
|
||||
{
|
||||
if (sched) {
|
||||
ast_sched_context_destroy(sched);
|
||||
sched = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int ast_dns_resolver_register(struct ast_dns_resolver *resolver)
|
||||
{
|
||||
struct ast_dns_resolver *iter;
|
||||
int inserted = 0;
|
||||
|
||||
if (!resolver) {
|
||||
return -1;
|
||||
} else if (ast_strlen_zero(resolver->name)) {
|
||||
ast_log(LOG_ERROR, "Registration of DNS resolver failed as it does not have a name\n");
|
||||
return -1;
|
||||
} else if (!resolver->resolve) {
|
||||
ast_log(LOG_ERROR, "DNS resolver '%s' does not implement the resolve callback which is required\n",
|
||||
resolver->name);
|
||||
return -1;
|
||||
} else if (!resolver->cancel) {
|
||||
ast_log(LOG_ERROR, "DNS resolver '%s' does not implement the cancel callback which is required\n",
|
||||
resolver->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
AST_RWLIST_WRLOCK(&resolvers);
|
||||
|
||||
/* On the first registration of a resolver start a scheduler for recurring queries */
|
||||
if (AST_LIST_EMPTY(&resolvers) && !sched) {
|
||||
sched = ast_sched_context_create();
|
||||
if (!sched) {
|
||||
ast_log(LOG_ERROR, "DNS resolver '%s' could not be registered: Failed to create scheduler for recurring DNS queries\n",
|
||||
resolver->name);
|
||||
AST_RWLIST_UNLOCK(&resolvers);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_sched_start_thread(sched)) {
|
||||
ast_log(LOG_ERROR, "DNS resolver '%s' could not be registered: Failed to start thread for recurring DNS queries\n",
|
||||
resolver->name);
|
||||
dns_shutdown();
|
||||
AST_RWLIST_UNLOCK(&resolvers);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_register_cleanup(dns_shutdown);
|
||||
}
|
||||
|
||||
AST_LIST_TRAVERSE(&resolvers, iter, next) {
|
||||
if (!strcmp(iter->name, resolver->name)) {
|
||||
ast_log(LOG_ERROR, "A DNS resolver with the name '%s' is already registered\n", resolver->name);
|
||||
AST_RWLIST_UNLOCK(&resolvers);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&resolvers, iter, next) {
|
||||
if (iter->priority > resolver->priority) {
|
||||
AST_RWLIST_INSERT_BEFORE_CURRENT(resolver, next);
|
||||
inserted = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
|
||||
if (!inserted) {
|
||||
AST_RWLIST_INSERT_TAIL(&resolvers, resolver, next);
|
||||
}
|
||||
|
||||
AST_RWLIST_UNLOCK(&resolvers);
|
||||
|
||||
ast_verb(2, "Registered DNS resolver '%s' with priority '%d'\n", resolver->name, resolver->priority);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ast_dns_resolver_unregister(struct ast_dns_resolver *resolver)
|
||||
{
|
||||
struct ast_dns_resolver *iter;
|
||||
|
||||
if (!resolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
AST_RWLIST_WRLOCK(&resolvers);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&resolvers, iter, next) {
|
||||
if (resolver == iter) {
|
||||
AST_RWLIST_REMOVE_CURRENT(next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&resolvers);
|
||||
|
||||
ast_verb(2, "Unregistered DNS resolver '%s'\n", resolver->name);
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief DNS NAPTR Record Support
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/dns_core.h"
|
||||
#include "asterisk/dns_naptr.h"
|
||||
|
||||
const char *ast_dns_naptr_get_flags(const struct ast_dns_record *record)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *ast_dns_naptr_get_service(const struct ast_dns_record *record)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *ast_dns_naptr_get_regexp(const struct ast_dns_record *record)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *ast_dns_naptr_get_replacement(const struct ast_dns_record *record)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned short ast_dns_naptr_get_order(const struct ast_dns_record *record)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned short ast_dns_naptr_get_preference(const struct ast_dns_record *record)
|
||||
{
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief DNS Query Set API
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/vector.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/dns_core.h"
|
||||
#include "asterisk/dns_query_set.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;
|
||||
};
|
||||
|
||||
struct ast_dns_query_set *ast_dns_query_set_create(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ast_dns_query_set_add(struct ast_dns_query_set *query_set, const char *name, int rr_type, int rr_class)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t ast_dns_query_set_num_queries(const struct ast_dns_query_set *query_set)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ast_dns_query *ast_dns_query_set_get(const struct ast_dns_query_set *query_set, unsigned int index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ast_dns_query_set_get_data(const struct ast_dns_query_set *query_set)
|
||||
{
|
||||
return query_set->user_data;
|
||||
}
|
||||
|
||||
void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dns_query_set_callback callback, void *data)
|
||||
{
|
||||
query_set->callback = callback;
|
||||
query_set->user_data = ao2_bump(data);
|
||||
}
|
||||
|
||||
void ast_query_set_resolve(struct ast_dns_query_set *query_set)
|
||||
{
|
||||
}
|
||||
|
||||
int ast_dns_query_set_resolve_cancel(struct ast_dns_query_set *query_set)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ast_dns_query_set_free(struct ast_dns_query_set *query_set)
|
||||
{
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief DNS Recurring Query Support
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/sched.h"
|
||||
#include "asterisk/strings.h"
|
||||
#include "asterisk/dns_core.h"
|
||||
#include "asterisk/dns_recurring.h"
|
||||
#include "asterisk/dns_internal.h"
|
||||
|
||||
#include <arpa/nameser.h>
|
||||
|
||||
/*! \brief Destructor for a DNS query */
|
||||
static void dns_query_recurring_destroy(void *data)
|
||||
{
|
||||
struct ast_dns_query_recurring *recurring = data;
|
||||
|
||||
ao2_cleanup(recurring->user_data);
|
||||
}
|
||||
|
||||
static void dns_query_recurring_resolution_callback(const struct ast_dns_query *query);
|
||||
|
||||
/*! \brief Scheduled recurring query callback */
|
||||
static int dns_query_recurring_scheduled_callback(const void *data)
|
||||
{
|
||||
struct ast_dns_query_recurring *recurring = (struct ast_dns_query_recurring *)data;
|
||||
|
||||
ao2_lock(recurring);
|
||||
recurring->timer = -1;
|
||||
if (!recurring->cancelled) {
|
||||
recurring->active = ast_dns_resolve_async(recurring->name, recurring->rr_type, recurring->rr_class, dns_query_recurring_resolution_callback,
|
||||
recurring);
|
||||
}
|
||||
ao2_unlock(recurring);
|
||||
|
||||
ao2_ref(recurring, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Query resolution callback */
|
||||
static void dns_query_recurring_resolution_callback(const struct ast_dns_query *query)
|
||||
{
|
||||
struct ast_dns_query_recurring *recurring = ast_dns_query_get_data(query);
|
||||
|
||||
/* Replace the user data so the actual callback sees what it provided */
|
||||
((struct ast_dns_query*)query)->user_data = ao2_bump(recurring->user_data);
|
||||
recurring->callback(query);
|
||||
|
||||
ao2_lock(recurring);
|
||||
/* So.. if something has not externally cancelled this we can reschedule based on the TTL */
|
||||
if (!recurring->cancelled) {
|
||||
const struct ast_dns_result *result = ast_dns_query_get_result(query);
|
||||
int ttl = MIN(ast_dns_result_get_lowest_ttl(result), INT_MAX / 1000);
|
||||
|
||||
if (ttl) {
|
||||
recurring->timer = ast_sched_add(ast_dns_get_sched(), ttl * 1000, dns_query_recurring_scheduled_callback, ao2_bump(recurring));
|
||||
if (recurring->timer < 0) {
|
||||
/* It is impossible for this to be the last reference as this callback function holds a reference itself */
|
||||
ao2_ref(recurring, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ao2_replace(recurring->active, NULL);
|
||||
ao2_unlock(recurring);
|
||||
|
||||
/* Since we stole the reference from the query we need to drop it ourselves */
|
||||
ao2_ref(recurring, -1);
|
||||
}
|
||||
|
||||
struct ast_dns_query_recurring *ast_dns_resolve_recurring(const char *name, int rr_type, int rr_class, ast_dns_resolve_callback callback, void *data)
|
||||
{
|
||||
struct ast_dns_query_recurring *recurring;
|
||||
|
||||
if (ast_strlen_zero(name) || !callback || !ast_dns_get_sched()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
recurring = ao2_alloc(sizeof(*recurring) + strlen(name) + 1, dns_query_recurring_destroy);
|
||||
if (!recurring) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
recurring->callback = callback;
|
||||
recurring->user_data = ao2_bump(data);
|
||||
recurring->timer = -1;
|
||||
recurring->rr_type = rr_type;
|
||||
recurring->rr_class = rr_class;
|
||||
strcpy(recurring->name, name); /* SAFE */
|
||||
|
||||
recurring->active = ast_dns_resolve_async(name, rr_type, rr_class, dns_query_recurring_resolution_callback, recurring);
|
||||
if (!recurring->active) {
|
||||
ao2_ref(recurring, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return recurring;
|
||||
}
|
||||
|
||||
int ast_dns_resolve_recurring_cancel(struct ast_dns_query_recurring *recurring)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
ao2_lock(recurring);
|
||||
|
||||
recurring->cancelled = 1;
|
||||
AST_SCHED_DEL_UNREF(ast_dns_get_sched(), recurring->timer, ao2_ref(recurring, -1));
|
||||
|
||||
if (recurring->active) {
|
||||
res = ast_dns_resolve_cancel(recurring->active);
|
||||
ao2_replace(recurring->active, NULL);
|
||||
}
|
||||
|
||||
ao2_unlock(recurring);
|
||||
|
||||
return res;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief DNS SRV Record Support
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/dns_core.h"
|
||||
#include "asterisk/dns_srv.h"
|
||||
|
||||
const char *ast_dns_srv_get_host(const struct ast_dns_record *record)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned short ast_dns_srv_get_priority(const struct ast_dns_record *record)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned short ast_dns_srv_get_weight(const struct ast_dns_record *record)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned short ast_dns_srv_get_port(const struct ast_dns_record *record)
|
||||
{
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief DNS TLSA Record Support
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/dns_core.h"
|
||||
#include "asterisk/dns_tlsa.h"
|
||||
|
||||
unsigned int ast_dns_tlsa_get_usage(const struct ast_dns_record *record)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int ast_dns_tlsa_get_selector(const struct ast_dns_record *record)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int ast_dns_tlsa_get_matching_type(const struct ast_dns_record *record)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *ast_dns_tlsa_get_association_data(const struct ast_dns_record *record)
|
||||
{
|
||||
return NULL;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,648 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2015, Mark Michelson
|
||||
*
|
||||
* Mark Michelson <mmichelson@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>TEST_FRAMEWORK</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <arpa/nameser.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/dns_core.h"
|
||||
#include "asterisk/dns_resolver.h"
|
||||
#include "asterisk/dns_recurring.h"
|
||||
#include "asterisk/dns_internal.h"
|
||||
|
||||
struct recurring_data {
|
||||
/*! TTL to place in first returned result */
|
||||
int ttl1;
|
||||
/*! TTL to place in second returned result */
|
||||
int ttl2;
|
||||
/*! Boolean indicator if query has completed */
|
||||
int query_complete;
|
||||
/*! Number of times recurring resolution has completed */
|
||||
int complete_resolutions;
|
||||
/*! Number of times resolve() method has been called */
|
||||
int resolves;
|
||||
/*! Indicates that the query is expected to be canceled */
|
||||
int cancel_expected;
|
||||
/*! Indicates that the query is ready to be canceled */
|
||||
int cancel_ready;
|
||||
/*! Indicates that the query has been canceled */
|
||||
int canceled;
|
||||
ast_mutex_t lock;
|
||||
ast_cond_t cond;
|
||||
};
|
||||
|
||||
static void recurring_data_destructor(void *obj)
|
||||
{
|
||||
struct recurring_data *rdata = obj;
|
||||
|
||||
ast_mutex_destroy(&rdata->lock);
|
||||
ast_cond_destroy(&rdata->cond);
|
||||
}
|
||||
|
||||
static struct recurring_data *recurring_data_alloc(void)
|
||||
{
|
||||
struct recurring_data *rdata;
|
||||
|
||||
rdata = ao2_alloc(sizeof(*rdata), recurring_data_destructor);
|
||||
if (!rdata) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_mutex_init(&rdata->lock);
|
||||
ast_cond_init(&rdata->cond, NULL);
|
||||
|
||||
return rdata;
|
||||
}
|
||||
|
||||
#define DNS_ANSWER "Yes sirree"
|
||||
#define DNS_ANSWER_SIZE strlen(DNS_ANSWER)
|
||||
|
||||
/*!
|
||||
* \brief Thread that performs asynchronous resolution.
|
||||
*
|
||||
* This thread uses the query's user data to determine how to
|
||||
* perform the resolution. The query may either be canceled or
|
||||
* it may be completed with records.
|
||||
*
|
||||
* \param dns_query The ast_dns_query that is being performed
|
||||
* \return NULL
|
||||
*/
|
||||
static void *resolution_thread(void *dns_query)
|
||||
{
|
||||
struct ast_dns_query *query = dns_query;
|
||||
|
||||
static const char *ADDR1 = "127.0.0.1";
|
||||
static const size_t ADDR1_BUFSIZE = sizeof(struct in_addr);
|
||||
char addr1_buf[ADDR1_BUFSIZE];
|
||||
|
||||
static const char *ADDR2 = "192.168.0.1";
|
||||
static const size_t ADDR2_BUFSIZE = sizeof(struct in_addr);
|
||||
char addr2_buf[ADDR2_BUFSIZE];
|
||||
|
||||
struct ast_dns_query_recurring *recurring = ast_dns_query_get_data(query);
|
||||
struct recurring_data *rdata = recurring->user_data;
|
||||
|
||||
ast_assert(rdata != NULL);
|
||||
|
||||
/* Canceling is an interesting dance. This thread needs to signal that it is
|
||||
* ready to be canceled. Then it needs to wait until the query is actually canceled.
|
||||
*/
|
||||
if (rdata->cancel_expected) {
|
||||
ast_mutex_lock(&rdata->lock);
|
||||
rdata->cancel_ready = 1;
|
||||
ast_cond_signal(&rdata->cond);
|
||||
|
||||
while (!rdata->canceled) {
|
||||
ast_cond_wait(&rdata->cond, &rdata->lock);
|
||||
}
|
||||
ast_mutex_unlock(&rdata->lock);
|
||||
|
||||
ast_dns_resolver_completed(query);
|
||||
ao2_ref(query, -1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* When the query isn't canceled, we set the TTL of the results based on what
|
||||
* we've been told to set it to
|
||||
*/
|
||||
ast_dns_resolver_set_result(query, 0, 0, ns_r_noerror, "asterisk.org", DNS_ANSWER, DNS_ANSWER_SIZE);
|
||||
|
||||
inet_pton(AF_INET, ADDR1, addr1_buf);
|
||||
ast_dns_resolver_add_record(query, ns_t_a, ns_c_in, rdata->ttl1, addr1_buf, ADDR1_BUFSIZE);
|
||||
|
||||
inet_pton(AF_INET, ADDR2, addr2_buf);
|
||||
ast_dns_resolver_add_record(query, ns_t_a, ns_c_in, rdata->ttl2, addr2_buf, ADDR2_BUFSIZE);
|
||||
|
||||
++rdata->complete_resolutions;
|
||||
|
||||
ast_dns_resolver_completed(query);
|
||||
|
||||
ao2_ref(query, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Resolver's resolve() method
|
||||
*
|
||||
* \param query The query that is to be resolved
|
||||
* \retval 0 Successfully created thread to perform the resolution
|
||||
* \retval non-zero Failed to create resolution thread
|
||||
*/
|
||||
static int recurring_resolve(struct ast_dns_query *query)
|
||||
{
|
||||
struct ast_dns_query_recurring *recurring = ast_dns_query_get_data(query);
|
||||
struct recurring_data *rdata = recurring->user_data;
|
||||
pthread_t resolver_thread;
|
||||
|
||||
ast_assert(rdata != NULL);
|
||||
++rdata->resolves;
|
||||
return ast_pthread_create_detached(&resolver_thread, NULL, resolution_thread, ao2_bump(query));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Resolver's cancel() method
|
||||
*
|
||||
* \param query The query to cancel
|
||||
* \return 0
|
||||
*/
|
||||
static int recurring_cancel(struct ast_dns_query *query)
|
||||
{
|
||||
struct ast_dns_query_recurring *recurring = ast_dns_query_get_data(query);
|
||||
struct recurring_data *rdata = recurring->user_data;
|
||||
|
||||
ast_mutex_lock(&rdata->lock);
|
||||
rdata->canceled = 1;
|
||||
ast_cond_signal(&rdata->cond);
|
||||
ast_mutex_unlock(&rdata->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_dns_resolver recurring_resolver = {
|
||||
.name = "test_recurring",
|
||||
.priority = 0,
|
||||
.resolve = recurring_resolve,
|
||||
.cancel = recurring_cancel,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Wait for a successful resolution to complete
|
||||
*
|
||||
* This is called whenever a successful DNS resolution occurs. This function
|
||||
* serves to ensure that parameters are as we expect them to be.
|
||||
*
|
||||
* \param test The test being executed
|
||||
* \param rdata DNS query user data
|
||||
* \param expected_lapse The amount of time we expect to wait for the query to complete
|
||||
* \param num_resolves The number of DNS resolutions that have been executed
|
||||
* \param num_completed The number of DNS resolutions we expect to have completed successfully
|
||||
* \param canceled Whether the query is expected to have been canceled
|
||||
*/
|
||||
static int wait_for_resolution(struct ast_test *test, struct recurring_data *rdata,
|
||||
int expected_lapse, int num_resolves, int num_completed, int canceled)
|
||||
{
|
||||
struct timespec begin;
|
||||
struct timespec end;
|
||||
struct timespec timeout;
|
||||
int secdiff;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &begin);
|
||||
|
||||
timeout.tv_sec = begin.tv_sec + 20;
|
||||
timeout.tv_nsec = begin.tv_nsec;
|
||||
|
||||
ast_mutex_lock(&rdata->lock);
|
||||
while (!rdata->query_complete) {
|
||||
if (ast_cond_timedwait(&rdata->cond, &rdata->lock, &timeout) == ETIMEDOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&rdata->lock);
|
||||
|
||||
if (!rdata->query_complete) {
|
||||
ast_test_status_update(test, "Query timed out\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rdata->query_complete = 0;
|
||||
clock_gettime(CLOCK_REALTIME, &end);
|
||||
|
||||
secdiff = end.tv_sec - begin.tv_sec;
|
||||
|
||||
/* Give ourselves some wiggle room */
|
||||
if (secdiff < expected_lapse - 2 || secdiff > expected_lapse + 2) {
|
||||
ast_test_status_update(test, "Query did not complete in expected time\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rdata->resolves != num_resolves || rdata->complete_resolutions != num_completed) {
|
||||
ast_test_status_update(test, "Query has not undergone expected number of resolutions\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rdata->canceled != canceled) {
|
||||
ast_test_status_update(test, "Query was canceled unexpectedly\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_test_status_update(test, "Query completed in expected time frame\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void async_callback(const struct ast_dns_query *query)
|
||||
{
|
||||
struct recurring_data *rdata = ast_dns_query_get_data(query);
|
||||
|
||||
ast_assert(rdata != NULL);
|
||||
|
||||
ast_mutex_lock(&rdata->lock);
|
||||
rdata->query_complete = 1;
|
||||
ast_cond_signal(&rdata->cond);
|
||||
ast_mutex_unlock(&rdata->lock);
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(recurring_query)
|
||||
{
|
||||
RAII_VAR(struct ast_dns_query_recurring *, recurring_query, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct recurring_data *, rdata, NULL, ao2_cleanup);
|
||||
|
||||
enum ast_test_result_state res = AST_TEST_PASS;
|
||||
int expected_lapse;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "recurring_query";
|
||||
info->category = "/main/dns/recurring/";
|
||||
info->summary = "Test nominal asynchronous recurring DNS queries\n";
|
||||
info->description =
|
||||
"This tests nominal recurring queries in the following ways:\n"
|
||||
"\t* An asynchronous query is sent to a mock resolver\n"
|
||||
"\t* The mock resolver returns two records with different TTLs\n"
|
||||
"\t* We ensure that the query re-occurs according to the lower of the TTLs\n"
|
||||
"\t* The mock resolver returns two records, this time with different TTLs\n"
|
||||
"\t from the first time the query was resolved\n"
|
||||
"\t* We ensure that the query re-occurs according to the new lower TTL\n";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ast_dns_resolver_register(&recurring_resolver)) {
|
||||
ast_test_status_update(test, "Failed to register recurring DNS resolver\n");
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
rdata = recurring_data_alloc();
|
||||
if (!rdata) {
|
||||
ast_test_status_update(test, "Failed to allocate data necessary for recurring test\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
expected_lapse = 0;
|
||||
rdata->ttl1 = 5;
|
||||
rdata->ttl2 = 20;
|
||||
|
||||
recurring_query = ast_dns_resolve_recurring("asterisk.org", ns_t_a, ns_c_in, async_callback, rdata);
|
||||
if (!recurring_query) {
|
||||
ast_test_status_update(test, "Failed to create recurring DNS query\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* This should be near instantaneous */
|
||||
if (wait_for_resolution(test, rdata, expected_lapse, 1, 1, 0)) {
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
expected_lapse = rdata->ttl1;
|
||||
rdata->ttl1 = 45;
|
||||
rdata->ttl2 = 10;
|
||||
|
||||
/* This should take approximately 5 seconds */
|
||||
if (wait_for_resolution(test, rdata, expected_lapse, 2, 2, 0)) {
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
expected_lapse = rdata->ttl2;
|
||||
|
||||
/* This should take approximately 10 seconds */
|
||||
if (wait_for_resolution(test, rdata, expected_lapse, 3, 3, 0)) {
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (recurring_query) {
|
||||
/* XXX I don't like calling this here since I'm not testing
|
||||
* canceling recurring queries, but I'm forced into it here
|
||||
*/
|
||||
ast_dns_resolve_recurring_cancel(recurring_query);
|
||||
}
|
||||
ast_dns_resolver_unregister(&recurring_resolver);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fail_resolve(struct ast_dns_query *query)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int stub_cancel(struct ast_dns_query *query)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stub_callback(const struct ast_dns_query *query)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(recurring_query_off_nominal)
|
||||
{
|
||||
struct ast_dns_resolver terrible_resolver = {
|
||||
.name = "Harold P. Warren's Filmography",
|
||||
.priority = 0,
|
||||
.resolve = fail_resolve,
|
||||
.cancel = stub_cancel,
|
||||
};
|
||||
|
||||
struct ast_dns_query_recurring *recurring;
|
||||
|
||||
struct dns_resolve_data {
|
||||
const char *name;
|
||||
int rr_type;
|
||||
int rr_class;
|
||||
ast_dns_resolve_callback callback;
|
||||
} resolves [] = {
|
||||
{ NULL, ns_t_a, ns_c_in, stub_callback },
|
||||
{ "asterisk.org", -1, ns_c_in, stub_callback },
|
||||
{ "asterisk.org", ns_t_max + 1, ns_c_in, stub_callback },
|
||||
{ "asterisk.org", ns_t_a, -1, stub_callback },
|
||||
{ "asterisk.org", ns_t_a, ns_c_max + 1, stub_callback },
|
||||
{ "asterisk.org", ns_t_a, ns_c_in, NULL },
|
||||
};
|
||||
int i;
|
||||
enum ast_test_result_state res = AST_TEST_PASS;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "recurring_query_off_nominal";
|
||||
info->category = "/main/dns/recurring/";
|
||||
info->summary = "Test off-nominal recurring DNS resolution";
|
||||
info->description =
|
||||
"This test performs several off-nominal recurring DNS resolutions:\n"
|
||||
"\t* Attempt resolution with NULL name\n",
|
||||
"\t* Attempt resolution with invalid RR type\n",
|
||||
"\t* Attempt resolution with invalid RR class\n",
|
||||
"\t* Attempt resolution with NULL callback pointer\n",
|
||||
"\t* Attempt resolution with resolver that returns an error\n";
|
||||
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ast_dns_resolver_register(&recurring_resolver)) {
|
||||
ast_test_status_update(test, "Failed to register test resolver\n");
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(resolves); ++i) {
|
||||
recurring = ast_dns_resolve_recurring(resolves[i].name, resolves[i].rr_type, resolves[i].rr_class,
|
||||
resolves[i].callback, NULL);
|
||||
if (recurring) {
|
||||
ast_test_status_update(test, "Successfully performed recurring resolution with invalid data\n");
|
||||
ast_dns_resolve_recurring_cancel(recurring);
|
||||
ao2_ref(recurring, -1);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
ast_dns_resolver_unregister(&recurring_resolver);
|
||||
|
||||
if (ast_dns_resolver_register(&terrible_resolver)) {
|
||||
ast_test_status_update(test, "Failed to register the DNS resolver\n");
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
recurring = ast_dns_resolve_recurring("asterisk.org", ns_t_a, ns_c_in, stub_callback, NULL);
|
||||
|
||||
ast_dns_resolver_unregister(&terrible_resolver);
|
||||
|
||||
if (recurring) {
|
||||
ast_test_status_update(test, "Successfully performed recurring resolution with invalid data\n");
|
||||
ast_dns_resolve_recurring_cancel(recurring);
|
||||
ao2_ref(recurring, -1);
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(recurring_query_cancel_between)
|
||||
{
|
||||
RAII_VAR(struct ast_dns_query_recurring *, recurring_query, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct recurring_data *, rdata, NULL, ao2_cleanup);
|
||||
|
||||
enum ast_test_result_state res = AST_TEST_PASS;
|
||||
struct timespec timeout;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "recurring_query_cancel_between";
|
||||
info->category = "/main/dns/recurring/";
|
||||
info->summary = "Test canceling a recurring DNS query during the downtime between queries\n";
|
||||
info->description = "This test does the following:\n"
|
||||
"\t* Issue a recurring DNS query.\n"
|
||||
"\t* Once results have been returned, cancel the recurring query.\n"
|
||||
"\t* Wait a while to ensure that no more queries are occurring.\n";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ast_dns_resolver_register(&recurring_resolver)) {
|
||||
ast_test_status_update(test, "Failed to register recurring DNS resolver\n");
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
rdata = recurring_data_alloc();
|
||||
if (!rdata) {
|
||||
ast_test_status_update(test, "Failed to allocate data necessary for recurring test\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rdata->ttl1 = 5;
|
||||
rdata->ttl2 = 20;
|
||||
|
||||
recurring_query = ast_dns_resolve_recurring("asterisk.org", ns_t_a, ns_c_in, async_callback, rdata);
|
||||
if (!recurring_query) {
|
||||
ast_test_status_update(test, "Unable to make recurring query\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (wait_for_resolution(test, rdata, 0, 1, 1, 0)) {
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (ast_dns_resolve_recurring_cancel(recurring_query)) {
|
||||
ast_test_status_update(test, "Failed to cancel recurring query\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Query has been canceled, so let's wait to make sure that we don't get
|
||||
* told another query has occurred.
|
||||
*/
|
||||
clock_gettime(CLOCK_REALTIME, &timeout);
|
||||
timeout.tv_sec += 10;
|
||||
|
||||
ast_mutex_lock(&rdata->lock);
|
||||
while (!rdata->query_complete) {
|
||||
if (ast_cond_timedwait(&rdata->cond, &rdata->lock, &timeout) == ETIMEDOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&rdata->lock);
|
||||
|
||||
if (rdata->query_complete) {
|
||||
ast_test_status_update(test, "Recurring query occurred after cancellation\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
ast_dns_resolver_unregister(&recurring_resolver);
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(recurring_query_cancel_during)
|
||||
{
|
||||
|
||||
RAII_VAR(struct ast_dns_query_recurring *, recurring_query, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct recurring_data *, rdata, NULL, ao2_cleanup);
|
||||
|
||||
enum ast_test_result_state res = AST_TEST_PASS;
|
||||
struct timespec timeout;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "recurring_query_cancel_during";
|
||||
info->category = "/main/dns/recurring/";
|
||||
info->summary = "Cancel a recurring DNS query while a query is actually happening\n";
|
||||
info->description = "This test does the following:\n"
|
||||
"\t* Initiate a recurring DNS query.\n"
|
||||
"\t* Allow the initial query to complete, and a second query to start\n"
|
||||
"\t* Cancel the recurring query while the second query is executing\n"
|
||||
"\t* Ensure that the resolver's cancel() method was called\n"
|
||||
"\t* Wait a while to make sure that recurring queries are no longer occurring\n";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ast_dns_resolver_register(&recurring_resolver)) {
|
||||
ast_test_status_update(test, "Failed to register recurring DNS resolver\n");
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
rdata = recurring_data_alloc();
|
||||
if (!rdata) {
|
||||
ast_test_status_update(test, "Failed to allocate data necessary for recurring test\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rdata->ttl1 = 5;
|
||||
rdata->ttl2 = 20;
|
||||
|
||||
recurring_query = ast_dns_resolve_recurring("asterisk.org", ns_t_a, ns_c_in, async_callback, rdata);
|
||||
if (!recurring_query) {
|
||||
ast_test_status_update(test, "Failed to make recurring DNS query\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (wait_for_resolution(test, rdata, 0, 1, 1, 0)) {
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Initial query has completed. Now let's make the next query expect a cancelation */
|
||||
rdata->cancel_expected = 1;
|
||||
|
||||
/* Wait to be told that the query should be canceled */
|
||||
ast_mutex_lock(&rdata->lock);
|
||||
while (!rdata->cancel_ready) {
|
||||
ast_cond_wait(&rdata->cond, &rdata->lock);
|
||||
}
|
||||
rdata->cancel_expected = 0;
|
||||
ast_mutex_unlock(&rdata->lock);
|
||||
|
||||
if (ast_dns_resolve_recurring_cancel(recurring_query)) {
|
||||
ast_test_status_update(test, "Failed to cancel recurring DNS query\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Query has been canceled. We'll be told that the query in flight has completed. */
|
||||
if (wait_for_resolution(test, rdata, 0, 2, 1, 1)) {
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Now ensure that no more queries get completed after cancellation. */
|
||||
clock_gettime(CLOCK_REALTIME, &timeout);
|
||||
timeout.tv_sec += 10;
|
||||
|
||||
ast_mutex_lock(&rdata->lock);
|
||||
while (!rdata->query_complete) {
|
||||
if (ast_cond_timedwait(&rdata->cond, &rdata->lock, &timeout) == ETIMEDOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&rdata->lock);
|
||||
|
||||
if (rdata->query_complete) {
|
||||
ast_test_status_update(test, "Recurring query occurred after cancellation\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
ast_dns_resolver_unregister(&recurring_resolver);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(recurring_query);
|
||||
AST_TEST_UNREGISTER(recurring_query_off_nominal);
|
||||
AST_TEST_UNREGISTER(recurring_query_cancel_between);
|
||||
AST_TEST_UNREGISTER(recurring_query_cancel_during);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
AST_TEST_REGISTER(recurring_query);
|
||||
AST_TEST_REGISTER(recurring_query_off_nominal);
|
||||
AST_TEST_REGISTER(recurring_query_cancel_between);
|
||||
AST_TEST_REGISTER(recurring_query_cancel_during);
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Recurring DNS query tests");
|
Loading…
Reference in new issue