Thu Oct 11 06:33:51 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: 372420 $")
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/netsock2.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, dr.dcontext, 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 crc, 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, crc);
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, crc);
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 = ast_alloca(srclen);
01394    decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
01395    /* Setup header */
01396    h = (struct dundi_hdr *)dst;
01397    *h = *ohdr;
01398    bytes = space - 6;
01399    if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
01400       ast_debug(1, "Ouch, uncompress failed :(\n");
01401       return NULL;
01402    }
01403    /* Update length */
01404    *dstlen = bytes + 6;
01405    /* Return new header */
01406    return h;
01407 }
01408 
01409 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
01410 {
01411    unsigned char *compress_space;
01412    int len;
01413    int res;
01414    unsigned long bytes;
01415    struct dundi_ie_data ied;
01416    struct dundi_peer *peer;
01417    unsigned char iv[16];
01418    len = pack->datalen + pack->datalen / 100 + 42;
01419    compress_space = ast_alloca(len);
01420    memset(compress_space, 0, len);
01421    /* We care about everthing save the first 6 bytes of header */
01422    bytes = len;
01423    res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
01424    if (res != Z_OK) {
01425       ast_debug(1, "Ouch, compression failed!\n");
01426       return -1;
01427    }
01428    memset(&ied, 0, sizeof(ied));
01429    /* Say who we are */
01430    if (!pack->h->iseqno && !pack->h->oseqno) {
01431       /* Need the key in the first copy */
01432       if (!(peer = find_peer(&trans->them_eid)))
01433          return -1;
01434       if (update_key(peer))
01435          return -1;
01436       if (!peer->sentfullkey)
01437          ast_set_flag(trans, FLAG_SENDFULLKEY);
01438       /* Append key data */
01439       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01440       if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
01441          dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01442          dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01443       } else {
01444          dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
01445       }
01446       /* Setup contexts */
01447       trans->ecx = peer->us_ecx;
01448       trans->dcx = peer->us_dcx;
01449 
01450       /* We've sent the full key */
01451       peer->sentfullkey = 1;
01452    }
01453    /* Build initialization vector */
01454    build_iv(iv);
01455    /* Add the field, rounded up to 16 bytes */
01456    dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
01457    /* Copy the data */
01458    if ((ied.pos + bytes) >= sizeof(ied.buf)) {
01459       ast_log(LOG_NOTICE, "Final packet too large!\n");
01460       return -1;
01461    }
01462    encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
01463    ied.pos += ((bytes + 15) / 16) * 16;
01464    /* Reconstruct header */
01465    pack->datalen = sizeof(struct dundi_hdr);
01466    pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
01467    pack->h->cmdflags = 0;
01468    memcpy(pack->h->ies, ied.buf, ied.pos);
01469    pack->datalen += ied.pos;
01470    return 0;
01471 }
01472 
01473 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
01474 {
01475    unsigned char dst[128];
01476    int res;
01477    struct ast_key *key, *skey;
01478    char eid_str[20];
01479    ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
01480    if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
01481       /* A match */
01482       return 1;
01483    } else if (!newkey || !newsig)
01484       return 0;
01485    if (!memcmp(peer->rxenckey, newkey, 128) &&
01486        !memcmp(peer->rxenckey + 128, newsig, 128)) {
01487       /* By definition, a match */
01488       return 1;
01489    }
01490    /* Decrypt key */
01491    key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01492    if (!key) {
01493       ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
01494          peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01495       return -1;
01496    }
01497 
01498    skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01499    if (!skey) {
01500       ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
01501          peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01502       return -1;
01503    }
01504 
01505    /* First check signature */
01506    res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
01507    if (res)
01508       return 0;
01509 
01510    res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
01511    if (res != 16) {
01512       if (res >= 0)
01513          ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
01514       return 0;
01515    }
01516    /* Decrypted, passes signature */
01517    ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
01518    memcpy(peer->rxenckey, newkey, 128);
01519    memcpy(peer->rxenckey + 128, newsig, 128);
01520    peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
01521    ast_aes_set_decrypt_key(dst, &peer->them_dcx);
01522    ast_aes_set_encrypt_key(dst, &peer->them_ecx);
01523    return 1;
01524 }
01525 
01526 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
01527 {
01528    struct permission *cur, *perm;
01529 
01530    memcpy(peer_dst, peer_src, sizeof(*peer_dst));
01531 
01532    memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
01533    memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
01534 
01535    AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
01536       if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
01537          continue;
01538 
01539       perm->allow = cur->allow;
01540       strcpy(perm->name, cur->name);
01541 
01542       AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
01543    }
01544 
01545    AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
01546       if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
01547          continue;
01548 
01549       perm->allow = cur->allow;
01550       strcpy(perm->name, cur->name);
01551 
01552       AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
01553    }
01554 }
01555 
01556 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
01557 {
01558    /* Handle canonical command / response */
01559    int final = hdr->cmdresp & 0x80;
01560    int cmd = hdr->cmdresp & 0x7f;
01561    int x,y,z;
01562    int resp;
01563    int res;
01564    int authpass=0;
01565    unsigned char *bufcpy;
01566 #ifdef LOW_MEMORY
01567    struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
01568 #else
01569    struct dundi_ie_data _ied = {
01570       .pos = 0,
01571    };
01572    struct dundi_ie_data *ied = &_ied;
01573 #endif
01574    struct dundi_ies ies = {
01575       .eidcount = 0,
01576    };
01577    struct dundi_peer *peer = NULL;
01578    char eid_str[20];
01579    char eid_str2[20];
01580    int retval = -1;
01581 
01582    if (!ied) {
01583       return -1;
01584    }
01585 
01586    if (datalen) {
01587       bufcpy = ast_alloca(datalen);
01588       /* Make a copy for parsing */
01589       memcpy(bufcpy, hdr->ies, datalen);
01590       ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
01591       if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
01592          ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
01593          goto return_cleanup;
01594       }
01595    }
01596    switch(cmd) {
01597    case DUNDI_COMMAND_DPDISCOVER:
01598    case DUNDI_COMMAND_EIDQUERY:
01599    case DUNDI_COMMAND_PRECACHERQ:
01600       if (cmd == DUNDI_COMMAND_EIDQUERY)
01601          resp = DUNDI_COMMAND_EIDRESPONSE;
01602       else if (cmd == DUNDI_COMMAND_PRECACHERQ)
01603          resp = DUNDI_COMMAND_PRECACHERP;
01604       else
01605          resp = DUNDI_COMMAND_DPRESPONSE;
01606       /* A dialplan or entity discover -- qualify by highest level entity */
01607       peer = find_peer(ies.eids[0]);
01608       if (!peer) {
01609          dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01610          dundi_send(trans, resp, 0, 1, ied);
01611       } else {
01612          int hasauth = 0;
01613          trans->us_eid = peer->us_eid;
01614          if (strlen(peer->inkey)) {
01615             hasauth = encrypted;
01616          } else
01617             hasauth = 1;
01618          if (hasauth) {
01619             /* Okay we're authentiated and all, now we check if they're authorized */
01620             if (!ies.called_context)
01621                ies.called_context = "e164";
01622             if (cmd == DUNDI_COMMAND_EIDQUERY) {
01623                res = dundi_answer_entity(trans, &ies, ies.called_context);
01624             } else {
01625                if (ast_strlen_zero(ies.called_number)) {
01626                   /* They're not permitted to access that context */
01627                   dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
01628                   dundi_send(trans, resp, 0, 1, ied);
01629                } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
01630                           (peer->model & DUNDI_MODEL_INBOUND) &&
01631                         has_permission(&peer->permit, ies.called_context)) {
01632                   res = dundi_answer_query(trans, &ies, ies.called_context);
01633                   if (res < 0) {
01634                      /* There is no such dundi context */
01635                      dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01636                      dundi_send(trans, resp, 0, 1, ied);
01637                   }
01638                } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
01639                           (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
01640                         has_permission(&peer->include, ies.called_context)) {
01641                   res = dundi_prop_precache(trans, &ies, ies.called_context);
01642                   if (res < 0) {
01643                      /* There is no such dundi context */
01644                      dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01645                      dundi_send(trans, resp, 0, 1, ied);
01646                   }
01647                } else {
01648                   /* They're not permitted to access that context */
01649                   dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
01650                   dundi_send(trans, resp, 0, 1, ied);
01651                }
01652             }
01653          } else {
01654             /* They're not permitted to access that context */
01655             dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
01656             dundi_send(trans, resp, 0, 1, ied);
01657          }
01658       }
01659       break;
01660    case DUNDI_COMMAND_REGREQ:
01661       /* A register request -- should only have one entity */
01662       peer = find_peer(ies.eids[0]);
01663 
01664       /* if the peer is not found and we have a valid 'any_peer' setting */
01665       if (any_peer && peer == any_peer) {
01666          /* copy any_peer into a new peer object */
01667          peer = ast_calloc(1, sizeof(*peer));
01668          if (peer) {
01669             deep_copy_peer(peer, any_peer);
01670 
01671             /* set EID to remote EID */
01672             peer->eid = *ies.eids[0];
01673 
01674             AST_LIST_LOCK(&peers);
01675             AST_LIST_INSERT_HEAD(&peers, peer, list);
01676             AST_LIST_UNLOCK(&peers);
01677          }
01678       }
01679 
01680       if (!peer || !peer->dynamic) {
01681          dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01682          dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
01683       } else {
01684          int hasauth = 0;
01685          trans->us_eid = peer->us_eid;
01686          if (!ast_strlen_zero(peer->inkey)) {
01687             hasauth = encrypted;
01688          } else
01689             hasauth = 1;
01690          if (hasauth) {
01691             int expire = default_expiration;
01692             char data[256];
01693             int needqual = 0;
01694             AST_SCHED_DEL(sched, peer->registerexpire);
01695             peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
01696             snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
01697                ntohs(trans->addr.sin_port), expire);
01698             ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
01699             if (inaddrcmp(&peer->addr, &trans->addr)) {
01700                ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
01701                      ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
01702                      ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
01703                needqual = 1;
01704             }
01705 
01706             memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
01707             dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
01708             dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
01709             if (needqual)
01710                qualify_peer(peer, 1);
01711          }
01712       }
01713       break;
01714    case DUNDI_COMMAND_DPRESPONSE:
01715       /* A dialplan response, lets see what we got... */
01716       if (ies.cause < 1) {
01717          /* Success of some sort */
01718          ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
01719          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01720             authpass = encrypted;
01721          } else
01722             authpass = 1;
01723          if (authpass) {
01724             /* Pass back up answers */
01725             if (trans->parent && trans->parent->dr) {
01726                y = trans->parent->respcount;
01727                for (x=0;x<ies.anscount;x++) {
01728                   if (trans->parent->respcount < trans->parent->maxcount) {
01729                      /* Make sure it's not already there */
01730                      for (z=0;z<trans->parent->respcount;z++) {
01731                         if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
01732                             !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
01733                               break;
01734                      }
01735                      if (z == trans->parent->respcount) {
01736                         /* Copy into parent responses */
01737                         trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
01738                         trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
01739                         trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
01740                         trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
01741                         if (ies.expiration > 0)
01742                            trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
01743                         else
01744                            trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
01745                         ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
01746                            sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
01747                            &ies.answers[x]->eid);
01748                         ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
01749                            sizeof(trans->parent->dr[trans->parent->respcount].dest));
01750                         ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
01751                            sizeof(trans->parent->dr[trans->parent->respcount].tech));
01752                         trans->parent->respcount++;
01753                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01754                      } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
01755                         /* Update weight if appropriate */
01756                         trans->parent->dr[z].weight = ies.answers[x]->weight;
01757                      }
01758                   } else
01759                      ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
01760                         trans->parent->number, trans->parent->dcontext);
01761                }
01762                /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
01763                   the cache know if this request was unaffected by our entity list. */
01764                cache_save(&trans->them_eid, trans->parent, y,
01765                      ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
01766                if (ies.hint) {
01767                   cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
01768                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01769                      ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01770                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
01771                      if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
01772                         ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
01773                            sizeof(trans->parent->hmd->exten));
01774                      }
01775                   } else {
01776                      ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01777                   }
01778                }
01779                if (ies.expiration > 0) {
01780                   if (trans->parent->expiration > ies.expiration) {
01781                      trans->parent->expiration = ies.expiration;
01782                   }
01783                }
01784             }
01785             /* Close connection if not final */
01786             if (!final)
01787                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01788          }
01789 
01790       } else {
01791          /* Auth failure, check for data */
01792          if (!final) {
01793             /* Cancel if they didn't already */
01794             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01795          }
01796       }
01797       break;
01798    case DUNDI_COMMAND_EIDRESPONSE:
01799       /* A dialplan response, lets see what we got... */
01800       if (ies.cause < 1) {
01801          /* Success of some sort */
01802          ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
01803          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01804             authpass = encrypted;
01805          } else
01806             authpass = 1;
01807          if (authpass) {
01808             /* Pass back up answers */
01809             if (trans->parent && trans->parent->dei && ies.q_org) {
01810                if (!trans->parent->respcount) {
01811                   trans->parent->respcount++;
01812                   if (ies.q_dept)
01813                      ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
01814                   if (ies.q_org)
01815                      ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
01816                   if (ies.q_locality)
01817                      ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
01818                   if (ies.q_stateprov)
01819                      ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
01820                   if (ies.q_country)
01821                      ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
01822                   if (ies.q_email)
01823                      ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
01824                   if (ies.q_phone)
01825                      ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
01826                   if (ies.q_ipaddr)
01827                      ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
01828                   if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
01829                      /* If it's them, update our address */
01830                      ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
01831                   }
01832                }
01833                if (ies.hint) {
01834                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01835                      ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01836                }
01837             }
01838             /* Close connection if not final */
01839             if (!final)
01840                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01841          }
01842 
01843       } else {
01844          /* Auth failure, check for data */
01845          if (!final) {
01846             /* Cancel if they didn't already */
01847             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01848          }
01849       }
01850       break;
01851    case DUNDI_COMMAND_REGRESPONSE:
01852       /* A dialplan response, lets see what we got... */
01853       if (ies.cause < 1) {
01854          int hasauth;
01855          /* Success of some sort */
01856          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01857             hasauth = encrypted;
01858          } else
01859             hasauth = 1;
01860 
01861          if (!hasauth) {
01862             ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
01863             if (!final) {
01864                dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
01865                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
01866             }
01867          } else {
01868             ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
01869                   ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
01870             /* Close connection if not final */
01871             if (!final)
01872                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01873          }
01874       } else {
01875          /* Auth failure, cancel if they didn't for some reason */
01876          if (!final) {
01877             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01878          }
01879       }
01880       break;
01881    case DUNDI_COMMAND_INVALID:
01882    case DUNDI_COMMAND_NULL:
01883    case DUNDI_COMMAND_PRECACHERP:
01884       /* Do nothing special */
01885       if (!final)
01886          dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01887       break;
01888    case DUNDI_COMMAND_ENCREJ:
01889       if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
01890          /* No really, it's over at this point */
01891          if (!final)
01892             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01893       } else {
01894          /* Send with full key */
01895          ast_set_flag(trans, FLAG_SENDFULLKEY);
01896          if (final) {
01897             /* Ooops, we got a final message, start by sending ACK... */
01898             dundi_ack(trans, hdr->cmdresp & 0x80);
01899             trans->aseqno = trans->iseqno;
01900             /* Now, we gotta create a new transaction */
01901             if (!reset_transaction(trans)) {
01902                /* Make sure handle_frame doesn't destroy us */
01903                hdr->cmdresp &= 0x7f;
01904                /* Parse the message we transmitted */
01905                memset(&ies, 0, sizeof(ies));
01906                dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
01907                /* Reconstruct outgoing encrypted packet */
01908                memset(ied, 0, sizeof(*ied));
01909                dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
01910                dundi_ie_append_raw(ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01911                dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01912                if (ies.encblock)
01913                   dundi_ie_append_encdata(ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
01914                dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
01915                peer->sentfullkey = 1;
01916             }
01917          }
01918       }
01919       break;
01920    case DUNDI_COMMAND_ENCRYPT:
01921       if (!encrypted) {
01922          /* No nested encryption! */
01923          if ((trans->iseqno == 1) && !trans->oseqno) {
01924             if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
01925                ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
01926                (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
01927                if (!final) {
01928                   dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01929                }
01930                break;
01931             }
01932             apply_peer(trans, peer);
01933             /* Key passed, use new contexts for this session */
01934             trans->ecx = peer->them_ecx;
01935             trans->dcx = peer->them_dcx;
01936          }
01937          if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
01938             struct dundi_hdr *dhdr;
01939             unsigned char decoded[MAX_PACKET_SIZE];
01940             int ddatalen;
01941             ddatalen = sizeof(decoded);
01942             dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
01943             if (dhdr) {
01944                /* Handle decrypted response */
01945                if (dundidebug)
01946                   dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
01947                handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
01948                /* Carry back final flag */
01949                hdr->cmdresp |= dhdr->cmdresp & 0x80;
01950                break;
01951             } else {
01952                ast_debug(1, "Ouch, decrypt failed :(\n");
01953             }
01954          }
01955       }
01956       if (!final) {
01957          /* Turn off encryption */
01958          ast_clear_flag(trans, FLAG_ENCRYPT);
01959          dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01960       }
01961       break;
01962    default:
01963       /* Send unknown command if we don't know it, with final flag IFF it's the
01964          first command in the dialog and only if we haven't received final notification */
01965       if (!final) {
01966          dundi_ie_append_byte(ied, DUNDI_IE_UNKNOWN, cmd);
01967          dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
01968       }
01969    }
01970 
01971    retval = 0;
01972 
01973 return_cleanup:
01974 #ifdef LOW_MEMORY
01975    ast_free(ied);
01976 #endif
01977    return retval;
01978 }
01979 
01980 static void destroy_packet(struct dundi_packet *pack, int needfree);
01981 static void destroy_packets(struct packetlist *p)
01982 {
01983    struct dundi_packet *pack;
01984 
01985    while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
01986       AST_SCHED_DEL(sched, pack->retransid);
01987       ast_free(pack);
01988    }
01989 }
01990 
01991 
01992 static int ack_trans(struct dundi_transaction *trans, int iseqno)
01993 {
01994    struct dundi_packet *pack;
01995 
01996    /* Ack transmitted packet corresponding to iseqno */
01997    AST_LIST_TRAVERSE(&trans->packets, pack, list) {
01998       if ((pack->h->oseqno + 1) % 255 == iseqno) {
01999          destroy_packet(pack, 0);
02000          if (!AST_LIST_EMPTY(&trans->lasttrans)) {
02001             ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
02002             destroy_packets(&trans->lasttrans);
02003          }
02004          AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
02005          AST_SCHED_DEL(sched, trans->autokillid);
02006          return 1;
02007       }
02008    }
02009 
02010    return 0;
02011 }
02012 
02013 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
02014 {
02015    struct dundi_transaction *trans;
02016    trans = find_transaction(h, sin);
02017    if (!trans) {
02018       dundi_reject(h, sin);
02019       return 0;
02020    }
02021    /* Got a transaction, see where this header fits in */
02022    if (h->oseqno == trans->iseqno) {
02023       /* Just what we were looking for...  Anything but ack increments iseqno */
02024       if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
02025          /* If final, we're done */
02026          destroy_trans(trans, 0);
02027          return 0;
02028       }
02029       if (h->cmdresp != DUNDI_COMMAND_ACK) {
02030          trans->oiseqno = trans->iseqno;
02031          trans->iseqno++;
02032          handle_command_response(trans, h, datalen, 0);
02033       }
02034       if (trans->aseqno != trans->iseqno) {
02035          dundi_ack(trans, h->cmdresp & 0x80);
02036          trans->aseqno = trans->iseqno;
02037       }
02038       /* Delete any saved last transmissions */
02039       destroy_packets(&trans->lasttrans);
02040       if (h->cmdresp & 0x80) {
02041          /* Final -- destroy now */
02042          destroy_trans(trans, 0);
02043       }
02044    } else if (h->oseqno == trans->oiseqno) {
02045       /* Last incoming sequence number -- send ACK without processing */
02046       dundi_ack(trans, 0);
02047    } else {
02048       /* Out of window -- simply drop */
02049       ast_debug(1, "Dropping packet out of window!\n");
02050    }
02051    return 0;
02052 }
02053 
02054 static int socket_read(int *id, int fd, short events, void *cbdata)
02055 {
02056    struct sockaddr_in sin;
02057    int res;
02058    struct dundi_hdr *h;
02059    char buf[MAX_PACKET_SIZE];
02060    socklen_t len = sizeof(sin);
02061 
02062    res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
02063    if (res < 0) {
02064       if (errno != ECONNREFUSED)
02065          ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
02066       return 1;
02067    }
02068    if (res < sizeof(struct dundi_hdr)) {
02069       ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
02070       return 1;
02071    }
02072    buf[res] = '\0';
02073    h = (struct dundi_hdr *) buf;
02074    if (dundidebug)
02075       dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
02076    AST_LIST_LOCK(&peers);
02077    handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
02078    AST_LIST_UNLOCK(&peers);
02079    return 1;
02080 }
02081 
02082 static void build_secret(char *secret, int seclen)
02083 {
02084    unsigned char tmp[16];
02085    char *s;
02086    build_iv(tmp);
02087    secret[0] = '\0';
02088    ast_base64encode(secret, tmp, sizeof(tmp), seclen);
02089    /* Eliminate potential bad characters */
02090    while((s = strchr(secret, ';'))) *s = '+';
02091    while((s = strchr(secret, '/'))) *s = '+';
02092    while((s = strchr(secret, ':'))) *s = '+';
02093    while((s = strchr(secret, '@'))) *s = '+';
02094 }
02095 
02096 
02097 static void save_secret(const char *newkey, const char *oldkey)
02098 {
02099    char tmp[256];
02100    if (oldkey)
02101       snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
02102    else
02103       snprintf(tmp, sizeof(tmp), "%s", newkey);
02104    rotatetime = time(NULL) + DUNDI_SECRET_TIME;
02105    ast_db_put(secretpath, "secret", tmp);
02106    snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
02107    ast_db_put(secretpath, "secretexpiry", tmp);
02108 }
02109 
02110 static void load_password(void)
02111 {
02112    char *current=NULL;
02113    char *last=NULL;
02114    char tmp[256];
02115    time_t expired;
02116 
02117    ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
02118    if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
02119       ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
02120       current = strchr(tmp, ';');
02121       if (!current)
02122          current = tmp;
02123       else {
02124          *current = '\0';
02125          current++;
02126       };
02127       if ((time(NULL) - expired) < 0) {
02128          if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
02129             expired = time(NULL) + DUNDI_SECRET_TIME;
02130       } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
02131          last = current;
02132          current = NULL;
02133       } else {
02134          last = NULL;
02135          current = NULL;
02136       }
02137    }
02138    if (current) {
02139       /* Current key is still valid, just setup rotatation properly */
02140       ast_copy_string(cursecret, current, sizeof(cursecret));
02141       rotatetime = expired;
02142    } else {
02143       /* Current key is out of date, rotate or eliminate all together */
02144       build_secret(cursecret, sizeof(cursecret));
02145       save_secret(cursecret, last);
02146    }
02147 }
02148 
02149 static void check_password(void)
02150 {
02151    char oldsecret[80];
02152    time_t now;
02153 
02154    time(&now);
02155 #if 0
02156    printf("%ld/%ld\n", now, rotatetime);
02157 #endif
02158    if ((now - rotatetime) >= 0) {
02159       /* Time to rotate keys */
02160       ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
02161       build_secret(cursecret, sizeof(cursecret));
02162       save_secret(cursecret, oldsecret);
02163    }
02164 }
02165 
02166 static void *network_thread(void *ignore)
02167 {
02168    /* Our job is simple: Send queued messages, retrying if necessary.  Read frames
02169       from the network, and queue them for delivery to the channels */
02170    int res;
02171    /* Establish I/O callback for socket read */
02172    ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
02173 
02174    while (!dundi_shutdown) {
02175       res = ast_sched_wait(sched);
02176       if ((res > 1000) || (res < 0))
02177          res = 1000;
02178       res = ast_io_wait(io, res);
02179       if (res >= 0) {
02180          AST_LIST_LOCK(&peers);
02181          ast_sched_runq(sched);
02182          AST_LIST_UNLOCK(&peers);
02183       }
02184       check_password();
02185    }
02186 
02187    netthreadid = AST_PTHREADT_NULL;
02188 
02189    return NULL;
02190 }
02191 
02192 static void *process_clearcache(void *ignore)
02193 {
02194    struct ast_db_entry *db_entry, *db_tree;
02195    int striplen = sizeof("/dundi/cache");
02196    time_t now;
02197 
02198    while (!dundi_shutdown) {
02199       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
02200 
02201       time(&now);
02202 
02203       db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
02204       for (; db_entry; db_entry = db_entry->next) {
02205          time_t expiry;
02206 
02207          if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
02208             if (expiry < now) {
02209                ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
02210                ast_db_del("dundi/cache", db_entry->key + striplen);
02211             }
02212          }
02213       }
02214       ast_db_freetree(db_tree);
02215 
02216       pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
02217       pthread_testcancel();
02218       sleep(60);
02219       pthread_testcancel();
02220    }
02221 
02222    clearcachethreadid = AST_PTHREADT_NULL;
02223    return NULL;
02224 }
02225 
02226 static void *process_precache(void *ign)
02227 {
02228    struct dundi_precache_queue *qe;
02229    time_t now;
02230    char context[256];
02231    char number[256];
02232    int run;
02233 
02234    while (!dundi_shutdown) {
02235       time(&now);
02236       run = 0;
02237       AST_LIST_LOCK(&pcq);
02238       if ((qe = AST_LIST_FIRST(&pcq))) {
02239          if (!qe->expiration) {
02240             /* Gone...  Remove... */
02241             AST_LIST_REMOVE_HEAD(&pcq, list);
02242             ast_free(qe);
02243          } else if (qe->expiration < now) {
02244             /* Process this entry */
02245             qe->expiration = 0;
02246             ast_copy_string(context, qe->context, sizeof(context));
02247             ast_copy_string(number, qe->number, sizeof(number));
02248             run = 1;
02249          }
02250       }
02251       AST_LIST_UNLOCK(&pcq);
02252       if (run) {
02253          dundi_precache(context, number);
02254       } else
02255          sleep(1);
02256    }
02257 
02258    precachethreadid = AST_PTHREADT_NULL;
02259 
02260    return NULL;
02261 }
02262 
02263 static int start_network_thread(void)
02264 {
02265    ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
02266    ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
02267    ast_pthread_create_background(&clearcachethreadid, NULL, process_clearcache, NULL);
02268    return 0;
02269 }
02270 
02271 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02272 {
02273    switch (cmd) {
02274    case CLI_INIT:
02275       e->command = "dundi set debug {on|off}";
02276       e->usage =
02277          "Usage: dundi set debug {on|off}\n"
02278          "       Enables/Disables dumping of DUNDi packets for debugging purposes\n";
02279       return NULL;
02280    case CLI_GENERATE:
02281       return NULL;
02282    }
02283 
02284    if (a->argc != e->args) {
02285       return CLI_SHOWUSAGE;
02286    }
02287    if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
02288       dundidebug = 1;
02289       ast_cli(a->fd, "DUNDi Debugging Enabled\n");
02290    } else {
02291       dundidebug = 0;
02292       ast_cli(a->fd, "DUNDi Debugging Disabled\n");
02293    }
02294    return CLI_SUCCESS;
02295 }
02296 
02297 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02298 {
02299    switch (cmd) {
02300    case CLI_INIT:
02301       e->command = "dundi store history {on|off}";
02302       e->usage =
02303          "Usage: dundi store history {on|off}\n"
02304          "       Enables/Disables storing of DUNDi requests and times for debugging\n"
02305          "purposes\n";
02306       return NULL;
02307    case CLI_GENERATE:
02308       return NULL;
02309    }
02310 
02311    if (a->argc != e->args) {
02312       return CLI_SHOWUSAGE;
02313    }
02314    if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
02315       global_storehistory = 1;
02316       ast_cli(a->fd, "DUNDi History Storage Enabled\n");
02317    } else {
02318       global_storehistory = 0;
02319       ast_cli(a->fd, "DUNDi History Storage Disabled\n");
02320    }
02321    return CLI_SUCCESS;
02322 }
02323 
02324 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02325 {
02326    int stats = 0;
02327    switch (cmd) {
02328    case CLI_INIT:
02329       e->command = "dundi flush [stats]";
02330       e->usage =
02331          "Usage: dundi flush [stats]\n"
02332          "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
02333          "'stats' is present, clears timer statistics instead of normal\n"
02334          "operation.\n";
02335       return NULL;
02336    case CLI_GENERATE:
02337       return NULL;
02338    }
02339    if ((a->argc < 2) || (a->argc > 3)) {
02340       return CLI_SHOWUSAGE;
02341    }
02342    if (a->argc > 2) {
02343       if (!strcasecmp(a->argv[2], "stats")) {
02344          stats = 1;
02345       } else {
02346          return CLI_SHOWUSAGE;
02347       }
02348    }
02349    if (stats) {
02350       /* Flush statistics */
02351       struct dundi_peer *p;
02352       int x;
02353       AST_LIST_LOCK(&peers);
02354       AST_LIST_TRAVERSE(&peers, p, list) {
02355          for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02356             if (p->lookups[x])
02357                ast_free(p->lookups[x]);
02358             p->lookups[x] = NULL;
02359             p->lookuptimes[x] = 0;
02360          }
02361          p->avgms = 0;
02362       }
02363       AST_LIST_UNLOCK(&peers);
02364    } else {
02365       ast_db_deltree("dundi/cache", NULL);
02366       ast_cli(a->fd, "DUNDi Cache Flushed\n");
02367    }
02368    return CLI_SUCCESS;
02369 }
02370 
02371 static char *model2str(int model)
02372 {
02373    switch(model) {
02374    case DUNDI_MODEL_INBOUND:
02375       return "Inbound";
02376    case DUNDI_MODEL_OUTBOUND:
02377       return "Outbound";
02378    case DUNDI_MODEL_SYMMETRIC:
02379       return "Symmetric";
02380    default:
02381       return "Unknown";
02382    }
02383 }
02384 
02385 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
02386 {
02387    int which=0, len;
02388    char *ret = NULL;
02389    struct dundi_peer *p;
02390    char eid_str[20];
02391 
02392    if (pos != rpos)
02393       return NULL;
02394    AST_LIST_LOCK(&peers);
02395    len = strlen(word);
02396    AST_LIST_TRAVERSE(&peers, p, list) {
02397       const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
02398       if (!strncasecmp(word, s, len) && ++which > state) {
02399          ret = ast_strdup(s);
02400          break;
02401       }
02402    }
02403    AST_LIST_UNLOCK(&peers);
02404    return ret;
02405 }
02406 
02407 static int rescomp(const void *a, const void *b)
02408 {
02409    const struct dundi_result *resa, *resb;
02410    resa = a;
02411    resb = b;
02412    if (resa->weight < resb->weight)
02413       return -1;
02414    if (resa->weight > resb->weight)
02415       return 1;
02416    return 0;
02417 }
02418 
02419 static void sort_results(struct dundi_result *results, int count)
02420 {
02421    qsort(results, count, sizeof(results[0]), rescomp);
02422 }
02423 
02424 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02425 {
02426    int res;
02427    char tmp[256];
02428    char fs[80] = "";
02429    char *context;
02430    int x;
02431    int bypass = 0;
02432    struct dundi_result dr[MAX_RESULTS];
02433    struct timeval start;
02434    switch (cmd) {
02435    case CLI_INIT:
02436       e->command = "dundi lookup";
02437       e->usage =
02438          "Usage: dundi lookup <number>[@context] [bypass]\n"
02439          "       Lookup the given number within the given DUNDi context\n"
02440          "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
02441          "keyword is specified.\n";
02442       return NULL;
02443    case CLI_GENERATE:
02444       return NULL;
02445    }
02446 
02447    if ((a->argc < 3) || (a->argc > 4)) {
02448       return CLI_SHOWUSAGE;
02449    }
02450    if (a->argc > 3) {
02451       if (!strcasecmp(a->argv[3], "bypass")) {
02452          bypass=1;
02453       } else {
02454          return CLI_SHOWUSAGE;
02455       }
02456    }
02457    ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02458    context = strchr(tmp, '@');
02459    if (context) {
02460       *context = '\0';
02461       context++;
02462    }
02463    start = ast_tvnow();
02464    res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
02465 
02466    if (res < 0)
02467       ast_cli(a->fd, "DUNDi lookup returned error.\n");
02468    else if (!res)
02469       ast_cli(a->fd, "DUNDi lookup returned no results.\n");
02470    else
02471       sort_results(dr, res);
02472    for (x=0;x<res;x++) {
02473       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));
02474       ast_cli(a->fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
02475    }
02476    ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02477    return CLI_SUCCESS;
02478 }
02479 
02480 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02481 {
02482    int res;
02483    char tmp[256];
02484    char *context;
02485    struct timeval start;
02486    switch (cmd) {
02487    case CLI_INIT:
02488       e->command = "dundi precache";
02489       e->usage =
02490          "Usage: dundi precache <number>[@context]\n"
02491          "       Lookup the given number within the given DUNDi context\n"
02492          "(or e164 if none is specified) and precaches the results to any\n"
02493          "upstream DUNDi push servers.\n";
02494       return NULL;
02495    case CLI_GENERATE:
02496       return NULL;
02497    }
02498    if ((a->argc < 3) || (a->argc > 3)) {
02499       return CLI_SHOWUSAGE;
02500    }
02501    ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02502    context = strchr(tmp, '@');
02503    if (context) {
02504       *context = '\0';
02505       context++;
02506    }
02507    start = ast_tvnow();
02508    res = dundi_precache(context, tmp);
02509 
02510    if (res < 0)
02511       ast_cli(a->fd, "DUNDi precache returned error.\n");
02512    else if (!res)
02513       ast_cli(a->fd, "DUNDi precache returned no error.\n");
02514    ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02515    return CLI_SUCCESS;
02516 }
02517 
02518 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02519 {
02520    int res;
02521    char tmp[256];
02522    char *context;
02523    dundi_eid eid;
02524    struct dundi_entity_info dei;
02525    switch (cmd) {
02526    case CLI_INIT:
02527       e->command = "dundi query";
02528       e->usage =
02529          "Usage: dundi query <entity>[@context]\n"
02530          "       Attempts to retrieve contact information for a specific\n"
02531          "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
02532          "e164 if none is specified).\n";
02533       return NULL;
02534    case CLI_GENERATE:
02535       return NULL;
02536    }
02537    if ((a->argc < 3) || (a->argc > 3)) {
02538       return CLI_SHOWUSAGE;
02539    }
02540    if (ast_str_to_eid(&eid, a->argv[2])) {
02541       ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
02542       return CLI_SHOWUSAGE;
02543    }
02544    ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02545    context = strchr(tmp, '@');
02546    if (context) {
02547       *context = '\0';
02548       context++;
02549    }
02550    res = dundi_query_eid(&dei, context, eid);
02551    if (res < 0)
02552       ast_cli(a->fd, "DUNDi Query EID returned error.\n");
02553    else if (!res)
02554       ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
02555    else {
02556       ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
02557       ast_cli(a->fd, "Department:      %s\n", dei.orgunit);
02558       ast_cli(a->fd, "Organization:    %s\n", dei.org);
02559       ast_cli(a->fd, "City/Locality:   %s\n", dei.locality);
02560       ast_cli(a->fd, "State/Province:  %s\n", dei.stateprov);
02561       ast_cli(a->fd, "Country:         %s\n", dei.country);
02562       ast_cli(a->fd, "E-mail:          %s\n", dei.email);
02563       ast_cli(a->fd, "Phone:           %s\n", dei.phone);
02564       ast_cli(a->fd, "IP Address:      %s\n", dei.ipaddr);
02565    }
02566    return CLI_SUCCESS;
02567 }
02568 
02569 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02570 {
02571    struct dundi_peer *peer;
02572    struct permission *p;
02573    char *order;
02574    char eid_str[20];
02575    int x, cnt;
02576    switch (cmd) {
02577    case CLI_INIT:
02578       e->command = "dundi show peer";
02579       e->usage =
02580          "Usage: dundi show peer [peer]\n"
02581          "       Provide a detailed description of a specifid DUNDi peer.\n";
02582       return NULL;
02583    case CLI_GENERATE:
02584       return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
02585    }
02586    if (a->argc != 4) {
02587       return CLI_SHOWUSAGE;
02588    }
02589    AST_LIST_LOCK(&peers);
02590    AST_LIST_TRAVERSE(&peers, peer, list) {
02591       if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
02592          break;
02593    }
02594    if (peer) {
02595       switch(peer->order) {
02596       case 0:
02597          order = "Primary";
02598          break;
02599       case 1:
02600          order = "Secondary";
02601          break;
02602       case 2:
02603          order = "Tertiary";
02604          break;
02605       case 3:
02606          order = "Quartiary";
02607          break;
02608       default:
02609          order = "Unknown";
02610       }
02611       ast_cli(a->fd, "Peer:    %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02612       ast_cli(a->fd, "Model:   %s\n", model2str(peer->model));
02613       ast_cli(a->fd, "Order:   %s\n", order);
02614       ast_cli(a->fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
02615       ast_cli(a->fd, "Port:    %d\n", ntohs(peer->addr.sin_port));
02616       ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
02617       ast_cli(a->fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
02618       ast_cli(a->fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
02619       ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
02620       if (!AST_LIST_EMPTY(&peer->include))
02621          ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
02622       AST_LIST_TRAVERSE(&peer->include, p, list)
02623          ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
02624       if (!AST_LIST_EMPTY(&peer->permit))
02625          ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
02626       AST_LIST_TRAVERSE(&peer->permit, p, list)
02627          ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
02628       cnt = 0;
02629       for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02630          if (peer->lookups[x]) {
02631             if (!cnt)
02632                ast_cli(a->fd, "Last few query times:\n");
02633             ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
02634             cnt++;
02635          }
02636       }
02637       if (cnt)
02638          ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
02639    } else
02640       ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
02641    AST_LIST_UNLOCK(&peers);
02642    return CLI_SUCCESS;
02643 }
02644 
02645 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02646 {
02647 #define FORMAT2 "%-20.20s %-15.15s     %-6.6s %-10.10s %-8.8s %-15.15s\n"
02648 #define FORMAT "%-20.20s %-15.15s %s %-6d %-10.10s %-8.8s %-15.15s\n"
02649    struct dundi_peer *peer;
02650    int registeredonly=0;
02651    char avgms[20];
02652    char eid_str[20];
02653    int online_peers = 0;
02654    int offline_peers = 0;
02655    int unmonitored_peers = 0;
02656    int total_peers = 0;
02657    switch (cmd) {
02658    case CLI_INIT:
02659       e->command = "dundi show peers [registered|include|exclude|begin]";
02660       e->usage =
02661          "Usage: dundi show peers [registered|include|exclude|begin]\n"
02662          "       Lists all known DUNDi peers.\n"
02663          "       If 'registered' is present, only registered peers are shown.\n";
02664       return NULL;
02665    case CLI_GENERATE:
02666       return NULL;
02667    }
02668 
02669    if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
02670       return CLI_SHOWUSAGE;
02671    }
02672    if ((a->argc == 4)) {
02673       if (!strcasecmp(a->argv[3], "registered")) {
02674          registeredonly = 1;
02675       } else {
02676          return CLI_SHOWUSAGE;
02677       }
02678    }
02679    AST_LIST_LOCK(&peers);
02680    ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
02681    AST_LIST_TRAVERSE(&peers, peer, list) {
02682       char status[20];
02683       int print_line = -1;
02684       char srch[2000];
02685       total_peers++;
02686       if (registeredonly && !peer->addr.sin_addr.s_addr)
02687          continue;
02688       if (peer->maxms) {
02689          if (peer->lastms < 0) {
02690             strcpy(status, "UNREACHABLE");
02691             offline_peers++;
02692          }
02693          else if (peer->lastms > peer->maxms) {
02694             snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
02695             offline_peers++;
02696          }
02697          else if (peer->lastms) {
02698             snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
02699             online_peers++;
02700          }
02701          else {
02702             strcpy(status, "UNKNOWN");
02703             offline_peers++;
02704          }
02705       } else {
02706          strcpy(status, "Unmonitored");
02707          unmonitored_peers++;
02708       }
02709       if (peer->avgms)
02710          snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
02711       else
02712          strcpy(avgms, "Unavail");
02713       snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
02714                peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
02715                peer->dynamic ? "(D)" : "(S)", ntohs(peer->addr.sin_port), model2str(peer->model), avgms, status);
02716 
02717                 if (a->argc == 5) {
02718                   if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
02719                         print_line = -1;
02720                    } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
02721                         print_line = 1;
02722                    } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
02723                         print_line = -1;
02724                    } else {
02725                         print_line = 0;
02726                   }
02727                 }
02728 
02729         if (print_line) {
02730          ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
02731                peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
02732                peer->dynamic ? "(D)" : "(S)", ntohs(peer->addr.sin_port), model2str(peer->model), avgms, status);
02733       }
02734    }
02735    ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
02736    AST_LIST_UNLOCK(&peers);
02737    return CLI_SUCCESS;
02738 #undef FORMAT
02739 #undef FORMAT2
02740 }
02741 
02742 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02743 {
02744 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
02745 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
02746    struct dundi_transaction *trans;
02747    switch (cmd) {
02748    case CLI_INIT:
02749       e->command = "dundi show trans";
02750       e->usage =
02751          "Usage: dundi show trans\n"
02752          "       Lists all known DUNDi transactions.\n";
02753       return NULL;
02754    case CLI_GENERATE:
02755       return NULL;
02756    }
02757    if (a->argc != 3) {
02758       return CLI_SHOWUSAGE;
02759    }
02760    AST_LIST_LOCK(&peers);
02761    ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
02762    AST_LIST_TRAVERSE(&alltrans, trans, all) {
02763       ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
02764          ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
02765    }
02766    AST_LIST_UNLOCK(&peers);
02767    return CLI_SUCCESS;
02768 #undef FORMAT
02769 #undef FORMAT2
02770 }
02771 
02772 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02773 {
02774    char eid_str[20];
02775    switch (cmd) {
02776    case CLI_INIT:
02777       e->command = "dundi show entityid";
02778       e->usage =
02779          "Usage: dundi show entityid\n"
02780          "       Displays the global entityid for this host.\n";
02781       return NULL;
02782    case CLI_GENERATE:
02783       return NULL;
02784    }
02785    if (a->argc != 3) {
02786       return CLI_SHOWUSAGE;
02787    }
02788    AST_LIST_LOCK(&peers);
02789    ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
02790    AST_LIST_UNLOCK(&peers);
02791    ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
02792    return CLI_SUCCESS;
02793 }
02794 
02795 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02796 {
02797 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
02798 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
02799    struct dundi_request *req;
02800    char eidstr[20];
02801    switch (cmd) {
02802    case CLI_INIT:
02803       e->command = "dundi show requests";
02804       e->usage =
02805          "Usage: dundi show requests\n"
02806          "       Lists all known pending DUNDi requests.\n";
02807       return NULL;
02808    case CLI_GENERATE:
02809       return NULL;
02810    }
02811    if (a->argc != 3) {
02812       return CLI_SHOWUSAGE;
02813    }
02814    AST_LIST_LOCK(&peers);
02815    ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
02816    AST_LIST_TRAVERSE(&requests, req, list) {
02817       ast_cli(a->fd, FORMAT, req->number, req->dcontext,
02818          dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
02819    }
02820    AST_LIST_UNLOCK(&peers);
02821    return CLI_SUCCESS;
02822 #undef FORMAT
02823 #undef FORMAT2
02824 }
02825 
02826 /* Grok-a-dial DUNDi */
02827 
02828 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02829 {
02830 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
02831 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
02832    struct dundi_mapping *map;
02833    char fs[256];
02834    char weight[8];
02835    switch (cmd) {
02836    case CLI_INIT:
02837       e->command = "dundi show mappings";
02838       e->usage =
02839          "Usage: dundi show mappings\n"
02840          "       Lists all known DUNDi mappings.\n";
02841       return NULL;
02842    case CLI_GENERATE:
02843       return NULL;
02844    }
02845    if (a->argc != 3) {
02846       return CLI_SHOWUSAGE;
02847    }
02848    AST_LIST_LOCK(&peers);
02849    ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
02850    AST_LIST_TRAVERSE(&mappings, map, list) {
02851       snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map, NULL));
02852       ast_cli(a->fd, FORMAT, map->dcontext, weight,
02853          ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
02854          dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
02855    }
02856    AST_LIST_UNLOCK(&peers);
02857    return CLI_SUCCESS;
02858 #undef FORMAT
02859 #undef FORMAT2
02860 }
02861 
02862 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02863 {
02864 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
02865 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
02866    struct dundi_precache_queue *qe;
02867    int h,m,s;
02868    time_t now;
02869    switch (cmd) {
02870    case CLI_INIT:
02871       e->command = "dundi show precache";
02872       e->usage =
02873          "Usage: dundi show precache\n"
02874          "       Lists all known DUNDi scheduled precache updates.\n";
02875       return NULL;
02876    case CLI_GENERATE:
02877       return NULL;
02878    }
02879    if (a->argc != 3) {
02880       return CLI_SHOWUSAGE;
02881    }
02882    time(&now);
02883    ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
02884    AST_LIST_LOCK(&pcq);
02885    AST_LIST_TRAVERSE(&pcq, qe, list) {
02886       s = qe->expiration - now;
02887       h = s / 3600;
02888       s = s % 3600;
02889       m = s / 60;
02890       s = s % 60;
02891       ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
02892    }
02893    AST_LIST_UNLOCK(&pcq);
02894 
02895    return CLI_SUCCESS;
02896 #undef FORMAT
02897 #undef FORMAT2
02898 }
02899 
02900 static char *dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02901 {
02902 #define FORMAT2 "%-12.12s %-16.16s %-10.10s  %-18s %-7s %s\n"
02903 #define FORMAT "%-12.12s %-16.16s %6d sec  %-18s %-7d %s/%s (%s)\n"
02904         struct ast_db_entry *db_tree, *db_entry;
02905    int cnt = 0;
02906    time_t ts, now;
02907    dundi_eid src_eid;
02908    char src_eid_str[20];
02909    int expiry, tech, weight;
02910    struct ast_flags flags;
02911    char fs[256];
02912    int length;
02913    char *ptr, *term, *src, *number, *context, *dst;
02914 
02915    switch (cmd) {
02916    case CLI_INIT:
02917       e->command = "dundi show cache";
02918       e->usage =
02919       "Usage: dundi show cache\n"
02920       "       Lists all DUNDi cache entries.\n";
02921       return NULL;
02922    case CLI_GENERATE:
02923       return NULL;
02924    }
02925 
02926    if (a->argc != 3) {
02927       return CLI_SHOWUSAGE;
02928    }
02929 
02930    time(&now);
02931    db_tree = ast_db_gettree("dundi/cache", NULL);
02932    ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration", "From", "Weight", "Destination (Flags)");
02933    for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
02934       if ((strncmp(db_entry->key, "/dundi/cache/hint/", 18) == 0) || ast_get_time_t(db_entry->data, &ts, 0, &length)) {
02935          continue;
02936       }
02937 
02938       expiry = ts - now;
02939 
02940       if (expiry <= 0) {
02941          continue;
02942       }
02943 
02944       ptr = db_entry->key + sizeof("/dundi/cache");
02945       strtok(ptr, "/");
02946       number = strtok(NULL, "/");
02947       context = strtok(NULL, "/");
02948       ptr = strtok(NULL, "/");
02949 
02950       if (*ptr != 'e') {
02951          continue;
02952       }
02953 
02954       ptr = db_entry->data + length + 1;
02955 
02956       if ((sscanf(ptr, "%30d/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) != 3)) {
02957          continue;
02958       }
02959 
02960       ptr += length;
02961       dst = ptr;
02962       term = strchr(ptr, '|');
02963 
02964       if (!term) {
02965          continue;
02966       }
02967 
02968       /* Ok, at this point we know we aren't going to skp the entry, so we go ahead and increment the count. */
02969       cnt++;
02970 
02971       *term = '\0';
02972       src = strrchr(ptr, '/');
02973       dundi_eid_zero(&src_eid);
02974 
02975       if (src) {
02976          *src = '\0';
02977          src++;
02978          dundi_str_short_to_eid(&src_eid, src);
02979          ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
02980       }
02981 
02982       ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str, weight, tech2str(tech), dst, dundi_flags2str(fs, sizeof(fs), flags.flags));
02983    }
02984 
02985    ast_cli(a->fd, "Number of entries: %d\n", cnt);
02986    ast_db_freetree(db_tree);
02987 
02988    return CLI_SUCCESS;
02989 #undef FORMAT
02990 #undef FORMAT2
02991 }
02992 
02993 static char *dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02994 {
02995 #define FORMAT2 "%-12.12s %-16.16s %-10.10s  %-18s\n"
02996 #define FORMAT "%-12.12s %-16.16s %6d sec  %-18s\n"
02997    struct ast_db_entry *db_tree, *db_entry;
02998    int cnt = 0;
02999    time_t ts, now;
03000    dundi_eid src_eid;
03001    char src_eid_str[20];
03002    int expiry;
03003    int length;
03004    char *ptr, *src, *number, *context;
03005 
03006    switch (cmd) {
03007    case CLI_INIT:
03008       e->command = "dundi show hints";
03009       e->usage =
03010          "Usage: dundi show hints\n"
03011          "       Lists all DUNDi 'DONTASK' hints in the cache.\n";
03012       return NULL;
03013    case CLI_GENERATE:
03014       return NULL;
03015    }
03016 
03017    if (a->argc != 3) {
03018       return CLI_SHOWUSAGE;
03019    }
03020 
03021    time(&now);
03022    db_tree = ast_db_gettree("dundi/cache/hint", NULL);
03023    ast_cli(a->fd, FORMAT2, "Prefix", "Context", "Expiration", "From");
03024 
03025    for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
03026       if (ast_get_time_t(db_entry->data, &ts, 0, &length)) {
03027          continue;
03028       }
03029 
03030       expiry = ts - now;
03031 
03032       if (expiry <= 0) {
03033          continue;
03034       }
03035 
03036       ptr = db_entry->key + sizeof("/dundi/cache/hint");
03037       src = strtok(ptr, "/");
03038       number = strtok(NULL, "/");
03039       context = strtok(NULL, "/");
03040       ptr = strtok(NULL, "/");
03041 
03042       if (*ptr != 'e') {
03043          continue;
03044       }
03045 
03046       cnt++;
03047       dundi_str_short_to_eid(&src_eid, src);
03048       ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
03049       ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str);
03050    }
03051 
03052    ast_cli(a->fd, "Number of entries: %d\n", cnt);
03053    ast_db_freetree(db_tree);
03054 
03055    return CLI_SUCCESS;
03056 #undef FORMAT
03057 #undef FORMAT2
03058 }
03059 
03060 static struct ast_cli_entry cli_dundi[] = {
03061    AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging"),
03062    AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records"),
03063    AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
03064    AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
03065    AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
03066    AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
03067    AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
03068    AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
03069    AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
03070    AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
03071    AST_CLI_DEFINE(dundi_show_cache, "Show DUNDi cache"),
03072    AST_CLI_DEFINE(dundi_show_hints, "Show DUNDi hints in the cache"),
03073    AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
03074    AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
03075    AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
03076 };
03077 
03078 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
03079 {
03080    struct dundi_transaction *trans;
03081    int tid;
03082 
03083    /* Don't allow creation of transactions to non-registered peers */
03084    if (p && !p->addr.sin_addr.s_addr)
03085       return NULL;
03086    tid = get_trans_id();
03087    if (tid < 1)
03088       return NULL;
03089    if (!(trans = ast_calloc(1, sizeof(*trans))))
03090       return NULL;
03091 
03092    if (global_storehistory) {
03093       trans->start = ast_tvnow();
03094       ast_set_flag(trans, FLAG_STOREHIST);
03095    }
03096    trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
03097    trans->autokillid = -1;
03098    if (p) {
03099       apply_peer(trans, p);
03100       if (!p->sentfullkey)
03101          ast_set_flag(trans, FLAG_SENDFULLKEY);
03102    }
03103    trans->strans = tid;
03104    AST_LIST_INSERT_HEAD(&alltrans, trans, all);
03105 
03106    return trans;
03107 }
03108 
03109 static int dundi_xmit(struct dundi_packet *pack)
03110 {
03111    int res;
03112    if (dundidebug)
03113       dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
03114    res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
03115    if (res < 0) {
03116       ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
03117          ast_inet_ntoa(pack->parent->addr.sin_addr),
03118          ntohs(pack->parent->addr.sin_port), strerror(errno));
03119    }
03120    if (res > 0)
03121       res = 0;
03122    return res;
03123 }
03124 
03125 static void destroy_packet(struct dundi_packet *pack, int needfree)
03126 {
03127    if (pack->parent)
03128       AST_LIST_REMOVE(&pack->parent->packets, pack, list);
03129    AST_SCHED_DEL(sched, pack->retransid);
03130    if (needfree)
03131       ast_free(pack);
03132 }
03133 
03134 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
03135 {
03136    struct dundi_peer *peer;
03137    int ms;
03138    int x;
03139    int cnt;
03140    char eid_str[20];
03141    if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
03142       AST_LIST_TRAVERSE(&peers, peer, list) {
03143          if (peer->regtrans == trans)
03144             peer->regtrans = NULL;
03145          if (peer->qualtrans == trans) {
03146             if (fromtimeout) {
03147                if (peer->lastms > -1)
03148                   ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
03149                peer->lastms = -1;
03150             } else {
03151                ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
03152                if (ms < 1)
03153                   ms = 1;
03154                if (ms < peer->maxms) {
03155                   if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
03156                      ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
03157                } else if (peer->lastms < peer->maxms) {
03158                   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);
03159                }
03160                peer->lastms = ms;
03161             }
03162             peer->qualtrans = NULL;
03163          }
03164          if (ast_test_flag(trans, FLAG_STOREHIST)) {
03165             if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
03166                if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
03167                   peer->avgms = 0;
03168                   cnt = 0;
03169                   if (peer->lookups[DUNDI_TIMING_HISTORY-1])
03170                      ast_free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
03171                   for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
03172                      peer->lookuptimes[x] = peer->lookuptimes[x-1];
03173                      peer->lookups[x] = peer->lookups[x-1];
03174                      if (peer->lookups[x]) {
03175                         peer->avgms += peer->lookuptimes[x];
03176                         cnt++;
03177                      }
03178                   }
03179                   peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
03180                   peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
03181                   if (peer->lookups[0]) {
03182                      sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
03183                      peer->avgms += peer->lookuptimes[0];
03184                      cnt++;
03185                   }
03186                   if (cnt)
03187                      peer->avgms /= cnt;
03188                }
03189             }
03190          }
03191       }
03192    }
03193    if (trans->parent) {
03194       /* Unlink from parent if appropriate */
03195       AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
03196       if (AST_LIST_EMPTY(&trans->parent->trans)) {
03197          /* Wake up sleeper */
03198          if (trans->parent->pfds[1] > -1) {
03199             if (write(trans->parent->pfds[1], "killa!", 6) < 0) {
03200                ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
03201             }
03202          }
03203       }
03204    }
03205    /* Unlink from all trans */
03206    AST_LIST_REMOVE(&alltrans, trans, all);
03207    destroy_packets(&trans->packets);
03208    destroy_packets(&trans->lasttrans);
03209    AST_SCHED_DEL(sched, trans->autokillid);
03210    if (trans->thread) {
03211       /* If used by a thread, mark as dead and be done */
03212       ast_set_flag(trans, FLAG_DEAD);
03213    } else
03214       ast_free(trans);
03215 }
03216 
03217 static int dundi_rexmit(const void *data)
03218 {
03219    struct dundi_packet *pack = (struct dundi_packet *)data;
03220    int res;
03221    AST_LIST_LOCK(&peers);
03222    if (pack->retrans < 1) {
03223       pack->retransid = -1;
03224       if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
03225          ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
03226             ast_inet_ntoa(pack->parent->addr.sin_addr),
03227             ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
03228       destroy_trans(pack->parent, 1);
03229       res = 0;
03230    } else {
03231       /* Decrement retransmission, try again */
03232       pack->retrans--;
03233       dundi_xmit(pack);
03234       res = 1;
03235    }
03236    AST_LIST_UNLOCK(&peers);
03237    return res;
03238 }
03239 
03240 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
03241 {
03242    struct dundi_packet *pack;
03243    int res;
03244    int len;
03245    char eid_str[20];
03246    len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
03247    /* Reserve enough space for encryption */
03248    if (ast_test_flag(trans, FLAG_ENCRYPT))
03249       len += 384;
03250    pack = ast_calloc(1, len);
03251    if (pack) {
03252       pack->h = (struct dundi_hdr *)(pack->data);
03253       if (cmdresp != DUNDI_COMMAND_ACK) {
03254          pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
03255          pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
03256          AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
03257       }
03258       pack->parent = trans;
03259       pack->h->strans = htons(trans->strans);
03260       pack->h->dtrans = htons(trans->dtrans);
03261       pack->h->iseqno = trans->iseqno;
03262       pack->h->oseqno = trans->oseqno;
03263       pack->h->cmdresp = cmdresp;
03264       pack->datalen = sizeof(struct dundi_hdr);
03265       if (ied) {
03266          memcpy(pack->h->ies, ied->buf, ied->pos);
03267          pack->datalen += ied->pos;
03268       }
03269       if (final) {
03270          pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
03271          ast_set_flag(trans, FLAG_FINAL);
03272       }
03273       pack->h->cmdflags = flags;
03274       if (cmdresp != DUNDI_COMMAND_ACK) {
03275          trans->oseqno++;
03276          trans->oseqno = trans->oseqno % 256;
03277       }
03278       trans->aseqno = trans->iseqno;
03279       /* If we have their public key, encrypt */
03280       if (ast_test_flag(trans, FLAG_ENCRYPT)) {
03281          switch(cmdresp) {
03282          case DUNDI_COMMAND_REGREQ:
03283          case DUNDI_COMMAND_REGRESPONSE:
03284          case DUNDI_COMMAND_DPDISCOVER:
03285          case DUNDI_COMMAND_DPRESPONSE:
03286          case DUNDI_COMMAND_EIDQUERY:
03287          case DUNDI_COMMAND_EIDRESPONSE:
03288          case DUNDI_COMMAND_PRECACHERQ:
03289          case DUNDI_COMMAND_PRECACHERP:
03290             if (dundidebug)
03291                dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
03292             res = dundi_encrypt(trans, pack);
03293             break;
03294          default:
03295             res = 0;
03296          }
03297       } else
03298          res = 0;
03299       if (!res)
03300          res = dundi_xmit(pack);
03301       if (res)
03302          ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03303 
03304       if (cmdresp == DUNDI_COMMAND_ACK)
03305          ast_free(pack);
03306       return res;
03307    }
03308    return -1;
03309 }
03310 
03311 static int do_autokill(const void *data)
03312 {
03313    struct dundi_transaction *trans = (struct dundi_transaction *)data;
03314    char eid_str[20];
03315    ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
03316       ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03317    trans->autokillid = -1;
03318    destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
03319    return 0;
03320 }
03321 
03322 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
03323 {
03324    struct dundi_peer *p;
03325    if (!ast_eid_cmp(eid, us)) {
03326       dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03327       return;
03328    }
03329    AST_LIST_LOCK(&peers);
03330    AST_LIST_TRAVERSE(&peers, p, list) {
03331       if (!ast_eid_cmp(&p->eid, eid)) {
03332          if (has_permission(&p->include, context))
03333             dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03334          else
03335             dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03336          break;
03337       }
03338    }
03339    if (!p)
03340       dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03341    AST_LIST_UNLOCK(&peers);
03342 }
03343 
03344 static int dundi_discover(struct dundi_transaction *trans)
03345 {
03346    struct dundi_ie_data ied;
03347    int x;
03348    if (!trans->parent) {
03349       ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03350       return -1;
03351    }
03352    memset(&ied, 0, sizeof(ied));
03353    dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03354    if (!dundi_eid_zero(&trans->us_eid))
03355       dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
03356    for (x=0;x<trans->eidcount;x++)
03357       dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
03358    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03359    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03360    dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03361    if (trans->parent->cbypass)
03362       dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
03363    if (trans->autokilltimeout)
03364       trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03365    return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
03366 }
03367 
03368 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
03369 {
03370    struct dundi_ie_data ied;
03371    int x, res;
03372    int max = 999999;
03373    int expiration = dundi_cache_time;
03374    int ouranswers=0;
03375    dundi_eid *avoid[1] = { NULL, };
03376    int direct[1] = { 0, };
03377    struct dundi_result dr[MAX_RESULTS];
03378    struct dundi_hint_metadata hmd;
03379    if (!trans->parent) {
03380       ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03381       return -1;
03382    }
03383    memset(&hmd, 0, sizeof(hmd));
03384    memset(&dr, 0, sizeof(dr));
03385    /* Look up the answers we're going to include */
03386    for (x=0;x<mapcount;x++)
03387       ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
03388    if (ouranswers < 0)
03389       ouranswers = 0;
03390    for (x=0;x<ouranswers;x++) {
03391       if (dr[x].weight < max)
03392          max = dr[x].weight;
03393    }
03394    if (max) {
03395       /* If we do not have a canonical result, keep looking */
03396       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);
03397       if (res > 0) {
03398          /* Append answer in result */
03399          ouranswers += res;
03400       }
03401    }
03402 
03403    if (ouranswers > 0) {
03404       *foundanswers += ouranswers;
03405       memset(&ied, 0, sizeof(ied));
03406       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03407       if (!dundi_eid_zero(&trans->us_eid))
03408          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03409       for (x=0;x<trans->eidcount;x++)
03410          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03411       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03412       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03413       dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03414       for (x=0;x<ouranswers;x++) {
03415          /* Add answers */
03416          if (dr[x].expiration && (expiration > dr[x].expiration))
03417             expiration = dr[x].expiration;
03418          dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
03419       }
03420       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
03421       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
03422       if (trans->autokilltimeout)
03423          trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03424       if (expiration < *minexp)
03425          *minexp = expiration;
03426       return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
03427    } else {
03428       /* Oops, nothing to send... */
03429       destroy_trans(trans, 0);
03430       return 0;
03431    }
03432 }
03433 
03434 static int dundi_query(struct dundi_transaction *trans)
03435 {
03436    struct dundi_ie_data ied;
03437    int x;
03438    if (!trans->parent) {
03439       ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
03440       return -1;
03441    }
03442    memset(&ied, 0, sizeof(ied));
03443    dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03444    if (!dundi_eid_zero(&trans->us_eid))
03445       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03446    for (x=0;x<trans->eidcount;x++)
03447       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03448    dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
03449    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03450    dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03451    if (trans->autokilltimeout)
03452       trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03453    return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
03454 }
03455 
03456 static int discover_transactions(struct dundi_request *dr)
03457 {
03458    struct dundi_transaction *trans;
03459    AST_LIST_LOCK(&peers);
03460    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03461       dundi_discover(trans);
03462    }
03463    AST_LIST_UNLOCK(&peers);
03464    return 0;
03465 }
03466 
03467 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
03468 {
03469    struct dundi_transaction *trans;
03470 
03471    /* Mark all as "in thread" so they don't disappear */
03472    AST_LIST_LOCK(&peers);
03473    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03474       if (trans->thread)
03475          ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
03476       trans->thread = 1;
03477    }
03478    AST_LIST_UNLOCK(&peers);
03479 
03480    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03481       if (!ast_test_flag(trans, FLAG_DEAD))
03482          precache_trans(trans, maps, mapcount, expiration, foundanswers);
03483    }
03484 
03485    /* Cleanup any that got destroyed in the mean time */
03486    AST_LIST_LOCK(&peers);
03487    AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
03488       trans->thread = 0;
03489       if (ast_test_flag(trans, FLAG_DEAD)) {
03490          ast_debug(1, "Our transaction went away!\n");
03491          /* This is going to remove the transaction from the dundi_request's list, as well
03492           * as the global transactions list */
03493          destroy_trans(trans, 0);
03494       }
03495    }
03496    AST_LIST_TRAVERSE_SAFE_END
03497    AST_LIST_UNLOCK(&peers);
03498 
03499    return 0;
03500 }
03501 
03502 static int query_transactions(struct dundi_request *dr)
03503 {
03504    struct dundi_transaction *trans;
03505 
03506    AST_LIST_LOCK(&peers);
03507    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03508       dundi_query(trans);
03509    }
03510    AST_LIST_UNLOCK(&peers);
03511 
03512    return 0;
03513 }
03514 
03515 static int optimize_transactions(struct dundi_request *dr, int order)
03516 {
03517    /* Minimize the message propagation through DUNDi by
03518       alerting the network to hops which should be not be considered */
03519    struct dundi_transaction *trans;
03520    struct dundi_peer *peer;
03521    dundi_eid tmp;
03522    int x;
03523    int needpush;
03524 
03525    AST_LIST_LOCK(&peers);
03526    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03527       /* Pop off the true root */
03528       if (trans->eidcount) {
03529          tmp = trans->eids[--trans->eidcount];
03530          needpush = 1;
03531       } else {
03532          tmp = trans->us_eid;
03533          needpush = 0;
03534       }
03535 
03536       AST_LIST_TRAVERSE(&peers, peer, list) {
03537          if (ast_eid_cmp(&peer->eid, &empty_eid) &&         /* peer's eid is not empty (in case of dynamic peers) */
03538             (peer->lastms > -1) &&                    /* peer is reachable */
03539             has_permission(&peer->include, dr->dcontext) && /* peer has destination context */
03540             ast_eid_cmp(&peer->eid, &trans->them_eid) && /* peer is not transaction endpoint */
03541             (peer->order <= order)) {
03542             /* For each other transaction, make sure we don't
03543                ask this EID about the others if they're not
03544                already in the list */
03545             if (!ast_eid_cmp(&tmp, &peer->eid))
03546                x = -1;
03547             else {
03548                for (x=0;x<trans->eidcount;x++) {
03549                   if (!ast_eid_cmp(&trans->eids[x], &peer->eid))
03550                      break;
03551                }
03552             }
03553             if (x == trans->eidcount) {
03554                /* Nope not in the list, if needed, add us at the end since we're the source */
03555                if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
03556                   trans->eids[trans->eidcount++] = peer->eid;
03557                   /* Need to insert the real root (or us) at the bottom now as
03558                      a requirement now.  */
03559                   needpush = 1;
03560                }
03561             }
03562          }
03563       }
03564       /* If necessary, push the true root back on the end */
03565       if (needpush)
03566          trans->eids[trans->eidcount++] = tmp;
03567    }
03568    AST_LIST_UNLOCK(&peers);
03569 
03570    return 0;
03571 }
03572 
03573 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
03574 {
03575    struct dundi_transaction *trans;
03576    int x;
03577    char eid_str[20];
03578    char eid_str2[20];
03579 
03580    /* Ignore if not registered */
03581    if (!p->addr.sin_addr.s_addr)
03582       return 0;
03583    if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
03584       return 0;
03585 
03586    if (ast_strlen_zero(dr->number))
03587       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);
03588    else
03589       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);
03590 
03591    trans = create_transaction(p);
03592    if (!trans)
03593       return -1;
03594    trans->parent = dr;
03595    trans->ttl = ttl;
03596    for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
03597       trans->eids[x] = *avoid[x];
03598    trans->eidcount = x;
03599    AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
03600 
03601    return 0;
03602 }
03603 
03604 static void cancel_request(struct dundi_request *dr)
03605 {
03606    struct dundi_transaction *trans;
03607 
03608    AST_LIST_LOCK(&peers);
03609    while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
03610       /* Orphan transaction from request */
03611       trans->parent = NULL;
03612       /* Send final cancel */
03613       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
03614    }
03615    AST_LIST_UNLOCK(&peers);
03616 }
03617 
03618 static void abort_request(struct dundi_request *dr)
03619 {
03620    struct dundi_transaction *trans;
03621 
03622    AST_LIST_LOCK(&peers);
03623    while ((trans = AST_LIST_FIRST(&dr->trans))) {
03624       /* This will remove the transaction from the list */
03625       destroy_trans(trans, 0);
03626    }
03627    AST_LIST_UNLOCK(&peers);
03628 }
03629 
03630 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[])
03631 {
03632    struct dundi_peer *p;
03633    int x;
03634    int res;
03635    int pass;
03636    int allowconnect;
03637    char eid_str[20];
03638    AST_LIST_LOCK(&peers);
03639    AST_LIST_TRAVERSE(&peers, p, list) {
03640       if (modeselect == 1) {
03641          /* Send the precache to push upstreams only! */
03642          pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
03643          allowconnect = 1;
03644       } else {
03645          /* Normal lookup / EID query */
03646          pass = has_permission(&p->include, dr->dcontext);
03647          allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
03648       }
03649       if (skip) {
03650          if (!ast_eid_cmp(skip, &p->eid))
03651             pass = 0;
03652       }
03653       if (pass) {
03654          if (p->order <= order) {
03655             /* Check order first, then check cache, regardless of
03656                omissions, this gets us more likely to not have an
03657                affected answer. */
03658             if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
03659                res = 0;
03660                /* Make sure we haven't already seen it and that it won't
03661                   affect our answer */
03662                for (x=0;avoid[x];x++) {
03663                   if (!ast_eid_cmp(avoid[x], &p->eid) || !ast_eid_cmp(avoid[x], &p->us_eid)) {
03664                      /* If not a direct connection, it affects our answer */
03665                      if (directs && !directs[x])
03666                         ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
03667                      break;
03668                   }
03669                }
03670                /* Make sure we can ask */
03671                if (allowconnect) {
03672                   if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
03673                      /* Check for a matching or 0 cache entry */
03674                      append_transaction(dr, p, ttl, avoid);
03675                   } else {
03676                      ast_debug(1, "Avoiding '%s' in transaction\n", ast_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
03677                   }
03678                }
03679             }
03680             *foundcache |= res;
03681          } else if (!*skipped || (p->order < *skipped))
03682             *skipped = p->order;
03683       }
03684    }
03685    AST_LIST_UNLOCK(&peers);
03686 }
03687 
03688 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
03689 {
03690    struct dundi_request *cur;
03691    int res=0;
03692    char eid_str[20];
03693    AST_LIST_LOCK(&peers);
03694    AST_LIST_TRAVERSE(&requests, cur, list) {
03695       ast_debug(1, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
03696          dr->dcontext, dr->number);
03697       if (!strcasecmp(cur->dcontext, dr->dcontext) &&
03698           !strcasecmp(cur->number, dr->number) &&
03699           (!ast_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
03700          ast_debug(1, "Found existing query for '%s@%s' for '%s' crc '%08x'\n",
03701             cur->dcontext, cur->number, ast_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
03702          *pending = cur;
03703          res = 1;
03704          break;
03705       }
03706    }
03707    if (!res) {
03708       ast_debug(1, "Registering request for '%s@%s' on behalf of '%s' crc '%08x'\n",
03709             dr->number, dr->dcontext, ast_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
03710       /* Go ahead and link us in since nobody else is searching for this */
03711       AST_LIST_INSERT_HEAD(&requests, dr, list);
03712       *pending = NULL;
03713    }
03714    AST_LIST_UNLOCK(&peers);
03715    return res;
03716 }
03717 
03718 static void unregister_request(struct dundi_request *dr)
03719 {
03720    AST_LIST_LOCK(&peers);
03721    AST_LIST_REMOVE(&requests, dr, list);
03722    AST_LIST_UNLOCK(&peers);
03723 }
03724 
03725 static int check_request(struct dundi_request *dr)
03726 {
03727    struct dundi_request *cur;
03728 
03729    AST_LIST_LOCK(&peers);
03730    AST_LIST_TRAVERSE(&requests, cur, list) {
03731       if (cur == dr)
03732          break;
03733    }
03734    AST_LIST_UNLOCK(&peers);
03735 
03736    return cur ? 1 : 0;
03737 }
03738 
03739 static unsigned long avoid_crc32(dundi_eid *avoid[])
03740 {
03741    /* Idea is that we're calculating a checksum which is independent of
03742       the order that the EID's are listed in */
03743    uint32_t acrc32 = 0;
03744    int x;
03745    for (x=0;avoid[x];x++) {
03746       /* Order doesn't matter */
03747       if (avoid[x+1]) {
03748          acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
03749       }
03750    }
03751    return acrc32;
03752 }
03753 
03754 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[])
03755 {
03756    int res;
03757    struct dundi_request dr, *pending;
03758    dundi_eid *rooteid=NULL;
03759    int x;
03760    int ttlms;
03761    int ms;
03762    int foundcache;
03763    int skipped=0;
03764    int order=0;
03765    char eid_str[20];
03766    struct timeval start;
03767 
03768    /* Don't do anthing for a hungup channel */
03769    if (chan && ast_check_hangup(chan))
03770       return 0;
03771 
03772    ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03773 
03774    for (x=0;avoid[x];x++)
03775       rooteid = avoid[x];
03776    /* Now perform real check */
03777    memset(&dr, 0, sizeof(dr));
03778    if (pipe(dr.pfds)) {
03779       ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
03780       return -1;
03781    }
03782    dr.dr = result;
03783    dr.hmd = hmd;
03784    dr.maxcount = maxret;
03785    dr.expiration = *expiration;
03786    dr.cbypass = cbypass;
03787    dr.crc32 = avoid_crc32(avoid);
03788    ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
03789    ast_copy_string(dr.number, number, sizeof(dr.number));
03790    if (rooteid)
03791       dr.root_eid = *rooteid;
03792    res = register_request(&dr, &pending);
03793    if (res) {
03794       /* Already a request */
03795       if (rooteid && !ast_eid_cmp(&dr.root_eid, &pending->root_eid)) {
03796          /* This is on behalf of someone else.  Go ahead and close this out since
03797             they'll get their answer anyway. */
03798          ast_debug(1, "Oooh, duplicate request for '%s@%s' for '%s'\n",
03799             dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
03800          close(dr.pfds[0]);
03801          close(dr.pfds[1]);
03802          return -2;
03803       } else {
03804          /* Wait for the cache to populate */
03805          ast_debug(1, "Waiting for similar request for '%s@%s' for '%s'\n",
03806             dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
03807          start = ast_tvnow();
03808          while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
03809             /* XXX Would be nice to have a way to poll/select here XXX */
03810             /* XXX this is a busy wait loop!!! */
03811             usleep(1);
03812          }
03813          /* Continue on as normal, our cache should kick in */
03814       }
03815    }
03816    /* Create transactions */
03817    do {
03818       order = skipped;
03819       skipped = 0;
03820       foundcache = 0;
03821       build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
03822    } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
03823    /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
03824       do this earlier because we didn't know if we were going to have transactions
03825       or not. */
03826    if (!ttl) {
03827       ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
03828       abort_request(&dr);
03829       unregister_request(&dr);
03830       close(dr.pfds[0]);
03831       close(dr.pfds[1]);
03832       return 0;
03833    }
03834 
03835    /* Optimize transactions */
03836    optimize_transactions(&dr, order);
03837    /* Actually perform transactions */
03838    discover_transactions(&dr);
03839    /* Wait for transaction to come back */
03840    start = ast_tvnow();
03841    while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
03842       ms = 100;
03843       ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03844    }
03845    if (chan && ast_check_hangup(chan))
03846       ast_debug(1, "Hrm, '%s' hungup before their query for %s@%s finished\n", ast_channel_name(chan), dr.number, dr.dcontext);
03847    cancel_request(&dr);
03848    unregister_request(&dr);
03849    res = dr.respcount;
03850    *expiration = dr.expiration;
03851    close(dr.pfds[0]);
03852    close(dr.pfds[1]);
03853    return res;
03854 }
03855 
03856 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
03857 {
03858    struct dundi_hint_metadata hmd;
03859    dundi_eid *avoid[1] = { NULL, };
03860    int direct[1] = { 0, };
03861    int expiration = dundi_cache_time;
03862    memset(&hmd, 0, sizeof(hmd));
03863    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
03864    return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
03865 }
03866 
03867 static void reschedule_precache(const char *number, const char *context, int expiration)
03868 {
03869    int len;
03870    struct dundi_precache_queue *qe, *prev;
03871 
03872    AST_LIST_LOCK(&pcq);
03873    AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) {
03874       if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
03875          AST_LIST_REMOVE_CURRENT(list);
03876          break;
03877       }
03878    }
03879    AST_LIST_TRAVERSE_SAFE_END;
03880    if (!qe) {
03881       len = sizeof(*qe);
03882       len += strlen(number) + 1;
03883       len += strlen(context) + 1;
03884       if (!(qe = ast_calloc(1, len))) {
03885          AST_LIST_UNLOCK(&pcq);
03886          return;
03887       }
03888       strcpy(qe->number, number);
03889       qe->context = qe->number + strlen(number) + 1;
03890       strcpy(qe->context, context);
03891    }
03892    time(&qe->expiration);
03893    qe->expiration += expiration;
03894    if ((prev = AST_LIST_FIRST(&pcq))) {
03895       while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
03896          prev = AST_LIST_NEXT(prev, list);
03897       AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
03898    } else
03899       AST_LIST_INSERT_HEAD(&pcq, qe, list);
03900    AST_LIST_UNLOCK(&pcq);
03901 }
03902 
03903 static void dundi_precache_full(void)
03904 {
03905    struct dundi_mapping *cur;
03906    struct ast_context *con;
03907    struct ast_exten *e;
03908 
03909    AST_LIST_TRAVERSE(&mappings, cur, list) {
03910       ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
03911       ast_rdlock_contexts();
03912       con = NULL;
03913       while ((con = ast_walk_contexts(con))) {
03914          if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
03915             continue;
03916          /* Found the match, now queue them all up */
03917          ast_rdlock_context(con);
03918          e = NULL;
03919          while ((e = ast_walk_context_extensions(con, e)))
03920             reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
03921          ast_unlock_context(con);
03922       }
03923       ast_unlock_contexts();
03924    }
03925 }
03926 
03927 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
03928 {
03929    struct dundi_request dr;
03930    struct dundi_hint_metadata hmd;
03931    struct dundi_result dr2[MAX_RESULTS];
03932    struct timeval start;
03933    struct dundi_mapping *maps = NULL, *cur;
03934    int nummaps = 0;
03935    int foundanswers;
03936    int foundcache, skipped, ttlms, ms;
03937    if (!context)
03938       context = "e164";
03939    ast_debug(1, "Precache internal (%s@%s)!\n", number, context);
03940 
03941    AST_LIST_LOCK(&peers);
03942    AST_LIST_TRAVERSE(&mappings, cur, list) {
03943       if (!strcasecmp(cur->dcontext, context))
03944          nummaps++;
03945    }
03946    if (nummaps) {
03947       maps = ast_alloca(nummaps * sizeof(*maps));
03948       nummaps = 0;
03949       AST_LIST_TRAVERSE(&mappings, cur, list) {
03950          if (!strcasecmp(cur->dcontext, context))
03951             maps[nummaps++] = *cur;
03952       }
03953    }
03954    AST_LIST_UNLOCK(&peers);
03955    if (!nummaps) {
03956       return -1;
03957    }
03958    ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03959    memset(&dr2, 0, sizeof(dr2));
03960    memset(&dr, 0, sizeof(dr));
03961    memset(&hmd, 0, sizeof(hmd));
03962    dr.dr = dr2;
03963    ast_copy_string(dr.number, number, sizeof(dr.number));
03964    ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
03965    dr.maxcount = MAX_RESULTS;
03966    dr.expiration = dundi_cache_time;
03967    dr.hmd = &hmd;
03968    dr.pfds[0] = dr.pfds[1] = -1;
03969    if (pipe(dr.pfds) < 0) {
03970       ast_log(LOG_WARNING, "pipe() failed: %s\n", strerror(errno));
03971       return -1;
03972    }
03973    build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
03974    optimize_transactions(&dr, 0);
03975    foundanswers = 0;
03976    precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
03977    if (foundanswers) {
03978       if (dr.expiration > 0)
03979          reschedule_precache(dr.number, dr.dcontext, dr.expiration);
03980       else
03981          ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
03982    }
03983    start = ast_tvnow();
03984    while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
03985       if (dr.pfds[0] > -1) {
03986          ms = 100;
03987          ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03988       } else
03989          usleep(1);
03990    }
03991    cancel_request(&dr);
03992    if (dr.pfds[0] > -1) {
03993       close(dr.pfds[0]);
03994       close(dr.pfds[1]);
03995    }
03996    return 0;
03997 }
03998 
03999 int dundi_precache(const char *context, const char *number)
04000 {
04001    dundi_eid *avoid[1] = { NULL, };
04002    return dundi_precache_internal(context, number, dundi_ttl, avoid);
04003 }
04004 
04005 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[])
04006 {
04007    int res;
04008    struct dundi_request dr;
04009    dundi_eid *rooteid=NULL;
04010    int x;
04011    int ttlms;
04012    int skipped=0;
04013    int foundcache=0;
04014    struct timeval start;
04015 
04016    ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
04017 
04018    for (x=0;avoid[x];x++)
04019       rooteid = avoid[x];
04020    /* Now perform real check */
04021    memset(&dr, 0, sizeof(dr));
04022    dr.hmd = hmd;
04023    dr.dei = dei;
04024    dr.pfds[0] = dr.pfds[1] = -1;
04025    ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
04026    memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
04027    if (rooteid)
04028       dr.root_eid = *rooteid;
04029    /* Create transactions */
04030    build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
04031 
04032    /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
04033       do this earlier because we didn't know if we were going to have transactions
04034       or not. */
04035    if (!ttl) {
04036       ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
04037       return 0;
04038    }
04039 
04040    /* Optimize transactions */
04041    optimize_transactions(&dr, 9999);
04042    /* Actually perform transactions */
04043    query_transactions(&dr);
04044    /* Wait for transaction to come back */
04045    start = ast_tvnow();
04046    while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
04047       usleep(1);
04048    res = dr.respcount;
04049    return res;
04050 }
04051 
04052 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
04053 {
04054    dundi_eid *avoid[1] = { NULL, };
04055    struct dundi_hint_metadata hmd;
04056    memset(&hmd, 0, sizeof(hmd));
04057    return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
04058 }
04059 
04060 enum {
04061    OPT_BYPASS_CACHE = (1 << 0),
04062 };
04063 
04064 AST_APP_OPTIONS(dundi_query_opts, BEGIN_OPTIONS
04065    AST_APP_OPTION('b', OPT_BYPASS_CACHE),
04066 END_OPTIONS );
04067 
04068 static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
04069 {
04070    int results;
04071    int x;
04072    struct ast_module_user *u;
04073    struct dundi_result dr[MAX_RESULTS];
04074    AST_DECLARE_APP_ARGS(args,
04075       AST_APP_ARG(number);
04076       AST_APP_ARG(context);
04077       AST_APP_ARG(options);
04078    );
04079    char *parse;
04080    struct ast_flags opts = { 0, };
04081 
04082    buf[0] = '\0';
04083 
04084    if (ast_strlen_zero(num)) {
04085       ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
04086       return -1;
04087    }
04088 
04089    u = ast_module_user_add(chan);
04090 
04091    parse = ast_strdupa(num);
04092 
04093    AST_STANDARD_APP_ARGS(args, parse);
04094 
04095    if (!ast_strlen_zero(args.options)) {
04096       ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
04097    }
04098    if (ast_strlen_zero(args.context)) {
04099       args.context = "e164";
04100    }
04101 
04102    results = dundi_lookup(dr, MAX_RESULTS, NULL, args.context, args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
04103    if (results > 0) {
04104       sort_results(dr, results);
04105       for (x = 0; x < results; x++) {
04106          if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
04107             snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
04108             break;
04109          }
04110       }
04111    }
04112 
04113    ast_module_user_remove(u);
04114 
04115    return 0;
04116 }
04117 
04118 /*! DUNDILOOKUP
04119  * \ingroup functions
04120 */
04121 
04122 static struct ast_custom_function dundi_function = {
04123    .name = "DUNDILOOKUP",
04124    .read = dundifunc_read,
04125 };
04126 
04127 static unsigned int dundi_result_id;
04128 
04129 struct dundi_result_datastore {
04130    struct dundi_result results[MAX_RESULTS];
04131    unsigned int num_results;
04132    unsigned int id;
04133 };
04134 
04135 static void drds_destroy(struct dundi_result_datastore *drds)
04136 {
04137    ast_free(drds);
04138 }
04139 
04140 static void drds_destroy_cb(void *data)
04141 {
04142    struct dundi_result_datastore *drds = data;
04143    drds_destroy(drds);
04144 }
04145 
04146 static const struct ast_datastore_info dundi_result_datastore_info = {
04147    .type = "DUNDIQUERY",
04148    .destroy = drds_destroy_cb,
04149 };
04150 
04151 static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
04152 {
04153    struct ast_module_user *u;
04154    AST_DECLARE_APP_ARGS(args,
04155       AST_APP_ARG(number);
04156       AST_APP_ARG(context);
04157       AST_APP_ARG(options);
04158    );
04159    struct ast_flags opts = { 0, };
04160    char *parse;
04161    struct dundi_result_datastore *drds;
04162    struct ast_datastore *datastore;
04163 
04164    u = ast_module_user_add(chan);
04165 
04166    if (ast_strlen_zero(data)) {
04167       ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
04168       ast_module_user_remove(u);
04169       return -1;
04170    }
04171 
04172    if (!chan) {
04173       ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
04174       ast_module_user_remove(u);
04175       return -1;
04176    }
04177 
04178    parse = ast_strdupa(data);
04179 
04180    AST_STANDARD_APP_ARGS(args, parse);
04181 
04182    if (!ast_strlen_zero(args.options))
04183       ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
04184 
04185    if (ast_strlen_zero(args.context))
04186       args.context = "e164";
04187 
04188    if (!(drds = ast_calloc(1, sizeof(*drds)))) {
04189       ast_module_user_remove(u);
04190       return -1;
04191    }
04192 
04193    drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1);
04194    snprintf(buf, len, "%u", drds->id);
04195 
04196    if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
04197       drds_destroy(drds);
04198       ast_module_user_remove(u);
04199       return -1;
04200    }
04201 
04202    datastore->data = drds;
04203 
04204    drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
04205       args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
04206 
04207    if (drds->num_results > 0)
04208       sort_results(drds->results, drds->num_results);
04209 
04210    ast_channel_lock(chan);
04211    ast_channel_datastore_add(chan, datastore);
04212    ast_channel_unlock(chan);
04213 
04214    ast_module_user_remove(u);
04215 
04216    return 0;
04217 }
04218 
04219 static struct ast_custom_function dundi_query_function = {
04220    .name = "DUNDIQUERY",
04221    .read = dundi_query_read,
04222 };
04223 
04224 static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
04225 {
04226    struct ast_module_user *u;
04227    AST_DECLARE_APP_ARGS(args,
04228       AST_APP_ARG(id);
04229       AST_APP_ARG(resultnum);
04230    );
04231    char *parse;
04232    unsigned int num;
04233    struct dundi_result_datastore *drds;
04234    struct ast_datastore *datastore;
04235    int res = -1;
04236 
04237    u = ast_module_user_add(chan);
04238 
04239    if (ast_strlen_zero(data)) {
04240       ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
04241       goto finish;
04242    }
04243 
04244    if (!chan) {
04245       ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n");
04246       goto finish;
04247    }
04248 
04249    parse = ast_strdupa(data);
04250 
04251    AST_STANDARD_APP_ARGS(args, parse);
04252 
04253    if (ast_strlen_zero(args.id)) {
04254       ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n");
04255       goto finish;
04256    }
04257 
04258    if (ast_strlen_zero(args.resultnum)) {
04259       ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n");
04260       goto finish;
04261    }
04262 
04263    ast_channel_lock(chan);
04264    datastore = ast_channel_datastore_find(chan, &dundi_result_datastore_info, args.id);
04265    ast_channel_unlock(chan);
04266 
04267    if (!datastore) {
04268       ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id);
04269       goto finish;
04270    }
04271 
04272    drds = datastore->data;
04273 
04274    if (!strcasecmp(args.resultnum, "getnum")) {
04275       snprintf(buf, len, "%u", drds->num_results);
04276       res = 0;
04277       goto finish;
04278    }
04279 
04280    if (sscanf(args.resultnum, "%30u", &num) != 1) {
04281       ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n",
04282          args.resultnum);
04283       goto finish;
04284    }
04285 
04286    if (num && num <= drds->num_results) {
04287       snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
04288       res = 0;
04289    } else
04290       ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
04291 
04292 finish:
04293    ast_module_user_remove(u);
04294 
04295    return res;
04296 }
04297 
04298 static struct ast_custom_function dundi_result_function = {
04299    .name = "DUNDIRESULT",
04300    .read = dundi_result_read,
04301 };
04302 
04303 static void mark_peers(void)
04304 {
04305    struct dundi_peer *peer;
04306    AST_LIST_LOCK(&peers);
04307    AST_LIST_TRAVERSE(&peers, peer, list) {
04308       peer->dead = 1;
04309    }
04310    AST_LIST_UNLOCK(&peers);
04311 }
04312 
04313 static void mark_mappings(void)
04314 {
04315    struct dundi_mapping *map;
04316 
04317    AST_LIST_LOCK(&peers);
04318    AST_LIST_TRAVERSE(&mappings, map, list) {
04319       map->dead = 1;
04320    }
04321    AST_LIST_UNLOCK(&peers);
04322 }
04323 
04324 static void destroy_permissions(struct permissionlist *permlist)
04325 {
04326    struct permission *perm;
04327 
04328    while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
04329       ast_free(perm);
04330 }
04331 
04332 static void destroy_peer(struct dundi_peer *peer)
04333 {
04334    AST_SCHED_DEL(sched, peer->registerid);
04335    if (peer->regtrans)
04336       destroy_trans(peer->regtrans, 0);
04337    AST_SCHED_DEL(sched, peer->qualifyid);
04338    destroy_permissions(&peer->permit);
04339    destroy_permissions(&peer->include);
04340    ast_free(peer);
04341 }
04342 
04343 static void destroy_map(struct dundi_mapping *map)
04344 {
04345    if (map->weightstr)
04346       ast_free(map->weightstr);
04347    ast_free(map);
04348 }
04349 
04350 static void prune_peers(void)
04351 {
04352    struct dundi_peer *peer;
04353 
04354    AST_LIST_LOCK(&peers);
04355    AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) {
04356       if (peer->dead) {
04357          AST_LIST_REMOVE_CURRENT(list);
04358          destroy_peer(peer);
04359       }
04360    }
04361    AST_LIST_TRAVERSE_SAFE_END;
04362    AST_LIST_UNLOCK(&peers);
04363 }
04364 
04365 static void prune_mappings(void)
04366 {
04367    struct dundi_mapping *map;
04368 
04369    AST_LIST_LOCK(&peers);
04370    AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) {
04371       if (map->dead) {
04372          AST_LIST_REMOVE_CURRENT(list);
04373          destroy_map(map);
04374       }
04375    }
04376    AST_LIST_TRAVERSE_SAFE_END;
04377    AST_LIST_UNLOCK(&peers);
04378 }
04379 
04380 static void append_permission(struct permissionlist *permlist, const char *s, int allow)
04381 {
04382    struct permission *perm;
04383 
04384    if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
04385       return;
04386 
04387    strcpy(perm->name, s);
04388    perm->allow = allow;
04389 
04390    AST_LIST_INSERT_TAIL(permlist, perm, list);
04391 }
04392 
04393 #define MAX_OPTS 128
04394 
04395 static void build_mapping(const char *name, const char *value)
04396 {
04397    char *t, *fields[MAX_OPTS];
04398    struct dundi_mapping *map;
04399    int x;
04400    int y;
04401 
04402    t = ast_strdupa(value);
04403 
04404    AST_LIST_TRAVERSE(&mappings, map, list) {
04405       /* Find a double match */
04406       if (!strcasecmp(map->dcontext, name) &&
04407          (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
04408            (!value[strlen(map->lcontext)] ||
04409             (value[strlen(map->lcontext)] == ','))))
04410          break;
04411    }
04412    if (!map) {
04413       if (!(map = ast_calloc(1, sizeof(*map))))
04414          return;
04415       AST_LIST_INSERT_HEAD(&mappings, map, list);
04416       map->dead = 1;
04417    }
04418    map->options = 0;
04419    memset(fields, 0, sizeof(fields));
04420    x = 0;
04421    while (t && x < MAX_OPTS) {
04422       fields[x++] = t;
04423       t = strchr(t, ',');
04424       if (t) {
04425          *t = '\0';
04426          t++;
04427       }
04428    } /* Russell was here, arrrr! */
04429    if ((x == 1) && ast_strlen_zero(fields[0])) {
04430       /* Placeholder mapping */
04431       ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
04432       map->dead = 0;
04433    } else if (x >= 4) {
04434       ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
04435       ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
04436       if ((sscanf(fields[1], "%30d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
04437          ast_copy_string(map->dest, fields[3], sizeof(map->dest));
04438          if ((map->tech = str2tech(fields[2])))
04439             map->dead = 0;
04440       } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') {
04441          map->weightstr = ast_strdup(fields[1]);
04442          ast_copy_string(map->dest, fields[3], sizeof(map->dest));
04443          if ((map->tech = str2tech(fields[2])))
04444             map->dead = 0;
04445       } else {
04446          ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
04447       }
04448       for (y = 4;y < x; y++) {
04449          if (!strcasecmp(fields[y], "nounsolicited"))
04450             map->options |= DUNDI_FLAG_NOUNSOLICITED;
04451          else if (!strcasecmp(fields[y], "nocomunsolicit"))
04452             map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
04453          else if (!strcasecmp(fields[y], "residential"))
04454             map->options |= DUNDI_FLAG_RESIDENTIAL;
04455          else if (!strcasecmp(fields[y], "commercial"))
04456             map->options |= DUNDI_FLAG_COMMERCIAL;
04457          else if (!strcasecmp(fields[y], "mobile"))
04458             map->options |= DUNDI_FLAG_MOBILE;
04459          else if (!strcasecmp(fields[y], "nopartial"))
04460             map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
04461          else
04462             ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
04463       }
04464    } else
04465       ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
04466 }
04467 
04468 /* \note Called with the peers list already locked */
04469 static int do_register(const void *data)
04470 {
04471    struct dundi_ie_data ied;
04472    struct dundi_peer *peer = (struct dundi_peer *)data;
04473    char eid_str[20];
04474    char eid_str2[20];
04475    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));
04476    peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
04477    /* Destroy old transaction if there is one */
04478    if (peer->regtrans)
04479       destroy_trans(peer->regtrans, 0);
04480    peer->regtrans = create_transaction(peer);
04481    if (peer->regtrans) {
04482       ast_set_flag(peer->regtrans, FLAG_ISREG);
04483       memset(&ied, 0, sizeof(ied));
04484       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
04485       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
04486       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
04487       dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
04488 
04489    } else
04490       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));
04491 
04492    return 0;
04493 }
04494 
04495 static int do_qualify(const void *data)
04496 {
04497    struct dundi_peer *peer = (struct dundi_peer *)data;
04498    peer->qualifyid = -1;
04499    qualify_peer(peer, 0);
04500    return 0;
04501 }
04502 
04503 static void qualify_peer(struct dundi_peer *peer, int schedonly)
04504 {
04505    int when;
04506    AST_SCHED_DEL(sched, peer->qualifyid);
04507    if (peer->qualtrans)
04508       destroy_trans(peer->qualtrans, 0);
04509    peer->qualtrans = NULL;
04510    if (peer->maxms > 0) {
04511       when = 60000;
04512       if (peer->lastms < 0)
04513          when = 10000;
04514       if (schedonly)
04515          when = 5000;
04516       peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
04517       if (!schedonly)
04518          peer->qualtrans = create_transaction(peer);
04519       if (peer->qualtrans) {
04520          peer->qualtx = ast_tvnow();
04521          ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
04522          dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
04523       }
04524    }
04525 }
04526 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
04527 {
04528    char data[256];
04529    char *c;
04530    int port, expire;
04531    char eid_str[20];
04532    ast_eid_to_str(eid_str, sizeof(eid_str), eid);
04533    if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
04534       c = strchr(data, ':');
04535       if (c) {
04536          *c = '\0';
04537          c++;
04538          if (sscanf(c, "%5d:%30d", &port, &expire) == 2) {
04539             /* Got it! */
04540             inet_aton(data, &peer->addr.sin_addr);
04541             peer->addr.sin_family = AF_INET;
04542             peer->addr.sin_port = htons(port);
04543             peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
04544          }
04545       }
04546    }
04547 }
04548 
04549 
04550 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
04551 {
04552    struct dundi_peer *peer;
04553    struct ast_hostent he;
04554    struct hostent *hp;
04555    dundi_eid testeid;
04556    int needregister=0;
04557    char eid_str[20];
04558 
04559    AST_LIST_LOCK(&peers);
04560    AST_LIST_TRAVERSE(&peers, peer, list) {
04561       if (!ast_eid_cmp(&peer->eid, eid)) {
04562          break;
04563       }
04564    }
04565    if (!peer) {
04566       /* Add us into the list */
04567       if (!(peer = ast_calloc(1, sizeof(*peer)))) {
04568          AST_LIST_UNLOCK(&peers);
04569          return;
04570       }
04571       peer->registerid = -1;
04572       peer->registerexpire = -1;
04573       peer->qualifyid = -1;
04574       peer->addr.sin_family = AF_INET;
04575       peer->addr.sin_port = htons(DUNDI_PORT);
04576       populate_addr(peer, eid);
04577       AST_LIST_INSERT_HEAD(&peers, peer, list);
04578    }
04579    peer->dead = 0;
04580    peer->eid = *eid;
04581    peer->us_eid = global_eid;
04582    destroy_permissions(&peer->permit);
04583    destroy_permissions(&peer->include);
04584    AST_SCHED_DEL(sched, peer->registerid);
04585    for (; v; v = v->next) {
04586       if (!strcasecmp(v->name, "inkey")) {
04587          ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
04588       } else if (!strcasecmp(v->name, "outkey")) {
04589          ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
04590       } else if (!strcasecmp(v->name, "port")) {
04591          peer->addr.sin_port = htons(atoi(v->value));
04592       } else if (!strcasecmp(v->name, "host")) {
04593          if (!strcasecmp(v->value, "dynamic")) {
04594             peer->dynamic = 1;
04595          } else {
04596             hp = ast_gethostbyname(v->value, &he);
04597             if (hp) {
04598                memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
04599                peer->dynamic = 0;
04600             } else {
04601                ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
04602                peer->dead = 1;
04603             }
04604          }
04605       } else if (!strcasecmp(v->name, "ustothem")) {
04606          if (!ast_str_to_eid(&testeid, v->value))
04607             peer->us_eid = testeid;
04608          else
04609             ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
04610       } else if (!strcasecmp(v->name, "include")) {
04611          append_permission(&peer->include, v->value, 1);
04612       } else if (!strcasecmp(v->name, "permit")) {
04613          append_permission(&peer->permit, v->value, 1);
04614       } else if (!strcasecmp(v->name, "noinclude")) {
04615          append_permission(&peer->include, v->value, 0);
04616       } else if (!strcasecmp(v->name, "deny")) {
04617          append_permission(&peer->permit, v->value, 0);
04618       } else if (!strcasecmp(v->name, "register")) {
04619          needregister = ast_true(v->value);
04620       } else if (!strcasecmp(v->name, "order")) {
04621          if (!strcasecmp(v->value, "primary"))
04622             peer->order = 0;
04623          else if (!strcasecmp(v->value, "secondary"))
04624             peer->order = 1;
04625          else if (!strcasecmp(v->value, "tertiary"))
04626             peer->order = 2;
04627          else if (!strcasecmp(v->value, "quartiary"))
04628             peer->order = 3;
04629          else {
04630             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);
04631          }
04632       } else if (!strcasecmp(v->name, "qualify")) {
04633          if (!strcasecmp(v->value, "no")) {
04634             peer->maxms = 0;
04635          } else if (!strcasecmp(v->value, "yes")) {
04636             peer->maxms = DEFAULT_MAXMS;
04637          } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
04638             ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
04639                ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
04640             peer->maxms = 0;
04641          }
04642       } else if (!strcasecmp(v->name, "model")) {
04643          if (!strcasecmp(v->value, "inbound"))
04644             peer->model = DUNDI_MODEL_INBOUND;
04645          else if (!strcasecmp(v->value, "outbound"))
04646             peer->model = DUNDI_MODEL_OUTBOUND;
04647          else if (!strcasecmp(v->value, "symmetric"))
04648             peer->model = DUNDI_MODEL_SYMMETRIC;
04649          else if (!strcasecmp(v->value, "none"))
04650             peer->model = 0;
04651          else {
04652             ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04653                v->value, v->lineno);
04654          }
04655       } else if (!strcasecmp(v->name, "precache")) {
04656          if (!strcasecmp(v->value, "inbound"))
04657             peer->pcmodel = DUNDI_MODEL_INBOUND;
04658          else if (!strcasecmp(v->value, "outbound"))
04659             peer->pcmodel = DUNDI_MODEL_OUTBOUND;
04660          else if (!strcasecmp(v->value, "symmetric"))
04661             peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
04662          else if (!strcasecmp(v->value, "none"))
04663             peer->pcmodel = 0;
04664          else {
04665             ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04666                v->value, v->lineno);
04667          }
04668       }
04669    }
04670    (*globalpcmode) |= peer->pcmodel;
04671    if (!peer->model && !peer->pcmodel) {
04672       ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
04673          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04674       peer->dead = 1;
04675    } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04676       ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
04677          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04678       peer->dead = 1;
04679    } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04680       ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
04681          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04682       peer->dead = 1;
04683    } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04684       ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
04685          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04686    } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04687       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",
04688          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04689    } else {
04690       if (needregister) {
04691          peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
04692       }
04693       if (ast_eid_cmp(&peer->eid, &empty_eid)) {
04694          qualify_peer(peer, 1);
04695       }
04696    }
04697    AST_LIST_UNLOCK(&peers);
04698 }
04699 
04700 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
04701 {
04702    struct dundi_result results[MAX_RESULTS];
04703    int res;
04704    int x;
04705    int found = 0;
04706    if (!strncasecmp(context, "macro-", 6)) {
04707       if (!chan) {
04708          ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04709          return -1;
04710       }
04711       /* If done as a macro, use macro extension */
04712       if (!strcasecmp(exten, "s")) {
04713          exten = pbx_builtin_getvar_helper(chan, "ARG1");
04714          if (ast_strlen_zero(exten))
04715             exten = ast_channel_macroexten(chan);
04716          if (ast_strlen_zero(exten))
04717             exten = ast_channel_exten(chan);
04718          if (ast_strlen_zero(exten)) {
04719             ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04720             return -1;
04721          }
04722       }
04723       if (ast_strlen_zero(data))
04724          data = "e164";
04725    } else {
04726       if (ast_strlen_zero(data))
04727          data = context;
04728    }
04729    res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04730    for (x=0;x<res;x++) {
04731       if (ast_test_flag(results + x, flag))
04732          found++;
04733    }
04734    if (found >= priority)
04735       return 1;
04736    return 0;
04737 }
04738 
04739 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04740 {
04741    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
04742 }
04743 
04744 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04745 {
04746    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
04747 }
04748 
04749 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04750 {
04751    struct dundi_result results[MAX_RESULTS];
04752    int res;
04753    int x=0;
04754    char req[1024];
04755    const char *dundiargs;
04756    struct ast_app *dial;
04757 
04758    if (!strncasecmp(context, "macro-", 6)) {
04759       if (!chan) {
04760          ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04761          return -1;
04762       }
04763       /* If done as a macro, use macro extension */
04764       if (!strcasecmp(exten, "s")) {
04765          exten = pbx_builtin_getvar_helper(chan, "ARG1");
04766          if (ast_strlen_zero(exten))
04767             exten = ast_channel_macroexten(chan);
04768          if (ast_strlen_zero(exten))
04769             exten = ast_channel_exten(chan);
04770          if (ast_strlen_zero(exten)) {
04771             ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04772             return -1;
04773          }
04774       }
04775       if (ast_strlen_zero(data))
04776          data = "e164";
04777    } else {
04778       if (ast_strlen_zero(data))
04779          data = context;
04780    }
04781    res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04782    if (res > 0) {
04783       sort_results(results, res);
04784       for (x=0;x<res;x++) {
04785          if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
04786             if (!--priority)
04787                break;
04788          }
04789       }
04790    }
04791    if (x < res) {
04792       /* Got a hit! */
04793       dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
04794       snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest,
04795          S_OR(dundiargs, ""));
04796       dial = pbx_findapp("Dial");
04797       if (dial)
04798          res = pbx_exec(chan, dial, req);
04799    } else
04800       res = -1;
04801    return res;
04802 }
04803 
04804 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04805 {
04806    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
04807 }
04808 
04809 static struct ast_switch dundi_switch = {
04810    .name        = "DUNDi",
04811    .description = "DUNDi Discovered Dialplan Switch",
04812    .exists      = dundi_exists,
04813    .canmatch    = dundi_canmatch,
04814    .exec        = dundi_exec,
04815    .matchmore   = dundi_matchmore,
04816 };
04817 
04818 static int set_config(char *config_file, struct sockaddr_in* sin, int reload)
04819 {
04820    struct ast_config *cfg;
04821    struct ast_variable *v;
04822    char *cat;
04823    int x;
04824    struct ast_flags config_flags = { 0 };
04825    char hn[MAXHOSTNAMELEN] = "";
04826    struct ast_hostent he;
04827    struct hostent *hp;
04828    struct sockaddr_in sin2;
04829    static int last_port = 0;
04830    int globalpcmodel = 0;
04831    dundi_eid testeid;
04832 
04833    if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
04834       ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
04835       return -1;
04836    }
04837 
04838    dundi_ttl = DUNDI_DEFAULT_TTL;
04839    dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
04840    any_peer = NULL;
04841 
04842    ipaddr[0] = '\0';
04843    if (!gethostname(hn, sizeof(hn)-1)) {
04844       hp = ast_gethostbyname(hn, &he);
04845       if (hp) {
04846          memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
04847          ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr));
04848       } else
04849          ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
04850    } else
04851       ast_log(LOG_WARNING, "Unable to get host name!\n");
04852    AST_LIST_LOCK(&peers);
04853 
04854    memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
04855 
04856    global_storehistory = 0;
04857    ast_copy_string(secretpath, "dundi", sizeof(secretpath));
04858    v = ast_variable_browse(cfg, "general");
04859    while(v) {
04860       if (!strcasecmp(v->name, "port")){
04861          sin->sin_port = htons(atoi(v->value));
04862          if(last_port==0){
04863             last_port=sin->sin_port;
04864          } else if(sin->sin_port != last_port)
04865             ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
04866       } else if (!strcasecmp(v->name, "bindaddr")) {
04867          struct hostent *hep;
04868          struct ast_hostent hent;
04869          hep = ast_gethostbyname(v->value, &hent);
04870          if (hep) {
04871             memcpy(&sin->sin_addr, hep->h_addr, sizeof(sin->sin_addr));
04872          } else
04873             ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
04874       } else if (!strcasecmp(v->name, "authdebug")) {
04875          authdebug = ast_true(v->value);
04876       } else if (!strcasecmp(v->name, "ttl")) {
04877          if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
04878             dundi_ttl = x;
04879          } else {
04880             ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
04881                v->value, v->lineno, DUNDI_DEFAULT_TTL);
04882          }
04883       } else if (!strcasecmp(v->name, "autokill")) {
04884          if (sscanf(v->value, "%30d", &x) == 1) {
04885             if (x >= 0)
04886                global_autokilltimeout = x;
04887             else
04888                ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
04889          } else if (ast_true(v->value)) {
04890             global_autokilltimeout = DEFAULT_MAXMS;
04891          } else {
04892             global_autokilltimeout = 0;
04893          }
04894       } else if (!strcasecmp(v->name, "entityid")) {
04895          if (!ast_str_to_eid(&testeid, v->value))
04896             global_eid = testeid;
04897          else
04898             ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
04899       } else if (!strcasecmp(v->name, "tos")) {
04900          if (ast_str2tos(v->value, &tos))
04901             ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
04902       } else if (!strcasecmp(v->name, "department")) {
04903          ast_copy_string(dept, v->value, sizeof(dept));
04904       } else if (!strcasecmp(v->name, "organization")) {
04905          ast_copy_string(org, v->value, sizeof(org));
04906       } else if (!strcasecmp(v->name, "locality")) {
04907          ast_copy_string(locality, v->value, sizeof(locality));
04908       } else if (!strcasecmp(v->name, "stateprov")) {
04909          ast_copy_string(stateprov, v->value, sizeof(stateprov));
04910       } else if (!strcasecmp(v->name, "country")) {
04911          ast_copy_string(country, v->value, sizeof(country));
04912       } else if (!strcasecmp(v->name, "email")) {
04913          ast_copy_string(email, v->value, sizeof(email));
04914       } else if (!strcasecmp(v->name, "phone")) {
04915          ast_copy_string(phone, v->value, sizeof(phone));
04916       } else if (!strcasecmp(v->name, "storehistory")) {
04917          global_storehistory = ast_true(v->value);
04918       } else if (!strcasecmp(v->name, "cachetime")) {
04919          if ((sscanf(v->value, "%30d", &x) == 1)) {
04920             dundi_cache_time = x;
04921          } else {
04922             ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
04923                v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
04924          }
04925       }
04926       v = v->next;
04927    }
04928    AST_LIST_UNLOCK(&peers);
04929    mark_mappings();
04930    v = ast_variable_browse(cfg, "mappings");
04931    while(v) {
04932       build_mapping(v->name, v->value);
04933       v = v->next;
04934    }
04935    prune_mappings();
04936    mark_peers();
04937    cat = ast_category_browse(cfg, NULL);
04938    while(cat) {
04939       if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
04940          /* Entries */
04941          if (!ast_str_to_eid(&testeid, cat))
04942             build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
04943          else if (!strcasecmp(cat, "*")) {
04944             build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
04945             any_peer = find_peer(NULL);
04946          } else
04947             ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
04948       }
04949       cat = ast_category_browse(cfg, cat);
04950    }
04951    prune_peers();
04952    ast_config_destroy(cfg);
04953    load_password();
04954    if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
04955       dundi_precache_full();
04956    return 0;
04957 }
04958 
04959 static int unload_module(void)
04960 {
04961    pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid, previous_clearcachethreadid = clearcachethreadid;
04962    ast_module_user_hangup_all();
04963 
04964    /* Stop all currently running threads */
04965    dundi_shutdown = 1;
04966    if (previous_netthreadid != AST_PTHREADT_NULL) {
04967       pthread_kill(previous_netthreadid, SIGURG);
04968       pthread_join(previous_netthreadid, NULL);
04969    }
04970    if (previous_precachethreadid != AST_PTHREADT_NULL) {
04971       pthread_kill(previous_precachethreadid, SIGURG);
04972       pthread_join(previous_precachethreadid, NULL);
04973    }
04974    if (previous_clearcachethreadid != AST_PTHREADT_NULL) {
04975       pthread_cancel(previous_clearcachethreadid);
04976       pthread_join(previous_clearcachethreadid, NULL);
04977    }
04978 
04979    ast_cli_unregister_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
04980    ast_unregister_switch(&dundi_switch);
04981    ast_custom_function_unregister(&dundi_function);
04982    ast_custom_function_unregister(&dundi_query_function);
04983    ast_custom_function_unregister(&dundi_result_function);
04984    close(netsocket);
04985    io_context_destroy(io);
04986    ast_sched_context_destroy(sched);
04987 
04988    mark_mappings();
04989    prune_mappings();
04990    mark_peers();
04991    prune_peers();
04992 
04993    return 0;
04994 }
04995 
04996 static int reload(void)
04997 {
04998    struct sockaddr_in sin;
04999 
05000    if (set_config("dundi.conf", &sin, 1))
05001       return AST_MODULE_LOAD_FAILURE;
05002 
05003    return AST_MODULE_LOAD_SUCCESS;
05004 }
05005 
05006 static int load_module(void)
05007 {
05008    struct sockaddr_in sin;
05009 
05010    dundi_set_output(dundi_debug_output);
05011    dundi_set_error(dundi_error_output);
05012 
05013    sin.sin_family = AF_INET;
05014    sin.sin_port = htons(DUNDI_PORT);
05015    sin.sin_addr.s_addr = INADDR_ANY;
05016 
05017    /* Make a UDP socket */
05018    io = io_context_create();
05019    sched = ast_sched_context_create();
05020 
05021    if (!io || !sched)
05022       return AST_MODULE_LOAD_DECLINE;
05023 
05024    if (set_config("dundi.conf", &sin, 0))
05025       return AST_MODULE_LOAD_DECLINE;
05026 
05027    netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
05028 
05029    if (netsocket < 0) {
05030       ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
05031       return AST_MODULE_LOAD_DECLINE;
05032    }
05033    if (bind(netsocket, (struct sockaddr *) &sin, sizeof(sin))) {
05034       ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n",
05035          ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
05036       return AST_MODULE_LOAD_DECLINE;
05037    }
05038 
05039    ast_set_qos(netsocket, tos, 0, "DUNDi");
05040 
05041    if (start_network_thread()) {
05042       ast_log(LOG_ERROR, "Unable to start network thread\n");
05043       close(netsocket);
05044       return AST_MODULE_LOAD_DECLINE;
05045    }
05046 
05047    ast_cli_register_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
05048    if (ast_register_switch(&dundi_switch))
05049       ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
05050    ast_custom_function_register(&dundi_function);
05051    ast_custom_function_register(&dundi_query_function);
05052    ast_custom_function_register(&dundi_result_function);
05053 
05054    ast_verb(2, "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
05055 
05056    return AST_MODULE_LOAD_SUCCESS;
05057 }
05058 
05059 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
05060       .load = load_module,
05061       .unload = unload_module,
05062       .reload = reload,
05063       .nonoptreq = "res_crypto",
05064           );
05065 

Generated on Thu Oct 11 06:33:51 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6