Thu Oct 11 06:47:19 2012

Asterisk developer's documentation


pbx_dundi.c

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

Generated on Thu Oct 11 06:47:19 2012 for Asterisk - the Open Source PBX by  doxygen 1.5.6