DNS: Create a system-level DNS resolver
authorAshley Sanders <asanders@digium.com>
Tue, 7 Jul 2015 20:03:34 +0000 (15:03 -0500)
committerAshley Sanders <asanders@digium.com>
Wed, 8 Jul 2015 02:31:49 +0000 (21:31 -0500)
Prior to this patch, the DNS core present in master had no default system-level
resolver implementation. Therefore, it was not possible for the DNS core to
perform resolutions unless the libunbound library was installed and the
res_resolver_unbound module was loaded.

This patch introduces a system-level DNS resolver implementation that will
register itself with the lowest consideration priority available (to ensure
that it is to be used only as a last resort). The resolver relies on low-level
DNS search functions to perform a rudimentary DNS search based on a provided
query and then supplies the search results to the DNS core.

ASTERISK-25146 #close
Reported By: Joshua Colp

Change-Id: I3b36ea17b889a98df4f8d80d50bb7ee175afa077

include/asterisk/_private.h
include/asterisk/dns.h
main/asterisk.c
main/dns.c
main/dns_system_resolver.c [new file with mode: 0755]

index d49de17..2966f87 100644 (file)
@@ -29,6 +29,7 @@ int ast_cli_perms_init(int reload);   /*!< Provided by cli.c */
 int dnsmgr_init(void);                 /*!< Provided by dnsmgr.c */
 void dnsmgr_start_refresh(void);       /*!< Provided by dnsmgr.c */
 int dnsmgr_reload(void);               /*!< Provided by dnsmgr.c */
+int ast_dns_system_resolver_init(void); /*!< Provided by dns_system_resolver.c */
 void threadstorage_init(void);         /*!< Provided by threadstorage.c */
 int ast_device_state_engine_init(void);        /*!< Provided by devicestate.c */
 int astobj2_init(void);                        /*!< Provided by astobj2.c */
index 4899fa8..c193a22 100644 (file)
 #ifndef _ASTERISK_DNS_H
 #define _ASTERISK_DNS_H
 
-/*!    \brief  Perform DNS lookup (used by DNS, enum and SRV lookups)
-       \param  context
-       \param  dname   Domain name to lookup (host, SRV domain, TXT record name)
-       \param  class   Record Class (see "man res_search")
-       \param  type    Record type (see "man res_search")
-       \param  callback Callback function for handling DNS result
-       \note   Asterisk DNS is synchronus at this time. This means that if your DNS
-               services does not work, Asterisk may lock while waiting for response.
-*/
+/*! \brief DNS search return values */
+enum ast_dns_search_result {
+       AST_DNS_SEARCH_FAILURE = -1,     /*!< DNS search resulted in failure */
+       AST_DNS_SEARCH_NO_RECORDS = 0,   /*!< DNS search yielded no results */
+       AST_DNS_SEARCH_SUCCESS = 1       /*!< DNS search yielded at least one discovered record */
+};
+
+/*!
+ * \brief Perform DNS lookup (used by DNS, enum and SRV lookups)
+ *
+ * \param  context   Void pointer containing data to use in the callback function.
+ * \param  dname     Domain name to lookup (host, SRV domain, TXT record name).
+ * \param  class     Record Class (see "man res_search").
+ * \param  type      Record type (see "man res_search").
+ * \param  answer    The full DNS response.
+ * \param  len       The length of the full DNS response.
+ * \param  callback  Callback function for handling the discovered resource records from
+ *                   the DNS search.
+ *
+ * \retval -1 on search failure
+ * \retval  0 on no records found
+ * \retval  1 on success
+ *
+ * \note Asterisk DNS is synchronus at this time. This means that if your DNS
+ *       service does not work, Asterisk may lock while waiting for a response.
+ */
 int ast_search_dns(void *context, const char *dname, int class, int type,
-        int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer));
+       int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer));
+
+/*!
+ * \brief Extended version of the DNS search function.
+ *
+ * \details Performs a DNS lookup, (used by DNS, enum and SRV lookups), parses the
+ *          results and notifies the observer with the response and discovered records
+ *          via invoking the provided callbacks (used by ast_dns_system_resolver).
+ *
+ * \param  context           Void pointer containing data to use in the handler functions.
+ * \param  dname             Domain name to lookup (host, SRV domain, TXT record name).
+ * \param  rr_class          Record Class (see "man res_search").
+ * \param  rr_type           Record type (see "man res_search").
+ * \param  response_handler  Callback function for handling the DNS response. Invoked upon
+ *                           completion of the DNS search.
+ * \param  record_handler    Callback function for handling the discovered resource
+ *                           records from the DNS search. Invoked n times, where n is the
+ *                           number of records discovered while parsing the DNS
+ *                           response.
+ *
+ * \retval AST_DNS_SEARCH_FAILURE    on search failure
+ * \retval AST_DNS_SEARCH_NO_RECORDS on no records found
+ * \retval AST_DNS_SEARCH_SUCCESS    on success
+ *
+ * \note Asterisk DNS is synchronus at this time. This means that if your DNS
+ *       service does not work, Asterisk may lock while waiting for a response.
+ */
+enum ast_dns_search_result ast_search_dns_ex(void *context, const char *dname, int rr_class, int rr_type,
+       int (*response_handler)(void *context, unsigned char *dns_response, int dns_response_len, int rcode),
+       int (*record_handler)(void *context, unsigned char *record, int record_len, int ttl));
 
 /*! \brief Retrieve the configured nameservers of the system */
 struct ao2_container *ast_dns_get_nameservers(void);
index 53bcead..0478f6c 100644 (file)
@@ -4511,6 +4511,11 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       if (ast_dns_system_resolver_init()) {           /* Initialize the default DNS resolver */
+               printf("Failed: ast_dns_system_resolver_init\n%s", term_quit());
+               exit(1);
+       }
+
        if ((moduleresult = load_modules(1))) {         /* Load modules, pre-load only */
                printf("Failed: load_modules\n%s", term_quit());
                exit(moduleresult == -2 ? 2 : 1);
index fd87420..409ed44 100644 (file)
@@ -45,6 +45,7 @@ ASTERISK_REGISTER_FILE()
 #include "asterisk/dns.h"
 #include "asterisk/endian.h"
 
+/*! \brief The maximum size permitted for the answer from the DNS server */
 #define MAX_SIZE 4096
 
 #ifdef __PDP_ENDIAN
@@ -59,6 +60,10 @@ ASTERISK_REGISTER_FILE()
 #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
 #endif
 
+#ifndef HAVE_RES_NINIT
+AST_MUTEX_DEFINE_STATIC(res_lock);
+#endif
+
 /* The dns_HEADER structure definition below originated
    in the arpa/nameser.h header file distributed with ISC
    BIND, which contains the following copyright and license
@@ -156,12 +161,23 @@ typedef struct {
 } dns_HEADER;
 
 struct dn_answer {
-       unsigned short rtype;
-       unsigned short class;
-       unsigned int ttl;
-       unsigned short size;
+       unsigned short rtype;       /*!< The resource record type. */
+       unsigned short class;       /*!< The resource record class. */
+       unsigned int ttl;           /*!< The resource record time to live. */
+       unsigned short size;        /*!< The resource record size. */
 } __attribute__((__packed__));
 
+/*!
+ * \brief Tries to find the position of the next field in the DNS response.
+ *
+ * \internal
+ *
+ * \param  s    A char pointer to the current frame in the DNS response.
+ * \param  len  The remaining available length of the DNS response.
+ *
+ * \retval The position of the next field
+ * \retval -1 if there are no remaining fields
+ */
 static int skip_name(unsigned char *s, int len)
 {
        int x = 0;
@@ -172,20 +188,149 @@ static int skip_name(unsigned char *s, int len)
                        x++;
                        break;
                }
+
                if ((*s & 0xc0) == 0xc0) {
                        s += 2;
                        x += 2;
                        break;
                }
+
                x += *s + 1;
                s += *s + 1;
        }
-       if (x >= len)
-               return -1;
+
+       /* If we are out of room to search, return failure. */
+       if (x >= len) {
+               return AST_DNS_SEARCH_FAILURE;
+       }
+
+       /* Return the value for the current position in the DNS response. This is the start
+       position of the next field. */
        return x;
 }
 
-/*! \brief Parse DNS lookup result, call callback */
+/*!
+ * \brief Advances the position of the DNS response pointer by the size of the current field.
+ *
+ * \internal
+ *
+ * \param  dns_response   A pointer to a char pointer to the current field in the DNS response.
+ * \param  remaining_len  The remaining available length in the DNS response to search.
+ * \param  field_size     A positive value representing the size of the current field
+                          pointed to by the dns_response parameter.
+ *
+ * \retval The remaining length in the DNS response
+ * \retval -1 there are no frames remaining in the DNS response
+ */
+static int dns_advance_field(unsigned char **dns_response, int remaining_len, int field_size)
+{
+       if (dns_response == NULL || field_size < 0 || remaining_len < field_size) {
+               return AST_DNS_SEARCH_FAILURE;
+       }
+
+       *dns_response += field_size;
+       remaining_len -= field_size;
+
+       return remaining_len;
+}
+
+#ifndef HAVE_RES_NINIT
+/*!
+ * \brief Handles the DNS search if the system has RES_INIT.
+ *
+ * \internal
+ *
+ * \param  dname             Domain name to lookup (host, SRV domain, TXT record name).
+ * \param  rr_class          Record Class (see "man res_search").
+ * \param  rr_type           Record type (see "man res_search").
+ * \param  dns_response      The full DNS response.
+ * \param  dns_response_len  The length of the full DNS response.
+ *
+ * \retval The length of the DNS response
+ * \retval -1 on search failure
+ */
+static int dns_search_res(const char *dname, int rr_class, int rr_type,
+       unsigned char *dns_response, int dns_response_len)
+{
+
+       int ret = AST_DNS_SEARCH_FAILURE;
+       struct __res_state dns_state;
+
+       ast_mutex_lock(&res_lock);
+       res_init();
+       ret = res_search(&dns_state,
+                        dname,
+                        rr_class,
+                        rr_type,
+                        dns_response,
+                        dns_response_len);
+
+#ifdef HAVE_RES_CLOSE
+       res_close();
+#endif
+
+       ast_mutex_unlock(&res_lock);
+
+       return ret;
+}
+#else
+/*!
+ * \brief Handles the DNS search if the system has RES_NINIT.
+ *
+ * \internal
+ *
+ * \param  dname             Domain name to lookup (host, SRV domain, TXT record name).
+ * \param  rr_class          Record Class (see "man res_search").
+ * \param  rr_type           Record type (see "man res_search").
+ * \param  dns_response      The full DNS response.
+ * \param  dns_response_len  The length of the full DNS response.
+ *
+ * \retval The length of the DNS response
+ * \retval -1 on search failure
+ */
+static int dns_search_res(const char *dname, int rr_class, int rr_type,
+       unsigned char *dns_response, int dns_response_len)
+{
+
+       int ret = AST_DNS_SEARCH_FAILURE;
+       struct __res_state dns_state;
+
+       memset(&dns_state, 0, sizeof(dns_state));
+       res_ninit(&dns_state);
+       ret = res_nsearch(&dns_state,
+                         dname,
+                         rr_class,
+                         rr_type,
+                         dns_response,
+                         dns_response_len);
+
+#ifdef HAVE_RES_NDESTROY
+       res_ndestroy(&dns_state);
+#else
+       res_nclose(&dns_state);
+#endif
+
+       return ret;
+}
+#endif
+
+/*!
+ * \brief Parse DNS lookup result, call callback
+ *
+ * \internal
+ *
+ * \param  context   Void pointer containing data to use in the callback functions.
+ * \param  dname     Domain name to lookup (host, SRV domain, TXT record name).
+ * \param  class     Record Class (see "man res_search").
+ * \param  type      Record type (see "man res_search").
+ * \param  answer    The full DNS response.
+ * \param  len       The length of the full DNS response.
+ * \param  callback  Callback function for handling the discovered resource records from the DNS search.
+ *
+ * \retval -1 on search failure
+ * \retval  0 on no records found
+ * \retval  1 on success
+ */
 static int dns_parse_answer(void *context,
        int class, int type, unsigned char *answer, int len,
        int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
@@ -244,13 +389,110 @@ static int dns_parse_answer(void *context,
        return ret;
 }
 
-#ifndef HAVE_RES_NINIT
-AST_MUTEX_DEFINE_STATIC(res_lock);
-#endif
+/*!
+ * \brief Extended version of the DNS Parsing function.
+ *
+ * \details Parses the DNS lookup result and notifies the observer of each discovered
+ *          resource record with the provided callback.
+ *
+ * \internal
+ *
+ * \param  context           Void pointer containing data to use in the callback functions.
+ * \param  dname             Domain name to lookup (host, SRV domain, TXT record name).
+ * \param  rr_class          Record Class (see "man res_search").
+ * \param  rr_type           Record type (see "man res_search").
+ * \param  answer            The full DNS response.
+ * \param  answer_len        The length of the full DNS response.
+ * \param  response_handler  Callback function for handling the DNS response.
+ * \param  record_handler    Callback function for handling the discovered resource records from the DNS search.
+ *
+ * \retval -1 on search failure
+ * \retval  0 on no records found
+ * \retval  1 on success
+ */
+static int dns_parse_answer_ex(void *context, int rr_class, int rr_type, unsigned char *answer, int answer_len,
+       int (*response_handler)(void *context, unsigned char *dns_response, int dns_response_len, int rcode),
+       int (*record_handler)(void *context, unsigned char *record, int record_len, int ttl))
+{
+       unsigned char *dns_response = answer;
+       dns_HEADER *dns_header = (dns_HEADER *)answer;
+
+       struct dn_answer *ans;
+       int res, x, pos, dns_response_len, ret;
+
+       dns_response_len = answer_len;
+       ret = AST_DNS_SEARCH_NO_RECORDS;
+
+       /* Invoke the response_handler callback to notify the observer of the raw DNS response */
+       response_handler(context, dns_response, dns_response_len, ntohs(dns_header->rcode));
+
+       /* Verify there is something to parse */
+       if (answer_len == 0) {
+               return ret;
+       }
+
+       /* Try advancing the cursor for the dns header */
+       if ((pos = dns_advance_field(&answer, answer_len, sizeof(dns_HEADER))) < 0) {
+               ast_log(LOG_WARNING, "Length of DNS answer exceeds available search frames\n");
+               return AST_DNS_SEARCH_FAILURE;
+       }
+
+       /* Skip domain name and QCODE / QCLASS */
+       for (x = 0; x < ntohs(dns_header->qdcount); x++) {
+               if ((res = skip_name(answer, pos)) < 0) {
+                       ast_log(LOG_WARNING, "Failed skipping name\n");
+                       return AST_DNS_SEARCH_FAILURE;
+               }
+
+               /* Try advancing the cursor for the name and QCODE / QCLASS fields */
+               if ((pos = dns_advance_field(&answer, pos, res + 4)) < 0) {
+                       return AST_DNS_SEARCH_FAILURE;
+               }
+       }
 
-/*! \brief Lookup record in DNS
-\note Asterisk DNS is synchronus at this time. This means that if your DNS does
-not work properly, Asterisk might not start properly or a channel may lock.
+       /* Extract the individual records */
+       for (x = 0; x < ntohs(dns_header->ancount); x++) {
+               if ((res = skip_name(answer, pos)) < 0) {
+                       ast_log(LOG_WARNING, "Failed skipping name\n");
+                       return AST_DNS_SEARCH_FAILURE;
+               }
+
+               /* Try advancing the cursor to the current record */
+               if ((pos = dns_advance_field(&answer, pos, res)) < 0) {
+                       ast_log(LOG_WARNING, "Length of DNS answer exceeds available search frames\n");
+                       return AST_DNS_SEARCH_FAILURE;
+               }
+
+               /* Cast the current value for the answer pointer as a dn_answer struct */
+               ans = (struct dn_answer *) answer;
+
+               /* Try advancing the cursor to the end of the current record  */
+               if ((pos = dns_advance_field(&answer, pos, sizeof(struct dn_answer)))  < 0) {
+                       ast_log(LOG_WARNING, "Length of DNS answer exceeds available search frames\n");
+                       return AST_DNS_SEARCH_FAILURE;
+               }
+
+               /* Skip over the records that do not have the same resource record class and type we care about */
+               if (ntohs(ans->class) == rr_class && ntohs(ans->rtype) == rr_type) {
+                       /* Invoke the record handler callback to deliver the discovered record */
+                       record_handler(context, answer, ntohs(ans->size), ans->ttl);
+                       /*At least one record was found */
+                       ret = AST_DNS_SEARCH_SUCCESS;
+               }
+
+               /* Try and update the field to the next record, but ignore any errors that come
+                * back because this may be the end of the line. */
+               pos = dns_advance_field(&answer, pos, res + ntohs(ans->size));
+       }
+
+       return ret;
+}
+
+/*!
+ * \brief Lookup record in DNS
+ *
+ * \note Asterisk DNS is synchronus at this time. This means that if your DNS does not
+ *       work properly, Asterisk might not start properly or a channel may lock.
 */
 int ast_search_dns(void *context,
           const char *dname, int class, int type,
@@ -297,6 +539,50 @@ int ast_search_dns(void *context,
        return ret;
 }
 
+enum ast_dns_search_result ast_search_dns_ex(void *context, const char *dname, int rr_class, int rr_type,
+          int (*response_handler)(void *context, unsigned char *dns_response, int dns_response_len, int rcode),
+          int (*record_handler)(void *context, unsigned char *record, int record_len, int ttl))
+{
+       int ret, dns_response_len;
+       unsigned char dns_response[MAX_SIZE];
+
+       /* Assert that the callbacks are not NULL */
+       ast_assert(response_handler != NULL);
+       ast_assert(record_handler != NULL);
+
+       /* Try the DNS search. */
+       dns_response_len = dns_search_res(dname,
+                                         rr_class,
+                                         rr_type,
+                                         dns_response,
+                                         sizeof(dns_response));
+
+       if (dns_response_len < 0) {
+               ast_log(LOG_ERROR, "DNS search failed for %s\n", dname);
+               return AST_DNS_SEARCH_FAILURE;
+       }
+
+       /* Parse records from DNS response */
+       ret = dns_parse_answer_ex(context,
+                                 rr_class,
+                                 rr_type,
+                                 dns_response,
+                                 dns_response_len,
+                                 response_handler,
+                                 record_handler);
+
+       /* Handle the return code from parsing the DNS response */
+       if (ret == AST_DNS_SEARCH_FAILURE) {
+               /* Parsing Error */
+               ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
+       } else if (ret == AST_DNS_SEARCH_NO_RECORDS) {
+               /* No results found */
+               ast_debug(1, "DNS search yielded no results for %s\n", dname);
+       }
+
+       return ret;
+}
+
 struct ao2_container *ast_dns_get_nameservers(void)
 {
 #ifdef HAVE_RES_NINIT
diff --git a/main/dns_system_resolver.c b/main/dns_system_resolver.c
new file mode 100755 (executable)
index 0000000..a5ac771
--- /dev/null
@@ -0,0 +1,267 @@
+/*\r
+ * Asterisk -- An open source telephony toolkit.\r
+ *\r
+ * Copyright (C) 2015, Digium, Inc.\r
+ *\r
+ * Ashley Sanders <asanders@digium.com>\r
+ *\r
+ * See http://www.asterisk.org for more information about\r
+ * the Asterisk project. Please do not directly contact\r
+ * any of the maintainers of this project for assistance;\r
+ * the project provides a web site, mailing lists and IRC\r
+ * channels for your use.\r
+ *\r
+ * This program is free software, distributed under the terms of\r
+ * the GNU General Public License Version 2. See the LICENSE file\r
+ * at the top of the source tree.\r
+ */\r
+\r
+/*! \file\r
+ *\r
+ * \brief The default DNS resolver for Asterisk.\r
+ *\r
+ * \arg See also \ref res_resolver_unbound\r
+ *\r
+ * \author Ashley Sanders <asanders@digium.com>\r
+ */\r
+\r
+#include "asterisk.h"\r
+\r
+ASTERISK_REGISTER_FILE()\r
+\r
+#include "asterisk/_private.h"\r
+#include "asterisk/astobj2.h"\r
+#include "asterisk/dns.h"\r
+#include "asterisk/dns_core.h"\r
+#include "asterisk/dns_resolver.h"\r
+#include "asterisk/linkedlists.h"\r
+#include "asterisk/taskprocessor.h"\r
+\r
+/*! \brief The consideration priority for this resolver implementation. */\r
+#define DNS_SYSTEM_RESOLVER_PRIORITY INT_MAX\r
+\r
+/*! \brief Resolver return code upon success. */\r
+#define DNS_SYSTEM_RESOLVER_SUCCESS 0\r
+\r
+/*! \brief Resolver return code upon failure. */\r
+#define DNS_SYSTEM_RESOLVER_FAILURE -1\r
+\r
+\r
+static int dns_system_resolver_add_record(void *context, unsigned char *record, int record_len, int ttl);\r
+static int dns_system_resolver_cancel(struct ast_dns_query *query);\r
+static void dns_system_resolver_destroy(void);\r
+static int dns_system_resolver_process_query(void *data);\r
+static int dns_system_resolver_resolve(struct ast_dns_query *query);\r
+static int dns_system_resolver_set_response(void *context, unsigned char *dns_response, int dns_response_len, int rcode);\r
+\r
+\r
+/*! \brief The task processor to use for making DNS searches asynchronous. */\r
+static struct ast_taskprocessor *dns_system_resolver_tp;\r
+\r
+/*! \brief The base definition for the dns_system_resolver */\r
+struct ast_dns_resolver dns_system_resolver_base = {\r
+       .name = "system",\r
+       .priority = DNS_SYSTEM_RESOLVER_PRIORITY,\r
+       .resolve = dns_system_resolver_resolve,\r
+       .cancel = dns_system_resolver_cancel,\r
+};\r
+\r
+/*!\r
+ * \brief Callback to handle processing resource records.\r
+ *\r
+ * \details Adds an individual resource record discovered with ast_search_dns_ex to the\r
+ *          ast_dns_query currently being resolved.\r
+ *\r
+ * \internal\r
+ *\r
+ * \param context     A void pointer to the ast_dns_query being processed.\r
+ * \param record      An individual resource record discovered during the DNS search.\r
+ * \param record_len  The length of the resource record.\r
+ * \param ttl         The resource record's expiration time limit (time to live).\r
+ *\r
+ * \retval  0 on success\r
+ * \retval -1 on failure\r
+ */\r
+static int dns_system_resolver_add_record(void *context, unsigned char *record, int record_len, int ttl)\r
+{\r
+       struct ast_dns_query *query = context;\r
+\r
+       /* Add the record to the query.*/\r
+       return ast_dns_resolver_add_record(query,\r
+                                          ast_dns_query_get_rr_type(query),\r
+                                          ast_dns_query_get_rr_class(query),\r
+                                          ttl,\r
+                                          (const char*) record,\r
+                                          record_len);\r
+}\r
+\r
+/*!\r
+ * \brief Cancels processing resolution for a given query.\r
+ *\r
+ * \note The system API calls block so there is no way to cancel them. Therefore, this function always\r
+ * returns failure when invoked.\r
+ *\r
+ * \internal\r
+ *\r
+ * \param query  The ast_dns_query to cancel.\r
+ *\r
+ * \retval  0 on success\r
+ * \retval -1 on failure\r
+ */\r
+static int dns_system_resolver_cancel(struct ast_dns_query *query)\r
+{\r
+       return DNS_SYSTEM_RESOLVER_FAILURE;\r
+}\r
+\r
+/*!\r
+ * \brief Destructor.\r
+ *\r
+ * \internal\r
+ */\r
+static void dns_system_resolver_destroy(void)\r
+{\r
+       /* Unreference the task processor */\r
+       dns_system_resolver_tp = ast_taskprocessor_unreference(dns_system_resolver_tp);\r
+\r
+       /* Unregister the base resolver */\r
+       ast_dns_resolver_unregister(&dns_system_resolver_base);\r
+}\r
+\r
+/*!\r
+ * \brief Callback to handle processing the query from the ast_taskprocessor instance.\r
+ *\r
+ * \internal\r
+ *\r
+ * \param data  A void pointer to the ast_dns_query being processed.\r
+ *\r
+ * \retval -1 on search failure\r
+ * \retval  0 on no records found\r
+ * \retval  1 on success\r
+ */\r
+static int dns_system_resolver_process_query(void *data)\r
+{\r
+       struct ast_dns_query *query = data;\r
+\r
+       /* Perform the DNS search */\r
+       enum ast_dns_search_result res = ast_search_dns_ex(query,\r
+                                                          ast_dns_query_get_name(query),\r
+                                                          ast_dns_query_get_rr_class(query),\r
+                                                          ast_dns_query_get_rr_type(query),\r
+                                                          dns_system_resolver_set_response,\r
+                                                          dns_system_resolver_add_record);\r
+\r
+       /* Handle the possible return values from the DNS search */\r
+       if (res == AST_DNS_SEARCH_FAILURE) {\r
+               ast_log(LOG_ERROR, "DNS search failed for query: '%s'\n",\r
+                       ast_dns_query_get_name(query));\r
+       } else if (res == AST_DNS_SEARCH_NO_RECORDS) {\r
+               ast_log(LOG_WARNING, "DNS search failed to yield any results for query: '%s'\n",\r
+                       ast_dns_query_get_name(query));\r
+       }\r
+\r
+       /* Mark the query as complete */\r
+       ast_dns_resolver_completed(query);\r
+\r
+       /* Reduce the reference count on the query object */\r
+       ao2_ref(query, -1);\r
+\r
+       return res;\r
+}\r
+\r
+/*!\r
+ * \brief Resolves a DNS query.\r
+ *\r
+ * \internal\r
+ *\r
+ * \param query  The ast_dns_query to resolve.\r
+ *\r
+ * \retval  0 on successful load of query handler to the ast_taskprocessor instance\r
+ * \retval -1 on failure to load the query handler to the ast_taskprocessor instance\r
+ */\r
+static int dns_system_resolver_resolve(struct ast_dns_query *query)\r
+{\r
+       /* Add query processing handler to the task processor */\r
+       int res = ast_taskprocessor_push(dns_system_resolver_tp,\r
+                                        dns_system_resolver_process_query,\r
+                                        ao2_bump(query));\r
+\r
+       /* The query processing handler was not added to the task processor */\r
+       if (res < 0) {\r
+               ast_log(LOG_ERROR, "Failed to perform async DNS resolution of '%s'\n",\r
+                       ast_dns_query_get_name(query));\r
+               ao2_ref(query, -1);\r
+       }\r
+\r
+       /* Return the result of adding the query processing handler to the task processor */\r
+       return res;\r
+}\r
+\r
+/*!\r
+ * \brief Callback to handle initializing the results field.\r
+ *\r
+ * \internal\r
+ *\r
+ * \param dns_response  The full DNS response.\r
+ * \param dns_response  The length of the full DNS response.\r
+ * \param rcode         The DNS response code.\r
+ *\r
+ * \retval  0 on success\r
+ * \retval -1 on failure\r
+ */\r
+static int dns_system_resolver_set_response(void *context, unsigned char *dns_response, int dns_response_len, int rcode)\r
+{\r
+       struct ast_dns_query *query = context;\r
+       int res;\r
+\r
+       /* Instantiate the query's result field (if necessary). */\r
+       if (!ast_dns_query_get_result(query)) {\r
+               res = ast_dns_resolver_set_result(query,\r
+                                                 0,\r
+                                                 0,\r
+                                                 rcode,\r
+                                                 ast_dns_query_get_name(query),\r
+                                                 (const char*) dns_response,\r
+                                                 dns_response_len);\r
+\r
+               if (res) {\r
+                       /* There was a problem instantiating the results field. */\r
+                       ast_log(LOG_ERROR, "Could not instantiate the results field for query: '%s'\n",\r
+                               ast_dns_query_get_name(query));\r
+               }\r
+       } else {\r
+               res = DNS_SYSTEM_RESOLVER_SUCCESS;\r
+       }\r
+\r
+       return res;\r
+}\r
+\r
+/*!\r
+ * \brief Initializes the resolver.\r
+ *\r
+ * \retval  0 on success\r
+ * \retval -1 on failure\r
+ */\r
+int ast_dns_system_resolver_init(void)\r
+{\r
+       /* Register the base resolver */\r
+       int res = ast_dns_resolver_register(&dns_system_resolver_base);\r
+\r
+       if (res) {\r
+               return DNS_SYSTEM_RESOLVER_FAILURE;\r
+       }\r
+\r
+       /* Instantiate the task processor */\r
+       dns_system_resolver_tp = ast_taskprocessor_get("dns_system_resolver_tp",\r
+                                                       TPS_REF_DEFAULT);\r
+\r
+       /* Return error if the task processor failed to instantiate */\r
+       if (!dns_system_resolver_tp) {\r
+               dns_system_resolver_destroy();\r
+               return DNS_SYSTEM_RESOLVER_FAILURE;\r
+       }\r
+\r
+       /* Register the cleanup function */\r
+       ast_register_cleanup(dns_system_resolver_destroy);\r
+\r
+       return DNS_SYSTEM_RESOLVER_SUCCESS;\r
+}\r