| 
									
										
										
										
											2015-03-25 12:32:26 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2015, Digium, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Joshua Colp <jcolp@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 DNS NAPTR Record Support | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \author Joshua Colp <jcolp@digium.com> | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*** MODULEINFO
 | 
					
						
							|  |  |  | 	<support_level>core</support_level> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | #include <arpa/nameser.h>
 | 
					
						
							| 
									
										
										
										
											2016-11-20 09:19:18 +11:00
										 |  |  | #include <netinet/in.h>
 | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | #include <resolv.h>
 | 
					
						
							|  |  |  | #include <regex.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-25 12:32:26 +00:00
										 |  |  | #include "asterisk/dns_core.h"
 | 
					
						
							|  |  |  | #include "asterisk/dns_naptr.h"
 | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | #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 | 
					
						
							| 
									
										
										
										
											2021-11-19 16:47:25 +01:00
										 |  |  |  * \param flags_size The size of the flags string in bytes | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  |  * \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; | 
					
						
							| 
									
										
										
										
											2015-04-09 14:58:02 +00:00
										 |  |  | 	const char *end_of_record; | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	enum flags_result flags_res; | 
					
						
							| 
									
										
										
										
											2018-10-25 09:25:58 -06:00
										 |  |  | 	size_t naptr_len; | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-09 14:58:02 +00:00
										 |  |  | 	ptr = dns_find_record(data, size, query->result->answer, query->result->answer_size); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2015-04-09 14:58:02 +00:00
										 |  |  | 	ptr += dns_parse_short((unsigned char *) ptr, &order); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	if (PAST_END_OF_RECORD) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* PREFERENCE */ | 
					
						
							| 
									
										
										
										
											2015-04-09 14:58:02 +00:00
										 |  |  | 	ptr += dns_parse_short((unsigned char *) ptr, &preference); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	if (PAST_END_OF_RECORD) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* FLAGS */ | 
					
						
							| 
									
										
										
										
											2015-04-09 14:58:02 +00:00
										 |  |  | 	ptr += dns_parse_string(ptr, &flags_size, &flags); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	if (PAST_END_OF_RECORD) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* SERVICES */ | 
					
						
							| 
									
										
										
										
											2015-04-09 14:58:02 +00:00
										 |  |  | 	ptr += dns_parse_string(ptr, &services_size, &services); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	if (PAST_END_OF_RECORD) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* REGEXP */ | 
					
						
							| 
									
										
										
										
											2015-04-09 14:58:02 +00:00
										 |  |  | 	ptr += dns_parse_string(ptr, ®exp_size, ®exp); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	if (PAST_END_OF_RECORD) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 09:25:58 -06:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The return value from dn_expand represents the size of the replacement | 
					
						
							|  |  |  | 	 * in the buffer which MAY be compressed.  Since the expanded replacement | 
					
						
							|  |  |  | 	 * is NULL terminated, you can use strlen() to get the expanded size. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	replacement_size = dn_expand((unsigned char *)query->result->answer, | 
					
						
							|  |  |  | 		(unsigned char *) end_of_record, (unsigned char *) ptr, | 
					
						
							|  |  |  | 		replacement, sizeof(replacement) - 1); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 09:25:58 -06:00
										 |  |  | 	naptr_len = sizeof(*naptr) + size + flags_size + 1 + services_size + 1 | 
					
						
							|  |  |  | 		+ regexp_size + 1 + strlen(replacement) + 1; | 
					
						
							|  |  |  | 	naptr = ast_calloc(1, naptr_len); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-25 12:32:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | const char *ast_dns_naptr_get_flags(const struct ast_dns_record *record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-20 09:19:18 +11:00
										 |  |  | 	ast_assert(ast_dns_record_get_rr_type(record) == T_NAPTR); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	return naptr->flags; | 
					
						
							| 
									
										
										
										
											2015-03-25 12:32:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const char *ast_dns_naptr_get_service(const struct ast_dns_record *record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-20 09:19:18 +11:00
										 |  |  | 	ast_assert(ast_dns_record_get_rr_type(record) == T_NAPTR); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	return naptr->service; | 
					
						
							| 
									
										
										
										
											2015-03-25 12:32:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const char *ast_dns_naptr_get_regexp(const struct ast_dns_record *record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-20 09:19:18 +11:00
										 |  |  | 	ast_assert(ast_dns_record_get_rr_type(record) == T_NAPTR); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	return naptr->regexp; | 
					
						
							| 
									
										
										
										
											2015-03-25 12:32:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const char *ast_dns_naptr_get_replacement(const struct ast_dns_record *record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-20 09:19:18 +11:00
										 |  |  | 	ast_assert(ast_dns_record_get_rr_type(record) == T_NAPTR); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	return naptr->replacement; | 
					
						
							| 
									
										
										
										
											2015-03-25 12:32:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | unsigned short ast_dns_naptr_get_order(const struct ast_dns_record *record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-20 09:19:18 +11:00
										 |  |  | 	ast_assert(ast_dns_record_get_rr_type(record) == T_NAPTR); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	return naptr->order; | 
					
						
							| 
									
										
										
										
											2015-03-25 12:32:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | unsigned short ast_dns_naptr_get_preference(const struct ast_dns_record *record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-20 09:19:18 +11:00
										 |  |  | 	ast_assert(ast_dns_record_get_rr_type(record) == T_NAPTR); | 
					
						
							| 
									
										
										
										
											2015-04-06 17:05:47 +00:00
										 |  |  | 	return naptr->preference; | 
					
						
							|  |  |  | } |