Mon Sep 20 2010 00:23:23

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: 255326 $")
00032 
00033 #include "asterisk/network.h"
00034 #include <sys/ioctl.h>
00035 #include <zlib.h>
00036 #include <sys/signal.h>
00037 #include <pthread.h>
00038 #include <net/if.h>
00039 
00040 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
00041 #include <net/if_dl.h>
00042 #include <ifaddrs.h>
00043 #include <signal.h>
00044 #endif
00045 
00046 #include "asterisk/file.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/config.h"
00050 #include "asterisk/pbx.h"
00051 #include "asterisk/module.h"
00052 #include "asterisk/frame.h"
00053 #include "asterisk/cli.h"
00054 #include "asterisk/lock.h"
00055 #include "asterisk/md5.h"
00056 #include "asterisk/dundi.h"
00057 #include "asterisk/sched.h"
00058 #include "asterisk/io.h"
00059 #include "asterisk/utils.h"
00060 #include "asterisk/netsock.h"
00061 #include "asterisk/crypto.h"
00062 #include "asterisk/astdb.h"
00063 #include "asterisk/acl.h"
00064 #include "asterisk/aes.h"
00065 #include "asterisk/app.h"
00066 
00067 #include "dundi-parser.h"
00068 
00069 /*** DOCUMENTATION
00070    <function name="DUNDILOOKUP" language="en_US">
00071       <synopsis>
00072          Do a DUNDi lookup of a phone number.
00073       </synopsis>
00074       <syntax>
00075          <parameter name="number" required="true"/>
00076          <parameter name="context">
00077             <para>If not specified the default will be <literal>e164</literal>.</para>
00078          </parameter>
00079          <parameter name="options">
00080             <optionlist>
00081                <option name="b">
00082                   <para>Bypass the internal DUNDi cache</para>
00083                </option>
00084             </optionlist>
00085          </parameter>
00086       </syntax>
00087       <description>
00088          <para>This will do a DUNDi lookup of the given phone number.</para>
00089          <para>This function will return the Technology/Resource found in the first result
00090          in the DUNDi lookup. If no results were found, the result will be blank.</para>
00091       </description>
00092    </function>
00093          
00094       
00095    <function name="DUNDIQUERY" language="en_US">
00096       <synopsis>
00097          Initiate a DUNDi query.
00098       </synopsis>
00099       <syntax>
00100          <parameter name="number" required="true"/>
00101          <parameter name="context">
00102             <para>If not specified the default will be <literal>e164</literal>.</para>
00103          </parameter>
00104          <parameter name="options">
00105             <optionlist>
00106                <option name="b">
00107                   <para>Bypass the internal DUNDi cache</para>
00108                </option>
00109             </optionlist>
00110          </parameter>
00111       </syntax>
00112       <description>
00113          <para>This will do a DUNDi lookup of the given phone number.</para>
00114          <para>The result of this function will be a numeric ID that can be used to retrieve
00115          the results with the <literal>DUNDIRESULT</literal> function.</para>
00116       </description>
00117    </function>
00118 
00119    <function name="DUNDIRESULT" language="en_US">
00120       <synopsis>
00121          Retrieve results from a DUNDIQUERY.
00122       </synopsis>
00123       <syntax>
00124          <parameter name="id" required="true">
00125             <para>The identifier returned by the <literal>DUNDIQUERY</literal> function.</para>
00126          </parameter>
00127          <parameter name="resultnum">
00128             <optionlist>
00129                <option name="number">
00130                   <para>The number of the result that you want to retrieve, this starts at <literal>1</literal></para>
00131                </option>
00132                <option name="getnum">
00133                   <para>The total number of results that are available.</para>
00134                </option>
00135             </optionlist>
00136          </parameter>
00137       </syntax>
00138       <description>
00139          <para>This function will retrieve results from a previous use\n"
00140          of the <literal>DUNDIQUERY</literal> function.</para>
00141       </description>
00142    </function>
00143  ***/
00144 
00145 #define MAX_RESULTS  64
00146 
00147 #define MAX_PACKET_SIZE 8192
00148 
00149 #define MAX_WEIGHT 59999
00150 
00151 #define DUNDI_MODEL_INBOUND      (1 << 0)
00152 #define DUNDI_MODEL_OUTBOUND  (1 << 1)
00153 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
00154 
00155 /*! Keep times of last 10 lookups */
00156 #define DUNDI_TIMING_HISTORY  10
00157 
00158 enum {
00159    FLAG_ISREG =       (1 << 0),  /*!< Transaction is register request */
00160    FLAG_DEAD =        (1 << 1),  /*!< Transaction is dead */
00161    FLAG_FINAL =       (1 << 2),  /*!< Transaction has final message sent */
00162    FLAG_ISQUAL =      (1 << 3),  /*!< Transaction is a qualification */
00163    FLAG_ENCRYPT =     (1 << 4),  /*!< Transaction is encrypted wiht ECX/DCX */
00164    FLAG_SENDFULLKEY = (1 << 5),  /*!< Send full key on transaction */
00165    FLAG_STOREHIST =   (1 << 6),  /*!< Record historic performance */
00166 };
00167 
00168 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
00169 
00170 #if 0
00171 #define DUNDI_SECRET_TIME 15  /* Testing only */
00172 #else
00173 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
00174 #endif
00175 
00176 static struct io_context *io;
00177 static struct sched_context *sched;
00178 static int netsocket = -1;
00179 static pthread_t netthreadid = AST_PTHREADT_NULL;
00180 static pthread_t precachethreadid = AST_PTHREADT_NULL;
00181 static pthread_t clearcachethreadid = AST_PTHREADT_NULL;
00182 static unsigned int tos = 0;
00183 static int dundidebug = 0;
00184 static int authdebug = 0;
00185 static int dundi_ttl = DUNDI_DEFAULT_TTL;
00186 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
00187 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
00188 static int global_autokilltimeout = 0;
00189 static dundi_eid global_eid;
00190 static int default_expiration = 60;
00191 static int global_storehistory = 0;
00192 static char dept[80];
00193 static char org[80];
00194 static char locality[80];
00195 static char stateprov[80];
00196 static char country[80];
00197 static char email[80];
00198 static char phone[80];
00199 static char secretpath[80];
00200 static char cursecret[80];
00201 static char ipaddr[80];
00202 static time_t rotatetime;
00203 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
00204 static int dundi_shutdown = 0;
00205 
00206 struct permission {
00207    AST_LIST_ENTRY(permission) list;
00208    int allow;
00209    char name[0];
00210 };
00211 
00212 struct dundi_packet {
00213    AST_LIST_ENTRY(dundi_packet) list;
00214    struct dundi_hdr *h;
00215    int datalen;
00216    struct dundi_transaction *parent;
00217    int retransid;
00218    int retrans;
00219    unsigned char data[0];
00220 };
00221 
00222 struct dundi_hint_metadata {
00223    unsigned short flags;
00224    char exten[AST_MAX_EXTENSION];
00225 };
00226 
00227 struct dundi_precache_queue {
00228    AST_LIST_ENTRY(dundi_precache_queue) list;
00229    char *context;
00230    time_t expiration;
00231    char number[0];
00232 };
00233 
00234 struct dundi_request;
00235 
00236 struct dundi_transaction {
00237    struct sockaddr_in addr;                       /*!< Other end of transaction */
00238    struct timeval start;                          /*!< When this transaction was created */
00239    dundi_eid eids[DUNDI_MAX_STACK + 1];
00240    int eidcount;                                  /*!< Number of eids in eids */
00241    dundi_eid us_eid;                              /*!< Our EID, to them */
00242    dundi_eid them_eid;                            /*!< Their EID, to us */
00243    ast_aes_encrypt_key ecx;                       /*!< AES 128 Encryption context */
00244    ast_aes_decrypt_key dcx;                       /*!< AES 128 Decryption context */
00245    unsigned int flags;                            /*!< Has final packet been sent */
00246    int ttl;                                       /*!< Remaining TTL for queries on this one */
00247    int thread;                                    /*!< We have a calling thread */
00248    int retranstimer;                              /*!< How long to wait before retransmissions */
00249    int autokillid;                                /*!< ID to kill connection if answer doesn't come back fast enough */
00250    int autokilltimeout;                           /*!< Recommended timeout for autokill */
00251    unsigned short strans;                         /*!< Our transaction identifier */
00252    unsigned short dtrans;                         /*!< Their transaction identifer */
00253    unsigned char iseqno;                          /*!< Next expected received seqno */
00254    unsigned char oiseqno;                         /*!< Last received incoming seqno */
00255    unsigned char oseqno;                          /*!< Next transmitted seqno */
00256    unsigned char aseqno;                          /*!< Last acknowledge seqno */
00257    AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets;  /*!< Packets to be retransmitted */
00258    struct packetlist lasttrans;                   /*!< Last transmitted / ACK'd packet */
00259    struct dundi_request *parent;                  /*!< Parent request (if there is one) */
00260    AST_LIST_ENTRY(dundi_transaction) parentlist;  /*!< Next with respect to the parent */
00261    AST_LIST_ENTRY(dundi_transaction) all;         /*!< Next with respect to all DUNDi transactions */
00262 };
00263 
00264 struct dundi_request {
00265    char dcontext[AST_MAX_EXTENSION];
00266    char number[AST_MAX_EXTENSION];
00267    dundi_eid query_eid;
00268    dundi_eid root_eid;
00269    struct dundi_result *dr;
00270    struct dundi_entity_info *dei;
00271    struct dundi_hint_metadata *hmd;
00272    int maxcount;
00273    int respcount;
00274    int expiration;
00275    int cbypass;
00276    int pfds[2];
00277    uint32_t crc32;                              /*!< CRC-32 of all but root EID's in avoid list */
00278    AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans;  /*!< Transactions */
00279    AST_LIST_ENTRY(dundi_request) list;
00280 };
00281 
00282 struct dundi_mapping {
00283    char dcontext[AST_MAX_EXTENSION];
00284    char lcontext[AST_MAX_EXTENSION];
00285    int _weight;
00286    char *weightstr;
00287    int options;
00288    int tech;
00289    int dead;
00290    char dest[AST_MAX_EXTENSION];
00291    AST_LIST_ENTRY(dundi_mapping) list;
00292 };
00293 
00294 struct dundi_peer {
00295    dundi_eid eid;
00296    struct sockaddr_in addr;               /*!< Address of DUNDi peer */
00297    AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
00298    struct permissionlist include;
00299    dundi_eid us_eid;
00300    char inkey[80];
00301    char outkey[80];
00302    int dead;
00303    int registerid;
00304    int qualifyid;
00305    int sentfullkey;
00306    int order;
00307    unsigned char txenckey[256];           /*!< Transmitted encrypted key + sig */
00308    unsigned char rxenckey[256];           /*!< Cache received encrypted key + sig */
00309    uint32_t us_keycrc32;                  /*!< CRC-32 of our key */
00310    ast_aes_encrypt_key us_ecx;            /*!< Cached AES 128 Encryption context */
00311    ast_aes_decrypt_key us_dcx;            /*!< Cached AES 128 Decryption context */
00312    uint32_t them_keycrc32;                /*!< CRC-32 of our key */
00313    ast_aes_encrypt_key them_ecx;          /*!< Cached AES 128 Encryption context */
00314    ast_aes_decrypt_key them_dcx;          /*!< Cached AES 128 Decryption context */
00315    time_t keyexpire;                      /*!< When to expire/recreate key */
00316    int registerexpire;
00317    int lookuptimes[DUNDI_TIMING_HISTORY];
00318    char *lookups[DUNDI_TIMING_HISTORY];
00319    int avgms;
00320    struct dundi_transaction *regtrans;    /*!< Registration transaction */
00321    struct dundi_transaction *qualtrans;   /*!< Qualify transaction */
00322    int model;                             /*!< Pull model */
00323    int pcmodel;                           /*!< Push/precache model */
00324    /*! Dynamic peers register with us */
00325    unsigned int dynamic:1;
00326    int lastms;                            /*!< Last measured latency */
00327    int maxms;                             /*!< Max permissible latency */
00328    struct timeval qualtx;                 /*!< Time of transmit */
00329    AST_LIST_ENTRY(dundi_peer) list;
00330 };
00331 
00332 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
00333 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
00334 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
00335 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
00336 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
00337 
00338 /*!
00339  * \brief Wildcard peer
00340  *
00341  * This peer is created if the [*] entry is specified in dundi.conf
00342  */
00343 static struct dundi_peer *any_peer;
00344 
00345 static int dundi_xmit(struct dundi_packet *pack);
00346 
00347 static void dundi_debug_output(const char *data)
00348 {
00349    if (dundidebug)
00350       ast_verbose("%s", data);
00351 }
00352 
00353 static void dundi_error_output(const char *data)
00354 {
00355    ast_log(LOG_WARNING, "%s", data);
00356 }
00357 
00358 static int has_permission(struct permissionlist *permlist, char *cont)
00359 {
00360    struct permission *perm;
00361    int res = 0;
00362 
00363    AST_LIST_TRAVERSE(permlist, perm, list) {
00364       if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
00365          res = perm->allow;
00366    }
00367 
00368    return res;
00369 }
00370 
00371 static char *tech2str(int tech)
00372 {
00373    switch(tech) {
00374    case DUNDI_PROTO_NONE:
00375       return "None";
00376    case DUNDI_PROTO_IAX:
00377       return "IAX2";
00378    case DUNDI_PROTO_SIP:
00379       return "SIP";
00380    case DUNDI_PROTO_H323:
00381       return "H323";
00382    default:
00383       return "Unknown";
00384    }
00385 }
00386 
00387 static int str2tech(char *str)
00388 {
00389    if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
00390       return DUNDI_PROTO_IAX;
00391    else if (!strcasecmp(str, "SIP"))
00392       return DUNDI_PROTO_SIP;
00393    else if (!strcasecmp(str, "H323"))
00394       return DUNDI_PROTO_H323;
00395    else
00396       return -1;
00397 }
00398 
00399 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
00400 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
00401 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
00402 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
00403 {
00404    struct dundi_transaction *trans;
00405 
00406    /* Look for an exact match first */
00407    AST_LIST_TRAVERSE(&alltrans, trans, all) {
00408       if (!inaddrcmp(&trans->addr, sin) &&
00409            ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
00410            ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
00411            if (hdr->strans)
00412               trans->dtrans = ntohs(hdr->strans) & 32767;
00413            return trans;
00414       }
00415    }
00416 
00417    switch(hdr->cmdresp & 0x7f) {
00418    case DUNDI_COMMAND_DPDISCOVER:
00419    case DUNDI_COMMAND_EIDQUERY:
00420    case DUNDI_COMMAND_PRECACHERQ:
00421    case DUNDI_COMMAND_REGREQ:
00422    case DUNDI_COMMAND_NULL:
00423    case DUNDI_COMMAND_ENCRYPT:
00424       if (!hdr->strans)
00425          break;
00426       /* Create new transaction */
00427       if (!(trans = create_transaction(NULL)))
00428          break;
00429       memcpy(&trans->addr, sin, sizeof(trans->addr));
00430       trans->dtrans = ntohs(hdr->strans) & 32767;
00431    default:
00432       break;
00433    }
00434 
00435    return trans;
00436 }
00437 
00438 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
00439 
00440 static int dundi_ack(struct dundi_transaction *trans, int final)
00441 {
00442    return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
00443 }
00444 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
00445 {
00446    struct {
00447       struct dundi_packet pack;
00448       struct dundi_hdr hdr;
00449    } tmp;
00450    struct dundi_transaction trans;
00451    /* Never respond to an INVALID with another INVALID */
00452    if (h->cmdresp == DUNDI_COMMAND_INVALID)
00453       return;
00454    memset(&tmp, 0, sizeof(tmp));
00455    memset(&trans, 0, sizeof(trans));
00456    memcpy(&trans.addr, sin, sizeof(trans.addr));
00457    tmp.hdr.strans = h->dtrans;
00458    tmp.hdr.dtrans = h->strans;
00459    tmp.hdr.iseqno = h->oseqno;
00460    tmp.hdr.oseqno = h->iseqno;
00461    tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
00462    tmp.hdr.cmdflags = 0;
00463    tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
00464    tmp.pack.datalen = sizeof(struct dundi_hdr);
00465    tmp.pack.parent = &trans;
00466    dundi_xmit(&tmp.pack);
00467 }
00468 
00469 static int get_trans_id(void)
00470 {
00471    struct dundi_transaction *t;
00472    int stid = (ast_random() % 32766) + 1;
00473    int tid = stid;
00474 
00475    do {
00476       AST_LIST_TRAVERSE(&alltrans, t, all) {
00477          if (t->strans == tid)
00478             break;
00479       }
00480       if (!t)
00481          return tid;
00482       tid = (tid % 32766) + 1;
00483    } while (tid != stid);
00484 
00485    return 0;
00486 }
00487 
00488 static int reset_transaction(struct dundi_transaction *trans)
00489 {
00490    int tid;
00491    tid = get_trans_id();
00492    if (tid < 1)
00493       return -1;
00494    trans->strans = tid;
00495    trans->dtrans = 0;
00496    trans->iseqno = 0;
00497    trans->oiseqno = 0;
00498    trans->oseqno = 0;
00499    trans->aseqno = 0;
00500    ast_clear_flag(trans, FLAG_FINAL);
00501    return 0;
00502 }
00503 
00504 static struct dundi_peer *find_peer(dundi_eid *eid)
00505 {
00506    struct dundi_peer *cur = NULL;
00507 
00508    if (!eid)
00509       eid = &empty_eid;
00510 
00511    AST_LIST_TRAVERSE(&peers, cur, list) {
00512       if (!ast_eid_cmp(&cur->eid,eid))
00513          break;
00514    }
00515 
00516    if (!cur && any_peer)
00517       cur = any_peer;
00518 
00519    return cur;
00520 }
00521 
00522 static void build_iv(unsigned char *iv)
00523 {
00524    /* XXX Would be nice to be more random XXX */
00525    unsigned int *fluffy;
00526    int x;
00527    fluffy = (unsigned int *)(iv);
00528    for (x=0;x<4;x++)
00529       fluffy[x] = ast_random();
00530 }
00531 
00532 struct dundi_query_state {
00533    dundi_eid *eids[DUNDI_MAX_STACK + 1];
00534    int directs[DUNDI_MAX_STACK + 1];
00535    dundi_eid reqeid;
00536    char called_context[AST_MAX_EXTENSION];
00537    char called_number[AST_MAX_EXTENSION];
00538    struct dundi_mapping *maps;
00539    int nummaps;
00540    int nocache;
00541    struct dundi_transaction *trans;
00542    void *chal;
00543    int challen;
00544    int ttl;
00545    char fluffy[0];
00546 };
00547 
00548 static int get_mapping_weight(struct dundi_mapping *map)
00549 {
00550    char buf[32];
00551 
00552    buf[0] = 0;
00553    if (map->weightstr) {
00554       pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
00555       if (sscanf(buf, "%30d", &map->_weight) != 1)
00556          map->_weight = MAX_WEIGHT;
00557    }
00558 
00559    return map->_weight;
00560 }
00561 
00562 static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd)
00563 {
00564    struct ast_flags flags = {0};
00565    int x;
00566    if (!ast_strlen_zero(map->lcontext)) {
00567       if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
00568          ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
00569       if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
00570          ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
00571       if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
00572          ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
00573       if (ast_ignore_pattern(map->lcontext, called_number))
00574          ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
00575 
00576       /* Clearly we can't say 'don't ask' anymore if we found anything... */
00577       if (ast_test_flag(&flags, AST_FLAGS_ALL))
00578          ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
00579 
00580       if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
00581          /* Skip partial answers */
00582          ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
00583       }
00584       if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
00585          struct varshead headp;
00586          struct ast_var_t *newvariable;
00587          ast_set_flag(&flags, map->options & 0xffff);
00588          ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
00589          dr[anscnt].techint = map->tech;
00590          dr[anscnt].weight = get_mapping_weight(map);
00591          dr[anscnt].expiration = dundi_cache_time;
00592          ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
00593          dr[anscnt].eid = *us_eid;
00594          ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
00595          if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
00596             AST_LIST_HEAD_INIT_NOLOCK(&headp);
00597             newvariable = ast_var_assign("NUMBER", called_number);
00598             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00599             newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
00600             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00601             newvariable = ast_var_assign("SECRET", cursecret);
00602             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00603             newvariable = ast_var_assign("IPADDR", ipaddr);
00604             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00605             pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
00606             while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
00607                ast_var_delete(newvariable);
00608          } else
00609             dr[anscnt].dest[0] = '\0';
00610          anscnt++;
00611       } else {
00612          /* No answers...  Find the fewest number of digits from the
00613             number for which we have no answer. */
00614          char tmp[AST_MAX_EXTENSION + 1] = "";
00615          for (x = 0; x < (sizeof(tmp) - 1); x++) {
00616             tmp[x] = called_number[x];
00617             if (!tmp[x])
00618                break;
00619             if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
00620                /* Oops found something we can't match.  If this is longer
00621                   than the running hint, we have to consider it */
00622                if (strlen(tmp) > strlen(hmd->exten)) {
00623                   ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
00624                }
00625                break;
00626             }
00627          }
00628       }
00629    }
00630    return anscnt;
00631 }
00632 
00633 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
00634 
00635 static void *dundi_lookup_thread(void *data)
00636 {
00637    struct dundi_query_state *st = data;
00638    struct dundi_result dr[MAX_RESULTS];
00639    struct dundi_ie_data ied;
00640    struct dundi_hint_metadata hmd;
00641    char eid_str[20];
00642    int res, x;
00643    int ouranswers=0;
00644    int max = 999999;
00645    int expiration = dundi_cache_time;
00646 
00647    ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
00648          st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00649    memset(&ied, 0, sizeof(ied));
00650    memset(&dr, 0, sizeof(dr));
00651    memset(&hmd, 0, sizeof(hmd));
00652    /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
00653    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00654    for (x=0;x<st->nummaps;x++)
00655       ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
00656    if (ouranswers < 0)
00657       ouranswers = 0;
00658    for (x=0;x<ouranswers;x++) {
00659       if (dr[x].weight < max)
00660          max = dr[x].weight;
00661    }
00662 
00663    if (max) {
00664       /* If we do not have a canonical result, keep looking */
00665       res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs);
00666       if (res > 0) {
00667          /* Append answer in result */
00668          ouranswers += res;
00669       } else {
00670          if ((res < -1) && (!ouranswers))
00671             dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
00672       }
00673    }
00674    AST_LIST_LOCK(&peers);
00675    /* Truncate if "don't ask" isn't present */
00676    if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00677       hmd.exten[0] = '\0';
00678    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00679       ast_debug(1, "Our transaction went away!\n");
00680       st->trans->thread = 0;
00681       destroy_trans(st->trans, 0);
00682    } else {
00683       for (x=0;x<ouranswers;x++) {
00684          /* Add answers */
00685          if (dr[x].expiration && (expiration > dr[x].expiration))
00686             expiration = dr[x].expiration;
00687          dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
00688       }
00689       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00690       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
00691       dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
00692       st->trans->thread = 0;
00693    }
00694    AST_LIST_UNLOCK(&peers);
00695    ast_free(st);
00696    return NULL;
00697 }
00698 
00699 static void *dundi_precache_thread(void *data)
00700 {
00701    struct dundi_query_state *st = data;
00702    struct dundi_ie_data ied;
00703    struct dundi_hint_metadata hmd;
00704    char eid_str[20];
00705 
00706    ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
00707       st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00708    memset(&ied, 0, sizeof(ied));
00709 
00710    /* Now produce precache */
00711    dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
00712 
00713    AST_LIST_LOCK(&peers);
00714    /* Truncate if "don't ask" isn't present */
00715    if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00716       hmd.exten[0] = '\0';
00717    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00718       ast_debug(1, "Our transaction went away!\n");
00719       st->trans->thread = 0;
00720       destroy_trans(st->trans, 0);
00721    } else {
00722       dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
00723       st->trans->thread = 0;
00724    }
00725    AST_LIST_UNLOCK(&peers);
00726    ast_free(st);
00727    return NULL;
00728 }
00729 
00730 static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[]);
00731 
00732 static void *dundi_query_thread(void *data)
00733 {
00734    struct dundi_query_state *st = data;
00735    struct dundi_entity_info dei;
00736    struct dundi_ie_data ied;
00737    struct dundi_hint_metadata hmd;
00738    char eid_str[20];
00739    int res;
00740 
00741    ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
00742       st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00743    memset(&ied, 0, sizeof(ied));
00744    memset(&dei, 0, sizeof(dei));
00745    memset(&hmd, 0, sizeof(hmd));
00746    if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
00747       /* Ooh, it's us! */
00748       ast_debug(1, "Neat, someone look for us!\n");
00749       ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
00750       ast_copy_string(dei.org, org, sizeof(dei.org));
00751       ast_copy_string(dei.locality, locality, sizeof(dei.locality));
00752       ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
00753       ast_copy_string(dei.country, country, sizeof(dei.country));
00754       ast_copy_string(dei.email, email, sizeof(dei.email));
00755       ast_copy_string(dei.phone, phone, sizeof(dei.phone));
00756       res = 1;
00757    } else {
00758       /* If we do not have a canonical result, keep looking */
00759       res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
00760    }
00761    AST_LIST_LOCK(&peers);
00762    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00763       ast_debug(1, "Our transaction went away!\n");
00764       st->trans->thread = 0;
00765       destroy_trans(st->trans, 0);
00766    } else {
00767       if (res) {
00768          dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
00769          dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
00770          dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
00771          dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
00772          dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
00773          dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
00774          dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
00775          if (!ast_strlen_zero(dei.ipaddr))
00776             dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
00777       }
00778       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00779       dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00780       st->trans->thread = 0;
00781    }
00782    AST_LIST_UNLOCK(&peers);
00783    ast_free(st);
00784    return NULL;
00785 }
00786 
00787 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00788 {
00789    struct dundi_query_state *st;
00790    int totallen;
00791    int x;
00792    int skipfirst=0;
00793    char eid_str[20];
00794    char *s;
00795    pthread_t lookupthread;
00796 
00797    if (ies->eidcount > 1) {
00798       /* Since it is a requirement that the first EID is the authenticating host
00799          and the last EID is the root, it is permissible that the first and last EID
00800          could be the same.  In that case, we should go ahead copy only the "root" section
00801          since we will not need it for authentication. */
00802       if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00803          skipfirst = 1;
00804    }
00805    totallen = sizeof(struct dundi_query_state);
00806    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00807    st = ast_calloc(1, totallen);
00808    if (st) {
00809       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00810       memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
00811       st->trans = trans;
00812       st->ttl = ies->ttl - 1;
00813       if (st->ttl < 0)
00814          st->ttl = 0;
00815       s = st->fluffy;
00816       for (x=skipfirst;ies->eids[x];x++) {
00817          st->eids[x-skipfirst] = (dundi_eid *)s;
00818          *st->eids[x-skipfirst] = *ies->eids[x];
00819          s += sizeof(dundi_eid);
00820       }
00821       ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
00822 
00823       trans->thread = 1;
00824       if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
00825          struct dundi_ie_data ied = { 0, };
00826          trans->thread = 0;
00827          ast_log(LOG_WARNING, "Unable to create thread!\n");
00828          ast_free(st);
00829          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
00830          dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00831          return -1;
00832       }
00833    } else {
00834       struct dundi_ie_data ied = { 0, };
00835       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
00836       dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00837       return -1;
00838    }
00839    return 0;
00840 }
00841 
00842 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
00843 {
00844    int unaffected;
00845    char key1[256];
00846    char key2[256];
00847    char eidpeer_str[20];
00848    char eidroot_str[20];
00849    char data[80];
00850    time_t timeout;
00851 
00852    if (expiration < 0)
00853       expiration = dundi_cache_time;
00854 
00855    /* Only cache hint if "don't ask" is there... */
00856    if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
00857       return 0;
00858 
00859    unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
00860 
00861    dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00862    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00863    snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08x", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
00864    snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
00865 
00866    time(&timeout);
00867    timeout += expiration;
00868    snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00869 
00870    ast_db_put("dundi/cache", key1, data);
00871    ast_debug(1, "Caching hint at '%s'\n", key1);
00872    ast_db_put("dundi/cache", key2, data);
00873    ast_debug(1, "Caching hint at '%s'\n", key2);
00874    return 0;
00875 }
00876 
00877 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
00878 {
00879    int x;
00880    char key1[256];
00881    char key2[256];
00882    char data[1024];
00883    char eidpeer_str[20];
00884    char eidroot_str[20];
00885    time_t timeout;
00886 
00887    if (expiration < 1)
00888       expiration = dundi_cache_time;
00889 
00890    /* Keep pushes a little longer, cut pulls a little short */
00891    if (push)
00892       expiration += 10;
00893    else
00894       expiration -= 10;
00895    if (expiration < 1)
00896       expiration = 1;
00897    dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00898    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00899    snprintf(key1, sizeof(key1), "%s/%s/%s/e%08x", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
00900    snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
00901    /* Build request string */
00902    time(&timeout);
00903    timeout += expiration;
00904    snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00905    for (x=start;x<req->respcount;x++) {
00906       /* Skip anything with an illegal pipe in it */
00907       if (strchr(req->dr[x].dest, '|'))
00908          continue;
00909       snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
00910          req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
00911          dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
00912    }
00913    ast_db_put("dundi/cache", key1, data);
00914    ast_db_put("dundi/cache", key2, data);
00915    return 0;
00916 }
00917 
00918 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00919 {
00920    struct dundi_query_state *st;
00921    int totallen;
00922    int x,z;
00923    struct dundi_ie_data ied;
00924    char *s;
00925    struct dundi_result dr2[MAX_RESULTS];
00926    struct dundi_request dr;
00927    struct dundi_hint_metadata hmd;
00928 
00929    struct dundi_mapping *cur;
00930    int mapcount;
00931    int skipfirst = 0;
00932 
00933    pthread_t lookupthread;
00934 
00935    memset(&dr2, 0, sizeof(dr2));
00936    memset(&dr, 0, sizeof(dr));
00937    memset(&hmd, 0, sizeof(hmd));
00938 
00939    /* Forge request structure to hold answers for cache */
00940    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00941    dr.dr = dr2;
00942    dr.maxcount = MAX_RESULTS;
00943    dr.expiration = dundi_cache_time;
00944    dr.hmd = &hmd;
00945    dr.pfds[0] = dr.pfds[1] = -1;
00946    trans->parent = &dr;
00947    ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
00948    ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
00949 
00950    for (x=0;x<ies->anscount;x++) {
00951       if (trans->parent->respcount < trans->parent->maxcount) {
00952          /* Make sure it's not already there */
00953          for (z=0;z<trans->parent->respcount;z++) {
00954             if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
00955                 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
00956                   break;
00957          }
00958          if (z == trans->parent->respcount) {
00959             /* Copy into parent responses */
00960             trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
00961             trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
00962             trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
00963             trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
00964             if (ies->expiration > 0)
00965                trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
00966             else
00967                trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
00968             ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
00969                sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
00970                &ies->answers[x]->eid);
00971             ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
00972                sizeof(trans->parent->dr[trans->parent->respcount].dest));
00973                ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
00974                sizeof(trans->parent->dr[trans->parent->respcount].tech));
00975             trans->parent->respcount++;
00976             ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
00977          } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
00978             /* Update weight if appropriate */
00979             trans->parent->dr[z].weight = ies->answers[x]->weight;
00980          }
00981       } else
00982          ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
00983             trans->parent->number, trans->parent->dcontext);
00984 
00985    }
00986    /* Save all the results (if any) we had.  Even if no results, still cache lookup. */
00987    cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
00988    if (ies->hint)
00989       cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
00990 
00991    totallen = sizeof(struct dundi_query_state);
00992    /* Count matching map entries */
00993    mapcount = 0;
00994    AST_LIST_TRAVERSE(&mappings, cur, list) {
00995       if (!strcasecmp(cur->dcontext, ccontext))
00996          mapcount++;
00997    }
00998 
00999    /* If no maps, return -1 immediately */
01000    if (!mapcount)
01001       return -1;
01002 
01003    if (ies->eidcount > 1) {
01004       /* Since it is a requirement that the first EID is the authenticating host
01005          and the last EID is the root, it is permissible that the first and last EID
01006          could be the same.  In that case, we should go ahead copy only the "root" section
01007          since we will not need it for authentication. */
01008       if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
01009          skipfirst = 1;
01010    }
01011 
01012    /* Prepare to run a query and then propagate that as necessary */
01013    totallen += mapcount * sizeof(struct dundi_mapping);
01014    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
01015    st = ast_calloc(1, totallen);
01016    if (st) {
01017       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
01018       ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
01019       st->trans = trans;
01020       st->ttl = ies->ttl - 1;
01021       st->nocache = ies->cbypass;
01022       if (st->ttl < 0)
01023          st->ttl = 0;
01024       s = st->fluffy;
01025       for (x=skipfirst;ies->eids[x];x++) {
01026          st->eids[x-skipfirst] = (dundi_eid *)s;
01027          *st->eids[x-skipfirst] = *ies->eids[x];
01028          st->directs[x-skipfirst] = ies->eid_direct[x];
01029          s += sizeof(dundi_eid);
01030       }
01031       /* Append mappings */
01032       x = 0;
01033       st->maps = (struct dundi_mapping *)s;
01034       AST_LIST_TRAVERSE(&mappings, cur, list) {
01035          if (!strcasecmp(cur->dcontext, ccontext)) {
01036             if (x < mapcount) {
01037                st->maps[x] = *cur;
01038                st->maps[x].list.next = NULL;
01039                x++;
01040             }
01041          }
01042       }
01043       st->nummaps = mapcount;
01044       ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
01045       trans->thread = 1;
01046       if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
01047          trans->thread = 0;
01048          ast_log(LOG_WARNING, "Unable to create thread!\n");
01049          ast_free(st);
01050          memset(&ied, 0, sizeof(ied));
01051          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01052          dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01053          return -1;
01054       }
01055    } else {
01056       ast_log(LOG_WARNING, "Out of memory!\n");
01057       memset(&ied, 0, sizeof(ied));
01058       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01059       dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01060       return -1;
01061    }
01062    return 0;
01063 }
01064 
01065 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
01066 {
01067    struct dundi_query_state *st;
01068    int totallen;
01069    int x;
01070    struct dundi_ie_data ied;
01071    char *s;
01072    struct dundi_mapping *cur;
01073    int mapcount = 0;
01074    int skipfirst = 0;
01075 
01076    pthread_t lookupthread;
01077    totallen = sizeof(struct dundi_query_state);
01078    /* Count matching map entries */
01079    AST_LIST_TRAVERSE(&mappings, cur, list) {
01080       if (!strcasecmp(cur->dcontext, ccontext))
01081          mapcount++;
01082    }
01083    /* If no maps, return -1 immediately */
01084    if (!mapcount)
01085       return -1;
01086 
01087    if (ies->eidcount > 1) {
01088       /* Since it is a requirement that the first EID is the authenticating host
01089          and the last EID is the root, it is permissible that the first and last EID
01090          could be the same.  In that case, we should go ahead copy only the "root" section
01091          since we will not need it for authentication. */
01092       if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
01093          skipfirst = 1;
01094    }
01095 
01096    totallen += mapcount * sizeof(struct dundi_mapping);
01097    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
01098    st = ast_calloc(1, totallen);
01099    if (st) {
01100       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
01101       ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
01102       st->trans = trans;
01103       st->ttl = ies->ttl - 1;
01104       st->nocache = ies->cbypass;
01105       if (st->ttl < 0)
01106          st->ttl = 0;
01107       s = st->fluffy;
01108       for (x=skipfirst;ies->eids[x];x++) {
01109          st->eids[x-skipfirst] = (dundi_eid *)s;
01110          *st->eids[x-skipfirst] = *ies->eids[x];
01111          st->directs[x-skipfirst] = ies->eid_direct[x];
01112          s += sizeof(dundi_eid);
01113       }
01114       /* Append mappings */
01115       x = 0;
01116       st->maps = (struct dundi_mapping *)s;
01117       AST_LIST_TRAVERSE(&mappings, cur, list) {
01118          if (!strcasecmp(cur->dcontext, ccontext)) {
01119             if (x < mapcount) {
01120                st->maps[x] = *cur;
01121                st->maps[x].list.next = NULL;
01122                x++;
01123             }
01124          }
01125       }
01126       st->nummaps = mapcount;
01127       ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
01128       trans->thread = 1;
01129       if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
01130          trans->thread = 0;
01131          ast_log(LOG_WARNING, "Unable to create thread!\n");
01132          ast_free(st);
01133          memset(&ied, 0, sizeof(ied));
01134          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01135          dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01136          return -1;
01137       }
01138    } else {
01139       ast_log(LOG_WARNING, "Out of memory!\n");
01140       memset(&ied, 0, sizeof(ied));
01141       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01142       dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01143       return -1;
01144    }
01145    return 0;
01146 }
01147 
01148 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
01149 {
01150    char data[1024];
01151    char *ptr, *term, *src;
01152    int tech;
01153    struct ast_flags flags;
01154    int weight;
01155    int length;
01156    int z;
01157    char fs[256];
01158 
01159    /* Build request string */
01160    if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
01161       time_t timeout;
01162       ptr = data;
01163       if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
01164          int expiration = timeout - now;
01165          if (expiration > 0) {
01166             ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
01167             ptr += length + 1;
01168             while((sscanf(ptr, "%30d/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
01169                ptr += length;
01170                term = strchr(ptr, '|');
01171                if (term) {
01172                   *term = '\0';
01173                   src = strrchr(ptr, '/');
01174                   if (src) {
01175                      *src = '\0';
01176                      src++;
01177                   } else
01178                      src = "";
01179                   ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
01180                      tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
01181                   /* Make sure it's not already there */
01182                   for (z=0;z<req->respcount;z++) {
01183                      if ((req->dr[z].techint == tech) &&
01184                          !strcmp(req->dr[z].dest, ptr))
01185                            break;
01186                   }
01187                   if (z == req->respcount) {
01188                      /* Copy into parent responses */
01189                      ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
01190                      req->dr[req->respcount].weight = weight;
01191                      req->dr[req->respcount].techint = tech;
01192                      req->dr[req->respcount].expiration = expiration;
01193                      dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
01194                      ast_eid_to_str(req->dr[req->respcount].eid_str,
01195                         sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
01196                      ast_copy_string(req->dr[req->respcount].dest, ptr,
01197                         sizeof(req->dr[req->respcount].dest));
01198                      ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
01199                         sizeof(req->dr[req->respcount].tech));
01200                      req->respcount++;
01201                      ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
01202                   } else if (req->dr[z].weight > weight)
01203                      req->dr[z].weight = weight;
01204                   ptr = term + 1;
01205                }
01206             }
01207             /* We found *something* cached */
01208             if (expiration < *lowexpiration)
01209                *lowexpiration = expiration;
01210             return 1;
01211          } else
01212             ast_db_del("dundi/cache", key);
01213       } else
01214          ast_db_del("dundi/cache", key);
01215    }
01216 
01217    return 0;
01218 }
01219 
01220 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc32, int *lowexpiration)
01221 {
01222    char key[256];
01223    char eid_str[20];
01224    char eidroot_str[20];
01225    time_t now;
01226    int res=0;
01227    int res2=0;
01228    char eid_str_full[20];
01229    char tmp[256]="";
01230    int x;
01231 
01232    time(&now);
01233    dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
01234    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
01235    ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
01236    snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, crc32);
01237    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01238    snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, 0);
01239    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01240    snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
01241    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01242    x = 0;
01243    if (!req->respcount) {
01244       while(!res2) {
01245          /* Look and see if we have a hint that would preclude us from looking at this
01246             peer for this number. */
01247          if (!(tmp[x] = req->number[x]))
01248             break;
01249          x++;
01250          /* Check for hints */
01251          snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, crc32);
01252          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01253          snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, 0);
01254          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01255          snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
01256          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01257          if (res2) {
01258             if (strlen(tmp) > strlen(req->hmd->exten)) {
01259                /* Update meta data if appropriate */
01260                ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
01261             }
01262          }
01263       }
01264       res |= res2;
01265    }
01266 
01267    return res;
01268 }
01269 
01270 static void qualify_peer(struct dundi_peer *peer, int schedonly);
01271 
01272 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
01273 {
01274    if (!trans->addr.sin_addr.s_addr)
01275       memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
01276    trans->us_eid = p->us_eid;
01277    trans->them_eid = p->eid;
01278    /* Enable encryption if appropriate */
01279    if (!ast_strlen_zero(p->inkey))
01280       ast_set_flag(trans, FLAG_ENCRYPT);
01281    if (p->maxms) {
01282       trans->autokilltimeout = p->maxms;
01283       trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01284       if (p->lastms > 1) {
01285          trans->retranstimer = p->lastms * 2;
01286          /* Keep it from being silly */
01287          if (trans->retranstimer < 150)
01288             trans->retranstimer = 150;
01289       }
01290       if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
01291          trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01292    } else
01293       trans->autokilltimeout = global_autokilltimeout;
01294 }
01295 
01296 /*! \note Called with the peers list already locked */
01297 static int do_register_expire(const void *data)
01298 {
01299    struct dundi_peer *peer = (struct dundi_peer *)data;
01300    char eid_str[20];
01301    ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01302    peer->registerexpire = -1;
01303    peer->lastms = 0;
01304    memset(&peer->addr, 0, sizeof(peer->addr));
01305    return 0;
01306 }
01307 
01308 static int update_key(struct dundi_peer *peer)
01309 {
01310    unsigned char key[16];
01311    struct ast_key *ekey, *skey;
01312    char eid_str[20];
01313    int res;
01314    if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
01315       build_iv(key);
01316       ast_aes_encrypt_key(key, &peer->us_ecx);
01317       ast_aes_decrypt_key(key, &peer->us_dcx);
01318       ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01319       if (!ekey) {
01320          ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
01321             peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01322          return -1;
01323       }
01324       skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01325       if (!skey) {
01326          ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
01327             peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01328          return -1;
01329       }
01330       if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
01331          ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
01332          return -1;
01333       }
01334       if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
01335          ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
01336          return -1;
01337       }
01338       peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
01339       peer->sentfullkey = 0;
01340       /* Looks good */
01341       time(&peer->keyexpire);
01342       peer->keyexpire += dundi_key_ttl;
01343    }
01344    return 0;
01345 }
01346 
01347 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
01348 {
01349    unsigned char curblock[16];
01350    int x;
01351    memcpy(curblock, iv, sizeof(curblock));
01352    while(len > 0) {
01353       for (x=0;x<16;x++)
01354          curblock[x] ^= src[x];
01355       ast_aes_encrypt(curblock, dst, ecx);
01356       memcpy(curblock, dst, sizeof(curblock));
01357       dst += 16;
01358       src += 16;
01359       len -= 16;
01360    }
01361    return 0;
01362 }
01363 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
01364 {
01365    unsigned char lastblock[16];
01366    int x;
01367    memcpy(lastblock, iv, sizeof(lastblock));
01368    while(len > 0) {
01369       ast_aes_decrypt(src, dst, dcx);
01370       for (x=0;x<16;x++)
01371          dst[x] ^= lastblock[x];
01372       memcpy(lastblock, src, sizeof(lastblock));
01373       dst += 16;
01374       src += 16;
01375       len -= 16;
01376    }
01377    return 0;
01378 }
01379 
01380 static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
01381 {
01382    int space = *dstlen;
01383    unsigned long bytes;
01384    struct dundi_hdr *h;
01385    unsigned char *decrypt_space;
01386    decrypt_space = alloca(srclen);
01387    if (!decrypt_space)
01388       return NULL;
01389    decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
01390    /* Setup header */
01391    h = (struct dundi_hdr *)dst;
01392    *h = *ohdr;
01393    bytes = space - 6;
01394    if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
01395       ast_debug(1, "Ouch, uncompress failed :(\n");
01396       return NULL;
01397    }
01398    /* Update length */
01399    *dstlen = bytes + 6;
01400    /* Return new header */
01401    return h;
01402 }
01403 
01404 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
01405 {
01406    unsigned char *compress_space;
01407    int len;
01408    int res;
01409    unsigned long bytes;
01410    struct dundi_ie_data ied;
01411    struct dundi_peer *peer;
01412    unsigned char iv[16];
01413    len = pack->datalen + pack->datalen / 100 + 42;
01414    compress_space = alloca(len);
01415    if (compress_space) {
01416       memset(compress_space, 0, len);
01417       /* We care about everthing save the first 6 bytes of header */
01418       bytes = len;
01419       res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
01420       if (res != Z_OK) {
01421          ast_debug(1, "Ouch, compression failed!\n");
01422          return -1;
01423       }
01424       memset(&ied, 0, sizeof(ied));
01425       /* Say who we are */
01426       if (!pack->h->iseqno && !pack->h->oseqno) {
01427          /* Need the key in the first copy */
01428          if (!(peer = find_peer(&trans->them_eid)))
01429             return -1;
01430          if (update_key(peer))
01431             return -1;
01432          if (!peer->sentfullkey)
01433             ast_set_flag(trans, FLAG_SENDFULLKEY);
01434          /* Append key data */
01435          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01436          if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
01437             dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01438             dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01439          } else {
01440             dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
01441          }
01442          /* Setup contexts */
01443          trans->ecx = peer->us_ecx;
01444          trans->dcx = peer->us_dcx;
01445 
01446          /* We've sent the full key */
01447          peer->sentfullkey = 1;
01448       }
01449       /* Build initialization vector */
01450       build_iv(iv);
01451       /* Add the field, rounded up to 16 bytes */
01452       dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
01453       /* Copy the data */
01454       if ((ied.pos + bytes) >= sizeof(ied.buf)) {
01455          ast_log(LOG_NOTICE, "Final packet too large!\n");
01456          return -1;
01457       }
01458       encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
01459       ied.pos += ((bytes + 15) / 16) * 16;
01460       /* Reconstruct header */
01461       pack->datalen = sizeof(struct dundi_hdr);
01462       pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
01463       pack->h->cmdflags = 0;
01464       memcpy(pack->h->ies, ied.buf, ied.pos);
01465       pack->datalen += ied.pos;
01466       return 0;
01467    }
01468    return -1;
01469 }
01470 
01471 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
01472 {
01473    unsigned char dst[128];
01474    int res;
01475    struct ast_key *key, *skey;
01476    char eid_str[20];
01477    ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
01478    if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
01479       /* A match */
01480       return 1;
01481    } else if (!newkey || !newsig)
01482       return 0;
01483    if (!memcmp(peer->rxenckey, newkey, 128) &&
01484        !memcmp(peer->rxenckey + 128, newsig, 128)) {
01485       /* By definition, a match */
01486       return 1;
01487    }
01488    /* Decrypt key */
01489    key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01490    if (!key) {
01491       ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
01492          peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01493       return -1;
01494    }
01495 
01496    skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01497    if (!skey) {
01498       ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
01499          peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01500       return -1;
01501    }
01502 
01503    /* First check signature */
01504    res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
01505    if (res)
01506       return 0;
01507 
01508    res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
01509    if (res != 16) {
01510       if (res >= 0)
01511          ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
01512       return 0;
01513    }
01514    /* Decrypted, passes signature */
01515    ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
01516    memcpy(peer->rxenckey, newkey, 128);
01517    memcpy(peer->rxenckey + 128, newsig, 128);
01518    peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
01519    ast_aes_decrypt_key(dst, &peer->them_dcx);
01520    ast_aes_encrypt_key(dst, &peer->them_ecx);
01521    return 1;
01522 }
01523 
01524 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
01525 {
01526    struct permission *cur, *perm;
01527 
01528    memcpy(peer_dst, peer_src, sizeof(*peer_dst));
01529 
01530    memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
01531    memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
01532 
01533    AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
01534       if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
01535          continue;
01536 
01537       perm->allow = cur->allow;
01538       strcpy(perm->name, cur->name);
01539 
01540       AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
01541    }
01542 
01543    AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
01544       if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
01545          continue;
01546 
01547       perm->allow = cur->allow;
01548       strcpy(perm->name, cur->name);
01549 
01550       AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
01551    }
01552 }
01553 
01554 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
01555 {
01556    /* Handle canonical command / response */
01557    int final = hdr->cmdresp & 0x80;
01558    int cmd = hdr->cmdresp & 0x7f;
01559    int x,y,z;
01560    int resp;
01561    int res;
01562    int authpass=0;
01563    unsigned char *bufcpy;
01564 #ifdef LOW_MEMORY
01565    struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
01566 #else
01567    struct dundi_ie_data _ied = {
01568       .pos = 0,
01569    };
01570    struct dundi_ie_data *ied = &_ied;
01571 #endif
01572    struct dundi_ies ies = {
01573       .eidcount = 0,
01574    };
01575    struct dundi_peer *peer = NULL;
01576    char eid_str[20];
01577    char eid_str2[20];
01578    int retval = -1;
01579 
01580    if (!ied) {
01581       return -1;
01582    }
01583 
01584    if (datalen) {
01585       bufcpy = alloca(datalen);
01586       if (!bufcpy) {
01587          goto return_cleanup;
01588       }
01589       /* Make a copy for parsing */
01590       memcpy(bufcpy, hdr->ies, datalen);
01591       ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
01592       if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
01593          ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
01594          goto return_cleanup;
01595       }
01596    }
01597    switch(cmd) {
01598    case DUNDI_COMMAND_DPDISCOVER:
01599    case DUNDI_COMMAND_EIDQUERY:
01600    case DUNDI_COMMAND_PRECACHERQ:
01601       if (cmd == DUNDI_COMMAND_EIDQUERY)
01602          resp = DUNDI_COMMAND_EIDRESPONSE;
01603       else if (cmd == DUNDI_COMMAND_PRECACHERQ)
01604          resp = DUNDI_COMMAND_PRECACHERP;
01605       else
01606          resp = DUNDI_COMMAND_DPRESPONSE;
01607       /* A dialplan or entity discover -- qualify by highest level entity */
01608       peer = find_peer(ies.eids[0]);
01609       if (!peer) {
01610          dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01611          dundi_send(trans, resp, 0, 1, ied);
01612       } else {
01613          int hasauth = 0;
01614          trans->us_eid = peer->us_eid;
01615          if (strlen(peer->inkey)) {
01616             hasauth = encrypted;
01617          } else
01618             hasauth = 1;
01619          if (hasauth) {
01620             /* Okay we're authentiated and all, now we check if they're authorized */
01621             if (!ies.called_context)
01622                ies.called_context = "e164";
01623             if (cmd == DUNDI_COMMAND_EIDQUERY) {
01624                res = dundi_answer_entity(trans, &ies, ies.called_context);
01625             } else {
01626                if (ast_strlen_zero(ies.called_number)) {
01627                   /* They're not permitted to access that context */
01628                   dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
01629                   dundi_send(trans, resp, 0, 1, ied);
01630                } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
01631                           (peer->model & DUNDI_MODEL_INBOUND) &&
01632                         has_permission(&peer->permit, ies.called_context)) {
01633                   res = dundi_answer_query(trans, &ies, ies.called_context);
01634                   if (res < 0) {
01635                      /* There is no such dundi context */
01636                      dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01637                      dundi_send(trans, resp, 0, 1, ied);
01638                   }
01639                } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
01640                           (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
01641                         has_permission(&peer->include, ies.called_context)) {
01642                   res = dundi_prop_precache(trans, &ies, ies.called_context);
01643                   if (res < 0) {
01644                      /* There is no such dundi context */
01645                      dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01646                      dundi_send(trans, resp, 0, 1, ied);
01647                   }
01648                } else {
01649                   /* They're not permitted to access that context */
01650                   dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
01651                   dundi_send(trans, resp, 0, 1, ied);
01652                }
01653             }
01654          } else {
01655             /* They're not permitted to access that context */
01656             dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
01657             dundi_send(trans, resp, 0, 1, ied);
01658          }
01659       }
01660       break;
01661    case DUNDI_COMMAND_REGREQ:
01662       /* A register request -- should only have one entity */
01663       peer = find_peer(ies.eids[0]);
01664 
01665       /* if the peer is not found and we have a valid 'any_peer' setting */
01666       if (any_peer && peer == any_peer) {
01667          /* copy any_peer into a new peer object */
01668          peer = ast_calloc(1, sizeof(*peer));
01669          if (peer) {
01670             deep_copy_peer(peer, any_peer);
01671 
01672             /* set EID to remote EID */
01673             peer->eid = *ies.eids[0];
01674 
01675             AST_LIST_LOCK(&peers);
01676             AST_LIST_INSERT_HEAD(&peers, peer, list);
01677             AST_LIST_UNLOCK(&peers);
01678          }
01679       }
01680 
01681       if (!peer || !peer->dynamic) {
01682          dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01683          dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
01684       } else {
01685          int hasauth = 0;
01686          trans->us_eid = peer->us_eid;
01687          if (!ast_strlen_zero(peer->inkey)) {
01688             hasauth = encrypted;
01689          } else
01690             hasauth = 1;
01691          if (hasauth) {
01692             int expire = default_expiration;
01693             char data[256];
01694             int needqual = 0;
01695             AST_SCHED_DEL(sched, peer->registerexpire);
01696             peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
01697             snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
01698                ntohs(trans->addr.sin_port), expire);
01699             ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
01700             if (inaddrcmp(&peer->addr, &trans->addr)) {
01701                ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
01702                      ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
01703                      ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
01704                needqual = 1;
01705             }
01706 
01707             memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
01708             dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
01709             dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
01710             if (needqual)
01711                qualify_peer(peer, 1);
01712          }
01713       }
01714       break;
01715    case DUNDI_COMMAND_DPRESPONSE:
01716       /* A dialplan response, lets see what we got... */
01717       if (ies.cause < 1) {
01718          /* Success of some sort */
01719          ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
01720          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01721             authpass = encrypted;
01722          } else
01723             authpass = 1;
01724          if (authpass) {
01725             /* Pass back up answers */
01726             if (trans->parent && trans->parent->dr) {
01727                y = trans->parent->respcount;
01728                for (x=0;x<ies.anscount;x++) {
01729                   if (trans->parent->respcount < trans->parent->maxcount) {
01730                      /* Make sure it's not already there */
01731                      for (z=0;z<trans->parent->respcount;z++) {
01732                         if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
01733                             !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
01734                               break;
01735                      }
01736                      if (z == trans->parent->respcount) {
01737                         /* Copy into parent responses */
01738                         trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
01739                         trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
01740                         trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
01741                         trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
01742                         if (ies.expiration > 0)
01743                            trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
01744                         else
01745                            trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
01746                         ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
01747                            sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
01748                            &ies.answers[x]->eid);
01749                         ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
01750                            sizeof(trans->parent->dr[trans->parent->respcount].dest));
01751                         ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
01752                            sizeof(trans->parent->dr[trans->parent->respcount].tech));
01753                         trans->parent->respcount++;
01754                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01755                      } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
01756                         /* Update weight if appropriate */
01757                         trans->parent->dr[z].weight = ies.answers[x]->weight;
01758                      }
01759                   } else
01760                      ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
01761                         trans->parent->number, trans->parent->dcontext);
01762                }
01763                /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
01764                   the cache know if this request was unaffected by our entity list. */
01765                cache_save(&trans->them_eid, trans->parent, y,
01766                      ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
01767                if (ies.hint) {
01768                   cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
01769                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01770                      ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01771                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
01772                      if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
01773                         ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
01774                            sizeof(trans->parent->hmd->exten));
01775                      }
01776                   } else {
01777                      ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01778                   }
01779                }
01780                if (ies.expiration > 0) {
01781                   if (trans->parent->expiration > ies.expiration) {
01782                      trans->parent->expiration = ies.expiration;
01783                   }
01784                }
01785             }
01786             /* Close connection if not final */
01787             if (!final)
01788                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01789          }
01790 
01791       } else {
01792          /* Auth failure, check for data */
01793          if (!final) {
01794             /* Cancel if they didn't already */
01795             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01796          }
01797       }
01798       break;
01799    case DUNDI_COMMAND_EIDRESPONSE:
01800       /* A dialplan response, lets see what we got... */
01801       if (ies.cause < 1) {
01802          /* Success of some sort */
01803          ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
01804          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01805             authpass = encrypted;
01806          } else
01807             authpass = 1;
01808          if (authpass) {
01809             /* Pass back up answers */
01810             if (trans->parent && trans->parent->dei && ies.q_org) {
01811                if (!trans->parent->respcount) {
01812                   trans->parent->respcount++;
01813                   if (ies.q_dept)
01814                      ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
01815                   if (ies.q_org)
01816                      ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
01817                   if (ies.q_locality)
01818                      ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
01819                   if (ies.q_stateprov)
01820                      ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
01821                   if (ies.q_country)
01822                      ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
01823                   if (ies.q_email)
01824                      ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
01825                   if (ies.q_phone)
01826                      ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
01827                   if (ies.q_ipaddr)
01828                      ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
01829                   if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
01830                      /* If it's them, update our address */
01831                      ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
01832                   }
01833                }
01834                if (ies.hint) {
01835                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01836                      ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01837                }
01838             }
01839             /* Close connection if not final */
01840             if (!final)
01841                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01842          }
01843 
01844       } else {
01845          /* Auth failure, check for data */
01846          if (!final) {
01847             /* Cancel if they didn't already */
01848             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01849          }
01850       }
01851       break;
01852    case DUNDI_COMMAND_REGRESPONSE:
01853       /* A dialplan response, lets see what we got... */
01854       if (ies.cause < 1) {
01855          int hasauth;
01856          /* Success of some sort */
01857          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01858             hasauth = encrypted;
01859          } else
01860             hasauth = 1;
01861 
01862          if (!hasauth) {
01863             ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
01864             if (!final) {
01865                dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
01866                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
01867             }
01868          } else {
01869             ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
01870                   ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
01871             /* Close connection if not final */
01872             if (!final)
01873                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01874          }
01875       } else {
01876          /* Auth failure, cancel if they didn't for some reason */
01877          if (!final) {
01878             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01879          }
01880       }
01881       break;
01882    case DUNDI_COMMAND_INVALID:
01883    case DUNDI_COMMAND_NULL:
01884    case DUNDI_COMMAND_PRECACHERP:
01885       /* Do nothing special */
01886       if (!final)
01887          dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01888       break;
01889    case DUNDI_COMMAND_ENCREJ:
01890       if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
01891          /* No really, it's over at this point */
01892          if (!final)
01893             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01894       } else {
01895          /* Send with full key */
01896          ast_set_flag(trans, FLAG_SENDFULLKEY);
01897          if (final) {
01898             /* Ooops, we got a final message, start by sending ACK... */
01899             dundi_ack(trans, hdr->cmdresp & 0x80);
01900             trans->aseqno = trans->iseqno;
01901             /* Now, we gotta create a new transaction */
01902             if (!reset_transaction(trans)) {
01903                /* Make sure handle_frame doesn't destroy us */
01904                hdr->cmdresp &= 0x7f;
01905                /* Parse the message we transmitted */
01906                memset(&ies, 0, sizeof(ies));
01907                dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
01908                /* Reconstruct outgoing encrypted packet */
01909                memset(ied, 0, sizeof(*ied));
01910                dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
01911                dundi_ie_append_raw(ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01912                dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01913                if (ies.encblock)
01914                   dundi_ie_append_encdata(ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
01915                dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
01916                peer->sentfullkey = 1;
01917             }
01918          }
01919       }
01920       break;
01921    case DUNDI_COMMAND_ENCRYPT:
01922       if (!encrypted) {
01923          /* No nested encryption! */
01924          if ((trans->iseqno == 1) && !trans->oseqno) {
01925             if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
01926                ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
01927                (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
01928                if (!final) {
01929                   dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01930                }
01931                break;
01932             }
01933             apply_peer(trans, peer);
01934             /* Key passed, use new contexts for this session */
01935             trans->ecx = peer->them_ecx;
01936             trans->dcx = peer->them_dcx;
01937          }
01938          if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
01939             struct dundi_hdr *dhdr;
01940             unsigned char decoded[MAX_PACKET_SIZE];
01941             int ddatalen;
01942             ddatalen = sizeof(decoded);
01943             dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
01944             if (dhdr) {
01945                /* Handle decrypted response */
01946                if (dundidebug)
01947                   dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
01948                handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
01949                /* Carry back final flag */
01950                hdr->cmdresp |= dhdr->cmdresp & 0x80;
01951                break;
01952             } else {
01953                ast_debug(1, "Ouch, decrypt failed :(\n");
01954             }
01955          }
01956       }
01957       if (!final) {
01958          /* Turn off encryption */
01959          ast_clear_flag(trans, FLAG_ENCRYPT);
01960          dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01961       }
01962       break;
01963    default:
01964       /* Send unknown command if we don't know it, with final flag IFF it's the
01965          first command in the dialog and only if we haven't received final notification */
01966       if (!final) {
01967          dundi_ie_append_byte(ied, DUNDI_IE_UNKNOWN, cmd);
01968          dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
01969       }
01970    }
01971 
01972    retval = 0;
01973 
01974 return_cleanup:
01975 #ifdef LOW_MEMORY
01976    ast_free(ied);
01977 #endif
01978    return retval;
01979 }
01980 
01981 static void destroy_packet(struct dundi_packet *pack, int needfree);
01982 static void destroy_packets(struct packetlist *p)
01983 {
01984    struct dundi_packet *pack;
01985 
01986    while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
01987       AST_SCHED_DEL(sched, pack->retransid);
01988       ast_free(pack);
01989    }
01990 }
01991 
01992 
01993 static int ack_trans(struct dundi_transaction *trans, int iseqno)
01994 {
01995    struct dundi_packet *pack;
01996 
01997    /* Ack transmitted packet corresponding to iseqno */
01998    AST_LIST_TRAVERSE(&trans->packets, pack, list) {
01999       if ((pack->h->oseqno + 1) % 255 == iseqno) {
02000          destroy_packet(pack, 0);
02001          if (!AST_LIST_EMPTY(&trans->lasttrans)) {
02002             ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
02003             destroy_packets(&trans->lasttrans);
02004          }
02005          AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
02006          AST_SCHED_DEL(sched, trans->autokillid);
02007          return 1;
02008       }
02009    }
02010 
02011    return 0;
02012 }
02013 
02014 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
02015 {
02016    struct dundi_transaction *trans;
02017    trans = find_transaction(h, sin);
02018    if (!trans) {
02019       dundi_reject(h, sin);
02020       return 0;
02021    }
02022    /* Got a transaction, see where this header fits in */
02023    if (h->oseqno == trans->iseqno) {
02024       /* Just what we were looking for...  Anything but ack increments iseqno */
02025       if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
02026          /* If final, we're done */
02027          destroy_trans(trans, 0);
02028          return 0;
02029       }
02030       if (h->cmdresp != DUNDI_COMMAND_ACK) {
02031          trans->oiseqno = trans->iseqno;
02032          trans->iseqno++;
02033          handle_command_response(trans, h, datalen, 0);
02034       }
02035       if (trans->aseqno != trans->iseqno) {
02036          dundi_ack(trans, h->cmdresp & 0x80);
02037          trans->aseqno = trans->iseqno;
02038       }
02039       /* Delete any saved last transmissions */
02040       destroy_packets(&trans->lasttrans);
02041       if (h->cmdresp & 0x80) {
02042          /* Final -- destroy now */
02043          destroy_trans(trans, 0);
02044       }
02045    } else if (h->oseqno == trans->oiseqno) {
02046       /* Last incoming sequence number -- send ACK without processing */
02047       dundi_ack(trans, 0);
02048    } else {
02049       /* Out of window -- simply drop */
02050       ast_debug(1, "Dropping packet out of window!\n");
02051    }
02052    return 0;
02053 }
02054 
02055 static int socket_read(int *id, int fd, short events, void *cbdata)
02056 {
02057    struct sockaddr_in sin;
02058    int res;
02059    struct dundi_hdr *h;
02060    char buf[MAX_PACKET_SIZE];
02061    socklen_t len = sizeof(sin);
02062 
02063    res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
02064    if (res < 0) {
02065       if (errno != ECONNREFUSED)
02066          ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
02067       return 1;
02068    }
02069    if (res < sizeof(struct dundi_hdr)) {
02070       ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
02071       return 1;
02072    }
02073    buf[res] = '\0';
02074    h = (struct dundi_hdr *) buf;
02075    if (dundidebug)
02076       dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
02077    AST_LIST_LOCK(&peers);
02078    handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
02079    AST_LIST_UNLOCK(&peers);
02080    return 1;
02081 }
02082 
02083 static void build_secret(char *secret, int seclen)
02084 {
02085    unsigned char tmp[16];
02086    char *s;
02087    build_iv(tmp);
02088    secret[0] = '\0';
02089    ast_base64encode(secret, tmp, sizeof(tmp), seclen);
02090    /* Eliminate potential bad characters */
02091    while((s = strchr(secret, ';'))) *s = '+';
02092    while((s = strchr(secret, '/'))) *s = '+';
02093    while((s = strchr(secret, ':'))) *s = '+';
02094    while((s = strchr(secret, '@'))) *s = '+';
02095 }
02096 
02097 
02098 static void save_secret(const char *newkey, const char *oldkey)
02099 {
02100    char tmp[256];
02101    if (oldkey)
02102       snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
02103    else
02104       snprintf(tmp, sizeof(tmp), "%s", newkey);
02105    rotatetime = time(NULL) + DUNDI_SECRET_TIME;
02106    ast_db_put(secretpath, "secret", tmp);
02107    snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
02108    ast_db_put(secretpath, "secretexpiry", tmp);
02109 }
02110 
02111 static void load_password(void)
02112 {
02113    char *current=NULL;
02114    char *last=NULL;
02115    char tmp[256];
02116    time_t expired;
02117 
02118    ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
02119    if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
02120       ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
02121       current = strchr(tmp, ';');
02122       if (!current)
02123          current = tmp;
02124       else {
02125          *current = '\0';
02126          current++;
02127       };
02128       if ((time(NULL) - expired) < 0) {
02129          if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
02130             expired = time(NULL) + DUNDI_SECRET_TIME;
02131       } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
02132          last = current;
02133          current = NULL;
02134       } else {
02135          last = NULL;
02136          current = NULL;
02137       }
02138    }
02139    if (current) {
02140       /* Current key is still valid, just setup rotatation properly */
02141       ast_copy_string(cursecret, current, sizeof(cursecret));
02142       rotatetime = expired;
02143    } else {
02144       /* Current key is out of date, rotate or eliminate all together */
02145       build_secret(cursecret, sizeof(cursecret));
02146       save_secret(cursecret, last);
02147    }
02148 }
02149 
02150 static void check_password(void)
02151 {
02152    char oldsecret[80];
02153    time_t now;
02154 
02155    time(&now);
02156 #if 0
02157    printf("%ld/%ld\n", now, rotatetime);
02158 #endif
02159    if ((now - rotatetime) >= 0) {
02160       /* Time to rotate keys */
02161       ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
02162       build_secret(cursecret, sizeof(cursecret));
02163       save_secret(cursecret, oldsecret);
02164    }
02165 }
02166 
02167 static void *network_thread(void *ignore)
02168 {
02169    /* Our job is simple: Send queued messages, retrying if necessary.  Read frames
02170       from the network, and queue them for delivery to the channels */
02171    int res;
02172    /* Establish I/O callback for socket read */
02173    ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
02174 
02175    while (!dundi_shutdown) {
02176       res = ast_sched_wait(sched);
02177       if ((res > 1000) || (res < 0))
02178          res = 1000;
02179       res = ast_io_wait(io, res);
02180       if (res >= 0) {
02181          AST_LIST_LOCK(&peers);
02182          ast_sched_runq(sched);
02183          AST_LIST_UNLOCK(&peers);
02184       }
02185       check_password();
02186    }
02187 
02188    netthreadid = AST_PTHREADT_NULL;
02189 
02190    return NULL;
02191 }
02192 
02193 static void *process_clearcache(void *ignore)
02194 {
02195    struct ast_db_entry *db_entry, *db_tree;
02196    int striplen = sizeof("/dundi/cache");
02197    time_t now;
02198 
02199    while (!dundi_shutdown) {
02200       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
02201 
02202       time(&now);
02203 
02204       db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
02205       for (; db_entry; db_entry = db_entry->next) {
02206          time_t expiry;
02207 
02208          if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
02209             if (expiry < now) {
02210                ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
02211                ast_db_del("dundi/cache", db_entry->key + striplen);
02212             }
02213          }
02214       }
02215       ast_db_freetree(db_tree);
02216 
02217       pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
02218       pthread_testcancel();
02219       sleep(60);
02220       pthread_testcancel();
02221    }
02222 
02223    clearcachethreadid = AST_PTHREADT_NULL;
02224    return NULL;
02225 }
02226 
02227 static void *process_precache(void *ign)
02228 {
02229    struct dundi_precache_queue *qe;
02230    time_t now;
02231    char context[256];
02232    char number[256];
02233    int run;
02234 
02235    while (!dundi_shutdown) {
02236       time(&now);
02237       run = 0;
02238       AST_LIST_LOCK(&pcq);
02239       if ((qe = AST_LIST_FIRST(&pcq))) {
02240          if (!qe->expiration) {
02241             /* Gone...  Remove... */
02242             AST_LIST_REMOVE_HEAD(&pcq, list);
02243             ast_free(qe);
02244          } else if (qe->expiration < now) {
02245             /* Process this entry */
02246             qe->expiration = 0;
02247             ast_copy_string(context, qe->context, sizeof(context));
02248             ast_copy_string(number, qe->number, sizeof(number));
02249             run = 1;
02250          }
02251       }
02252       AST_LIST_UNLOCK(&pcq);
02253       if (run) {
02254          dundi_precache(context, number);
02255       } else
02256          sleep(1);
02257    }
02258 
02259    precachethreadid = AST_PTHREADT_NULL;
02260 
02261    return NULL;
02262 }
02263 
02264 static int start_network_thread(void)
02265 {
02266    ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
02267    ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
02268    ast_pthread_create_background(&clearcachethreadid, NULL, process_clearcache, NULL);
02269    return 0;
02270 }
02271 
02272 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02273 {
02274    switch (cmd) {
02275    case CLI_INIT:
02276       e->command = "dundi set debug {on|off}";
02277       e->usage =
02278          "Usage: dundi set debug {on|off}\n"
02279          "       Enables/Disables dumping of DUNDi packets for debugging purposes\n";
02280       return NULL;
02281    case CLI_GENERATE:
02282       return NULL;
02283    }
02284 
02285    if (a->argc != e->args)
02286       return CLI_SHOWUSAGE;
02287 
02288    if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
02289       dundidebug = 1;
02290       ast_cli(a->fd, "DUNDi Debugging Enabled\n");
02291    } else {
02292       dundidebug = 0;
02293       ast_cli(a->fd, "DUNDi Debugging Disabled\n");
02294    }
02295    return CLI_SUCCESS;
02296 }
02297 
02298 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02299 {
02300    switch (cmd) {
02301    case CLI_INIT:
02302       e->command = "dundi store history {on|off}";
02303       e->usage =
02304          "Usage: dundi store history {on|off}\n"
02305          "       Enables/Disables storing of DUNDi requests and times for debugging\n"
02306          "purposes\n";
02307       return NULL;
02308    case CLI_GENERATE:
02309       return NULL;
02310    }
02311 
02312    if (a->argc != e->args)
02313       return CLI_SHOWUSAGE;
02314 
02315    if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
02316       global_storehistory = 1;
02317       ast_cli(a->fd, "DUNDi History Storage Enabled\n");
02318    } else {
02319       global_storehistory = 0;
02320       ast_cli(a->fd, "DUNDi History Storage Disabled\n");
02321    }
02322    return CLI_SUCCESS;
02323 }
02324 
02325 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02326 {
02327    int stats = 0;
02328    switch (cmd) {
02329    case CLI_INIT:
02330       e->command = "dundi flush [stats]";
02331       e->usage =
02332          "Usage: dundi flush [stats]\n"
02333          "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
02334          "'stats' is present, clears timer statistics instead of normal\n"
02335          "operation.\n";
02336       return NULL;
02337    case CLI_GENERATE:
02338       return NULL;
02339    }
02340    if ((a->argc < 2) || (a->argc > 3))
02341       return CLI_SHOWUSAGE;
02342    if (a->argc > 2) {
02343       if (!strcasecmp(a->argv[2], "stats"))
02344          stats = 1;
02345       else
02346          return CLI_SHOWUSAGE;
02347    }
02348    if (stats) {
02349       /* Flush statistics */
02350       struct dundi_peer *p;
02351       int x;
02352       AST_LIST_LOCK(&peers);
02353       AST_LIST_TRAVERSE(&peers, p, list) {
02354          for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02355             if (p->lookups[x])
02356                ast_free(p->lookups[x]);
02357             p->lookups[x] = NULL;
02358             p->lookuptimes[x] = 0;
02359          }
02360          p->avgms = 0;
02361       }
02362       AST_LIST_UNLOCK(&peers);
02363    } else {
02364       ast_db_deltree("dundi/cache", NULL);
02365       ast_cli(a->fd, "DUNDi Cache Flushed\n");
02366    }
02367    return CLI_SUCCESS;
02368 }
02369 
02370 static char *model2str(int model)
02371 {
02372    switch(model) {
02373    case DUNDI_MODEL_INBOUND:
02374       return "Inbound";
02375    case DUNDI_MODEL_OUTBOUND:
02376       return "Outbound";
02377    case DUNDI_MODEL_SYMMETRIC:
02378       return "Symmetric";
02379    default:
02380       return "Unknown";
02381    }
02382 }
02383 
02384 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
02385 {
02386    int which=0, len;
02387    char *ret = NULL;
02388    struct dundi_peer *p;
02389    char eid_str[20];
02390 
02391    if (pos != rpos)
02392       return NULL;
02393    AST_LIST_LOCK(&peers);
02394    len = strlen(word);
02395    AST_LIST_TRAVERSE(&peers, p, list) {
02396       const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
02397       if (!strncasecmp(word, s, len) && ++which > state) {
02398          ret = ast_strdup(s);
02399          break;
02400       }
02401    }
02402    AST_LIST_UNLOCK(&peers);
02403    return ret;
02404 }
02405 
02406 static int rescomp(const void *a, const void *b)
02407 {
02408    const struct dundi_result *resa, *resb;
02409    resa = a;
02410    resb = b;
02411    if (resa->weight < resb->weight)
02412       return -1;
02413    if (resa->weight > resb->weight)
02414       return 1;
02415    return 0;
02416 }
02417 
02418 static void sort_results(struct dundi_result *results, int count)
02419 {
02420    qsort(results, count, sizeof(results[0]), rescomp);
02421 }
02422 
02423 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02424 {
02425    int res;
02426    char tmp[256];
02427    char fs[80] = "";
02428    char *context;
02429    int x;
02430    int bypass = 0;
02431    struct dundi_result dr[MAX_RESULTS];
02432    struct timeval start;
02433    switch (cmd) {
02434    case CLI_INIT:
02435       e->command = "dundi lookup";
02436       e->usage =
02437          "Usage: dundi lookup <number>[@context] [bypass]\n"
02438          "       Lookup the given number within the given DUNDi context\n"
02439          "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
02440          "keyword is specified.\n";
02441       return NULL;
02442    case CLI_GENERATE:
02443       return NULL;
02444    }
02445 
02446    if ((a->argc < 3) || (a->argc > 4))
02447       return CLI_SHOWUSAGE;
02448    if (a->argc > 3) {
02449       if (!strcasecmp(a->argv[3], "bypass"))
02450          bypass=1;
02451       else
02452          return CLI_SHOWUSAGE;
02453    }
02454    ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02455    context = strchr(tmp, '@');
02456    if (context) {
02457       *context = '\0';
02458       context++;
02459    }
02460    start = ast_tvnow();
02461    res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
02462 
02463    if (res < 0)
02464       ast_cli(a->fd, "DUNDi lookup returned error.\n");
02465    else if (!res)
02466       ast_cli(a->fd, "DUNDi lookup returned no results.\n");
02467    else
02468       sort_results(dr, res);
02469    for (x=0;x<res;x++) {
02470       ast_cli(a->fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
02471       ast_cli(a->fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
02472    }
02473    ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02474    return CLI_SUCCESS;
02475 }
02476 
02477 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02478 {
02479    int res;
02480    char tmp[256];
02481    char *context;
02482    struct timeval start;
02483    switch (cmd) {
02484    case CLI_INIT:
02485       e->command = "dundi precache";
02486       e->usage =
02487          "Usage: dundi precache <number>[@context]\n"
02488          "       Lookup the given number within the given DUNDi context\n"
02489          "(or e164 if none is specified) and precaches the results to any\n"
02490          "upstream DUNDi push servers.\n";
02491       return NULL;
02492    case CLI_GENERATE:
02493       return NULL;
02494    }
02495    if ((a->argc < 3) || (a->argc > 3))
02496       return CLI_SHOWUSAGE;
02497    ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02498    context = strchr(tmp, '@');
02499    if (context) {
02500       *context = '\0';
02501       context++;
02502    }
02503    start = ast_tvnow();
02504    res = dundi_precache(context, tmp);
02505 
02506    if (res < 0)
02507       ast_cli(a->fd, "DUNDi precache returned error.\n");
02508    else if (!res)
02509       ast_cli(a->fd, "DUNDi precache returned no error.\n");
02510    ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02511    return CLI_SUCCESS;
02512 }
02513 
02514 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02515 {
02516    int res;
02517    char tmp[256];
02518    char *context;
02519    dundi_eid eid;
02520    struct dundi_entity_info dei;
02521    switch (cmd) {
02522    case CLI_INIT:
02523       e->command = "dundi query";
02524       e->usage =
02525          "Usage: dundi query <entity>[@context]\n"
02526          "       Attempts to retrieve contact information for a specific\n"
02527          "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
02528          "e164 if none is specified).\n";
02529       return NULL;
02530    case CLI_GENERATE:
02531       return NULL;
02532    }
02533    if ((a->argc < 3) || (a->argc > 3))
02534       return CLI_SHOWUSAGE;
02535    if (ast_str_to_eid(&eid, a->argv[2])) {
02536       ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
02537       return CLI_SHOWUSAGE;
02538    }
02539    ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02540    context = strchr(tmp, '@');
02541    if (context) {
02542       *context = '\0';
02543       context++;
02544    }
02545    res = dundi_query_eid(&dei, context, eid);
02546    if (res < 0)
02547       ast_cli(a->fd, "DUNDi Query EID returned error.\n");
02548    else if (!res)
02549       ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
02550    else {
02551       ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
02552       ast_cli(a->fd, "Department:      %s\n", dei.orgunit);
02553       ast_cli(a->fd, "Organization:    %s\n", dei.org);
02554       ast_cli(a->fd, "City/Locality:   %s\n", dei.locality);
02555       ast_cli(a->fd, "State/Province:  %s\n", dei.stateprov);
02556       ast_cli(a->fd, "Country:         %s\n", dei.country);
02557       ast_cli(a->fd, "E-mail:          %s\n", dei.email);
02558       ast_cli(a->fd, "Phone:           %s\n", dei.phone);
02559       ast_cli(a->fd, "IP Address:      %s\n", dei.ipaddr);
02560    }
02561    return CLI_SUCCESS;
02562 }
02563 
02564 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02565 {
02566    struct dundi_peer *peer;
02567    struct permission *p;
02568    char *order;
02569    char eid_str[20];
02570    int x, cnt;
02571    switch (cmd) {
02572    case CLI_INIT:
02573       e->command = "dundi show peer";
02574       e->usage =
02575          "Usage: dundi show peer [peer]\n"
02576          "       Provide a detailed description of a specifid DUNDi peer.\n";
02577       return NULL;
02578    case CLI_GENERATE:
02579       return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
02580    }
02581    if (a->argc != 4)
02582       return CLI_SHOWUSAGE;
02583    AST_LIST_LOCK(&peers);
02584    AST_LIST_TRAVERSE(&peers, peer, list) {
02585       if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
02586          break;
02587    }
02588    if (peer) {
02589       switch(peer->order) {
02590       case 0:
02591          order = "Primary";
02592          break;
02593       case 1:
02594          order = "Secondary";
02595          break;
02596       case 2:
02597          order = "Tertiary";
02598          break;
02599       case 3:
02600          order = "Quartiary";
02601          break;
02602       default:
02603          order = "Unknown";
02604       }
02605       ast_cli(a->fd, "Peer:    %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02606       ast_cli(a->fd, "Model:   %s\n", model2str(peer->model));
02607       ast_cli(a->fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
02608       ast_cli(a->fd, "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     %-10.10s %-8.8s %-15.15s\n"
02640 #define FORMAT "%-20.20s %-15.15s %s %-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", "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)", 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)", 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 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, "host")) {
04413          if (!strcasecmp(v->value, "dynamic")) {
04414             peer->dynamic = 1;
04415          } else {
04416             hp = ast_gethostbyname(v->value, &he);
04417             if (hp) {
04418                memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
04419                peer->dynamic = 0;
04420             } else {
04421                ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
04422                peer->dead = 1;
04423             }
04424          }
04425       } else if (!strcasecmp(v->name, "ustothem")) {
04426          if (!ast_str_to_eid(&testeid, v->value))
04427             peer->us_eid = testeid;
04428          else
04429             ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
04430       } else if (!strcasecmp(v->name, "include")) {
04431          append_permission(&peer->include, v->value, 1);
04432       } else if (!strcasecmp(v->name, "permit")) {
04433          append_permission(&peer->permit, v->value, 1);
04434       } else if (!strcasecmp(v->name, "noinclude")) {
04435          append_permission(&peer->include, v->value, 0);
04436       } else if (!strcasecmp(v->name, "deny")) {
04437          append_permission(&peer->permit, v->value, 0);
04438       } else if (!strcasecmp(v->name, "register")) {
04439          needregister = ast_true(v->value);
04440       } else if (!strcasecmp(v->name, "order")) {
04441          if (!strcasecmp(v->value, "primary"))
04442             peer->order = 0;
04443          else if (!strcasecmp(v->value, "secondary"))
04444             peer->order = 1;
04445          else if (!strcasecmp(v->value, "tertiary"))
04446             peer->order = 2;
04447          else if (!strcasecmp(v->value, "quartiary"))
04448             peer->order = 3;
04449          else {
04450             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);
04451          }
04452       } else if (!strcasecmp(v->name, "qualify")) {
04453          if (!strcasecmp(v->value, "no")) {
04454             peer->maxms = 0;
04455          } else if (!strcasecmp(v->value, "yes")) {
04456             peer->maxms = DEFAULT_MAXMS;
04457          } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
04458             ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
04459                ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
04460             peer->maxms = 0;
04461          }
04462       } else if (!strcasecmp(v->name, "model")) {
04463          if (!strcasecmp(v->value, "inbound"))
04464             peer->model = DUNDI_MODEL_INBOUND;
04465          else if (!strcasecmp(v->value, "outbound"))
04466             peer->model = DUNDI_MODEL_OUTBOUND;
04467          else if (!strcasecmp(v->value, "symmetric"))
04468             peer->model = DUNDI_MODEL_SYMMETRIC;
04469          else if (!strcasecmp(v->value, "none"))
04470             peer->model = 0;
04471          else {
04472             ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04473                v->value, v->lineno);
04474          }
04475       } else if (!strcasecmp(v->name, "precache")) {
04476          if (!strcasecmp(v->value, "inbound"))
04477             peer->pcmodel = DUNDI_MODEL_INBOUND;
04478          else if (!strcasecmp(v->value, "outbound"))
04479             peer->pcmodel = DUNDI_MODEL_OUTBOUND;
04480          else if (!strcasecmp(v->value, "symmetric"))
04481             peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
04482          else if (!strcasecmp(v->value, "none"))
04483             peer->pcmodel = 0;
04484          else {
04485             ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04486                v->value, v->lineno);
04487          }
04488       }
04489    }
04490    (*globalpcmode) |= peer->pcmodel;
04491    if (!peer->model && !peer->pcmodel) {
04492       ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
04493          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04494       peer->dead = 1;
04495    } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04496       ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
04497          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04498       peer->dead = 1;
04499    } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04500       ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
04501          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04502       peer->dead = 1;
04503    } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04504       ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
04505          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04506    } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04507       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",
04508          ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04509    } else {
04510       if (needregister) {
04511          peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
04512       }
04513       qualify_peer(peer, 1);
04514    }
04515    AST_LIST_UNLOCK(&peers);
04516 }
04517 
04518 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
04519 {
04520    struct dundi_result results[MAX_RESULTS];
04521    int res;
04522    int x;
04523    int found = 0;
04524    if (!strncasecmp(context, "macro-", 6)) {
04525       if (!chan) {
04526          ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04527          return -1;
04528       }
04529       /* If done as a macro, use macro extension */
04530       if (!strcasecmp(exten, "s")) {
04531          exten = pbx_builtin_getvar_helper(chan, "ARG1");
04532          if (ast_strlen_zero(exten))
04533             exten = chan->macroexten;
04534          if (ast_strlen_zero(exten))
04535             exten = chan->exten;
04536          if (ast_strlen_zero(exten)) {
04537             ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04538             return -1;
04539          }
04540       }
04541       if (ast_strlen_zero(data))
04542          data = "e164";
04543    } else {
04544       if (ast_strlen_zero(data))
04545          data = context;
04546    }
04547    res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04548    for (x=0;x<res;x++) {
04549       if (ast_test_flag(results + x, flag))
04550          found++;
04551    }
04552    if (found >= priority)
04553       return 1;
04554    return 0;
04555 }
04556 
04557 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04558 {
04559    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
04560 }
04561 
04562 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04563 {
04564    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
04565 }
04566 
04567 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04568 {
04569    struct dundi_result results[MAX_RESULTS];
04570    int res;
04571    int x=0;
04572    char req[1024];
04573    const char *dundiargs;
04574    struct ast_app *dial;
04575 
04576    if (!strncasecmp(context, "macro-", 6)) {
04577       if (!chan) {
04578          ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04579          return -1;
04580       }
04581       /* If done as a macro, use macro extension */
04582       if (!strcasecmp(exten, "s")) {
04583          exten = pbx_builtin_getvar_helper(chan, "ARG1");
04584          if (ast_strlen_zero(exten))
04585             exten = chan->macroexten;
04586          if (ast_strlen_zero(exten))
04587             exten = chan->exten;
04588          if (ast_strlen_zero(exten)) {
04589             ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04590             return -1;
04591          }
04592       }
04593       if (ast_strlen_zero(data))
04594          data = "e164";
04595    } else {
04596       if (ast_strlen_zero(data))
04597          data = context;
04598    }
04599    res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04600    if (res > 0) {
04601       sort_results(results, res);
04602       for (x=0;x<res;x++) {
04603          if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
04604             if (!--priority)
04605                break;
04606          }
04607       }
04608    }
04609    if (x < res) {
04610       /* Got a hit! */
04611       dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
04612       snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest,
04613          S_OR(dundiargs, ""));
04614       dial = pbx_findapp("Dial");
04615       if (dial)
04616          res = pbx_exec(chan, dial, req);
04617    } else
04618       res = -1;
04619    return res;
04620 }
04621 
04622 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04623 {
04624    return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
04625 }
04626 
04627 static struct ast_switch dundi_switch = {
04628    .name        = "DUNDi",
04629    .description = "DUNDi Discovered Dialplan Switch",
04630    .exists      = dundi_exists,
04631    .canmatch    = dundi_canmatch,
04632    .exec        = dundi_exec,
04633    .matchmore   = dundi_matchmore,
04634 };
04635 
04636 static int set_config(char *config_file, struct sockaddr_in* sin, int reload)
04637 {
04638    struct ast_config *cfg;
04639    struct ast_variable *v;
04640    char *cat;
04641    int x;
04642    struct ast_flags config_flags = { 0 };
04643    char hn[MAXHOSTNAMELEN] = "";
04644    struct ast_hostent he;
04645    struct hostent *hp;
04646    struct sockaddr_in sin2;
04647    static int last_port = 0;
04648    int globalpcmodel = 0;
04649    dundi_eid testeid;
04650 
04651    if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
04652       ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
04653       return -1;
04654    }
04655 
04656    dundi_ttl = DUNDI_DEFAULT_TTL;
04657    dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
04658    any_peer = NULL;
04659 
04660    ipaddr[0] = '\0';
04661    if (!gethostname(hn, sizeof(hn)-1)) {
04662       hp = ast_gethostbyname(hn, &he);
04663       if (hp) {
04664          memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
04665          ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr));
04666       } else
04667          ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
04668    } else
04669       ast_log(LOG_WARNING, "Unable to get host name!\n");
04670    AST_LIST_LOCK(&peers);
04671 
04672    memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
04673 
04674    global_storehistory = 0;
04675    ast_copy_string(secretpath, "dundi", sizeof(secretpath));
04676    v = ast_variable_browse(cfg, "general");
04677    while(v) {
04678       if (!strcasecmp(v->name, "port")){
04679          sin->sin_port = ntohs(atoi(v->value));
04680          if(last_port==0){
04681             last_port=sin->sin_port;
04682          } else if(sin->sin_port != last_port)
04683             ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
04684       } else if (!strcasecmp(v->name, "bindaddr")) {
04685          struct hostent *hep;
04686          struct ast_hostent hent;
04687          hep = ast_gethostbyname(v->value, &hent);
04688          if (hep) {
04689             memcpy(&sin->sin_addr, hep->h_addr, sizeof(sin->sin_addr));
04690          } else
04691             ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
04692       } else if (!strcasecmp(v->name, "authdebug")) {
04693          authdebug = ast_true(v->value);
04694       } else if (!strcasecmp(v->name, "ttl")) {
04695          if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
04696             dundi_ttl = x;
04697          } else {
04698             ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
04699                v->value, v->lineno, DUNDI_DEFAULT_TTL);
04700          }
04701       } else if (!strcasecmp(v->name, "autokill")) {
04702          if (sscanf(v->value, "%30d", &x) == 1) {
04703             if (x >= 0)
04704                global_autokilltimeout = x;
04705             else
04706                ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
04707          } else if (ast_true(v->value)) {
04708             global_autokilltimeout = DEFAULT_MAXMS;
04709          } else {
04710             global_autokilltimeout = 0;
04711          }
04712       } else if (!strcasecmp(v->name, "entityid")) {
04713          if (!ast_str_to_eid(&testeid, v->value))
04714             global_eid = testeid;
04715          else
04716             ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
04717       } else if (!strcasecmp(v->name, "tos")) {
04718          if (ast_str2tos(v->value, &tos))
04719             ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
04720       } else if (!strcasecmp(v->name, "department")) {
04721          ast_copy_string(dept, v->value, sizeof(dept));
04722       } else if (!strcasecmp(v->name, "organization")) {
04723          ast_copy_string(org, v->value, sizeof(org));
04724       } else if (!strcasecmp(v->name, "locality")) {
04725          ast_copy_string(locality, v->value, sizeof(locality));
04726       } else if (!strcasecmp(v->name, "stateprov")) {
04727          ast_copy_string(stateprov, v->value, sizeof(stateprov));
04728       } else if (!strcasecmp(v->name, "country")) {
04729          ast_copy_string(country, v->value, sizeof(country));
04730       } else if (!strcasecmp(v->name, "email")) {
04731          ast_copy_string(email, v->value, sizeof(email));
04732       } else if (!strcasecmp(v->name, "phone")) {
04733          ast_copy_string(phone, v->value, sizeof(phone));
04734       } else if (!strcasecmp(v->name, "storehistory")) {
04735          global_storehistory = ast_true(v->value);
04736       } else if (!strcasecmp(v->name, "cachetime")) {
04737          if ((sscanf(v->value, "%30d", &x) == 1)) {
04738             dundi_cache_time = x;
04739          } else {
04740             ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
04741                v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
04742          }
04743       }
04744       v = v->next;
04745    }
04746    AST_LIST_UNLOCK(&peers);
04747    mark_mappings();
04748    v = ast_variable_browse(cfg, "mappings");
04749    while(v) {
04750       build_mapping(v->name, v->value);
04751       v = v->next;
04752    }
04753    prune_mappings();
04754    mark_peers();
04755    cat = ast_category_browse(cfg, NULL);
04756    while(cat) {
04757       if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
04758          /* Entries */
04759          if (!ast_str_to_eid(&testeid, cat))
04760             build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
04761          else if (!strcasecmp(cat, "*")) {
04762             build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
04763             any_peer = find_peer(NULL);
04764          } else
04765             ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
04766       }
04767       cat = ast_category_browse(cfg, cat);
04768    }
04769    prune_peers();
04770    ast_config_destroy(cfg);
04771    load_password();
04772    if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
04773       dundi_precache_full();
04774    return 0;
04775 }
04776 
04777 static int unload_module(void)
04778 {
04779    pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid, previous_clearcachethreadid = clearcachethreadid;
04780    ast_module_user_hangup_all();
04781 
04782    /* Stop all currently running threads */
04783    dundi_shutdown = 1;
04784    if (previous_netthreadid != AST_PTHREADT_NULL) {
04785       pthread_kill(previous_netthreadid, SIGURG);
04786       pthread_join(previous_netthreadid, NULL);
04787    }
04788    if (previous_precachethreadid != AST_PTHREADT_NULL) {
04789       pthread_kill(previous_precachethreadid, SIGURG);
04790       pthread_join(previous_precachethreadid, NULL);
04791    }
04792    if (previous_clearcachethreadid != AST_PTHREADT_NULL) {
04793       pthread_cancel(previous_clearcachethreadid);
04794       pthread_join(previous_clearcachethreadid, NULL);
04795    }
04796 
04797    ast_cli_unregister_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
04798    ast_unregister_switch(&dundi_switch);
04799    ast_custom_function_unregister(&dundi_function);
04800    ast_custom_function_unregister(&dundi_query_function);
04801    ast_custom_function_unregister(&dundi_result_function);
04802    close(netsocket);
04803    io_context_destroy(io);
04804    sched_context_destroy(sched);
04805 
04806    mark_mappings();
04807    prune_mappings();
04808    mark_peers();
04809    prune_peers();
04810 
04811    return 0;
04812 }
04813 
04814 static int reload(void)
04815 {
04816    struct sockaddr_in sin;
04817 
04818    if (set_config("dundi.conf", &sin, 1))
04819       return AST_MODULE_LOAD_FAILURE;
04820 
04821    return AST_MODULE_LOAD_SUCCESS;
04822 }
04823 
04824 static int load_module(void)
04825 {
04826    struct sockaddr_in sin;
04827 
04828    dundi_set_output(dundi_debug_output);
04829    dundi_set_error(dundi_error_output);
04830 
04831    sin.sin_family = AF_INET;
04832    sin.sin_port = ntohs(DUNDI_PORT);
04833    sin.sin_addr.s_addr = INADDR_ANY;
04834 
04835    /* Make a UDP socket */
04836    io = io_context_create();
04837    sched = sched_context_create();
04838 
04839    if (!io || !sched)
04840       return AST_MODULE_LOAD_DECLINE;
04841 
04842    if (set_config("dundi.conf", &sin, 0))
04843       return AST_MODULE_LOAD_DECLINE;
04844 
04845    netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
04846 
04847    if (netsocket < 0) {
04848       ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
04849       return AST_MODULE_LOAD_DECLINE;
04850    }
04851    if (bind(netsocket, (struct sockaddr *) &sin, sizeof(sin))) {
04852       ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n",
04853          ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
04854       return AST_MODULE_LOAD_DECLINE;
04855    }
04856 
04857    ast_netsock_set_qos(netsocket, tos, 0, "DUNDi");
04858 
04859    if (start_network_thread()) {
04860       ast_log(LOG_ERROR, "Unable to start network thread\n");
04861       close(netsocket);
04862       return AST_MODULE_LOAD_DECLINE;
04863    }
04864 
04865    ast_cli_register_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
04866    if (ast_register_switch(&dundi_switch))
04867       ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
04868    ast_custom_function_register(&dundi_function);
04869    ast_custom_function_register(&dundi_query_function);
04870    ast_custom_function_register(&dundi_result_function);
04871 
04872    ast_verb(2, "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
04873 
04874    return AST_MODULE_LOAD_SUCCESS;
04875 }
04876 
04877 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
04878       .load = load_module,
04879       .unload = unload_module,
04880       .reload = reload,
04881           );
04882