dns_naptr.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2015, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief DNS NAPTR Record Support
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>core</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 434490 $")
00033 
00034 #include <arpa/nameser.h>
00035 #include <resolv.h>
00036 #include <regex.h>
00037 
00038 #include "asterisk/dns_core.h"
00039 #include "asterisk/dns_naptr.h"
00040 #include "asterisk/linkedlists.h"
00041 #include "asterisk/dns_internal.h"
00042 #include "asterisk/utils.h"
00043 
00044 /*!
00045  * \brief Result of analyzing NAPTR flags on a record
00046  */
00047 enum flags_result {
00048    /*! Terminal record, meaning the DDDS algorithm can be stopped */
00049    FLAGS_TERMINAL,
00050    /*! No flags provided, likely meaning another NAPTR lookup */
00051    FLAGS_EMPTY,
00052    /*! Unrecognized but valid flags. We cannot conclude what they mean */
00053    FLAGS_UNKNOWN,
00054    /*! Non-alphanumeric or invalid combination of flags */
00055    FLAGS_INVALID,
00056 };
00057 
00058 /*!
00059  * \brief Analyze and interpret NAPTR flags as per RFC 3404
00060  *
00061  * \note The flags string passed into this function is NOT NULL-terminated
00062  *
00063  * \param flags The flags string from a NAPTR record
00064  * \flags_size The size of the flags string in bytes
00065  * \return flag result
00066  */
00067 static enum flags_result interpret_flags(const char *flags, uint8_t flags_size)
00068 {
00069    int i;
00070    char known_flag_found = 0;
00071 
00072    if (flags_size == 0) {
00073       return FLAGS_EMPTY;
00074    }
00075 
00076    /* Take care of the most common (and easy) case, one character */
00077    if (flags_size == 1) {
00078       if (*flags == 's' || *flags == 'S' ||
00079             *flags == 'a' || *flags == 'A' ||
00080             *flags == 'u' || *flags == 'U') {
00081          return FLAGS_TERMINAL;
00082       } else if (!isalnum(*flags)) {
00083          return FLAGS_INVALID;
00084       } else {
00085          return FLAGS_UNKNOWN;
00086       }
00087    }
00088 
00089    /*
00090     * Multiple flags are allowed, but you cannot mix the
00091     * S, A, U, and P flags together.
00092     */
00093    for (i = 0; i < flags_size; ++i) {
00094       if (!isalnum(flags[i])) {
00095          return FLAGS_INVALID;
00096       } else if (flags[i] == 's' || flags[i] == 'S') {
00097          if (known_flag_found && known_flag_found != 's') {
00098             return FLAGS_INVALID;
00099          }
00100          known_flag_found = 's';
00101       } else if (flags[i] == 'u' || flags[i] == 'U') {
00102          if (known_flag_found && known_flag_found != 'u') {
00103             return FLAGS_INVALID;
00104          }
00105          known_flag_found = 'u';
00106       } else if (flags[i] == 'a' || flags[i] == 'A') {
00107          if (known_flag_found && known_flag_found != 'a') {
00108             return FLAGS_INVALID;
00109          }
00110          known_flag_found = 'a';
00111       } else if (flags[i] == 'p' || flags[i] == 'P') {
00112          if (known_flag_found && known_flag_found != 'p') {
00113             return FLAGS_INVALID;
00114          }
00115          known_flag_found = 'p';
00116       }
00117    }
00118 
00119    return (!known_flag_found || known_flag_found == 'p') ? FLAGS_UNKNOWN : FLAGS_TERMINAL;
00120 }
00121 
00122 /*!
00123  * \brief Analyze NAPTR services for validity as defined by RFC 3404
00124  *
00125  * \note The services string passed to this function is NOT NULL-terminated
00126  * \param services The services string parsed from a NAPTR record
00127  * \param services_size The size of the services string
00128  * \retval 0 Services are valid
00129  * \retval -1 Services are invalid
00130  */
00131 static int services_invalid(const char *services, uint8_t services_size)
00132 {
00133    const char *current_pos = services;
00134    const char *end_of_services = services + services_size;
00135 
00136    if (services_size == 0) {
00137       return 0;
00138    }
00139 
00140    /* Services are broken into sections divided by a + sign. Each section
00141     * must start with an alphabetic character, and then can only contain
00142     * alphanumeric characters. The size of any section is limited to
00143     * 32 characters
00144     */
00145    while (1) {
00146       char *plus_pos = memchr(current_pos, '+', end_of_services - current_pos);
00147       uint8_t current_size = plus_pos ? plus_pos - current_pos : end_of_services - current_pos;
00148       int i;
00149 
00150       if (!isalpha(current_pos[0])) {
00151          return -1;
00152       }
00153 
00154       if (current_size > 32) {
00155          return -1;
00156       }
00157 
00158       for (i = 1; i < current_size; ++i) {
00159          if (!isalnum(current_pos[i])) {
00160             return -1;
00161          }
00162       }
00163 
00164       if (!plus_pos) {
00165          break;
00166       }
00167       current_pos = plus_pos + 1;
00168    }
00169 
00170    return 0;
00171 }
00172 
00173 /*!
00174  * \brief Determine if flags in the regexp are invalid
00175  *
00176  * A NAPTR regexp is structured like so
00177  * /pattern/repl/FLAGS
00178  *
00179  * This ensures that the flags on the regexp are valid. Regexp
00180  * flags can either be zero or one character long. If the flags
00181  * are one character long, that character must be "i" to indicate
00182  * the regex evaluation is case-insensitive.
00183  *
00184  * \note The flags string passed to this function is not NULL-terminated
00185  * \param flags The regexp flags from the NAPTR record
00186  * \param end A pointer to the end of the flags string
00187  * \retval 0 Flags are valid
00188  * \retval -1 Flags are invalid
00189  */
00190 static int regexp_flags_invalid(const char *flags, const char *end)
00191 {
00192    if (flags >= end) {
00193       return 0;
00194    }
00195 
00196    if (end - flags > 1) {
00197       return -1;
00198    }
00199 
00200    if (*flags != 'i') {
00201       return -1;
00202    }
00203 
00204    return 0;
00205 }
00206 
00207 /*!
00208  * \brief Determine if the replacement in the regexp is invalid
00209  *
00210  * A NAPTR regexp is structured like so
00211  * /pattern/REPL/flags
00212  *
00213  * This ensures that the replacement on the regexp is valid. The regexp
00214  * replacement is free to use any character it wants, plus backreferences
00215  * and an escaped regexp delimiter.
00216  *
00217  * This function does not attempt to ensure that the backreferences refer
00218  * to valid portions of the regexp's regex pattern.
00219  *
00220  * \note The repl string passed to this function is NOT NULL-terminated
00221  *
00222  * \param repl The regexp replacement string
00223  * \param end Pointer to the end of the replacement string
00224  * \param delim The delimiter character for the regexp
00225  *
00226  * \retval 0 Replacement is valid
00227  * \retval -1 Replacement is invalid
00228  */
00229 static int regexp_repl_invalid(const char *repl, const char *end, char delim)
00230 {
00231    const char *ptr = repl;
00232 
00233    if (repl == end) {
00234       /* Kind of weird, but this is fine */
00235       return 0;
00236    }
00237 
00238    while (1) {
00239       char *backslash_pos = memchr(ptr, '\\', end - ptr);
00240       if (!backslash_pos) {
00241          break;
00242       }
00243 
00244       ast_assert(backslash_pos < end - 1);
00245 
00246       /* XXX RFC 3402 is unclear about whether other backslash-escaped characters
00247        * (such as a backslash-escaped backslash) are legal
00248        */
00249       if (!strchr("12345689", backslash_pos[1]) && backslash_pos[1] != delim) {
00250          return -1;
00251       }
00252 
00253       ptr = backslash_pos + 1;
00254    }
00255 
00256    return 0;
00257 }
00258 
00259 /*!
00260  * \brief Determine if the pattern in a regexp is invalid
00261  *
00262  * A NAPTR regexp is structured like so
00263  * /PATTERN/repl/flags
00264  *
00265  * This ensures that the pattern on the regexp is valid. The pattern is
00266  * passed to a regex compiler to determine its validity.
00267  *
00268  * \note The pattern string passed to this function is NOT NULL-terminated
00269  *
00270  * \param pattern The pattern from the NAPTR record
00271  * \param end A pointer to the end of the pattern
00272  *
00273  * \retval 0 Pattern is valid
00274  * \retval non-zero Pattern is invalid
00275  */
00276 static int regexp_pattern_invalid(const char *pattern, const char *end)
00277 {
00278    int pattern_size = end - pattern;
00279    char pattern_str[pattern_size + 1];
00280    regex_t reg;
00281    int res;
00282 
00283    /* regcomp requires a NULL-terminated string */
00284    memcpy(pattern_str, pattern, pattern_size);
00285    pattern_str[pattern_size] = '\0';
00286 
00287    res = regcomp(&reg, pattern_str, REG_EXTENDED);
00288 
00289    regfree(&reg);
00290 
00291    return res;
00292 }
00293 
00294 /*!
00295  * \brief Determine if the regexp in a NAPTR record is invalid
00296  *
00297  * The goal of this function is to divide the regexp into its
00298  * constituent parts and then let validation subroutines determine
00299  * if each part is valid. If all parts are valid, then the entire
00300  * regexp is valid.
00301  *
00302  * \note The regexp string passed to this function is NOT NULL-terminated
00303  *
00304  * \param regexp The regexp from the NAPTR record
00305  * \param regexp_size The size of the regexp string
00306  *
00307  * \retval 0 regexp is valid
00308  * \retval non-zero regexp is invalid
00309  */
00310 static int regexp_invalid(const char *regexp, uint8_t regexp_size)
00311 {
00312    char delim;
00313    const char *delim2_pos;
00314    const char *delim3_pos;
00315    const char *ptr = regexp;
00316    const char *end_of_regexp = regexp + regexp_size;
00317    const char *regex_pos;
00318    const char *repl_pos;
00319    const char *flags_pos;
00320 
00321    if (regexp_size == 0) {
00322       return 0;
00323    }
00324 
00325    /* The delimiter will be a ! or / in most cases, but the rules allow
00326     * for the delimiter to be nearly any character. It cannot be 'i' because
00327     * the delimiter cannot be the same as regexp flags. The delimiter cannot
00328     * be 1-9 because the delimiter cannot be a backreference number. RFC
00329     * 2915 specified that backslash was also not allowed as a delimiter, but
00330     * RFC 3402 does not say this. We've gone ahead and made the character
00331     * illegal for our purposes.
00332     */
00333    delim = *ptr;
00334    if (strchr("123456789\\i", delim)) {
00335       return -1;
00336    }
00337    ++ptr;
00338    regex_pos = ptr;
00339 
00340    /* Find the other two delimiters. If the delim is escaped with a backslash, it doesn't count */
00341    while (1) {
00342       delim2_pos = memchr(ptr, delim, end_of_regexp - ptr);
00343       if (!delim2_pos) {
00344          return -1;
00345       }
00346       ptr = delim2_pos + 1;
00347       if (delim2_pos[-1] != '\\') {
00348          break;
00349       }
00350    }
00351 
00352    if (ptr >= end_of_regexp) {
00353       return -1;
00354    }
00355 
00356    repl_pos = ptr;
00357 
00358    while (1) {
00359       delim3_pos = memchr(ptr, delim, end_of_regexp - ptr);
00360       if (!delim3_pos) {
00361          return -1;
00362       }
00363       ptr = delim3_pos + 1;
00364       if (delim3_pos[-1] != '\\') {
00365          break;
00366       }
00367    }
00368    flags_pos = ptr;
00369 
00370    if (regexp_flags_invalid(flags_pos, end_of_regexp) ||
00371          regexp_repl_invalid(repl_pos, delim3_pos, delim) ||
00372          regexp_pattern_invalid(regex_pos, delim2_pos)) {
00373       return -1;
00374    }
00375 
00376    return 0;
00377 }
00378 
00379 #define PAST_END_OF_RECORD ptr >= end_of_record
00380 
00381 struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char *data, const size_t size)
00382 {
00383    struct ast_dns_naptr_record *naptr;
00384    char *ptr = NULL;
00385    uint16_t order;
00386    uint16_t preference;
00387    uint8_t flags_size;
00388    char *flags;
00389    uint8_t services_size;
00390    char *services;
00391    uint8_t regexp_size;
00392    char *regexp;
00393    char replacement[256] = "";
00394    int replacement_size;
00395    const char *end_of_record;
00396    enum flags_result flags_res;
00397 
00398    ptr = dns_find_record(data, size, query->result->answer, query->result->answer_size);
00399    ast_assert(ptr != NULL);
00400 
00401    end_of_record = ptr + size;
00402 
00403    /* ORDER */
00404    /* This assignment takes a big-endian 16-bit value and stores it in the
00405     * machine's native byte order. Using this method allows us to avoid potential
00406     * alignment issues in case the order is not on a short-addressable boundary.
00407     * See http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html for
00408     * more information
00409     */
00410    ptr += dns_parse_short((unsigned char *) ptr, &order);
00411    if (PAST_END_OF_RECORD) {
00412       return NULL;
00413    }
00414 
00415    /* PREFERENCE */
00416    ptr += dns_parse_short((unsigned char *) ptr, &preference);
00417    if (PAST_END_OF_RECORD) {
00418       return NULL;
00419    }
00420 
00421    /* FLAGS */
00422    ptr += dns_parse_string(ptr, &flags_size, &flags);
00423    if (PAST_END_OF_RECORD) {
00424       return NULL;
00425    }
00426 
00427    /* SERVICES */
00428    ptr += dns_parse_string(ptr, &services_size, &services);
00429    if (PAST_END_OF_RECORD) {
00430       return NULL;
00431    }
00432 
00433    /* REGEXP */
00434    ptr += dns_parse_string(ptr, &regexp_size, &regexp);
00435    if (PAST_END_OF_RECORD) {
00436       return NULL;
00437    }
00438 
00439    replacement_size = dn_expand((unsigned char *)query->result->answer, (unsigned char *) end_of_record, (unsigned char *) ptr, replacement, sizeof(replacement) - 1);
00440    if (replacement_size < 0) {
00441       ast_log(LOG_ERROR, "Failed to expand domain name: %s\n", strerror(errno));
00442       return NULL;
00443    }
00444 
00445    ptr += replacement_size;
00446 
00447    if (ptr != end_of_record) {
00448       ast_log(LOG_ERROR, "NAPTR record gave undersized string indications.\n");
00449       return NULL;
00450    }
00451 
00452    /* We've validated the size of the NAPTR record. Now we can validate
00453     * the individual parts
00454     */
00455    flags_res = interpret_flags(flags, flags_size);
00456    if (flags_res == FLAGS_INVALID) {
00457       ast_log(LOG_ERROR, "NAPTR Record contained invalid flags %.*s\n", flags_size, flags);
00458       return NULL;
00459    }
00460 
00461    if (services_invalid(services, services_size)) {
00462       ast_log(LOG_ERROR, "NAPTR record contained invalid services %.*s\n", services_size, services);
00463       return NULL;
00464    }
00465 
00466    if (regexp_invalid(regexp, regexp_size)) {
00467       ast_log(LOG_ERROR, "NAPTR record contained invalid regexp %.*s\n", regexp_size, regexp);
00468       return NULL;
00469    }
00470 
00471    /* replacement_size takes into account the NULL label, so a NAPTR record with no replacement
00472     * will have a replacement_size of 1.
00473     */
00474    if (regexp_size && replacement_size > 1) {
00475       ast_log(LOG_ERROR, "NAPTR record contained both a regexp and replacement\n");
00476       return NULL;
00477    }
00478 
00479    naptr = ast_calloc(1, sizeof(*naptr) + size + flags_size + 1 + services_size + 1 + regexp_size + 1 + replacement_size + 1);
00480    if (!naptr) {
00481       return NULL;
00482    }
00483 
00484    naptr->order = order;
00485    naptr->preference = preference;
00486 
00487    ptr = naptr->data;
00488    ptr += size;
00489 
00490    strncpy(ptr, flags, flags_size);
00491    ptr[flags_size] = '\0';
00492    naptr->flags = ptr;
00493    ptr += flags_size + 1;
00494 
00495    strncpy(ptr, services, services_size);
00496    ptr[services_size] = '\0';
00497    naptr->service = ptr;
00498    ptr += services_size + 1;
00499 
00500    strncpy(ptr, regexp, regexp_size);
00501    ptr[regexp_size] = '\0';
00502    naptr->regexp = ptr;
00503    ptr += regexp_size + 1;
00504 
00505    strcpy(ptr, replacement);
00506    naptr->replacement = ptr;
00507 
00508    naptr->generic.data_ptr = naptr->data;
00509 
00510    return (struct ast_dns_record *)naptr;
00511 }
00512 
00513 
00514 static int compare_order(const void *record1, const void *record2)
00515 {
00516    const struct ast_dns_naptr_record **left = (const struct ast_dns_naptr_record **)record1;
00517    const struct ast_dns_naptr_record **right = (const struct ast_dns_naptr_record **)record2;
00518 
00519    if ((*left)->order < (*right)->order) {
00520       return -1;
00521    } else if ((*left)->order > (*right)->order) {
00522       return 1;
00523    } else {
00524       return 0;
00525    }
00526 }
00527 
00528 static int compare_preference(const void *record1, const void *record2)
00529 {
00530    const struct ast_dns_naptr_record **left = (const struct ast_dns_naptr_record **)record1;
00531    const struct ast_dns_naptr_record **right = (const struct ast_dns_naptr_record **)record2;
00532 
00533    if ((*left)->preference < (*right)->preference) {
00534       return -1;
00535    } else if ((*left)->preference > (*right)->preference) {
00536       return 1;
00537    } else {
00538       return 0;
00539    }
00540 }
00541 
00542 void dns_naptr_sort(struct ast_dns_result *result)
00543 {
00544    struct ast_dns_record *current;
00545    size_t num_records = 0;
00546    struct ast_dns_naptr_record **records;
00547    int i = 0;
00548    int j = 0;
00549    int cur_order;
00550 
00551    /* Determine the number of records */
00552    AST_LIST_TRAVERSE(&result->records, current, list) {
00553       ++num_records;
00554    }
00555 
00556    /* No point in continuing if there are no records */
00557    if (num_records == 0) {
00558       return;
00559    }
00560 
00561    /* Allocate an array with that number of records */
00562    records = ast_alloca(num_records * sizeof(*records));
00563 
00564    /* Move records from the list to the array */
00565    AST_LIST_TRAVERSE_SAFE_BEGIN(&result->records, current, list) {
00566       records[i++] = (struct ast_dns_naptr_record *) current;
00567       AST_LIST_REMOVE_CURRENT(list);
00568    }
00569    AST_LIST_TRAVERSE_SAFE_END;
00570 
00571    /* Sort the array by order */
00572    qsort(records, num_records, sizeof(*records), compare_order);
00573 
00574    /* Sort subarrays by preference */
00575    for (i = 0; i < num_records; i = j) {
00576       cur_order = records[i]->order;
00577       for (j = i + 1; j < num_records; ++j) {
00578          if (records[j]->order != cur_order) {
00579             break;
00580          }
00581       }
00582       qsort(&records[i], j - i, sizeof(*records), compare_preference);
00583    }
00584 
00585    /* Place sorted records back into the original list */
00586    for (i = 0; i < num_records; ++i) {
00587       AST_LIST_INSERT_TAIL(&result->records, (struct ast_dns_record *)(records[i]), list);
00588    }
00589 }
00590 
00591 const char *ast_dns_naptr_get_flags(const struct ast_dns_record *record)
00592 {
00593    struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
00594 
00595    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
00596    return naptr->flags;
00597 }
00598 
00599 const char *ast_dns_naptr_get_service(const struct ast_dns_record *record)
00600 {
00601    struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
00602 
00603    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
00604    return naptr->service;
00605 }
00606 
00607 const char *ast_dns_naptr_get_regexp(const struct ast_dns_record *record)
00608 {
00609    struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
00610 
00611    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
00612    return naptr->regexp;
00613 }
00614 
00615 const char *ast_dns_naptr_get_replacement(const struct ast_dns_record *record)
00616 {
00617    struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
00618 
00619    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
00620    return naptr->replacement;
00621 }
00622 
00623 unsigned short ast_dns_naptr_get_order(const struct ast_dns_record *record)
00624 {
00625    struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
00626 
00627    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
00628    return naptr->order;
00629 }
00630 
00631 unsigned short ast_dns_naptr_get_preference(const struct ast_dns_record *record)
00632 {
00633    struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
00634 
00635    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
00636    return naptr->preference;
00637 }

Generated on Thu Apr 16 06:27:33 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6