Wed Jan 27 20:02:13 2016

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

Generated on 27 Jan 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1