Wed Aug 18 22:33:54 2010

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

Generated on Wed Aug 18 22:33:54 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7