Sat Nov 1 06:28:35 2008

Asterisk developer's documentation


pbx_dundi.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  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Distributed Universal Number Discovery (DUNDi)
00022  */
00023 
00024 /*** MODULEINFO
00025    <depend>zlib</depend>
00026  ***/
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 134595 $")
00031 
00032 #include <stdlib.h>
00033 #include <stdio.h>
00034 #include <unistd.h>
00035 #include <netinet/in.h>
00036 #include <arpa/inet.h>
00037 #include <sys/socket.h>
00038 #include <string.h>
00039 #include <errno.h>
00040 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
00041 #include <sys/types.h>
00042 #include <netinet/in_systm.h>
00043 #endif
00044 #include <netinet/ip.h>
00045 #include <sys/ioctl.h>
00046 #include <netinet/in.h>
00047 #include <net/if.h>
00048 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
00049 #include <net/if_dl.h>
00050 #include <ifaddrs.h>
00051 #endif
00052 #include <zlib.h>
00053 #include <sys/signal.h>
00054 #include <pthread.h>
00055 
00056 #include "asterisk/file.h"
00057 #include "asterisk/logger.h"
00058 #include "asterisk/channel.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/options.h"
00061 #include "asterisk/pbx.h"
00062 #include "asterisk/module.h"
00063 #include "asterisk/frame.h"
00064 #include "asterisk/file.h"
00065 #include "asterisk/cli.h"
00066 #include "asterisk/lock.h"
00067 #include "asterisk/md5.h"
00068 #include "asterisk/dundi.h"
00069 #include "asterisk/sched.h"
00070 #include "asterisk/io.h"
00071 #include "asterisk/utils.h"
00072 #include "asterisk/crypto.h"
00073 #include "asterisk/astdb.h"
00074 #include "asterisk/acl.h"
00075 #include "asterisk/aes.h"
00076 
00077 #include "dundi-parser.h"
00078 
00079 #define MAX_RESULTS  64
00080 
00081 #define MAX_PACKET_SIZE 8192
00082 
00083 #define DUNDI_MODEL_INBOUND      (1 << 0)
00084 #define DUNDI_MODEL_OUTBOUND  (1 << 1)
00085 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
00086 
00087 /*! Keep times of last 10 lookups */
00088 #define DUNDI_TIMING_HISTORY  10
00089 
00090 #define FLAG_ISREG       (1 << 0)   /*!< Transaction is register request */
00091 #define FLAG_DEAD        (1 << 1)   /*!< Transaction is dead */
00092 #define FLAG_FINAL       (1 << 2)   /*!< Transaction has final message sent */
00093 #define FLAG_ISQUAL      (1 << 3)   /*!< Transaction is a qualification */
00094 #define FLAG_ENCRYPT     (1 << 4)   /*!< Transaction is encrypted wiht ECX/DCX */
00095 #define FLAG_SENDFULLKEY (1 << 5)   /*!< Send full key on transaction */
00096 #define FLAG_STOREHIST   (1 << 6)   /*!< Record historic performance */
00097 
00098 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
00099 
00100 #if 0
00101 #define DUNDI_SECRET_TIME 15  /* Testing only */
00102 #else
00103 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
00104 #endif
00105 
00106 #define KEY_OUT         0
00107 #define KEY_IN       1
00108 
00109 static struct io_context *io;
00110 static struct sched_context *sched;
00111 static int netsocket = -1;
00112 static pthread_t netthreadid = AST_PTHREADT_NULL;
00113 static pthread_t precachethreadid = AST_PTHREADT_NULL;
00114 static int tos = 0;
00115 static int dundidebug = 0;
00116 static int authdebug = 0;
00117 static int dundi_ttl = DUNDI_DEFAULT_TTL;
00118 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
00119 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
00120 static int global_autokilltimeout = 0;
00121 static dundi_eid global_eid;
00122 static int default_expiration = 60;
00123 static int global_storehistory = 0;
00124 static char dept[80];
00125 static char org[80];
00126 static char locality[80];
00127 static char stateprov[80];
00128 static char country[80];
00129 static char email[80];
00130 static char phone[80];
00131 static char secretpath[80];
00132 static char cursecret[80];
00133 static char ipaddr[80];
00134 static time_t rotatetime;
00135 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
00136 static int dundi_shutdown = 0;
00137 
00138 struct permission {
00139    AST_LIST_ENTRY(permission) list;
00140    int allow;
00141    char name[0];
00142 };
00143 
00144 struct dundi_packet {
00145    AST_LIST_ENTRY(dundi_packet) list;
00146    struct dundi_hdr *h;
00147    int datalen;
00148    struct dundi_transaction *parent;
00149    int retransid;
00150    int retrans;
00151    unsigned char data[0];
00152 };
00153 
00154 struct dundi_hint_metadata {
00155    unsigned short flags;
00156    char exten[AST_MAX_EXTENSION];
00157 };
00158 
00159 struct dundi_precache_queue {
00160    AST_LIST_ENTRY(dundi_precache_queue) list;
00161    char *context;
00162    time_t expiration;
00163    char number[0];
00164 };
00165 
00166 struct dundi_request;
00167 
00168 struct dundi_transaction {
00169    struct sockaddr_in addr;                       /*!< Other end of transaction */
00170    struct timeval start;                          /*!< When this transaction was created */
00171    dundi_eid eids[DUNDI_MAX_STACK + 1];
00172    int eidcount;                                  /*!< Number of eids in eids */
00173    dundi_eid us_eid;                              /*!< Our EID, to them */
00174    dundi_eid them_eid;                            /*!< Their EID, to us */
00175    aes_encrypt_ctx   ecx;                           /*!< AES 128 Encryption context */
00176    aes_decrypt_ctx   dcx;                           /*!< AES 128 Decryption context */
00177    unsigned int flags;                            /*!< Has final packet been sent */
00178    int ttl;                                       /*!< Remaining TTL for queries on this one */
00179    int thread;                                    /*!< We have a calling thread */
00180    int retranstimer;                              /*!< How long to wait before retransmissions */
00181    int autokillid;                                /*!< ID to kill connection if answer doesn't come back fast enough */
00182    int autokilltimeout;                           /*!< Recommended timeout for autokill */
00183    unsigned short strans;                         /*!< Our transaction identifier */
00184    unsigned short dtrans;                         /*!< Their transaction identifer */
00185    unsigned char iseqno;                          /*!< Next expected received seqno */
00186    unsigned char oiseqno;                         /*!< Last received incoming seqno */
00187    unsigned char oseqno;                          /*!< Next transmitted seqno */
00188    unsigned char aseqno;                          /*!< Last acknowledge seqno */
00189    AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets;  /*!< Packets to be retransmitted */
00190    struct packetlist lasttrans;                   /*!< Last transmitted / ACK'd packet */
00191    struct dundi_request *parent;                  /*!< Parent request (if there is one) */
00192    AST_LIST_ENTRY(dundi_transaction) parentlist;  /*!< Next with respect to the parent */
00193    AST_LIST_ENTRY(dundi_transaction) all;         /*!< Next with respect to all DUNDi transactions */
00194 };
00195 
00196 struct dundi_request {
00197    char dcontext[AST_MAX_EXTENSION];
00198    char number[AST_MAX_EXTENSION];
00199    dundi_eid query_eid;
00200    dundi_eid root_eid;
00201    struct dundi_result *dr;
00202    struct dundi_entity_info *dei;
00203    struct dundi_hint_metadata *hmd;
00204    int maxcount;
00205    int respcount;
00206    int expiration;
00207    int cbypass;
00208    int pfds[2];
00209    unsigned long crc32;                              /*!< CRC-32 of all but root EID's in avoid list */
00210    AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans;  /*!< Transactions */
00211    AST_LIST_ENTRY(dundi_request) list;
00212 };
00213 
00214 struct dundi_mapping {
00215    char dcontext[AST_MAX_EXTENSION];
00216    char lcontext[AST_MAX_EXTENSION];
00217    int weight;
00218    int options;
00219    int tech;
00220    int dead;
00221    char dest[AST_MAX_EXTENSION];
00222    AST_LIST_ENTRY(dundi_mapping) list;
00223 };
00224 
00225 struct dundi_peer {
00226    dundi_eid eid;
00227    struct sockaddr_in addr;               /*!< Address of DUNDi peer */
00228    AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
00229    struct permissionlist include;
00230    dundi_eid us_eid;
00231    char inkey[80];
00232    char outkey[80];
00233    int dead;
00234    int registerid;
00235    int qualifyid;
00236    int sentfullkey;
00237    int order;
00238    unsigned char txenckey[256];           /*!< Transmitted encrypted key + sig */
00239    unsigned char rxenckey[256];           /*!< Cache received encrypted key + sig */
00240    unsigned long us_keycrc32;             /*!< CRC-32 of our key */
00241    aes_encrypt_ctx   us_ecx;                /*!< Cached AES 128 Encryption context */
00242    aes_decrypt_ctx   us_dcx;                 /*!< Cached AES 128 Decryption context */
00243    unsigned long them_keycrc32;           /*!< CRC-32 of our key */
00244    aes_encrypt_ctx   them_ecx;              /*!< Cached AES 128 Encryption context */
00245    aes_decrypt_ctx   them_dcx;              /*!< Cached AES 128 Decryption context */
00246    time_t keyexpire;                      /*!< When to expire/recreate key */
00247    int registerexpire;
00248    int lookuptimes[DUNDI_TIMING_HISTORY];
00249    char *lookups[DUNDI_TIMING_HISTORY];
00250    int avgms;
00251    struct dundi_transaction *regtrans;    /*!< Registration transaction */
00252    struct dundi_transaction *qualtrans;   /*!< Qualify transaction */
00253    int model;                             /*!< Pull model */
00254    int pcmodel;                           /*!< Push/precache model */
00255    /*! Dynamic peers register with us */
00256    unsigned int dynamic:1;
00257    int lastms;                            /*!< Last measured latency */
00258    int maxms;                             /*!< Max permissible latency */
00259    struct timeval qualtx;                 /*!< Time of transmit */
00260    AST_LIST_ENTRY(dundi_peer) list;
00261 };
00262 
00263 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
00264 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
00265 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
00266 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
00267 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
00268 
00269 /*!
00270  * \brief Wildcard peer
00271  *
00272  * This peer is created if the [*] entry is specified in dundi.conf
00273  */
00274 static struct dundi_peer *any_peer;
00275 
00276 static int dundi_xmit(struct dundi_packet *pack);
00277 
00278 static void dundi_debug_output(const char *data)
00279 {
00280    if (dundidebug)
00281       ast_verbose("%s", data);
00282 }
00283 
00284 static void dundi_error_output(const char *data)
00285 {
00286    ast_log(LOG_WARNING, "%s", data);
00287 }
00288 
00289 static int has_permission(struct permissionlist *permlist, char *cont)
00290 {
00291    struct permission *perm;
00292    int res = 0;
00293 
00294    AST_LIST_TRAVERSE(permlist, perm, list) {
00295       if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
00296          res = perm->allow;
00297    }
00298 
00299    return res;
00300 }
00301 
00302 static char *tech2str(int tech)
00303 {
00304    switch(tech) {
00305    case DUNDI_PROTO_NONE:
00306       return "None";
00307    case DUNDI_PROTO_IAX:
00308       return "IAX2";
00309    case DUNDI_PROTO_SIP:
00310       return "SIP";
00311    case DUNDI_PROTO_H323:
00312       return "H323";
00313    default:
00314       return "Unknown";
00315    }
00316 }
00317 
00318 static int str2tech(char *str)
00319 {
00320    if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 
00321       return DUNDI_PROTO_IAX;
00322    else if (!strcasecmp(str, "SIP"))
00323       return DUNDI_PROTO_SIP;
00324    else if (!strcasecmp(str, "H323"))
00325       return DUNDI_PROTO_H323;
00326    else
00327       return -1;
00328 }
00329 
00330 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
00331 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
00332 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
00333 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
00334 {
00335    struct dundi_transaction *trans;
00336 
00337    /* Look for an exact match first */
00338    AST_LIST_TRAVERSE(&alltrans, trans, all) {
00339       if (!inaddrcmp(&trans->addr, sin) && 
00340            ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
00341            ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
00342            if (hdr->strans)
00343               trans->dtrans = ntohs(hdr->strans) & 32767;
00344            break;
00345       }
00346    }
00347    if (!trans) {
00348       switch(hdr->cmdresp & 0x7f) {
00349       case DUNDI_COMMAND_DPDISCOVER:
00350       case DUNDI_COMMAND_EIDQUERY:
00351       case DUNDI_COMMAND_PRECACHERQ:
00352       case DUNDI_COMMAND_REGREQ:
00353       case DUNDI_COMMAND_NULL:
00354       case DUNDI_COMMAND_ENCRYPT:
00355          if (hdr->strans) {   
00356             /* Create new transaction */
00357             trans = create_transaction(NULL);
00358             if (trans) {
00359                memcpy(&trans->addr, sin, sizeof(trans->addr));
00360                trans->dtrans = ntohs(hdr->strans) & 32767;
00361             } else
00362                ast_log(LOG_WARNING, "Out of memory!\n");
00363          }
00364          break;
00365       default:
00366          break;
00367       }
00368    }
00369    return trans;
00370 }
00371 
00372 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
00373 
00374 static int dundi_ack(struct dundi_transaction *trans, int final)
00375 {
00376    return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
00377 }
00378 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
00379 {
00380    struct {
00381       struct dundi_packet pack;
00382       struct dundi_hdr hdr;
00383    } tmp;
00384    struct dundi_transaction trans;
00385    /* Never respond to an INVALID with another INVALID */
00386    if (h->cmdresp == DUNDI_COMMAND_INVALID)
00387       return;
00388    memset(&tmp, 0, sizeof(tmp));
00389    memset(&trans, 0, sizeof(trans));
00390    memcpy(&trans.addr, sin, sizeof(trans.addr));
00391    tmp.hdr.strans = h->dtrans;
00392    tmp.hdr.dtrans = h->strans;
00393    tmp.hdr.iseqno = h->oseqno;
00394    tmp.hdr.oseqno = h->iseqno;
00395    tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
00396    tmp.hdr.cmdflags = 0;
00397    tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
00398    tmp.pack.datalen = sizeof(struct dundi_hdr);
00399    tmp.pack.parent = &trans;
00400    dundi_xmit(&tmp.pack);
00401 }
00402 
00403 static void reset_global_eid(void)
00404 {
00405 #if defined(SIOCGIFHWADDR)
00406    int x,s;
00407    char eid_str[20];
00408    struct ifreq ifr;
00409 
00410    s = socket(AF_INET, SOCK_STREAM, 0);
00411    if (s > 0) {
00412       x = 0;
00413       for(x=0;x<10;x++) {
00414          memset(&ifr, 0, sizeof(ifr));
00415          snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
00416          if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
00417             memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
00418             ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
00419             close(s);
00420             return;
00421          }
00422         }
00423       close(s);
00424    }
00425 #else
00426 #if defined(ifa_broadaddr) && !defined(SOLARIS)
00427    char eid_str[20];
00428    struct ifaddrs *ifap;
00429    
00430    if (getifaddrs(&ifap) == 0) {
00431       struct ifaddrs *p;
00432       for (p = ifap; p; p = p->ifa_next) {
00433          if ((p->ifa_addr->sa_family == AF_LINK) && !(p->ifa_flags & IFF_LOOPBACK) && (p->ifa_flags & IFF_RUNNING)) {
00434             struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
00435             memcpy(&(global_eid.eid), sdp->sdl_data + sdp->sdl_nlen, 6);
00436             ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s' using 'getifaddrs'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), p->ifa_name);
00437             freeifaddrs(ifap);
00438             return;
00439          }
00440       }
00441       freeifaddrs(ifap);
00442    }
00443 #endif
00444 #endif
00445    ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID. You will have to set it manually.\n");
00446 }
00447 
00448 static int get_trans_id(void)
00449 {
00450    struct dundi_transaction *t;
00451    int stid = (ast_random() % 32766) + 1;
00452    int tid = stid;
00453 
00454    do {
00455       AST_LIST_TRAVERSE(&alltrans, t, all) {
00456          if (t->strans == tid) 
00457             break;
00458       }
00459       if (!t)
00460          return tid;
00461       tid = (tid % 32766) + 1;
00462    } while (tid != stid);
00463 
00464    return 0;
00465 }
00466 
00467 static int reset_transaction(struct dundi_transaction *trans)
00468 {
00469    int tid;
00470    tid = get_trans_id();
00471    if (tid < 1)
00472       return -1;
00473    trans->strans = tid;
00474    trans->dtrans = 0;
00475    trans->iseqno = 0;
00476    trans->oiseqno = 0;
00477    trans->oseqno = 0;
00478    trans->aseqno = 0;
00479    ast_clear_flag(trans, FLAG_FINAL);  
00480    return 0;
00481 }
00482 
00483 static struct dundi_peer *find_peer(dundi_eid *eid)
00484 {
00485    struct dundi_peer *cur = NULL;
00486 
00487    if (!eid)
00488       eid = &empty_eid;
00489    
00490    AST_LIST_TRAVERSE(&peers, cur, list) {
00491       if (!dundi_eid_cmp(&cur->eid,eid))
00492          break;
00493    }
00494 
00495    if (!cur && any_peer)
00496       cur = any_peer;
00497 
00498    return cur;
00499 }
00500 
00501 static void build_iv(unsigned char *iv)
00502 {
00503    /* XXX Would be nice to be more random XXX */
00504    unsigned int *fluffy;
00505    int x;
00506    fluffy = (unsigned int *)(iv);
00507    for (x=0;x<4;x++)
00508       fluffy[x] = ast_random();
00509 }
00510 
00511 struct dundi_query_state {
00512    dundi_eid *eids[DUNDI_MAX_STACK + 1]; 
00513    int directs[DUNDI_MAX_STACK + 1]; 
00514    dundi_eid reqeid;
00515    char called_context[AST_MAX_EXTENSION];
00516    char called_number[AST_MAX_EXTENSION];
00517    struct dundi_mapping *maps;
00518    int nummaps;
00519    int nocache;
00520    struct dundi_transaction *trans;
00521    void *chal;
00522    int challen;
00523    int ttl;
00524    char fluffy[0];
00525 };
00526 
00527 static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd)
00528 {
00529    struct ast_flags flags = {0};
00530    int x;
00531    if (!ast_strlen_zero(map->lcontext)) {
00532       if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
00533          ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
00534       if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
00535          ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
00536       if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
00537          ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
00538       if (ast_ignore_pattern(map->lcontext, called_number))
00539          ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
00540 
00541       /* Clearly we can't say 'don't ask' anymore if we found anything... */
00542       if (ast_test_flag(&flags, AST_FLAGS_ALL)) 
00543          ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
00544 
00545       if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
00546          /* Skip partial answers */
00547          ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
00548       }
00549       if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
00550          struct varshead headp;
00551          struct ast_var_t *newvariable;
00552          ast_set_flag(&flags, map->options & 0xffff);
00553          ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
00554          dr[anscnt].techint = map->tech;
00555          dr[anscnt].weight = map->weight;
00556          dr[anscnt].expiration = dundi_cache_time;
00557          ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
00558          dr[anscnt].eid = *us_eid;
00559          dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
00560          if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
00561             AST_LIST_HEAD_INIT_NOLOCK(&headp);
00562             newvariable = ast_var_assign("NUMBER", called_number);
00563             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00564             newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
00565             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00566             newvariable = ast_var_assign("SECRET", cursecret);
00567             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00568             newvariable = ast_var_assign("IPADDR", ipaddr);
00569             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00570             pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
00571             while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
00572                ast_var_delete(newvariable);
00573          } else
00574             dr[anscnt].dest[0] = '\0';
00575          anscnt++;
00576       } else {
00577          /* No answers...  Find the fewest number of digits from the
00578             number for which we have no answer. */
00579          char tmp[AST_MAX_EXTENSION + 1] = "";
00580          for (x = 0; x < (sizeof(tmp) - 1); x++) {
00581             tmp[x] = called_number[x];
00582             if (!tmp[x])
00583                break;
00584             if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
00585                /* Oops found something we can't match.  If this is longer
00586                   than the running hint, we have to consider it */
00587                if (strlen(tmp) > strlen(hmd->exten)) {
00588                   ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
00589                }
00590                break;
00591             }
00592          }
00593       }
00594    }
00595    return anscnt;
00596 }
00597 
00598 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
00599 
00600 static void *dundi_lookup_thread(void *data)
00601 {
00602    struct dundi_query_state *st = data;
00603    struct dundi_result dr[MAX_RESULTS];
00604    struct dundi_ie_data ied;
00605    struct dundi_hint_metadata hmd;
00606    char eid_str[20];
00607    int res, x;
00608    int ouranswers=0;
00609    int max = 999999;
00610    int expiration = dundi_cache_time;
00611 
00612    ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00613       st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00614    memset(&ied, 0, sizeof(ied));
00615    memset(&dr, 0, sizeof(dr));
00616    memset(&hmd, 0, sizeof(hmd));
00617    /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
00618    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00619    for (x=0;x<st->nummaps;x++)
00620       ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
00621    if (ouranswers < 0)
00622       ouranswers = 0;
00623    for (x=0;x<ouranswers;x++) {
00624       if (dr[x].weight < max)
00625          max = dr[x].weight;
00626    }
00627       
00628    if (max) {
00629       /* If we do not have a canonical result, keep looking */
00630       res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs);
00631       if (res > 0) {
00632          /* Append answer in result */
00633          ouranswers += res;
00634       } else {
00635          if ((res < -1) && (!ouranswers))
00636             dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
00637       }
00638    }
00639    AST_LIST_LOCK(&peers);
00640    /* Truncate if "don't ask" isn't present */
00641    if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00642       hmd.exten[0] = '\0';
00643    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00644       ast_log(LOG_DEBUG, "Our transaction went away!\n");
00645       st->trans->thread = 0;
00646       destroy_trans(st->trans, 0);
00647    } else {
00648       for (x=0;x<ouranswers;x++) {
00649          /* Add answers */
00650          if (dr[x].expiration && (expiration > dr[x].expiration))
00651             expiration = dr[x].expiration;
00652          dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
00653       }
00654       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00655       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
00656       dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
00657       st->trans->thread = 0;
00658    }
00659    AST_LIST_UNLOCK(&peers);
00660    free(st);
00661    return NULL;   
00662 }
00663 
00664 static void *dundi_precache_thread(void *data)
00665 {
00666    struct dundi_query_state *st = data;
00667    struct dundi_ie_data ied;
00668    struct dundi_hint_metadata hmd;
00669    char eid_str[20];
00670 
00671    ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00672       st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00673    memset(&ied, 0, sizeof(ied));
00674 
00675    /* Now produce precache */
00676    dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
00677 
00678    AST_LIST_LOCK(&peers);
00679    /* Truncate if "don't ask" isn't present */
00680    if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00681       hmd.exten[0] = '\0';
00682    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00683       ast_log(LOG_DEBUG, "Our transaction went away!\n");
00684       st->trans->thread = 0;
00685       destroy_trans(st->trans, 0);
00686    } else {
00687       dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
00688       st->trans->thread = 0;
00689    }
00690    AST_LIST_UNLOCK(&peers);
00691    free(st);
00692    return NULL;   
00693 }
00694 
00695 static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[]);
00696 
00697 static void *dundi_query_thread(void *data)
00698 {
00699    struct dundi_query_state *st = data;
00700    struct dundi_entity_info dei;
00701    struct dundi_ie_data ied;
00702    struct dundi_hint_metadata hmd;
00703    char eid_str[20];
00704    int res;
00705    ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00706       st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00707    memset(&ied, 0, sizeof(ied));
00708    memset(&dei, 0, sizeof(dei));
00709    memset(&hmd, 0, sizeof(hmd));
00710    if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
00711       /* Ooh, it's us! */
00712       ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
00713       ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
00714       ast_copy_string(dei.org, org, sizeof(dei.org));
00715       ast_copy_string(dei.locality, locality, sizeof(dei.locality));
00716       ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
00717       ast_copy_string(dei.country, country, sizeof(dei.country));
00718       ast_copy_string(dei.email, email, sizeof(dei.email));
00719       ast_copy_string(dei.phone, phone, sizeof(dei.phone));
00720       res = 1;
00721    } else {
00722       /* If we do not have a canonical result, keep looking */
00723       res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
00724    }
00725    AST_LIST_LOCK(&peers);
00726    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00727       ast_log(LOG_DEBUG, "Our transaction went away!\n");
00728       st->trans->thread = 0;
00729       destroy_trans(st->trans, 0);
00730    } else {
00731       if (res) {
00732          dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
00733          dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
00734          dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
00735          dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
00736          dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
00737          dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
00738          dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
00739          if (!ast_strlen_zero(dei.ipaddr))
00740             dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
00741       }
00742       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00743       dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00744       st->trans->thread = 0;
00745    }
00746    AST_LIST_UNLOCK(&peers);
00747    free(st);
00748    return NULL;   
00749 }
00750 
00751 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00752 {
00753    struct dundi_query_state *st;
00754    int totallen;
00755    int x;
00756    int skipfirst=0;
00757    struct dundi_ie_data ied;
00758    char eid_str[20];
00759    char *s;
00760    pthread_t lookupthread;
00761    pthread_attr_t attr;
00762    if (ies->eidcount > 1) {
00763       /* Since it is a requirement that the first EID is the authenticating host
00764          and the last EID is the root, it is permissible that the first and last EID
00765          could be the same.  In that case, we should go ahead copy only the "root" section
00766          since we will not need it for authentication. */
00767       if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00768          skipfirst = 1;
00769    }
00770    totallen = sizeof(struct dundi_query_state);
00771    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00772    st = ast_calloc(1, totallen);
00773    if (st) {
00774       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00775       memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
00776       st->trans = trans;
00777       st->ttl = ies->ttl - 1;
00778       if (st->ttl < 0)
00779          st->ttl = 0;
00780       s = st->fluffy;
00781       for (x=skipfirst;ies->eids[x];x++) {
00782          st->eids[x-skipfirst] = (dundi_eid *)s;
00783          *st->eids[x-skipfirst] = *ies->eids[x];
00784          s += sizeof(dundi_eid);
00785       }
00786       ast_log(LOG_DEBUG, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
00787       pthread_attr_init(&attr);
00788       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00789       trans->thread = 1;
00790       if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
00791          trans->thread = 0;
00792          ast_log(LOG_WARNING, "Unable to create thread!\n");
00793          free(st);
00794          memset(&ied, 0, sizeof(ied));
00795          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
00796          dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00797          pthread_attr_destroy(&attr);
00798