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

Generated on Fri Aug 24 02:22:16 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1