Author: mmichelson Date: Mon Apr 6 12:05:47 2015 New Revision: 434068 URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=434068 Log: Merge NAPTR support into trunk.
This adds NAPTR record allocation and sorting, as well as unit tests that verify that NAPTR records are parsed and sorted correctly. Review: https://reviewboard.asterisk.org/r/4542 Modified: trunk/include/asterisk/dns_internal.h (contents, props changed) trunk/main/dns_core.c trunk/main/dns_naptr.c trunk/res/res_resolver_unbound.c trunk/tests/test_dns.c (props changed) trunk/tests/test_dns_recurring.c (props changed) Modified: trunk/include/asterisk/dns_internal.h URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/dns_internal.h?view=diff&rev=434068&r1=434067&r2=434068 ============================================================================== --- trunk/include/asterisk/dns_internal.h (original) +++ trunk/include/asterisk/dns_internal.h Mon Apr 6 12:05:47 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); - Propchange: trunk/include/asterisk/dns_internal.h ------------------------------------------------------------------------------ --- svn:keywords (original) +++ svn:keywords Mon Apr 6 12:05:47 2015 @@ -1,1 +1,1 @@ -Author Date Id Revision +'Author Date Id Revision' Modified: trunk/main/dns_core.c URL: http://svnview.digium.com/svn/asterisk/trunk/main/dns_core.c?view=diff&rev=434068&r1=434067&r2=434068 ============================================================================== --- trunk/main/dns_core.c (original) +++ trunk/main/dns_core.c Mon Apr 6 12:05:47 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: trunk/main/dns_naptr.c URL: http://svnview.digium.com/svn/asterisk/trunk/main/dns_naptr.c?view=diff&rev=434068&r1=434067&r2=434068 ============================================================================== --- trunk/main/dns_naptr.c (original) +++ trunk/main/dns_naptr.c Mon Apr 6 12:05:47 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(®, pattern_str, REG_EXTENDED); + + regfree(®); + + 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: trunk/res/res_resolver_unbound.c URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_resolver_unbound.c?view=diff&rev=434068&r1=434067&r2=434068 ============================================================================== --- trunk/res/res_resolver_unbound.c (original) +++ trunk/res/res_resolver_unbound.c Mon Apr 6 12:05:47 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; Propchange: trunk/tests/test_dns.c ------------------------------------------------------------------------------ --- svn:keywords (original) +++ svn:keywords Mon Apr 6 12:05:47 2015 @@ -1,1 +1,1 @@ -Author Date Id Revision +'Author Date Id Revision' Propchange: trunk/tests/test_dns_recurring.c ------------------------------------------------------------------------------ --- svn:keywords (original) +++ svn:keywords Mon Apr 6 12:05:47 2015 @@ -1,1 +1,1 @@ -Author Date Id Revision +'Author Date Id Revision' -- _____________________________________________________________________ -- Bandwidth and Colocation Provided by http://www.api-digital.com -- svn-commits mailing list To UNSUBSCRIBE or update options visit: http://lists.digium.com/mailman/listinfo/svn-commits