Sat Feb 11 06:33:19 2012

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

Generated on Sat Feb 11 06:33:20 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6