Thu Jul 9 13:40:38 2009

Asterisk developer's documentation


pbx_dundi.c

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

Generated on Thu Jul 9 13:40:38 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7