Wed Oct 28 15:47:55 2009

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 - 2005, 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 
00025 #include <stdlib.h>
00026 #include <stdio.h>
00027 #include <unistd.h>
00028 #include <netinet/in.h>
00029 #include <arpa/inet.h>
00030 #include <sys/socket.h>
00031 #include <string.h>
00032 #include <errno.h>
00033 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
00034 #include <sys/types.h>
00035 #include <netinet/in_systm.h>
00036 #endif
00037 #include <netinet/ip.h>
00038 #include <sys/ioctl.h>
00039 #include <netinet/in.h>
00040 #include <net/if.h>
00041 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
00042 #include <net/if_dl.h>
00043 #include <ifaddrs.h>
00044 #endif
00045 #include <zlib.h>
00046 
00047 #include "asterisk.h"
00048 
00049 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211526 $")
00050 
00051 #include "asterisk/file.h"
00052 #include "asterisk/logger.h"
00053 #include "asterisk/channel.h"
00054 #include "asterisk/config.h"
00055 #include "asterisk/options.h"
00056 #include "asterisk/pbx.h"
00057 #include "asterisk/module.h"
00058 #include "asterisk/frame.h"
00059 #include "asterisk/file.h"
00060 #include "asterisk/cli.h"
00061 #include "asterisk/lock.h"
00062 #include "asterisk/md5.h"
00063 #include "asterisk/dundi.h"
00064 #include "asterisk/sched.h"
00065 #include "asterisk/io.h"
00066 #include "asterisk/utils.h"
00067 #include "asterisk/crypto.h"
00068 #include "asterisk/astdb.h"
00069 #include "asterisk/acl.h"
00070 #include "asterisk/aes.h"
00071 
00072 #include "dundi-parser.h"
00073 
00074 #define MAX_RESULTS  64
00075 
00076 #define MAX_PACKET_SIZE 8192
00077 
00078 extern char ast_config_AST_KEY_DIR[];
00079 
00080 static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
00081 
00082 static char *app = "DUNDiLookup";
00083 static char *synopsis = "Look up a number with DUNDi";
00084 static char *descrip = 
00085 "DUNDiLookup(number[|context[|options]])\n"
00086 "      Looks up a given number in the global context specified or in\n"
00087 "the reserved 'e164' context if not specified.  Returns -1 if the channel\n"
00088 "is hungup during the lookup or 0 otherwise.  On completion, the variable\n"
00089 "${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
00090 "of the appropriate technology and destination to access the number. If no\n"
00091 "answer was found, and the priority n + 101 exists, execution will continue\n"
00092 "at that location. Note that this will only occur if the global priority\n"
00093 "jumping option is enabled in extensions.conf. If the 'b' option is specified,\n"
00094 "the internal DUNDi cache will by bypassed.\n";
00095 
00096 #define DUNDI_MODEL_INBOUND      (1 << 0)
00097 #define DUNDI_MODEL_OUTBOUND  (1 << 1)
00098 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
00099 
00100 /* Keep times of last 10 lookups */
00101 #define DUNDI_TIMING_HISTORY  10
00102 
00103 #define FLAG_ISREG      (1 << 0)    /* Transaction is register request */
00104 #define FLAG_DEAD    (1 << 1)    /* Transaction is dead */
00105 #define FLAG_FINAL      (1 << 2)    /* Transaction has final message sent */
00106 #define FLAG_ISQUAL     (1 << 3)    /* Transaction is a qualification */
00107 #define FLAG_ENCRYPT (1 << 4)    /* Transaction is encrypted wiht ECX/DCX */
00108 #define FLAG_SENDFULLKEY   (1 << 5)    /* Send full key on transaction */
00109 #define FLAG_STOREHIST  (1 << 6)    /* Record historic performance */
00110 
00111 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
00112 
00113 #if 0
00114 #define DUNDI_SECRET_TIME 15  /* Testing only */
00115 #else
00116 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
00117 #endif
00118 
00119 #define KEY_OUT         0
00120 #define KEY_IN       1
00121 
00122 static struct io_context *io;
00123 static struct sched_context *sched;
00124 static int netsocket = -1;
00125 static pthread_t netthreadid = AST_PTHREADT_NULL;
00126 static pthread_t precachethreadid = AST_PTHREADT_NULL;
00127 static int tos = 0;
00128 static int dundidebug = 0;
00129 static int authdebug = 0;
00130 static int dundi_ttl = DUNDI_DEFAULT_TTL;
00131 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
00132 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
00133 static int global_autokilltimeout = 0;
00134 static dundi_eid global_eid;
00135 static int default_expiration = 60;
00136 static int global_storehistory = 0;
00137 static char dept[80];
00138 static char org[80];
00139 static char locality[80];
00140 static char stateprov[80];
00141 static char country[80];
00142 static char email[80];
00143 static char phone[80];
00144 static char secretpath[80];
00145 static char cursecret[80];
00146 static char ipaddr[80];
00147 static time_t rotatetime;
00148 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
00149 struct permission {
00150    struct permission *next;
00151    int allow;
00152    char name[0];
00153 };
00154 
00155 struct dundi_packet {
00156    struct dundi_hdr *h;
00157    struct dundi_packet *next;
00158    int datalen;
00159    struct dundi_transaction *parent;
00160    int retransid;
00161    int retrans;
00162    unsigned char data[0];
00163 };
00164 
00165 struct dundi_hint_metadata {
00166    unsigned short flags;
00167    char exten[AST_MAX_EXTENSION];
00168 };
00169 
00170 struct dundi_precache_queue {
00171    struct dundi_precache_queue *next;
00172    char *context;
00173    time_t expiration;
00174    char number[0];
00175 };
00176 
00177 struct dundi_request;
00178 
00179 struct dundi_transaction {
00180    struct sockaddr_in addr;   /* Other end of transaction */
00181    struct timeval start;      /* When this transaction was created */
00182    dundi_eid eids[DUNDI_MAX_STACK + 1];
00183    int eidcount;           /* Number of eids in eids */
00184    dundi_eid us_eid;       /* Our EID, to them */
00185    dundi_eid them_eid;        /* Their EID, to us */
00186    aes_encrypt_ctx   ecx;     /* AES 128 Encryption context */
00187    aes_decrypt_ctx   dcx;     /* AES 128 Decryption context */
00188    unsigned int flags;           /* Has final packet been sent */
00189    int ttl;             /* Remaining TTL for queries on this one */
00190    int thread;             /* We have a calling thread */
00191    int retranstimer;       /* How long to wait before retransmissions */
00192    int autokillid;            /* ID to kill connection if answer doesn't come back fast enough */
00193    int autokilltimeout;    /* Recommended timeout for autokill */
00194    unsigned short strans;     /* Our transaction identifier */
00195    unsigned short dtrans;     /* Their transaction identifer */
00196    unsigned char iseqno;      /* Next expected received seqno */
00197    unsigned char oiseqno;     /* Last received incoming seqno */
00198    unsigned char oseqno;      /* Next transmitted seqno */
00199    unsigned char aseqno;      /* Last acknowledge seqno */
00200    struct dundi_packet *packets; /* Packets to be retransmitted */
00201    struct dundi_packet *lasttrans;  /* Last transmitted / ACK'd packet */
00202    struct dundi_transaction *next;  /* Next with respect to the parent */
00203    struct dundi_request *parent; /* Parent request (if there is one) */
00204    struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
00205 } *alltrans;
00206 
00207 struct dundi_request {
00208    char dcontext[AST_MAX_EXTENSION];
00209    char number[AST_MAX_EXTENSION];
00210    dundi_eid query_eid;
00211    dundi_eid root_eid;
00212    struct dundi_result *dr;
00213    struct dundi_entity_info *dei;
00214    struct dundi_hint_metadata *hmd;
00215    int maxcount;
00216    int respcount;
00217    int expiration;
00218    int cbypass;
00219    int pfds[2];
00220    unsigned long crc32;                   /* CRC-32 of all but root EID's in avoid list */
00221    struct dundi_transaction *trans; /* Transactions */
00222    struct dundi_request *next;
00223 } *requests;
00224 
00225 static struct dundi_mapping {
00226    char dcontext[AST_MAX_EXTENSION];
00227    char lcontext[AST_MAX_EXTENSION];
00228    int weight;
00229    int options;
00230    int tech;
00231    int dead;
00232    char dest[AST_MAX_EXTENSION];
00233    struct dundi_mapping *next;
00234 } *mappings = NULL;
00235 
00236 static struct dundi_peer {
00237    dundi_eid eid;
00238    struct sockaddr_in addr;   /* Address of DUNDi peer */
00239    struct permission *permit;
00240    struct permission *include;
00241    struct permission *precachesend;
00242    struct permission *precachereceive;
00243    dundi_eid us_eid;
00244    char inkey[80];
00245    char outkey[80];
00246    int dead;
00247    int registerid;
00248    int qualifyid;
00249    int sentfullkey;
00250    int order;
00251    unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
00252    unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
00253    unsigned long us_keycrc32; /* CRC-32 of our key */
00254    aes_encrypt_ctx   us_ecx;     /* Cached AES 128 Encryption context */
00255    aes_decrypt_ctx   us_dcx;     /* Cached AES 128 Decryption context */
00256    unsigned long them_keycrc32;/* CRC-32 of our key */
00257    aes_encrypt_ctx   them_ecx;   /* Cached AES 128 Encryption context */
00258    aes_decrypt_ctx   them_dcx;   /* Cached AES 128 Decryption context */
00259    time_t keyexpire;       /* When to expire/recreate key */
00260    int registerexpire;
00261    int lookuptimes[DUNDI_TIMING_HISTORY];
00262    char *lookups[DUNDI_TIMING_HISTORY];
00263    int avgms;
00264    struct dundi_transaction *regtrans; /* Registration transaction */
00265    struct dundi_transaction *qualtrans;   /* Qualify transaction */
00266    struct dundi_transaction *keypending;
00267    int model;              /* Pull model */
00268    int pcmodel;            /* Push/precache model */
00269    int dynamic;            /* Are we dynamic? */
00270    int lastms;             /* Last measured latency */
00271    int maxms;              /* Max permissible latency */
00272    struct timeval qualtx;     /* Time of transmit */
00273    struct dundi_peer *next;
00274 } *peers;
00275 
00276 static struct dundi_precache_queue *pcq;
00277 
00278 AST_MUTEX_DEFINE_STATIC(peerlock);
00279 AST_MUTEX_DEFINE_STATIC(pclock);
00280 
00281 static int dundi_xmit(struct dundi_packet *pack);
00282 
00283 static void dundi_debug_output(const char *data)
00284 {
00285    if (dundidebug)
00286       ast_verbose("%s", data);
00287 }
00288 
00289 static void dundi_error_output(const char *data)
00290 {
00291    ast_log(LOG_WARNING, "%s", data);
00292 }
00293 
00294 static int has_permission(struct permission *ps, char *cont)
00295 {
00296    int res=0;
00297    while(ps) {
00298       if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
00299          res = ps->allow;
00300       ps = ps->next;
00301    }
00302    return res;
00303 }
00304 
00305 static char *tech2str(int tech)
00306 {
00307    switch(tech) {
00308    case DUNDI_PROTO_NONE:
00309       return "None";
00310    case DUNDI_PROTO_IAX:
00311       return "IAX2";
00312    case DUNDI_PROTO_SIP:
00313       return "SIP";
00314    case DUNDI_PROTO_H323:
00315       return "H323";
00316    default:
00317       return "Unknown";
00318    }
00319 }
00320 
00321 static int str2tech(char *str)
00322 {
00323    if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 
00324       return DUNDI_PROTO_IAX;
00325    else if (!strcasecmp(str, "SIP"))
00326       return DUNDI_PROTO_SIP;
00327    else if (!strcasecmp(str, "H323"))
00328       return DUNDI_PROTO_H323;
00329    else
00330       return -1;
00331 }
00332 
00333 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[]);
00334 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
00335 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
00336 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
00337 {
00338    /* Look for an exact match first */
00339    struct dundi_transaction *trans;
00340    trans = alltrans;
00341    while(trans) {
00342       if (!inaddrcmp(&trans->addr, sin) && 
00343            ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
00344            ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
00345            if (hdr->strans)
00346               trans->dtrans = ntohs(hdr->strans) & 32767;
00347            break;
00348       }
00349       trans = trans->allnext;
00350    }
00351    if (!trans) {
00352       switch(hdr->cmdresp & 0x7f) {
00353       case DUNDI_COMMAND_DPDISCOVER:
00354       case DUNDI_COMMAND_EIDQUERY:
00355       case DUNDI_COMMAND_PRECACHERQ:
00356       case DUNDI_COMMAND_REGREQ:
00357       case DUNDI_COMMAND_NULL:
00358       case DUNDI_COMMAND_ENCRYPT:
00359          if (hdr->strans) {   
00360             /* Create new transaction */
00361             trans = create_transaction(NULL);
00362             if (trans) {
00363                memcpy(&trans->addr, sin, sizeof(trans->addr));
00364                trans->dtrans = ntohs(hdr->strans) & 32767;
00365             } else
00366                ast_log(LOG_WARNING, "Out of memory!\n");
00367          }
00368          break;
00369       default:
00370          break;
00371       }
00372    }
00373    return trans;
00374 }
00375 
00376 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
00377 
00378 static int dundi_ack(struct dundi_transaction *trans, int final)
00379 {
00380    return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
00381 }
00382 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
00383 {
00384    struct {
00385       struct dundi_packet pack;
00386       struct dundi_hdr hdr;
00387    } tmp;
00388    struct dundi_transaction trans;
00389    /* Never respond to an INVALID with another INVALID */
00390    if (h->cmdresp == DUNDI_COMMAND_INVALID)
00391       return;
00392    memset(&tmp, 0, sizeof(tmp));
00393    memset(&trans, 0, sizeof(trans));
00394    memcpy(&trans.addr, sin, sizeof(trans.addr));
00395    tmp.hdr.strans = h->dtrans;
00396    tmp.hdr.dtrans = h->strans;
00397    tmp.hdr.iseqno = h->oseqno;
00398    tmp.hdr.oseqno = h->iseqno;
00399    tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
00400    tmp.hdr.cmdflags = 0;
00401    tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
00402    tmp.pack.datalen = sizeof(struct dundi_hdr);
00403    tmp.pack.parent = &trans;
00404    dundi_xmit(&tmp.pack);
00405 }
00406 
00407 static void reset_global_eid(void)
00408 {
00409 #if defined(SIOCGIFHWADDR)
00410    int x,s;
00411    char eid_str[20];
00412    struct ifreq ifr;
00413 
00414    s = socket(AF_INET, SOCK_STREAM, 0);
00415    if (s > 0) {
00416       x = 0;
00417       for(x=0;x<10;x++) {
00418          memset(&ifr, 0, sizeof(ifr));
00419          snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
00420          if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
00421             memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
00422             ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
00423             close(s);
00424             return;
00425          }
00426         }
00427       close(s);
00428    }
00429 #else
00430 #if defined(ifa_broadaddr) && !defined(SOLARIS)
00431    char eid_str[20];
00432    struct ifaddrs *ifap;
00433    
00434    if (getifaddrs(&ifap) == 0) {
00435       struct ifaddrs *p;
00436       for (p = ifap; p; p = p->ifa_next) {
00437          if (p->ifa_addr->sa_family == AF_LINK) {
00438             struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
00439             memcpy(
00440                &(global_eid.eid),
00441                sdp->sdl_data + sdp->sdl_nlen, 6);
00442             ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifap->ifa_name);
00443             freeifaddrs(ifap);
00444             return;
00445          }
00446       }
00447       freeifaddrs(ifap);
00448    }
00449 #endif
00450 #endif
00451    ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID  You will have to set it manually.\n");
00452 }
00453 
00454 static int get_trans_id(void)
00455 {
00456    struct dundi_transaction *t;
00457    int stid = (rand() % 32766) + 1;
00458    int tid = stid;
00459    do {
00460       t = alltrans;
00461       while(t) {
00462          if (t->strans == tid) 
00463             break;
00464          t = t->allnext;
00465       }
00466       if (!t)
00467          return tid;
00468       tid = (tid % 32766) + 1;
00469    } while (tid != stid);
00470    return 0;
00471 }
00472 
00473 static int reset_transaction(struct dundi_transaction *trans)
00474 {
00475    int tid;
00476    tid = get_trans_id();
00477    if (tid < 1)
00478       return -1;
00479    trans->strans = tid;
00480    trans->dtrans = 0;
00481    trans->iseqno = 0;
00482    trans->oiseqno = 0;
00483    trans->oseqno = 0;
00484    trans->aseqno = 0;
00485    ast_clear_flag(trans, FLAG_FINAL);  
00486    return 0;
00487 }
00488 
00489 static struct dundi_peer *find_peer(dundi_eid *eid)
00490 {
00491    struct dundi_peer *cur;
00492    if (!eid)
00493       eid = &empty_eid;
00494    cur = peers;
00495    while(cur) {
00496       if (!dundi_eid_cmp(&cur->eid,eid))
00497          return cur;
00498       cur = cur->next;
00499    }
00500    return NULL;
00501 }
00502 
00503 static void build_iv(unsigned char *iv)
00504 {
00505    /* XXX Would be nice to be more random XXX */
00506    unsigned int *fluffy;
00507    int x;
00508    fluffy = (unsigned int *)(iv);
00509    for (x=0;x<4;x++)
00510       fluffy[x] = rand();
00511 }
00512 
00513 struct dundi_query_state {
00514    dundi_eid *eids[DUNDI_MAX_STACK + 1]; 
00515    int directs[DUNDI_MAX_STACK + 1]; 
00516    dundi_eid reqeid;
00517    char called_context[AST_MAX_EXTENSION];
00518    char called_number[AST_MAX_EXTENSION];
00519    struct dundi_mapping *maps;
00520    int nummaps;
00521    int nocache;
00522    struct dundi_transaction *trans;
00523    void *chal;
00524    int challen;
00525    int ttl;
00526    char fluffy[0];
00527 };
00528 
00529 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)
00530 {
00531    struct ast_flags flags = {0};
00532    int x;
00533    if (!ast_strlen_zero(map->lcontext)) {
00534       if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
00535          ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
00536       if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
00537          ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
00538       if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
00539          ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
00540       if (ast_ignore_pattern(map->lcontext, called_number))
00541          ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
00542 
00543       /* Clearly we can't say 'don't ask' anymore if we found anything... */
00544       if (ast_test_flag(&flags, AST_FLAGS_ALL)) 
00545          ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
00546 
00547       if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
00548          /* Skip partial answers */
00549          ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
00550       }
00551       if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
00552          struct varshead headp;
00553          struct ast_var_t *newvariable;
00554          ast_set_flag(&flags, map->options & 0xffff);
00555          ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
00556          dr[anscnt].techint = map->tech;
00557          dr[anscnt].weight = map->weight;
00558          dr[anscnt].expiration = dundi_cache_time;
00559          ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
00560          dr[anscnt].eid = *us_eid;
00561          dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
00562          if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
00563             AST_LIST_HEAD_INIT_NOLOCK(&headp);
00564             newvariable = ast_var_assign("NUMBER", called_number);
00565             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00566             newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
00567             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00568             newvariable = ast_var_assign("SECRET", cursecret);
00569             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00570             newvariable = ast_var_assign("IPADDR", ipaddr);
00571             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00572             pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
00573             while (!AST_LIST_EMPTY(&headp)) {           /* List Deletion. */
00574                newvariable = AST_LIST_REMOVE_HEAD(&headp, entries);
00575                ast_var_delete(newvariable);
00576             }
00577          } else
00578             dr[anscnt].dest[0] = '\0';
00579          anscnt++;
00580       } else {
00581          /* No answers...  Find the fewest number of digits from the
00582             number for which we have no answer. */
00583          char tmp[AST_MAX_EXTENSION];
00584          for (x=0;x<AST_MAX_EXTENSION;x++) {
00585             tmp[x] = called_number[x];
00586             if (!tmp[x])
00587                break;
00588             if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
00589                /* Oops found something we can't match.  If this is longer
00590                   than the running hint, we have to consider it */
00591                if (strlen(tmp) > strlen(hmd->exten)) {
00592                   ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
00593                }
00594                break;
00595             }
00596          }
00597       }
00598    }
00599    return anscnt;
00600 }
00601 
00602 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
00603 
00604 static void *dundi_lookup_thread(void *data)
00605 {
00606    struct dundi_query_state *st = data;
00607    struct dundi_result dr[MAX_RESULTS];
00608    struct dundi_ie_data ied;
00609    struct dundi_hint_metadata hmd;
00610    char eid_str[20];
00611    int res, x;
00612    int ouranswers=0;
00613    int max = 999999;
00614    int expiration = dundi_cache_time;
00615 
00616    ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00617       st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00618    memset(&ied, 0, sizeof(ied));
00619    memset(&dr, 0, sizeof(dr));
00620    memset(&hmd, 0, sizeof(hmd));
00621    /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
00622    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00623    for (x=0;x<st->nummaps;x++)
00624       ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
00625    if (ouranswers < 0)
00626       ouranswers = 0;
00627    for (x=0;x<ouranswers;x++) {
00628       if (dr[x].weight < max)
00629          max = dr[x].weight;
00630    }
00631       
00632    if (max) {
00633       /* If we do not have a canonical result, keep looking */
00634       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);
00635       if (res > 0) {
00636          /* Append answer in result */
00637          ouranswers += res;
00638       } else {
00639          if ((res < -1) && (!ouranswers))
00640             dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
00641       }
00642    }
00643    ast_mutex_lock(&peerlock);
00644    /* Truncate if "don't ask" isn't present */
00645    if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00646       hmd.exten[0] = '\0';
00647    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00648       ast_log(LOG_DEBUG, "Our transaction went away!\n");
00649       st->trans->thread = 0;
00650       destroy_trans(st->trans, 0);
00651    } else {
00652       for (x=0;x<ouranswers;x++) {
00653          /* Add answers */
00654          if (dr[x].expiration && (expiration > dr[x].expiration))
00655             expiration = dr[x].expiration;
00656          dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
00657       }
00658       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00659       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
00660       dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
00661       st->trans->thread = 0;
00662    }
00663    ast_mutex_unlock(&peerlock);
00664    free(st);
00665    return NULL;   
00666 }
00667 
00668 static void *dundi_precache_thread(void *data)
00669 {
00670    struct dundi_query_state *st = data;
00671    struct dundi_ie_data ied;
00672    struct dundi_hint_metadata hmd;
00673    char eid_str[20];
00674 
00675    ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00676       st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00677    memset(&ied, 0, sizeof(ied));
00678 
00679    /* Now produce precache */
00680    dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
00681 
00682    ast_mutex_lock(&peerlock);
00683    /* Truncate if "don't ask" isn't present */
00684    if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00685       hmd.exten[0] = '\0';
00686    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00687       ast_log(LOG_DEBUG, "Our transaction went away!\n");
00688       st->trans->thread = 0;
00689       destroy_trans(st->trans, 0);
00690    } else {
00691       dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
00692       st->trans->thread = 0;
00693    }
00694    ast_mutex_unlock(&peerlock);
00695    free(st);
00696    return NULL;   
00697 }
00698 
00699 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[]);
00700 
00701 static void *dundi_query_thread(void *data)
00702 {
00703    struct dundi_query_state *st = data;
00704    struct dundi_entity_info dei;
00705    struct dundi_ie_data ied;
00706    struct dundi_hint_metadata hmd;
00707    char eid_str[20];
00708    int res;
00709    ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00710       st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00711    memset(&ied, 0, sizeof(ied));
00712    memset(&dei, 0, sizeof(dei));
00713    memset(&hmd, 0, sizeof(hmd));
00714    if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
00715       /* Ooh, it's us! */
00716       ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
00717       ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
00718       ast_copy_string(dei.org, org, sizeof(dei.org));
00719       ast_copy_string(dei.locality, locality, sizeof(dei.locality));
00720       ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
00721       ast_copy_string(dei.country, country, sizeof(dei.country));
00722       ast_copy_string(dei.email, email, sizeof(dei.email));
00723       ast_copy_string(dei.phone, phone, sizeof(dei.phone));
00724       res = 1;
00725    } else {
00726       /* If we do not have a canonical result, keep looking */
00727       res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
00728    }
00729    ast_mutex_lock(&peerlock);
00730    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00731       ast_log(LOG_DEBUG, "Our transaction went away!\n");
00732       st->trans->thread = 0;
00733       destroy_trans(st->trans, 0);
00734    } else {
00735       if (res) {
00736          dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
00737          dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
00738          dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
00739          dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
00740          dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
00741          dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
00742          dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
00743          if (!ast_strlen_zero(dei.ipaddr))
00744             dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
00745       }
00746       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00747       dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00748       st->trans->thread = 0;
00749    }
00750    ast_mutex_unlock(&peerlock);
00751    free(st);
00752    return NULL;   
00753 }
00754 
00755 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00756 {
00757    struct dundi_query_state *st;
00758    int totallen;
00759    int x;
00760    int skipfirst=0;
00761    struct dundi_ie_data ied;
00762    char eid_str[20];
00763    char *s;
00764    pthread_t lookupthread;
00765    pthread_attr_t attr;
00766    if (ies->eidcount > 1) {
00767       /* Since it is a requirement that the first EID is the authenticating host
00768          and the last EID is the root, it is permissible that the first and last EID
00769          could be the same.  In that case, we should go ahead copy only the "root" section
00770          since we will not need it for authentication. */
00771       if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00772          skipfirst = 1;
00773    }
00774    totallen = sizeof(struct dundi_query_state);
00775    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00776    st = malloc(totallen);
00777    if (st) {
00778       memset(st, 0, totallen);
00779       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00780       memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
00781       st->trans = trans;
00782       st->ttl = ies->ttl - 1;
00783       if (st->ttl < 0)
00784          st->ttl = 0;
00785       s = st->fluffy;
00786       for (x=skipfirst;ies->eids[x];x++) {
00787          st->eids[x-skipfirst] = (dundi_eid *)s;
00788          *st->eids[x-skipfirst] = *ies->eids[x];
00789          s += sizeof(dundi_eid);
00790       }
00791       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);
00792       pthread_attr_init(&attr);
00793       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00794       trans->thread = 1;
00795       if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
00796          trans->thread = 0;
00797          ast_log(LOG_WARNING, "Unable to create thread!\n");
00798          free(st);
00799          memset(&ied, 0, sizeof(ied));
00800          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
00801          dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00802          pthread_attr_destroy(&attr);
00803          return -1;
00804       }
00805       pthread_attr_destroy(&attr);
00806    } else {
00807       ast_log(LOG_WARNING, "Out of memory!\n");
00808       memset(&ied, 0, sizeof(ied));
00809       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
00810       dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00811       return -1;
00812    }
00813    return 0;
00814 }
00815 
00816 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
00817 {
00818    int unaffected;
00819    char key1[256];
00820    char key2[256];
00821    char eidpeer_str[20];
00822    char eidroot_str[20];
00823    char data[80];
00824    time_t timeout;
00825 
00826    if (expiration < 0)
00827       expiration = dundi_cache_time;
00828 
00829    /* Only cache hint if "don't ask" is there... */
00830    if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))   
00831       return 0;
00832 
00833    unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
00834 
00835    dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00836    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00837    snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
00838    snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
00839 
00840    time(&timeout);
00841    timeout += expiration;
00842    snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00843    
00844    ast_db_put("dundi/cache", key1, data);
00845    ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
00846    ast_db_put("dundi/cache", key2, data);
00847    ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
00848    return 0;
00849 }
00850 
00851 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
00852 {
00853    int x;
00854    char key1[256];
00855    char key2[256];
00856    char data[1024];
00857    char eidpeer_str[20];
00858    char eidroot_str[20];
00859    time_t timeout;
00860 
00861    if (expiration < 1)  
00862       expiration = dundi_cache_time;
00863 
00864    /* Keep pushes a little longer, cut pulls a little short */
00865    if (push)
00866       expiration += 10;
00867    else
00868       expiration -= 10;
00869    if (expiration < 1)
00870       expiration = 1;
00871    dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00872    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00873    snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
00874    snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
00875    /* Build request string */
00876    time(&timeout);
00877    timeout += expiration;
00878    snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00879    for (x=start;x<req->respcount;x++) {
00880       /* Skip anything with an illegal pipe in it */
00881       if (strchr(req->dr[x].dest, '|'))
00882          continue;
00883       snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 
00884          req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 
00885          dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
00886    }
00887    ast_db_put("dundi/cache", key1, data);
00888    ast_db_put("dundi/cache", key2, data);
00889    return 0;
00890 }
00891 
00892 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00893 {
00894    struct dundi_query_state *st;
00895    int totallen;
00896    int x,z;
00897    struct dundi_ie_data ied;
00898    char *s;
00899    struct dundi_result dr2[MAX_RESULTS];
00900    struct dundi_request dr;
00901    struct dundi_hint_metadata hmd;
00902 
00903    struct dundi_mapping *cur;
00904    int mapcount;
00905    int skipfirst = 0;
00906    
00907    pthread_t lookupthread;
00908    pthread_attr_t attr;
00909 
00910    memset(&dr2, 0, sizeof(dr2));
00911    memset(&dr, 0, sizeof(dr));
00912    memset(&hmd, 0, sizeof(hmd));
00913    
00914    /* Forge request structure to hold answers for cache */
00915    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00916    dr.dr = dr2;
00917    dr.maxcount = MAX_RESULTS;
00918    dr.expiration = dundi_cache_time;
00919    dr.hmd = &hmd;
00920    dr.pfds[0] = dr.pfds[1] = -1;
00921    trans->parent = &dr;
00922    ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
00923    ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
00924    
00925    for (x=0;x<ies->anscount;x++) {
00926       if (trans->parent->respcount < trans->parent->maxcount) {
00927          /* Make sure it's not already there */
00928          for (z=0;z<trans->parent->respcount;z++) {
00929             if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
00930                 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data)) 
00931                   break;
00932          }
00933          if (z == trans->parent->respcount) {
00934             /* Copy into parent responses */
00935             trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
00936             trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
00937             trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
00938             trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
00939             if (ies->expiration > 0)
00940                trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
00941             else
00942                trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
00943             dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
00944                sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
00945                &ies->answers[x]->eid);
00946             ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
00947                sizeof(trans->parent->dr[trans->parent->respcount].dest));
00948                ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
00949                sizeof(trans->parent->dr[trans->parent->respcount].tech));
00950             trans->parent->respcount++;
00951             ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);   
00952          } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
00953             /* Update weight if appropriate */
00954             trans->parent->dr[z].weight = ies->answers[x]->weight;
00955          }
00956       } else
00957          ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
00958             trans->parent->number, trans->parent->dcontext);
00959 
00960    }
00961    /* Save all the results (if any) we had.  Even if no results, still cache lookup. */
00962    cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
00963    if (ies->hint)
00964       cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
00965 
00966    totallen = sizeof(struct dundi_query_state);
00967    /* Count matching map entries */
00968    mapcount = 0;
00969    cur = mappings;
00970    while(cur) {
00971       if (!strcasecmp(cur->dcontext, ccontext))
00972          mapcount++;
00973       cur = cur->next;
00974    }
00975    
00976    /* If no maps, return -1 immediately */
00977    if (!mapcount)
00978       return -1;
00979 
00980    if (ies->eidcount > 1) {
00981       /* Since it is a requirement that the first EID is the authenticating host
00982          and the last EID is the root, it is permissible that the first and last EID
00983          could be the same.  In that case, we should go ahead copy only the "root" section
00984          since we will not need it for authentication. */
00985       if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00986          skipfirst = 1;
00987    }
00988 
00989    /* Prepare to run a query and then propagate that as necessary */
00990    totallen += mapcount * sizeof(struct dundi_mapping);
00991    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00992    st = malloc(totallen);
00993    if (st) {
00994       memset(st, 0, totallen);
00995       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00996       ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
00997       st->trans = trans;
00998       st->ttl = ies->ttl - 1;
00999       st->nocache = ies->cbypass;
01000       if (st->ttl < 0)
01001          st->ttl = 0;
01002       s = st->fluffy;
01003       for (x=skipfirst;ies->eids[x];x++) {
01004          st->eids[x-skipfirst] = (dundi_eid *)s;
01005          *st->eids[x-skipfirst] = *ies->eids[x];
01006          st->directs[x-skipfirst] = ies->eid_direct[x];
01007          s += sizeof(dundi_eid);
01008       }
01009       /* Append mappings */
01010       x = 0;
01011       st->maps = (struct dundi_mapping *)s;
01012       cur = mappings;
01013       while(cur) {
01014          if (!strcasecmp(cur->dcontext, ccontext)) {
01015             if (x < mapcount) {
01016                st->maps[x] = *cur;
01017                st->maps[x].next = NULL;
01018                x++;
01019             }
01020          }
01021          cur = cur->next;
01022       }
01023       st->nummaps = mapcount;
01024       ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
01025       pthread_attr_init(&attr);
01026       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01027       trans->thread = 1;
01028       if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
01029          trans->thread = 0;
01030          ast_log(LOG_WARNING, "Unable to create thread!\n");
01031          free(st);
01032          memset(&ied, 0, sizeof(ied));
01033          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01034          dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01035          pthread_attr_destroy(&attr);
01036          return -1;
01037       }
01038       pthread_attr_destroy(&attr);
01039    } else {
01040       ast_log(LOG_WARNING, "Out of memory!\n");
01041       memset(&ied, 0, sizeof(ied));
01042       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01043       dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01044       return -1;
01045    }
01046    return 0;
01047 }
01048 
01049 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
01050 {
01051    struct dundi_query_state *st;
01052    int totallen;
01053    int x;
01054    struct dundi_ie_data ied;
01055    char *s;
01056    struct dundi_mapping *cur;
01057    int mapcount;
01058    int skipfirst = 0;
01059    
01060    pthread_t lookupthread;
01061    pthread_attr_t attr;
01062    totallen = sizeof(struct dundi_query_state);
01063    /* Count matching map entries */
01064    mapcount = 0;
01065    cur = mappings;
01066    while(cur) {
01067       if (!strcasecmp(cur->dcontext, ccontext))
01068          mapcount++;
01069       cur = cur->next;
01070    }
01071    /* If no maps, return -1 immediately */
01072    if (!mapcount)
01073       return -1;
01074 
01075    if (ies->eidcount > 1) {
01076       /* Since it is a requirement that the first EID is the authenticating host
01077          and the last EID is the root, it is permissible that the first and last EID
01078          could be the same.  In that case, we should go ahead copy only the "root" section
01079          since we will not need it for authentication. */
01080       if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
01081          skipfirst = 1;
01082    }
01083 
01084    totallen += mapcount * sizeof(struct dundi_mapping);
01085    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
01086    st = malloc(totallen);
01087    if (st) {
01088       memset(st, 0, totallen);
01089       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
01090       ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
01091       st->trans = trans;
01092       st->ttl = ies->ttl - 1;
01093       st->nocache = ies->cbypass;
01094       if (st->ttl < 0)
01095          st->ttl = 0;
01096       s = st->fluffy;
01097       for (x=skipfirst;ies->eids[x];x++) {
01098          st->eids[x-skipfirst] = (dundi_eid *)s;
01099          *st->eids[x-skipfirst] = *ies->eids[x];
01100          st->directs[x-skipfirst] = ies->eid_direct[x];
01101          s += sizeof(dundi_eid);
01102       }
01103       /* Append mappings */
01104       x = 0;
01105       st->maps = (struct dundi_mapping *)s;
01106       cur = mappings;
01107       while(cur) {
01108          if (!strcasecmp(cur->dcontext, ccontext)) {
01109             if (x < mapcount) {
01110                st->maps[x] = *cur;
01111                st->maps[x].next = NULL;
01112                x++;
01113             }
01114          }
01115          cur = cur->next;
01116       }
01117       st->nummaps = mapcount;
01118       ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
01119       pthread_attr_init(&attr);
01120       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01121       trans->thread = 1;
01122       if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
01123          trans->thread = 0;
01124          ast_log(LOG_WARNING, "Unable to create thread!\n");
01125          free(st);
01126          memset(&ied, 0, sizeof(ied));
01127          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01128          dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01129          pthread_attr_destroy(&attr);
01130          return -1;
01131       }
01132       pthread_attr_destroy(&attr);
01133    } else {
01134       ast_log(LOG_WARNING, "Out of memory!\n");
01135       memset(&ied, 0, sizeof(ied));
01136       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01137       dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01138       return -1;
01139    }
01140    return 0;
01141 }
01142 
01143 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
01144 {
01145    char data[1024];
01146    char *ptr, *term, *src;
01147    int tech;
01148    struct ast_flags flags;
01149    int weight;
01150    int length;
01151    int z;
01152    int expiration;
01153    char fs[256];
01154    time_t timeout;
01155    /* Build request string */
01156    if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
01157       ptr = data;
01158       if (sscanf(ptr, "%30d|%n", (int *)&timeout, &length) == 1) {
01159          expiration = timeout - now;
01160          if (expiration > 0) {
01161             ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
01162             ptr += length;
01163             while((sscanf(ptr, "%30d/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
01164                ptr += length;
01165                term = strchr(ptr, '|');
01166                if (term) {
01167                   *term = '\0';
01168                   src = strrchr(ptr, '/');
01169                   if (src) {
01170                      *src = '\0';
01171                      src++;
01172                   } else
01173                      src = "";
01174                   ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n", 
01175                      tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
01176                   /* Make sure it's not already there */
01177                   for (z=0;z<req->respcount;z++) {
01178                      if ((req->dr[z].techint == tech) &&
01179                          !strcmp(req->dr[z].dest, ptr)) 
01180                            break;
01181                   }
01182                   if (z == req->respcount) {
01183                      /* Copy into parent responses */
01184                      ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);   
01185                      req->dr[req->respcount].weight = weight;
01186                      req->dr[req->respcount].techint = tech;
01187                      req->dr[req->respcount].expiration = expiration;
01188                      dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
01189                      dundi_eid_to_str(req->dr[req->respcount].eid_str, 
01190                         sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
01191                      ast_copy_string(req->dr[req->respcount].dest, ptr,
01192                         sizeof(req->dr[req->respcount].dest));
01193                      ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
01194                         sizeof(req->dr[req->respcount].tech));
01195                      req->respcount++;
01196                      ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK); 
01197                   } else if (req->dr[z].weight > weight)
01198                      req->dr[z].weight = weight;
01199                   ptr = term + 1;
01200                }
01201             }
01202             /* We found *something* cached */
01203             if (expiration < *lowexpiration)
01204                *lowexpiration = expiration;
01205             return 1;
01206          } else 
01207             ast_db_del("dundi/cache", key);
01208       } else 
01209          ast_db_del("dundi/cache", key);
01210    }
01211       
01212    return 0;
01213 }
01214 
01215 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
01216 {
01217    char key[256];
01218    char eid_str[20];
01219    char eidroot_str[20];
01220    time_t now;
01221    int res=0;
01222    int res2=0;
01223    char eid_str_full[20];
01224    char tmp[256]="";
01225    int x;
01226 
01227    time(&now);
01228    dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
01229    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
01230    dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
01231    snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
01232    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01233    snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
01234    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01235    snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
01236    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01237    x = 0;
01238    if (!req->respcount) {
01239       while(!res2) {
01240          /* Look and see if we have a hint that would preclude us from looking at this
01241             peer for this number. */
01242          if (!(tmp[x] = req->number[x])) 
01243             break;
01244          x++;
01245          /* Check for hints */
01246          snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
01247          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01248          snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
01249          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01250          snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
01251          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01252          if (res2) {
01253             if (strlen(tmp) > strlen(req->hmd->exten)) {
01254                /* Update meta data if appropriate */
01255                ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
01256             }
01257          }
01258       }
01259       res |= res2;
01260    }
01261 
01262    return res;
01263 }
01264 
01265 static void qualify_peer(struct dundi_peer *peer, int schedonly);
01266 
01267 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
01268 {
01269    if (!trans->addr.sin_addr.s_addr)
01270       memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
01271    trans->us_eid = p->us_eid;
01272    trans->them_eid = p->eid;
01273    /* Enable encryption if appropriate */
01274    if (!ast_strlen_zero(p->inkey))
01275       ast_set_flag(trans, FLAG_ENCRYPT);  
01276    if (p->maxms) {
01277       trans->autokilltimeout = p->maxms;
01278       trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01279       if (p->lastms > 1) {
01280          trans->retranstimer = p->lastms * 2;
01281          /* Keep it from being silly */
01282          if (trans->retranstimer < 150)
01283             trans->retranstimer = 150;
01284       }
01285       if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
01286          trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01287    } else
01288       trans->autokilltimeout = global_autokilltimeout;
01289 }
01290 
01291 static int do_register_expire(void *data)
01292 {
01293    struct dundi_peer *peer = data;
01294    char eid_str[20];
01295    /* Called with peerlock already held */
01296    ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01297    peer->registerexpire = -1;
01298    peer->lastms = 0;
01299    memset(&peer->addr, 0, sizeof(peer->addr));
01300    return 0;
01301 }
01302 
01303 static int update_key(struct dundi_peer *peer)
01304 {
01305    unsigned char key[16];
01306    struct ast_key *ekey, *skey;
01307    char eid_str[20];
01308    int res;
01309    if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
01310       build_iv(key);
01311       aes_encrypt_key128(key, &peer->us_ecx);
01312       aes_decrypt_key128(key, &peer->us_dcx);
01313       ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01314       if (!ekey) {
01315          ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
01316             peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01317          return -1;
01318       }
01319       skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01320       if (!skey) {
01321          ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
01322             peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01323          return -1;
01324       }
01325       if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
01326          ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
01327          return -1;
01328       }
01329       if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
01330          ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
01331          return -1;
01332       }
01333       peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
01334       peer->sentfullkey = 0;
01335       /* Looks good */
01336       time(&peer->keyexpire);
01337       peer->keyexpire += dundi_key_ttl;
01338    }
01339    return 0;
01340 }
01341 
01342 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx) 
01343 {
01344    unsigned char curblock[16];
01345    int x;
01346    memcpy(curblock, iv, sizeof(curblock));
01347    while(len > 0) {
01348       for (x=0;x<16;x++)
01349          curblock[x] ^= src[x];
01350       aes_encrypt(curblock, dst, ecx);
01351       memcpy(curblock, dst, sizeof(curblock)); 
01352       dst += 16;
01353       src += 16;
01354       len -= 16;
01355    }
01356    return 0;
01357 }
01358 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx) 
01359 {
01360    unsigned char lastblock[16];
01361    int x;
01362    memcpy(lastblock, iv, sizeof(lastblock));
01363    while(len > 0) {
01364       aes_decrypt(src, dst, dcx);
01365       for (x=0;x<16;x++)
01366          dst[x] ^= lastblock[x];
01367       memcpy(lastblock, src, sizeof(lastblock));
01368       dst += 16;
01369       src += 16;
01370       len -= 16;
01371    }
01372    return 0;
01373 }
01374 
01375 static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
01376 {
01377    int space = *dstlen;
01378    unsigned long bytes;
01379    struct dundi_hdr *h;
01380    unsigned char *decrypt_space;
01381    decrypt_space = alloca(srclen);
01382    if (!decrypt_space)
01383       return NULL;
01384    decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
01385    /* Setup header */
01386    h = (struct dundi_hdr *)dst;
01387    *h = *ohdr;
01388    bytes = space - 6;
01389    if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
01390       ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
01391       return NULL;
01392    }
01393    /* Update length */
01394    *dstlen = bytes + 6;
01395    /* Return new header */
01396    return h;
01397 }
01398 
01399 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
01400 {
01401    unsigned char *compress_space;
01402    int len;
01403    int res;
01404    unsigned long bytes;
01405    struct dundi_ie_data ied;
01406    struct dundi_peer *peer;
01407    unsigned char iv[16];
01408    len = pack->datalen + pack->datalen / 100 + 42;
01409    compress_space = alloca(len);
01410    if (compress_space) {
01411       memset(compress_space, 0, len);
01412       /* We care about everthing save the first 6 bytes of header */
01413       bytes = len;
01414       res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
01415       if (res != Z_OK) {
01416          ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
01417          return -1;
01418       }
01419       memset(&ied, 0, sizeof(ied));
01420       /* Say who we are */
01421       if (!pack->h->iseqno && !pack->h->oseqno) {
01422          /* Need the key in the first copy */
01423          if (!(peer = find_peer(&trans->them_eid))) 
01424             return -1;
01425          if (update_key(peer))
01426             return -1;
01427          if (!peer->sentfullkey)
01428             ast_set_flag(trans, FLAG_SENDFULLKEY); 
01429          /* Append key data */
01430          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01431          if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
01432             dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01433             dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01434          } else {
01435             dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
01436          }
01437          /* Setup contexts */
01438          trans->ecx = peer->us_ecx;
01439          trans->dcx = peer->us_dcx;
01440 
01441          /* We've sent the full key */
01442          peer->sentfullkey = 1;
01443       }
01444       /* Build initialization vector */
01445       build_iv(iv);
01446       /* Add the field, rounded up to 16 bytes */
01447       dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
01448       /* Copy the data */
01449       if ((ied.pos + bytes) >= sizeof(ied.buf)) {
01450          ast_log(LOG_NOTICE, "Final packet too large!\n");
01451          return -1;
01452       }
01453       encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
01454       ied.pos += ((bytes + 15) / 16) * 16;
01455       /* Reconstruct header */
01456       pack->datalen = sizeof(struct dundi_hdr);
01457       pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
01458       pack->h->cmdflags = 0;
01459       memcpy(pack->h->ies, ied.buf, ied.pos);
01460       pack->datalen += ied.pos;
01461       return 0;
01462    }
01463    return -1;
01464 }
01465 
01466 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
01467 {
01468    unsigned char dst[128];
01469    int res;
01470    struct ast_key *key, *skey;
01471    char eid_str[20];
01472    if (option_debug)
01473       ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
01474    if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
01475       /* A match */
01476       return 1;
01477    } else if (!newkey || !newsig)
01478       return 0;
01479    if (!memcmp(peer->rxenckey, newkey, 128) &&
01480        !memcmp(peer->rxenckey + 128, newsig, 128)) {
01481       /* By definition, a match */
01482       return 1;
01483    }
01484    /* Decrypt key */
01485    key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01486    if (!key) {
01487       ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
01488          peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01489       return -1;
01490    }
01491 
01492    skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01493    if (!skey) {
01494       ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
01495          peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01496       return -1;
01497    }
01498 
01499    /* First check signature */
01500    res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
01501    if (res) 
01502       return 0;
01503 
01504    res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
01505    if (res != 16) {
01506       if (res >= 0)
01507          ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
01508       return 0;
01509    }
01510    /* Decrypted, passes signature */
01511    ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
01512    memcpy(peer->rxenckey, newkey, 128);
01513    memcpy(peer->rxenckey + 128, newsig, 128);
01514    peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
01515    aes_decrypt_key128(dst, &peer->them_dcx);
01516    aes_encrypt_key128(dst, &peer->them_ecx);
01517    return 1;
01518 }
01519 
01520 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
01521 {
01522    /* Handle canonical command / response */
01523    int final = hdr->cmdresp & 0x80;
01524    int cmd = hdr->cmdresp & 0x7f;
01525    int x,y,z;
01526    int resp;
01527    int res;
01528    int authpass=0;
01529    unsigned char *bufcpy;
01530    struct dundi_ie_data ied;
01531    struct dundi_ies ies;
01532    struct dundi_peer *peer;
01533    char eid_str[20];
01534    char eid_str2[20];
01535    memset(&ied, 0, sizeof(ied));
01536    memset(&ies, 0, sizeof(ies));
01537    if (datalen) {
01538       bufcpy = alloca(datalen);
01539       if (!bufcpy)
01540          return -1;
01541       /* Make a copy for parsing */
01542       memcpy(bufcpy, hdr->ies, datalen);
01543       ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
01544       if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
01545          ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
01546          return -1;
01547       }
01548    }
01549    switch(cmd) {
01550    case DUNDI_COMMAND_DPDISCOVER:
01551    case DUNDI_COMMAND_EIDQUERY:
01552    case DUNDI_COMMAND_PRECACHERQ:
01553       if (cmd == DUNDI_COMMAND_EIDQUERY)
01554          resp = DUNDI_COMMAND_EIDRESPONSE;
01555       else if (cmd == DUNDI_COMMAND_PRECACHERQ)
01556          resp = DUNDI_COMMAND_PRECACHERP;
01557       else
01558          resp = DUNDI_COMMAND_DPRESPONSE;
01559       /* A dialplan or entity discover -- qualify by highest level entity */
01560       peer = find_peer(ies.eids[0]);
01561       if (!peer) {
01562          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01563          dundi_send(trans, resp, 0, 1, &ied);
01564       } else {
01565          int hasauth = 0;
01566          trans->us_eid = peer->us_eid;
01567          if (strlen(peer->inkey)) {
01568             hasauth = encrypted;
01569          } else 
01570             hasauth = 1;
01571          if (hasauth) {
01572             /* Okay we're authentiated and all, now we check if they're authorized */
01573             if (!ies.called_context)
01574                ies.called_context = "e164";
01575             if (cmd == DUNDI_COMMAND_EIDQUERY) {
01576                res = dundi_answer_entity(trans, &ies, ies.called_context);
01577             } else {
01578                if (ast_strlen_zero(ies.called_number)) {
01579                   /* They're not permitted to access that context */
01580                   dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
01581                   dundi_send(trans, resp, 0, 1, &ied);
01582                } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) && 
01583                           (peer->model & DUNDI_MODEL_INBOUND) && 
01584                         has_permission(peer->permit, ies.called_context)) {
01585                   res = dundi_answer_query(trans, &ies, ies.called_context);
01586                   if (res < 0) {
01587                      /* There is no such dundi context */
01588                      dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01589                      dundi_send(trans, resp, 0, 1, &ied);
01590                   }
01591                } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) && 
01592                           (peer->pcmodel & DUNDI_MODEL_INBOUND) && 
01593                         has_permission(peer->include, ies.called_context)) {
01594                   res = dundi_prop_precache(trans, &ies, ies.called_context);
01595                   if (res < 0) {
01596                      /* There is no such dundi context */
01597                      dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01598                      dundi_send(trans, resp, 0, 1, &ied);
01599                   }
01600                } else {
01601                   /* They're not permitted to access that context */
01602                   dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
01603                   dundi_send(trans, resp, 0, 1, &ied);
01604                }
01605             }
01606          } else {
01607             /* They're not permitted to access that context */
01608             dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
01609             dundi_send(trans, resp, 0, 1, &ied);
01610          }
01611       }
01612       break;
01613    case DUNDI_COMMAND_REGREQ:
01614       /* A register request -- should only have one entity */
01615       peer = find_peer(ies.eids[0]);
01616       if (!peer || !peer->dynamic) {
01617          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01618          dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
01619       } else {
01620          int hasauth = 0;
01621          trans->us_eid = peer->us_eid;
01622          if (!ast_strlen_zero(peer->inkey)) {
01623             hasauth = encrypted;
01624          } else
01625             hasauth = 1;
01626          if (hasauth) {
01627             int expire = default_expiration;
01628             char iabuf[INET_ADDRSTRLEN];
01629             char data[256];
01630             int needqual = 0;
01631             if (peer->registerexpire > -1)
01632                ast_sched_del(sched, peer->registerexpire);
01633             peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
01634             ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
01635             snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
01636             ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
01637             if (inaddrcmp(&peer->addr, &trans->addr)) {
01638                if (option_verbose > 2)
01639                   ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), iabuf, ntohs(trans->addr.sin_port));
01640                needqual = 1;
01641             }
01642                
01643             memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
01644             dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
01645             dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
01646             if (needqual)
01647                qualify_peer(peer, 1);
01648          }
01649       }
01650       break;
01651    case DUNDI_COMMAND_DPRESPONSE:
01652       /* A dialplan response, lets see what we got... */
01653       if (ies.cause < 1) {
01654          /* Success of some sort */
01655          ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
01656          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01657             authpass = encrypted;
01658          } else 
01659             authpass = 1;
01660          if (authpass) {
01661             /* Pass back up answers */
01662             if (trans->parent && trans->parent->dr) {
01663                y = trans->parent->respcount;
01664                for (x=0;x<ies.anscount;x++) {
01665                   if (trans->parent->respcount < trans->parent->maxcount) {
01666                      /* Make sure it's not already there */
01667                      for (z=0;z<trans->parent->respcount;z++) {
01668                         if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
01669                             !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data)) 
01670                               break;
01671                      }
01672                      if (z == trans->parent->respcount) {
01673                         /* Copy into parent responses */
01674                         trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
01675                         trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
01676                         trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
01677                         trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
01678                         if (ies.expiration > 0)
01679                            trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
01680                         else
01681                            trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
01682                         dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
01683                            sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
01684                            &ies.answers[x]->eid);
01685                         ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
01686                            sizeof(trans->parent->dr[trans->parent->respcount].dest));
01687                         ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
01688                            sizeof(trans->parent->dr[trans->parent->respcount].tech));
01689                         trans->parent->respcount++;
01690                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01691                      } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
01692                         /* Update weight if appropriate */
01693                         trans->parent->dr[z].weight = ies.answers[x]->weight;
01694                      }
01695                   } else
01696                      ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
01697                         trans->parent->number, trans->parent->dcontext);
01698                }
01699                /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
01700                   the cache know if this request was unaffected by our entity list. */
01701                cache_save(&trans->them_eid, trans->parent, y, 
01702                      ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
01703                if (ies.hint) {
01704                   cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
01705                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01706                      ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01707                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) { 
01708                      if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
01709                         ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data, 
01710                            sizeof(trans->parent->hmd->exten));
01711                      }
01712                   } else {
01713                      ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01714                   }
01715                }
01716                if (ies.expiration > 0) {
01717                   if (trans->parent->expiration > ies.expiration) {
01718                      trans->parent->expiration = ies.expiration;
01719                   }
01720                }
01721             }
01722             /* Close connection if not final */
01723             if (!final) 
01724                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01725          }
01726          
01727       } else {
01728          /* Auth failure, check for data */
01729          if (!final) {
01730             /* Cancel if they didn't already */
01731             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01732          }
01733       }
01734       break;
01735    case DUNDI_COMMAND_EIDRESPONSE:
01736       /* A dialplan response, lets see what we got... */
01737       if (ies.cause < 1) {
01738          /* Success of some sort */
01739          ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
01740          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01741             authpass = encrypted;
01742          } else 
01743             authpass = 1;
01744          if (authpass) {
01745             /* Pass back up answers */
01746             if (trans->parent && trans->parent->dei && ies.q_org) {
01747                if (!trans->parent->respcount) {
01748                   trans->parent->respcount++;
01749                   if (ies.q_dept)
01750                      ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
01751                   if (ies.q_org)
01752                      ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
01753                   if (ies.q_locality)
01754                      ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
01755                   if (ies.q_stateprov)
01756                      ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
01757                   if (ies.q_country)
01758                      ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
01759                   if (ies.q_email)
01760                      ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
01761                   if (ies.q_phone)
01762                      ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
01763                   if (ies.q_ipaddr)
01764                      ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
01765                   if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
01766                      /* If it's them, update our address */
01767                      ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
01768                         trans->addr.sin_addr);
01769                   }
01770                }
01771                if (ies.hint) {
01772                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01773                      ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01774                }
01775             }
01776             /* Close connection if not final */
01777             if (!final) 
01778                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01779          }
01780          
01781       } else {
01782          /* Auth failure, check for data */
01783          if (!final) {
01784             /* Cancel if they didn't already */
01785             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01786          }
01787       }
01788       break;
01789    case DUNDI_COMMAND_REGRESPONSE:
01790       /* A dialplan response, lets see what we got... */
01791       if (ies.cause < 1) {
01792          int hasauth;
01793          /* Success of some sort */
01794          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01795             hasauth = encrypted;
01796          } else 
01797             hasauth = 1;
01798          
01799          if (!hasauth) {
01800             ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
01801             if (!final) {
01802                dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
01803                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
01804             }
01805          } else {
01806             ast_log(LOG_DEBUG, "Yay, we've registered as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
01807                      dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
01808             /* Close connection if not final */
01809             if (!final) 
01810                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01811          }
01812       } else {
01813          /* Auth failure, cancel if they didn't for some reason */
01814          if (!final) {
01815             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01816          }
01817       }
01818       break;
01819    case DUNDI_COMMAND_INVALID:
01820    case DUNDI_COMMAND_NULL:
01821    case DUNDI_COMMAND_PRECACHERP:
01822       /* Do nothing special */
01823       if (!final) 
01824          dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01825       break;
01826    case DUNDI_COMMAND_ENCREJ:
01827       if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
01828          /* No really, it's over at this point */
01829          if (!final) 
01830             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01831       } else {
01832          /* Send with full key */
01833          ast_set_flag(trans, FLAG_SENDFULLKEY);
01834          if (final) {
01835             /* Ooops, we got a final message, start by sending ACK... */
01836             dundi_ack(trans, hdr->cmdresp & 0x80);
01837             trans->aseqno = trans->iseqno;
01838             /* Now, we gotta create a new transaction */
01839             if (!reset_transaction(trans)) {
01840                /* Make sure handle_frame doesn't destroy us */
01841                hdr->cmdresp &= 0x7f;
01842                /* Parse the message we transmitted */
01843                memset(&ies, 0, sizeof(ies));
01844                dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
01845                /* Reconstruct outgoing encrypted packet */
01846                memset(&ied, 0, sizeof(ied));
01847                dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01848                dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01849                dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01850                if (ies.encblock) 
01851                   dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
01852                dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
01853                peer->sentfullkey = 1;
01854             }
01855          }
01856       }
01857       break;
01858    case DUNDI_COMMAND_ENCRYPT:
01859       if (!encrypted) {
01860          /* No nested encryption! */
01861          if ((trans->iseqno == 1) && !trans->oseqno) {
01862             if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || 
01863                ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || 
01864                (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
01865                if (!final) {
01866                   dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01867                }
01868                break;
01869             }
01870             apply_peer(trans, peer);
01871             /* Key passed, use new contexts for this session */
01872             trans->ecx = peer->them_ecx;
01873             trans->dcx = peer->them_dcx;
01874          }
01875          if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
01876             struct dundi_hdr *dhdr;
01877             unsigned char decoded[MAX_PACKET_SIZE];
01878             int ddatalen;
01879             ddatalen = sizeof(decoded);
01880             dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
01881             if (dhdr) {
01882                /* Handle decrypted response */
01883                if (dundidebug)
01884                   dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
01885                handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
01886                /* Carry back final flag */
01887                hdr->cmdresp |= dhdr->cmdresp & 0x80;
01888                break;
01889             } else
01890                ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
01891          }
01892       }
01893       if (!final) {
01894          /* Turn off encryption */
01895          ast_clear_flag(trans, FLAG_ENCRYPT);
01896          dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01897       }
01898       break;
01899    default:
01900       /* Send unknown command if we don't know it, with final flag IFF it's the
01901          first command in the dialog and only if we haven't recieved final notification */
01902       if (!final) {
01903          dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
01904          dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
01905       }
01906    }
01907    return 0;
01908 }
01909 
01910 static void destroy_packet(struct dundi_packet *pack, int needfree);
01911 static void destroy_packets(struct dundi_packet *p)
01912 {
01913    struct dundi_packet *prev;
01914    while(p) {
01915       prev = p;
01916       p = p->next;
01917       if (prev->retransid > -1)
01918          ast_sched_del(sched, prev->retransid);
01919       free(prev);
01920    }
01921 }
01922 
01923 
01924 static int ack_trans(struct dundi_transaction *trans, int iseqno)
01925 {
01926    /* Ack transmitted packet corresponding to iseqno */
01927    struct dundi_packet *pack;
01928    pack = trans->packets;
01929    while(pack) {
01930       if ((pack->h->oseqno + 1) % 255 == iseqno) {
01931          destroy_packet(pack, 0);
01932          if (trans->lasttrans) {
01933             ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
01934             destroy_packets(trans->lasttrans);
01935          }
01936          trans->lasttrans = pack;
01937          if (trans->autokillid > -1)
01938             ast_sched_del(sched, trans->autokillid);
01939          trans->autokillid = -1;
01940          return 1;
01941       }
01942       pack = pack->next;
01943    }
01944    return 0;
01945 }
01946 
01947 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
01948 {
01949    struct dundi_transaction *trans;
01950    trans = find_transaction(h, sin);
01951    if (!trans) {
01952       dundi_reject(h, sin);
01953       return 0;
01954    }
01955    /* Got a transaction, see where this header fits in */
01956    if (h->oseqno == trans->iseqno) {
01957       /* Just what we were looking for...  Anything but ack increments iseqno */
01958       if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
01959          /* If final, we're done */
01960          destroy_trans(trans, 0);
01961          return 0;
01962       }
01963       if (h->cmdresp != DUNDI_COMMAND_ACK) {
01964          trans->oiseqno = trans->iseqno;
01965          trans->iseqno++;
01966          handle_command_response(trans, h, datalen, 0);
01967       }
01968       if (trans->aseqno != trans->iseqno) {
01969          dundi_ack(trans, h->cmdresp & 0x80);
01970          trans->aseqno = trans->iseqno;
01971       }
01972       /* Delete any saved last transmissions */
01973       destroy_packets(trans->lasttrans);
01974       trans->lasttrans = NULL;
01975       if (h->cmdresp & 0x80) {
01976          /* Final -- destroy now */
01977          destroy_trans(trans, 0);
01978       }
01979    } else if (h->oseqno == trans->oiseqno) {
01980       /* Last incoming sequence number -- send ACK without processing */
01981       dundi_ack(trans, 0);
01982    } else {
01983       /* Out of window -- simply drop */
01984       ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
01985    }
01986    return 0;
01987 }
01988 
01989 static int socket_read(int *id, int fd, short events, void *cbdata)
01990 {
01991    struct sockaddr_in sin;
01992    int res;
01993    struct dundi_hdr *h;
01994    char buf[MAX_PACKET_SIZE];
01995    socklen_t len;
01996    len = sizeof(sin);
01997    res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
01998    if (res < 0) {
01999       if (errno != ECONNREFUSED)
02000          ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
02001       return 1;
02002    }
02003    if (res < sizeof(struct dundi_hdr)) {
02004       ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
02005       return 1;
02006    }
02007    buf[res] = '\0';
02008    h = (struct dundi_hdr *)buf;
02009    if (dundidebug)
02010       dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
02011    ast_mutex_lock(&peerlock);
02012    handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
02013    ast_mutex_unlock(&peerlock);
02014    return 1;
02015 }
02016 
02017 static void build_secret(char *secret, int seclen)
02018 {
02019    unsigned char tmp[16];
02020    char *s;
02021    build_iv(tmp);
02022    secret[0] = '\0';
02023    ast_base64encode(secret, tmp, sizeof(tmp), seclen);
02024    /* Eliminate potential bad characters */
02025    while((s = strchr(secret, ';'))) *s = '+';
02026    while((s = strchr(secret, '/'))) *s = '+';
02027    while((s = strchr(secret, ':'))) *s = '+';
02028    while((s = strchr(secret, '@'))) *s = '+';
02029 }
02030 
02031 
02032 static void save_secret(const char *newkey, const char *oldkey)
02033 {
02034    char tmp[256];
02035    if (oldkey)
02036       snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
02037    else
02038       snprintf(tmp, sizeof(tmp), "%s", newkey);
02039    rotatetime = time(NULL) + DUNDI_SECRET_TIME;
02040    ast_db_put(secretpath, "secret", tmp);
02041    snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
02042    ast_db_put(secretpath, "secretexpiry", tmp);
02043 }
02044 
02045 static void load_password(void)
02046 {
02047    char *current=NULL;
02048    char *last=NULL;
02049    char tmp[256];
02050    time_t expired;
02051    
02052    ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
02053    if (sscanf(tmp, "%30d", (int *)&expired) == 1) {
02054       ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
02055       current = strchr(tmp, ';');
02056       if (!current)
02057          current = tmp;
02058       else {
02059          *current = '\0';
02060          current++;
02061       };
02062       if ((time(NULL) - expired) < 0) {
02063          if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
02064             expired = time(NULL) + DUNDI_SECRET_TIME;
02065       } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
02066          last = current;
02067          current = NULL;
02068       } else {
02069          last = NULL;
02070          current = NULL;
02071       }
02072    }
02073    if (current) {
02074       /* Current key is still valid, just setup rotatation properly */
02075       ast_copy_string(cursecret, current, sizeof(cursecret));
02076       rotatetime = expired;
02077    } else {
02078       /* Current key is out of date, rotate or eliminate all together */
02079       build_secret(cursecret, sizeof(cursecret));
02080       save_secret(cursecret, last);
02081    }
02082 }
02083 
02084 static void check_password(void)
02085 {
02086    char oldsecret[80];
02087    time_t now;
02088    
02089    time(&now); 
02090 #if 0
02091    printf("%ld/%ld\n", now, rotatetime);
02092 #endif
02093    if ((now - rotatetime) >= 0) {
02094       /* Time to rotate keys */
02095       ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
02096       build_secret(cursecret, sizeof(cursecret));
02097       save_secret(cursecret, oldsecret);
02098    }
02099 }
02100 
02101 static void *network_thread(void *ignore)
02102 {
02103    /* Our job is simple: Send queued messages, retrying if necessary.  Read frames 
02104       from the network, and queue them for delivery to the channels */
02105    int res;
02106    /* Establish I/O callback for socket read */
02107    ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
02108    for(;;) {
02109       res = ast_sched_wait(sched);
02110       if ((res > 1000) || (res < 0))
02111          res = 1000;
02112       res = ast_io_wait(io, res);
02113       if (res >= 0) {
02114          ast_mutex_lock(&peerlock);
02115          ast_sched_runq(sched);
02116          ast_mutex_unlock(&peerlock);
02117       }
02118       check_password();
02119    }
02120    return NULL;
02121 }
02122 
02123 static void *process_precache(void *ign)
02124 {
02125    struct dundi_precache_queue *qe;
02126    time_t now;
02127    char context[256];
02128    char number[256];
02129    int run;
02130    for (;;) {
02131       time(&now);
02132       run = 0;
02133       ast_mutex_lock(&pclock);
02134       if (pcq) {
02135          if (!pcq->expiration) {
02136             /* Gone...  Remove... */
02137             qe = pcq;
02138             pcq = pcq->next;
02139             free(qe);
02140          } else if (pcq->expiration < now) {
02141             /* Process this entry */
02142             pcq->expiration = 0;
02143             ast_copy_string(context, pcq->context, sizeof(context));
02144             ast_copy_string(number, pcq->number, sizeof(number));
02145             run = 1;
02146          }
02147       }
02148       ast_mutex_unlock(&pclock);
02149       if (run) {
02150          dundi_precache(context, number);
02151       } else
02152          sleep(1);
02153    }
02154    return NULL;
02155 }
02156 
02157 static int start_network_thread(void)
02158 {
02159    ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
02160    ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
02161    return 0;
02162 }
02163 
02164 static int dundi_do_debug(int fd, int argc, char *argv[])
02165 {
02166    if (argc != 2)
02167       return RESULT_SHOWUSAGE;
02168    dundidebug = 1;
02169    ast_cli(fd, "DUNDi Debugging Enabled\n");
02170    return RESULT_SUCCESS;
02171 }
02172 
02173 static int dundi_do_store_history(int fd, int argc, char *argv[])
02174 {
02175    if (argc != 3)
02176       return RESULT_SHOWUSAGE;
02177    global_storehistory = 1;
02178    ast_cli(fd, "DUNDi History Storage Enabled\n");
02179    return RESULT_SUCCESS;
02180 }
02181 
02182 static int dundi_flush(int fd, int argc, char *argv[])
02183 {
02184    int stats=0;
02185    if ((argc < 2) || (argc > 3))
02186       return RESULT_SHOWUSAGE;
02187    if (argc > 2) {
02188       if (!strcasecmp(argv[2], "stats"))
02189          stats = 1;
02190       else
02191          return RESULT_SHOWUSAGE;
02192    }
02193    if (stats) {
02194       /* Flush statistics */
02195       struct dundi_peer *p;
02196       int x;
02197       ast_mutex_lock(&peerlock);
02198       p = peers;
02199       while(p) {
02200          for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
02201             if (p->lookups[x])
02202                free(p->lookups[x]);
02203             p->lookups[x] = NULL;
02204             p->lookuptimes[x] = 0;
02205          }
02206          p->avgms = 0;
02207          p = p->next;
02208       }
02209       ast_mutex_unlock(&peerlock);
02210    } else {
02211       ast_db_deltree("dundi/cache", NULL);
02212       ast_cli(fd, "DUNDi Cache Flushed\n");
02213    }
02214    return RESULT_SUCCESS;
02215 }
02216 
02217 static int dundi_no_debug(int fd, int argc, char *argv[])
02218 {
02219    if (argc != 3)
02220       return RESULT_SHOWUSAGE;
02221    dundidebug = 0;
02222    ast_cli(fd, "DUNDi Debugging Disabled\n");
02223    return RESULT_SUCCESS;
02224 }
02225 
02226 static int dundi_no_store_history(int fd, int argc, char *argv[])
02227 {
02228    if (argc != 4)
02229       return RESULT_SHOWUSAGE;
02230    global_storehistory = 0;
02231    ast_cli(fd, "DUNDi History Storage Disabled\n");
02232    return RESULT_SUCCESS;
02233 }
02234 
02235 static char *model2str(int model)
02236 {
02237    switch(model) {
02238    case DUNDI_MODEL_INBOUND:
02239       return "Inbound";
02240    case DUNDI_MODEL_OUTBOUND:
02241       return "Outbound";
02242    case DUNDI_MODEL_SYMMETRIC:
02243       return "Symmetric";
02244    default:
02245       return "Unknown";
02246    }
02247 }
02248 
02249 static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
02250 {
02251    int which=0;
02252    char *ret;
02253    struct dundi_peer *p;
02254    char eid_str[20];
02255    if (pos != rpos)
02256       return NULL;
02257    ast_mutex_lock(&peerlock);
02258    p = peers;
02259    while(p) {
02260       if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
02261          if (++which > state)
02262             break;
02263       }
02264       p = p->next;
02265    }
02266    if (p) {
02267       ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
02268    } else
02269       ret = NULL;
02270    ast_mutex_unlock(&peerlock);
02271    return ret;
02272 }
02273 
02274 static char *complete_peer_4(char *line, char *word, int pos, int state)
02275 {
02276    return complete_peer_helper(line, word, pos, state, 3);
02277 }
02278 
02279 static int rescomp(const void *a, const void *b)
02280 {
02281    const struct dundi_result *resa, *resb;
02282    resa = a;
02283    resb = b;
02284    if (resa->weight < resb->weight)
02285       return -1;
02286    if (resa->weight > resb->weight)
02287       return 1;
02288    return 0;
02289 }
02290 
02291 static void sort_results(struct dundi_result *results, int count)
02292 {
02293    qsort(results, count, sizeof(results[0]), rescomp);
02294 }
02295 
02296 static int dundi_do_lookup(int fd, int argc, char *argv[])
02297 {
02298    int res;
02299    char tmp[256];
02300    char fs[80] = "";
02301    char *context;
02302    int x;
02303    int bypass = 0;
02304    struct dundi_result dr[MAX_RESULTS];
02305    struct timeval start;
02306    if ((argc < 3) || (argc > 4))
02307       return RESULT_SHOWUSAGE;
02308    if (argc > 3) {
02309       if (!strcasecmp(argv[3], "bypass"))
02310          bypass=1;
02311       else
02312          return RESULT_SHOWUSAGE;
02313    }
02314    ast_copy_string(tmp, argv[2], sizeof(tmp));
02315    context = strchr(tmp, '@');
02316    if (context) {
02317       *context = '\0';
02318       context++;
02319    }
02320    start = ast_tvnow();
02321    res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
02322    
02323    if (res < 0) 
02324       ast_cli(fd, "DUNDi lookup returned error.\n");
02325    else if (!res) 
02326       ast_cli(fd, "DUNDi lookup returned no results.\n");
02327    else
02328       sort_results(dr, res);
02329    for (x=0;x<res;x++) {
02330       ast_cli(fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
02331       ast_cli(fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
02332    }
02333    ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02334    return RESULT_SUCCESS;
02335 }
02336 
02337 static int dundi_do_precache(int fd, int argc, char *argv[])
02338 {
02339    int res;
02340    char tmp[256];
02341    char *context;
02342    struct timeval start;
02343    if ((argc < 3) || (argc > 3))
02344       return RESULT_SHOWUSAGE;
02345    ast_copy_string(tmp, argv[2], sizeof(tmp));
02346    context = strchr(tmp, '@');
02347    if (context) {
02348       *context = '\0';
02349       context++;
02350    }
02351    start = ast_tvnow();
02352    res = dundi_precache(context, tmp);
02353    
02354    if (res < 0) 
02355       ast_cli(fd, "DUNDi precache returned error.\n");
02356    else if (!res) 
02357       ast_cli(fd, "DUNDi precache returned no error.\n");
02358    ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02359    return RESULT_SUCCESS;
02360 }
02361 
02362 static int dundi_do_query(int fd, int argc, char *argv[])
02363 {
02364    int res;
02365    char tmp[256];
02366    char *context;
02367    dundi_eid eid;
02368    struct dundi_entity_info dei;
02369    if ((argc < 3) || (argc > 3))
02370       return RESULT_SHOWUSAGE;
02371    if (dundi_str_to_eid(&eid, argv[2])) {
02372       ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
02373       return RESULT_SHOWUSAGE;
02374    }
02375    ast_copy_string(tmp, argv[2], sizeof(tmp));
02376    context = strchr(tmp, '@');
02377    if (context) {
02378       *context = '\0';
02379       context++;
02380    }
02381    res = dundi_query_eid(&dei, context, eid);
02382    if (res < 0) 
02383       ast_cli(fd, "DUNDi Query EID returned error.\n");
02384    else if (!res) 
02385       ast_cli(fd, "DUNDi Query EID returned no results.\n");
02386    else {
02387       ast_cli(fd, "DUNDi Query EID succeeded:\n");
02388       ast_cli(fd, "Department:      %s\n", dei.orgunit);
02389       ast_cli(fd, "Organization:    %s\n", dei.org);
02390       ast_cli(fd, "City/Locality:   %s\n", dei.locality);
02391       ast_cli(fd, "State/Province:  %s\n", dei.stateprov);
02392       ast_cli(fd, "Country:         %s\n", dei.country);
02393       ast_cli(fd, "E-mail:          %s\n", dei.email);
02394       ast_cli(fd, "Phone:           %s\n", dei.phone);
02395       ast_cli(fd, "IP Address:      %s\n", dei.ipaddr);
02396    }
02397    return RESULT_SUCCESS;
02398 }
02399 
02400 static int dundi_show_peer(int fd, int argc, char *argv[])
02401 {
02402    struct dundi_peer *peer;
02403    struct permission *p;
02404    char *order;
02405    char iabuf[INET_ADDRSTRLEN];
02406    char eid_str[20];
02407    int x, cnt;
02408    
02409    if (argc != 4)
02410       return RESULT_SHOWUSAGE;
02411    ast_mutex_lock(&peerlock);
02412    peer = peers;
02413    while(peer) {
02414       if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
02415          break;
02416       peer = peer->next;
02417    }
02418    if (peer) {
02419       switch(peer->order) {
02420       case 0:
02421          order = "Primary";
02422          break;
02423       case 1:
02424          order = "Secondary";
02425          break;
02426       case 2:
02427          order = "Tertiary";
02428          break;
02429       case 3:
02430          order = "Quartiary";
02431          break;
02432       default:
02433          order = "Unknown";
02434       }
02435       ast_cli(fd, "Peer:    %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02436       ast_cli(fd, "Model:   %s\n", model2str(peer->model));
02437       ast_cli(fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
02438       ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
02439       ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
02440       ast_cli(fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
02441       ast_cli(fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
02442       ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
02443       if (peer->include) {
02444          ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
02445       }
02446       p = peer->include;
02447       while(p) {
02448          ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
02449          p = p->next;
02450       }
02451       if (peer->permit) {
02452          ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
02453       }
02454       p = peer->permit;
02455       while(p) {
02456          ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
02457          p = p->next;
02458       }
02459       cnt = 0;
02460       for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
02461          if (peer->lookups[x]) {
02462             if (!cnt)
02463                ast_cli(fd, "Last few query times:\n");
02464             ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
02465             cnt++;
02466          }
02467       }
02468       if (cnt)
02469          ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
02470    } else
02471       ast_cli(fd, "No such peer '%s'\n", argv[3]);
02472    ast_mutex_unlock(&peerlock);
02473    return RESULT_SUCCESS;
02474 }
02475 
02476 static int dundi_show_peers(int fd, int argc, char *argv[])
02477 {
02478 #define FORMAT2 "%-20.20s %-15.15s     %-10.10s %-8.8s %-15.15s\n"
02479 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
02480    struct dundi_peer *peer;
02481    char iabuf[INET_ADDRSTRLEN];
02482    int registeredonly=0;
02483    char avgms[20];
02484    char eid_str[20];
02485    int online_peers = 0;
02486    int offline_peers = 0;
02487    int unmonitored_peers = 0;
02488    int total_peers = 0;
02489 
02490    if ((argc != 3) && (argc != 4) && (argc != 5))
02491       return RESULT_SHOWUSAGE;
02492    if ((argc == 4)) {
02493       if (!strcasecmp(argv[3], "registered")) {
02494          registeredonly = 1;
02495       } else
02496          return RESULT_SHOWUSAGE;
02497    }
02498    ast_mutex_lock(&peerlock);
02499    ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
02500    for (peer = peers;peer;peer = peer->next) {
02501       char status[20];
02502       int print_line = -1;
02503       char srch[2000];
02504       total_peers++;
02505       if (registeredonly && !peer->addr.sin_addr.s_addr)
02506          continue;
02507       if (peer->maxms) {
02508          if (peer->lastms < 0) {
02509             strcpy(status, "UNREACHABLE");
02510             offline_peers++;
02511          }
02512          else if (peer->lastms > peer->maxms) {
02513             snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
02514             offline_peers++;
02515          }
02516          else if (peer->lastms) {
02517             snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
02518             online_peers++;
02519          }
02520          else {
02521             strcpy(status, "UNKNOWN");
02522             offline_peers++;
02523          }
02524       } else {
02525          strcpy(status, "Unmonitored");
02526          unmonitored_peers++;
02527       }
02528       if (peer->avgms) 
02529          snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
02530       else
02531          strcpy(avgms, "Unavail");
02532       snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
02533                peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
02534                peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
02535 
02536                 if (argc == 5) {
02537                   if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
02538                         print_line = -1;
02539                    } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
02540                         print_line = 1;
02541                    } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
02542                         print_line = -1;
02543                    } else {
02544                         print_line = 0;
02545                   }
02546                 }
02547       
02548         if (print_line) {
02549          ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
02550                peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
02551                peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
02552       }
02553    }
02554    ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
02555    ast_mutex_unlock(&peerlock);
02556    return RESULT_SUCCESS;
02557 #undef FORMAT
02558 #undef FORMAT2
02559 }
02560 
02561 static int dundi_show_trans(int fd, int argc, char *argv[])
02562 {
02563 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
02564 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
02565    struct dundi_transaction *trans;
02566    char iabuf[INET_ADDRSTRLEN];
02567    if (argc != 3)
02568       return RESULT_SHOWUSAGE;
02569    ast_mutex_lock(&peerlock);
02570    ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
02571    for (trans = alltrans;trans;trans = trans->allnext) {
02572          ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr), 
02573                ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
02574    }
02575    ast_mutex_unlock(&peerlock);
02576    return RESULT_SUCCESS;
02577 #undef FORMAT
02578 #undef FORMAT2
02579 }
02580 
02581 static int dundi_show_entityid(int fd, int argc, char *argv[])
02582 {
02583    char eid_str[20];
02584    if (argc != 3)
02585       return RESULT_SHOWUSAGE;
02586    ast_mutex_lock(&peerlock);
02587    dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
02588    ast_mutex_unlock(&peerlock);
02589    ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
02590    return RESULT_SUCCESS;
02591 }
02592 
02593 static int dundi_show_requests(int fd, int argc, char *argv[])
02594 {
02595 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
02596 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
02597    struct dundi_request *req;
02598    char eidstr[20];
02599    if (argc != 3)
02600       return RESULT_SHOWUSAGE;
02601    ast_mutex_lock(&peerlock);
02602    ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
02603    for (req = requests;req;req = req->next) {
02604          ast_cli(fd, FORMAT, req->number, req->dcontext,
02605                   dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
02606    }
02607    ast_mutex_unlock(&peerlock);
02608    return RESULT_SUCCESS;
02609 #undef FORMAT
02610 #undef FORMAT2
02611 }
02612 
02613 /* Grok-a-dial DUNDi */
02614 
02615 static int dundi_show_mappings(int fd, int argc, char *argv[])
02616 {
02617 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
02618 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
02619    struct dundi_mapping *map;
02620    char fs[256];
02621    if (argc != 3)
02622       return RESULT_SHOWUSAGE;
02623    ast_mutex_lock(&peerlock);
02624    ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
02625    for (map = mappings;map;map = map->next) {
02626          ast_cli(fd, FORMAT, map->dcontext, map->weight, 
02627                              ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
02628                         dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
02629    }
02630    ast_mutex_unlock(&peerlock);
02631    return RESULT_SUCCESS;
02632 #undef FORMAT
02633 #undef FORMAT2
02634 }
02635 
02636 static int dundi_show_precache(int fd, int argc, char *argv[])
02637 {
02638 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
02639 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
02640    struct dundi_precache_queue *qe;
02641    int h,m,s;
02642    time_t now;
02643    
02644    if (argc != 3)
02645       return RESULT_SHOWUSAGE;
02646    time(&now);
02647    ast_mutex_lock(&pclock);
02648    ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
02649    for (qe = pcq;qe;qe = qe->next) {
02650       s = qe->expiration - now;
02651       h = s / 3600;
02652       s = s % 3600;
02653       m = s / 60;
02654       s = s % 60;
02655       ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
02656    }
02657    ast_mutex_unlock(&pclock);
02658    return RESULT_SUCCESS;
02659 #undef FORMAT
02660 #undef FORMAT2
02661 }
02662 
02663 static char debug_usage[] = 
02664 "Usage: dundi debug\n"
02665 "       Enables dumping of DUNDi packets for debugging purposes\n";
02666 
02667 static char no_debug_usage[] = 
02668 "Usage: dundi no debug\n"
02669 "       Disables dumping of DUNDi packets for debugging purposes\n";
02670 
02671 static char store_history_usage[] = 
02672 "Usage: dundi store history\n"
02673 "       Enables storing of DUNDi requests and times for debugging\n"
02674 "purposes\n";
02675 
02676 static char no_store_history_usage[] = 
02677 "Usage: dundi no store history\n"
02678 "       Disables storing of DUNDi requests and times for debugging\n"
02679 "purposes\n";
02680 
02681 static char show_peers_usage[] = 
02682 "Usage: dundi show peers\n"
02683 "       Lists all known DUNDi peers.\n";
02684 
02685 static char show_trans_usage[] = 
02686 "Usage: dundi show trans\n"
02687 "       Lists all known DUNDi transactions.\n";
02688 
02689 static char show_mappings_usage[] = 
02690 "Usage: dundi show mappings\n"
02691 "       Lists all known DUNDi mappings.\n";
02692 
02693 static char show_precache_usage[] = 
02694 "Usage: dundi show precache\n"
02695 "       Lists all known DUNDi scheduled precache updates.\n";
02696 
02697 static char show_entityid_usage[] = 
02698 "Usage: dundi show entityid\n"
02699 "       Displays the global entityid for this host.\n";
02700 
02701 static char show_peer_usage[] = 
02702 "Usage: dundi show peer [peer]\n"
02703 "       Provide a detailed description of a specifid DUNDi peer.\n";
02704 
02705 static char show_requests_usage[] = 
02706 "Usage: dundi show requests\n"
02707 "       Lists all known pending DUNDi requests.\n";
02708 
02709 static char lookup_usage[] =
02710 "Usage: dundi lookup <number>[@context] [bypass]\n"
02711 "       Lookup the given number within the given DUNDi context\n"
02712 "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
02713 "keyword is specified.\n";
02714 
02715 static char precache_usage[] =
02716 "Usage: dundi precache <number>[@context]\n"
02717 "       Lookup the given number within the given DUNDi context\n"
02718 "(or e164 if none is specified) and precaches the results to any\n"
02719 "upstream DUNDi push servers.\n";
02720 
02721 static char query_usage[] =
02722 "Usage: dundi query <entity>[@context]\n"
02723 "       Attempts to retrieve contact information for a specific\n"
02724 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
02725 "e164 if none is specified).\n";
02726 
02727 static char flush_usage[] =
02728 "Usage: dundi flush [stats]\n"
02729 "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
02730 "'stats' is present, clears timer statistics instead of normal\n"
02731 "operation.\n";
02732 
02733 static struct ast_cli_entry  cli_debug =
02734    { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
02735 
02736 static struct ast_cli_entry  cli_store_history =
02737    { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
02738 
02739 static struct ast_cli_entry  cli_no_store_history =
02740    { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
02741 
02742 static struct ast_cli_entry  cli_flush =
02743    { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
02744 
02745 static struct ast_cli_entry  cli_no_debug =
02746    { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
02747 
02748 static struct ast_cli_entry  cli_show_peers =
02749    { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
02750 
02751 static struct ast_cli_entry  cli_show_trans =
02752    { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
02753 
02754 static struct ast_cli_entry  cli_show_entityid =
02755    { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
02756 
02757 static struct ast_cli_entry  cli_show_mappings =
02758    { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
02759 
02760 static struct ast_cli_entry  cli_show_precache =
02761    { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
02762 
02763 static struct ast_cli_entry  cli_show_requests =
02764    { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
02765 
02766 static struct ast_cli_entry  cli_show_peer =
02767    { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
02768 
02769 static struct ast_cli_entry  cli_lookup =
02770    { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
02771 
02772 static struct ast_cli_entry  cli_precache =
02773    { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
02774 
02775 static struct ast_cli_entry  cli_queryeid =
02776    { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
02777 
02778 STANDARD_LOCAL_USER;
02779 
02780 LOCAL_USER_DECL;
02781 
02782 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
02783 {
02784    struct dundi_transaction *trans;
02785    int tid;
02786    
02787    /* Don't allow creation of transactions to non-registered peers */
02788    if (p && !p->addr.sin_addr.s_addr)
02789       return NULL;
02790    tid = get_trans_id();
02791    if (tid < 1)
02792       return NULL;
02793    trans = malloc(sizeof(struct dundi_transaction));
02794    if (trans) {
02795       memset(trans, 0, sizeof(struct dundi_transaction));
02796       if (global_storehistory) {
02797          trans->start = ast_tvnow();
02798          ast_set_flag(trans, FLAG_STOREHIST);
02799       }
02800       trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
02801       trans->autokillid = -1;
02802       if (p) {
02803          apply_peer(trans, p);
02804          if (!p->sentfullkey)
02805             ast_set_flag(trans, FLAG_SENDFULLKEY);
02806       }
02807       trans->strans = tid;
02808       trans->allnext = alltrans;
02809       alltrans = trans;
02810    }
02811    return trans;
02812 }
02813 
02814 static int dundi_xmit(struct dundi_packet *pack)
02815 {
02816    int res;
02817    char iabuf[INET_ADDRSTRLEN];
02818    if (dundidebug)
02819       dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
02820    res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
02821    if (res < 0) {
02822       ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
02823          ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
02824          ntohs(pack->parent->addr.sin_port), strerror(errno));
02825    }
02826    if (res > 0)
02827       res = 0;
02828    return res;
02829 }
02830 
02831 static void destroy_packet(struct dundi_packet *pack, int needfree)
02832 {
02833    struct dundi_packet *prev, *cur;
02834    if (pack->parent) {
02835       prev = NULL;
02836       cur = pack->parent->packets;
02837       while(cur) {
02838          if (cur == pack) {
02839             if (prev)
02840                prev->next = cur->next;
02841             else
02842                pack->parent->packets = cur->next;
02843             break;
02844          }
02845          prev = cur;
02846          cur = cur->next;
02847       }
02848    }
02849    if (pack->retransid > -1)
02850       ast_sched_del(sched, pack->retransid);
02851    if (needfree)
02852       free(pack);
02853    else {
02854       pack->retransid = -1;
02855       pack->next = NULL;
02856    }
02857 }
02858 
02859 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
02860 {
02861    struct dundi_transaction *cur, *prev;
02862    struct dundi_peer *peer;
02863    int ms;
02864    int x;
02865    int cnt;
02866    char eid_str[20];
02867    if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
02868       peer = peers;
02869       while (peer) {
02870          if (peer->regtrans == trans)
02871             peer->regtrans = NULL;
02872          if (peer->keypending == trans)
02873             peer->keypending = NULL;
02874          if (peer->qualtrans == trans) {
02875             if (fromtimeout) {
02876                if (peer->lastms > -1)
02877                   ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02878                peer->lastms = -1;
02879             } else {
02880                ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
02881                if (ms < 1)
02882                   ms = 1;
02883                if (ms < peer->maxms) {
02884                   if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
02885                      ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02886                } else if (peer->lastms < peer->maxms) {
02887                   ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
02888                }
02889                peer->lastms = ms;
02890             }
02891             peer->qualtrans = NULL;
02892          }
02893          if (ast_test_flag(trans, FLAG_STOREHIST)) {
02894             if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
02895                if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
02896                   peer->avgms = 0;
02897                   cnt = 0;
02898                   if (peer->lookups[DUNDI_TIMING_HISTORY-1])
02899                      free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
02900                   for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
02901                      peer->lookuptimes[x] = peer->lookuptimes[x-1];
02902                      peer->lookups[x] = peer->lookups[x-1];
02903                      if (peer->lookups[x]) {
02904                         peer->avgms += peer->lookuptimes[x];
02905                         cnt++;
02906                      }
02907                   }
02908                   peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
02909                   peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
02910                   if (peer->lookups[0]) {
02911                      sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
02912                      peer->avgms += peer->lookuptimes[0];
02913                      cnt++;
02914                   }
02915                   if (cnt)
02916                      peer->avgms /= cnt;
02917                }
02918             }
02919          }
02920          peer = peer->next;
02921       }
02922    }
02923    if (trans->parent) {
02924       /* Unlink from parent if appropriate */
02925       prev = NULL;
02926       cur = trans->parent->trans;
02927       while(cur) {
02928          if (cur == trans) {
02929             if (prev)
02930                prev->next = trans->next;
02931             else
02932                trans->parent->trans = trans->next;
02933             break;
02934          }
02935          prev = cur;
02936          cur = cur->next;
02937       }
02938       if (!trans->parent->trans) {
02939          /* Wake up sleeper */
02940          if (trans->parent->pfds[1] > -1) {
02941             write(trans->parent->pfds[1], "killa!", 6);
02942          }
02943       }
02944    }
02945    /* Unlink from all trans */
02946    prev = NULL;
02947    cur = alltrans;
02948    while(cur) {
02949       if (cur == trans) {
02950          if (prev)
02951             prev->allnext = trans->allnext;
02952          else
02953             alltrans = trans->allnext;
02954          break;
02955       }
02956       prev = cur;
02957       cur = cur->allnext;
02958    }
02959    destroy_packets(trans->packets);
02960    destroy_packets(trans->lasttrans);
02961    trans->packets = NULL;
02962    trans->lasttrans = NULL;
02963    if (trans->autokillid > -1)
02964       ast_sched_del(sched, trans->autokillid);
02965    trans->autokillid = -1;
02966    if (trans->thread) {
02967       /* If used by a thread, mark as dead and be done */
02968       ast_set_flag(trans, FLAG_DEAD);
02969    } else
02970       free(trans);
02971 }
02972 
02973 static int dundi_rexmit(void *data)
02974 {
02975    struct dundi_packet *pack;
02976    char iabuf[INET_ADDRSTRLEN];
02977    int res;
02978    ast_mutex_lock(&peerlock);
02979    pack = data;
02980    if (pack->retrans < 1) {
02981       pack->retransid = -1;
02982       if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
02983          ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
02984             ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr), 
02985             ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
02986       destroy_trans(pack->parent, 1);
02987       res = 0;
02988    } else {
02989       /* Decrement retransmission, try again */
02990       pack->retrans--;
02991       dundi_xmit(pack);
02992       res = 1;
02993    }
02994    ast_mutex_unlock(&peerlock);
02995    return res;
02996 }
02997 
02998 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
02999 {
03000    struct dundi_packet *pack;
03001    int res;
03002    int len;
03003    char eid_str[20];
03004    len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
03005    /* Reserve enough space for encryption */
03006    if (ast_test_flag(trans, FLAG_ENCRYPT))
03007       len += 384;
03008    pack = malloc(len);
03009    if (pack) {
03010       memset(pack, 0, len);
03011       pack->h = (struct dundi_hdr *)(pack->data);
03012       if (cmdresp != DUNDI_COMMAND_ACK) {
03013          pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
03014          pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
03015          pack->next = trans->packets;
03016          trans->packets = pack;
03017       }
03018       pack->parent = trans;
03019       pack->h->strans = htons(trans->strans);
03020       pack->h->dtrans = htons(trans->dtrans);
03021       pack->h->iseqno = trans->iseqno;
03022       pack->h->oseqno = trans->oseqno;
03023       pack->h->cmdresp = cmdresp;
03024       pack->datalen = sizeof(struct dundi_hdr);
03025       if (ied) {
03026          memcpy(pack->h->ies, ied->buf, ied->pos);
03027          pack->datalen += ied->pos;
03028       } 
03029       if (final) {
03030          pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
03031          ast_set_flag(trans, FLAG_FINAL);
03032       }
03033       pack->h->cmdflags = flags;
03034       if (cmdresp != DUNDI_COMMAND_ACK) {
03035          trans->oseqno++;
03036          trans->oseqno = trans->oseqno % 256;
03037       }
03038       trans->aseqno = trans->iseqno;
03039       /* If we have their public key, encrypt */
03040       if (ast_test_flag(trans, FLAG_ENCRYPT)) {
03041          switch(cmdresp) {
03042          case DUNDI_COMMAND_REGREQ:
03043          case DUNDI_COMMAND_REGRESPONSE:
03044          case DUNDI_COMMAND_DPDISCOVER:
03045          case DUNDI_COMMAND_DPRESPONSE:
03046          case DUNDI_COMMAND_EIDQUERY:
03047          case DUNDI_COMMAND_EIDRESPONSE:
03048          case DUNDI_COMMAND_PRECACHERQ:
03049          case DUNDI_COMMAND_PRECACHERP:
03050             if (dundidebug)
03051                dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
03052             res = dundi_encrypt(trans, pack);
03053             break;
03054          default:
03055             res = 0;
03056          }
03057       } else 
03058          res = 0;
03059       if (!res) 
03060          res = dundi_xmit(pack);
03061       if (res)
03062          ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03063             
03064       if (cmdresp == DUNDI_COMMAND_ACK)
03065          free(pack);
03066       return res;
03067    }
03068    return -1;
03069 }
03070 
03071 static int do_autokill(void *data)
03072 {
03073    struct dundi_transaction *trans = data;
03074    char eid_str[20];
03075    ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 
03076       dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03077    trans->autokillid = -1;
03078    destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
03079    return 0;
03080 }
03081 
03082 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
03083 {
03084    struct dundi_peer *p;
03085    if (!dundi_eid_cmp(eid, us)) {
03086       dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03087       return;
03088    }
03089    ast_mutex_lock(&peerlock);
03090    p = peers;
03091    while(p) {
03092       if (!dundi_eid_cmp(&p->eid, eid)) {
03093          if (has_permission(p->include, context))
03094             dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03095          else
03096             dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03097          break;
03098       }
03099       p = p->next;
03100    }
03101    if (!p)
03102       dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03103    ast_mutex_unlock(&peerlock);
03104 }
03105 
03106 static int dundi_discover(struct dundi_transaction *trans)
03107 {
03108    struct dundi_ie_data ied;
03109    int x;
03110    if (!trans->parent) {
03111       ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03112       return -1;
03113    }
03114    memset(&ied, 0, sizeof(ied));
03115    dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03116    if (!dundi_eid_zero(&trans->us_eid))
03117       dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
03118    for (x=0;x<trans->eidcount;x++)
03119       dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
03120    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03121    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03122    dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03123    if (trans->parent->cbypass)
03124       dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
03125    if (trans->autokilltimeout)
03126       trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03127    return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
03128 }
03129 
03130 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
03131 {
03132    struct dundi_ie_data ied;
03133    int x, res;
03134    int max = 999999;
03135    int expiration = dundi_cache_time;
03136    int ouranswers=0;
03137    dundi_eid *avoid[1] = { NULL, };
03138    int direct[1] = { 0, };
03139    struct dundi_result dr[MAX_RESULTS];
03140    struct dundi_hint_metadata hmd;
03141    if (!trans->parent) {
03142       ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03143       return -1;
03144    }
03145    memset(&hmd, 0, sizeof(hmd));
03146    memset(&dr, 0, sizeof(dr));
03147    /* Look up the answers we're going to include */
03148    for (x=0;x<mapcount;x++)
03149       ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
03150    if (ouranswers < 0)
03151       ouranswers = 0;
03152    for (x=0;x<ouranswers;x++) {
03153       if (dr[x].weight < max)
03154          max = dr[x].weight;
03155    }
03156    if (max) {
03157       /* If we do not have a canonical result, keep looking */
03158       res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
03159       if (res > 0) {
03160          /* Append answer in result */
03161          ouranswers += res;
03162       }
03163    }
03164    
03165    if (ouranswers > 0) {
03166       *foundanswers += ouranswers;
03167       memset(&ied, 0, sizeof(ied));
03168       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03169       if (!dundi_eid_zero(&trans->us_eid))
03170          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03171       for (x=0;x<trans->eidcount;x++)
03172          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03173       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03174       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03175       dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03176       for (x=0;x<ouranswers;x++) {
03177          /* Add answers */
03178          if (dr[x].expiration && (expiration > dr[x].expiration))
03179             expiration = dr[x].expiration;
03180          dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
03181       }
03182       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
03183       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
03184       if (trans->autokilltimeout)
03185          trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03186       if (expiration < *minexp)
03187          *minexp = expiration;
03188       return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
03189    } else {
03190       /* Oops, nothing to send... */
03191       destroy_trans(trans, 0);
03192       return 0;
03193    }
03194 }
03195 
03196 static int dundi_query(struct dundi_transaction *trans)
03197 {
03198    struct dundi_ie_data ied;
03199    int x;
03200    if (!trans->parent) {
03201       ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
03202       return -1;
03203    }
03204    memset(&ied, 0, sizeof(ied));
03205    dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03206    if (!dundi_eid_zero(&trans->us_eid))
03207       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03208    for (x=0;x<trans->eidcount;x++)
03209       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03210    dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
03211    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03212    dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03213    if (trans->autokilltimeout)
03214       trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03215    return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
03216 }
03217 
03218 static int discover_transactions(struct dundi_request *dr)
03219 {
03220    struct dundi_transaction *trans;
03221    ast_mutex_lock(&peerlock);
03222    trans = dr->trans;
03223    while(trans) {
03224       dundi_discover(trans);
03225       trans = trans->next;
03226    }
03227    ast_mutex_unlock(&peerlock);
03228    return 0;
03229 }
03230 
03231 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
03232 {
03233    struct dundi_transaction *trans, *transn;
03234    /* Mark all as "in thread" so they don't disappear */
03235    ast_mutex_lock(&peerlock);
03236    trans = dr->trans;
03237    while(trans) {
03238       if (trans->thread)
03239          ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
03240       trans->thread = 1;
03241       trans = trans->next;
03242    }
03243    ast_mutex_unlock(&peerlock);
03244 
03245    trans = dr->trans;
03246    while(trans) {
03247       if (!ast_test_flag(trans, FLAG_DEAD))
03248          precache_trans(trans, maps, mapcount, expiration, foundanswers);
03249       trans = trans->next;
03250    }
03251 
03252    /* Cleanup any that got destroyed in the mean time */
03253    ast_mutex_lock(&peerlock);
03254    trans = dr->trans;
03255    while(trans) {
03256       transn = trans->next;
03257       trans->thread = 0;
03258       if (ast_test_flag(trans, FLAG_DEAD)) {
03259          ast_log(LOG_DEBUG, "Our transaction went away!\n");
03260          destroy_trans(trans, 0);
03261       }
03262       trans = transn;
03263    }
03264    ast_mutex_unlock(&peerlock);
03265    return 0;
03266 }
03267 
03268 static int query_transactions(struct dundi_request *dr)
03269 {
03270    struct dundi_transaction *trans;
03271    ast_mutex_lock(&peerlock);
03272    trans = dr->trans;
03273    while(trans) {
03274       dundi_query(trans);
03275       trans = trans->next;
03276    }
03277    ast_mutex_unlock(&peerlock);
03278    return 0;
03279 }
03280 
03281 static int optimize_transactions(struct dundi_request *dr, int order)
03282 {
03283    /* Minimize the message propagation through DUNDi by
03284       alerting the network to hops which should be not be considered */
03285    struct dundi_transaction *trans;
03286    struct dundi_peer *peer;
03287    dundi_eid tmp;
03288    int x;
03289    int needpush;
03290    ast_mutex_lock(&peerlock);
03291    trans = dr->trans;
03292    while(trans) {
03293       /* Pop off the true root */
03294       if (trans->eidcount) {
03295          tmp = trans->eids[--trans->eidcount];
03296          needpush = 1;
03297       } else {
03298          tmp = trans->us_eid;
03299          needpush = 0;
03300       }
03301 
03302       peer = peers;
03303       while(peer) {
03304          if (has_permission(peer->include, dr->dcontext) && 
03305              dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
03306             (peer->order <= order)) {
03307             /* For each other transaction, make sure we don't
03308                ask this EID about the others if they're not
03309                already in the list */
03310             if (!dundi_eid_cmp(&tmp, &peer->eid)) 
03311                x = -1;
03312             else {
03313                for (x=0;x<trans->eidcount;x++) {
03314                   if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
03315                      break;
03316                }
03317             }
03318             if (x == trans->eidcount) {
03319                /* Nope not in the list, if needed, add us at the end since we're the source */
03320                if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
03321                   trans->eids[trans->eidcount++] = peer->eid;
03322                   /* Need to insert the real root (or us) at the bottom now as
03323                      a requirement now.  */
03324                   needpush = 1;
03325                }
03326             }
03327          }
03328          peer = peer->next;
03329       }
03330       /* If necessary, push the true root back on the end */
03331       if (needpush)
03332          trans->eids[trans->eidcount++] = tmp;
03333       trans = trans->next;
03334    }
03335    ast_mutex_unlock(&peerlock);
03336    return 0;
03337 }
03338 
03339 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
03340 {
03341    struct dundi_transaction *trans;
03342    int x;
03343    char eid_str[20];
03344    char eid_str2[20];
03345    /* Ignore if not registered */
03346    if (!p->addr.sin_addr.s_addr)
03347       return 0;
03348    if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
03349       return 0;
03350    if (ast_strlen_zero(dr->number))
03351       ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
03352    else
03353       ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
03354    trans = create_transaction(p);
03355    if (!trans)
03356       return -1;
03357    trans->next = dr->trans;
03358    trans->parent = dr;
03359    trans->ttl = ttl;
03360    for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
03361       trans->eids[x] = *avoid[x];
03362    trans->eidcount = x;
03363    dr->trans = trans;
03364    return 0;
03365 }
03366 
03367 static void cancel_request(struct dundi_request *dr)
03368 {
03369    struct dundi_transaction *trans, *next;
03370 
03371    ast_mutex_lock(&peerlock);
03372    trans = dr->trans;
03373    
03374    while(trans) {
03375       next = trans->next;
03376       /* Orphan transaction from request */
03377       trans->parent = NULL;
03378       trans->next = NULL;
03379       /* Send final cancel */
03380       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
03381       trans = next;
03382    }
03383    ast_mutex_unlock(&peerlock);
03384 }
03385 
03386 static void abort_request(struct dundi_request *dr)
03387 {
03388    ast_mutex_lock(&peerlock);
03389    while(dr->trans) 
03390       destroy_trans(dr->trans, 0);
03391    ast_mutex_unlock(&peerlock);
03392 }
03393 
03394 static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
03395 {
03396    struct dundi_peer *p;
03397    int x;
03398    int res;
03399    int pass;
03400    int allowconnect;
03401    char eid_str[20];
03402    ast_mutex_lock(&peerlock);
03403    p = peers;
03404    while(p) {
03405       if (modeselect == 1) {
03406          /* Send the precache to push upstreams only! */
03407          pass = has_permission(p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
03408          allowconnect = 1;
03409       } else {
03410          /* Normal lookup / EID query */
03411          pass = has_permission(p->include, dr->dcontext);
03412          allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
03413       }
03414       if (skip) {
03415          if (!dundi_eid_cmp(skip, &p->eid))
03416             pass = 0;
03417       }
03418       if (pass) {
03419          if (p->order <= order) {
03420             /* Check order first, then check cache, regardless of
03421                omissions, this gets us more likely to not have an
03422                affected answer. */
03423             if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
03424                res = 0;
03425                /* Make sure we haven't already seen it and that it won't
03426                   affect our answer */
03427                for (x=0;avoid[x];x++) {
03428                   if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
03429                      /* If not a direct connection, it affects our answer */
03430                      if (directs && !directs[x]) 
03431                         ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
03432                      break;
03433                   }
03434                }
03435                /* Make sure we can ask */
03436                if (allowconnect) {
03437                   if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
03438                      /* Check for a matching or 0 cache entry */
03439                      append_transaction(dr, p, ttl, avoid);
03440                   } else
03441                      ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
03442                }
03443             }
03444             *foundcache |= res;
03445          } else if (!*skipped || (p->order < *skipped))
03446             *skipped = p->order;
03447       }
03448       p = p->next;
03449    }
03450    ast_mutex_unlock(&peerlock);
03451 }
03452 
03453 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
03454 {
03455    struct dundi_request *cur;
03456    int res=0;
03457    char eid_str[20];
03458    ast_mutex_lock(&peerlock);
03459    cur = requests;
03460    while(cur) {
03461       if (option_debug)
03462          ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
03463             dr->dcontext, dr->number);
03464       if (!strcasecmp(cur->dcontext, dr->dcontext) &&
03465           !strcasecmp(cur->number, dr->number) &&
03466          (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
03467             ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n", 
03468                cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
03469             *pending = cur;
03470          res = 1;
03471          break;
03472       }
03473       cur = cur->next;
03474    }
03475    if (!res) {
03476       ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n", 
03477             dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
03478       /* Go ahead and link us in since nobody else is searching for this */
03479       dr->next = requests;
03480       requests = dr;
03481       *pending = NULL;
03482    }
03483    ast_mutex_unlock(&peerlock);
03484    return res;
03485 }
03486 
03487 static void unregister_request(struct dundi_request *dr)
03488 {
03489    struct dundi_request *cur, *prev;
03490    ast_mutex_lock(&peerlock);
03491    prev = NULL;
03492    cur = requests;
03493    while(cur) {
03494       if (cur == dr) {
03495          if (prev)
03496             prev->next = cur->next;
03497          else
03498             requests = cur->next;
03499          break;
03500       }
03501       prev = cur;
03502       cur = cur->next;
03503    }
03504    ast_mutex_unlock(&peerlock);
03505 }
03506 
03507 static int check_request(struct dundi_request *dr)
03508 {
03509    struct dundi_request *cur;
03510    int res = 0;
03511    ast_mutex_lock(&peerlock);
03512    cur = requests;
03513    while(cur) {
03514       if (cur == dr) {
03515          res = 1;
03516          break;
03517       }
03518       cur = cur->next;
03519    }
03520    ast_mutex_unlock(&peerlock);
03521    return res;
03522 }
03523 
03524 static unsigned long avoid_crc32(dundi_eid *avoid[])
03525 {
03526    /* Idea is that we're calculating a checksum which is independent of
03527       the order that the EID's are listed in */
03528    unsigned long acrc32 = 0;
03529    int x;
03530    for (x=0;avoid[x];x++) {
03531       /* Order doesn't matter */
03532       if (avoid[x+1]) {
03533          acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
03534       }
03535    }
03536    return acrc32;
03537 }
03538 
03539 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 *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
03540 {
03541    int res;
03542    struct dundi_request dr, *pending;
03543    dundi_eid *rooteid=NULL;
03544    int x;
03545    int ttlms;
03546    int ms;
03547    int foundcache;
03548    int skipped=0;
03549    int order=0;
03550    char eid_str[20];
03551    struct timeval start;
03552    
03553    /* Don't do anthing for a hungup channel */
03554    if (chan && chan->_softhangup)
03555       return 0;
03556 
03557    ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03558 
03559    for (x=0;avoid[x];x++)
03560       rooteid = avoid[x];
03561    /* Now perform real check */
03562    memset(&dr, 0, sizeof(dr));
03563    if (pipe(dr.pfds)) {
03564       ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
03565       return -1;
03566    }
03567    dr.dr = result;
03568    dr.hmd = hmd;
03569    dr.maxcount = maxret;
03570    dr.expiration = *expiration;
03571    dr.cbypass = cbypass;
03572    dr.crc32 = avoid_crc32(avoid);
03573    ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
03574    ast_copy_string(dr.number, number, sizeof(dr.number));
03575    if (rooteid)
03576       dr.root_eid = *rooteid;
03577    res = register_request(&dr, &pending);
03578    if (res) {
03579       /* Already a request */
03580       if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
03581          /* This is on behalf of someone else.  Go ahead and close this out since
03582             they'll get their answer anyway. */
03583          ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
03584             dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
03585          close(dr.pfds[0]);
03586          close(dr.pfds[1]);
03587          return -2;
03588       } else {
03589          /* Wait for the cache to populate */
03590          ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
03591             dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
03592          start = ast_tvnow();
03593          while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
03594             /* XXX Would be nice to have a way to poll/select here XXX */
03595             /* XXX this is a busy wait loop!!! */
03596             usleep(1);
03597          }
03598          /* Continue on as normal, our cache should kick in */
03599       }
03600    }
03601    /* Create transactions */
03602    do {
03603       order = skipped;
03604       skipped = 0;
03605       foundcache = 0;
03606       build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
03607    } while (skipped && !foundcache && !dr.trans);
03608    /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
03609       do this earlier because we didn't know if we were going to have transactions
03610       or not. */
03611    if (!ttl) {
03612       ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
03613       abort_request(&dr);
03614       unregister_request(&dr);
03615       close(dr.pfds[0]);
03616       close(dr.pfds[1]);
03617       return 0;
03618    }
03619       
03620    /* Optimize transactions */
03621    optimize_transactions(&dr, order);
03622    /* Actually perform transactions */
03623    discover_transactions(&dr);
03624    /* Wait for transaction to come back */
03625    start = ast_tvnow();
03626    while(dr.trans && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
03627       ms = 100;
03628       ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03629    }
03630    if (chan && chan->_softhangup)
03631       ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
03632    cancel_request(&dr);
03633    unregister_request(&dr);
03634    res = dr.respcount;
03635    *expiration = dr.expiration;
03636    close(dr.pfds[0]);
03637    close(dr.pfds[1]);
03638    return res;
03639 }
03640 
03641 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
03642 {
03643    struct dundi_hint_metadata hmd;
03644    dundi_eid *avoid[1] = { NULL, };
03645    int direct[1] = { 0, };
03646    int expiration = dundi_cache_time;
03647    memset(&hmd, 0, sizeof(hmd));
03648    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
03649    return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
03650 }
03651 
03652 static void reschedule_precache(const char *number, const char *context, int expiration)
03653 {
03654    int len;
03655    struct dundi_precache_queue *qe, *prev=NULL;
03656    ast_mutex_lock(&pclock);
03657    qe = pcq;
03658    while(qe) {
03659       if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
03660          if (prev)
03661             prev->next = qe->next;
03662          else
03663             pcq = qe->next;
03664          qe->next = NULL;
03665          break;
03666       }
03667       prev = qe;
03668       qe = qe->next;
03669    };
03670    if (!qe) {
03671       len = sizeof(struct dundi_precache_queue);
03672       len += strlen(number) + 1;
03673       len += strlen(context) + 1;
03674       qe = malloc(len);
03675       if (qe) {
03676          memset(qe, 0, len);
03677          strcpy(qe->number, number);
03678          qe->context = qe->number + strlen(number) + 1;
03679          strcpy(qe->context, context);
03680       }
03681    }
03682    time(&qe->expiration);
03683    qe->expiration += expiration;
03684    prev = pcq;
03685    if (prev) {
03686       while(prev->next && (prev->next->expiration <= qe->expiration))
03687          prev = prev->next;
03688       qe->next = prev->next;
03689       prev->next = qe;
03690    } else
03691       pcq = qe;
03692    ast_mutex_unlock(&pclock);
03693    
03694 }
03695 
03696 static void dundi_precache_full(void)
03697 {
03698    struct dundi_mapping *cur;
03699    struct ast_context *con;
03700    struct ast_exten *e;
03701    cur = mappings;
03702    while(cur) {
03703       ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
03704       ast_lock_contexts();
03705       con = ast_walk_contexts(NULL);
03706       while(con) {
03707          if (!strcasecmp(cur->lcontext, ast_get_context_name(con))) {
03708             /* Found the match, now queue them all up */
03709             ast_lock_context(con);
03710             e = ast_walk_context_extensions(con, NULL);
03711             while(e) {
03712                reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
03713                e = ast_walk_context_extensions(con, e);
03714             }
03715             ast_unlock_context(con);
03716          }
03717          con = ast_walk_contexts(con);
03718       }
03719       ast_unlock_contexts();
03720       cur = cur->next;
03721    }
03722 }
03723 
03724 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
03725 {
03726    struct dundi_request dr;
03727    struct dundi_hint_metadata hmd;
03728    struct dundi_result dr2[MAX_RESULTS];
03729    struct timeval start;
03730    struct dundi_mapping *maps=NULL, *cur;
03731    int nummaps;
03732    int foundanswers;
03733    int foundcache, skipped, ttlms, ms;
03734    if (!context)
03735       context = "e164";
03736    ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context);
03737 
03738    ast_mutex_lock(&peerlock);
03739    nummaps = 0;
03740    cur = mappings;
03741    while(cur) {
03742       if (!strcasecmp(cur->dcontext, context))
03743          nummaps++;
03744       cur = cur->next;
03745    }
03746    if (nummaps) {
03747       maps = alloca(nummaps * sizeof(struct dundi_mapping));
03748       nummaps = 0;
03749       if (maps) {
03750          cur = mappings;
03751          while(cur) {
03752             if (!strcasecmp(cur->dcontext, context))
03753                maps[nummaps++] = *cur;
03754             cur = cur->next;
03755          }
03756       }
03757    }
03758    ast_mutex_unlock(&peerlock);
03759    if (!nummaps || !maps)
03760       return -1;
03761    ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03762    memset(&dr2, 0, sizeof(dr2));
03763    memset(&dr, 0, sizeof(dr));
03764    memset(&hmd, 0, sizeof(hmd));
03765    dr.dr = dr2;
03766    ast_copy_string(dr.number, number, sizeof(dr.number));
03767    ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
03768    dr.maxcount = MAX_RESULTS;
03769    dr.expiration = dundi_cache_time;
03770    dr.hmd = &hmd;
03771    dr.pfds[0] = dr.pfds[1] = -1;
03772    pipe(dr.pfds);
03773    build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
03774    optimize_transactions(&dr, 0);
03775    foundanswers = 0;
03776    precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
03777    if (foundanswers) {
03778       if (dr.expiration > 0) 
03779          reschedule_precache(dr.number, dr.dcontext, dr.expiration);
03780       else
03781          ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
03782    }
03783    start = ast_tvnow();
03784    while(dr.trans && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
03785       if (dr.pfds[0] > -1) {
03786          ms = 100;
03787          ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03788       } else
03789          usleep(1);
03790    }
03791    cancel_request(&dr);
03792    if (dr.pfds[0] > -1) {
03793       close(dr.pfds[0]);
03794       close(dr.pfds[1]);
03795    }
03796    return 0;
03797 }
03798 
03799 int dundi_precache(const char *context, const char *number)
03800 {
03801    dundi_eid *avoid[1] = { NULL, };
03802    return dundi_precache_internal(context, number, dundi_ttl, avoid);
03803 }
03804 
03805 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[])
03806 {
03807    int res;
03808    struct dundi_request dr;
03809    dundi_eid *rooteid=NULL;
03810    int x;
03811    int ttlms;
03812    int skipped=0;
03813    int foundcache=0;
03814    struct timeval start;
03815    
03816    ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03817 
03818    for (x=0;avoid[x];x++)
03819       rooteid = avoid[x];
03820    /* Now perform real check */
03821    memset(&dr, 0, sizeof(dr));
03822    dr.hmd = hmd;
03823    dr.dei = dei;
03824    dr.pfds[0] = dr.pfds[1] = -1;
03825    ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
03826    memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
03827    if (rooteid)
03828       dr.root_eid = *rooteid;
03829    /* Create transactions */
03830    build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
03831 
03832    /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
03833       do this earlier because we didn't know if we were going to have transactions
03834       or not. */
03835    if (!ttl) {
03836       ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
03837       return 0;
03838    }
03839       
03840    /* Optimize transactions */
03841    optimize_transactions(&dr, 9999);
03842    /* Actually perform transactions */
03843    query_transactions(&dr);
03844    /* Wait for transaction to come back */
03845    start = ast_tvnow();
03846    while(dr.trans && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
03847       usleep(1);
03848    res = dr.respcount;
03849    return res;
03850 }
03851 
03852 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
03853 {
03854    dundi_eid *avoid[1] = { NULL, };
03855    struct dundi_hint_metadata hmd;
03856    memset(&hmd, 0, sizeof(hmd));
03857    return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
03858 }
03859 
03860 /*! 
03861  * \ingroup applications
03862  */
03863 static int dundi_lookup_exec(struct ast_channel *chan, void *data)
03864 {
03865    char *num;
03866    char *context;
03867    char *opts;
03868    int results;
03869    int x;
03870    int bypass = 0;
03871    struct localuser *u;
03872    struct dundi_result dr[MAX_RESULTS];
03873    static int dep_warning = 0;
03874 
03875    LOCAL_USER_ADD(u);
03876 
03877    if (!dep_warning) {
03878       ast_log(LOG_WARNING, "This application has been deprecated in favor of the DUNDILOOKUP dialplan function.\n");
03879       dep_warning = 1;
03880    }
03881 
03882    if (ast_strlen_zero(data)) {
03883       ast_log(LOG_WARNING, "DUNDiLookup requires an argument (number)\n");
03884       LOCAL_USER_REMOVE(u);
03885       return 0;
03886    }
03887 
03888    num = ast_strdupa(data);
03889    if (!num) {
03890       ast_log(LOG_ERROR, "Out of memory!\n");
03891       LOCAL_USER_REMOVE(u);
03892       return 0;
03893    }
03894 
03895    context = strchr(num, '|');
03896    if (context) {
03897       *context = '\0';
03898       context++;
03899       opts = strchr(context, '|');
03900       if (opts) {
03901          *opts = '\0';
03902          opts++;
03903          if (strchr(opts, 'b'))
03904             bypass = 1;
03905       }
03906    }
03907 
03908    if (ast_strlen_zero(context))
03909       context = "e164";
03910    
03911    results = dundi_lookup(dr, MAX_RESULTS, NULL, context, num, bypass);
03912    if (results > 0) {
03913       sort_results(dr, results);
03914       for (x = 0; x < results; x++) {
03915          if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
03916             pbx_builtin_setvar_helper(chan, "DUNDTECH", dr[x].tech);
03917             pbx_builtin_setvar_helper(chan, "DUNDDEST", dr[x].dest);
03918             break;
03919          }
03920       }
03921    } else if (option_priority_jumping)
03922       ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03923 
03924    LOCAL_USER_REMOVE(u);
03925 
03926    return 0;
03927 }
03928 
03929 static char *dundifunc_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03930 {
03931    char *num;
03932    char *context;
03933    char *opts;
03934    int results;
03935    int x;
03936    int bypass = 0;
03937    struct localuser *u;
03938    struct dundi_result dr[MAX_RESULTS];
03939 
03940    LOCAL_USER_ACF_ADD(u);
03941 
03942    buf[0] = '\0';
03943 
03944    if (ast_strlen_zero(data)) {
03945       ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
03946       LOCAL_USER_REMOVE(u);
03947       return buf;
03948    }
03949 
03950    num = ast_strdupa(data);
03951    if (!num) {
03952       ast_log(LOG_ERROR, "Out of memory!\n");
03953       LOCAL_USER_REMOVE(u);
03954       return buf;
03955    }
03956 
03957    context = strchr(num, '|');
03958    if (context) {
03959       *context = '\0';
03960       context++;
03961       opts = strchr(context, '|');
03962       if (opts) {
03963          *opts = '\0';
03964          opts++;
03965          if (strchr(opts, 'b'))
03966             bypass = 1;
03967       }
03968    }
03969 
03970    if (ast_strlen_zero(context))
03971       context = "e164";
03972    
03973    results = dundi_lookup(dr, MAX_RESULTS, NULL, context, num, bypass);
03974    if (results > 0) {
03975       sort_results(dr, results);
03976       for (x = 0; x < results; x++) {
03977          if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
03978             snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
03979             break;
03980          }
03981       }
03982    }
03983 
03984    LOCAL_USER_REMOVE(u);
03985 
03986    return buf;
03987 }
03988 
03989 /*! DUNDILOOKUP
03990  * \ingroup functions
03991 */
03992 
03993 static struct ast_custom_function dundi_function = {
03994    .name = "DUNDILOOKUP",
03995    .synopsis = "Do a DUNDi lookup of a phone number.",
03996    .syntax = "DUNDILOOKUP(number[|context[|options]])",
03997    .desc = "This will do a DUNDi lookup of the given phone number.\n"
03998    "If no context is given, the default will be e164. The result of\n"
03999    "this function will the Technology/Resource found in the DUNDi\n"
04000    "lookup. If no results were found, the result will be blank.\n"
04001    "If the 'b' option is specified, the internal DUNDi cache will\n"
04002    "be bypassed.\n",
04003    .read = dundifunc_read,
04004 };
04005 
04006 static void mark_peers(void)
04007 {
04008    struct dundi_peer *peer;
04009    ast_mutex_lock(&peerlock);
04010    peer = peers;
04011    while(peer) {
04012       peer->dead = 1;
04013       peer = peer->next;
04014    }
04015    ast_mutex_unlock(&peerlock);
04016 }
04017 
04018 static void mark_mappings(void)
04019 {
04020    struct dundi_mapping *map;
04021    ast_mutex_lock(&peerlock);
04022    map = mappings;
04023    while(map) {
04024       map->dead = 1;
04025       map = map->next;
04026    }
04027    ast_mutex_unlock(&peerlock);
04028 }
04029 
04030 static void destroy_permissions(struct permission *p)
04031 {
04032    struct permission *prev;
04033    while(p) {
04034       prev = p;
04035       p = p->next;
04036       free(prev);
04037    }
04038 }
04039 
04040 static void destroy_peer(struct dundi_peer *peer)
04041 {
04042    if (peer->registerid > -1)
04043       ast_sched_del(sched, peer->registerid);
04044    if (peer->regtrans)
04045       destroy_trans(peer->regtrans, 0);
04046    if (peer->keypending)
04047       destroy_trans(peer->keypending, 0);
04048    if (peer->qualifyid > -1)
04049       ast_sched_del(sched, peer->qualifyid);
04050    destroy_permissions(peer->permit);
04051    destroy_permissions(peer->include);
04052    free(peer);
04053 }
04054 
04055 static void destroy_map(struct dundi_mapping *map)
04056 {
04057    free(map);
04058 }
04059 
04060 static void prune_peers(void)
04061 {
04062    struct dundi_peer *peer, *prev, *next;
04063    ast_mutex_lock(&peerlock);
04064    peer = peers;
04065    prev = NULL;
04066    while(peer) {
04067       next = peer->next;
04068       if (peer->dead) {
04069          if (prev)
04070             prev->next = peer->next;
04071          else
04072             peers = peer->next;
04073          destroy_peer(peer);
04074       } else
04075          prev = peer;
04076       peer = next;
04077    }
04078    ast_mutex_unlock(&peerlock);
04079 }
04080 
04081 static void prune_mappings(void)
04082 {
04083    struct dundi_mapping *map, *prev, *next;
04084    ast_mutex_lock(&peerlock);
04085    map = mappings;
04086    prev = NULL;
04087    while(map) {
04088       next = map->next;
04089       if (map->dead) {
04090          if (prev)
04091             prev->next = map->next;
04092          else
04093             mappings = map->next;
04094          destroy_map(map);
04095       } else
04096          prev = map;
04097       map = next;
04098    }
04099    ast_mutex_unlock(&peerlock);
04100 }
04101 
04102 static struct permission *append_permission(struct permission *p, char *s, int allow)
04103 {
04104    struct permission *start;
04105    start = p;
04106    if (p) {
04107       while(p->next)
04108          p = p->next;
04109    }
04110    if (p) {
04111       p->next = malloc(sizeof(struct permission) + strlen(s) + 1);
04112       p = p->next;
04113    } else {
04114       p = malloc(sizeof(struct permission) + strlen(s) + 1);
04115    }
04116    if (p) {
04117       memset(p, 0, sizeof(struct permission));
04118       memcpy(p->name, s, strlen(s) + 1);
04119       p->allow = allow;
04120    }
04121    return start ? start : p;
04122 }
04123 
04124 #define MAX_OPTS 128
04125 
04126 static void build_mapping(char *name, char *value)
04127 {
04128    char *t, *fields[MAX_OPTS];
04129    struct dundi_mapping *map;
04130    int x;
04131    int y;
04132    t = ast_strdupa(value);
04133    if (t) {
04134       map = mappings;
04135       while(map) {
04136          /* Find a double match */
04137          if (!strcasecmp(map->dcontext, name) && 
04138             (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) && 
04139               (!value[strlen(map->lcontext)] || 
04140                (value[strlen(map->lcontext)] == ','))))
04141             break;
04142          map = map->next;
04143       }
04144       if (!map) {
04145          map = malloc(sizeof(struct dundi_mapping));
04146          if (map) {
04147             memset(map, 0, sizeof(struct dundi_mapping));
04148             map->next = mappings;
04149             mappings = map;
04150             map->dead = 1;
04151          }
04152       }
04153       if (map) {
04154          map->options = 0;
04155          memset(fields, 0, sizeof(fields));
04156          x = 0;
04157          while(t && x < MAX_OPTS) {
04158             fields[x++] = t;
04159             t = strchr(t, ',');
04160             if (t) {
04161                *t = '\0';
04162                t++;
04163             }
04164          } /* Russell was here, arrrr! */
04165          if ((x == 1) && ast_strlen_zero(fields[0])) {
04166             /* Placeholder mapping */
04167             ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
04168             map->dead = 0;
04169          } else if (x >= 4) {
04170             ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
04171             ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
04172             if ((sscanf(fields[1], "%30d", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) {
04173                ast_copy_string(map->dest, fields[3], sizeof(map->dest));
04174                if ((map->tech = str2tech(fields[2]))) {
04175                   map->dead = 0;
04176                }
04177             } else {
04178                ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
04179             }
04180             for (y=4;y<x;y++) {
04181                if (!strcasecmp(fields[y], "nounsolicited"))
04182                   map->options |= DUNDI_FLAG_NOUNSOLICITED;
04183                else if (!strcasecmp(fields[y], "nocomunsolicit"))
04184                   map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
04185                else if (!strcasecmp(fields[y], "residential"))
04186                   map->options |= DUNDI_FLAG_RESIDENTIAL;
04187                else if (!strcasecmp(fields[y], "commercial"))
04188                   map->options |= DUNDI_FLAG_COMMERCIAL;
04189                else if (!strcasecmp(fields[y], "mobile"))
04190                   map->options |= DUNDI_FLAG_MOBILE;
04191                else if (!strcasecmp(fields[y], "nopartial"))
04192                   map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
04193                else
04194                   ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
04195             }
04196          } else 
04197             ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
04198       }
04199    }
04200 }
04201 
04202 static int do_register(void *data)
04203 {
04204    struct dundi_ie_data ied;
04205    struct dundi_peer *peer = data;
04206    char eid_str[20];
04207    char eid_str2[20];
04208    /* Called with peerlock already held */
04209    ast_log(LOG_DEBUG, "Register us as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid));
04210    peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
04211    /* Destroy old transaction if there is one */
04212    if (peer->regtrans)
04213       destroy_trans(peer->regtrans, 0);
04214    peer->regtrans = create_transaction(peer);
04215    if (peer->regtrans) {
04216       ast_set_flag(peer->regtrans, FLAG_ISREG);
04217       memset(&ied, 0, sizeof(ied));
04218       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
04219       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
04220       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
04221       dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
04222       
04223    } else
04224       ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04225 
04226    return 0;
04227 }
04228 
04229 static int do_qualify(void *data)
04230 {
04231    struct dundi_peer *peer;
04232    peer = data;
04233    peer->qualifyid = -1;
04234    qualify_peer(peer, 0);
04235    return 0;
04236 }
04237 
04238 static void qualify_peer(struct dundi_peer *peer, int schedonly)
04239 {
04240    int when;
04241    if (peer->qualifyid > -1)
04242       ast_sched_del(sched, peer->qualifyid);
04243    peer->qualifyid = -1;
04244    if (peer->qualtrans)
04245       destroy_trans(peer->qualtrans, 0);
04246    peer->qualtrans = NULL;
04247    if (peer->maxms > 0) {
04248       when = 60000;
04249       if (peer->lastms < 0)
04250          when = 10000;
04251       if (schedonly)
04252          when = 5000;
04253       peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
04254       if (!schedonly)
04255          peer->qualtrans = create_transaction(peer);
04256       if (peer->qualtrans) {
04257          peer->qualtx = ast_tvnow();
04258          ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
04259          dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
04260       }
04261    }
04262 }
04263 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
04264 {
04265    char data[256];
04266    char *c;
04267    int port, expire;
04268    char eid_str[20];
04269    dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
04270    if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
04271       c = strchr(data, ':');
04272       if (c) {
04273          *c = '\0';
04274          c++;
04275          if (sscanf(c, "%30d:%30d", &port, &expire) == 2) {
04276             /* Got it! */
04277             inet_aton(data, &peer->addr.sin_addr);
04278             peer->addr.sin_family = AF_INET;
04279             peer->addr.sin_port = htons(port);
04280             peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
04281          }
04282       }
04283    }
04284 }
04285 
04286 
04287 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
04288 {
04289    struct dundi_peer *peer;
04290    struct ast_hostent he;
04291    struct hostent *hp;
04292    dundi_eid testeid;
04293    int needregister=0;
04294    char eid_str[20];
04295 
04296    ast_mutex_lock(&peerlock);
04297    peer = peers;
04298    while(peer) {
04299       if (!dundi_eid_cmp(&peer->eid, eid)) { 
04300          break;
04301       }
04302       peer = peer->next;
04303    }
04304    if (!peer) {
04305       /* Add us into the list */
04306       peer = malloc(sizeof(struct dundi_peer));
04307       if (peer) {
04308          memset(peer, 0, sizeof(struct dundi_peer));
04309          peer->registerid = -1;
04310          peer->registerexpire = -1;
04311          peer->qualifyid = -1;
04312          peer->addr.sin_family = AF_INET;
04313          peer->addr.sin_port = htons(DUNDI_PORT);
04314          populate_addr(peer, eid);
04315          peer->next = peers;
04316          peers = peer;
04317       }
04318    }
04319    if (peer) {
04320       peer->dead = 0;
04321       peer->eid = *eid;
04322       peer->us_eid = global_eid;
04323       destroy_permissions(peer->permit);
04324       destroy_permissions(peer->include);
04325       peer->permit = NULL;
04326       peer->include = NULL;
04327       if (peer->registerid > -1)
04328          ast_sched_del(sched, peer->registerid);
04329       peer->registerid = -1;
04330       while(v) {
04331          if (!strcasecmp(v->name, "inkey")) {
04332             ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
04333          } else if (!strcasecmp(v->name, "outkey")) {
04334             ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
04335          } else if (!strcasecmp(v->name, "host")) {
04336             if (!strcasecmp(v->value, "dynamic")) {
04337                peer->dynamic = 1;
04338             } else {
04339                hp = ast_gethostbyname(v->value, &he);
04340                if (hp) {
04341                   memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
04342                   peer->dynamic = 0;
04343                } else {
04344                   ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
04345                   peer->dead = 1;
04346                }
04347             }
04348          } else if (!strcasecmp(v->name, "ustothem")) {
04349             if (!dundi_str_to_eid(&testeid, v->value))
04350                peer->us_eid = testeid;
04351             else
04352                ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
04353          } else if (!strcasecmp(v->name, "include")) {
04354             peer->include = append_permission(peer->include, v->value, 1);
04355          } else if (!strcasecmp(v->name, "permit")) {
04356             peer->permit = append_permission(peer->permit, v->value, 1);
04357          } else if (!strcasecmp(v->name, "noinclude")) {
04358             peer->include = append_permission(peer->include, v->value, 0);
04359          } else if (!strcasecmp(v->name, "deny")) {
04360             peer->permit = append_permission(peer->permit, v->value, 0);
04361          } else if (!strcasecmp(v->name, "register")) {
04362             needregister = ast_true(v->value);
04363          } else if (!strcasecmp(v->name, "order")) {
04364             if (!strcasecmp(v->value, "primary"))
04365                peer->order = 0;
04366             else if (!strcasecmp(v->value, "secondary"))
04367                peer->order = 1;
04368             else if (!strcasecmp(v->value, "tertiary"))
04369                peer->order = 2;
04370             else if (!strcasecmp(v->value, "quartiary"))
04371                peer->order = 3;
04372             else {
04373                ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno);
04374             }
04375          } else if (!strcasecmp(v->name, "qualify")) {
04376             if (!strcasecmp(v->value, "no")) {
04377                peer->maxms = 0;
04378             } else if (!strcasecmp(v->value, "yes")) {
04379                peer->maxms = DEFAULT_MAXMS;
04380             } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
04381                ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n", 
04382                   dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
04383                peer->maxms = 0;
04384             }
04385          } else if (!strcasecmp(v->name, "model")) {
04386             if (!strcasecmp(v->value, "inbound"))
04387                peer->model = DUNDI_MODEL_INBOUND;
04388             else if (!strcasecmp(v->value, "outbound")) 
04389                peer->model = DUNDI_MODEL_OUTBOUND;
04390             else if (!strcasecmp(v->value, "symmetric"))
04391                peer->model = DUNDI_MODEL_SYMMETRIC;
04392             else if (!strcasecmp(v->value, "none"))
04393                peer->model = 0;
04394             else {
04395                ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n", 
04396                   v->value, v->lineno);
04397             }
04398          } else if (!strcasecmp(v->name, "precache")) {
04399             if (!strcasecmp(v->value, "inbound"))
04400                peer->pcmodel = DUNDI_MODEL_INBOUND;
04401             else if (!strcasecmp(v->value, "outbound")) 
04402                peer->pcmodel = DUNDI_MODEL_OUTBOUND;
04403             else if (!strcasecmp(v->value, "symmetric"))
04404                peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
04405             else if (!strcasecmp(v->value, "none"))
04406                peer->pcmodel = 0;
04407             else {
04408                ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n", 
04409                   v->value, v->lineno);
04410             }
04411          }
04412          v = v->next;
04413       }
04414       (*globalpcmode) |= peer->pcmodel;
04415       if (!peer->model && !peer->pcmodel) {
04416          ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n", 
04417             dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04418          peer->dead = 1;
04419       } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04420          ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n", 
04421             dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04422          peer->dead = 1;
04423       } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04424          ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n", 
04425             dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04426          peer->dead = 1;
04427       } else if (peer->include && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04428          ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n", 
04429             dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04430       } else if (peer->permit && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04431          ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n", 
04432             dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04433       } else { 
04434          if (needregister) {
04435             peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
04436          }
04437          qualify_peer(peer, 1);
04438       }
04439    }
04440    ast_mutex_unlock(&peerlock);
04441 }
04442 
04443 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
04444 {
04445    struct dundi_result results[MAX_RESULTS];
04446    int res;
04447    int x;
04448    int found = 0;
04449    if (!strncasecmp(context, "macro-", 6)) {
04450       if (!chan) {   
04451          ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04452          return -1;
04453       }
04454       /* If done as a macro, use macro extension */
04455       if (!strcasecmp(exten, "s")) {
04456          exten = pbx_builtin_getvar_helper(chan, "ARG1");
04457          if (ast_strlen_zero(exten))
04458             exten = chan->macroexten;
04459          if (ast_strlen_zero(exten))
04460             exten = chan->exten;
04461          if (ast_strlen_zero(exten)) { 
04462             ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04463             return -1;
04464          }
04465       }
04466       if (ast_strlen_zero(data))
04467          data = "e164";
04468    } else {
04469       if (ast_strlen_zero(data))
04470          data = context;
04471    }
04472    res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04473    for (x=0;x<res;x++) {
04474       if (ast_test_flag(results + x, flag))
04475          found++;
04476    }
04477    if (found >= priority)
04478       return 1;
04479    return 0;
04480 }
04481 
04482 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04483 {
04484    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
04485 }
04486 
04487 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04488 {
04489    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
04490 }
04491 
04492 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, int newstack, const char *data)
04493 {
04494    struct dundi_result results[MAX_RESULTS];
04495    int res;
04496    int x=0;
04497    char req[1024];
04498    struct ast_app *dial;
04499    
04500    if (!strncasecmp(context, "macro-", 6)) {
04501       if (!chan) {   
04502          ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04503          return -1;
04504       }
04505       /* If done as a macro, use macro extension */
04506       if (!strcasecmp(exten, "s")) {
04507          exten = pbx_builtin_getvar_helper(chan, "ARG1");
04508          if (ast_strlen_zero(exten))
04509             exten = chan->macroexten;
04510          if (ast_strlen_zero(exten))
04511             exten = chan->exten;
04512          if (ast_strlen_zero(exten)) { 
04513             ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04514             return -1;
04515          }
04516       }
04517       if (ast_strlen_zero(data))
04518          data = "e164";
04519    } else {
04520       if (ast_strlen_zero(data))
04521          data = context;
04522    }
04523    res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04524    if (res > 0) {
04525       sort_results(results, res);
04526       for (x=0;x<res;x++) {
04527          if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
04528             if (!--priority)
04529                break;
04530          }
04531       }
04532    }
04533    if (x < res) {
04534       /* Got a hit! */
04535       snprintf(req, sizeof(req), "%s/%s", results[x].tech, results[x].dest);
04536       dial = pbx_findapp("Dial");
04537       if (dial)
04538          res = pbx_exec(chan, dial, req, newstack);
04539    } else
04540       res = -1;
04541    return res;
04542 }
04543 
04544 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04545 {
04546    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
04547 }
04548 
04549 static struct ast_switch dundi_switch =
04550 {
04551         name:                   "DUNDi",
04552         description:          "DUNDi Discovered Dialplan Switch",
04553         exists:                 dundi_exists,
04554         canmatch:               dundi_canmatch,
04555         exec:                   dundi_exec,
04556         matchmore:              dundi_matchmore,
04557 };
04558 
04559 static int set_config(char *config_file, struct sockaddr_in* sin)
04560 {
04561    struct ast_config *cfg;
04562    struct ast_variable *v;
04563    char *cat;
04564    int format;
04565    int x;
04566    char hn[MAXHOSTNAMELEN] = "";
04567    struct ast_hostent he;
04568    struct hostent *hp;
04569    struct sockaddr_in sin2;
04570    static int last_port = 0;
04571    int globalpcmodel = 0;
04572    dundi_eid testeid;
04573 
04574    dundi_ttl = DUNDI_DEFAULT_TTL;
04575    dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
04576    cfg = ast_config_load(config_file);
04577    
04578    
04579    if (!cfg) {
04580       ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
04581       return -1;
04582    }
04583    ipaddr[0] = '\0';
04584    if (!gethostname(hn, sizeof(hn)-1)) {
04585       hp = ast_gethostbyname(hn, &he);
04586       if (hp) {
04587          memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
04588          ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin2.sin_addr);
04589       } else
04590          ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
04591    } else
04592       ast_log(LOG_WARNING, "Unable to get host name!\n");
04593    ast_mutex_lock(&peerlock);
04594    reset_global_eid();
04595    global_storehistory = 0;
04596    ast_copy_string(secretpath, "dundi", sizeof(secretpath));
04597    v = ast_variable_browse(cfg, "general");
04598    while(v) {
04599       if (!strcasecmp(v->name, "port")){ 
04600          sin->sin_port = ntohs(atoi(v->value));
04601          if(last_port==0){
04602             last_port=sin->sin_port;
04603          } else if(sin->sin_port != last_port)
04604             ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
04605       } else if (!strcasecmp(v->name, "bindaddr")) {
04606          struct hostent *hp;
04607          struct ast_hostent he;
04608          hp = ast_gethostbyname(v->value, &he);
04609          if (hp) {
04610             memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
04611          } else
04612             ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
04613       } else if (!strcasecmp(v->name, "authdebug")) {
04614          authdebug = ast_true(v->value);
04615       } else if (!strcasecmp(v->name, "ttl")) {
04616          if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
04617             dundi_ttl = x;
04618          } else {
04619             ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
04620                v->value, v->lineno, DUNDI_DEFAULT_TTL);
04621          }
04622       } else if (!strcasecmp(v->name, "autokill")) {
04623          if (sscanf(v->value, "%30d", &x) == 1) {
04624             if (x >= 0)
04625                global_autokilltimeout = x;
04626             else
04627                ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
04628          } else if (ast_true(v->value)) {
04629             global_autokilltimeout = DEFAULT_MAXMS;
04630          } else {
04631             global_autokilltimeout = 0;
04632          }
04633       } else if (!strcasecmp(v->name, "entityid")) {
04634          if (!dundi_str_to_eid(&testeid, v->value))
04635             global_eid = testeid;
04636          else
04637             ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
04638       } else if (!strcasecmp(v->name, "tos")) {
04639          if (sscanf(v->value, "%30d", &format) == 1)
04640             tos = format & 0xff;
04641          else if (!strcasecmp(v->value, "lowdelay"))
04642             tos = IPTOS_LOWDELAY;
04643          else if (!strcasecmp(v->value, "throughput"))
04644             tos = IPTOS_THROUGHPUT;
04645          else if (!strcasecmp(v->value, "reliability"))
04646             tos = IPTOS_RELIABILITY;
04647 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
04648          else if (!strcasecmp(v->value, "mincost"))
04649             tos = IPTOS_MINCOST;
04650 #endif
04651          else if (!strcasecmp(v->value, "none"))
04652             tos = 0;
04653          else
04654 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
04655             ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
04656 #else
04657             ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v->lineno);
04658 #endif
04659       } else if (!strcasecmp(v->name, "department")) {
04660          ast_copy_string(dept, v->value, sizeof(dept));
04661       } else if (!strcasecmp(v->name, "organization")) {
04662          ast_copy_string(org, v->value, sizeof(org));
04663       } else if (!strcasecmp(v->name, "locality")) {
04664          ast_copy_string(locality, v->value, sizeof(locality));
04665       } else if (!strcasecmp(v->name, "stateprov")) {
04666          ast_copy_string(stateprov, v->value, sizeof(stateprov));
04667       } else if (!strcasecmp(v->name, "country")) {
04668          ast_copy_string(country, v->value, sizeof(country));
04669       } else if (!strcasecmp(v->name, "email")) {
04670          ast_copy_string(email, v->value, sizeof(email));
04671       } else if (!strcasecmp(v->name, "phone")) {
04672          ast_copy_string(phone, v->value, sizeof(phone));
04673       } else if (!strcasecmp(v->name, "storehistory")) {
04674          global_storehistory = ast_true(v->value);
04675       } else if (!strcasecmp(v->name, "cachetime")) {
04676          if ((sscanf(v->value, "%30d", &x) == 1)) {
04677             dundi_cache_time = x;
04678          } else {
04679             ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
04680                v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
04681          }
04682       }
04683       v = v->next;
04684    }
04685    ast_mutex_unlock(&peerlock);
04686    mark_mappings();
04687    v = ast_variable_browse(cfg, "mappings");
04688    while(v) {
04689       build_mapping(v->name, v->value);
04690       v = v->next;
04691    }
04692    prune_mappings();
04693    mark_peers();
04694    cat = ast_category_browse(cfg, NULL);
04695    while(cat) {
04696       if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
04697          /* Entries */
04698          if (!dundi_str_to_eid(&testeid, cat))
04699             build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
04700          else
04701             ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
04702       }
04703       cat = ast_category_browse(cfg, cat);
04704    }
04705    prune_peers();
04706    ast_config_destroy(cfg);
04707    load_password();
04708    if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
04709       dundi_precache_full();
04710    return 0;
04711 }
04712 
04713 int unload_module(void)
04714 {
04715    int res;
04716    STANDARD_HANGUP_LOCALUSERS;
04717    ast_cli_unregister(&cli_debug);
04718    ast_cli_unregister(&cli_store_history);
04719    ast_cli_unregister(&cli_flush);
04720    ast_cli_unregister(&cli_no_debug);
04721    ast_cli_unregister(&cli_no_store_history);
04722    ast_cli_unregister(&cli_show_peers);
04723    ast_cli_unregister(&cli_show_entityid);
04724    ast_cli_unregister(&cli_show_trans);
04725    ast_cli_unregister(&cli_show_requests);
04726    ast_cli_unregister(&cli_show_mappings);
04727    ast_cli_unregister(&cli_show_precache);
04728    ast_cli_unregister(&cli_show_peer);
04729    ast_cli_unregister(&cli_lookup);
04730    ast_cli_unregister(&cli_precache);
04731    ast_cli_unregister(&cli_queryeid);
04732    ast_unregister_switch(&dundi_switch);
04733    ast_custom_function_unregister(&dundi_function);
04734    res = ast_unregister_application(app);
04735    sched_context_destroy(sched);
04736    return res;
04737 }
04738 
04739 int reload(void)
04740 {
04741    struct sockaddr_in sin;
04742    set_config("dundi.conf",&sin);
04743    return 0;
04744 }
04745 
04746 int load_module(void)
04747 {
04748    int res = 0;
04749    struct sockaddr_in sin;
04750    char iabuf[INET_ADDRSTRLEN];
04751    
04752    dundi_set_output(dundi_debug_output);
04753    dundi_set_error(dundi_error_output);
04754    
04755    sin.sin_family = AF_INET;
04756    sin.sin_port = ntohs(DUNDI_PORT);
04757    sin.sin_addr.s_addr = INADDR_ANY;
04758 
04759    /* Make a UDP socket */
04760    io = io_context_create();
04761    sched = sched_context_create();
04762    
04763    if (!io || !sched) {
04764       ast_log(LOG_ERROR, "Out of memory\n");
04765       return -1;
04766    }
04767 
04768    set_config("dundi.conf",&sin);
04769 
04770    netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
04771    
04772    if (netsocket < 0) {
04773       ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
04774       return -1;
04775    }
04776    if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
04777       ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
04778       return -1;
04779    }
04780 
04781    if (option_verbose > 1)
04782       ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
04783 
04784    if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) 
04785       ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
04786    
04787    res = start_network_thread();
04788    if (res) {
04789       ast_log(LOG_ERROR, "Unable to start network thread\n");
04790       close(netsocket);
04791       return -1;
04792    }
04793 
04794    if (option_verbose > 1)
04795       ast_verbose(VERBOSE_PREFIX_2 "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
04796 
04797    ast_cli_register(&cli_debug);
04798    ast_cli_register(&cli_store_history);
04799    ast_cli_register(&cli_flush);
04800    ast_cli_register(&cli_no_debug);
04801    ast_cli_register(&cli_no_store_history);
04802    ast_cli_register(&cli_show_peers);
04803    ast_cli_register(&cli_show_entityid);
04804    ast_cli_register(&cli_show_trans);
04805    ast_cli_register(&cli_show_requests);
04806    ast_cli_register(&cli_show_mappings);
04807    ast_cli_register(&cli_show_precache);
04808    ast_cli_register(&cli_show_peer);
04809    ast_cli_register(&cli_lookup);
04810    ast_cli_register(&cli_precache);
04811    ast_cli_register(&cli_queryeid);
04812    if (ast_register_switch(&dundi_switch))
04813       ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
04814    ast_register_application(app, dundi_lookup_exec, synopsis, descrip);
04815    ast_custom_function_register(&dundi_function); 
04816    
04817    return res;
04818 }
04819 
04820 char *description(void)
04821 {
04822    return tdesc;
04823 }
04824 
04825 int usecount(void)
04826 {
04827    int res;
04828    /* XXX DUNDi cannot be unloaded XXX */
04829    return 1;
04830    STANDARD_USECOUNT(res);
04831    return res;
04832 }
04833 
04834 char *key()
04835 {
04836    return ASTERISK_GPL_KEY;
04837 }

Generated on Wed Oct 28 15:47:55 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6