* - ENUM SIP: http://www.ietf.org/rfc/rfc3764.txt
* - IANA ENUM Services: http://www.iana.org/assignments/enum-services
*
+ * - I-ENUM:
+ * http://tools.ietf.org/wg/enum/draft-ietf-enum-combined/
+ * http://tools.ietf.org/wg/enum/draft-ietf-enum-branch-location-record/
+ *
* \par Possible improvement
* \todo Implement a caching mechanism for multile enum lookups
* - See http://bugs.digium.com/view.php?id=6739
+ * \todo The service type selection needs to be redone.
*/
#include "asterisk.h"
#define T_TXT 16
#endif
-#define TOPLEV "e164.arpa." /*!< The IETF Enum standard root, managed by the ITU */
+static char ienum_branchlabel[32] = "i";
+/* how to do infrastructure enum branch location resolution? */
+#define ENUMLOOKUP_BLR_CC 0
+#define ENUMLOOKUP_BLR_TXT 1
+#define ENUMLOOKUP_BLR_EBL 2
+static int ebl_alg = ENUMLOOKUP_BLR_CC;
+
+/* EBL record provisional type code */
+#define T_EBL 65300
-/* Linked list from config file */
-static struct enum_search {
- char toplev[512];
- struct enum_search *next;
-} *toplevs;
+AST_MUTEX_DEFINE_STATIC(enumlock);
-static int enumver;
+/*! \brief Determine the length of a country code when given an E.164 string */
+/*
+ * Input: E.164 number w/o leading +
+ *
+ * Output: number of digits in the country code
+ * 0 on invalid number
+ *
+ * Algorithm:
+ * 3 digits is the default length of a country code.
+ * country codes 1 and 7 are a single digit.
+ * the following country codes are two digits: 20, 27, 30-34, 36, 39,
+ * 40, 41, 43-49, 51-58, 60-66, 81, 82, 84, 86, 90-95, 98.
+ */
+static int cclen(const char *number)
+{
+ int cc;
+ char digits[3] = "";
-AST_MUTEX_DEFINE_STATIC(enumlock);
+ if (!number || (strlen(number) < 3)) {
+ return 0;
+ }
+
+ strncpy(digits, number, 2);
+
+ if (!sscanf(digits, "%d", &cc)) {
+ return 0;
+ }
+
+ if (cc / 10 == 1 || cc / 10 == 7)
+ return 1;
+
+ if (cc == 20 || cc == 27 || (cc >= 30 && cc <= 34) || cc == 36 ||
+ cc == 39 || cc == 40 || cc == 41 || (cc >= 40 && cc <= 41) ||
+ (cc >= 43 && cc <= 49) || (cc >= 51 && cc <= 58) ||
+ (cc >= 60 && cc <= 66) || cc == 81 || cc == 82 || cc == 84 ||
+ cc == 86 || (cc >= 90 && cc <= 95) || cc == 98) {
+ return 2;
+ }
+
+ return 3;
+}
+
+struct txt_context {
+ char txt[1024]; /* TXT record in TXT lookup */
+ int txtlen; /* Length */
+};
+
+/*! \brief Callback for TXT record lookup, /ol version */
+static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
+{
+ struct txt_context *c = context;
+ unsigned int i;
+
+ c->txt[0] = 0; /* default to empty */
+ c->txtlen = 0;
+
+ if (answer == NULL) {
+ return 0;
+ }
+
+ /* RFC1035:
+ *
+ * <character-string> is a single length octet followed by that number of characters.
+ * TXT-DATA One or more <character-string>s.
+ *
+ * We only take the first string here.
+ */
+
+ i = *answer++;
+ len -= 1;
+
+ if (i > len) { /* illegal packet */
+ ast_log(LOG_WARNING, "txt_callback: malformed TXT record.\n");
+ return 0;
+ }
+
+ if (i >= sizeof(c->txt)) { /* too long? */
+ ast_log(LOG_WARNING, "txt_callback: TXT record too long.\n");
+ i = sizeof(c->txt) - 1;
+ }
+
+ ast_copy_string(c->txt, (char *)answer, i + 1); /* this handles the \0 termination */
+ c->txtlen = i;
+
+ return 1;
+}
+
+/*! \brief Determine the branch location record as stored in a TXT record */
+/*
+ * Input: CC code
+ *
+ * Output: number of digits in the number before the i-enum branch
+ *
+ * Algorithm: Build <ienum_branchlabel>.c.c.<suffix> and look for a TXT lookup.
+ * Return atoi(TXT-record).
+ * Return -1 on not found.
+ *
+ */
+static int blr_txt(const char *cc, const char *suffix)
+{
+ struct txt_context context;
+ char domain[128] = "";
+ char *p1, *p2;
+ int ret;
+
+ ast_mutex_lock(&enumlock);
+
+ ast_verb(4, "blr_txt() cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel);
+
+ if (sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) {
+ ast_mutex_unlock(&enumlock);
+ ast_log(LOG_WARNING, "ERROR: string sizing in blr_txt.\n");
+ return -1;
+ }
+
+ p1 = domain + snprintf(domain, sizeof(domain), "%s.", ienum_branchlabel);
+ ast_mutex_unlock(&enumlock);
+
+ for (p2 = (char *) cc + strlen(cc) - 1; p2 >= cc; p2--) {
+ if (isdigit(*p2)) {
+ *p1++ = *p2;
+ *p1++ = '.';
+ }
+ }
+ strcat(p1, suffix);
+
+ ast_verb(4, "blr_txt() FQDN for TXT record: %s, cc was %s\n", domain, cc);
+
+ ret = ast_search_dns(&context, domain, C_IN, T_TXT, txt_callback);
+
+ if (ret > 0) {
+ ret = atoi(context.txt);
+
+ if ((ret >= 0) && (ret < 20)) {
+ ast_verb(3, "blr_txt() BLR TXT record for %s is %d (apex: %s)\n", cc, ret, suffix);
+ return ret;
+ }
+ }
+
+ ast_verb(3, "blr_txt() BLR TXT record for %s not found (apex: %s)\n", cc, suffix);
+
+ return -1;
+}
+
+struct ebl_context {
+ unsigned char pos;
+ char separator[256]; /* label to insert */
+ int sep_len; /* Length */
+ char apex[256]; /* new Apex */
+ int apex_len; /* Length */
+};
+
+/*! \brief Callback for EBL record lookup */
+static int ebl_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
+{
+ struct ebl_context *c = context;
+ unsigned int i;
+
+ c->pos = 0; /* default to empty */
+ c->separator[0] = 0;
+ c->sep_len = 0;
+ c->apex[0] = 0;
+ c->apex_len = 0;
+
+ if (answer == NULL) {
+ return 0;
+ }
+
+ /* draft-lendl-enum-branch-location-record-00
+ *
+ * 0 1 2 3 4 5 6 7
+ * +--+--+--+--+--+--+--+--+
+ * | POSITION |
+ * +--+--+--+--+--+--+--+--+
+ * / SEPARATOR /
+ * +--+--+--+--+--+--+--+--+
+ * / APEX /
+ * +--+--+--+--+--+--+--+--+
+ *
+ * where POSITION is a single byte, SEPARATOR is a <character-string>
+ * and APEX is a <domain-name>.
+ *
+ */
+
+ c->pos = *answer++;
+ len -= 1;
+
+ if ((c->pos > 15) || len < 2) { /* illegal packet */
+ ast_log(LOG_WARNING, "ebl_callback: malformed EBL record.\n");
+ return 0;
+ }
+
+ i = *answer++;
+ len -= 1;
+ if (i > len) { /* illegal packet */
+ ast_log(LOG_WARNING, "ebl_callback: malformed EBL record.\n");
+ return 0;
+ }
+
+ ast_copy_string(c->separator, (char *)answer, i + 1);
+ c->sep_len = i;
+
+ answer += i;
+ len -= i;
+
+ if ((i = dn_expand((unsigned char *)fullanswer, (unsigned char *)answer + len,
+ (unsigned char *)answer, c->apex, sizeof(c->apex) - 1)) < 0) {
+ ast_log(LOG_WARNING, "Failed to expand hostname\n");
+ return 0;
+ }
+ c->apex[i] = 0;
+ c->apex_len = i;
+
+ return 1;
+}
+
+/*! \brief Evaluate the I-ENUM branch as stored in an EBL record */
+/*
+ * Input: CC code
+ *
+ * Output: number of digits in the number before the i-enum branch
+ *
+ * Algorithm: Build <ienum_branchlabel>.c.c.<suffix> and look for an EBL record
+ * Return pos and fill in separator and apex.
+ * Return -1 on not found.
+ *
+ */
+static int blr_ebl(const char *cc, const char *suffix, char *separator, int sep_len, char* apex, int apex_len)
+{
+ struct ebl_context context;
+ char domain[128] = "";
+ char *p1,*p2;
+ int ret;
+
+ ast_mutex_lock(&enumlock);
+
+ ast_verb(4, "blr_ebl() cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel);
+
+ if (sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) {
+ ast_mutex_unlock(&enumlock);
+ ast_log(LOG_WARNING, "ERROR: string sizing in blr_EBL.\n");
+ return -1;
+ }
+
+ p1 = domain + snprintf(domain, sizeof(domain), "%s.", ienum_branchlabel);
+ ast_mutex_unlock(&enumlock);
+
+ for (p2 = (char *) cc + strlen(cc) - 1; p2 >= cc; p2--) {
+ if (isdigit(*p2)) {
+ *p1++ = *p2;
+ *p1++ = '.';
+ }
+ }
+ strcat(p1, suffix);
+
+ ast_verb(4, "blr_ebl() FQDN for EBL record: %s, cc was %s\n", domain, cc);
+
+ ret = ast_search_dns(&context, domain, C_IN, T_EBL, ebl_callback);
+ if (ret > 0) {
+ ret = context.pos;
+
+ if ((ret >= 0) && (ret < 20)) {
+ ast_verb(3, "blr_txt() BLR EBL record for %s is %d/%s/%s)\n", cc, ret, context.separator, context.apex);
+ ast_copy_string(separator, context.separator, sep_len);
+ ast_copy_string(apex, context.apex, apex_len);
+ return ret;
+ }
+ }
+ ast_verb(3, "blr_txt() BLR EBL record for %s not found (apex: %s)\n", cc, suffix);
+ return -1;
+}
/*! \brief Parse NAPTR record information elements */
static unsigned int parse_ie(char *data, unsigned int maxdatalen, unsigned char *src, unsigned int srclen)
}
/*! \brief Parse DNS NAPTR record used in ENUM ---*/
-static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, char *naptrinput)
+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];
- unsigned char *oanswer = answer;
+ char *oanswer = (char *)answer;
char flags[512] = "";
char services[512] = "";
char *p;
char regexp[512] = "";
char repl[512] = "";
char temp[512] = "";
+ char errbuff[512] = "";
char delim;
char *delim2;
- char *pattern, *subst, *d;
+ char *pattern, *subst, *d, *number;
int res;
- int regexp_len, size, backref;
+ int regexp_len, rc;
+ int size;
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)) {
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;
len -= res;
}
- if ((res = dn_expand(oanswer, answer + len, answer, repl, sizeof(repl) - 1)) < 0) {
+ 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;
regfree(&preg);
return -1;
}
-
- if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
- ast_log(LOG_WARNING, "NAPTR Regex match failed.\n");
+
+ if (0 != (rc = regexec(&preg, (char *)naptrinput, 0, 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 = temp;
+
+ number = (char *)(naptrinput + (*naptrinput == '+'));
+
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 ((subst[0] == '\\' && isdigit(subst[1]))) {
+ size = strlen(number);
+ //ast_log(LOG_WARNING, "size:%d: offset:%s: temp:%s:\n",size,offset,temp);
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;
+ }
+ memcpy(d, number, size);
d_len -= size;
subst += 2;
+ d += size;
+ //ast_log(LOG_WARNING, "after dlen:%d: temp:%s:\n",d_len,temp);
} else if (isprint(*subst)) {
*d++ = *subst++;
d_len--;
}
}
*d = 0;
- ast_copy_string(dst, temp, dstsize);
+ ast_copy_string((char *)dst, temp,dstsize);
dst[dstsize - 1] = '\0';
+// ast_log(LOG_WARNING, "after dst:%s: temp:%s:\n",dst,temp);
if (*tech != '\0'){ /* check if it is requested NAPTR */
if (!strncasecmp(tech, "ALL", techsize)){
- return 1; /* return or count any RR */
+ return 0; /* return or count any RR */
}
- if (!strncasecmp(tech_return, tech, sizeof(tech_return)<techsize?sizeof(tech_return):techsize)){
+ 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 */
+ return 0; /* we got our RR */
} else { /* go to the next RR in the DNS answer */
- return 0;
+ return 1;
}
}
/* tech was not specified, return first parsed RR */
ast_copy_string(tech, tech_return, techsize);
- return 1;
+ return 0;
}
/* do not return requested value, just count RRs and return thei number in dst */
#define ENUMLOOKUP_OPTIONS_COUNT 1
-
-
-/*! \brief Callback for TXT record lookup */
-static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
-{
- struct enum_context *c = (struct enum_context *)context;
-
- if (answer == NULL) {
- c->txt = NULL;
- c->txtlen = 0;
- return 0;
- }
-
- /* skip over first byte, as for some reason it's a vertical tab character */
- answer += 1;
- len -= 1;
-
- /* answer is not null-terminated, but should be */
- /* this is safe to do, as answer has extra bytes on the end we can
- * safely overwrite with a null */
- answer[len] = '\0';
- /* now increment len so that len includes the null, so that we can
- * compare apples to apples */
- len +=1;
-
- /* finally, copy the answer into c->txt */
- ast_copy_string(c->txt, (const char *) answer, len < c->txtlen ? len : (c->txtlen));
-
- /* just to be safe, let's make sure c->txt is null terminated */
- c->txt[(c->txtlen) - 1] = '\0';
-
- return 1;
-}
+/* do an ISN style lookup */
+#define ENUMLOOKUP_OPTIONS_ISN 2
+/* do a infrastructure ENUM lookup */
+#define ENUMLOOKUP_OPTIONS_IENUM 4
+/* do a direct DNS lookup: no reversal */
+#define ENUMLOOKUP_OPTIONS_DIRECT 8
/*! \brief Callback from ENUM lookup function */
static int enum_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
void *p = NULL;
int res;
- res = parse_naptr(c->dst, c->dstlen, c->tech, c->techlen, answer, len, c->naptrinput);
+ res = parse_naptr((unsigned char *)c->dst, c->dstlen, c->tech, c->techlen, answer, len, (unsigned char *)c->naptrinput);
if (res < 0) {
- ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
+ ast_log(LOG_WARNING, "Failed to parse naptr\n");
return -1;
- } else if (res > 0 && !ast_strlen_zero(c->dst)){ /* ok, we got needed NAPTR */
- if (c->options & ENUMLOOKUP_OPTIONS_COUNT){ /* counting RRs */
- c->position++;
- snprintf(c->dst, c->dstlen, "%d", c->position);
+ } else if ((res == 0) && !ast_strlen_zero(c->dst)) { /* ok, we got needed NAPTR */
+ if (c->options & ENUMLOOKUP_OPTIONS_COUNT) { /* counting RRs */
+ c->count++;
+ snprintf(c->dst, c->dstlen, "%d", c->count);
} else {
if ((p = ast_realloc(c->naptr_rrs, sizeof(*c->naptr_rrs) * (c->naptr_rrs_count + 1)))) {
c->naptr_rrs = p;
return 0;
}
- if (c->options & ENUMLOOKUP_OPTIONS_COUNT) { /* counting RRs */
- snprintf(c->dst, c->dstlen, "%d", c->position);
- }
-
return 0;
}
int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options, unsigned int record, struct enum_context **argcontext)
{
struct enum_context *context;
- char tmp[259 + 512];
- char naptrinput[512];
- int pos = strlen(number) - 1;
- int newpos = 0;
+ char tmp[512];
+ char domain[256];
+ char left[128];
+ char middle[128];
+ char naptrinput[128];
+ char apex[128] = "";
int ret = -1;
- struct enum_search *s = NULL;
- int version = -1;
/* for ISN rewrite */
char *p1 = NULL;
char *p2 = NULL;
+ char *p3 = NULL;
int k = 0;
int i = 0;
int z = 0;
+ int spaceleft = 0;
+ struct timeval time_start, time_end;
+
+ if (ast_strlen_zero(suffix)) {
+ ast_log(LOG_WARNING, "ast_get_enum need a suffix parameter now.\n");
+ return -1;
+ }
+
+ ast_verb(2, "ast_get_enum(num='%s', tech='%s', suffix='%s', options='%s', record=%d\n", number, tech, suffix, options, record);
+
+/*
+ We don't need that any more, that "n" preceding the number has been replaced by a flag
+ in the options paramter.
+ ast_copy_string(naptrinput, number, sizeof(naptrinput));
+*/
+/*
+ * The "number" parameter includes a leading '+' if it's a full E.164 number (and not ISN)
+ * We need to preserve that as the regex inside NAPTRs expect the +.
+ *
+ * But for the domain generation, the '+' is a nuissance, so we get rid of it.
+*/
+ ast_copy_string(naptrinput, number[0] == 'n' ? number + 1 : number, sizeof(naptrinput));
+ if (number[0] == '+') {
+ number++;
+ }
if (!(context = ast_calloc(1, sizeof(*context))))
return -1;
- ast_copy_string(naptrinput, number[0] == 'n' ? number + 1 : number, sizeof(naptrinput));
+ if((p3 = strchr(naptrinput, '*'))) {
+ *p3='\0';
+ }
context->naptrinput = naptrinput; /* The number */
context->dst = dst; /* Return string */
context->tech = tech;
context->techlen = techlen;
context->options = 0;
- context->position = record;
+ context->position = record > 0 ? record : 1;
+ context->count = 0;
context->naptr_rrs = NULL;
context->naptr_rrs_count = 0;
+ /*
+ * Process options:
+ *
+ * c Return count, not URI
+ * i Use infrastructure ENUM
+ * s Do ISN transformation
+ * d Direct DNS query: no reversing.
+ *
+ */
if (options != NULL) {
- if (*options == 'c') {
- context->options = ENUMLOOKUP_OPTIONS_COUNT;
- context->position = 0;
+ if (strchr(options,'s')) {
+ context->options |= ENUMLOOKUP_OPTIONS_ISN;
+ } else if (strchr(options,'i')) {
+ context->options |= ENUMLOOKUP_OPTIONS_IENUM;
+ } else if (strchr(options,'d')) {
+ context->options |= ENUMLOOKUP_OPTIONS_DIRECT;
+ }
+ if (strchr(options,'c')) {
+ context->options |= ENUMLOOKUP_OPTIONS_COUNT;
+ }
+ if (strchr(number,'*')) {
+ context->options |= ENUMLOOKUP_OPTIONS_ISN;
}
}
-
+ ast_verb(2, "ENUM options(%s): pos=%d, options='%d'\n", options, context->position, context->options);
ast_debug(1, "ast_get_enum(): n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n",
number, tech, suffix, context->options, context->position);
- if (pos > 128)
- pos = 128;
-
+ /*
+ * This code does more than simple RFC3261 ENUM. All these rewriting
+ * schemes have in common that they build the FQDN for the NAPTR lookup
+ * by concatenating
+ * - a number which needs be flipped and "."-seperated (left)
+ * - some fixed string (middle)
+ * - an Apex. (apex)
+ *
+ * The RFC3261 ENUM is: left=full number, middle="", apex=from args.
+ * ISN: number = "middle*left", apex=from args
+ * I-ENUM: EBL parameters build the split, can change apex
+ * Direct: left="", middle=argument, apex=from args
+ *
+ */
+
+ /* default: the whole number will be flipped, no middle domain component */
+ ast_copy_string(left, number, sizeof(left));
+ middle[0] = '\0';
+ /*
+ * I-ENUM can change the apex, thus we copy it
+ */
+ ast_copy_string(apex, suffix, sizeof(apex));
/* ISN rewrite */
- p1 = strchr(number, '*');
+ if ((context->options & ENUMLOOKUP_OPTIONS_ISN) && (p1 = strchr(number, '*'))) {
+ *p1++ = '\0';
+ ast_copy_string(left, number, sizeof(left));
+ ast_copy_string(middle, p1, sizeof(middle) - 1);
+ strcat(middle, ".");
+
+ ast_verb(2, "ISN ENUM: left=%s, middle='%s'\n", left, middle);
+ /* Direct DNS lookup rewrite */
+ } else if (context->options & ENUMLOOKUP_OPTIONS_DIRECT) {
+ left[0] = 0; /* nothing to flip around */
+ ast_copy_string(middle, number, sizeof(middle) - 1);
+ strcat(middle, ".");
+
+ ast_verb(2, "DIRECT ENUM: middle='%s'\n", middle);
+ /* Infrastructure ENUM rewrite */
+ } else if (context->options & ENUMLOOKUP_OPTIONS_IENUM) {
+ int sdl = 0;
+ char cc[8];
+ char sep[256], n_apex[256];
+ int cc_len = cclen(number);
+ sdl = cc_len;
+ ast_mutex_lock(&enumlock);
+ ast_copy_string(sep, ienum_branchlabel, sizeof(sep)); /* default */
+ ast_mutex_unlock(&enumlock);
- if (number[0] == 'n') { /* do not perform ISN rewrite ('n' is testing flag) */
- p1 = NULL;
- k = 1; /* strip 'n' from number */
- }
+ switch (ebl_alg) {
+ case ENUMLOOKUP_BLR_EBL:
+ ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */
+ sdl = blr_ebl(cc, suffix, sep, sizeof(sep) - 1, n_apex, sizeof(n_apex) - 1);
- if (p1 != NULL) {
- p2 = p1 + 1;
- while (p1 > number){
- p1--;
- tmp[newpos++] = *p1;
- tmp[newpos++] = '.';
- }
- if (*p2) {
- while (*p2 && newpos < 128){
- tmp[newpos++] = *p2;
- p2++;
+ if (sdl >= 0) {
+ ast_copy_string(apex, n_apex, sizeof(apex));
+ ast_verb(2, "EBL ENUM: sep=%s, apex='%s'\n", sep, n_apex);
+ } else {
+ sdl = cc_len;
}
- tmp[newpos++] = '.';
+ break;
+ case ENUMLOOKUP_BLR_TXT:
+ ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */
+ sdl = blr_txt(cc, suffix);
+
+ if (sdl < 0)
+ sdl = cc_len;
+ break;
+
+ case ENUMLOOKUP_BLR_CC: /* BLR is at the country-code level */
+ default:
+ sdl = cc_len;
+ break;
}
- } else {
- while (pos >= k) {
- if (isdigit(number[pos])) {
- tmp[newpos++] = number[pos];
- tmp[newpos++] = '.';
+ if (sdl > strlen(number)) { /* Number too short for this sdl? */
+ ast_log(LOG_WARNING, "I-ENUM: subdomain location %d behind number %s\n", sdl, number);
+ return 0;
+ }
+ ast_copy_string(left, number + sdl, sizeof(left));
+
+ ast_mutex_lock(&enumlock);
+ ast_copy_string(middle, sep, sizeof(middle) - 1);
+ strcat(middle, ".");
+ ast_mutex_unlock(&enumlock);
+
+ /* check the space we need for middle */
+ if ((sdl * 2 + strlen(middle) + 2) > sizeof(middle)) {
+ ast_log(LOG_WARNING, "ast_get_enum: not enough space for I-ENUM rewrite.\n");
+ return -1;
+ }
+
+ p1 = middle + strlen(middle);
+ for (p2 = (char *) number + sdl - 1; p2 >= number; p2--) {
+ if (isdigit(*p2)) {
+ *p1++ = *p2;
+ *p1++ = '.';
}
- pos--;
+ }
+ *p1 = '\0';
+
+ ast_verb(2, "I-ENUM: cclen=%d, left=%s, middle='%s', apex='%s'\n", cc_len, left, middle, apex);
+ }
+
+ if (strlen(left) * 2 + 2 > sizeof(domain)) {
+ ast_log(LOG_WARNING, "string to long in ast_get_enum\n");
+ return -1;
+ }
+
+ /* flip left into domain */
+ p1 = domain;
+ for (p2 = left + strlen(left); p2 >= left; p2--) {
+ if (isdigit(*p2)) {
+ *p1++ = *p2;
+ *p1++ = '.';
}
}
+ *p1 = '\0';
if (chan && ast_autoservice_start(chan) < 0) {
ast_free(context);
return -1;
}
- if (suffix) {
- ast_copy_string(tmp + newpos, suffix, sizeof(tmp) - newpos);
- ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback);
- ast_debug(1, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret);
- } else {
- ret = -1; /* this is actually dead code since the demise of app_enum.c */
- for (;;) {
- ast_mutex_lock(&enumlock);
- if (version != enumver) {
- /* Ooh, a reload... */
- s = toplevs;
- version = enumver;
- } else {
- s = s->next;
- }
- ast_mutex_unlock(&enumlock);
+ spaceleft = sizeof(tmp) - 2;
+ ast_copy_string(tmp, domain, spaceleft);
+ spaceleft -= strlen(domain);
- if (!s)
- break;
-
- ast_copy_string(tmp + newpos, s->toplev, sizeof(tmp) - newpos);
- ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback);
- ast_debug(1, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret);
- if (ret > 0)
- break;
- }
+ if (*middle) {
+ strncat(tmp, middle, spaceleft);
+ spaceleft -= strlen(middle);
}
+ strncat(tmp,apex,spaceleft);
+ time_start = ast_tvnow();
+ ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback);
+ time_end = ast_tvnow();
+
+ ast_verb(2, "ast_get_enum() profiling: %s, %s, %d ms\n",
+ (ret == 0) ? "OK" : "FAIL", tmp, ast_tvdiff_ms(time_end, time_start));
+
if (ret < 0) {
ast_debug(1, "No such number found: %s (%s)\n", tmp, strerror(errno));
strcpy(dst, "0");
for (i = 0; i < context->naptr_rrs_count; i++) {
/* use order first and then preference to compare */
if ((ntohs(context->naptr_rrs[k].naptr.order) < ntohs(context->naptr_rrs[i].naptr.order)
- && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
- || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order)
- && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)){
+ && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
+ || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order)
+ && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
z = context->naptr_rrs[k].sort_pos;
context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
context->naptr_rrs[i].sort_pos = z;
}
if (ntohs(context->naptr_rrs[k].naptr.order) == ntohs(context->naptr_rrs[i].naptr.order)) {
if ((ntohs(context->naptr_rrs[k].naptr.pref) < ntohs(context->naptr_rrs[i].naptr.pref)
- && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
- || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref)
- && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)){
+ && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
+ || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref)
+ && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
z = context->naptr_rrs[k].sort_pos;
context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
context->naptr_rrs[i].sort_pos = z;
}
} else if (!(context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
context->dst[0] = 0;
+ } else if ((context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
+ snprintf(context->dst,context->dstlen,"%d",context->count);
}
+
if (chan)
ret |= ast_autoservice_stop(chan);
return ret;
}
-/* Get TXT record from DNS. Really has nothing to do with enum, but anyway... */
-int ast_get_txt(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char *txt, int txtlen)
+/*!\brief Get TXT record from DNS.
+ * Really has nothing to do with enum, but anyway...
+ *
+ * Actually, there is now an internet-draft which describes how callerID should
+ * be stored in ENUM domains: draft-ietf-enum-cnam-04.txt
+ *
+ * The algorithm implemented here will thus be obsolete soon.
+ */
+int ast_get_txt(struct ast_channel *chan, const char *number, char *txt, int txtlen, char *suffix)
{
- struct enum_context context;
+ struct txt_context context;
char tmp[259 + 512];
- char naptrinput[512] = "+";
int pos = strlen(number) - 1;
int newpos = 0;
int ret = -1;
- struct enum_search *s = NULL;
- int version = -1;
-
- strncat(naptrinput, number, sizeof(naptrinput) - 2);
- context.naptrinput = naptrinput;
- context.dst = dst;
- context.dstlen = dstlen;
- context.tech = tech;
- context.techlen = techlen;
- context.txt = txt;
- context.txtlen = txtlen;
+ ast_debug(4, "ast_get_txt: Number = '%s', suffix = '%s'\n", number, suffix);
- if (pos > 128)
+ if (chan && ast_autoservice_start(chan) < 0) {
+ return -1;
+ }
+
+ if (pos > 128) {
pos = 128;
- while (pos >= 0) {
- tmp[newpos++] = number[pos--];
- tmp[newpos++] = '.';
}
- if (chan && ast_autoservice_start(chan) < 0)
- return -1;
-
- for (;;) {
- ast_mutex_lock(&enumlock);
- if (version != enumver) {
- /* Ooh, a reload... */
- s = toplevs;
- version = enumver;
- } else {
- s = s->next;
- }
- if (s) {
- ast_copy_string(tmp + newpos, s->toplev, sizeof(tmp) - newpos);
+ while (pos >= 0) {
+ if (isdigit(number[pos])) {
+ tmp[newpos++] = number[pos];
+ tmp[newpos++] = '.';
}
- ast_mutex_unlock(&enumlock);
- if (!s)
- break;
-
- ret = ast_search_dns(&context, tmp, C_IN, T_TXT, txt_callback);
- if (ret > 0)
- break;
+ pos--;
}
+
+ ast_copy_string(&tmp[newpos], suffix, sizeof(tmp) - newpos);
+
if (ret < 0) {
ast_debug(2, "No such number found in ENUM: %s (%s)\n", tmp, strerror(errno));
ret = 0;
+ } else {
+ ast_copy_string(txt, context.txt, txtlen);
}
- if (chan)
+ if (chan) {
ret |= ast_autoservice_stop(chan);
- return ret;
-}
-
-/*! \brief Add enum tree to linked list */
-static struct enum_search *enum_newtoplev(const char *s)
-{
- struct enum_search *tmp;
-
- if ((tmp = ast_calloc(1, sizeof(*tmp)))) {
- ast_copy_string(tmp->toplev, s, sizeof(tmp->toplev));
}
- return tmp;
+ return ret;
}
/*! \brief Initialize the ENUM support subsystem */
static int private_enum_init(int reload)
{
struct ast_config *cfg;
- struct enum_search *s, *sl;
- struct ast_variable *v;
+ const char *string;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
if ((cfg = ast_config_load2("enum.conf", "enum", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
/* Destroy existing list */
ast_mutex_lock(&enumlock);
- s = toplevs;
- while (s) {
- sl = s;
- s = s->next;
- ast_free(sl);
- }
- toplevs = NULL;
if (cfg) {
- sl = NULL;
- v = ast_variable_browse(cfg, "general");
- while (v) {
- if (!strcasecmp(v->name, "search")) {
- s = enum_newtoplev(v->value);
- if (s) {
- if (sl)
- sl->next = s;
- else
- toplevs = s;
- sl = s;
- }
- }
- v = v->next;
+ if ((string = ast_variable_retrieve(cfg, "ienum", "branchlabel"))) {
+ ast_copy_string(ienum_branchlabel, string, sizeof(ienum_branchlabel));
+ }
+
+ if ((string = ast_variable_retrieve(cfg, "ienum", "ebl_alg"))) {
+ ebl_alg = ENUMLOOKUP_BLR_CC; /* default */
+
+ if (!strcasecmp(string, "txt"))
+ ebl_alg = ENUMLOOKUP_BLR_TXT;
+ else if (!strcasecmp(string, "ebl"))
+ ebl_alg = ENUMLOOKUP_BLR_EBL;
+ else if (!strcasecmp(string, "cc"))
+ ebl_alg = ENUMLOOKUP_BLR_CC;
+ else
+ ast_log(LOG_WARNING, "No valid parameter for ienum/ebl_alg.\n");
}
ast_config_destroy(cfg);
- } else {
- toplevs = enum_newtoplev(TOPLEV);
}
- enumver++;
ast_mutex_unlock(&enumlock);
manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Enum\r\nStatus: Enabled\r\nMessage: ENUM reload Requested\r\n");
return 0;