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

Generated on Thu Apr 16 06:27:33 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6