Mon May 14 04:43:00 2007

Asterisk developer's documentation


pbx_dundi.c

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

Generated on Mon May 14 04:43:00 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1