Wed Jan 27 20:02:10 2016

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

Generated on 27 Jan 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1