From 6a3d540199393f21de0f8db8fd7af40f6fc6f6a4 Mon Sep 17 00:00:00 2001 From: Sean Bright Date: Wed, 23 Aug 2017 12:01:01 -0400 Subject: [PATCH] enum: Handle optional NAPTR regular expression flags ASTERISK-26711 #close Reported by: Vitold Change-Id: I4ec0cc68a46559ada9d36a81b2d2959262df5f02 --- main/enum.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 26 deletions(-) diff --git a/main/enum.c b/main/enum.c index bae1299..79dc625 100644 --- a/main/enum.c +++ b/main/enum.c @@ -394,6 +394,88 @@ static unsigned int parse_ie(char *data, unsigned int maxdatalen, unsigned char return olen + 1; } +static int parse_naptr_regexp(char *regexp, char **pattern, char **subst, int *cflags) +{ + size_t len = strlen(regexp); + char delim, *extra; + int delims[3] = { 0 }, escaped = 0; + size_t delim_count = 1, i; + + /* Make sure everything is set upon return */ + *pattern = *subst = NULL; + *cflags = 0; + + if (len < 7) { + ast_log(LOG_WARNING, "The substitution expression is too short to be meaningful\n"); + return -1; + } + + /* The first character is the delimiter */ + delim = regexp[0]; + + /* Digits and the escape character are explicitly invalid */ + if (isdigit(delim) || delim == '\\') { + ast_log(LOG_ERROR, "Invalid delimiter \"%c\" specified for substitution expression\n", delim); + return -1; + } + + for (i = 1; i < len; i++) { + char c = regexp[i]; + + if (escaped || c == '\\') { + escaped = !escaped; + continue; + } + + if (c == delim) { + if (delim_count >= 3) { + ast_log(LOG_ERROR, "Found too many delimiters in substitution expression: %s\n", regexp); + return -1; + } + delims[delim_count++] = i; + } + } + + if (delim_count != 3) { + ast_log(LOG_ERROR, "Expected 3 delimiters in substitution expression but only found %zu\n", delim_count); + return -1; + } + + if (delims[1] - delims[0] < 2) { + ast_log(LOG_ERROR, "Found empty match expression in substitution expression: %s\n", regexp); + return -1; + } + + /* Note: RFC2915 requires a replacement expression, but RFC3402 does not, so we defer + * to RFC3402 and allow empty replacement expressions here. */ + + /* Handle trailing data */ + extra = ®exp[delims[2] + 1]; + + switch (strlen(extra)) { + case 0: + break; + case 1: + /* Anything other than 'i' is invalid */ + if (*extra == 'i') { + *cflags = REG_ICASE; + break; + } + default: + /* Fallthrough */ + ast_log(LOG_ERROR, "Trailing junk \"%s\" following substitution expression: %s\n", extra, regexp); + return -1; + } + + /* Zero terminate our substrings */ + regexp[delims[1]] = regexp[delims[2]] = '\0'; + + *pattern = ®exp[1]; + *subst = ®exp[delims[1] + 1]; + + return 0; +} + /*! \brief Parse DNS NAPTR record used in ENUM ---*/ static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, unsigned char *naptrinput) { @@ -406,16 +488,15 @@ static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize char repl[512] = ""; char tempdst[512] = ""; char errbuff[512] = ""; - char delim; - char *delim2; char *pattern, *subst, *d; int res; - int regexp_len, rc; + int rc; static const int max_bt = 10; /* max num of regexp backreference allowed, must remain 10 to guarantee a valid backreference index */ int size, matchindex; /* size is the size of the backreference sub. */ size_t d_len = sizeof(tempdst) - 1; regex_t preg; regmatch_t pmatch[max_bt]; + int cflags = 0; tech_return[0] = '\0'; dst[0] = '\0'; @@ -486,33 +567,15 @@ static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize } } - regexp_len = strlen(regexp); - if (regexp_len < 7) { - ast_log(LOG_WARNING, "Regex too short to be meaningful.\n"); + if (parse_naptr_regexp(regexp, &pattern, &subst, &cflags)) { return -1; } - /* this takes the first character of the regexp (which is a delimiter) - * and uses that character to find the index of the second delimiter */ - delim = regexp[0]; - delim2 = strchr(regexp + 1, delim); - if ((delim2 == NULL) || (regexp[regexp_len - 1] != delim)) { /* is the second delimiter found, and is the end of the regexp a delimiter */ - ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n", regexp); - return -1; - } else if (strchr((delim2 + 1), delim) == NULL) { /* if the second delimiter is found, make sure there is a third instance. this could be the end one instead of the middle */ - ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n", regexp); - return -1; - } - pattern = regexp + 1; /* pattern is the regex without the begining and ending delimiter */ - *delim2 = 0; /* zero out the middle delimiter */ - subst = delim2 + 1; /* dst substring is everything after the second delimiter. */ - regexp[regexp_len - 1] = 0; /* zero out the last delimiter */ - -/* - * now do the regex wizardry. - */ + /* + * now do the regex wizardry. + */ - if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) { + if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE | cflags)) { ast_log(LOG_WARNING, "NAPTR Regex compilation error (regex = \"%s\").\n", regexp); return -1; } -- 2.7.4