Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

enum.c

Go to the documentation of this file.
00001 /*
00002  * ENUM Support for Asterisk
00003  *
00004  * Copyright (C) 2003 Digium
00005  *
00006  * Written by Mark Spencer <markster@digium.com>
00007  *
00008  * Funding provided by nic.at
00009  *
00010  * Distributed under the terms of the GNU GPL
00011  *
00012  */
00013 
00014 #include <string.h>
00015 #include <fcntl.h>
00016 #include <unistd.h>
00017 #include <stdlib.h>
00018 #include <sys/types.h>
00019 #include <sys/socket.h>
00020 #include <netinet/in.h>
00021 #include <arpa/nameser.h>
00022 #include <resolv.h>
00023 #include <errno.h>
00024 #include <ctype.h>
00025 #include <regex.h>
00026 
00027 
00028 #include <asterisk/logger.h>
00029 #include <asterisk/options.h>
00030 #include <asterisk/enum.h>
00031 #include <asterisk/channel.h>
00032 #include <asterisk/config.h>
00033 
00034 #define MAX_SIZE 4096
00035 
00036 #define TOPLEV "e164.arpa."
00037 
00038 typedef struct {
00039    unsigned id :16;     /* query identification number */
00040 #if BYTE_ORDER == BIG_ENDIAN
00041          /* fields in third byte */
00042    unsigned qr: 1;      /* response flag */
00043    unsigned opcode: 4;  /* purpose of message */
00044    unsigned aa: 1;      /* authoritive answer */
00045    unsigned tc: 1;      /* truncated message */
00046    unsigned rd: 1;      /* recursion desired */
00047          /* fields in fourth byte */
00048    unsigned ra: 1;      /* recursion available */
00049    unsigned unused :1;  /* unused bits (MBZ as of 4.9.3a3) */
00050    unsigned ad: 1;      /* authentic data from named */
00051    unsigned cd: 1;      /* checking disabled by resolver */
00052    unsigned rcode :4;   /* response code */
00053 #endif
00054 #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
00055          /* fields in third byte */
00056    unsigned rd :1;      /* recursion desired */
00057    unsigned tc :1;      /* truncated message */
00058    unsigned aa :1;      /* authoritive answer */
00059    unsigned opcode :4;  /* purpose of message */
00060    unsigned qr :1;      /* response flag */
00061          /* fields in fourth byte */
00062    unsigned rcode :4;   /* response code */
00063    unsigned cd: 1;      /* checking disabled by resolver */
00064    unsigned ad: 1;      /* authentic data from named */
00065    unsigned unused :1;  /* unused bits (MBZ as of 4.9.3a3) */
00066    unsigned ra :1;      /* recursion available */
00067 #endif
00068          /* remaining bytes */
00069    unsigned qdcount :16;   /* number of question entries */
00070    unsigned ancount :16;   /* number of answer entries */
00071    unsigned nscount :16;   /* number of authority entries */
00072    unsigned arcount :16;   /* number of resource entries */
00073 } dns_HEADER;
00074 
00075 static struct enum_search {
00076    char toplev[80];
00077    struct enum_search *next;
00078 } *toplevs;
00079 
00080 static int enumver = 0;
00081 
00082 static ast_mutex_t enumlock = AST_MUTEX_INITIALIZER;
00083 
00084 static int skip_name(unsigned char *s, int len)
00085 {
00086    /* Shamelessly take from SER */
00087    int x = 0;
00088    while(x < len) {
00089       if (!*s) {
00090          s++;
00091          x++;
00092          break;
00093       }
00094       if (((*s) & 0xc0) == 0xc0) {
00095          s += 2;
00096          x += 2;
00097          break;
00098       }
00099       x += *s + 1;
00100       s += *s + 1;
00101    }
00102    if (x >= len)
00103       return -1;
00104    return x;
00105 }
00106 
00107 struct dn_answer {
00108    unsigned short rtype;
00109    unsigned short class;
00110    unsigned int ttl;
00111    unsigned short size;
00112 } __attribute__ ((__packed__));
00113 
00114 struct naptr {
00115    unsigned short order;
00116    unsigned short pref;
00117 } __attribute__ ((__packed__));
00118 
00119 static int parse_ie(unsigned char *data, int maxdatalen, unsigned char *src, int srclen)
00120 {
00121    int len, olen;
00122    len = olen = (int)src[0];
00123    src++;
00124    srclen--;
00125    if (len > srclen) {
00126       ast_log(LOG_WARNING, "Want %d, got %d\n", len, srclen);
00127       return -1;
00128    }
00129    if (len > maxdatalen)
00130       len = maxdatalen;
00131    memcpy(data, src, len);
00132    return olen + 1;
00133 }
00134 
00135 static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, char *naptrinput)
00136 {
00137    unsigned char *oanswer = answer;
00138    unsigned char flags[80] = "";
00139    unsigned char services[80] = "";
00140    unsigned char regexp[80] = "";
00141    unsigned char repl[80] = "";
00142    unsigned char temp[80] = "";
00143    unsigned char delim;
00144    unsigned char *delim2;
00145    unsigned char *pattern, *subst, *d;
00146    int res;
00147    int regexp_len, size, backref;
00148    int d_len = sizeof(temp) - 1;
00149    regex_t preg;
00150    regmatch_t pmatch[9];
00151 
00152    
00153    if (len < sizeof(struct naptr)) {
00154       printf("Length too short\n");
00155       return -1;
00156    }
00157    answer += sizeof(struct naptr);
00158    len -= sizeof(struct naptr);
00159    if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
00160       ast_log(LOG_WARNING, "Failed to get flags\n");
00161       return -1; 
00162    } else { answer += res; len -= res; }
00163    if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
00164       ast_log(LOG_WARNING, "Failed to get services\n");
00165       return -1; 
00166    } else { answer += res; len -= res; }
00167    if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0)
00168       return -1; else { answer += res; len -= res; }
00169    if ((res = dn_expand(oanswer,answer + len,answer, repl, sizeof(repl) - 1)) < 0) {
00170       ast_log(LOG_WARNING, "Failed to expand hostname\n");
00171       return -1;
00172    } 
00173 
00174 #if 0
00175    printf("Input: %s\n", naptrinput);
00176    printf("Flags: %s\n", flags);
00177    printf("Services: %s\n", services);
00178    printf("Regexp: %s\n", regexp);
00179    printf("Repl: %s\n", repl);
00180 #endif
00181 
00182    if (tolower(flags[0]) != 'u') {
00183       ast_log(LOG_WARNING, "Flag must be 'U' or 'u'.\n");
00184       return -1;
00185    }
00186 
00187    if ((!strncasecmp(services, "e2u+sip", 7)) || 
00188        (!strncasecmp(services, "sip+e2u", 7))) {
00189       strncpy(tech, "sip", techsize -1); 
00190    } else if ((!strncasecmp(services, "e2u+h323", 7)) || 
00191        (!strncasecmp(services, "h323+e2u", 7))) {
00192       strncpy(tech, "h323", techsize -1); 
00193    } else if ((!strncasecmp(services, "e2u+iax", 7)) || 
00194        (!strncasecmp(services, "iax+e2u", 7))) {
00195       strncpy(tech, "iax", techsize -1); 
00196    } else if ((!strncasecmp(services, "e2u+iax2", 7)) || 
00197        (!strncasecmp(services, "iax2+e2u", 7))) {
00198       strncpy(tech, "iax2", techsize -1); 
00199    } else if ((!strncasecmp(services, "e2u+tel", 7)) || 
00200        (!strncasecmp(services, "tel+e2u", 7))) {
00201       strncpy(tech, "tel", techsize -1); 
00202    } else if (strncasecmp(services, "e2u+voice:", 10)) {
00203       ast_log(LOG_WARNING, "Services must be e2u+sip, sip+e2u, e2u+h323, h323+e2u, e2u+iax, iax+e2u, e2u+iax2, iax2+e2u, e2u+tel, tel+e2u or e2u+voice:\n");
00204       return -1;
00205    }
00206 
00207    /* DEDBUGGING STUB
00208    strcpy(regexp, "!^\\+43(.*)$!\\1@bla.fasel!");
00209    */
00210 
00211    regexp_len = strlen(regexp);
00212    if (regexp_len < 7) {
00213       ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
00214       return -1;
00215    } 
00216 
00217 
00218    delim = regexp[0];
00219    delim2 = strchr(regexp + 1, delim);
00220    if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) {
00221       ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp);
00222       return -1;
00223    }
00224 
00225    pattern = regexp + 1;
00226    *delim2 = 0;
00227    subst   = delim2 + 1;
00228    regexp[regexp_len-1] = 0;
00229 
00230 #if 0
00231    printf("Pattern: %s\n", pattern);
00232    printf("Subst: %s\n", subst);
00233 #endif
00234 
00235 /*
00236  * now do the regex wizardry.
00237  */
00238 
00239    if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
00240       ast_log(LOG_WARNING, "Regex compilation error (regex = \"%s\").\n",regexp);
00241       return -1;
00242    }
00243 
00244    if (preg.re_nsub > 9) {
00245       ast_log(LOG_WARNING, "Regex compilation error: too many subs.\n");
00246       regfree(&preg);
00247       return -1;
00248    }
00249 
00250    if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
00251       ast_log(LOG_WARNING, "Regex match failed.\n");
00252       regfree(&preg);
00253       return -1;
00254    }
00255    regfree(&preg);
00256 
00257    d = temp; d_len--; 
00258    while( *subst && (d_len > 0) ) {
00259       if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) {
00260          backref = subst[1]-'0';
00261          size = pmatch[backref].rm_eo - pmatch[backref].rm_so;
00262          if (size > d_len) {
00263             ast_log(LOG_WARNING, "Not enough space during regex substitution.\n");
00264             return -1;
00265             }
00266          memcpy(d, naptrinput + pmatch[backref].rm_so, size);
00267          d += size;
00268          d_len -= size;
00269          subst += 2;
00270       } else if (isprint(*subst)) {
00271          *d++ = *subst++;
00272          d_len--;
00273       } else {
00274          ast_log(LOG_WARNING, "Error during regex substitution.\n");
00275          return -1;
00276       }
00277    }
00278    *d = 0;
00279    strncpy(dst, temp, dstsize);
00280    d = strchr(services, ':');
00281    if (d) 
00282       strncpy(tech, d+1, techsize -1); 
00283    return 0;
00284 }
00285 
00286 static int parse_answer(unsigned char *dst, int dstlen, unsigned char *tech, int techlen, unsigned char *answer, int len, char *naptrinput)
00287 {
00288    /*
00289     * This function is influenced by "ser" the SIP router.
00290     */
00291    int x;
00292    int res;
00293    dns_HEADER *h;
00294    struct dn_answer *ans;
00295    dst[0] = '\0';
00296    tech[0] = '\0';
00297 #if 0
00298    for (x=0;x<len;x++) {
00299       if ((answer[x] < 32) || (answer[x] > 127)) {
00300          if (lastlit)
00301             printf("\"");
00302          printf(" 0x%02x", answer[x]);
00303          lastlit = 0;
00304       } else {
00305          if (!lastlit) 
00306             printf(" \"");
00307          printf("%c", answer[x]);
00308          lastlit = 1;
00309       }
00310    }
00311    printf("\n");
00312 #endif   
00313    h = (dns_HEADER *)answer;
00314    /* Skip over DNS header */
00315    answer += sizeof(dns_HEADER);
00316    len -= sizeof(dns_HEADER);
00317 #if 0
00318    printf("Query count: %d\n", ntohs(h->qdcount));
00319 #endif
00320    for (x=0;x<ntohs(h->qdcount);x++) {
00321       if ((res = skip_name(answer, len)) < 0) {
00322          ast_log(LOG_WARNING, "Couldn't skip over name\n");
00323          return -1;
00324       }
00325       answer += res;
00326       len -= res;
00327       answer += 4;   /* Skip QCODE / QCLASS */
00328       len -= 4;
00329       if (len < 0) {
00330          ast_log(LOG_WARNING, "Strange query size\n");
00331          return -1;
00332       }
00333    }
00334 #if 0
00335    printf("Length remaining: %d\n", len);
00336    printf("Answer count: %d\n", ntohs(h->ancount));
00337    printf("Looking for %d/%d\n", C_IN, T_NAPTR);
00338 #endif
00339    for (x=0;x<ntohs(h->ancount);x++) {
00340       if ((res = skip_name(answer, len)) < 0) {
00341          ast_log(LOG_WARNING, "Failed to skip name :(\n");
00342          return -1;
00343       }
00344       answer += res;
00345       len -= res;
00346       ans = (struct dn_answer *)answer;
00347       answer += sizeof(struct dn_answer);
00348       len -= sizeof(struct dn_answer);
00349       if (len < 0)
00350          return -1;
00351 #if 0
00352       printf("Type: %d, class: %d, ttl: %d, length: %d\n", ntohs(ans->rtype), ntohs(ans->class),
00353          ntohl(ans->ttl), ntohs(ans->size));
00354 #endif         
00355       len -= ntohs(ans->size);
00356       if (len < 0) {
00357          ast_log(LOG_WARNING, "Length exceeds frame\n");
00358          return -1;
00359       }
00360       if ((ntohs(ans->class) == C_IN) && (ntohs(ans->rtype) == T_NAPTR)) {
00361          if (parse_naptr(dst, dstlen, tech, techlen, answer, ntohs(ans->size), naptrinput))
00362             ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
00363          if (strlen(dst))
00364             return 0;
00365       }
00366       answer += ntohs(ans->size);
00367    }
00368    return 0;
00369 }
00370 
00371 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen)
00372 {
00373    unsigned char answer[MAX_SIZE];
00374    char tmp[259 + 80];
00375    char naptrinput[80] = "+";
00376    int pos = strlen(number) - 1;
00377    int newpos=0;
00378    int res = -1;
00379    int ret = -1;
00380    struct enum_search *s = NULL;
00381    int version = -1;
00382    struct __res_state enumstate;
00383    res_ninit(&enumstate);  
00384    if (chan && ast_autoservice_start(chan) < 0)
00385       return -1;
00386 
00387    strncat(naptrinput, number, sizeof(naptrinput) - 2);
00388 
00389    if (pos > 128)
00390       pos = 128;
00391    while(pos >= 0) {
00392       tmp[newpos++] = number[pos--];
00393       tmp[newpos++] = '.';
00394    }
00395 #if 0
00396    printf("Looking for '%s'\n", tmp);
00397 #endif   
00398    
00399    for(;;) {
00400       ast_mutex_lock(&enumlock);
00401       if (version != enumver) {
00402          /* Ooh, a reload... */
00403          s = toplevs;
00404          version = enumver;
00405       } else {
00406          s = s->next;
00407       }
00408       if (s) {
00409          strcpy(tmp + newpos, s->toplev);
00410       }
00411       ast_mutex_unlock(&enumlock);
00412       if (!s)
00413          break;
00414       res = res_nsearch(&enumstate, tmp, C_IN, T_NAPTR, answer, sizeof(answer));
00415       if (res > 0)
00416          break;
00417    }
00418    if (res > 0) {
00419       if ((res = parse_answer(dst, dstlen, tech, techlen, answer, res, naptrinput))) {
00420          ast_log(LOG_WARNING, "Parse error returned %d\n", res);
00421          ret = 0;
00422       } else {
00423          ast_log(LOG_DEBUG, "Found technology '%s', destination '%s'\n", tech, dst);
00424          ret = 1;
00425       }
00426    } else {
00427       ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
00428       ret = 0;
00429    }
00430    if (chan)
00431       ret |= ast_autoservice_stop(chan);
00432    res_nclose(&enumstate);
00433    return ret;
00434 }
00435 
00436 static struct enum_search *enum_newtoplev(char *s)
00437 {
00438    struct enum_search *tmp;
00439    tmp = malloc(sizeof(struct enum_search));
00440    if (tmp) {
00441       memset(tmp, 0, sizeof(struct enum_search));
00442       strncpy(tmp->toplev, s, sizeof(tmp->toplev) - 1);
00443    }
00444    return tmp;
00445 }
00446 
00447 int ast_enum_init(void)
00448 {
00449    struct ast_config *cfg;
00450    struct enum_search *s, *sl;
00451    struct ast_variable *v;
00452 
00453    /* Destroy existing list */
00454    ast_mutex_lock(&enumlock);
00455    s = toplevs;
00456    while(s) {
00457       sl = s;
00458       s = s->next;
00459       free(sl);
00460    }
00461    toplevs = NULL;
00462    cfg = ast_load("enum.conf");
00463    if (cfg) {
00464       sl = NULL;
00465       v = ast_variable_browse(cfg, "general");
00466       while(v) {
00467          if (!strcasecmp(v->name, "search")) {
00468             s = enum_newtoplev(v->value);
00469             if (s) {
00470                if (sl)
00471                   sl->next = s;
00472                else
00473                   toplevs = s;
00474                sl = s;
00475             }
00476          }
00477          v = v->next;
00478       }
00479       ast_destroy(cfg);
00480    } else {
00481       toplevs = enum_newtoplev(TOPLEV);
00482    }
00483    enumver++;
00484    ast_mutex_unlock(&enumlock);
00485    return 0;
00486 }
00487 
00488 int ast_enum_reload(void)
00489 {
00490    return ast_enum_init();
00491 }

Generated on Fri Oct 31 07:05:06 2003 for Asterisk by doxygen 1.3.4