Sat Mar 10 01:54:21 2012

Asterisk developer's documentation


pbx_dundi.c

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

Generated on Sat Mar 10 01:54:22 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7