Sat Apr 26 2014 22:01:39

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