Fri May 26 01:45:35 2006

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

Generated on Fri May 26 01:45:35 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6