[asterisk-commits] mmichelson: branch group/dns r434067 - in /team/group/dns: include/asterisk/ ...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Apr 6 11:59:25 CDT 2015


Author: mmichelson
Date: Mon Apr  6 11:59:22 2015
New Revision: 434067

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=434067
Log:
Merge NAPTR changes into the main DNS branch.

Modified:
    team/group/dns/include/asterisk/dns_internal.h
    team/group/dns/main/dns_core.c
    team/group/dns/main/dns_naptr.c
    team/group/dns/res/res_resolver_unbound.c

Modified: team/group/dns/include/asterisk/dns_internal.h
URL: http://svnview.digium.com/svn/asterisk/team/group/dns/include/asterisk/dns_internal.h?view=diff&rev=434067&r1=434066&r2=434067
==============================================================================
--- team/group/dns/include/asterisk/dns_internal.h (original)
+++ team/group/dns/include/asterisk/dns_internal.h Mon Apr  6 11:59:22 2015
@@ -35,6 +35,14 @@
 	size_t data_len;
 	/*! \brief Linked list information */
 	AST_LIST_ENTRY(ast_dns_record) list;
+	/*! \brief pointer to record-specific data.
+	 *
+	 * For certain "subclasses" of DNS records, the
+	 * location of the raw DNS data will differ from
+	 * the generic case. This pointer will reliably
+	 * be set to point to the raw DNS data, no matter
+	 * where in the structure it may lie.
+	 */
 	char *data_ptr;
 	/*! \brief The raw DNS record */
 	char data[0];
@@ -74,6 +82,13 @@
 	unsigned short order;
 	/*! \brief The preference of the NAPTR record */
 	unsigned short preference;
+	/*! \brief Buffer for NAPTR-specific data
+	 *
+	 * This includes the raw NAPTR record, as well as
+	 * the area where the flags, service, regexp, and
+	 * replacement strings are stored.
+	 */
+	char data[0];
 };
 
 /*! \brief The result of a DNS query */
@@ -152,6 +167,25 @@
 struct ast_sched_context *ast_dns_get_sched(void);
 
 /*!
+ * \brief Allocate and parse a DNS NAPTR record
+ *
+ * \param query The DNS query
+ * \param data This specific NAPTR record
+ * \param size The size of the NAPTR record
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char *data, const size_t size);
+
+/*!
+ * \brief Sort the NAPTR records on a result
+ *
+ * \param result The DNS result
+ */
+void dns_naptr_sort(struct ast_dns_result *result);
+
+/*!
  * \brief Allocate and parse a DNS SRV record
  *
  * \param query The DNS query
@@ -170,4 +204,3 @@
  */
 void ast_dns_srv_sort(struct ast_dns_result *result);
 
-

Modified: team/group/dns/main/dns_core.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns/main/dns_core.c?view=diff&rev=434067&r1=434066&r2=434067
==============================================================================
--- team/group/dns/main/dns_core.c (original)
+++ team/group/dns/main/dns_core.c Mon Apr  6 11:59:22 2015
@@ -460,7 +460,9 @@
 		return -1;
 	}
 
-	if (rr_type == ns_t_srv) {
+	if (rr_type == ns_t_naptr) {
+		record = dns_naptr_alloc(query, data, size);
+	} else if (rr_type == ns_t_srv) {
 		record = ast_dns_srv_alloc(query, data, size);
 	} else {
 		record = generic_record_alloc(query, data, size);
@@ -483,7 +485,9 @@
 
 void ast_dns_resolver_completed(struct ast_dns_query *query)
 {
-	if (ast_dns_query_get_rr_type(query) == ns_t_srv) {
+	if (ast_dns_query_get_rr_type(query) == ns_t_naptr) {
+		dns_naptr_sort(query->result);
+	} else if (ast_dns_query_get_rr_type(query) == ns_t_srv) {
 		ast_dns_srv_sort(query->result);
 	}
 

Modified: team/group/dns/main/dns_naptr.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns/main/dns_naptr.c?view=diff&rev=434067&r1=434066&r2=434067
==============================================================================
--- team/group/dns/main/dns_naptr.c (original)
+++ team/group/dns/main/dns_naptr.c Mon Apr  6 11:59:22 2015
@@ -31,35 +31,673 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <regex.h>
+
 #include "asterisk/dns_core.h"
 #include "asterisk/dns_naptr.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/dns_internal.h"
+#include "asterisk/utils.h"
+
+/*!
+ * \brief Result of analyzing NAPTR flags on a record
+ */
+enum flags_result {
+	/*! Terminal record, meaning the DDDS algorithm can be stopped */
+	FLAGS_TERMINAL,
+	/*! No flags provided, likely meaning another NAPTR lookup */
+	FLAGS_EMPTY,
+	/*! Unrecognized but valid flags. We cannot conclude what they mean */
+	FLAGS_UNKNOWN,
+	/*! Non-alphanumeric or invalid combination of flags */
+	FLAGS_INVALID,
+};
+
+/*!
+ * \brief Analyze and interpret NAPTR flags as per RFC 3404
+ *
+ * \note The flags string passed into this function is NOT NULL-terminated
+ *
+ * \param flags The flags string from a NAPTR record
+ * \flags_size The size of the flags string in bytes
+ * \return flag result
+ */
+static enum flags_result interpret_flags(const char *flags, uint8_t flags_size)
+{
+	int i;
+	char known_flag_found = 0;
+
+	if (flags_size == 0) {
+		return FLAGS_EMPTY;
+	}
+
+	/* Take care of the most common (and easy) case, one character */
+	if (flags_size == 1) {
+		if (*flags == 's' || *flags == 'S' ||
+				*flags == 'a' || *flags == 'A' ||
+				*flags == 'u' || *flags == 'U') {
+			return FLAGS_TERMINAL;
+		} else if (!isalnum(*flags)) {
+			return FLAGS_INVALID;
+		} else {
+			return FLAGS_UNKNOWN;
+		}
+	}
+
+	/*
+	 * Multiple flags are allowed, but you cannot mix the
+	 * S, A, U, and P flags together.
+	 */
+	for (i = 0; i < flags_size; ++i) {
+		if (!isalnum(flags[i])) {
+			return FLAGS_INVALID;
+		} else if (flags[i] == 's' || flags[i] == 'S') {
+			if (known_flag_found && known_flag_found != 's') {
+				return FLAGS_INVALID;
+			}
+			known_flag_found = 's';
+		} else if (flags[i] == 'u' || flags[i] == 'U') {
+			if (known_flag_found && known_flag_found != 'u') {
+				return FLAGS_INVALID;
+			}
+			known_flag_found = 'u';
+		} else if (flags[i] == 'a' || flags[i] == 'A') {
+			if (known_flag_found && known_flag_found != 'a') {
+				return FLAGS_INVALID;
+			}
+			known_flag_found = 'a';
+		} else if (flags[i] == 'p' || flags[i] == 'P') {
+			if (known_flag_found && known_flag_found != 'p') {
+				return FLAGS_INVALID;
+			}
+			known_flag_found = 'p';
+		}
+	}
+
+	return (!known_flag_found || known_flag_found == 'p') ? FLAGS_UNKNOWN : FLAGS_TERMINAL;
+}
+
+/*!
+ * \brief Analyze NAPTR services for validity as defined by RFC 3404
+ *
+ * \note The services string passed to this function is NOT NULL-terminated
+ * \param services The services string parsed from a NAPTR record
+ * \param services_size The size of the services string
+ * \retval 0 Services are valid
+ * \retval -1 Services are invalid
+ */
+static int services_invalid(const char *services, uint8_t services_size)
+{
+	const char *current_pos = services;
+	const char *end_of_services = services + services_size;
+
+	if (services_size == 0) {
+		return 0;
+	}
+
+	/* Services are broken into sections divided by a + sign. Each section
+	 * must start with an alphabetic character, and then can only contain
+	 * alphanumeric characters. The size of any section is limited to
+	 * 32 characters
+	 */
+	while (1) {
+		char *plus_pos = memchr(current_pos, '+', end_of_services - current_pos);
+		uint8_t current_size = plus_pos ? plus_pos - current_pos : end_of_services - current_pos;
+		int i;
+
+		if (!isalpha(current_pos[0])) {
+			return -1;
+		}
+
+		if (current_size > 32) {
+			return -1;
+		}
+
+		for (i = 1; i < current_size; ++i) {
+			if (!isalnum(current_pos[i])) {
+				return -1;
+			}
+		}
+
+		if (!plus_pos) {
+			break;
+		}
+		current_pos = plus_pos + 1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Determine if flags in the regexp are invalid
+ *
+ * A NAPTR regexp is structured like so
+ * /pattern/repl/FLAGS
+ *
+ * This ensures that the flags on the regexp are valid. Regexp
+ * flags can either be zero or one character long. If the flags
+ * are one character long, that character must be "i" to indicate
+ * the regex evaluation is case-insensitive.
+ *
+ * \note The flags string passed to this function is not NULL-terminated
+ * \param flags The regexp flags from the NAPTR record
+ * \param end A pointer to the end of the flags string
+ * \retval 0 Flags are valid
+ * \retval -1 Flags are invalid
+ */
+static int regexp_flags_invalid(const char *flags, const char *end)
+{
+	if (flags >= end) {
+		return 0;
+	}
+
+	if (end - flags > 1) {
+		return -1;
+	}
+
+	if (*flags != 'i') {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Determine if the replacement in the regexp is invalid
+ *
+ * A NAPTR regexp is structured like so
+ * /pattern/REPL/flags
+ *
+ * This ensures that the replacement on the regexp is valid. The regexp
+ * replacement is free to use any character it wants, plus backreferences
+ * and an escaped regexp delimiter.
+ *
+ * This function does not attempt to ensure that the backreferences refer
+ * to valid portions of the regexp's regex pattern.
+ *
+ * \note The repl string passed to this function is NOT NULL-terminated
+ *
+ * \param repl The regexp replacement string
+ * \param end Pointer to the end of the replacement string
+ * \param delim The delimiter character for the regexp
+ *
+ * \retval 0 Replacement is valid
+ * \retval -1 Replacement is invalid
+ */
+static int regexp_repl_invalid(const char *repl, const char *end, char delim)
+{
+	const char *ptr = repl;
+
+	if (repl == end) {
+		/* Kind of weird, but this is fine */
+		return 0;
+	}
+
+	while (1) {
+		char *backslash_pos = memchr(ptr, '\\', end - ptr);
+		if (!backslash_pos) {
+			break;
+		}
+
+		ast_assert(backslash_pos < end - 1);
+
+		/* XXX RFC 3402 is unclear about whether other backslash-escaped characters
+		 * (such as a backslash-escaped backslash) are legal
+		 */
+		if (!strchr("12345689", backslash_pos[1]) && backslash_pos[1] != delim) {
+			return -1;
+		}
+
+		ptr = backslash_pos + 1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Determine if the pattern in a regexp is invalid
+ *
+ * A NAPTR regexp is structured like so
+ * /PATTERN/repl/flags
+ *
+ * This ensures that the pattern on the regexp is valid. The pattern is
+ * passed to a regex compiler to determine its validity.
+ *
+ * \note The pattern string passed to this function is NOT NULL-terminated
+ *
+ * \param pattern The pattern from the NAPTR record
+ * \param end A pointer to the end of the pattern
+ *
+ * \retval 0 Pattern is valid
+ * \retval non-zero Pattern is invalid
+ */
+static int regexp_pattern_invalid(const char *pattern, const char *end)
+{
+	int pattern_size = end - pattern;
+	char pattern_str[pattern_size + 1];
+	regex_t reg;
+	int res;
+
+	/* regcomp requires a NULL-terminated string */
+	memcpy(pattern_str, pattern, pattern_size);
+	pattern_str[pattern_size] = '\0';
+
+	res = regcomp(&reg, pattern_str, REG_EXTENDED);
+
+	regfree(&reg);
+
+	return res;
+}
+
+/*!
+ * \brief Determine if the regexp in a NAPTR record is invalid
+ *
+ * The goal of this function is to divide the regexp into its
+ * constituent parts and then let validation subroutines determine
+ * if each part is valid. If all parts are valid, then the entire
+ * regexp is valid.
+ *
+ * \note The regexp string passed to this function is NOT NULL-terminated
+ *
+ * \param regexp The regexp from the NAPTR record
+ * \param regexp_size The size of the regexp string
+ *
+ * \retval 0 regexp is valid
+ * \retval non-zero regexp is invalid
+ */
+static int regexp_invalid(const char *regexp, uint8_t regexp_size)
+{
+	char delim;
+	const char *delim2_pos;
+	const char *delim3_pos;
+	const char *ptr = regexp;
+	const char *end_of_regexp = regexp + regexp_size;
+	const char *regex_pos;
+	const char *repl_pos;
+	const char *flags_pos;
+
+	if (regexp_size == 0) {
+		return 0;
+	}
+
+	/* The delimiter will be a ! or / in most cases, but the rules allow
+	 * for the delimiter to be nearly any character. It cannot be 'i' because
+	 * the delimiter cannot be the same as regexp flags. The delimiter cannot
+	 * be 1-9 because the delimiter cannot be a backreference number. RFC
+	 * 2915 specified that backslash was also not allowed as a delimiter, but
+	 * RFC 3402 does not say this. We've gone ahead and made the character
+	 * illegal for our purposes.
+	 */
+	delim = *ptr;
+	if (strchr("123456789\\i", delim)) {
+		return -1;
+	}
+	++ptr;
+	regex_pos = ptr;
+
+	/* Find the other two delimiters. If the delim is escaped with a backslash, it doesn't count */
+	while (1) {
+		delim2_pos = memchr(ptr, delim, end_of_regexp - ptr);
+		if (!delim2_pos) {
+			return -1;
+		}
+		ptr = delim2_pos + 1;
+		if (delim2_pos[-1] != '\\') {
+			break;
+		}
+	}
+
+	if (ptr >= end_of_regexp) {
+		return -1;
+	}
+
+	repl_pos = ptr;
+
+	while (1) {
+		delim3_pos = memchr(ptr, delim, end_of_regexp - ptr);
+		if (!delim3_pos) {
+			return -1;
+		}
+		ptr = delim3_pos + 1;
+		if (delim3_pos[-1] != '\\') {
+			break;
+		}
+	}
+	flags_pos = ptr;
+
+	if (regexp_flags_invalid(flags_pos, end_of_regexp) ||
+			regexp_repl_invalid(repl_pos, delim3_pos, delim) ||
+			regexp_pattern_invalid(regex_pos, delim2_pos)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+#define PAST_END_OF_RECORD ptr >= end_of_record
+
+struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char *data, const size_t size)
+{
+	struct ast_dns_naptr_record *naptr;
+	char *ptr = NULL;
+	uint16_t order;
+	uint16_t preference;
+	uint8_t flags_size;
+	char *flags;
+	uint8_t services_size;
+	char *services;
+	uint8_t regexp_size;
+	char *regexp;
+	char replacement[256] = "";
+	int replacement_size;
+	char *naptr_offset;
+	char *naptr_search_base = (char *)query->result->answer;
+	size_t remaining_size = query->result->answer_size;
+	char *end_of_record;
+	enum flags_result flags_res;
+
+	/*
+	 * This is bordering on the hackiest thing I've ever written.
+	 * Part of parsing a NAPTR record is to parse a potential replacement
+	 * domain name. Decoding this domain name requires the use of the
+	 * dn_expand() function. This function requires that the domain you
+	 * pass in be a pointer to within the full DNS answer. Unfortunately,
+	 * libunbound gives its RRs back as copies of data from the DNS answer
+	 * instead of pointers to within the DNS answer. This means that in order
+	 * to be able to parse the domain name correctly, I need to find the
+	 * current NAPTR record inside the DNS answer and operate on it. This
+	 * loop is designed to find the current NAPTR record within the full
+	 * DNS answer and set the "ptr" variable to the beginning of the
+	 * NAPTR RDATA
+	 */
+	while (1) {
+		naptr_offset = memchr(naptr_search_base, data[0], remaining_size);
+
+		/* Since the NAPTR record we have been given came from the DNS answer,
+		 * we should never run into a situation where we can't find ourself
+		 * in the answer
+		 */
+		ast_assert(naptr_offset != NULL);
+		ast_assert(naptr_search_base + remaining_size - naptr_offset >= size);
+
+		/* ... but just to be on the safe side, let's be sure we can break
+		 * out if the assertion doesn't hold
+		 */
+		if (!naptr_offset || naptr_search_base + remaining_size - naptr_offset < size) {
+			ast_log(LOG_ERROR, "Failed to locate NAPTR record within DNS result\n");
+			return NULL;
+		}
+
+		if (!memcmp(naptr_offset, data, size)) {
+			/* BAM! FOUND IT! */
+			ptr = naptr_offset;
+			break;
+		}
+		/* Data didn't match us, so keep looking */
+		remaining_size -= naptr_offset - naptr_search_base;
+		naptr_search_base = naptr_offset + 1;
+	}
+
+	ast_assert(ptr != NULL);
+
+	end_of_record = ptr + size;
+
+	/* ORDER */
+	/* This assignment takes a big-endian 16-bit value and stores it in the
+	 * machine's native byte order. Using this method allows us to avoid potential
+	 * alignment issues in case the order is not on a short-addressable boundary.
+	 * See http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html for
+	 * more information
+	 */
+	order = ((unsigned char)(ptr[1]) << 0) | ((unsigned char)(ptr[0]) << 8);
+	ptr += 2;
+
+	if (PAST_END_OF_RECORD) {
+		return NULL;
+	}
+
+	/* PREFERENCE */
+	preference = ((unsigned char) (ptr[1]) << 0) | ((unsigned char)(ptr[0]) << 8);
+	ptr += 2;
+
+	if (PAST_END_OF_RECORD) {
+		return NULL;
+	}
+
+	/* FLAGS */
+	flags_size = *ptr;
+	++ptr;
+	if (PAST_END_OF_RECORD) {
+		return NULL;
+	}
+	flags = ptr;
+	ptr += flags_size;
+	if (PAST_END_OF_RECORD) {
+		return NULL;
+	}
+
+	/* SERVICES */
+	services_size = *ptr;
+	++ptr;
+	if (PAST_END_OF_RECORD) {
+		return NULL;
+	}
+	services = ptr;
+	ptr += services_size;
+	if (PAST_END_OF_RECORD) {
+		return NULL;
+	}
+
+	/* REGEXP */
+	regexp_size = *ptr;
+	++ptr;
+	if (PAST_END_OF_RECORD) {
+		return NULL;
+	}
+	regexp = ptr;
+	ptr += regexp_size;
+	if (PAST_END_OF_RECORD) {
+		return NULL;
+	}
+
+	replacement_size = dn_expand((unsigned char *)query->result->answer, (unsigned char *) end_of_record, (unsigned char *) ptr, replacement, sizeof(replacement) - 1);
+	if (replacement_size < 0) {
+		ast_log(LOG_ERROR, "Failed to expand domain name: %s\n", strerror(errno));
+		return NULL;
+	}
+
+	ptr += replacement_size;
+
+	if (ptr != end_of_record) {
+		ast_log(LOG_ERROR, "NAPTR record gave undersized string indications.\n");
+		return NULL;
+	}
+
+	/* We've validated the size of the NAPTR record. Now we can validate
+	 * the individual parts
+	 */
+	flags_res = interpret_flags(flags, flags_size);
+	if (flags_res == FLAGS_INVALID) {
+		ast_log(LOG_ERROR, "NAPTR Record contained invalid flags %.*s\n", flags_size, flags);
+		return NULL;
+	}
+
+	if (services_invalid(services, services_size)) {
+		ast_log(LOG_ERROR, "NAPTR record contained invalid services %.*s\n", services_size, services);
+		return NULL;
+	}
+
+	if (regexp_invalid(regexp, regexp_size)) {
+		ast_log(LOG_ERROR, "NAPTR record contained invalid regexp %.*s\n", regexp_size, regexp);
+		return NULL;
+	}
+
+	/* replacement_size takes into account the NULL label, so a NAPTR record with no replacement
+	 * will have a replacement_size of 1.
+	 */
+	if (regexp_size && replacement_size > 1) {
+		ast_log(LOG_ERROR, "NAPTR record contained both a regexp and replacement\n");
+		return NULL;
+	}
+
+	naptr = ast_calloc(1, sizeof(*naptr) + size + flags_size + 1 + services_size + 1 + regexp_size + 1 + replacement_size + 1);
+	if (!naptr) {
+		return NULL;
+	}
+
+	naptr->order = order;
+	naptr->preference = preference;
+
+	ptr = naptr->data;
+	ptr += size;
+
+	strncpy(ptr, flags, flags_size);
+	ptr[flags_size] = '\0';
+	naptr->flags = ptr;
+	ptr += flags_size + 1;
+
+	strncpy(ptr, services, services_size);
+	ptr[services_size] = '\0';
+	naptr->service = ptr;
+	ptr += services_size + 1;
+
+	strncpy(ptr, regexp, regexp_size);
+	ptr[regexp_size] = '\0';
+	naptr->regexp = ptr;
+	ptr += regexp_size + 1;
+
+	strcpy(ptr, replacement);
+	naptr->replacement = ptr;
+
+	naptr->generic.data_ptr = naptr->data;
+
+	return (struct ast_dns_record *)naptr;
+}
+
+
+static int compare_order(const void *record1, const void *record2)
+{
+	const struct ast_dns_naptr_record **left = (const struct ast_dns_naptr_record **)record1;
+	const struct ast_dns_naptr_record **right = (const struct ast_dns_naptr_record **)record2;
+
+	if ((*left)->order < (*right)->order) {
+		return -1;
+	} else if ((*left)->order > (*right)->order) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+static int compare_preference(const void *record1, const void *record2)
+{
+	const struct ast_dns_naptr_record **left = (const struct ast_dns_naptr_record **)record1;
+	const struct ast_dns_naptr_record **right = (const struct ast_dns_naptr_record **)record2;
+
+	if ((*left)->preference < (*right)->preference) {
+		return -1;
+	} else if ((*left)->preference > (*right)->preference) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+void dns_naptr_sort(struct ast_dns_result *result)
+{
+	struct ast_dns_record *current;
+	size_t num_records = 0;
+	struct ast_dns_naptr_record **records;
+	int i = 0;
+	int j = 0;
+	int cur_order;
+
+	/* Determine the number of records */
+	AST_LIST_TRAVERSE(&result->records, current, list) {
+		++num_records;
+	}
+
+	/* No point in continuing if there are no records */
+	if (num_records == 0) {
+		return;
+	}
+
+	/* Allocate an array with that number of records */
+	records = ast_alloca(num_records * sizeof(*records));
+
+	/* Move records from the list to the array */
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&result->records, current, list) {
+		records[i++] = (struct ast_dns_naptr_record *) current;
+		AST_LIST_REMOVE_CURRENT(list);
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	/* Sort the array by order */
+	qsort(records, num_records, sizeof(*records), compare_order);
+
+	/* Sort subarrays by preference */
+	for (i = 0; i < num_records; i = j) {
+		cur_order = records[i]->order;
+		for (j = i + 1; j < num_records; ++j) {
+			if (records[j]->order != cur_order) {
+				break;
+			}
+		}
+		qsort(&records[i], j - i, sizeof(*records), compare_preference);
+	}
+
+	/* Place sorted records back into the original list */
+	for (i = 0; i < num_records; ++i) {
+		AST_LIST_INSERT_TAIL(&result->records, (struct ast_dns_record *)(records[i]), list);
+	}
+}
 
 const char *ast_dns_naptr_get_flags(const struct ast_dns_record *record)
 {
-	return NULL;
+	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
+
+	ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
+	return naptr->flags;
 }
 
 const char *ast_dns_naptr_get_service(const struct ast_dns_record *record)
 {
-	return NULL;
+	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
+
+	ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
+	return naptr->service;
 }
 
 const char *ast_dns_naptr_get_regexp(const struct ast_dns_record *record)
 {
-	return NULL;
+	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
+
+	ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
+	return naptr->regexp;
 }
 
 const char *ast_dns_naptr_get_replacement(const struct ast_dns_record *record)
 {
-	return NULL;
+	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
+
+	ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
+	return naptr->replacement;
 }
 
 unsigned short ast_dns_naptr_get_order(const struct ast_dns_record *record)
 {
-	return 0;
+	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
+
+	ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
+	return naptr->order;
 }
 
 unsigned short ast_dns_naptr_get_preference(const struct ast_dns_record *record)
 {
-	return 0;
-}
+	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
+
+	ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
+	return naptr->preference;
+}

Modified: team/group/dns/res/res_resolver_unbound.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns/res/res_resolver_unbound.c?view=diff&rev=434067&r1=434066&r2=434067
==============================================================================
--- team/group/dns/res/res_resolver_unbound.c (original)
+++ team/group/dns/res/res_resolver_unbound.c Mon Apr  6 11:59:22 2015
@@ -303,7 +303,6 @@
 
 	ao2_ref(data, -1);
 	ao2_ref(cfg, -1);
-
 	return res;
 }
 
@@ -492,6 +491,8 @@
 }
 
 #ifdef TEST_FRAMEWORK
+
+#include "asterisk/dns_naptr.h"
 
 /*!
  * \brief A DNS record to be used during a test
@@ -1186,6 +1187,123 @@
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(resolve_naptr)
+{
+	RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
+	RAII_VAR(struct unbound_config *, cfg, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);
+
+	const struct ast_dns_record *record;
+
+	static const char * DOMAIN1 = "goose.feathers";
+	int i;
+	enum ast_test_result_state res = AST_TEST_PASS;
+
+	struct naptr_record {
+		const char *zone_entry;
+		uint16_t order;
+		uint16_t preference;
+		const char *flags;
+		const char *services;
+		const char *regexp;
+		const char *replacement;
+		int visited;
+	} records [] = {
+		{ "goose.feathers 12345 IN NAPTR 100 100 A SIP+D2U \"\" goose.down", 100, 100, "A", "SIP+D2U", "", "goose.down", 0},
+		{ "goose.feathers 12345 IN NAPTR 100 200 A SIP+D2T \"\" duck.down", 100, 200, "A", "SIP+D2T", "", "duck.down", 0},
+		{ "goose.feathers 12345 IN NAPTR 200 100 A SIPS+D2U \"\" pheasant.down", 200, 100, "A", "SIPS+D2U", "", "pheasant.down", 0},
+		{ "goose.feathers 12345 IN NAPTR 200 200 A SIPS+D2T \"\" platypus.fur", 200, 200, "A", "SIPS+D2T", "", "platypus.fur", 0},
+	};
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "resolve_naptr";
+		info->category = "/res/res_resolver_unbound/";
+		info->summary = "Attempt resolution of NAPTR record\n";
+		info->description = "This test performs a NAPTR lookup and ensures that\n"
+			"the returned record has the appropriate values set\n";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	cfg = ao2_global_obj_ref(globals);
+	resolver = ao2_bump(cfg->global->state->resolver);
+
+	ub_ctx_zone_add(resolver->context, DOMAIN1, "static");
+
+	for (i = 0; i < ARRAY_LEN(records); ++i) {
+		ub_ctx_data_add(resolver->context, records[i].zone_entry);
+	}
+
+	if (ast_dns_resolve(DOMAIN1, ns_t_naptr, ns_c_in, &result)) {
+		ast_test_status_update(test, "Failed to resolve domain\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!result) {
+		ast_test_status_update(test, "Successful resolution set a NULL result\n");
+		return AST_TEST_FAIL;
+	}
+
+	record = ast_dns_result_get_records(result);
+	if (!record) {
+		ast_test_status_update(test, "Failed to get any DNS records from the result\n");
+		return AST_TEST_FAIL;
+	}
+
+	i = 0;
+	for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
+		if (ast_dns_naptr_get_order(record) != records[i].order) {
+			ast_test_status_update(test, "Expected order %hu, got order %hu from NAPTR record\n",
+					records[i].order, ast_dns_naptr_get_order(record));
+			res = AST_TEST_FAIL;
+		}
+		if (ast_dns_naptr_get_preference(record) != records[i].preference) {
+			ast_test_status_update(test, "Expected preference %hu, got preference %hu from NAPTR record\n",
+					records[i].preference, ast_dns_naptr_get_preference(record));
+			res = AST_TEST_FAIL;
+		}
+		if (strcmp(ast_dns_naptr_get_flags(record), records[i].flags)) {
+			ast_test_status_update(test, "Expected flags %s, got flags %s from NAPTR record\n",
+					records[i].flags, ast_dns_naptr_get_flags(record));
+			res = AST_TEST_FAIL;
+		}
+		if (strcmp(ast_dns_naptr_get_service(record), records[i].services)) {
+			ast_test_status_update(test, "Expected services %s, got services %s from NAPTR record\n",
+					records[i].services, ast_dns_naptr_get_service(record));
+			res = AST_TEST_FAIL;
+		}
+		if (strcmp(ast_dns_naptr_get_regexp(record), records[i].regexp)) {
+			ast_test_status_update(test, "Expected regexp %s, got regexp %s from NAPTR record\n",
+					records[i].regexp, ast_dns_naptr_get_regexp(record));
+			res = AST_TEST_FAIL;
+		}
+		if (strcmp(ast_dns_naptr_get_replacement(record), records[i].replacement)) {
+			ast_test_status_update(test, "Expected replacement %s, got replacement %s from NAPTR record\n",
+					records[i].replacement, ast_dns_naptr_get_replacement(record));
+			res = AST_TEST_FAIL;
+		}
+		records[i].visited = 1;
+		++i;
+	}
+
+	if (i != ARRAY_LEN(records)) {
+		ast_test_status_update(test, "Unexpected number of records visited\n");
+		res = AST_TEST_FAIL;
+	}
+
+	for (i = 0; i < ARRAY_LEN(records); ++i) {
+		if (!records[i].visited) {
+			ast_test_status_update(test, "Did not visit all expected NAPTR records\n");
+			res = AST_TEST_FAIL;
+		}
+	}
+
+	return res;
+
+}
+
 AST_TEST_DEFINE(resolve_srv)
 {
 	RAII_VAR(struct unbound_resolver *, resolver, NULL, ao2_cleanup);
@@ -1274,6 +1392,7 @@
 	AST_TEST_UNREGISTER(resolve_sync_off_nominal);
 	AST_TEST_UNREGISTER(resolve_sync_off_nominal);
 	AST_TEST_UNREGISTER(resolve_cancel_off_nominal);
+	AST_TEST_UNREGISTER(resolve_naptr);
 	AST_TEST_UNREGISTER(resolve_srv);
 	return 0;
 }
@@ -1331,6 +1450,7 @@
 	AST_TEST_REGISTER(resolve_sync_off_nominal);
 	AST_TEST_REGISTER(resolve_async_off_nominal);
 	AST_TEST_REGISTER(resolve_cancel_off_nominal);
+	AST_TEST_REGISTER(resolve_naptr);
 	AST_TEST_REGISTER(resolve_srv);
 
 	return AST_MODULE_LOAD_SUCCESS;




More information about the asterisk-commits mailing list