Wed Apr 6 11:29:46 2011

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

Generated on Wed Apr 6 11:29:46 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7