Mon Jun 27 16:50:53 2011

Asterisk developer's documentation


enum.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * Funding provided by nic.at
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  * This program is free software, distributed under the terms of
00017  * the GNU General Public License Version 2. See the LICENSE file
00018  * at the top of the source tree.
00019  */
00020 
00021 /*! \file
00022  *
00023  * \brief ENUM Support for Asterisk
00024  *
00025  * \author Mark Spencer <markster@digium.com>
00026  *
00027  * \arg Funding provided by nic.at
00028  *
00029  * \par Enum standards
00030  *
00031  * - NAPTR records: http://ietf.nri.reston.va.us/rfc/rfc2915.txt
00032  * - DNS SRV records: http://www.ietf.org/rfc/rfc2782.txt
00033  * - ENUM http://www.ietf.org/rfc/rfc3761.txt
00034  * - ENUM for H.323: http://www.ietf.org/rfc/rfc3762.txt
00035  * - ENUM SIP: http://www.ietf.org/rfc/rfc3764.txt
00036  * - IANA ENUM Services: http://www.iana.org/assignments/enum-services
00037  *
00038  * - I-ENUM: 
00039  *   http://tools.ietf.org/wg/enum/draft-ietf-enum-combined/
00040  *   http://tools.ietf.org/wg/enum/draft-ietf-enum-branch-location-record/
00041  *
00042  * \par Possible improvement
00043  * \todo Implement a caching mechanism for multile enum lookups
00044  * - See https://issues.asterisk.org/view.php?id=6739
00045  * \todo The service type selection needs to be redone.
00046  */
00047 
00048 #include "asterisk.h"
00049 
00050 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 241143 $")
00051 
00052 #include <sys/socket.h>
00053 #include <netinet/in.h>
00054 #include <arpa/nameser.h>
00055 #ifdef __APPLE__
00056 #if __APPLE_CC__ >= 1495
00057 #include <arpa/nameser_compat.h>
00058 #endif
00059 #endif
00060 #include <resolv.h>
00061 #include <ctype.h>
00062 #include <regex.h>
00063 
00064 #include "asterisk/enum.h"
00065 #include "asterisk/dns.h"
00066 #include "asterisk/channel.h"
00067 #include "asterisk/config.h"
00068 #include "asterisk/utils.h"
00069 #include "asterisk/manager.h"
00070 
00071 #ifdef __APPLE__
00072 #undef T_NAPTR
00073 #define T_NAPTR 35
00074 #endif
00075 
00076 #ifdef __APPLE__
00077 #undef T_TXT
00078 #define T_TXT 16
00079 #endif
00080 
00081 static char ienum_branchlabel[32] = "i";
00082 /* how to do infrastructure enum branch location resolution? */
00083 #define ENUMLOOKUP_BLR_CC       0
00084 #define ENUMLOOKUP_BLR_TXT      1
00085 #define ENUMLOOKUP_BLR_EBL      2
00086 static int ebl_alg = ENUMLOOKUP_BLR_CC;
00087  
00088 /* EBL record provisional type code */
00089 #define T_EBL      65300
00090 
00091 AST_MUTEX_DEFINE_STATIC(enumlock);
00092 
00093 /*! \brief Determine the length of a country code when given an E.164 string */
00094 /*
00095  * Input: E.164 number w/o leading +
00096  *
00097  * Output: number of digits in the country code
00098  *       0 on invalid number
00099  *
00100  * Algorithm:
00101  *   3 digits is the default length of a country code.
00102  *   country codes 1 and 7 are a single digit.
00103  *   the following country codes are two digits: 20, 27, 30-34, 36, 39,
00104  *     40, 41, 43-49, 51-58, 60-66, 81, 82, 84, 86, 90-95, 98.
00105  */
00106 static int cclen(const char *number)
00107 {
00108    int cc;
00109    char digits[3] = "";
00110 
00111    if (!number || (strlen(number) < 3)) {
00112       return 0;
00113    }
00114 
00115    strncpy(digits, number, 2);
00116    
00117    if (!sscanf(digits, "%30d", &cc)) {
00118       return 0;
00119    }
00120 
00121    if (cc / 10 == 1 || cc / 10 == 7)
00122          return 1;
00123 
00124    if (cc == 20 || cc == 27 || (cc >= 30 && cc <= 34) || cc == 36 ||
00125        cc == 39 || cc == 40 || cc == 41 || (cc >= 40 && cc <= 41) ||
00126        (cc >= 43 && cc <= 49) || (cc >= 51 && cc <= 58) ||
00127        (cc >= 60 && cc <= 66) || cc == 81 || cc == 82 || cc == 84 ||
00128        cc == 86 || (cc >= 90 && cc <= 95) || cc == 98) {
00129       return 2;
00130    }
00131 
00132    return 3;
00133 }
00134 
00135 struct txt_context {
00136    char txt[1024];      /* TXT record in TXT lookup */
00137    int txtlen;    /* Length */
00138 };
00139 
00140 /*! \brief Callback for TXT record lookup, /ol version */
00141 static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
00142 {
00143    struct txt_context *c = context;
00144    unsigned int i;
00145 
00146    c->txt[0] = 0; /* default to empty */
00147    c->txtlen = 0;
00148 
00149    if (answer == NULL) {
00150       return 0;
00151    }
00152 
00153    /* RFC1035: 
00154     *
00155     * <character-string> is a single length octet followed by that number of characters.
00156     * TXT-DATA        One or more <character-string>s.
00157     *
00158     * We only take the first string here.
00159     */
00160 
00161    i = *answer++;
00162    len -= 1;
00163 
00164    if (i > len) { /* illegal packet */
00165       ast_log(LOG_WARNING, "txt_callback: malformed TXT record.\n");
00166       return 0;
00167    }
00168 
00169    if (i >= sizeof(c->txt)) { /* too long? */
00170       ast_log(LOG_WARNING, "txt_callback: TXT record too long.\n");
00171       i = sizeof(c->txt) - 1;
00172    }
00173 
00174    ast_copy_string(c->txt, (char *)answer, i + 1);  /* this handles the \0 termination */
00175    c->txtlen = i;
00176 
00177    return 1;
00178 }
00179 
00180 /*! \brief Determine the branch location record as stored in a TXT record */
00181 /*
00182  * Input: CC code
00183  *
00184  * Output: number of digits in the number before the i-enum branch 
00185  *
00186  * Algorithm:  Build <ienum_branchlabel>.c.c.<suffix> and look for a TXT lookup.
00187  *       Return atoi(TXT-record).
00188  *       Return -1 on not found.
00189  *
00190  */
00191 static int blr_txt(const char *cc, const char *suffix)
00192 {
00193    struct txt_context context;
00194    char domain[128] = "";
00195    char *p1, *p2;
00196    int ret;
00197 
00198    ast_mutex_lock(&enumlock);
00199 
00200    ast_verb(4, "blr_txt()  cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel);
00201 
00202    if (sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) {
00203       ast_mutex_unlock(&enumlock);
00204       ast_log(LOG_WARNING, "ERROR: string sizing in blr_txt.\n");
00205       return -1;
00206    }
00207 
00208    p1 = domain + snprintf(domain, sizeof(domain), "%s.", ienum_branchlabel);
00209    ast_mutex_unlock(&enumlock);
00210 
00211    for (p2 = (char *) cc + strlen(cc) - 1; p2 >= cc; p2--) {
00212       if (isdigit(*p2)) {
00213          *p1++ = *p2;
00214          *p1++ = '.';
00215       }
00216    }
00217    strcat(p1, suffix);
00218 
00219    ast_verb(4, "blr_txt() FQDN for TXT record: %s, cc was %s\n", domain, cc);
00220 
00221    ret = ast_search_dns(&context, domain, C_IN, T_TXT, txt_callback);
00222 
00223    if (ret > 0) {
00224       ret = atoi(context.txt);
00225 
00226       if ((ret >= 0) && (ret < 20)) {
00227          ast_verb(3, "blr_txt() BLR TXT record for %s is %d (apex: %s)\n", cc, ret, suffix);
00228          return ret;
00229       }
00230    }
00231    
00232    ast_verb(3, "blr_txt() BLR TXT record for %s not found (apex: %s)\n", cc, suffix);
00233 
00234    return -1;
00235 }
00236 
00237 struct ebl_context {
00238    unsigned char pos;
00239    char separator[256];    /* label to insert */
00240    int sep_len;         /* Length */
00241    char apex[256];         /* new Apex */
00242    int apex_len;        /* Length */
00243 };
00244 
00245 /*! \brief Callback for EBL record lookup */
00246 static int ebl_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
00247 {
00248    struct ebl_context *c = context;
00249    unsigned int i;
00250 
00251    c->pos = 0; /* default to empty */
00252    c->separator[0] = 0;
00253    c->sep_len = 0;
00254    c->apex[0] = 0;   
00255    c->apex_len = 0;
00256 
00257    if (answer == NULL) {
00258       return 0;
00259    }
00260 
00261    /* draft-lendl-enum-branch-location-record-00
00262     *
00263     *      0  1  2  3  4  5  6  7
00264     *    +--+--+--+--+--+--+--+--+
00265     *    |       POSITION        |
00266     *    +--+--+--+--+--+--+--+--+
00267     *    /       SEPARATOR       /
00268     *    +--+--+--+--+--+--+--+--+
00269     *    /         APEX          /
00270     *    +--+--+--+--+--+--+--+--+
00271     *
00272     *  where POSITION is a single byte, SEPARATOR is a <character-string>
00273     *  and APEX is a <domain-name>. 
00274     * 
00275     */
00276 
00277    c->pos = *answer++;
00278    len -= 1;
00279 
00280    if ((c->pos > 15) || len < 2) {  /* illegal packet */
00281       ast_log(LOG_WARNING, "ebl_callback: malformed EBL record.\n");
00282       return 0;
00283    }
00284 
00285    i = *answer++;
00286    len -= 1;
00287    if (i > len) { /* illegal packet */
00288       ast_log(LOG_WARNING, "ebl_callback: malformed EBL record.\n");
00289       return 0;
00290    }
00291 
00292    ast_copy_string(c->separator, (char *)answer, i + 1);
00293    c->sep_len = i;
00294 
00295    answer += i;
00296    len -= i;
00297 
00298    if ((i = dn_expand((unsigned char *)fullanswer, (unsigned char *)answer + len, 
00299             (unsigned char *)answer, c->apex, sizeof(c->apex) - 1)) < 0) {
00300       ast_log(LOG_WARNING, "Failed to expand hostname\n");
00301       return 0;
00302    }
00303    c->apex[i] = 0;
00304    c->apex_len = i;
00305 
00306    return 1;
00307 }
00308 
00309 /*! \brief Evaluate the I-ENUM branch as stored in an EBL record */
00310 /*
00311  * Input: CC code
00312  *
00313  * Output: number of digits in the number before the i-enum branch 
00314  *
00315  * Algorithm:  Build <ienum_branchlabel>.c.c.<suffix> and look for an EBL record 
00316  *       Return pos and fill in separator and apex.
00317  *       Return -1 on not found.
00318  *
00319  */
00320 static int blr_ebl(const char *cc, const char *suffix, char *separator, int sep_len, char* apex, int apex_len)
00321 {
00322    struct ebl_context context;
00323    char domain[128] = "";
00324    char *p1,*p2;
00325    int ret;
00326 
00327    ast_mutex_lock(&enumlock);
00328 
00329    ast_verb(4, "blr_ebl()  cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel);
00330 
00331    if (sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) {
00332       ast_mutex_unlock(&enumlock);
00333       ast_log(LOG_WARNING, "ERROR: string sizing in blr_EBL.\n");
00334       return -1;
00335    }
00336 
00337    p1 = domain + snprintf(domain, sizeof(domain), "%s.", ienum_branchlabel);
00338    ast_mutex_unlock(&enumlock);
00339 
00340    for (p2 = (char *) cc + strlen(cc) - 1; p2 >= cc; p2--) {
00341       if (isdigit(*p2)) {
00342          *p1++ = *p2;
00343          *p1++ = '.';
00344       }
00345    }
00346    strcat(p1, suffix);
00347 
00348    ast_verb(4, "blr_ebl() FQDN for EBL record: %s, cc was %s\n", domain, cc);
00349 
00350    ret = ast_search_dns(&context, domain, C_IN, T_EBL, ebl_callback);
00351    if (ret > 0) {
00352       ret = context.pos;
00353 
00354       if ((ret >= 0) && (ret < 20)) {
00355          ast_verb(3, "blr_txt() BLR EBL record for %s is %d/%s/%s)\n", cc, ret, context.separator, context.apex);
00356          ast_copy_string(separator, context.separator, sep_len);
00357          ast_copy_string(apex, context.apex, apex_len);
00358          return ret;
00359       }
00360    }
00361    ast_verb(3, "blr_txt() BLR EBL record for %s not found (apex: %s)\n", cc, suffix);
00362    return -1;
00363 }
00364 
00365 /*! \brief Parse NAPTR record information elements */
00366 static unsigned int parse_ie(char *data, unsigned int maxdatalen, unsigned char *src, unsigned int srclen)
00367 {
00368    unsigned int len, olen;
00369 
00370    len = olen = (unsigned int) src[0];
00371    src++;
00372    srclen--;
00373 
00374    if (len > srclen) {
00375       ast_log(LOG_WARNING, "ENUM parsing failed: Wanted %d characters, got %d\n", len, srclen);
00376       return -1;
00377    }
00378 
00379    if (len > maxdatalen)
00380       len = maxdatalen;
00381    memcpy(data, src, len);
00382 
00383    return olen + 1;
00384 }
00385 
00386 /*! \brief Parse DNS NAPTR record used in ENUM ---*/
00387 static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, unsigned char *naptrinput)
00388 {
00389    char tech_return[80];
00390    char *oanswer = (char *)answer;
00391    char flags[512] = "";
00392    char services[512] = "";
00393    char *p;
00394    char regexp[512] = "";
00395    char repl[512] = "";
00396    char tempdst[512] = "";
00397    char errbuff[512] = "";
00398    char delim;
00399    char *delim2;
00400    char *pattern, *subst, *d;
00401    int res;
00402    int regexp_len, rc;
00403    static const int max_bt = 10; /* max num of regexp backreference allowed, must remain 10 to guarantee a valid backreference index */
00404    int size, matchindex; /* size is the size of the backreference sub. */
00405    size_t d_len = sizeof(tempdst) - 1;
00406    regex_t preg;
00407    regmatch_t pmatch[max_bt];
00408 
00409    tech_return[0] = '\0';
00410    dst[0] = '\0';
00411 
00412    if (len < sizeof(struct naptr)) {
00413       ast_log(LOG_WARNING, "NAPTR record length too short\n");
00414       return -1;
00415    }
00416    answer += sizeof(struct naptr);
00417    len -= sizeof(struct naptr);
00418    if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
00419       ast_log(LOG_WARNING, "Failed to get flags from NAPTR record\n");
00420       return -1;
00421    } else {
00422       answer += res;
00423       len -= res;
00424    }
00425 
00426    if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
00427       ast_log(LOG_WARNING, "Failed to get services from NAPTR record\n");
00428       return -1;
00429    } else {
00430       answer += res;
00431       len -= res;
00432    }
00433    if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0) {
00434       ast_log(LOG_WARNING, "Failed to get regexp from NAPTR record\n");
00435       return -1;
00436    } else {
00437       answer += res;
00438       len -= res;
00439    }
00440 
00441    if ((res = dn_expand((unsigned char *)oanswer, (unsigned char *)answer + len, (unsigned char *)answer, repl, sizeof(repl) - 1)) < 0) {
00442       ast_log(LOG_WARNING, "Failed to expand hostname\n");
00443       return -1;
00444    }
00445 
00446    ast_debug(3, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
00447       naptrinput, flags, services, regexp, repl);
00448 
00449 
00450    if (tolower(flags[0]) != 'u') {
00451       ast_log(LOG_WARNING, "NAPTR Flag must be 'U' or 'u'.\n");
00452       return -1;
00453    }
00454 
00455    p = strstr(services, "e2u+");
00456    if (p == NULL)
00457       p = strstr(services, "E2U+");
00458    if (p){
00459       p = p + 4;
00460       if (strchr(p, ':')){
00461          p = strchr(p, ':') + 1;
00462       }
00463       ast_copy_string(tech_return, p, sizeof(tech_return));
00464    } else {
00465 
00466       p = strstr(services, "+e2u");
00467       if (p == NULL)
00468          p = strstr(services, "+E2U");
00469       if (p) {
00470          *p = 0;
00471          p = strchr(services, ':');
00472          if (p)
00473             *p = 0;
00474          ast_copy_string(tech_return, services, sizeof(tech_return));
00475       }
00476    }
00477 
00478    regexp_len = strlen(regexp);
00479    if (regexp_len < 7) {
00480       ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
00481       return -1;
00482    }
00483 
00484    /* this takes the first character of the regexp (which is a delimiter) 
00485     * and uses that character to find the index of the second delimiter */
00486    delim = regexp[0];
00487    delim2 = strchr(regexp + 1, delim);
00488    if ((delim2 == NULL) || (regexp[regexp_len - 1] != delim)) {  /* is the second delimiter found, and is the end of the regexp a delimiter */
00489       ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n", regexp);
00490       return -1;
00491    } 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 */
00492       ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n", regexp);
00493       return -1;
00494    }
00495    pattern = regexp + 1;   /* pattern is the regex without the begining and ending delimiter */
00496    *delim2 = 0;    /* zero out the middle delimiter */
00497    subst   = delim2 + 1; /* dst substring is everything after the second delimiter. */
00498    regexp[regexp_len - 1] = 0; /* zero out the last delimiter */
00499 
00500 /*
00501  * now do the regex wizardry.
00502  */
00503 
00504    if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
00505       ast_log(LOG_WARNING, "NAPTR Regex compilation error (regex = \"%s\").\n", regexp);
00506       return -1;
00507    }
00508 
00509    if (preg.re_nsub > ARRAY_LEN(pmatch)) {
00510       ast_log(LOG_WARNING, "NAPTR Regex compilation error: too many subs.\n");
00511       regfree(&preg);
00512       return -1;
00513    }
00514    /* pmatch is an array containing the substring indexes for the regex backreference sub.
00515     * max_bt is the maximum number of backreferences allowed to be stored in pmatch */
00516    if ((rc = regexec(&preg, (char *) naptrinput, max_bt, pmatch, 0))) {
00517       regerror(rc, &preg, errbuff, sizeof(errbuff));
00518       ast_log(LOG_WARNING, "NAPTR Regex match failed. Reason: %s\n", errbuff);
00519       regfree(&preg);
00520       return -1;
00521    }
00522    regfree(&preg);
00523 
00524    d = tempdst;
00525    d_len--;
00526 
00527    /* perform the backreference sub. Search the subst for backreferences,
00528     * when a backreference is found, retrieve the backreferences number.
00529     * use the backreference number as an index for pmatch to retrieve the
00530     * beginning and ending indexes of the substring to insert as the backreference.
00531     * if no backreference is found, continue copying the subst into tempdst */
00532    while (*subst && (d_len > 0)) {
00533       if ((subst[0] == '\\') && isdigit(subst[1])) { /* is this character the beginning of a backreference */
00534          matchindex = (int) (subst[1] - '0');
00535          if (matchindex >= ARRAY_LEN(pmatch)) {
00536             ast_log(LOG_WARNING, "Error during regex substitution. Invalid pmatch index.\n");
00537             return -1;
00538          }
00539          /* pmatch len is 10. we are garanteed a single char 0-9 is a valid index */
00540          size = pmatch[matchindex].rm_eo - pmatch[matchindex].rm_so;
00541          if (size > d_len) {
00542             ast_log(LOG_WARNING, "Not enough space during NAPTR regex substitution.\n");
00543             return -1;
00544          }
00545          /* are the pmatch indexes valid for the input length */
00546          if ((strlen((char *) naptrinput) >= pmatch[matchindex].rm_eo) && (pmatch[matchindex].rm_so <= pmatch[matchindex].rm_eo)) {
00547             memcpy(d, (naptrinput + (int) pmatch[matchindex].rm_so), size);  /* copy input substring into backreference marker */
00548             d_len -= size;
00549             subst += 2;  /* skip over backreference characters to next valid character */
00550             d += size;
00551          } else {
00552             ast_log(LOG_WARNING, "Error during regex substitution. Invalid backreference index.\n");
00553             return -1;
00554          }
00555       } else if (isprint(*subst)) {
00556          *d++ = *subst++;
00557          d_len--;
00558       } else {
00559          ast_log(LOG_WARNING, "Error during regex substitution.\n");
00560          return -1;
00561       }
00562    }
00563    *d = 0;
00564    ast_copy_string((char *) dst, tempdst, dstsize);
00565    dst[dstsize - 1] = '\0';
00566 
00567    if (*tech != '\0'){ /* check if it is requested NAPTR */
00568       if (!strncasecmp(tech, "ALL", techsize)){
00569          return 0; /* return or count any RR */
00570       }
00571       if (!strncasecmp(tech_return, tech, sizeof(tech_return) < techsize ? sizeof(tech_return): techsize)){
00572          ast_copy_string(tech, tech_return, techsize);
00573          return 0; /* we got our RR */
00574       } else { /* go to the next RR in the DNS answer */
00575          return 1;
00576       }
00577    }
00578 
00579    /* tech was not specified, return first parsed RR */
00580    ast_copy_string(tech, tech_return, techsize);
00581 
00582    return 0;
00583 }
00584 
00585 /* do not return requested value, just count RRs and return thei number in dst */
00586 #define ENUMLOOKUP_OPTIONS_COUNT       1
00587 /* do an ISN style lookup */
00588 #define ENUMLOOKUP_OPTIONS_ISN      2
00589 /* do a infrastructure ENUM lookup */
00590 #define ENUMLOOKUP_OPTIONS_IENUM 4
00591 /* do a direct DNS lookup: no reversal */
00592 #define ENUMLOOKUP_OPTIONS_DIRECT   8
00593 
00594 /*! \brief Callback from ENUM lookup function */
00595 static int enum_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
00596 {
00597    struct enum_context *c = context;
00598    void *p = NULL;
00599    int res;
00600 
00601    res = parse_naptr((unsigned char *)c->dst, c->dstlen, c->tech, c->techlen, answer, len, (unsigned char *)c->naptrinput);
00602 
00603    if (res < 0) {
00604       ast_log(LOG_WARNING, "Failed to parse naptr\n");
00605       return -1;
00606    } else if ((res == 0) && !ast_strlen_zero(c->dst)) { /* ok, we got needed NAPTR */
00607       if (c->options & ENUMLOOKUP_OPTIONS_COUNT) { /* counting RRs */
00608          c->count++;
00609          snprintf(c->dst, c->dstlen, "%d", c->count);
00610       } else  {
00611          if ((p = ast_realloc(c->naptr_rrs, sizeof(*c->naptr_rrs) * (c->naptr_rrs_count + 1)))) {
00612             c->naptr_rrs = p;
00613             memcpy(&c->naptr_rrs[c->naptr_rrs_count].naptr, answer, sizeof(c->naptr_rrs->naptr));
00614             c->naptr_rrs[c->naptr_rrs_count].result = ast_strdup(c->dst);
00615             c->naptr_rrs[c->naptr_rrs_count].tech = ast_strdup(c->tech);
00616             c->naptr_rrs[c->naptr_rrs_count].sort_pos = c->naptr_rrs_count;
00617             c->naptr_rrs_count++;
00618          }
00619          c->dst[0] = 0;
00620       }
00621       return 0;
00622    }
00623 
00624    return 0;
00625 }
00626 
00627 /* ENUM lookup */
00628 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)
00629 {
00630    struct enum_context *context;
00631    char tmp[512];
00632    char domain[256];
00633    char left[128];
00634    char middle[128];
00635    char naptrinput[128];
00636    char apex[128] = "";
00637    int ret = -1;
00638    /* for ISN rewrite */
00639    char *p1 = NULL;
00640    char *p2 = NULL;
00641    char *p3 = NULL;
00642    int k = 0;
00643    int i = 0;
00644    int z = 0;
00645    int spaceleft = 0;
00646    struct timeval time_start, time_end;
00647  
00648    if (ast_strlen_zero(suffix)) {
00649       ast_log(LOG_WARNING, "ast_get_enum need a suffix parameter now.\n");
00650       return -1;
00651    }
00652 
00653    ast_verb(2, "ast_get_enum(num='%s', tech='%s', suffix='%s', options='%s', record=%d\n", number, tech, suffix, options, record);
00654 
00655 /*
00656   We don't need that any more, that "n" preceding the number has been replaced by a flag
00657   in the options paramter.
00658    ast_copy_string(naptrinput, number, sizeof(naptrinput));
00659 */
00660 /*
00661  * The "number" parameter includes a leading '+' if it's a full E.164 number (and not ISN)
00662  * We need to preserve that as the regex inside NAPTRs expect the +.
00663  *
00664  * But for the domain generation, the '+' is a nuissance, so we get rid of it.
00665 */
00666    ast_copy_string(naptrinput, number[0] == 'n' ? number + 1 : number, sizeof(naptrinput));
00667    if (number[0] == '+') {
00668       number++;
00669    }
00670 
00671    if (!(context = ast_calloc(1, sizeof(*context))))
00672       return -1;
00673 
00674    if((p3 = strchr(naptrinput, '*'))) {
00675       *p3='\0';      
00676    }
00677 
00678    context->naptrinput = naptrinput;   /* The number */
00679    context->dst = dst;        /* Return string */
00680    context->dstlen = dstlen;
00681    context->tech = tech;
00682    context->techlen = techlen;
00683    context->options = 0;
00684    context->position = record > 0 ? record : 1;
00685    context->count = 0;
00686    context->naptr_rrs = NULL;
00687    context->naptr_rrs_count = 0;
00688 
00689    /*
00690     * Process options:
00691     *
00692     *    c  Return count, not URI
00693     *    i  Use infrastructure ENUM 
00694     *    s  Do ISN transformation
00695     *    d  Direct DNS query: no reversing.
00696     *
00697     */
00698    if (options != NULL) {
00699       if (strchr(options,'s')) {
00700          context->options |= ENUMLOOKUP_OPTIONS_ISN;
00701       } else if (strchr(options,'i')) {
00702          context->options |= ENUMLOOKUP_OPTIONS_IENUM;
00703       } else if (strchr(options,'d')) {
00704          context->options |= ENUMLOOKUP_OPTIONS_DIRECT;
00705       }
00706       if (strchr(options,'c')) {
00707          context->options |= ENUMLOOKUP_OPTIONS_COUNT;
00708       }
00709       if (strchr(number,'*')) {
00710          context->options |= ENUMLOOKUP_OPTIONS_ISN;
00711       }
00712    }
00713    ast_verb(2, "ENUM options(%s): pos=%d, options='%d'\n", options, context->position, context->options);
00714    ast_debug(1, "ast_get_enum(): n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n",
00715          number, tech, suffix, context->options, context->position);
00716 
00717    /*
00718     * This code does more than simple RFC3261 ENUM. All these rewriting 
00719     * schemes have in common that they build the FQDN for the NAPTR lookup
00720     * by concatenating
00721     *    - a number which needs be flipped and "."-seperated   (left)
00722     *    - some fixed string              (middle)
00723     *    - an Apex.                 (apex)
00724     *
00725     * The RFC3261 ENUM is: left=full number, middle="", apex=from args.
00726     * ISN:  number = "middle*left", apex=from args
00727     * I-ENUM: EBL parameters build the split, can change apex
00728     * Direct: left="", middle=argument, apex=from args
00729     *
00730     */
00731 
00732    /* default: the whole number will be flipped, no middle domain component */
00733    ast_copy_string(left, number, sizeof(left));
00734    middle[0] = '\0';
00735    /*
00736     * I-ENUM can change the apex, thus we copy it 
00737     */
00738    ast_copy_string(apex, suffix, sizeof(apex));
00739    /* ISN rewrite */
00740    if ((context->options & ENUMLOOKUP_OPTIONS_ISN) && (p1 = strchr(number, '*'))) {
00741       *p1++ = '\0';
00742       ast_copy_string(left, number, sizeof(left));
00743       ast_copy_string(middle, p1, sizeof(middle) - 1);
00744       strcat(middle, ".");
00745 
00746       ast_verb(2, "ISN ENUM: left=%s, middle='%s'\n", left, middle);
00747    /* Direct DNS lookup rewrite */
00748    } else if (context->options & ENUMLOOKUP_OPTIONS_DIRECT) {
00749       left[0] = 0; /* nothing to flip around */
00750       ast_copy_string(middle, number, sizeof(middle) - 1);
00751       strcat(middle, ".");
00752  
00753       ast_verb(2, "DIRECT ENUM:  middle='%s'\n", middle);
00754    /* Infrastructure ENUM rewrite */
00755    } else if (context->options & ENUMLOOKUP_OPTIONS_IENUM) {
00756       int sdl = 0;
00757       char cc[8];
00758       char sep[256], n_apex[256];
00759       int cc_len = cclen(number);
00760       sdl = cc_len;
00761       ast_mutex_lock(&enumlock);
00762       ast_copy_string(sep, ienum_branchlabel, sizeof(sep)); /* default */
00763       ast_mutex_unlock(&enumlock);
00764 
00765       switch (ebl_alg) {
00766       case ENUMLOOKUP_BLR_EBL:
00767          ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */
00768          sdl = blr_ebl(cc, suffix, sep, sizeof(sep) - 1, n_apex, sizeof(n_apex) - 1);
00769 
00770          if (sdl >= 0) {
00771             ast_copy_string(apex, n_apex, sizeof(apex));
00772             ast_verb(2, "EBL ENUM: sep=%s, apex='%s'\n", sep, n_apex);
00773          } else {
00774             sdl = cc_len;
00775          }
00776          break;
00777       case ENUMLOOKUP_BLR_TXT:
00778          ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */
00779          sdl = blr_txt(cc, suffix);
00780 
00781          if (sdl < 0) 
00782             sdl = cc_len;
00783          break;
00784 
00785       case ENUMLOOKUP_BLR_CC: /* BLR is at the country-code level */
00786       default:
00787          sdl = cc_len;
00788          break;
00789       }
00790 
00791       if (sdl > strlen(number)) {   /* Number too short for this sdl? */
00792          ast_log(LOG_WARNING, "I-ENUM: subdomain location %d behind number %s\n", sdl, number);
00793          return 0;
00794       }
00795       ast_copy_string(left, number + sdl, sizeof(left));
00796 
00797       ast_mutex_lock(&enumlock);
00798       ast_copy_string(middle, sep, sizeof(middle) - 1);
00799       strcat(middle, ".");
00800       ast_mutex_unlock(&enumlock);
00801 
00802       /* check the space we need for middle */
00803       if ((sdl * 2 + strlen(middle) + 2) > sizeof(middle)) {
00804          ast_log(LOG_WARNING, "ast_get_enum: not enough space for I-ENUM rewrite.\n");
00805          return -1;
00806       }
00807 
00808       p1 = middle + strlen(middle);
00809       for (p2 = (char *) number + sdl - 1; p2 >= number; p2--) {
00810          if (isdigit(*p2)) {
00811             *p1++ = *p2;
00812             *p1++ = '.';
00813          }
00814       }
00815       *p1 = '\0';
00816 
00817       ast_verb(2, "I-ENUM: cclen=%d, left=%s, middle='%s', apex='%s'\n", cc_len, left, middle, apex);
00818    }
00819 
00820    if (strlen(left) * 2 + 2 > sizeof(domain)) {
00821       ast_log(LOG_WARNING, "string to long in ast_get_enum\n");
00822       return -1;
00823    }
00824 
00825    /* flip left into domain */
00826    p1 = domain;
00827    for (p2 = left + strlen(left); p2 >= left; p2--) {
00828       if (isdigit(*p2)) {
00829          *p1++ = *p2;
00830          *p1++ = '.';
00831       }
00832    }
00833    *p1 = '\0';
00834 
00835    if (chan && ast_autoservice_start(chan) < 0) {
00836       ast_free(context);
00837       return -1;
00838    }
00839 
00840    spaceleft = sizeof(tmp) - 2;
00841    ast_copy_string(tmp, domain, spaceleft);
00842    spaceleft -= strlen(domain);
00843 
00844    if (*middle) {
00845       strncat(tmp, middle, spaceleft);
00846       spaceleft -= strlen(middle);
00847    }
00848 
00849    strncat(tmp,apex,spaceleft);
00850    time_start = ast_tvnow();
00851    ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback);
00852    time_end = ast_tvnow();
00853 
00854    ast_verb(2, "ast_get_enum() profiling: %s, %s, %" PRIi64 " ms\n", 
00855          (ret == 0) ? "OK" : "FAIL", tmp, ast_tvdiff_ms(time_end, time_start));
00856 
00857    if (ret < 0) {
00858       ast_debug(1, "No such number found: %s (%s)\n", tmp, strerror(errno));
00859       strcpy(dst, "0");
00860       ret = 0;
00861    }
00862 
00863    if (context->naptr_rrs_count >= context->position && ! (context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
00864       /* sort array by NAPTR order/preference */
00865       for (k = 0; k < context->naptr_rrs_count; k++) {
00866          for (i = 0; i < context->naptr_rrs_count; i++) {
00867             /* use order first and then preference to compare */
00868             if ((ntohs(context->naptr_rrs[k].naptr.order) < ntohs(context->naptr_rrs[i].naptr.order)
00869                  && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
00870                  || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order)
00871                  && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
00872                z = context->naptr_rrs[k].sort_pos;
00873                context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
00874                context->naptr_rrs[i].sort_pos = z;
00875                continue;
00876             }
00877             if (ntohs(context->naptr_rrs[k].naptr.order) == ntohs(context->naptr_rrs[i].naptr.order)) {
00878                if ((ntohs(context->naptr_rrs[k].naptr.pref) < ntohs(context->naptr_rrs[i].naptr.pref)
00879                     && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
00880                     || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref)
00881                     && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
00882                   z = context->naptr_rrs[k].sort_pos;
00883                   context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
00884                   context->naptr_rrs[i].sort_pos = z;
00885                }
00886             }
00887          }
00888       }
00889       for (k = 0; k < context->naptr_rrs_count; k++) {
00890          if (context->naptr_rrs[k].sort_pos == context->position - 1) {
00891             ast_copy_string(context->dst, context->naptr_rrs[k].result, dstlen);
00892             ast_copy_string(context->tech, context->naptr_rrs[k].tech, techlen);
00893             break;
00894          }
00895       }
00896    } else if (!(context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
00897       context->dst[0] = 0;
00898    } else if ((context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
00899       snprintf(context->dst,context->dstlen,"%d",context->count);
00900    }
00901 
00902    if (chan)
00903       ret |= ast_autoservice_stop(chan);
00904 
00905    if (!argcontext) {
00906       for (k = 0; k < context->naptr_rrs_count; k++) {
00907          ast_free(context->naptr_rrs[k].result);
00908          ast_free(context->naptr_rrs[k].tech);
00909       }
00910       ast_free(context->naptr_rrs);
00911       ast_free(context);
00912    } else
00913       *argcontext = context;
00914 
00915    return ret;
00916 }
00917 
00918 int ast_get_txt(struct ast_channel *chan, const char *number, char *txt, int txtlen, char *suffix)
00919 {
00920    struct txt_context context;
00921    char tmp[259 + 512];
00922    int pos = strlen(number) - 1;
00923    int newpos = 0;
00924    int ret = -1;
00925 
00926    ast_debug(4, "ast_get_txt: Number = '%s', suffix = '%s'\n", number, suffix);
00927 
00928    if (chan && ast_autoservice_start(chan) < 0) {
00929       return -1;
00930    }
00931  
00932    if (pos > 128) {
00933       pos = 128;
00934    }
00935 
00936    while (pos >= 0) {
00937       if (isdigit(number[pos])) {
00938          tmp[newpos++] = number[pos];
00939          tmp[newpos++] = '.';
00940       }
00941       pos--;
00942    }
00943 
00944    ast_copy_string(&tmp[newpos], suffix, sizeof(tmp) - newpos);
00945 
00946    if (ret < 0) {
00947       ast_debug(2, "No such number found in ENUM: %s (%s)\n", tmp, strerror(errno));
00948       ret = 0;
00949    } else {
00950       ast_copy_string(txt, context.txt, txtlen);
00951    }
00952    if (chan) {
00953       ret |= ast_autoservice_stop(chan);
00954    }
00955    return ret;
00956 }
00957 
00958 /*! \brief Initialize the ENUM support subsystem */
00959 static int private_enum_init(int reload)
00960 {
00961    struct ast_config *cfg;
00962    const char *string;
00963    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00964 
00965    if ((cfg = ast_config_load2("enum.conf", "enum", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
00966       return 0;
00967    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
00968       return 0;
00969    }
00970 
00971    /* Destroy existing list */
00972    ast_mutex_lock(&enumlock);
00973    if (cfg) {
00974       if ((string = ast_variable_retrieve(cfg, "ienum", "branchlabel"))) {
00975          ast_copy_string(ienum_branchlabel, string, sizeof(ienum_branchlabel));
00976       }
00977 
00978       if ((string = ast_variable_retrieve(cfg, "ienum", "ebl_alg"))) {
00979          ebl_alg = ENUMLOOKUP_BLR_CC; /* default */
00980 
00981          if (!strcasecmp(string, "txt"))
00982             ebl_alg = ENUMLOOKUP_BLR_TXT; 
00983          else if (!strcasecmp(string, "ebl"))
00984             ebl_alg = ENUMLOOKUP_BLR_EBL; 
00985          else if (!strcasecmp(string, "cc"))
00986             ebl_alg = ENUMLOOKUP_BLR_CC; 
00987          else
00988             ast_log(LOG_WARNING, "No valid parameter for ienum/ebl_alg.\n");
00989       }
00990       ast_config_destroy(cfg);
00991    }
00992    ast_mutex_unlock(&enumlock);
00993    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Enum\r\nStatus: Enabled\r\nMessage: ENUM reload Requested\r\n");
00994    return 0;
00995 }
00996 
00997 int ast_enum_init(void)
00998 {
00999    return private_enum_init(0);
01000 }
01001 
01002 int ast_enum_reload(void)
01003 {
01004    return private_enum_init(1);
01005 }

Generated on Mon Jun 27 16:50:53 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7