Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:52 2007

Asterisk developer's documentation :: Codename Pineapple


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 
00025 /*** MODULEINFO
00026    <depend>zlib</depend>
00027  ***/
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 49726 $")
00032 
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035 #include <unistd.h>
00036 #include <netinet/in.h>
00037 #include <arpa/inet.h>
00038 #include <sys/socket.h>
00039 #include <string.h>
00040 #include <errno.h>
00041 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
00042 #include <sys/types.h>
00043 #include <netinet/in_systm.h>
00044 #endif
00045 #include <netinet/ip.h>
00046 #include <sys/ioctl.h>
00047 #include <netinet/in.h>
00048 #include <net/if.h>
00049 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
00050 #include <net/if_dl.h>
00051 #include <ifaddrs.h>
00052 #endif
00053 #include <zlib.h>
00054 
00055 #include "asterisk/file.h"
00056 #include "asterisk/logger.h"
00057 #include "asterisk/channel.h"
00058 #include "asterisk/config.h"
00059 #include "asterisk/options.h"
00060 #include "asterisk/pbx.h"
00061 #include "asterisk/module.h"
00062 #include "asterisk/frame.h"
00063 #include "asterisk/file.h"
00064 #include "asterisk/cli.h"
00065 #include "asterisk/lock.h"
00066 #include "asterisk/md5.h"
00067 #include "asterisk/dundi.h"
00068 #include "asterisk/sched.h"
00069 #include "asterisk/io.h"
00070 #include "asterisk/utils.h"
00071 #include "asterisk/crypto.h"
00072 #include "asterisk/astdb.h"
00073 #include "asterisk/acl.h"
00074 #include "asterisk/aes.h"
00075 
00076 #include "dundi-parser.h"
00077 
00078 #define MAX_RESULTS  64
00079 
00080 #define MAX_PACKET_SIZE 8192
00081 
00082 #define DUNDI_MODEL_INBOUND      (1 << 0)
00083 #define DUNDI_MODEL_OUTBOUND  (1 << 1)
00084 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
00085 
00086 /*! Keep times of last 10 lookups */
00087 #define DUNDI_TIMING_HISTORY  10
00088 
00089 enum {
00090    FLAG_ISREG =       (1 << 0),  /*!< Transaction is register request */
00091    FLAG_DEAD =        (1 << 1),  /*!< Transaction is dead */
00092    FLAG_FINAL =       (1 << 2),  /*!< Transaction has final message sent */
00093    FLAG_ISQUAL =      (1 << 3),  /*!< Transaction is a qualification */
00094    FLAG_ENCRYPT =     (1 << 4),  /*!< Transaction is encrypted wiht ECX/DCX */
00095    FLAG_SENDFULLKEY = (1 << 5),  /*!< Send full key on transaction */
00096    FLAG_STOREHIST =   (1 << 6),  /*!< Record historic performance */
00097 };
00098 
00099 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
00100 
00101 #if 0
00102 #define DUNDI_SECRET_TIME 15  /* Testing only */
00103 #else
00104 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
00105 #endif
00106 
00107 static struct io_context *io;
00108 static struct sched_context *sched;
00109 static int netsocket = -1;
00110 static pthread_t netthreadid = AST_PTHREADT_NULL;
00111 static pthread_t precachethreadid = AST_PTHREADT_NULL;
00112 static int tos = 0;
00113 static int dundidebug = 0;
00114 static int authdebug = 0;
00115 static int dundi_ttl = DUNDI_DEFAULT_TTL;
00116 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
00117 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
00118 static int global_autokilltimeout = 0;
00119 static dundi_eid global_eid;
00120 static int default_expiration = 60;
00121 static int global_storehistory = 0;
00122 static char dept[80];
00123 static char org[80];
00124 static char locality[80];
00125 static char stateprov[80];
00126 static char country[80];
00127 static char email[80];
00128 static char phone[80];
00129 static char secretpath[80];
00130 static char cursecret[80];
00131 static char ipaddr[80];
00132 static time_t rotatetime;
00133 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
00134 
00135 struct permission {
00136    AST_LIST_ENTRY(permission) list;
00137    int allow;
00138    char name[0];
00139 };
00140 
00141 struct dundi_packet {
00142    AST_LIST_ENTRY(dundi_packet) list;
00143    struct dundi_hdr *h;
00144    int datalen;
00145    struct dundi_transaction *parent;
00146    int retransid;
00147    int retrans;
00148    unsigned char data[0];
00149 };
00150 
00151 struct dundi_hint_metadata {
00152    unsigned short flags;
00153    char exten[AST_MAX_EXTENSION];
00154 };
00155 
00156 struct dundi_precache_queue {
00157    AST_LIST_ENTRY(dundi_precache_queue) list;
00158    char *context;
00159    time_t expiration;
00160    char number[0];
00161 };
00162 
00163 struct dundi_request;
00164 
00165 struct dundi_transaction {
00166    struct sockaddr_in addr;                       /*!< Other end of transaction */
00167    struct timeval start;                          /*!< When this transaction was created */
00168    dundi_eid eids[DUNDI_MAX_STACK + 1];
00169    int eidcount;                                  /*!< Number of eids in eids */
00170    dundi_eid us_eid;                              /*!< Our EID, to them */
00171    dundi_eid them_eid;                            /*!< Their EID, to us */
00172    aes_encrypt_ctx   ecx;                           /*!< AES 128 Encryption context */
00173    aes_decrypt_ctx   dcx;                           /*!< AES 128 Decryption context */
00174    unsigned int flags;                            /*!< Has final packet been sent */
00175    int ttl;                                       /*!< Remaining TTL for queries on this one */
00176    int thread;                                    /*!< We have a calling thread */
00177    int retranstimer;                              /*!< How long to wait before retransmissions */
00178    int autokillid;                                /*!< ID to kill connection if answer doesn't come back fast enough */
00179    int autokilltimeout;                           /*!< Recommended timeout for autokill */
00180    unsigned short strans;                         /*!< Our transaction identifier */
00181    unsigned short dtrans;                         /*!< Their transaction identifer */
00182    unsigned char iseqno;                          /*!< Next expected received seqno */
00183    unsigned char oiseqno;                         /*!< Last received incoming seqno */
00184    unsigned char oseqno;                          /*!< Next transmitted seqno */
00185    unsigned char aseqno;                          /*!< Last acknowledge seqno */
00186    AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets;  /*!< Packets to be retransmitted */
00187    struct packetlist lasttrans;                   /*!< Last transmitted / ACK'd packet */
00188    struct dundi_request *parent;                  /*!< Parent request (if there is one) */
00189    AST_LIST_ENTRY(dundi_transaction) parentlist;  /*!< Next with respect to the parent */
00190    AST_LIST_ENTRY(dundi_transaction) all;         /*!< Next with respect to all DUNDi transactions */
00191 };
00192 
00193 struct dundi_request {
00194    char dcontext[AST_MAX_EXTENSION];
00195    char number[AST_MAX_EXTENSION];
00196    dundi_eid query_eid;
00197    dundi_eid root_eid;
00198    struct dundi_result *dr;
00199    struct dundi_entity_info *dei;
00200    struct dundi_hint_metadata *hmd;
00201    int maxcount;
00202    int respcount;
00203    int expiration;
00204    int cbypass;
00205    int pfds[2];
00206    unsigned long crc32;                              /*!< CRC-32 of all but root EID's in avoid list */
00207    AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans;  /*!< Transactions */
00208    AST_LIST_ENTRY(dundi_request) list;
00209 };
00210 
00211 struct dundi_mapping {
00212    char dcontext[AST_MAX_EXTENSION];
00213    char lcontext[AST_MAX_EXTENSION];
00214    int weight;
00215    int options;
00216    int tech;
00217    int dead;
00218    char dest[AST_MAX_EXTENSION];
00219    AST_LIST_ENTRY(dundi_mapping) list;
00220 };
00221 
00222 struct dundi_peer {
00223    dundi_eid eid;
00224    struct sockaddr_in addr;               /*!< Address of DUNDi peer */
00225    AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
00226    struct permissionlist include;
00227    dundi_eid us_eid;
00228    char inkey[80];
00229    char outkey[80];
00230    int dead;
00231    int registerid;
00232    int qualifyid;
00233    int sentfullkey;
00234    int order;
00235    unsigned char txenckey[256];           /*!< Transmitted encrypted key + sig */
00236    unsigned char rxenckey[256];           /*!< Cache received encrypted key + sig */
00237    unsigned long us_keycrc32;             /*!< CRC-32 of our key */
00238    aes_encrypt_ctx   us_ecx;                /*!< Cached AES 128 Encryption context */
00239    aes_decrypt_ctx   us_dcx;                 /*!< Cached AES 128 Decryption context */
00240    unsigned long them_keycrc32;           /*!< CRC-32 of our key */
00241    aes_encrypt_ctx   them_ecx;              /*!< Cached AES 128 Encryption context */
00242    aes_decrypt_ctx   them_dcx;              /*!< Cached AES 128 Decryption context */
00243    time_t keyexpire;                      /*!< When to expire/recreate key */
00244    int registerexpire;
00245    int lookuptimes[DUNDI_TIMING_HISTORY];
00246    char *lookups[DUNDI_TIMING_HISTORY];
00247    int avgms;
00248    struct dundi_transaction *regtrans;    /*!< Registration transaction */
00249    struct dundi_transaction *qualtrans;   /*!< Qualify transaction */
00250    int model;                             /*!< Pull model */
00251    int pcmodel;                           /*!< Push/precache model */
00252    int dynamic;                           /*!< Are we dynamic? */
00253    int lastms;                            /*!< Last measured latency */
00254    int maxms;                             /*!< Max permissible latency */
00255    struct timeval qualtx;                 /*!< Time of transmit */
00256    AST_LIST_ENTRY(dundi_peer) list;
00257 };
00258 
00259 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
00260 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
00261 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
00262 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
00263 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
00264 
00265 static int dundi_xmit(struct dundi_packet *pack);
00266 
00267 static void dundi_debug_output(const char *data)
00268 {
00269    if (dundidebug)
00270       ast_verbose("%s", data);
00271 }
00272 
00273 static void dundi_error_output(const char *data)
00274 {
00275    ast_log(LOG_WARNING, "%s", data);
00276 }
00277 
00278 static int has_permission(struct permissionlist *permlist, char *cont)
00279 {
00280    struct permission *perm;
00281    int res = 0;
00282 
00283    AST_LIST_TRAVERSE(permlist, perm, list) {
00284       if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
00285          res = perm->allow;
00286    }
00287 
00288    return res;
00289 }
00290 
00291 static char *tech2str(int tech)
00292 {
00293    switch(tech) {
00294    case DUNDI_PROTO_NONE:
00295       return "None";
00296    case DUNDI_PROTO_IAX:
00297       return "IAX2";
00298    case DUNDI_PROTO_SIP:
00299       return "SIP";
00300    case DUNDI_PROTO_H323:
00301       return "H323";
00302    default:
00303       return "Unknown";
00304    }
00305 }
00306 
00307 static int str2tech(char *str)
00308 {
00309    if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 
00310       return DUNDI_PROTO_IAX;
00311    else if (!strcasecmp(str, "SIP"))
00312       return DUNDI_PROTO_SIP;
00313    else if (!strcasecmp(str, "H323"))
00314       return DUNDI_PROTO_H323;
00315    else
00316       return -1;
00317 }
00318 
00319 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[]);
00320 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
00321 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
00322 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
00323 {
00324    struct dundi_transaction *trans;
00325 
00326    /* Look for an exact match first */
00327    AST_LIST_TRAVERSE(&alltrans, trans, all) {
00328       if (!inaddrcmp(&trans->addr, sin) && 
00329            ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
00330            ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
00331            if (hdr->strans)
00332               trans->dtrans = ntohs(hdr->strans) & 32767;
00333            return trans;
00334       }
00335    }
00336    
00337    switch(hdr->cmdresp & 0x7f) {
00338    case DUNDI_COMMAND_DPDISCOVER:
00339    case DUNDI_COMMAND_EIDQUERY:
00340    case DUNDI_COMMAND_PRECACHERQ:
00341    case DUNDI_COMMAND_REGREQ:
00342    case DUNDI_COMMAND_NULL:
00343    case DUNDI_COMMAND_ENCRYPT:
00344       if (!hdr->strans)
00345          break;
00346       /* Create new transaction */
00347       if (!(trans = create_transaction(NULL)))
00348          break;
00349       memcpy(&trans->addr, sin, sizeof(trans->addr));
00350       trans->dtrans = ntohs(hdr->strans) & 32767;
00351    default:
00352       break;
00353    }
00354    
00355    return trans;
00356 }
00357 
00358 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
00359 
00360 static int dundi_ack(struct dundi_transaction *trans, int final)
00361 {
00362    return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
00363 }
00364 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
00365 {
00366    struct {
00367       struct dundi_packet pack;
00368       struct dundi_hdr hdr;
00369    } tmp;
00370    struct dundi_transaction trans;
00371    /* Never respond to an INVALID with another INVALID */
00372    if (h->cmdresp == DUNDI_COMMAND_INVALID)
00373       return;
00374    memset(&tmp, 0, sizeof(tmp));
00375    memset(&trans, 0, sizeof(trans));
00376    memcpy(&trans.addr, sin, sizeof(trans.addr));
00377    tmp.hdr.strans = h->dtrans;
00378    tmp.hdr.dtrans = h->strans;
00379    tmp.hdr.iseqno = h->oseqno;
00380    tmp.hdr.oseqno = h->iseqno;
00381    tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
00382    tmp.hdr.cmdflags = 0;
00383    tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
00384    tmp.pack.datalen = sizeof(struct dundi_hdr);
00385    tmp.pack.parent = &trans;
00386    dundi_xmit(&tmp.pack);
00387 }
00388 
00389 static void reset_global_eid(void)
00390 {
00391 #if defined(SIOCGIFHWADDR)
00392    int s, x = 0;
00393    char eid_str[20];
00394    struct ifreq ifr;
00395 
00396    s = socket(AF_INET, SOCK_STREAM, 0);
00397    if (s < 0)
00398       return;
00399    for (x = 0; x < 10; x++) {
00400       memset(&ifr, 0, sizeof(ifr));
00401       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
00402       if (ioctl(s, SIOCGIFHWADDR, &ifr))
00403          continue;
00404       memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
00405       if (option_debug) {
00406          ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", 
00407             dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
00408       }
00409       break;
00410    }
00411    close(s);
00412 #else
00413 #if defined(ifa_broadaddr) && !defined(SOLARIS)
00414    char eid_str[20];
00415    struct ifaddrs *ifap;
00416    
00417    if (getifaddrs(&ifap) == 0) {
00418       struct ifaddrs *p;
00419       for (p = ifap; p; p = p->ifa_next) {
00420          if (p->ifa_addr->sa_family == AF_LINK) {
00421             struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
00422             memcpy(
00423                &(global_eid.eid),
00424                sdp->sdl_data + sdp->sdl_nlen, 6);
00425             if (option_debug)
00426                ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifap->ifa_name);
00427             freeifaddrs(ifap);
00428             return;
00429          }
00430       }
00431       freeifaddrs(ifap);
00432    }
00433 #endif
00434 #endif
00435    ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID  You will have to set it manually.\n");
00436 }
00437 
00438 static int get_trans_id(void)
00439 {
00440    struct dundi_transaction *t;
00441    int stid = (ast_random() % 32766) + 1;
00442    int tid = stid;
00443 
00444    do {
00445       AST_LIST_TRAVERSE(&alltrans, t, all) {
00446          if (t->strans == tid) 
00447             break;
00448       }
00449       if (!t)
00450          return tid;
00451       tid = (tid % 32766) + 1;
00452    } while (tid != stid);
00453 
00454    return 0;
00455 }
00456 
00457 static int reset_transaction(struct dundi_transaction *trans)
00458 {
00459    int tid;
00460    tid = get_trans_id();
00461    if (tid < 1)
00462       return -1;
00463    trans->strans = tid;
00464    trans->dtrans = 0;
00465    trans->iseqno = 0;
00466    trans->oiseqno = 0;
00467    trans->oseqno = 0;
00468    trans->aseqno = 0;
00469    ast_clear_flag(trans, FLAG_FINAL);  
00470    return 0;
00471 }
00472 
00473 static struct dundi_peer *find_peer(dundi_eid *eid)
00474 {
00475    struct dundi_peer *cur = NULL;
00476 
00477    if (!eid)
00478       eid = &empty_eid;
00479    
00480    AST_LIST_TRAVERSE(&peers, cur, list) {
00481       if (!dundi_eid_cmp(&cur->eid,eid))
00482          break;
00483    }
00484 
00485    return cur;
00486 }
00487 
00488 static void build_iv(unsigned char *iv)
00489 {
00490    /* XXX Would be nice to be more random XXX */
00491    unsigned int *fluffy;
00492    int x;
00493    fluffy = (unsigned int *)(iv);
00494    for (x=0;x<4;x++)
00495       fluffy[x] = ast_random();
00496 }
00497 
00498 struct dundi_query_state {
00499    dundi_eid *eids[DUNDI_MAX_STACK + 1]; 
00500    int directs[DUNDI_MAX_STACK + 1]; 
00501    dundi_eid reqeid;
00502    char called_context[AST_MAX_EXTENSION];
00503    char called_number[AST_MAX_EXTENSION];
00504    struct dundi_mapping *maps;
00505    int nummaps;
00506    int nocache;
00507    struct dundi_transaction *trans;
00508    void *chal;
00509    int challen;
00510    int ttl;
00511    char fluffy[0];
00512 };
00513 
00514 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)
00515 {
00516    struct ast_flags flags = {0};
00517    int x;
00518    if (!ast_strlen_zero(map->lcontext)) {
00519       if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
00520          ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
00521       if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
00522          ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
00523       if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
00524          ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
00525       if (ast_ignore_pattern(map->lcontext, called_number))
00526          ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
00527 
00528       /* Clearly we can't say 'don't ask' anymore if we found anything... */
00529       if (ast_test_flag(&flags, AST_FLAGS_ALL)) 
00530          ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
00531 
00532       if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
00533          /* Skip partial answers */
00534          ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
00535       }
00536       if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
00537          struct varshead headp;
00538          struct ast_var_t *newvariable;
00539          ast_set_flag(&flags, map->options & 0xffff);
00540          ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
00541          dr[anscnt].techint = map->tech;
00542          dr[anscnt].weight = map->weight;
00543          dr[anscnt].expiration = dundi_cache_time;
00544          ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
00545          dr[anscnt].eid = *us_eid;
00546          dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
00547          if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
00548             AST_LIST_HEAD_INIT_NOLOCK(&headp);
00549             newvariable = ast_var_assign("NUMBER", called_number);
00550             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00551             newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
00552             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00553             newvariable = ast_var_assign("SECRET", cursecret);
00554             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00555             newvariable = ast_var_assign("IPADDR", ipaddr);
00556             AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00557             pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
00558             while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
00559                ast_var_delete(newvariable);
00560          } else
00561             dr[anscnt].dest[0] = '\0';
00562          anscnt++;
00563       } else {
00564          /* No answers...  Find the fewest number of digits from the
00565             number for which we have no answer. */
00566          char tmp[AST_MAX_EXTENSION];
00567          for (x=0;x<AST_MAX_EXTENSION;x++) {
00568             tmp[x] = called_number[x];
00569             if (!tmp[x])
00570                break;
00571             if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
00572                /* Oops found something we can't match.  If this is longer
00573                   than the running hint, we have to consider it */
00574                if (strlen(tmp) > strlen(hmd->exten)) {
00575                   ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
00576                }
00577                break;
00578             }
00579          }
00580       }
00581    }
00582    return anscnt;
00583 }
00584 
00585 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
00586 
00587 static void *dundi_lookup_thread(void *data)
00588 {
00589    struct dundi_query_state *st = data;
00590    struct dundi_result dr[MAX_RESULTS];
00591    struct dundi_ie_data ied;
00592    struct dundi_hint_metadata hmd;
00593    char eid_str[20];
00594    int res, x;
00595    int ouranswers=0;
00596    int max = 999999;
00597    int expiration = dundi_cache_time;
00598 
00599    if (option_debug)
00600       ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00601          st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00602    memset(&ied, 0, sizeof(ied));
00603    memset(&dr, 0, sizeof(dr));
00604    memset(&hmd, 0, sizeof(hmd));
00605    /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
00606    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00607    for (x=0;x<st->nummaps;x++)
00608       ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
00609    if (ouranswers < 0)
00610       ouranswers = 0;
00611    for (x=0;x<ouranswers;x++) {
00612       if (dr[x].weight < max)
00613          max = dr[x].weight;
00614    }
00615       
00616    if (max) {
00617       /* If we do not have a canonical result, keep looking */
00618       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);
00619       if (res > 0) {
00620          /* Append answer in result */
00621          ouranswers += res;
00622       } else {
00623          if ((res < -1) && (!ouranswers))
00624             dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
00625       }
00626    }
00627    AST_LIST_LOCK(&peers);
00628    /* Truncate if "don't ask" isn't present */
00629    if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00630       hmd.exten[0] = '\0';
00631    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00632       if (option_debug)
00633          ast_log(LOG_DEBUG, "Our transaction went away!\n");
00634       st->trans->thread = 0;
00635       destroy_trans(st->trans, 0);
00636    } else {
00637       for (x=0;x<ouranswers;x++) {
00638          /* Add answers */
00639          if (dr[x].expiration && (expiration > dr[x].expiration))
00640             expiration = dr[x].expiration;
00641          dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
00642       }
00643       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00644       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
00645       dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
00646       st->trans->thread = 0;
00647    }
00648    AST_LIST_UNLOCK(&peers);
00649    free(st);
00650    return NULL;   
00651 }
00652 
00653 static void *dundi_precache_thread(void *data)
00654 {
00655    struct dundi_query_state *st = data;
00656    struct dundi_ie_data ied;
00657    struct dundi_hint_metadata hmd;
00658    char eid_str[20];
00659 
00660    if (option_debug)
00661       ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00662          st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00663    memset(&ied, 0, sizeof(ied));
00664 
00665    /* Now produce precache */
00666    dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
00667 
00668    AST_LIST_LOCK(&peers);
00669    /* Truncate if "don't ask" isn't present */
00670    if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00671       hmd.exten[0] = '\0';
00672    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00673       if (option_debug)
00674          ast_log(LOG_DEBUG, "Our transaction went away!\n");
00675       st->trans->thread = 0;
00676       destroy_trans(st->trans, 0);
00677    } else {
00678       dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
00679       st->trans->thread = 0;
00680    }
00681    AST_LIST_UNLOCK(&peers);
00682    free(st);
00683    return NULL;   
00684 }
00685 
00686 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[]);
00687 
00688 static void *dundi_query_thread(void *data)
00689 {
00690    struct dundi_query_state *st = data;
00691    struct dundi_entity_info dei;
00692    struct dundi_ie_data ied;
00693    struct dundi_hint_metadata hmd;
00694    char eid_str[20];
00695    int res;
00696 
00697    if (option_debug)
00698       ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
00699          st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
00700    memset(&ied, 0, sizeof(ied));
00701    memset(&dei, 0, sizeof(dei));
00702    memset(&hmd, 0, sizeof(hmd));
00703    if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
00704       /* Ooh, it's us! */
00705       if (option_debug)
00706          ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
00707       ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
00708       ast_copy_string(dei.org, org, sizeof(dei.org));
00709       ast_copy_string(dei.locality, locality, sizeof(dei.locality));
00710       ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
00711       ast_copy_string(dei.country, country, sizeof(dei.country));
00712       ast_copy_string(dei.email, email, sizeof(dei.email));
00713       ast_copy_string(dei.phone, phone, sizeof(dei.phone));
00714       res = 1;
00715    } else {
00716       /* If we do not have a canonical result, keep looking */
00717       res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
00718    }
00719    AST_LIST_LOCK(&peers);
00720    if (ast_test_flag(st->trans, FLAG_DEAD)) {
00721       if (option_debug)
00722          ast_log(LOG_DEBUG, "Our transaction went away!\n");
00723       st->trans->thread = 0;
00724       destroy_trans(st->trans, 0);
00725    } else {
00726       if (res) {
00727          dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
00728          dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
00729          dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
00730          dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
00731          dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
00732          dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
00733          dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
00734          if (!ast_strlen_zero(dei.ipaddr))
00735             dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
00736       }
00737       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00738       dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00739       st->trans->thread = 0;
00740    }
00741    AST_LIST_UNLOCK(&peers);
00742    free(st);
00743    return NULL;   
00744 }
00745 
00746 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00747 {
00748    struct dundi_query_state *st;
00749    int totallen;
00750    int x;
00751    int skipfirst=0;
00752    struct dundi_ie_data ied;
00753    char eid_str[20];
00754    char *s;
00755    pthread_t lookupthread;
00756    pthread_attr_t attr;
00757    if (ies->eidcount > 1) {
00758       /* Since it is a requirement that the first EID is the authenticating host
00759          and the last EID is the root, it is permissible that the first and last EID
00760          could be the same.  In that case, we should go ahead copy only the "root" section
00761          since we will not need it for authentication. */
00762       if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00763          skipfirst = 1;
00764    }
00765    totallen = sizeof(struct dundi_query_state);
00766    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00767    st = ast_calloc(1, totallen);
00768    if (st) {
00769       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00770       memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
00771       st->trans = trans;
00772       st->ttl = ies->ttl - 1;
00773       if (st->ttl < 0)
00774          st->ttl = 0;
00775       s = st->fluffy;
00776       for (x=skipfirst;ies->eids[x];x++) {
00777          st->eids[x-skipfirst] = (dundi_eid *)s;
00778          *st->eids[x-skipfirst] = *ies->eids[x];
00779          s += sizeof(dundi_eid);
00780       }
00781       if (option_debug)
00782          ast_log(LOG_DEBUG, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
00783       pthread_attr_init(&attr);
00784       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00785       trans->thread = 1;
00786       if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
00787          trans->thread = 0;
00788          ast_log(LOG_WARNING, "Unable to create thread!\n");
00789          free(st);
00790          memset(&ied, 0, sizeof(ied));
00791          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
00792          dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00793          return -1;
00794       }
00795    } else {
00796       ast_log(LOG_WARNING, "Out of memory!\n");
00797       memset(&ied, 0, sizeof(ied));
00798       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
00799       dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00800       return -1;
00801    }
00802    return 0;
00803 }
00804 
00805 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
00806 {
00807    int unaffected;
00808    char key1[256];
00809    char key2[256];
00810    char eidpeer_str[20];
00811    char eidroot_str[20];
00812    char data[80];
00813    time_t timeout;
00814 
00815    if (expiration < 0)
00816       expiration = dundi_cache_time;
00817 
00818    /* Only cache hint if "don't ask" is there... */
00819    if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))   
00820       return 0;
00821 
00822    unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
00823 
00824    dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00825    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00826    snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
00827    snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
00828 
00829    time(&timeout);
00830    timeout += expiration;
00831    snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00832    
00833    ast_db_put("dundi/cache", key1, data);
00834    if (option_debug)
00835       ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
00836    ast_db_put("dundi/cache", key2, data);
00837    if (option_debug)
00838       ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
00839    return 0;
00840 }
00841 
00842 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
00843 {
00844    int x;
00845    char key1[256];
00846    char key2[256];
00847    char data[1024];
00848    char eidpeer_str[20];
00849    char eidroot_str[20];
00850    time_t timeout;
00851 
00852    if (expiration < 1)  
00853       expiration = dundi_cache_time;
00854 
00855    /* Keep pushes a little longer, cut pulls a little short */
00856    if (push)
00857       expiration += 10;
00858    else
00859       expiration -= 10;
00860    if (expiration < 1)
00861       expiration = 1;
00862    dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00863    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00864    snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
00865    snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
00866    /* Build request string */
00867    time(&timeout);
00868    timeout += expiration;
00869    snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00870    for (x=start;x<req->respcount;x++) {
00871       /* Skip anything with an illegal pipe in it */
00872       if (strchr(req->dr[x].dest, '|'))
00873          continue;
00874       snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 
00875          req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 
00876          dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
00877    }
00878    ast_db_put("dundi/cache", key1, data);
00879    ast_db_put("dundi/cache", key2, data);
00880    return 0;
00881 }
00882 
00883 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00884 {
00885    struct dundi_query_state *st;
00886    int totallen;
00887    int x,z;
00888    struct dundi_ie_data ied;
00889    char *s;
00890    struct dundi_result dr2[MAX_RESULTS];
00891    struct dundi_request dr;
00892    struct dundi_hint_metadata hmd;
00893 
00894    struct dundi_mapping *cur;
00895    int mapcount;
00896    int skipfirst = 0;
00897    
00898    pthread_t lookupthread;
00899    pthread_attr_t attr;
00900 
00901    memset(&dr2, 0, sizeof(dr2));
00902    memset(&dr, 0, sizeof(dr));
00903    memset(&hmd, 0, sizeof(hmd));
00904    
00905    /* Forge request structure to hold answers for cache */
00906    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00907    dr.dr = dr2;
00908    dr.maxcount = MAX_RESULTS;
00909    dr.expiration = dundi_cache_time;
00910    dr.hmd = &hmd;
00911    dr.pfds[0] = dr.pfds[1] = -1;
00912    trans->parent = &dr;
00913    ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
00914    ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
00915    
00916    for (x=0;x<ies->anscount;x++) {
00917       if (trans->parent->respcount < trans->parent->maxcount) {
00918          /* Make sure it's not already there */
00919          for (z=0;z<trans->parent->respcount;z++) {
00920             if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
00921                 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data)) 
00922                   break;
00923          }
00924          if (z == trans->parent->respcount) {
00925             /* Copy into parent responses */
00926             trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
00927             trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
00928             trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
00929             trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
00930             if (ies->expiration > 0)
00931                trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
00932             else
00933                trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
00934             dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
00935                sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
00936                &ies->answers[x]->eid);
00937             ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
00938                sizeof(trans->parent->dr[trans->parent->respcount].dest));
00939                ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
00940                sizeof(trans->parent->dr[trans->parent->respcount].tech));
00941             trans->parent->respcount++;
00942             ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);   
00943          } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
00944             /* Update weight if appropriate */
00945             trans->parent->dr[z].weight = ies->answers[x]->weight;
00946          }
00947       } else
00948          ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
00949             trans->parent->number, trans->parent->dcontext);
00950 
00951    }
00952    /* Save all the results (if any) we had.  Even if no results, still cache lookup. */
00953    cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
00954    if (ies->hint)
00955       cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
00956 
00957    totallen = sizeof(struct dundi_query_state);
00958    /* Count matching map entries */
00959    mapcount = 0;
00960    AST_LIST_TRAVERSE(&mappings, cur, list) {
00961       if (!strcasecmp(cur->dcontext, ccontext))
00962          mapcount++;
00963    }
00964    
00965    /* If no maps, return -1 immediately */
00966    if (!mapcount)
00967       return -1;
00968 
00969    if (ies->eidcount > 1) {
00970       /* Since it is a requirement that the first EID is the authenticating host
00971          and the last EID is the root, it is permissible that the first and last EID
00972          could be the same.  In that case, we should go ahead copy only the "root" section
00973          since we will not need it for authentication. */
00974       if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00975          skipfirst = 1;
00976    }
00977 
00978    /* Prepare to run a query and then propagate that as necessary */
00979    totallen += mapcount * sizeof(struct dundi_mapping);
00980    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00981    st = ast_calloc(1, totallen);
00982    if (st) {
00983       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00984       ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
00985       st->trans = trans;
00986       st->ttl = ies->ttl - 1;
00987       st->nocache = ies->cbypass;
00988       if (st->ttl < 0)
00989          st->ttl = 0;
00990       s = st->fluffy;
00991       for (x=skipfirst;ies->eids[x];x++) {
00992          st->eids[x-skipfirst] = (dundi_eid *)s;
00993          *st->eids[x-skipfirst] = *ies->eids[x];
00994          st->directs[x-skipfirst] = ies->eid_direct[x];
00995          s += sizeof(dundi_eid);
00996       }
00997       /* Append mappings */
00998       x = 0;
00999       st->maps = (struct dundi_mapping *)s;
01000       AST_LIST_TRAVERSE(&mappings, cur, list) {
01001          if (!strcasecmp(cur->dcontext, ccontext)) {
01002             if (x < mapcount) {
01003                st->maps[x] = *cur;
01004                st->maps[x].list.next = NULL;
01005                x++;
01006             }
01007          }
01008       }
01009       st->nummaps = mapcount;
01010       if (option_debug)
01011          ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
01012       pthread_attr_init(&attr);
01013       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01014       trans->thread = 1;
01015       if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
01016          trans->thread = 0;
01017          ast_log(LOG_WARNING, "Unable to create thread!\n");
01018          free(st);
01019          memset(&ied, 0, sizeof(ied));
01020          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01021          dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01022          return -1;
01023       }
01024    } else {
01025       ast_log(LOG_WARNING, "Out of memory!\n");
01026       memset(&ied, 0, sizeof(ied));
01027       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01028       dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01029       return -1;
01030    }
01031    return 0;
01032 }
01033 
01034 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
01035 {
01036    struct dundi_query_state *st;
01037    int totallen;
01038    int x;
01039    struct dundi_ie_data ied;
01040    char *s;
01041    struct dundi_mapping *cur;
01042    int mapcount = 0;
01043    int skipfirst = 0;
01044    
01045    pthread_t lookupthread;
01046    pthread_attr_t attr;
01047    totallen = sizeof(struct dundi_query_state);
01048    /* Count matching map entries */
01049    AST_LIST_TRAVERSE(&mappings, cur, list) {
01050       if (!strcasecmp(cur->dcontext, ccontext))
01051          mapcount++;
01052    }
01053    /* If no maps, return -1 immediately */
01054    if (!mapcount)
01055       return -1;
01056 
01057    if (ies->eidcount > 1) {
01058       /* Since it is a requirement that the first EID is the authenticating host
01059          and the last EID is the root, it is permissible that the first and last EID
01060          could be the same.  In that case, we should go ahead copy only the "root" section
01061          since we will not need it for authentication. */
01062       if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
01063          skipfirst = 1;
01064    }
01065 
01066    totallen += mapcount * sizeof(struct dundi_mapping);
01067    totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
01068    st = ast_calloc(1, totallen);
01069    if (st) {
01070       ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
01071       ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
01072       st->trans = trans;
01073       st->ttl = ies->ttl - 1;
01074       st->nocache = ies->cbypass;
01075       if (st->ttl < 0)
01076          st->ttl = 0;
01077       s = st->fluffy;
01078       for (x=skipfirst;ies->eids[x];x++) {
01079          st->eids[x-skipfirst] = (dundi_eid *)s;
01080          *st->eids[x-skipfirst] = *ies->eids[x];
01081          st->directs[x-skipfirst] = ies->eid_direct[x];
01082          s += sizeof(dundi_eid);
01083       }
01084       /* Append mappings */
01085       x = 0;
01086       st->maps = (struct dundi_mapping *)s;
01087       AST_LIST_TRAVERSE(&mappings, cur, list) {
01088          if (!strcasecmp(cur->dcontext, ccontext)) {
01089             if (x < mapcount) {
01090                st->maps[x] = *cur;
01091                st->maps[x].list.next = NULL;
01092                x++;
01093             }
01094          }
01095       }
01096       st->nummaps = mapcount;
01097       if (option_debug)
01098          ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
01099       pthread_attr_init(&attr);
01100       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01101       trans->thread = 1;
01102       if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
01103          trans->thread = 0;
01104          ast_log(LOG_WARNING, "Unable to create thread!\n");
01105          free(st);
01106          memset(&ied, 0, sizeof(ied));
01107          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01108          dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01109          return -1;
01110       }
01111    } else {
01112       ast_log(LOG_WARNING, "Out of memory!\n");
01113       memset(&ied, 0, sizeof(ied));
01114       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01115       dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01116       return -1;
01117    }
01118    return 0;
01119 }
01120 
01121 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
01122 {
01123    char data[1024];
01124    char *ptr, *term, *src;
01125    int tech;
01126    struct ast_flags flags;
01127    int weight;
01128    int length;
01129    int z;
01130    char fs[256];
01131 
01132    /* Build request string */
01133    if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
01134       time_t timeout;
01135       ptr = data;
01136       if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
01137          int expiration = timeout - now;
01138          if (expiration > 0) {
01139             if (option_debug)
01140                ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
01141             ptr += length + 1;
01142             while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
01143                ptr += length;
01144                term = strchr(ptr, '|');
01145                if (term) {
01146                   *term = '\0';
01147                   src = strrchr(ptr, '/');
01148                   if (src) {
01149                      *src = '\0';
01150                      src++;
01151                   } else
01152                      src = "";
01153                   if (option_debug)
01154                      ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n", 
01155                         tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
01156                   /* Make sure it's not already there */
01157                   for (z=0;z<req->respcount;z++) {
01158                      if ((req->dr[z].techint == tech) &&
01159                          !strcmp(req->dr[z].dest, ptr)) 
01160                            break;
01161                   }
01162                   if (z == req->respcount) {
01163                      /* Copy into parent responses */
01164                      ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);   
01165                      req->dr[req->respcount].weight = weight;
01166                      req->dr[req->respcount].techint = tech;
01167                      req->dr[req->respcount].expiration = expiration;
01168                      dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
01169                      dundi_eid_to_str(req->dr[req->respcount].eid_str, 
01170                         sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
01171                      ast_copy_string(req->dr[req->respcount].dest, ptr,
01172                         sizeof(req->dr[req->respcount].dest));
01173                      ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
01174                         sizeof(req->dr[req->respcount].tech));
01175                      req->respcount++;
01176                      ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK); 
01177                   } else if (req->dr[z].weight > weight)
01178                      req->dr[z].weight = weight;
01179                   ptr = term + 1;
01180                }
01181             }
01182             /* We found *something* cached */
01183             if (expiration < *lowexpiration)
01184                *lowexpiration = expiration;
01185             return 1;
01186          } else 
01187             ast_db_del("dundi/cache", key);
01188       } else 
01189          ast_db_del("dundi/cache", key);
01190    }
01191       
01192    return 0;
01193 }
01194 
01195 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
01196 {
01197    char key[256];
01198    char eid_str[20];
01199    char eidroot_str[20];
01200    time_t now;
01201    int res=0;
01202    int res2=0;
01203    char eid_str_full[20];
01204    char tmp[256]="";
01205    int x;
01206 
01207    time(&now);
01208    dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
01209    dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
01210    dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
01211    snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
01212    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01213    snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
01214    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01215    snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
01216    res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01217    x = 0;
01218    if (!req->respcount) {
01219       while(!res2) {
01220          /* Look and see if we have a hint that would preclude us from looking at this
01221             peer for this number. */
01222          if (!(tmp[x] = req->number[x])) 
01223             break;
01224          x++;
01225          /* Check for hints */
01226          snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
01227          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01228          snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
01229          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01230          snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
01231          res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01232          if (res2) {
01233             if (strlen(tmp) > strlen(req->hmd->exten)) {
01234                /* Update meta data if appropriate */
01235                ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
01236             }
01237          }
01238       }
01239       res |= res2;
01240    }
01241 
01242    return res;
01243 }
01244 
01245 static void qualify_peer(struct dundi_peer *peer, int schedonly);
01246 
01247 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
01248 {
01249    if (!trans->addr.sin_addr.s_addr)
01250       memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
01251    trans->us_eid = p->us_eid;
01252    trans->them_eid = p->eid;
01253    /* Enable encryption if appropriate */
01254    if (!ast_strlen_zero(p->inkey))
01255       ast_set_flag(trans, FLAG_ENCRYPT);  
01256    if (p->maxms) {
01257       trans->autokilltimeout = p->maxms;
01258       trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01259       if (p->lastms > 1) {
01260          trans->retranstimer = p->lastms * 2;
01261          /* Keep it from being silly */
01262          if (trans->retranstimer < 150)
01263             trans->retranstimer = 150;
01264       }
01265       if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
01266          trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01267    } else
01268       trans->autokilltimeout = global_autokilltimeout;
01269 }
01270 
01271 /*! \note Called with the peers list already locked */
01272 static int do_register_expire(void *data)
01273 {
01274    struct dundi_peer *peer = data;
01275    char eid_str[20];
01276    if (option_debug)
01277       ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01278    peer->registerexpire = -1;
01279    peer->lastms = 0;
01280    memset(&peer->addr, 0, sizeof(peer->addr));
01281    return 0;
01282 }
01283 
01284 static int update_key(struct dundi_peer *peer)
01285 {
01286    unsigned char key[16];
01287    struct ast_key *ekey, *skey;
01288    char eid_str[20];
01289    int res;
01290    if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
01291       build_iv(key);
01292       aes_encrypt_key128(key, &peer->us_ecx);
01293       aes_decrypt_key128(key, &peer->us_dcx);
01294       ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01295       if (!ekey) {
01296          ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
01297             peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01298          return -1;
01299       }
01300       skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01301       if (!skey) {
01302          ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
01303             peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01304          return -1;
01305       }
01306       if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
01307          ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
01308          return -1;
01309       }
01310       if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
01311          ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
01312          return -1;
01313       }
01314       peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
01315       peer->sentfullkey = 0;
01316       /* Looks good */
01317       time(&peer->keyexpire);
01318       peer->keyexpire += dundi_key_ttl;
01319    }
01320    return 0;
01321 }
01322 
01323 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx) 
01324 {
01325    unsigned char curblock[16];
01326    int x;
01327    memcpy(curblock, iv, sizeof(curblock));
01328    while(len > 0) {
01329       for (x=0;x<16;x++)
01330          curblock[x] ^= src[x];
01331       aes_encrypt(curblock, dst, ecx);
01332       memcpy(curblock, dst, sizeof(curblock)); 
01333       dst += 16;
01334       src += 16;
01335       len -= 16;
01336    }
01337    return 0;
01338 }
01339 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx) 
01340 {
01341    unsigned char lastblock[16];
01342    int x;
01343    memcpy(lastblock, iv, sizeof(lastblock));
01344    while(len > 0) {
01345       aes_decrypt(src, dst, dcx);
01346       for (x=0;x<16;x++)
01347          dst[x] ^= lastblock[x];
01348       memcpy(lastblock, src, sizeof(lastblock));
01349       dst += 16;
01350       src += 16;
01351       len -= 16;
01352    }
01353    return 0;
01354 }
01355 
01356 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)
01357 {
01358    int space = *dstlen;
01359    unsigned long bytes;
01360    struct dundi_hdr *h;
01361    unsigned char *decrypt_space;
01362    decrypt_space = alloca(srclen);
01363    if (!decrypt_space)
01364       return NULL;
01365    decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
01366    /* Setup header */
01367    h = (struct dundi_hdr *)dst;
01368    *h = *ohdr;
01369    bytes = space - 6;
01370    if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
01371       if (option_debug)
01372          ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
01373       return NULL;
01374    }
01375    /* Update length */
01376    *dstlen = bytes + 6;
01377    /* Return new header */
01378    return h;
01379 }
01380 
01381 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
01382 {
01383    unsigned char *compress_space;
01384    int len;
01385    int res;
01386    unsigned long bytes;
01387    struct dundi_ie_data ied;
01388    struct dundi_peer *peer;
01389    unsigned char iv[16];
01390    len = pack->datalen + pack->datalen / 100 + 42;
01391    compress_space = alloca(len);
01392    if (compress_space) {
01393       memset(compress_space, 0, len);
01394       /* We care about everthing save the first 6 bytes of header */
01395       bytes = len;
01396       res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
01397       if (res != Z_OK) {
01398          if (option_debug)
01399             ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
01400          return -1;
01401       }
01402       memset(&ied, 0, sizeof(ied));
01403       /* Say who we are */
01404       if (!pack->h->iseqno && !pack->h->oseqno) {
01405          /* Need the key in the first copy */
01406          if (!(peer = find_peer(&trans->them_eid))) 
01407             return -1;
01408          if (update_key(peer))
01409             return -1;
01410          if (!peer->sentfullkey)
01411             ast_set_flag(trans, FLAG_SENDFULLKEY); 
01412          /* Append key data */
01413          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01414          if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
01415             dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01416             dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01417          } else {
01418             dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
01419          }
01420          /* Setup contexts */
01421          trans->ecx = peer->us_ecx;
01422          trans->dcx = peer->us_dcx;
01423 
01424          /* We've sent the full key */
01425          peer->sentfullkey = 1;
01426       }
01427       /* Build initialization vector */
01428       build_iv(iv);
01429       /* Add the field, rounded up to 16 bytes */
01430       dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
01431       /* Copy the data */
01432       if ((ied.pos + bytes) >= sizeof(ied.buf)) {
01433          ast_log(LOG_NOTICE, "Final packet too large!\n");
01434          return -1;
01435       }
01436       encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
01437       ied.pos += ((bytes + 15) / 16) * 16;
01438       /* Reconstruct header */
01439       pack->datalen = sizeof(struct dundi_hdr);
01440       pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
01441       pack->h->cmdflags = 0;
01442       memcpy(pack->h->ies, ied.buf, ied.pos);
01443       pack->datalen += ied.pos;
01444       return 0;
01445    }
01446    return -1;
01447 }
01448 
01449 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
01450 {
01451    unsigned char dst[128];
01452    int res;
01453    struct ast_key *key, *skey;
01454    char eid_str[20];
01455    if (option_debug)
01456       ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
01457    if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
01458       /* A match */
01459       return 1;
01460    } else if (!newkey || !newsig)
01461       return 0;
01462    if (!memcmp(peer->rxenckey, newkey, 128) &&
01463        !memcmp(peer->rxenckey + 128, newsig, 128)) {
01464       /* By definition, a match */
01465       return 1;
01466    }
01467    /* Decrypt key */
01468    key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01469    if (!key) {
01470       ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
01471          peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01472       return -1;
01473    }
01474 
01475    skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01476    if (!skey) {
01477       ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
01478          peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01479       return -1;
01480    }
01481 
01482    /* First check signature */
01483    res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
01484    if (res) 
01485       return 0;
01486 
01487    res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
01488    if (res != 16) {
01489       if (res >= 0)
01490          ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
01491       return 0;
01492    }
01493    /* Decrypted, passes signature */
01494    if (option_debug)
01495       ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
01496    memcpy(peer->rxenckey, newkey, 128);
01497    memcpy(peer->rxenckey + 128, newsig, 128);
01498    peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
01499    aes_decrypt_key128(dst, &peer->them_dcx);
01500    aes_encrypt_key128(dst, &peer->them_ecx);
01501    return 1;
01502 }
01503 
01504 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
01505 {
01506    /* Handle canonical command / response */
01507    int final = hdr->cmdresp & 0x80;
01508    int cmd = hdr->cmdresp & 0x7f;
01509    int x,y,z;
01510    int resp;
01511    int res;
01512    int authpass=0;
01513    unsigned char *bufcpy;
01514    struct dundi_ie_data ied;
01515    struct dundi_ies ies;
01516    struct dundi_peer *peer;
01517    char eid_str[20];
01518    char eid_str2[20];
01519    memset(&ied, 0, sizeof(ied));
01520    memset(&ies, 0, sizeof(ies));
01521    if (datalen) {
01522       bufcpy = alloca(datalen);
01523       if (!bufcpy)
01524          return -1;
01525       /* Make a copy for parsing */
01526       memcpy(bufcpy, hdr->ies, datalen);
01527       if (option_debug)
01528          ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
01529       if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
01530          ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
01531          return -1;
01532       }
01533    }
01534    switch(cmd) {
01535    case DUNDI_COMMAND_DPDISCOVER:
01536    case DUNDI_COMMAND_EIDQUERY:
01537    case DUNDI_COMMAND_PRECACHERQ:
01538       if (cmd == DUNDI_COMMAND_EIDQUERY)
01539          resp = DUNDI_COMMAND_EIDRESPONSE;
01540       else if (cmd == DUNDI_COMMAND_PRECACHERQ)
01541          resp = DUNDI_COMMAND_PRECACHERP;
01542       else
01543          resp = DUNDI_COMMAND_DPRESPONSE;
01544       /* A dialplan or entity discover -- qualify by highest level entity */
01545       peer = find_peer(ies.eids[0]);
01546       if (!peer) {
01547          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01548          dundi_send(trans, resp, 0, 1, &ied);
01549       } else {
01550          int hasauth = 0;
01551          trans->us_eid = peer->us_eid;
01552          if (strlen(peer->inkey)) {
01553             hasauth = encrypted;
01554          } else 
01555             hasauth = 1;
01556          if (hasauth) {
01557             /* Okay we're authentiated and all, now we check if they're authorized */
01558             if (!ies.called_context)
01559                ies.called_context = "e164";
01560             if (cmd == DUNDI_COMMAND_EIDQUERY) {
01561                res = dundi_answer_entity(trans, &ies, ies.called_context);
01562             } else {
01563                if (ast_strlen_zero(ies.called_number)) {
01564                   /* They're not permitted to access that context */
01565                   dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
01566                   dundi_send(trans, resp, 0, 1, &ied);
01567                } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) && 
01568                           (peer->model & DUNDI_MODEL_INBOUND) && 
01569                         has_permission(&peer->permit, ies.called_context)) {
01570                   res = dundi_answer_query(trans, &ies, ies.called_context);
01571                   if (res < 0) {
01572                      /* There is no such dundi context */
01573                      dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01574                      dundi_send(trans, resp, 0, 1, &ied);
01575                   }
01576                } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) && 
01577                           (peer->pcmodel & DUNDI_MODEL_INBOUND) && 
01578                         has_permission(&peer->include, ies.called_context)) {
01579                   res = dundi_prop_precache(trans, &ies, ies.called_context);
01580                   if (res < 0) {
01581                      /* There is no such dundi context */
01582                      dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01583                      dundi_send(trans, resp, 0, 1, &ied);
01584                   }
01585                } else {
01586                   /* They're not permitted to access that context */
01587                   dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
01588                   dundi_send(trans, resp, 0, 1, &ied);
01589                }
01590             }
01591          } else {
01592             /* They're not permitted to access that context */
01593             dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
01594             dundi_send(trans, resp, 0, 1, &ied);
01595          }
01596       }
01597       break;
01598    case DUNDI_COMMAND_REGREQ:
01599       /* A register request -- should only have one entity */
01600       peer = find_peer(ies.eids[0]);
01601       if (!peer || !peer->dynamic) {
01602          dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01603          dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
01604       } else {
01605          int hasauth = 0;
01606          trans->us_eid = peer->us_eid;
01607          if (!ast_strlen_zero(peer->inkey)) {
01608             hasauth = encrypted;
01609          } else
01610             hasauth = 1;
01611          if (hasauth) {
01612             int expire = default_expiration;
01613             char data[256];
01614             int needqual = 0;
01615             if (peer->registerexpire > -1)
01616                ast_sched_del(sched, peer->registerexpire);
01617             peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
01618             snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr), 
01619                ntohs(trans->addr.sin_port), expire);
01620             ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
01621             if (inaddrcmp(&peer->addr, &trans->addr)) {
01622                if (option_verbose > 2) {
01623                   ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", 
01624                      dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
01625                      ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
01626                }
01627                needqual = 1;
01628             }
01629                
01630             memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
01631             dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
01632             dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
01633             if (needqual)
01634                qualify_peer(peer, 1);
01635          }
01636       }
01637       break;
01638    case DUNDI_COMMAND_DPRESPONSE:
01639       /* A dialplan response, lets see what we got... */
01640       if (ies.cause < 1) {
01641          /* Success of some sort */
01642          if (option_debug)
01643             ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
01644          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01645             authpass = encrypted;
01646          } else 
01647             authpass = 1;
01648          if (authpass) {
01649             /* Pass back up answers */
01650             if (trans->parent && trans->parent->dr) {
01651                y = trans->parent->respcount;
01652                for (x=0;x<ies.anscount;x++) {
01653                   if (trans->parent->respcount < trans->parent->maxcount) {
01654                      /* Make sure it's not already there */
01655                      for (z=0;z<trans->parent->respcount;z++) {
01656                         if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
01657                             !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data)) 
01658                               break;
01659                      }
01660                      if (z == trans->parent->respcount) {
01661                         /* Copy into parent responses */
01662                         trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
01663                         trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
01664                         trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
01665                         trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
01666                         if (ies.expiration > 0)
01667                            trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
01668                         else
01669                            trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
01670                         dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
01671                            sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
01672                            &ies.answers[x]->eid);
01673                         ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
01674                            sizeof(trans->parent->dr[trans->parent->respcount].dest));
01675                         ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
01676                            sizeof(trans->parent->dr[trans->parent->respcount].tech));
01677                         trans->parent->respcount++;
01678                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01679                      } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
01680                         /* Update weight if appropriate */
01681                         trans->parent->dr[z].weight = ies.answers[x]->weight;
01682                      }
01683                   } else
01684                      ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
01685                         trans->parent->number, trans->parent->dcontext);
01686                }
01687                /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
01688                   the cache know if this request was unaffected by our entity list. */
01689                cache_save(&trans->them_eid, trans->parent, y, 
01690                      ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
01691                if (ies.hint) {
01692                   cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
01693                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01694                      ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01695                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) { 
01696                      if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
01697                         ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data, 
01698                            sizeof(trans->parent->hmd->exten));
01699                      }
01700                   } else {
01701                      ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01702                   }
01703                }
01704                if (ies.expiration > 0) {
01705                   if (trans->parent->expiration > ies.expiration) {
01706                      trans->parent->expiration = ies.expiration;
01707                   }
01708                }
01709             }
01710             /* Close connection if not final */
01711             if (!final) 
01712                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01713          }
01714          
01715       } else {
01716          /* Auth failure, check for data */
01717          if (!final) {
01718             /* Cancel if they didn't already */
01719             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01720          }
01721       }
01722       break;
01723    case DUNDI_COMMAND_EIDRESPONSE:
01724       /* A dialplan response, lets see what we got... */
01725       if (ies.cause < 1) {
01726          /* Success of some sort */
01727          if (option_debug)
01728             ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
01729          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01730             authpass = encrypted;
01731          } else 
01732             authpass = 1;
01733          if (authpass) {
01734             /* Pass back up answers */
01735             if (trans->parent && trans->parent->dei && ies.q_org) {
01736                if (!trans->parent->respcount) {
01737                   trans->parent->respcount++;
01738                   if (ies.q_dept)
01739                      ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
01740                   if (ies.q_org)
01741                      ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
01742                   if (ies.q_locality)
01743                      ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
01744                   if (ies.q_stateprov)
01745                      ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
01746                   if (ies.q_country)
01747                      ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
01748                   if (ies.q_email)
01749                      ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
01750                   if (ies.q_phone)
01751                      ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
01752                   if (ies.q_ipaddr)
01753                      ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
01754                   if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
01755                      /* If it's them, update our address */
01756                      ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
01757                   }
01758                }
01759                if (ies.hint) {
01760                   if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01761                      ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01762                }
01763             }
01764             /* Close connection if not final */
01765             if (!final) 
01766                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01767          }
01768          
01769       } else {
01770          /* Auth failure, check for data */
01771          if (!final) {
01772             /* Cancel if they didn't already */
01773             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01774          }
01775       }
01776       break;
01777    case DUNDI_COMMAND_REGRESPONSE:
01778       /* A dialplan response, lets see what we got... */
01779       if (ies.cause < 1) {
01780          int hasauth;
01781          /* Success of some sort */
01782          if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01783             hasauth = encrypted;
01784          } else 
01785             hasauth = 1;
01786          
01787          if (!hasauth) {
01788             ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
01789             if (!final) {
01790                dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
01791                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
01792             }
01793          } else {
01794             if (option_debug)
01795                ast_log(LOG_DEBUG, "Yay, we've registered as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
01796                         dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
01797             /* Close connection if not final */
01798             if (!final) 
01799                dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01800          }
01801       } else {
01802          /* Auth failure, cancel if they didn't for some reason */
01803          if (!final) {
01804             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01805          }
01806       }
01807       break;
01808    case DUNDI_COMMAND_INVALID:
01809    case DUNDI_COMMAND_NULL:
01810    case DUNDI_COMMAND_PRECACHERP:
01811       /* Do nothing special */
01812       if (!final) 
01813          dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01814       break;
01815    case DUNDI_COMMAND_ENCREJ:
01816       if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
01817          /* No really, it's over at this point */
01818          if (!final) 
01819             dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01820       } else {
01821          /* Send with full key */
01822          ast_set_flag(trans, FLAG_SENDFULLKEY);
01823          if (final) {
01824             /* Ooops, we got a final message, start by sending ACK... */
01825             dundi_ack(trans, hdr->cmdresp & 0x80);
01826             trans->aseqno = trans->iseqno;
01827             /* Now, we gotta create a new transaction */
01828             if (!reset_transaction(trans)) {
01829                /* Make sure handle_frame doesn't destroy us */
01830                hdr->cmdresp &= 0x7f;
01831                /* Parse the message we transmitted */
01832                memset(&ies, 0, sizeof(ies));
01833                dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
01834                /* Reconstruct outgoing encrypted packet */
01835                memset(&ied, 0, sizeof(ied));
01836                dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01837                dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01838                dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01839                if (ies.encblock) 
01840                   dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
01841                dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
01842                peer->sentfullkey = 1;
01843             }
01844          }
01845       }
01846       break;
01847    case DUNDI_COMMAND_ENCRYPT:
01848       if (!encrypted) {
01849          /* No nested encryption! */
01850          if ((trans->iseqno == 1) && !trans->oseqno) {
01851             if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || 
01852                ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || 
01853                (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
01854                if (!final) {
01855                   dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01856                }
01857                break;
01858             }
01859             apply_peer(trans, peer);
01860             /* Key passed, use new contexts for this session */
01861             trans->ecx = peer->them_ecx;
01862             trans->dcx = peer->them_dcx;
01863          }
01864          if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
01865             struct dundi_hdr *dhdr;
01866             unsigned char decoded[MAX_PACKET_SIZE];
01867             int ddatalen;
01868             ddatalen = sizeof(decoded);
01869             dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
01870             if (dhdr) {
01871                /* Handle decrypted response */
01872                if (dundidebug)
01873                   dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
01874                handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
01875                /* Carry back final flag */
01876                hdr->cmdresp |= dhdr->cmdresp & 0x80;
01877                break;
01878             } else {
01879                if (option_debug)
01880                   ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
01881             }
01882          }
01883       }
01884       if (!final) {
01885          /* Turn off encryption */
01886          ast_clear_flag(trans, FLAG_ENCRYPT);
01887          dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01888       }
01889       break;
01890    default:
01891       /* Send unknown command if we don't know it, with final flag IFF it's the
01892          first command in the dialog and only if we haven't recieved final notification */
01893       if (!final) {
01894          dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
01895          dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
01896       }
01897    }
01898    return 0;
01899 }
01900 
01901 static void destroy_packet(struct dundi_packet *pack, int needfree);
01902 static void destroy_packets(struct packetlist *p)
01903 {
01904    struct dundi_packet *pack;
01905    
01906    while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
01907       if (pack->retransid > -1)
01908          ast_sched_del(sched, pack->retransid);
01909       free(pack);
01910    }
01911 }
01912 
01913 
01914 static int ack_trans(struct dundi_transaction *trans, int iseqno)
01915 {
01916    struct dundi_packet *pack;
01917 
01918    /* Ack transmitted packet corresponding to iseqno */
01919    AST_LIST_TRAVERSE(&trans->packets, pack, list) {
01920       if ((pack->h->oseqno + 1) % 255 == iseqno) {
01921          destroy_packet(pack, 0);
01922          if (!AST_LIST_EMPTY(&trans->lasttrans)) {
01923             ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
01924             destroy_packets(&trans->lasttrans);
01925          }
01926          AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
01927          if (trans->autokillid > -1)
01928             ast_sched_del(sched, trans->autokillid);
01929          trans->autokillid = -1;
01930          return 1;
01931       }
01932    }
01933 
01934    return 0;
01935 }
01936 
01937 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
01938 {
01939    struct dundi_transaction *trans;
01940    trans = find_transaction(h, sin);
01941    if (!trans) {
01942       dundi_reject(h, sin);
01943       return 0;
01944    }
01945    /* Got a transaction, see where this header fits in */
01946    if (h->oseqno == trans->iseqno) {
01947       /* Just what we were looking for...  Anything but ack increments iseqno */
01948       if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
01949          /* If final, we're done */
01950          destroy_trans(trans, 0);
01951          return 0;
01952       }
01953       if (h->cmdresp != DUNDI_COMMAND_ACK) {
01954          trans->oiseqno = trans->iseqno;
01955          trans->iseqno++;
01956          handle_command_response(trans, h, datalen, 0);
01957       }
01958       if (trans->aseqno != trans->iseqno) {
01959          dundi_ack(trans, h->cmdresp & 0x80);
01960          trans->aseqno = trans->iseqno;
01961       }
01962       /* Delete any saved last transmissions */
01963       destroy_packets(&trans->lasttrans);
01964       if (h->cmdresp & 0x80) {
01965          /* Final -- destroy now */
01966          destroy_trans(trans, 0);
01967       }
01968    } else if (h->oseqno == trans->oiseqno) {
01969       /* Last incoming sequence number -- send ACK without processing */
01970       dundi_ack(trans, 0);
01971    } else {
01972       /* Out of window -- simply drop */
01973       if (option_debug)
01974          ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
01975    }
01976    return 0;
01977 }
01978 
01979 static int socket_read(int *id, int fd, short events, void *cbdata)
01980 {
01981    struct sockaddr_in sin;
01982    int res;
01983    struct dundi_hdr *h;
01984    char buf[MAX_PACKET_SIZE];
01985    socklen_t len = sizeof(sin);
01986    
01987    res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
01988    if (res < 0) {
01989       if (errno != ECONNREFUSED)
01990          ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
01991       return 1;
01992    }
01993    if (res < sizeof(struct dundi_hdr)) {
01994       ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
01995       return 1;
01996    }
01997    buf[res] = '\0';
01998    h = (struct dundi_hdr *) buf;
01999    if (dundidebug)
02000       dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
02001    AST_LIST_LOCK(&peers);
02002    handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
02003    AST_LIST_UNLOCK(&peers);
02004    return 1;
02005 }
02006 
02007 static void build_secret(char *secret, int seclen)
02008 {
02009    unsigned char tmp[16];
02010    char *s;
02011    build_iv(tmp);
02012    secret[0] = '\0';
02013    ast_base64encode(secret, tmp, sizeof(tmp), seclen);
02014    /* Eliminate potential bad characters */
02015    while((s = strchr(secret, ';'))) *s = '+';
02016    while((s = strchr(secret, '/'))) *s = '+';
02017    while((s = strchr(secret, ':'))) *s = '+';
02018    while((s = strchr(secret, '@'))) *s = '+';
02019 }
02020 
02021 
02022 static void save_secret(const char *newkey, const char *oldkey)
02023 {
02024    char tmp[256];
02025    if (oldkey)
02026       snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
02027    else
02028       snprintf(tmp, sizeof(tmp), "%s", newkey);
02029    rotatetime = time(NULL) + DUNDI_SECRET_TIME;
02030    ast_db_put(secretpath, "secret", tmp);
02031    snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
02032    ast_db_put(secretpath, "secretexpiry", tmp);
02033 }
02034 
02035 static void load_password(void)
02036 {
02037    char *current=NULL;
02038    char *last=NULL;
02039    char tmp[256];
02040    time_t expired;
02041    
02042    ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
02043    if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
02044       ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
02045       current = strchr(tmp, ';');
02046       if (!current)
02047          current = tmp;
02048       else {
02049          *current = '\0';
02050          current++;
02051       };
02052       if ((time(NULL) - expired) < 0) {
02053          if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
02054             expired = time(NULL) + DUNDI_SECRET_TIME;
02055       } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
02056          last = current;
02057          current = NULL;
02058       } else {
02059          last = NULL;
02060          current = NULL;
02061       }
02062    }
02063    if (current) {
02064       /* Current key is still valid, just setup rotatation properly */
02065       ast_copy_string(cursecret, current, sizeof(cursecret));
02066       rotatetime = expired;
02067    } else {
02068       /* Current key is out of date, rotate or eliminate all together */
02069       build_secret(cursecret, sizeof(cursecret));
02070       save_secret(cursecret, last);
02071    }
02072 }
02073 
02074 static void check_password(void)
02075 {
02076    char oldsecret[80];
02077    time_t now;
02078    
02079    time(&now); 
02080 #if 0
02081    printf("%ld/%ld\n", now, rotatetime);
02082 #endif
02083    if ((now - rotatetime) >= 0) {
02084       /* Time to rotate keys */
02085       ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
02086       build_secret(cursecret, sizeof(cursecret));
02087       save_secret(cursecret, oldsecret);
02088    }
02089 }
02090 
02091 static void *network_thread(void *ignore)
02092 {
02093    /* Our job is simple: Send queued messages, retrying if necessary.  Read frames 
02094       from the network, and queue them for delivery to the channels */
02095    int res;
02096    /* Establish I/O callback for socket read */
02097    ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
02098    for(;;) {
02099       res = ast_sched_wait(sched);
02100       if ((res > 1000) || (res < 0))
02101          res = 1000;
02102       res = ast_io_wait(io, res);
02103       if (res >= 0) {
02104          AST_LIST_LOCK(&peers);
02105          ast_sched_runq(sched);
02106          AST_LIST_UNLOCK(&peers);
02107       }
02108       check_password();
02109    }
02110    return NULL;
02111 }
02112 
02113 static void *process_precache(void *ign)
02114 {
02115    struct dundi_precache_queue *qe;
02116    time_t now;
02117    char context[256];
02118    char number[256];
02119    int run;
02120 
02121    for (;;) {
02122       time(&now);
02123       run = 0;
02124       AST_LIST_LOCK(&pcq);
02125       if ((qe = AST_LIST_FIRST(&pcq))) {
02126          if (!qe->expiration) {
02127             /* Gone...  Remove... */
02128             AST_LIST_REMOVE_HEAD(&pcq, list);
02129             free(qe);
02130          } else if (qe->expiration < now) {
02131             /* Process this entry */
02132             qe->expiration = 0;
02133             ast_copy_string(context, qe->context, sizeof(context));
02134             ast_copy_string(number, qe->number, sizeof(number));
02135             run = 1;
02136          }
02137       }
02138       AST_LIST_UNLOCK(&pcq);
02139       if (run) {
02140          dundi_precache(context, number);
02141       } else
02142          sleep(1);
02143    }
02144 
02145    return NULL;
02146 }
02147 
02148 static int start_network_thread(void)
02149 {
02150    ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
02151    ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
02152    return 0;
02153 }
02154 
02155 static int dundi_do_debug(int fd, int argc, char *argv[])
02156 {
02157    if (argc != 2)
02158       return RESULT_SHOWUSAGE;
02159    dundidebug = 1;
02160    ast_cli(fd, "DUNDi Debugging Enabled\n");
02161    return RESULT_SUCCESS;
02162 }
02163 
02164 static int dundi_do_store_history(int fd, int argc, char *argv[])
02165 {
02166    if (argc != 3)
02167       return RESULT_SHOWUSAGE;
02168    global_storehistory = 1;
02169    ast_cli(fd, "DUNDi History Storage Enabled\n");
02170    return RESULT_SUCCESS;
02171 }
02172 
02173 static int dundi_flush(int fd, int argc, char *argv[])
02174 {
02175    int stats = 0;
02176    if ((argc < 2) || (argc > 3))
02177       return RESULT_SHOWUSAGE;
02178    if (argc > 2) {
02179       if (!strcasecmp(argv[2], "stats"))
02180          stats = 1;
02181       else
02182          return RESULT_SHOWUSAGE;
02183    }
02184    if (stats) {
02185       /* Flush statistics */
02186       struct dundi_peer *p;
02187       int x;
02188       AST_LIST_LOCK(&peers);
02189       AST_LIST_TRAVERSE(&peers, p, list) {
02190          for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02191             if (p->lookups[x])
02192                free(p->lookups[x]);
02193             p->lookups[x] = NULL;
02194             p->lookuptimes[x] = 0;
02195          }
02196          p->avgms = 0;
02197       }
02198       AST_LIST_UNLOCK(&peers);
02199    } else {
02200       ast_db_deltree("dundi/cache", NULL);
02201       ast_cli(fd, "DUNDi Cache Flushed\n");
02202    }
02203    return RESULT_SUCCESS;
02204 }
02205 
02206 static int dundi_no_debug(int fd, int argc, char *argv[])
02207 {
02208    if (argc != 3)
02209       return RESULT_SHOWUSAGE;
02210    dundidebug = 0;
02211    ast_cli(fd, "DUNDi Debugging Disabled\n");
02212    return RESULT_SUCCESS;
02213 }
02214 
02215 static int dundi_no_store_history(int fd, int argc, char *argv[])
02216 {
02217    if (argc != 4)
02218       return RESULT_SHOWUSAGE;
02219    global_storehistory = 0;
02220    ast_cli(fd, "DUNDi History Storage Disabled\n");
02221    return RESULT_SUCCESS;
02222 }
02223 
02224 static char *model2str(int model)
02225 {
02226    switch(model) {
02227    case DUNDI_MODEL_INBOUND:
02228       return "Inbound";
02229    case DUNDI_MODEL_OUTBOUND:
02230       return "Outbound";
02231    case DUNDI_MODEL_SYMMETRIC:
02232       return "Symmetric";
02233    default:
02234       return "Unknown";
02235    }
02236 }
02237 
02238 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
02239 {
02240    int which=0, len;
02241    char *ret = NULL;
02242    struct dundi_peer *p;
02243    char eid_str[20];
02244 
02245    if (pos != rpos)
02246       return NULL;
02247    AST_LIST_LOCK(&peers);
02248    len = strlen(word);
02249    AST_LIST_TRAVERSE(&peers, p, list) {
02250       const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
02251       if (!strncasecmp(word, s, len) && ++which > state)
02252          ret = ast_strdup(s);
02253    }
02254    AST_LIST_UNLOCK(&peers);
02255    return ret;
02256 }
02257 
02258 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
02259 {
02260    return complete_peer_helper(line, word, pos, state, 3);
02261 }
02262 
02263 static int rescomp(const void *a, const void *b)
02264 {
02265    const struct dundi_result *resa, *resb;
02266    resa = a;
02267    resb = b;
02268    if (resa->weight < resb->weight)
02269       return -1;
02270    if (resa->weight > resb->weight)
02271       return 1;
02272    return 0;
02273 }
02274 
02275 static void sort_results(struct dundi_result *results, int count)
02276 {
02277    qsort(results, count, sizeof(results[0]), rescomp);
02278 }
02279 
02280 static int dundi_do_lookup(int fd, int argc, char *argv[])
02281 {
02282    int res;
02283    char tmp[256];
02284    char fs[80] = "";
02285    char *context;
02286    int x;
02287    int bypass = 0;
02288    struct dundi_result dr[MAX_RESULTS];
02289    struct timeval start;
02290    if ((argc < 3) || (argc > 4))
02291       return RESULT_SHOWUSAGE;
02292    if (argc > 3) {
02293       if (!strcasecmp(argv[3], "bypass"))
02294          bypass=1;
02295       else
02296          return RESULT_SHOWUSAGE;
02297    }
02298    ast_copy_string(tmp, argv[2], sizeof(tmp));
02299    context = strchr(tmp, '@');
02300    if (context) {
02301       *context = '\0';
02302       context++;
02303    }
02304    start = ast_tvnow();
02305    res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
02306    
02307    if (res < 0) 
02308       ast_cli(fd, "DUNDi lookup returned error.\n");
02309    else if (!res) 
02310       ast_cli(fd, "DUNDi lookup returned no results.\n");
02311    else
02312       sort_results(dr, res);
02313    for (x=0;x<res;x++) {
02314       ast_cli(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));
02315       ast_cli(fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
02316    }
02317    ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02318    return RESULT_SUCCESS;
02319 }
02320 
02321 static int dundi_do_precache(int fd, int argc, char *argv[])
02322 {
02323    int res;
02324    char tmp[256];
02325    char *context;
02326    struct timeval start;
02327    if ((argc < 3) || (argc > 3))
02328       return RESULT_SHOWUSAGE;
02329    ast_copy_string(tmp, argv[2], sizeof(tmp));
02330    context = strchr(tmp, '@');
02331    if (context) {
02332       *context = '\0';
02333       context++;
02334    }
02335    start = ast_tvnow();
02336    res = dundi_precache(context, tmp);
02337    
02338    if (res < 0) 
02339       ast_cli(fd, "DUNDi precache returned error.\n");
02340    else if (!res) 
02341       ast_cli(fd, "DUNDi precache returned no error.\n");
02342    ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02343    return RESULT_SUCCESS;
02344 }
02345 
02346 static int dundi_do_query(int fd, int argc, char *argv[])
02347 {
02348    int res;
02349    char tmp[256];
02350    char *context;
02351    dundi_eid eid;
02352    struct dundi_entity_info dei;
02353    if ((argc < 3) || (argc > 3))
02354       return RESULT_SHOWUSAGE;
02355    if (dundi_str_to_eid(&eid, argv[2])) {
02356       ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
02357       return RESULT_SHOWUSAGE;
02358    }
02359    ast_copy_string(tmp, argv[2], sizeof(tmp));
02360    context = strchr(tmp, '@');
02361    if (context) {
02362       *context = '\0';
02363       context++;
02364    }
02365    res = dundi_query_eid(&dei, context, eid);
02366    if (res < 0) 
02367       ast_cli(fd, "DUNDi Query EID returned error.\n");
02368    else if (!res) 
02369       ast_cli(fd, "DUNDi Query EID returned no results.\n");
02370    else {
02371       ast_cli(fd, "DUNDi Query EID succeeded:\n");
02372       ast_cli(fd, "Department:      %s\n", dei.orgunit);
02373       ast_cli(fd, "Organization:    %s\n", dei.org);
02374       ast_cli(fd, "City/Locality:   %s\n", dei.locality);
02375       ast_cli(fd, "State/Province:  %s\n", dei.stateprov);
02376       ast_cli(fd, "Country:         %s\n", dei.country);
02377       ast_cli(fd, "E-mail:          %s\n", dei.email);
02378       ast_cli(fd, "Phone:           %s\n", dei.phone);
02379       ast_cli(fd, "IP Address:      %s\n", dei.ipaddr);
02380    }
02381    return RESULT_SUCCESS;
02382 }
02383 
02384 static int dundi_show_peer(int fd, int argc, char *argv[])
02385 {
02386    struct dundi_peer *peer;
02387    struct permission *p;
02388    char *order;
02389    char eid_str[20];
02390    int x, cnt;
02391    
02392    if (argc != 4)
02393       return RESULT_SHOWUSAGE;
02394    AST_LIST_LOCK(&peers);
02395    AST_LIST_TRAVERSE(&peers, peer, list) {
02396       if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
02397          break;
02398    }
02399    if (peer) {
02400       switch(peer->order) {
02401       case 0:
02402          order = "Primary";
02403          break;
02404       case 1:
02405          order = "Secondary";
02406          break;
02407       case 2:
02408          order = "Tertiary";
02409          break;
02410       case 3:
02411          order = "Quartiary";
02412          break;
02413       default:
02414          order = "Unknown";
02415       }
02416       ast_cli(fd, "Peer:    %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02417       ast_cli(fd, "Model:   %s\n", model2str(peer->model));
02418       ast_cli(fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
02419       ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
02420       ast_cli(fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
02421       ast_cli(fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
02422       ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
02423       if (!AST_LIST_EMPTY(&peer->include))
02424          ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
02425       AST_LIST_TRAVERSE(&peer->include, p, list)
02426          ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
02427       if (!AST_LIST_EMPTY(&peer->permit))
02428          ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
02429       AST_LIST_TRAVERSE(&peer->permit, p, list)
02430          ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
02431       cnt = 0;
02432       for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02433          if (peer->lookups[x]) {
02434             if (!cnt)
02435                ast_cli(fd, "Last few query times:\n");
02436             ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
02437             cnt++;
02438          }
02439       }
02440       if (cnt)
02441          ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
02442    } else
02443       ast_cli(fd, "No such peer '%s'\n", argv[3]);
02444    AST_LIST_UNLOCK(&peers);
02445    return RESULT_SUCCESS;
02446 }
02447 
02448 static int dundi_show_peers(int fd, int argc, char *argv[])
02449 {
02450 #define FORMAT2 "%-20.20s %-15.15s     %-10.10s %-8.8s %-15.15s\n"
02451 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
02452    struct dundi_peer *peer;
02453    int registeredonly=0;
02454    char avgms[20];
02455    char eid_str[20];
02456    int online_peers = 0;
02457    int offline_peers = 0;
02458    int unmonitored_peers = 0;
02459    int total_peers = 0;
02460 
02461    if ((argc != 3) && (argc != 4) && (argc != 5))
02462       return RESULT_SHOWUSAGE;
02463    if ((argc == 4)) {
02464       if (!strcasecmp(argv[3], "registered")) {
02465          registeredonly = 1;
02466       } else
02467          return RESULT_SHOWUSAGE;
02468    }
02469    AST_LIST_LOCK(&peers);
02470    ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
02471    AST_LIST_TRAVERSE(&peers, peer, list) {
02472       char status[20];
02473       int print_line = -1;
02474       char srch[2000];
02475       total_peers++;
02476       if (registeredonly && !peer->addr.sin_addr.s_addr)
02477          continue;
02478       if (peer->maxms) {
02479          if (peer->lastms < 0) {
02480             strcpy(status, "UNREACHABLE");
02481             offline_peers++;
02482          }
02483          else if (peer->lastms > peer->maxms) {
02484             snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
02485             offline_peers++;
02486          }
02487          else if (peer->lastms) {
02488             snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
02489             online_peers++;
02490          }
02491          else {
02492             strcpy(status, "UNKNOWN");
02493             offline_peers++;
02494          }
02495       } else {
02496          strcpy(status, "Unmonitored");
02497          unmonitored_peers++;
02498       }
02499       if (peer->avgms) 
02500          snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
02501       else
02502          strcpy(avgms, "Unavail");
02503       snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
02504                peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
02505                peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
02506 
02507                 if (argc == 5) {
02508                   if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
02509                         print_line = -1;
02510                    } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
02511                         print_line = 1;
02512                    } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
02513                         print_line = -1;
02514                    } else {
02515                         print_line = 0;
02516                   }
02517                 }
02518       
02519         if (print_line) {
02520          ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
02521                peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
02522                peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
02523       }
02524    }
02525    ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
02526    AST_LIST_UNLOCK(&peers);
02527    return RESULT_SUCCESS;
02528 #undef FORMAT
02529 #undef FORMAT2
02530 }
02531 
02532 static int dundi_show_trans(int fd, int argc, char *argv[])
02533 {
02534 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
02535 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
02536    struct dundi_transaction *trans;
02537    if (argc != 3)
02538       return RESULT_SHOWUSAGE;
02539    AST_LIST_LOCK(&peers);
02540    ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
02541    AST_LIST_TRAVERSE(&alltrans, trans, all) {
02542       ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr), 
02543          ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
02544    }
02545    AST_LIST_UNLOCK(&peers);
02546    return RESULT_SUCCESS;
02547 #undef FORMAT
02548 #undef FORMAT2
02549 }
02550 
02551 static int dundi_show_entityid(int fd, int argc, char *argv[])
02552 {
02553    char eid_str[20];
02554    if (argc != 3)
02555       return RESULT_SHOWUSAGE;
02556    AST_LIST_LOCK(&peers);
02557    dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
02558    AST_LIST_UNLOCK(&peers);
02559    ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
02560    return RESULT_SUCCESS;
02561 }
02562 
02563 static int dundi_show_requests(int fd, int argc, char *argv[])
02564 {
02565 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
02566 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
02567    struct dundi_request *req;
02568    char eidstr[20];
02569    if (argc != 3)
02570       return RESULT_SHOWUSAGE;
02571    AST_LIST_LOCK(&peers);
02572    ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
02573    AST_LIST_TRAVERSE(&requests, req, list) {
02574       ast_cli(fd, FORMAT, req->number, req->dcontext,
02575          dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
02576    }
02577    AST_LIST_UNLOCK(&peers);
02578    return RESULT_SUCCESS;
02579 #undef FORMAT
02580 #undef FORMAT2
02581 }
02582 
02583 /* Grok-a-dial DUNDi */
02584 
02585 static int dundi_show_mappings(int fd, int argc, char *argv[])
02586 {
02587 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
02588 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
02589    struct dundi_mapping *map;
02590    char fs[256];
02591    if (argc != 3)
02592       return RESULT_SHOWUSAGE;
02593    AST_LIST_LOCK(&peers);
02594    ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
02595    AST_LIST_TRAVERSE(&mappings, map, list) {
02596       ast_cli(fd, FORMAT, map->dcontext, map->weight, 
02597          ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
02598          dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
02599    }
02600    AST_LIST_UNLOCK(&peers);
02601    return RESULT_SUCCESS;
02602 #undef FORMAT
02603 #undef FORMAT2
02604 }
02605 
02606 static int dundi_show_precache(int fd, int argc, char *argv[])
02607 {
02608 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
02609 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
02610    struct dundi_precache_queue *qe;
02611    int h,m,s;
02612    time_t now;
02613    
02614    if (argc != 3)
02615       return RESULT_SHOWUSAGE;
02616    time(&now);
02617    ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
02618    AST_LIST_LOCK(&pcq);
02619    AST_LIST_TRAVERSE(&pcq, qe, list) {
02620       s = qe->expiration - now;
02621       h = s / 3600;
02622       s = s % 3600;
02623       m = s / 60;
02624       s = s % 60;
02625       ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
02626    }
02627    AST_LIST_UNLOCK(&pcq);
02628    
02629    return RESULT_SUCCESS;
02630 #undef FORMAT
02631 #undef FORMAT2
02632 }
02633 
02634 static const char debug_usage[] = 
02635 "Usage: dundi debug\n"
02636 "       Enables dumping of DUNDi packets for debugging purposes\n";
02637 
02638 static const char no_debug_usage[] = 
02639 "Usage: dundi no debug\n"
02640 "       Disables dumping of DUNDi packets for debugging purposes\n";
02641 
02642 static const char store_history_usage[] = 
02643 "Usage: dundi store history\n"
02644 "       Enables storing of DUNDi requests and times for debugging\n"
02645 "purposes\n";
02646 
02647 static const char no_store_history_usage[] = 
02648 "Usage: dundi no store history\n"
02649 "       Disables storing of DUNDi requests and times for debugging\n"
02650 "purposes\n";
02651 
02652 static const char show_peers_usage[] = 
02653 "Usage: dundi show peers\n"
02654 "       Lists all known DUNDi peers.\n";
02655 
02656 static const char show_trans_usage[] = 
02657 "Usage: dundi show trans\n"
02658 "       Lists all known DUNDi transactions.\n";
02659 
02660 static const char show_mappings_usage[] = 
02661 "Usage: dundi show mappings\n"
02662 "       Lists all known DUNDi mappings.\n";
02663 
02664 static const char show_precache_usage[] = 
02665 "Usage: dundi show precache\n"
02666 "       Lists all known DUNDi scheduled precache updates.\n";
02667 
02668 static const char show_entityid_usage[] = 
02669 "Usage: dundi show entityid\n"
02670 "       Displays the global entityid for this host.\n";
02671 
02672 static const char show_peer_usage[] = 
02673 "Usage: dundi show peer [peer]\n"
02674 "       Provide a detailed description of a specifid DUNDi peer.\n";
02675 
02676 static const char show_requests_usage[] = 
02677 "Usage: dundi show requests\n"
02678 "       Lists all known pending DUNDi requests.\n";
02679 
02680 static const char lookup_usage[] =
02681 "Usage: dundi lookup <number>[@context] [bypass]\n"
02682 "       Lookup the given number within the given DUNDi context\n"
02683 "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
02684 "keyword is specified.\n";
02685 
02686 static const char precache_usage[] =
02687 "Usage: dundi precache <number>[@context]\n"
02688 "       Lookup the given number within the given DUNDi context\n"
02689 "(or e164 if none is specified) and precaches the results to any\n"
02690 "upstream DUNDi push servers.\n";
02691 
02692 static const char query_usage[] =
02693 "Usage: dundi query <entity>[@context]\n"
02694 "       Attempts to retrieve contact information for a specific\n"
02695 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
02696 "e164 if none is specified).\n";
02697 
02698 static const char flush_usage[] =
02699 "Usage: dundi flush [stats]\n"
02700 "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
02701 "'stats' is present, clears timer statistics instead of normal\n"
02702 "operation.\n";
02703 
02704 static struct ast_cli_entry cli_dundi[] = {
02705    { { "dundi", "debug", NULL },
02706    dundi_do_debug, "Enable DUNDi debugging",
02707    debug_usage },
02708 
02709    { { "dundi", "store", "history", NULL },
02710    dundi_do_store_history, "Enable DUNDi historic records",
02711    store_history_usage },
02712 
02713    { { "dundi", "no", "store", "history", NULL },
02714    dundi_no_store_history, "Disable DUNDi historic records",
02715    no_store_history_usage },
02716 
02717    { { "dundi", "flush", NULL },
02718    dundi_flush, "Flush DUNDi cache",
02719    flush_usage },
02720 
02721    { { "dundi", "no", "debug", NULL },
02722    dundi_no_debug, "Disable DUNDi debugging",
02723    no_debug_usage },
02724 
02725    { { "dundi", "show", "peers", NULL },
02726    dundi_show_peers, "Show defined DUNDi peers",
02727    show_peers_usage },
02728 
02729    { { "dundi", "show", "trans", NULL },
02730    dundi_show_trans, "Show active DUNDi transactions",
02731    show_trans_usage },
02732 
02733    { { "dundi", "show", "entityid", NULL },
02734    dundi_show_entityid, "Display Global Entity ID",
02735    show_entityid_usage },
02736 
02737    { { "dundi", "show", "mappings", NULL },
02738    dundi_show_mappings, "Show DUNDi mappings",
02739    show_mappings_usage },
02740 
02741    { { "dundi", "show", "precache", NULL },
02742    dundi_show_precache, "Show DUNDi precache",
02743    show_precache_usage },
02744 
02745    { { "dundi", "show", "requests", NULL },
02746    dundi_show_requests, "Show DUNDi requests",
02747    show_requests_usage },
02748 
02749    { { "dundi", "show", "peer", NULL },
02750    dundi_show_peer, "Show info on a specific DUNDi peer",
02751    show_peer_usage, complete_peer_4 },
02752 
02753    { { "dundi", "lookup", NULL },
02754    dundi_do_lookup, "Lookup a number in DUNDi",
02755    lookup_usage },
02756 
02757    { { "dundi", "precache", NULL },
02758    dundi_do_precache, "Precache a number in DUNDi",
02759    precache_usage },
02760 
02761    { { "dundi", "query", NULL },
02762    dundi_do_query, "Query a DUNDi EID",
02763    query_usage },
02764 };
02765 
02766 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
02767 {
02768    struct dundi_transaction *trans;
02769    int tid;
02770    
02771    /* Don't allow creation of transactions to non-registered peers */
02772    if (p && !p->addr.sin_addr.s_addr)
02773       return NULL;
02774    tid = get_trans_id();
02775    if (tid < 1)
02776       return NULL;
02777    if (!(trans = ast_calloc(1, sizeof(*trans))))
02778       return NULL;
02779 
02780    if (global_storehistory) {
02781       trans->start = ast_tvnow();
02782       ast_set_flag(trans, FLAG_STOREHIST);
02783    }
02784    trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
02785    trans->autokillid = -1;
02786    if (p) {
02787       apply_peer(trans, p);
02788       if (!p->sentfullkey)
02789          ast_set_flag(trans, FLAG_SENDFULLKEY);
02790    }
02791    trans->strans = tid;
02792    AST_LIST_INSERT_HEAD(&alltrans, trans, all);
02793    
02794    return trans;
02795 }
02796 
02797 static int dundi_xmit(struct dundi_packet *pack)
02798 {
02799    int res;
02800    if (dundidebug)
02801       dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
02802    res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
02803    if (res < 0) {
02804       ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
02805          ast_inet_ntoa(pack->parent->addr.sin_addr),
02806          ntohs(pack->parent->addr.sin_port), strerror(errno));
02807    }
02808    if (res > 0)
02809       res = 0;
02810    return res;
02811 }
02812 
02813 static void destroy_packet(struct dundi_packet *pack, int needfree)
02814 {
02815    if (pack->parent)
02816       AST_LIST_REMOVE(&pack->parent->packets, pack, list);
02817    if (pack->retransid > -1)
02818       ast_sched_del(sched, pack->retransid);
02819    if (needfree)
02820       free(pack);
02821    else
02822       pack->retransid = -1;
02823 }
02824 
02825 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
02826 {
02827    struct dundi_peer *peer;
02828    int ms;
02829    int x;
02830    int cnt;
02831    char eid_str[20];
02832    if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
02833       AST_LIST_TRAVERSE(&peers, peer, list) {
02834          if (peer->regtrans == trans)
02835             peer->regtrans = NULL;
02836          if (peer->qualtrans == trans) {
02837             if (fromtimeout) {
02838                if (peer->lastms > -1)
02839                   ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02840                peer->lastms = -1;
02841             } else {
02842                ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
02843                if (ms < 1)
02844                   ms = 1;
02845                if (ms < peer->maxms) {
02846                   if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
02847                      ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02848                } else if (peer->lastms < peer->maxms) {
02849                   ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
02850                }
02851                peer->lastms = ms;
02852             }
02853             peer->qualtrans = NULL;
02854          }
02855          if (ast_test_flag(trans, FLAG_STOREHIST)) {
02856             if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
02857                if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
02858                   peer->avgms = 0;
02859                   cnt = 0;
02860                   if (peer->lookups[DUNDI_TIMING_HISTORY-1])
02861                      free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
02862                   for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
02863                      peer->lookuptimes[x] = peer->lookuptimes[x-1];
02864                      peer->lookups[x] = peer->lookups[x-1];
02865                      if (peer->lookups[x]) {
02866                         peer->avgms += peer->lookuptimes[x];
02867                         cnt++;
02868                      }
02869                   }
02870                   peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
02871                   peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
02872                   if (peer->lookups[0]) {
02873                      sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
02874                      peer->avgms += peer->lookuptimes[0];
02875                      cnt++;
02876                   }
02877                   if (cnt)
02878                      peer->avgms /= cnt;
02879                }
02880             }
02881          }
02882       }
02883    }
02884    if (trans->parent) {
02885       /* Unlink from parent if appropriate */
02886       AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
02887       if (AST_LIST_EMPTY(&trans->parent->trans)) {
02888          /* Wake up sleeper */
02889          if (trans->parent->pfds[1] > -1) {
02890             write(trans->parent->pfds[1], "killa!", 6);
02891          }
02892       }
02893    }
02894    /* Unlink from all trans */
02895    AST_LIST_REMOVE(&alltrans, trans, all);
02896    destroy_packets(&trans->packets);
02897    destroy_packets(&trans->lasttrans);
02898    if (trans->autokillid > -1)
02899       ast_sched_del(sched, trans->autokillid);
02900    trans->autokillid = -1;
02901    if (trans->thread) {
02902       /* If used by a thread, mark as dead and be done */
02903       ast_set_flag(trans, FLAG_DEAD);
02904    } else
02905       free(trans);
02906 }
02907 
02908 static int dundi_rexmit(void *data)
02909 {
02910    struct dundi_packet *pack;
02911    int res;
02912    AST_LIST_LOCK(&peers);
02913    pack = data;
02914    if (pack->retrans < 1) {
02915       pack->retransid = -1;
02916       if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
02917          ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
02918             ast_inet_ntoa(pack->parent->addr.sin_addr), 
02919             ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
02920       destroy_trans(pack->parent, 1);
02921       res = 0;
02922    } else {
02923       /* Decrement retransmission, try again */
02924       pack->retrans--;
02925       dundi_xmit(pack);
02926       res = 1;
02927    }
02928    AST_LIST_UNLOCK(&peers);
02929    return res;
02930 }
02931 
02932 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
02933 {
02934    struct dundi_packet *pack;
02935    int res;
02936    int len;
02937    char eid_str[20];
02938    len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
02939    /* Reserve enough space for encryption */
02940    if (ast_test_flag(trans, FLAG_ENCRYPT))
02941       len += 384;
02942    pack = ast_calloc(1, len);
02943    if (pack) {
02944       pack->h = (struct dundi_hdr *)(pack->data);
02945       if (cmdresp != DUNDI_COMMAND_ACK) {
02946          pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
02947          pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
02948          AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
02949       }
02950       pack->parent = trans;
02951       pack->h->strans = htons(trans->strans);
02952       pack->h->dtrans = htons(trans->dtrans);
02953       pack->h->iseqno = trans->iseqno;
02954       pack->h->oseqno = trans->oseqno;
02955       pack->h->cmdresp = cmdresp;
02956       pack->datalen = sizeof(struct dundi_hdr);
02957       if (ied) {
02958          memcpy(pack->h->ies, ied->buf, ied->pos);
02959          pack->datalen += ied->pos;
02960       } 
02961       if (final) {
02962          pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
02963          ast_set_flag(trans, FLAG_FINAL);
02964       }
02965       pack->h->cmdflags = flags;
02966       if (cmdresp != DUNDI_COMMAND_ACK) {
02967          trans->oseqno++;
02968          trans->oseqno = trans->oseqno % 256;
02969       }
02970       trans->aseqno = trans->iseqno;
02971       /* If we have their public key, encrypt */
02972       if (ast_test_flag(trans, FLAG_ENCRYPT)) {
02973          switch(cmdresp) {
02974          case DUNDI_COMMAND_REGREQ:
02975          case DUNDI_COMMAND_REGRESPONSE:
02976          case DUNDI_COMMAND_DPDISCOVER:
02977          case DUNDI_COMMAND_DPRESPONSE:
02978          case DUNDI_COMMAND_EIDQUERY:
02979          case DUNDI_COMMAND_EIDRESPONSE:
02980          case DUNDI_COMMAND_PRECACHERQ:
02981          case DUNDI_COMMAND_PRECACHERP:
02982             if (dundidebug)
02983                dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
02984             res = dundi_encrypt(trans, pack);
02985             break;
02986          default:
02987             res = 0;
02988          }
02989       } else 
02990          res = 0;
02991       if (!res) 
02992          res = dundi_xmit(pack);
02993       if (res)
02994          ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
02995             
02996       if (cmdresp == DUNDI_COMMAND_ACK)
02997          free(pack);
02998       return res;
02999    }
03000    return -1;
03001 }
03002 
03003 static int do_autokill(void *data)
03004 {
03005    struct dundi_transaction *trans = data;
03006    char eid_str[20];
03007    ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 
03008       dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03009    trans->autokillid = -1;
03010    destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
03011    return 0;
03012 }
03013 
03014 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
03015 {
03016    struct dundi_peer *p;
03017    if (!dundi_eid_cmp(eid, us)) {
03018       dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03019       return;
03020    }
03021    AST_LIST_LOCK(&peers);
03022    AST_LIST_TRAVERSE(&peers, p, list) {
03023       if (!dundi_eid_cmp(&p->eid, eid)) {
03024          if (has_permission(&p->include, context))
03025             dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03026          else
03027             dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03028          break;
03029       }
03030    }
03031    if (!p)
03032       dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03033    AST_LIST_UNLOCK(&peers);
03034 }
03035 
03036 static int dundi_discover(struct dundi_transaction *trans)
03037 {
03038    struct dundi_ie_data ied;
03039    int x;
03040    if (!trans->parent) {
03041       ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03042       return -1;
03043    }
03044    memset(&ied, 0, sizeof(ied));
03045    dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03046    if (!dundi_eid_zero(&trans->us_eid))
03047       dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
03048    for (x=0;x<trans->eidcount;x++)
03049       dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
03050    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03051    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03052    dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03053    if (trans->parent->cbypass)
03054       dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
03055    if (trans->autokilltimeout)
03056       trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03057    return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
03058 }
03059 
03060 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
03061 {
03062    struct dundi_ie_data ied;
03063    int x, res;
03064    int max = 999999;
03065    int expiration = dundi_cache_time;
03066    int ouranswers=0;
03067    dundi_eid *avoid[1] = { NULL, };
03068    int direct[1] = { 0, };
03069    struct dundi_result dr[MAX_RESULTS];
03070    struct dundi_hint_metadata hmd;
03071    if (!trans->parent) {
03072       ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03073       return -1;
03074    }
03075    memset(&hmd, 0, sizeof(hmd));
03076    memset(&dr, 0, sizeof(dr));
03077    /* Look up the answers we're going to include */
03078    for (x=0;x<mapcount;x++)
03079       ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
03080    if (ouranswers < 0)
03081       ouranswers = 0;
03082    for (x=0;x<ouranswers;x++) {
03083       if (dr[x].weight < max)
03084          max = dr[x].weight;
03085    }
03086    if (max) {
03087       /* If we do not have a canonical result, keep looking */
03088       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);
03089       if (res > 0) {
03090          /* Append answer in result */
03091          ouranswers += res;
03092       }
03093    }
03094    
03095    if (ouranswers > 0) {
03096       *foundanswers += ouranswers;
03097       memset(&ied, 0, sizeof(ied));
03098       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03099       if (!dundi_eid_zero(&trans->us_eid))
03100          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03101       for (x=0;x<trans->eidcount;x++)
03102          dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03103       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03104       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03105       dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03106       for (x=0;x<ouranswers;x++) {
03107          /* Add answers */
03108          if (dr[x].expiration && (expiration > dr[x].expiration))
03109             expiration = dr[x].expiration;
03110          dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
03111       }
03112       dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
03113       dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
03114       if (trans->autokilltimeout)
03115          trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03116       if (expiration < *minexp)
03117          *minexp = expiration;
03118       return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
03119    } else {
03120       /* Oops, nothing to send... */
03121       destroy_trans(trans, 0);
03122       return 0;
03123    }
03124 }
03125 
03126 static int dundi_query(struct dundi_transaction *trans)
03127 {
03128    struct dundi_ie_data ied;
03129    int x;
03130    if (!trans->parent) {
03131       ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
03132       return -1;
03133    }
03134    memset(&ied, 0, sizeof(ied));
03135    dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03136    if (!dundi_eid_zero(&trans->us_eid))
03137       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03138    for (x=0;x<trans->eidcount;x++)
03139       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03140    dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
03141    dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03142    dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03143    if (trans->autokilltimeout)
03144       trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03145    return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
03146 }
03147 
03148 static int discover_transactions(struct dundi_request *dr)
03149 {
03150    struct dundi_transaction *trans;
03151    AST_LIST_LOCK(&peers);
03152    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03153       dundi_discover(trans);
03154    }
03155    AST_LIST_UNLOCK(&peers);
03156    return 0;
03157 }
03158 
03159 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
03160 {
03161    struct dundi_transaction *trans;
03162 
03163    /* Mark all as "in thread" so they don't disappear */
03164    AST_LIST_LOCK(&peers);
03165    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03166       if (trans->thread)
03167          ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
03168       trans->thread = 1;
03169    }
03170    AST_LIST_UNLOCK(&peers);
03171 
03172    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03173       if (!ast_test_flag(trans, FLAG_DEAD))
03174          precache_trans(trans, maps, mapcount, expiration, foundanswers);
03175    }
03176 
03177    /* Cleanup any that got destroyed in the mean time */
03178    AST_LIST_LOCK(&peers);
03179    AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
03180       trans->thread = 0;
03181       if (ast_test_flag(trans, FLAG_DEAD)) {
03182          if (option_debug)
03183             ast_log(LOG_DEBUG, "Our transaction went away!\n");
03184          /* This is going to remove the transaction from the dundi_request's list, as well
03185           * as the global transactions list */
03186          destroy_trans(trans, 0);
03187       }
03188    }
03189    AST_LIST_TRAVERSE_SAFE_END
03190    AST_LIST_UNLOCK(&peers);
03191 
03192    return 0;
03193 }
03194 
03195 static int query_transactions(struct dundi_request *dr)
03196 {
03197    struct dundi_transaction *trans;
03198 
03199    AST_LIST_LOCK(&peers);
03200    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03201       dundi_query(trans);
03202    }
03203    AST_LIST_UNLOCK(&peers);
03204 
03205    return 0;
03206 }
03207 
03208 static int optimize_transactions(struct dundi_request *dr, int order)
03209 {
03210    /* Minimize the message propagation through DUNDi by
03211       alerting the network to hops which should be not be considered */
03212    struct dundi_transaction *trans;
03213    struct dundi_peer *peer;
03214    dundi_eid tmp;
03215    int x;
03216    int needpush;
03217 
03218    AST_LIST_LOCK(&peers);
03219    AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03220       /* Pop off the true root */
03221       if (trans->eidcount) {
03222          tmp = trans->eids[--trans->eidcount];
03223          needpush = 1;
03224       } else {
03225          tmp = trans->us_eid;
03226          needpush = 0;
03227       }
03228 
03229       AST_LIST_TRAVERSE(&peers, peer, list) {
03230          if (has_permission(&peer->include, dr->dcontext) && 
03231              dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
03232             (peer->order <= order)) {
03233             /* For each other transaction, make sure we don't
03234                ask this EID about the others if they're not
03235                already in the list */
03236             if (!dundi_eid_cmp(&tmp, &peer->eid)) 
03237                x = -1;
03238             else {
03239                for (x=0;x<trans->eidcount;x++) {
03240                   if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
03241                      break;
03242                }
03243             }
03244             if (x == trans->eidcount) {
03245                /* Nope not in the list, if needed, add us at the end since we're the source */
03246                if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
03247                   trans->eids[trans->eidcount++] = peer->eid;
03248                   /* Need to insert the real root (or us) at the bottom now as
03249                      a requirement now.  */
03250                   needpush = 1;
03251                }
03252             }
03253          }
03254       }
03255       /* If necessary, push the true root back on the end */
03256       if (needpush)
03257          trans->eids[trans->eidcount++] = tmp;
03258    }
03259    AST_LIST_UNLOCK(&peers);
03260 
03261    return 0;
03262 }
03263 
03264 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
03265 {
03266    struct dundi_transaction *trans;
03267    int x;
03268    char eid_str[20];
03269    char eid_str2[20];
03270 
03271    /* Ignore if not registered */
03272    if (!p->addr.sin_addr.s_addr)
03273       return 0;
03274    if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
03275       return 0;
03276 
03277    if (option_debug) {
03278       if (ast_strlen_zero(dr->number))
03279          ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
03280       else
03281          ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
03282    }
03283 
03284    trans = create_transaction(p);
03285    if (!trans)
03286       return -1;
03287    trans->parent = dr;
03288    trans->ttl = ttl;
03289    for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
03290       trans->eids[x] = *avoid[x];
03291    trans->eidcount = x;
03292    AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
03293    
03294    return 0;
03295 }
03296 
03297 static void cancel_request(struct dundi_request *dr)
03298 {
03299    struct dundi_transaction *trans;
03300 
03301    AST_LIST_LOCK(&peers);
03302    while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
03303       /* Orphan transaction from request */
03304       trans->parent = NULL;
03305       /* Send final cancel */
03306       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
03307    }
03308    AST_LIST_UNLOCK(&peers);
03309 }
03310 
03311 static void abort_request(struct dundi_request *dr)
03312 {
03313    struct dundi_transaction *trans;
03314 
03315    AST_LIST_LOCK(&peers);
03316    while ((trans = AST_LIST_FIRST(&dr->trans))) {
03317       /* This will remove the transaction from the list */
03318       destroy_trans(trans, 0);
03319    }
03320    AST_LIST_UNLOCK(&peers);
03321 }
03322 
03323 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[])
03324 {
03325    struct dundi_peer *p;
03326    int x;
03327    int res;
03328    int pass;
03329    int allowconnect;
03330    char eid_str[20];
03331    AST_LIST_LOCK(&peers);
03332    AST_LIST_TRAVERSE(&peers, p, list) {
03333       if (modeselect == 1) {
03334          /* Send the precache to push upstreams only! */
03335          pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
03336          allowconnect = 1;
03337       } else {
03338          /* Normal lookup / EID query */
03339          pass = has_permission(&p->include, dr->dcontext);
03340          allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
03341       }
03342       if (skip) {
03343          if (!dundi_eid_cmp(skip, &p->eid))
03344             pass = 0;
03345       }
03346       if (pass) {
03347          if (p->order <= order) {
03348             /* Check order first, then check cache, regardless of
03349                omissions, this gets us more likely to not have an
03350                affected answer. */
03351             if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
03352                res = 0;
03353                /* Make sure we haven't already seen it and that it won't
03354                   affect our answer */
03355                for (x=0;avoid[x];x++) {
03356                   if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
03357                      /* If not a direct connection, it affects our answer */
03358                      if (directs && !directs[x]) 
03359                         ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
03360                      break;
03361                   }
03362                }
03363                /* Make sure we can ask */
03364                if (allowconnect) {
03365                   if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
03366                      /* Check for a matching or 0 cache entry */
03367                      append_transaction(dr, p, ttl, avoid);
03368                   } else {
03369                      if (option_debug)
03370                         ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
03371                   }
03372                }
03373             }
03374             *foundcache |= res;
03375          } else if (!*skipped || (p->order < *skipped))
03376             *skipped = p->order;
03377       }
03378    }
03379    AST_LIST_UNLOCK(&peers);
03380 }
03381 
03382 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
03383 {
03384    struct dundi_request *cur;
03385    int res=0;
03386    char eid_str[20];
03387    AST_LIST_LOCK(&peers);
03388    AST_LIST_TRAVERSE(&requests, cur, list) {
03389       if (option_debug)
03390          ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
03391             dr->dcontext, dr->number);
03392       if (!strcasecmp(cur->dcontext, dr->dcontext) &&
03393           !strcasecmp(cur->number, dr->number) &&
03394           (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
03395          if (option_debug)
03396             ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n", 
03397                cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
03398          *pending = cur;
03399          res = 1;
03400          break;
03401       }
03402    }
03403    if (!res) {
03404       if (option_debug)
03405          ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n", 
03406                dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
03407       /* Go ahead and link us in since nobody else is searching for this */
03408       AST_LIST_INSERT_HEAD(&requests, dr, list);
03409       *pending = NULL;
03410    }
03411    AST_LIST_UNLOCK(&peers);
03412    return res;
03413 }
03414 
03415 static void unregister_request(struct dundi_request *dr)
03416 {
03417    AST_LIST_LOCK(&peers);
03418    AST_LIST_REMOVE(&requests, dr, list);
03419    AST_LIST_UNLOCK(&peers);
03420 }
03421 
03422 static int check_request(struct dundi_request *dr)
03423 {
03424    struct dundi_request *cur;
03425 
03426    AST_LIST_LOCK(&peers);
03427    AST_LIST_TRAVERSE(&requests, cur, list) {
03428       if (cur == dr)
03429          break;
03430    }
03431    AST_LIST_UNLOCK(&peers);
03432    
03433    return cur ? 1 : 0;
03434 }
03435 
03436 static unsigned long avoid_crc32(dundi_eid *avoid[])
03437 {
03438    /* Idea is that we're calculating a checksum which is independent of
03439       the order that the EID's are listed in */
03440    unsigned long acrc32 = 0;
03441    int x;
03442    for (x=0;avoid[x];x++) {
03443       /* Order doesn't matter */
03444       if (avoid[x+1]) {
03445          acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
03446       }
03447    }
03448    return acrc32;
03449 }
03450 
03451 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[])
03452 {
03453    int res;
03454    struct dundi_request dr, *pending;
03455    dundi_eid *rooteid=NULL;
03456    int x;
03457    int ttlms;
03458    int ms;
03459    int foundcache;
03460    int skipped=0;
03461    int order=0;
03462    char eid_str[20];
03463    struct timeval start;
03464    
03465    /* Don't do anthing for a hungup channel */
03466    if (chan && chan->_softhangup)
03467       return 0;
03468 
03469    ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03470 
03471    for (x=0;avoid[x];x++)
03472       rooteid = avoid[x];
03473    /* Now perform real check */
03474    memset(&dr, 0, sizeof(dr));
03475    if (pipe(dr.pfds)) {
03476       ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
03477       return -1;
03478    }
03479    dr.dr = result;
03480    dr.hmd = hmd;
03481    dr.maxcount = maxret;
03482    dr.expiration = *expiration;
03483    dr.cbypass = cbypass;
03484    dr.crc32 = avoid_crc32(avoid);
03485    ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
03486    ast_copy_string(dr.number, number, sizeof(dr.number));
03487    if (rooteid)
03488       dr.root_eid = *rooteid;
03489    res = register_request(&dr, &pending);
03490    if (res) {
03491       /* Already a request */
03492       if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
03493          /* This is on behalf of someone else.  Go ahead and close this out since
03494             they'll get their answer anyway. */
03495          if (option_debug)
03496             ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
03497                dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
03498          close(dr.pfds[0]);
03499          close(dr.pfds[1]);
03500          return -2;
03501       } else {
03502          /* Wait for the cache to populate */
03503          if (option_debug)
03504             ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
03505                dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
03506          start = ast_tvnow();
03507          while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
03508             /* XXX Would be nice to have a way to poll/select here XXX */
03509             /* XXX this is a busy wait loop!!! */
03510             usleep(1);
03511          }
03512          /* Continue on as normal, our cache should kick in */
03513       }
03514    }
03515    /* Create transactions */
03516    do {
03517       order = skipped;
03518       skipped = 0;
03519       foundcache = 0;
03520       build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
03521    } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
03522    /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
03523       do this earlier because we didn't know if we were going to have transactions
03524       or not. */
03525    if (!ttl) {
03526       ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
03527       abort_request(&dr);
03528       unregister_request(&dr);
03529       close(dr.pfds[0]);
03530       close(dr.pfds[1]);
03531       return 0;
03532    }
03533       
03534    /* Optimize transactions */
03535    optimize_transactions(&dr, order);
03536    /* Actually perform transactions */
03537    discover_transactions(&dr);
03538    /* Wait for transaction to come back */
03539    start = ast_tvnow();
03540    while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
03541       ms = 100;
03542       ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03543    }
03544    if (chan && chan->_softhangup && option_debug)
03545       ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
03546    cancel_request(&dr);
03547    unregister_request(&dr);
03548    res = dr.respcount;
03549    *expiration = dr.expiration;
03550    close(dr.pfds[0]);
03551    close(dr.pfds[1]);
03552    return res;
03553 }
03554 
03555 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
03556 {
03557    struct dundi_hint_metadata hmd;
03558    dundi_eid *avoid[1] = { NULL, };
03559    int direct[1] = { 0, };
03560    int expiration = dundi_cache_time;
03561    memset(&hmd, 0, sizeof(hmd));
03562    hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
03563    return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
03564 }
03565 
03566 static void reschedule_precache(const char *number, const char *context, int expiration)
03567 {
03568    int len;
03569    struct dundi_precache_queue *qe, *prev;
03570 
03571    AST_LIST_LOCK(&pcq);
03572    AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) {
03573       if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
03574          AST_LIST_REMOVE_CURRENT(&pcq, list);
03575          break;
03576       }
03577    }
03578    AST_LIST_TRAVERSE_SAFE_END
03579    if (!qe) {
03580       len = sizeof(*qe);
03581       len += strlen(number) + 1;
03582       len += strlen(context) + 1;
03583       if (!(qe = ast_calloc(1, len))) {
03584          AST_LIST_UNLOCK(&pcq);
03585          return;
03586       }
03587       strcpy(qe->number, number);
03588       qe->context = qe->number + strlen(number) + 1;
03589       strcpy(qe->context, context);
03590    }
03591    time(&qe->expiration);
03592    qe->expiration += expiration;
03593    if ((prev = AST_LIST_FIRST(&pcq))) {
03594       while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
03595          prev = AST_LIST_NEXT(prev, list);
03596       AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
03597    } else
03598       AST_LIST_INSERT_HEAD(&pcq, qe, list);
03599    AST_LIST_UNLOCK(&pcq);
03600 }
03601 
03602 static void dundi_precache_full(void)
03603 {
03604    struct dundi_mapping *cur;
03605    struct ast_context *con;
03606    struct ast_exten *e;
03607 
03608    AST_LIST_TRAVERSE(&mappings, cur, list) {
03609       ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
03610       ast_lock_contexts();
03611       con = NULL;
03612       while ((con = ast_walk_contexts(con))) {
03613          if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
03614             continue;
03615          /* Found the match, now queue them all up */
03616          ast_lock_context(con);
03617          e = NULL;
03618          while ((e = ast_walk_context_extensions(con, e)))
03619             reschedule_precache(ast_get_extension_name(e), cur->