/*! \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) { char tech_return[80]; char *oanswer = (char *)answer; char flags[512] = ""; char services[512] = ""; char *p; char regexp[512] = ""; char repl[512] = ""; char tempdst[512] = ""; char errbuff[512] = ""; char delim; char *delim2; char *pattern, *subst, *d; int res; int regexp_len, 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]; tech_return[0] = '\0'; dst[0] = '\0'; if (len < sizeof(struct naptr)) { ast_log(LOG_WARNING, "NAPTR record length too short\n"); return -1; } answer += sizeof(struct naptr); len -= sizeof(struct naptr); if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get flags from NAPTR record\n"); return -1; } else { answer += res; len -= res; } if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get services from NAPTR record\n"); return -1; } else { answer += res; len -= res; } if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get regexp from NAPTR record\n"); return -1; } else { answer += res; len -= res; } if ((res = dn_expand((unsigned char *)oanswer, (unsigned char *)answer + len, (unsigned char *)answer, repl, sizeof(repl) - 1)) < 0) { ast_log(LOG_WARNING, "Failed to expand hostname\n"); return -1; } ast_debug(3, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n", naptrinput, flags, services, regexp, repl); if (tolower(flags[0]) != 'u') { ast_log(LOG_WARNING, "NAPTR Flag must be 'U' or 'u'.\n"); return -1; } p = strstr(services, "e2u+"); if (p == NULL) p = strstr(services, "E2U+"); if (p){ p = p + 4; if (strchr(p, ':')){ p = strchr(p, ':') + 1; } ast_copy_string(tech_return, p, sizeof(tech_return)); } else { p = strstr(services, "+e2u"); if (p == NULL) p = strstr(services, "+E2U"); if (p) { *p = 0; p = strchr(services, ':'); if (p) *p = 0; ast_copy_string(tech_return, services, sizeof(tech_return)); } } regexp_len = strlen(regexp); if (regexp_len < 7) { ast_log(LOG_WARNING, "Regex too short to be meaningful.\n"); 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. */ if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) { ast_log(LOG_WARNING, "NAPTR Regex compilation error (regex = \"%s\").\n", regexp); return -1; } if (preg.re_nsub > ARRAY_LEN(pmatch)) { ast_log(LOG_WARNING, "NAPTR Regex compilation error: too many subs.\n"); regfree(&preg); return -1; } /* pmatch is an array containing the substring indexes for the regex backreference sub. * max_bt is the maximum number of backreferences allowed to be stored in pmatch */ if ((rc = regexec(&preg, (char *) naptrinput, max_bt, pmatch, 0))) { regerror(rc, &preg, errbuff, sizeof(errbuff)); ast_log(LOG_WARNING, "NAPTR Regex match failed. Reason: %s\n", errbuff); regfree(&preg); return -1; } regfree(&preg); d = tempdst; d_len--; /* perform the backreference sub. Search the subst for backreferences, * when a backreference is found, retrieve the backreferences number. * use the backreference number as an index for pmatch to retrieve the * beginning and ending indexes of the substring to insert as the backreference. * if no backreference is found, continue copying the subst into tempdst */ while (*subst && (d_len > 0)) { if ((subst[0] == '\\') && isdigit(subst[1])) { /* is this character the beginning of a backreference */ matchindex = (int) (subst[1] - '0'); if (matchindex >= ARRAY_LEN(pmatch)) { ast_log(LOG_WARNING, "Error during regex substitution. Invalid pmatch index.\n"); return -1; } /* pmatch len is 10. we are garanteed a single char 0-9 is a valid index */ size = pmatch[matchindex].rm_eo - pmatch[matchindex].rm_so; if (size > d_len) { ast_log(LOG_WARNING, "Not enough space during NAPTR regex substitution.\n"); return -1; } /* are the pmatch indexes valid for the input length */ if ((strlen((char *) naptrinput) >= pmatch[matchindex].rm_eo) && (pmatch[matchindex].rm_so <= pmatch[matchindex].rm_eo)) { memcpy(d, (naptrinput + (int) pmatch[matchindex].rm_so), size); /* copy input substring into backreference marker */ d_len -= size; subst += 2; /* skip over backreference characters to next valid character */ d += size; } else { ast_log(LOG_WARNING, "Error during regex substitution. Invalid backreference index.\n"); return -1; } } else if (isprint(*subst)) { *d++ = *subst++; d_len--; } else { ast_log(LOG_WARNING, "Error during regex substitution.\n"); return -1; } } *d = 0; ast_copy_string((char *) dst, tempdst, dstsize); dst[dstsize - 1] = '\0'; if (*tech != '\0'){ /* check if it is requested NAPTR */ if (!strncasecmp(tech, "ALL", techsize)){ return 0; /* return or count any RR */ } if (!strncasecmp(tech_return, tech, sizeof(tech_return) < techsize ? sizeof(tech_return): techsize)){ ast_copy_string(tech, tech_return, techsize); return 0; /* we got our RR */ } else { /* go to the next RR in the DNS answer */ return 1; } } /* tech was not specified, return first parsed RR */ ast_copy_string(tech, tech_return, techsize); return 0; }
/*--- parse_naptr: 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) { char tech_return[80]; char *oanswer = answer; char flags[512] = ""; char services[512] = ""; char *p; char regexp[512] = ""; char repl[512] = ""; char temp[512] = ""; char delim; char *delim2; char *pattern, *subst, *d; int res; int regexp_len, size, backref; int d_len = sizeof(temp) - 1; regex_t preg; regmatch_t pmatch[9]; tech_return[0] = '\0'; dst[0] = '\0'; if (len < sizeof(struct naptr)) { ast_log(LOG_WARNING, "NAPTR record length too short\n"); return -1; } answer += sizeof(struct naptr); len -= sizeof(struct naptr); if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get flags from NAPTR record\n"); return -1; } else { answer += res; len -= res; } if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get services from NAPTR record\n"); return -1; } else { answer += res; len -= res; } if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0) { ast_log(LOG_WARNING, "Failed to get regexp from NAPTR record\n"); return -1; } else { answer += res; len -= res; } if ((res = dn_expand((unsigned char *)oanswer, (unsigned char *)answer + len, (unsigned char *)answer, repl, sizeof(repl) - 1)) < 0) { ast_log(LOG_WARNING, "Failed to expand hostname\n"); return -1; } if (option_debug > 2) /* Advanced NAPTR debugging */ ast_log(LOG_DEBUG, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n", naptrinput, flags, services, regexp, repl); if (tolower(flags[0]) != 'u') { ast_log(LOG_WARNING, "NAPTR Flag must be 'U' or 'u'.\n"); return -1; } p = strstr(services, "e2u+"); if (p == NULL) p = strstr(services, "E2U+"); if (p){ p = p + 4; if (strchr(p, ':')){ p = strchr(p, ':') + 1; } ast_copy_string(tech_return, p, sizeof(tech_return)); } else { p = strstr(services, "+e2u"); if (p == NULL) p = strstr(services, "+E2U"); if (p) { *p = 0; p = strchr(services, ':'); if (p) *p = 0; ast_copy_string(tech_return, services, sizeof(tech_return)); } } /* DEDBUGGING STUB ast_copy_string(regexp, "!^\\+43(.*)$!\\[email protected]!", sizeof(regexp) - 1); */ regexp_len = strlen(regexp); if (regexp_len < 7) { ast_log(LOG_WARNING, "Regex too short to be meaningful.\n"); return -1; } delim = regexp[0]; delim2 = strchr(regexp + 1, delim); if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) { ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp); return -1; } pattern = regexp + 1; *delim2 = 0; subst = delim2 + 1; regexp[regexp_len-1] = 0; /* * now do the regex wizardry. */ if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) { ast_log(LOG_WARNING, "NAPTR Regex compilation error (regex = \"%s\").\n",regexp); return -1; } if (preg.re_nsub > 9) { ast_log(LOG_WARNING, "NAPTR Regex compilation error: too many subs.\n"); regfree(&preg); return -1; } if (regexec(&preg, naptrinput, 9, pmatch, 0)) { ast_log(LOG_WARNING, "NAPTR Regex match failed.\n"); regfree(&preg); return -1; } regfree(&preg); d = temp; d_len--; while (*subst && (d_len > 0)) { if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) { backref = subst[1]-'0'; size = pmatch[backref].rm_eo - pmatch[backref].rm_so; if (size > d_len) { ast_log(LOG_WARNING, "Not enough space during NAPTR regex substitution.\n"); return -1; } memcpy(d, naptrinput + pmatch[backref].rm_so, size); d += size; d_len -= size; subst += 2; } else if (isprint(*subst)) { *d++ = *subst++; d_len--; } else { ast_log(LOG_WARNING, "Error during regex substitution.\n"); return -1; } } *d = 0; ast_copy_string(dst, temp, dstsize); dst[dstsize - 1] = '\0'; if (*tech != '\0'){ /* check if it is requested NAPTR */ if (!strncasecmp(tech, "ALL", techsize)){ return 1; /* return or count any RR */ } if (!strncasecmp(tech_return, tech, sizeof(tech_return)<techsize?sizeof(tech_return):techsize)){ ast_copy_string(tech, tech_return, techsize); return 1; /* we got out RR */ } else { /* go to the next RR in the DNS answer */ return 0; } } /* tech was not specified, return first parsed RR */ ast_copy_string(tech, tech_return, techsize); return 1; }