[Asterisk-code-review] ASTERISK-25146: Created a system-level DNS resolver for the ... (asterisk[master])

Ashley Sanders asteriskteam at digium.com
Tue Jun 30 13:32:58 CDT 2015


Ashley Sanders has uploaded a new change for review.

  https://gerrit.asterisk.org/749

Change subject: ASTERISK-25146: Created a system-level DNS resolver for the DNS core.
......................................................................

ASTERISK-25146: Created a system-level DNS resolver for the DNS core.

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.

Change-Id: I3b36ea17b889a98df4f8d80d50bb7ee175afa077
---
M include/asterisk/_private.h
M include/asterisk/dns.h
A include/asterisk/dns_system_resolver.h
M main/asterisk.c
M main/dns.c
A main/dns_system_resolver.c
6 files changed, 642 insertions(+), 19 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/49/749/1

diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index d49de17..3f9f094 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -29,6 +29,7 @@
 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 */
diff --git a/include/asterisk/dns.h b/include/asterisk/dns.h
index 4899fa8..6f66ecc 100644
--- a/include/asterisk/dns.h
+++ b/include/asterisk/dns.h
@@ -24,17 +24,48 @@
 #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 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
+ *       services does not work, Asterisk may lock while waiting for 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. Performs a DNS lookup, (used by
+ *         DNS, enum and SRV lookups), parses the results and notifies observers of any
+ *         discovered resource records (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.
+ * \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
+ *
+ * \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.
+ */
+int 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);
diff --git a/include/asterisk/dns_system_resolver.h b/include/asterisk/dns_system_resolver.h
new file mode 100755
index 0000000..f879777
--- /dev/null
+++ b/include/asterisk/dns_system_resolver.h
@@ -0,0 +1,32 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Ashley Sanders <asanders at 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 Definitions for the default DNS resolver for Asterisk.
+ *
+ * \author Ashley Sanders <asanders at digium.com>
+ */
+
+/*!
+ * \brief Initializes the resolver.
+ *
+ * \retval  0 on success.
+ * \retval -1 on failure.
+ */
+int ast_dns_system_resolver_init(void);
diff --git a/main/asterisk.c b/main/asterisk.c
index 53bcead..0478f6c 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -4511,6 +4511,11 @@
 		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);
diff --git a/main/dns.c b/main/dns.c
index fd87420..a14eac7 100644
--- a/main/dns.c
+++ b/main/dns.c
@@ -45,7 +45,17 @@
 #include "asterisk/dns.h"
 #include "asterisk/endian.h"
 
+/*! \brief Size used for creating the DNS response container. */
 #define MAX_SIZE 4096
+
+/*! \brief Return code upon failure. */
+#define DNS_SEARCH_FAILURE -1
+
+/*! \brief Return code upon no records found. */
+#define DNS_SEARCH_NO_RECORDS 0
+
+/*! \brief Return code upon success. */
+#define DNS_SEARCH_SUCCESS 1
 
 #ifdef __PDP_ENDIAN
 #if __BYTE_ORDER == __PDP_ENDIAN
@@ -57,6 +67,10 @@
 #endif
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 #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
@@ -156,12 +170,23 @@
 } 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 +197,151 @@
 			x++;
 			break;
 		}
+
 		if ((*s & 0xc0) == 0xc0) {
 			s += 2;
 			x += 2;
 			break;
 		}
+
 		x += *s + 1;
 		s += *s + 1;
 	}
+
+	/* If we are out of room to search, return failure. */
 	if (x >= len)
-		return -1;
+		return 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 Updates the current frame in the DNS response by the requested `length
+ *
+ * \internal
+ *
+ * \param  dns_response   A char pointer to the current frame in the DNS response.
+ * \param  remaining_len  The remaining available length in the DNS response to search.
+ *
+ * \retval The remaining length in the DNS response
+ * \retval -1 there are no frames remaining in the DNS response
+ */
+static int dns_update_frame(unsigned char *dns_response, int remaining_len, int frame_size)
+{
+	dns_response += frame_size;
+	remaining_len -= frame_size;
+
+	if (remaining_len < 0) {
+		ast_log(LOG_WARNING, "Length of DNS answer exceeds frame\n");
+		return DNS_SEARCH_FAILURE;
+	}
+
+	return remaining_len;
+}
+
+/*!
+ * \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_n(const char *dname, int rr_class, int rr_type,
+	unsigned char *dns_response, int dns_response_len)
+{
+
+#ifndef HAVE_RES_NINIT
+
+	struct __res_state dns_state;
+
+	ast_mutex_lock(&res_lock);
+	res_init();
+	dns_response_len = res_nsearch(&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 dns_response_len;         /* Search is complete */
+#endif
+
+	return DNS_SEARCH_FAILURE;       /* No search performed */
+}
+
+/*!
+ * \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_ninit(const char *dname, int rr_class, int rr_type,
+	unsigned char *dns_response, int dns_response_len)
+{
+
+#ifdef HAVE_RES_NINIT
+	struct __res_state dns_state;
+
+	memset(&dns_state, 0, sizeof(dns_state));
+	res_ninit(&dns_state);
+	dns_response_len = 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 dns_response_len;        /* Search is complete */
+#endif
+
+	return DNS_SEARCH_FAILURE;       /* No search performed */
+}
+
+/*!
+ * \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,9 +400,97 @@
 	return ret;
 }
 
-#ifndef HAVE_RES_NINIT
-AST_MUTEX_DEFINE_STATIC(res_lock);
-#endif
+/*!
+ * \brief Extended version of the DNS Parsing function. 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 = DNS_SEARCH_NO_RECORDS;
+
+	/* Invoke the response_handler callback, if provided, to notify the observer of the raw DNS response */
+	if (response_handler) {
+		response_handler(context, dns_response, dns_response_len, ntohs(dns_header->rcode));
+	}
+
+	/* Verify there is something to parse */
+	if (answer_len == 0) {
+		return DNS_SEARCH_NO_RECORDS;
+	}
+
+	/* Try updating the cursors for the dns header */
+	if ((pos = dns_update_frame(answer, answer_len, sizeof(dns_HEADER))) < 0) {
+		return 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 DNS_SEARCH_FAILURE;
+		}
+
+		/* Try updating the cursors for the name and QCODE / QCLASS fields */
+		if ((pos = dns_update_frame(answer, pos, res + 4)) < 0) {
+			return DNS_SEARCH_FAILURE;
+		}
+	}
+
+	/* 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 DNS_SEARCH_FAILURE;
+		}
+
+		/* Try updating the cursors for the current record */
+		if ((pos = dns_update_frame(answer, pos, res + sizeof(struct dn_answer)))  < 0) {
+			return DNS_SEARCH_FAILURE;
+		}
+
+		/* Cast the current value for the answer pointer as a dn_answer struct */
+		ans = (struct dn_answer *)answer;
+
+		/* 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 callback, if provided, to deliver the discovered record */
+			if (record_handler) {
+				record_handler(context, answer, ntohs(ans->size), ans->ttl);
+				ret = DNS_SEARCH_SUCCESS;
+			}
+		}
+
+		/* Try and update the frame to the next record, but ignore any errors that come
+		 * back because this may be the end of the line. */
+		pos = dns_update_frame(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
@@ -297,6 +541,48 @@
 	return ret;
 }
 
+int 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];
+
+	/* Try the DNS search. */
+	if ((dns_response_len = dns_search_res_ninit(dname,
+	                                             rr_class,
+	                                             rr_type,
+	                                             dns_response,
+	                                             sizeof(dns_response))) < 0) {
+		if ((dns_response_len = dns_search_res_n(dname,
+		                                         rr_class,
+		                                         rr_type,
+		                                         dns_response,
+		                                         sizeof(dns_response))) < 0) {
+			ast_log(LOG_WARNING, "DNS Search was not performed; System does not have either RES_INIT or RES_NINIT .");
+			return 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 parse result */
+	if (ret == DNS_SEARCH_FAILURE) {
+		ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);                  /* Parsing Error */
+	} else if (ret == DNS_SEARCH_NO_RECORDS) {
+		ast_log(LOG_WARNING, "DNS search yielded no results for %s\n", dname);    /* No results found */
+	}
+
+	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
index 0000000..3bb367b
--- /dev/null
+++ b/main/dns_system_resolver.c
@@ -0,0 +1,268 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Ashley Sanders <asanders at 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 The default DNS resolver for Asterisk.
+ *
+ * \arg See also \ref res_resolver_unbound
+ *
+ * \author Ashley Sanders <asanders at digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include "asterisk/astobj2.h"
+#include "asterisk/dns.h"
+#include "asterisk/dns_core.h"
+#include "asterisk/dns_system_resolver.h"
+#include "asterisk/dns_resolver.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/taskprocessor.h"
+
+/*! \brief The consideration priority for this resolver implementation. */
+#define DNS_SYSTEM_RESOLVER_PRIORITY INT_MAX
+
+/*! \brief Resolver return code upon success. */
+#define DNS_SYSTEM_RESOLVER_SUCCESS 0
+
+/*! \brief Resolver return code upon failure. */
+#define DNS_SYSTEM_RESOLVER_FAILURE -1
+
+
+static int dns_system_resolver_add_record(void *context, unsigned char *record, int record_len, int ttl);
+static int dns_system_resolver_cancel(struct ast_dns_query *query);
+static void dns_system_resolver_destroy(void);
+static int dns_system_resolver_process_query(void *data);
+static int dns_system_resolver_resolve(struct ast_dns_query *query);
+static int dns_system_resolver_set_response(void *context, unsigned char *dns_response, int dns_response_len, int rcode);
+
+
+/*! \brief The task processor to use for making DNS searches asynchronous. */
+static struct ast_taskprocessor *dns_system_resolver_tp;
+
+/*! \brief The base definition for the dns_system_resolver */
+struct ast_dns_resolver dns_system_resolver_base = {
+	.name = "system",
+	.priority = DNS_SYSTEM_RESOLVER_PRIORITY,
+	.resolve = dns_system_resolver_resolve,
+	.cancel = dns_system_resolver_cancel,
+};
+
+/*!
+ * \brief Callback to handle processing an individual resource record discovered from ast_search_dns_ex.
+ *
+ * \internal
+ *
+ * \param context     A void pointer to the ast_dns_query being processed.
+ * \param record      An individual resource record discovered during the DNS search.
+ * \param record_len  The length of the resource record.
+ * \param ttl         The resource record's expiration time limit (time to live).
+ *
+ * \retval  0 on success
+ * \retval -1 on failure
+ */
+static int dns_system_resolver_add_record(void *context, unsigned char *record, int record_len, int ttl)
+{
+	struct ast_dns_query *query = context;
+
+	/* Add the record to the query.*/
+	return ast_dns_resolver_add_record(query,                               /* The DNS query */
+	                                   ast_dns_query_get_rr_type(query),    /* Resource record type */
+	                                   ast_dns_query_get_rr_class(query),   /* Resource record class */
+	                                   ttl,                                 /* TTL of the record */
+	                                   (const char*)record,                 /* The raw DNS record */
+	                                   record_len);                         /* The size of the raw DNS record */
+}
+
+/*!
+ * \brief Cancels processing resolution for a given query.
+ *
+ * \note The system API calls block so there is no way to cancel them. Therefore, this function always
+ * returns failure when invoked.
+ *
+ * \internal
+ *
+ * \param query  The ast_dns_query to cancel.
+ *
+ * \retval  0 on success
+ * \retval -1 on failure
+ */
+static int dns_system_resolver_cancel(struct ast_dns_query *query)
+{
+	return DNS_SYSTEM_RESOLVER_FAILURE;
+}
+
+/*!
+ * \brief Destructor.
+ *
+ * \internal
+ */
+static void dns_system_resolver_destroy(void)
+{
+	/* Unreference the task processor */
+	dns_system_resolver_tp = ast_taskprocessor_unreference(dns_system_resolver_tp);
+
+	/* Unregister the base resolver */
+	ast_dns_resolver_unregister(&dns_system_resolver_base);
+
+	return;
+}
+
+/*!
+ * \brief Callback to handle processing the query from the ast_taskprocessor instance.
+ *
+ * \internal
+ *
+ * \param data  A void pointer to the ast_dns_query being processed.
+ *
+ * \retval -1 on search failure
+ * \retval  0 on no records found
+ * \retval  1 on success
+ */
+static int dns_system_resolver_process_query(void *data)
+{
+	struct ast_dns_query *query = data;
+
+	/* Perform the DNS search */
+	int res = ast_search_dns_ex(query,
+	                            ast_dns_query_get_name(query),
+	                            ast_dns_query_get_rr_class(query),
+	                            ast_dns_query_get_rr_type(query),
+	                            dns_system_resolver_set_response,
+	                            dns_system_resolver_add_record);
+
+	/* Handle the possible return values from the DNS search */
+	if (res < 0) {
+		/* DNS parse error */
+		ast_log(LOG_ERROR, "DNS search failed for query: '%s'\n",
+		        ast_dns_query_get_name(query));
+	} else if (res == 0) {
+		/* No results */
+		ast_log(LOG_ERROR, "DNS search failed to yield any results for query: '%s'\n",
+		        ast_dns_query_get_name(query));
+	}
+
+	/* Mark the query as complete */
+	ast_dns_resolver_completed(query);
+
+	/* Reduce the reference count on the query object */
+	ao2_ref(query, -1);
+
+	return res;
+}
+
+/*!
+ * \brief Resolves a DNS query.
+ *
+ * \internal
+ *
+ * \param query  The ast_dns_query to resolve.
+ *
+ * \retval  0 on successful load of query handler to the ast_taskprocessor instance
+ * \retval -1 on failure to load the query handler to the ast_taskprocessor instance
+ */
+static int dns_system_resolver_resolve(struct ast_dns_query *query)
+{
+	/* Add query processing handler to the task processor */
+	int res = ast_taskprocessor_push(dns_system_resolver_tp,
+	                                 dns_system_resolver_process_query,
+	                                 ao2_bump(query));
+
+	/* The query processing handler was not added to the task processor */
+	if (res == DNS_SYSTEM_RESOLVER_FAILURE) {
+		ast_log(LOG_ERROR, "Failed to perform async DNS resolution of '%s'\n",
+		        ast_dns_query_get_name(query));
+		ao2_ref(query, -1);
+	}
+
+	/* Return the result of adding the query processing handler to the task processor */
+	return res;
+}
+
+/*!
+ * \brief Callback to handle initializing the results field.
+ *
+ * \internal
+ *
+ * \param dns_response  The full DNS response.
+ * \param dns_response  The length of the full DNS response.
+ * \param rcode         The DNS response code.
+ *
+ * \retval  0 on success
+ * \retval -1 on failure
+ */
+static int dns_system_resolver_set_response(void *context, unsigned char *dns_response, int dns_response_len, int rcode)
+{
+	struct ast_dns_query *query = context;
+	int res;
+
+	/* Instantiate the query's result field (if necessary). */
+	if (!ast_dns_query_get_result(query)) {
+		res = ast_dns_resolver_set_result(query,                          /* The DNS query */
+		                                  0,                              /* Is the result secure? */
+		                                  0,                              /* Is the result bogus? */
+		                                  rcode,                          /* Optional response code */
+		                                  ast_dns_query_get_name(query),  /* Canonical name */
+		                                  (const char*)dns_response,      /* The raw DNS answer */
+		                                  dns_response_len);              /* The size of the raw DNS answer */
+
+		if (res) {
+			/* There was a problem instantiating the results field. */
+			ast_log(LOG_ERROR, "Could not instantiate the results field for query: '%s'\n",
+			        ast_dns_query_get_name(query));
+		}
+	} else {
+		res = DNS_SYSTEM_RESOLVER_SUCCESS;
+	}
+
+	return res;
+}
+
+/*!
+ * \brief Initializes the resolver.
+ *
+ * \retval  0 on success
+ * \retval -1 on failure
+ */
+int ast_dns_system_resolver_init(void)
+{
+	/* Register the base resolver */
+	int res = ast_dns_resolver_register(&dns_system_resolver_base);
+
+	if (res) {
+		return DNS_SYSTEM_RESOLVER_FAILURE;
+	}
+
+	/* Instantiate the task processor */
+	dns_system_resolver_tp = ast_taskprocessor_get("dns_system_resolver_tp",
+	                                                TPS_REF_DEFAULT);
+
+	/* Return error if the task processor failed to instantiate */
+	if (!dns_system_resolver_tp) {
+		dns_system_resolver_destroy();
+		return DNS_SYSTEM_RESOLVER_FAILURE;
+	}
+
+	/* Register the cleanup function */
+	ast_register_cleanup(dns_system_resolver_destroy);
+
+	return DNS_SYSTEM_RESOLVER_SUCCESS;
+}

-- 
To view, visit https://gerrit.asterisk.org/749
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I3b36ea17b889a98df4f8d80d50bb7ee175afa077
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Ashley Sanders <asanders at digium.com>



More information about the asterisk-code-review mailing list