Tue Aug 20 16:34:36 2013

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

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1