Pass format string
[asterisk/asterisk.git] / pbx / pbx_dundi.c
index 7dc3467..a7d2644 100755 (executable)
@@ -77,6 +77,8 @@ static char *descrip =
 #define DUNDI_MODEL_OUTBOUND   (1 << 1)
 #define DUNDI_MODEL_SYMMETRIC  (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
 
+/* Keep times of last 10 lookups */
+#define DUNDI_TIMING_HISTORY   10
 
 #define FLAG_ISREG             (1 << 0)                /* Transaction is register request */
 #define FLAG_DEAD              (1 << 1)                /* Transaction is dead */
@@ -84,6 +86,7 @@ static char *descrip =
 #define FLAG_ISQUAL            (1 << 3)                /* Transaction is a qualification */
 #define FLAG_ENCRYPT   (1 << 4)                /* Transaction is encrypted wiht ECX/DCX */
 #define FLAG_SENDFULLKEY       (1 << 5)                /* Send full key on transaction */
+#define FLAG_STOREHIST (1 << 6)                /* Record historic performance */
 
 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
 
@@ -108,6 +111,7 @@ static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
 static int global_autokilltimeout = 0;
 static dundi_eid global_eid;
 static int default_expiration = 60;
+static int global_storehistory = 0;
 static char dept[80];
 static char org[80];
 static char locality[80];
@@ -145,6 +149,7 @@ struct dundi_request;
 
 struct dundi_transaction {
        struct sockaddr_in addr;        /* Other end of transaction */
+       struct timeval start;           /* When this transaction was created */
        dundi_eid eids[DUNDI_MAX_STACK + 1];
        int eidcount;                           /* Number of eids in eids */
        dundi_eid us_eid;                       /* Our EID, to them */
@@ -154,6 +159,7 @@ struct dundi_transaction {
        int flags;                                      /* Has final packet been sent */
        int ttl;                                        /* Remaining TTL for queries on this one */
        int thread;                                     /* We have a calling thread */
+       int retranstimer;                       /* How long to wait before retransmissions */
        int autokillid;                         /* ID to kill connection if answer doesn't come back fast enough */
        int autokilltimeout;            /* Recommended timeout for autokill */
        unsigned short strans;          /* Our transaction identifier */
@@ -180,6 +186,8 @@ struct dundi_request {
        int maxcount;
        int respcount;
        int expiration;
+       int cbypass;
+       int pfds[2];
        unsigned long crc32;                                                    /* CRC-32 of all but root EID's in avoid list */
        struct dundi_transaction *trans;        /* Transactions */
        struct dundi_request *next;
@@ -220,6 +228,9 @@ static struct dundi_peer {
        aes_decrypt_ctx them_dcx;       /* Cached AES 128 Decryption context */
        time_t keyexpire;                       /* When to expire/recreate key */
        int registerexpire;
+       int lookuptimes[DUNDI_TIMING_HISTORY];
+       char *lookups[DUNDI_TIMING_HISTORY];
+       int avgms;
        struct dundi_transaction *regtrans;     /* Registration transaction */
        struct dundi_transaction *qualtrans;    /* Qualify transaction */
        struct dundi_transaction *keypending;
@@ -238,12 +249,12 @@ static int dundi_xmit(struct dundi_packet *pack);
 static void dundi_debug_output(const char *data)
 {
        if (dundidebug)
-               ast_verbose(data);
+               ast_verbose("%s", data);
 }
 
 static void dundi_error_output(const char *data)
 {
-       ast_log(LOG_WARNING, data);
+       ast_log(LOG_WARNING, "%s", data);
 }
 
 static int has_permission(struct permission *ps, char *cont)
@@ -285,7 +296,7 @@ static int str2tech(char *str)
                return -1;
 }
 
-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, dundi_eid *avoid[], int direct[]);
+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, dundi_eid *avoid[], int direct[]);
 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
 {
@@ -469,6 +480,7 @@ struct dundi_query_state {
        char called_number[AST_MAX_EXTENSION];
        struct dundi_mapping *maps;
        int nummaps;
+       int nocache;
        struct dundi_transaction *trans;
        void *chal;
        int challen;
@@ -480,7 +492,6 @@ static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map
 {
        int flags;
        int x;
-       struct ast_channel *chan=NULL;
        if (!ast_strlen_zero(map->lcontext)) {
                flags = 0;
                if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
@@ -501,27 +512,35 @@ static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map
                        flags &= ~(DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
                }
                if (flags) {
-                       /* Clearly we can't say 'don't ask' anymore... */
-                       chan = ast_channel_alloc(0);
-                       if (chan) {
-                               flags |= map->options & 0xffff;
-                               dr[anscnt].flags = flags;
-                               dr[anscnt].techint = map->tech;
-                               dr[anscnt].weight = map->weight;
-                               dr[anscnt].expiration = DUNDI_DEFAULT_CACHE_TIME;
-                               strncpy(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
-                               dr[anscnt].eid = *us_eid;
-                               dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
-                               if (flags & DUNDI_FLAG_EXISTS) {
-                                       pbx_builtin_setvar_helper(chan, "NUMBER", called_number);
-                                       pbx_builtin_setvar_helper(chan, "EID", dr[anscnt].eid_str);
-                                       pbx_builtin_setvar_helper(chan, "SECRET", cursecret);
-                                       pbx_builtin_setvar_helper(chan, "IPADDR", ipaddr);
-                                       pbx_substitute_variables_helper(chan, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
-                               } else
-                                       dr[anscnt].dest[0] = '\0';
-                               anscnt++;
-                       }
+                       struct varshead headp;
+                       struct ast_var_t *newvariable;
+                       flags |= map->options & 0xffff;
+                       dr[anscnt].flags = flags;
+                       dr[anscnt].techint = map->tech;
+                       dr[anscnt].weight = map->weight;
+                       dr[anscnt].expiration = DUNDI_DEFAULT_CACHE_TIME;
+                       strncpy(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
+                       dr[anscnt].eid = *us_eid;
+                       dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
+                       if (flags & DUNDI_FLAG_EXISTS) {
+                               AST_LIST_HEAD_INIT(&headp);
+                               newvariable = ast_var_assign("NUMBER", called_number);
+                               AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
+                               newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
+                               AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
+                               newvariable = ast_var_assign("SECRET", cursecret);
+                               AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
+                               newvariable = ast_var_assign("IPADDR", ipaddr);
+                               AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
+                               pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
+                               while (!AST_LIST_EMPTY(&headp)) {           /* List Deletion. */
+                                       newvariable = AST_LIST_FIRST(&headp);
+                                       AST_LIST_REMOVE_HEAD(&headp, entries);
+                                       ast_var_delete(newvariable);
+                               }
+                       } else
+                               dr[anscnt].dest[0] = '\0';
+                       anscnt++;
                } else {
                        /* No answers...  Find the fewest number of digits from the
                           number for which we have no answer. */
@@ -541,8 +560,6 @@ static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map
                        }
                }
        }
-       if (chan)
-               ast_hangup(chan);
        return anscnt;
 }
 
@@ -578,7 +595,7 @@ static void *dundi_lookup_thread(void *data)
                
        if (max) {
                /* If we do not have a canonical result, keep looking */
-               res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->eids, st->directs);
+               res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, st->eids, st->directs);
                if (res > 0) {
                        /* Append answer in result */
                        ouranswers += res;
@@ -778,6 +795,7 @@ static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies
                strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
                st->trans = trans;
                st->ttl = ies->ttl - 1;
+               st->nocache = ies->cbypass;
                if (st->ttl < 0)
                        st->ttl = 0;
                s = st->fluffy;
@@ -1024,9 +1042,17 @@ static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
        /* Enable encryption if appropriate */
        if (!ast_strlen_zero(p->inkey))
                trans->flags |= FLAG_ENCRYPT;
-       if (p->maxms)
+       if (p->maxms) {
                trans->autokilltimeout = p->maxms;
-       else
+               if (p->lastms > 1) {
+                       trans->retranstimer = p->lastms * 2;
+                       if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
+                               trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
+                       /* Keep it from being silly */
+                       if (trans->retranstimer < 10)
+                               trans->retranstimer = 10;
+               }
+       } else
                trans->autokilltimeout = global_autokilltimeout;
 }
 
@@ -1935,12 +1961,47 @@ static int dundi_do_debug(int fd, int argc, char *argv[])
        return RESULT_SUCCESS;
 }
 
+static int dundi_do_store_history(int fd, int argc, char *argv[])
+{
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       global_storehistory = 1;
+       ast_cli(fd, "DUNDi History Storage Enabled\n");
+       return RESULT_SUCCESS;
+}
+
 static int dundi_flush(int fd, int argc, char *argv[])
 {
-       if (argc != 2)
+       int stats=0;
+       if ((argc < 2) || (argc > 3))
                return RESULT_SHOWUSAGE;
-       ast_db_deltree("dundi/cache", NULL);
-       ast_cli(fd, "DUNDi Cache Flushed\n");
+       if (argc > 2) {
+               if (!strcasecmp(argv[2], "stats"))
+                       stats = 1;
+               else
+                       return RESULT_SHOWUSAGE;
+       }
+       if (stats) {
+               /* Flush statistics */
+               struct dundi_peer *p;
+               int x;
+               ast_mutex_lock(&peerlock);
+               p = peers;
+               while(p) {
+                       for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
+                               if (p->lookups[x])
+                                       free(p->lookups[x]);
+                               p->lookups[x] = NULL;
+                               p->lookuptimes[x] = 0;
+                       }
+                       p->avgms = 0;
+                       p = p->next;
+               }
+               ast_mutex_unlock(&peerlock);
+       } else {
+               ast_db_deltree("dundi/cache", NULL);
+               ast_cli(fd, "DUNDi Cache Flushed\n");
+       }
        return RESULT_SUCCESS;
 }
 
@@ -1953,6 +2014,15 @@ static int dundi_no_debug(int fd, int argc, char *argv[])
        return RESULT_SUCCESS;
 }
 
+static int dundi_no_store_history(int fd, int argc, char *argv[])
+{
+       if (argc != 4)
+               return RESULT_SHOWUSAGE;
+       global_storehistory = 0;
+       ast_cli(fd, "DUNDi History Storage Disabled\n");
+       return RESULT_SUCCESS;
+}
+
 static char *model2str(int model)
 {
        switch(model) {
@@ -2021,16 +2091,26 @@ static int dundi_do_lookup(int fd, int argc, char *argv[])
        char fs[80] = "";
        char *context;
        int x;
+       int bypass = 0;
        struct dundi_result dr[MAX_RESULTS];
-       if ((argc < 3) || (argc > 3))
+       struct timeval start;
+       if ((argc < 3) || (argc > 4))
                return RESULT_SHOWUSAGE;
+       if (argc > 3) {
+               if (!strcasecmp(argv[3], "bypass"))
+                       bypass=1;
+               else
+                       return RESULT_SHOWUSAGE;
+       }
        strncpy(tmp, argv[2], sizeof(tmp) - 1);
        context = strchr(tmp, '@');
        if (context) {
                *context = '\0';
                context++;
        }
-       res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp);
+       gettimeofday(&start, NULL);
+       res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
+       
        if (res < 0) 
                ast_cli(fd, "DUNDi lookup returned error.\n");
        else if (!res) 
@@ -2040,6 +2120,7 @@ static int dundi_do_lookup(int fd, int argc, char *argv[])
        for (x=0;x<res;x++) {
                ast_cli(fd, "%d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
        }
+       ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
        return RESULT_SUCCESS;
 }
 
@@ -2088,6 +2169,7 @@ static int dundi_show_peer(int fd, int argc, char *argv[])
        char *order;
        char iabuf[INET_ADDRSTRLEN];
        char eid_str[20];
+       int x, cnt;
        
        if (argc != 4)
                return RESULT_SHOWUSAGE;
@@ -2139,7 +2221,17 @@ static int dundi_show_peer(int fd, int argc, char *argv[])
                        ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
                        p = p->next;
                }
-               
+               cnt = 0;
+               for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
+                       if (peer->lookups[x]) {
+                               if (!cnt)
+                                       ast_cli(fd, "Last few query times:\n");
+                               ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
+                               cnt++;
+                       }
+               }
+               if (cnt)
+                       ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
        } else
                ast_cli(fd, "No such peer '%s'\n", argv[3]);
        ast_mutex_unlock(&peerlock);
@@ -2148,11 +2240,12 @@ static int dundi_show_peer(int fd, int argc, char *argv[])
 
 static int dundi_show_peers(int fd, int argc, char *argv[])
 {
-#define FORMAT2 "%-20.20s %-15.15s     %-15.15s %-15.15s\n"
-#define FORMAT "%-20.20s %-15.15s %s %-15.15s %-15.15s\n"
+#define FORMAT2 "%-20.20s %-15.15s     %-10.10s %-8.8s %-15.15s\n"
+#define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
        struct dundi_peer *peer;
        char iabuf[INET_ADDRSTRLEN];
        int registeredonly=0;
+       char avgms[20];
        char eid_str[20];
        if ((argc != 3) && (argc != 4) && (argc != 5))
                return RESULT_SHOWUSAGE;
@@ -2163,7 +2256,7 @@ static int dundi_show_peers(int fd, int argc, char *argv[])
                        return RESULT_SHOWUSAGE;
        }
        ast_mutex_lock(&peerlock);
-       ast_cli(fd, FORMAT2, "EID", "Host", "Model", "Status");
+       ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
        for (peer = peers;peer;peer = peer->next) {
                char status[20] = "";
         int print_line = -1;
@@ -2181,10 +2274,13 @@ static int dundi_show_peers(int fd, int argc, char *argv[])
                                strncpy(status, "UNKNOWN", sizeof(status) - 1);
                } else 
                        strncpy(status, "Unmonitored", sizeof(status) - 1);
-
+               if (peer->avgms) 
+                       snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
+               else
+                       strcpy(avgms, "Unavail");
                snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
                                        peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
-                                       peer->dynamic ? "(D)" : "(S)", model2str(peer->model), status);
+                                       peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
 
                 if (argc == 5) {
                   if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
@@ -2201,7 +2297,7 @@ static int dundi_show_peers(int fd, int argc, char *argv[])
         if (print_line) {
                        ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
                                        peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
-                                       peer->dynamic ? "(D)" : "(S)", model2str(peer->model), status);
+                                       peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
                }
        }
        ast_mutex_unlock(&peerlock);
@@ -2293,6 +2389,16 @@ static char no_debug_usage[] =
 "Usage: dundi no debug\n"
 "       Disables dumping of DUNDi packets for debugging purposes\n";
 
+static char store_history_usage[] = 
+"Usage: dundi store history\n"
+"       Enables storing of DUNDi requests and times for debugging\n"
+"purposes\n";
+
+static char no_store_history_usage[] = 
+"Usage: dundi no store history\n"
+"       Disables storing of DUNDi requests and times for debugging\n"
+"purposes\n";
+
 static char show_peers_usage[] = 
 "Usage: dundi show peers\n"
 "       Lists all known DUNDi peers.\n";
@@ -2318,10 +2424,10 @@ static char show_requests_usage[] =
 "       Lists all known pending DUNDi requests.\n";
 
 static char lookup_usage[] =
-"Usage: dundi lookup <number>[@context]\n"
+"Usage: dundi lookup <number>[@context] [bypass]\n"
 "       Lookup the given number within the given DUNDi context\n"
-"(or e164 if none is specified).\n"
-"if specified.\n";
+"(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
+"keyword is specified.\n";
 
 static char query_usage[] =
 "Usage: dundi query <entity>[@context]\n"
@@ -2330,12 +2436,20 @@ static char query_usage[] =
 "e164 if none is specified).\n";
 
 static char flush_usage[] =
-"Usage: dundi flush\n"
-"       Flushes DUNDi answer cache, used primarily for debug.\n";
+"Usage: dundi flush [stats]\n"
+"       Flushes DUNDi answer cache, used primarily for debug.  If\n"
+"'stats' is present, clears timer statistics instead of normal\n"
+"operation.\n";
 
 static struct ast_cli_entry  cli_debug =
        { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
 
+static struct ast_cli_entry  cli_store_history =
+       { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
+
+static struct ast_cli_entry  cli_no_store_history =
+       { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
+
 static struct ast_cli_entry  cli_flush =
        { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
 
@@ -2384,6 +2498,11 @@ static struct dundi_transaction *create_transaction(struct dundi_peer *p)
        trans = malloc(sizeof(struct dundi_transaction));
        if (trans) {
                memset(trans, 0, sizeof(struct dundi_transaction));
+               if (global_storehistory) {
+                       gettimeofday(&trans->start, NULL);
+                       trans->flags |= FLAG_STOREHIST;
+               }
+               trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
                trans->autokillid = -1;
                if (p) {
                        apply_peer(trans, p);
@@ -2448,8 +2567,10 @@ static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
        struct dundi_peer *peer;
        struct timeval tv;
        int ms;
+       int x;
+       int cnt;
        char eid_str[20];
-       if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL)) {
+       if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
                peer = peers;
                while (peer) {
                        if (peer->regtrans == trans)
@@ -2477,6 +2598,33 @@ static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
                                }
                                peer->qualtrans = NULL;
                        }
+                       if (trans->flags & FLAG_STOREHIST) {
+                               if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
+                                       if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
+                                               peer->avgms = 0;
+                                               cnt = 0;
+                                               if (peer->lookups[DUNDI_TIMING_HISTORY-1])
+                                                       free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
+                                               for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
+                                                       peer->lookuptimes[x] = peer->lookuptimes[x-1];
+                                                       peer->lookups[x] = peer->lookups[x-1];
+                                                       if (peer->lookups[x]) {
+                                                               peer->avgms += peer->lookuptimes[x];
+                                                               cnt++;
+                                                       }
+                                               }
+                                               peer->lookuptimes[0] = calc_ms(&trans->start);
+                                               peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
+                                               if (peer->lookups[0]) {
+                                                       sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
+                                                       peer->avgms += peer->lookuptimes[0];
+                                                       cnt++;
+                                               }
+                                               if (cnt)
+                                                       peer->avgms /= cnt;
+                                       }
+                               }
+                       }
                        peer = peer->next;
                }
        }
@@ -2495,6 +2643,12 @@ static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
                        prev = cur;
                        cur = cur->next;
                }
+               if (!trans->parent->trans) {
+                       /* Wake up sleeper */
+                       if (trans->parent->pfds[1] > -1) {
+                               write(trans->parent->pfds[1], "killa!", 6);
+                       }
+               }
        }
        /* Unlink from all trans */
        prev = NULL;
@@ -2563,7 +2717,7 @@ static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, i
                memset(pack, 0, len);
                pack->h = (struct dundi_hdr *)(pack->data);
                if (cmdresp != DUNDI_COMMAND_ACK) {
-                       pack->retransid = ast_sched_add(sched, DUNDI_DEFAULT_RETRANS_TIMER, dundi_rexmit, pack);
+                       pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
                        pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
                        pack->next = trans->packets;
                        trans->packets = pack;
@@ -2671,6 +2825,8 @@ static int dundi_discover(struct dundi_transaction *trans)
        dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
        dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
        dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
+       if (trans->parent->cbypass)
+               dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
        if (trans->autokilltimeout)
                trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
        return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
@@ -2837,7 +2993,7 @@ static void abort_request(struct dundi_request *dr)
        ast_mutex_unlock(&peerlock);
 }
 
-static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, dundi_eid *avoid[], int directs[])
+static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, dundi_eid *avoid[], int directs[])
 {
        struct dundi_peer *p;
        int x;
@@ -2851,7 +3007,8 @@ static void build_transactions(struct dundi_request *dr, int ttl, int order, int
                                /* Check order first, then check cache, regardless of
                                   omissions, this gets us more likely to not have an
                                   affected answer. */
-                               if (!(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration))) {
+                               if(nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration))) {
+                                       res = 0;
                                        /* Make sure we haven't already seen it and that it won't
                                           affect our answer */
                                        for (x=0;avoid[x];x++) {
@@ -2964,13 +3121,14 @@ static unsigned long avoid_crc32(dundi_eid *avoid[])
        return acrc32;
 }
 
-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, dundi_eid *avoid[], int direct[])
+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, dundi_eid *avoid[], int direct[])
 {
        int res;
        struct dundi_request dr, *pending;
        dundi_eid *rooteid=NULL;
        int x;
        int ttlms;
+       int ms;
        int foundcache;
        int skipped=0;
        int order=0;
@@ -2987,10 +3145,15 @@ static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct
                rooteid = avoid[x];
        /* Now perform real check */
        memset(&dr, 0, sizeof(dr));
+       if (pipe(dr.pfds)) {
+               ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
+               return -1;
+       }
        dr.dr = result;
        dr.hmd = hmd;
        dr.maxcount = maxret;
        dr.expiration = *expiration;
+       dr.cbypass = cbypass;
        dr.crc32 = avoid_crc32(avoid);
        strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
        strncpy(dr.number, number, sizeof(dr.number) - 1);
@@ -3004,14 +3167,18 @@ static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct
                           they'll get their answer anyway. */
                        ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
                                dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
+                       close(dr.pfds[0]);
+                       close(dr.pfds[1]);
                        return -2;
                } else {
                        /* Wait for the cache to populate */
                        ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
                                dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
                        gettimeofday(&start, NULL);
-                       while(check_request(pending) && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup))
+                       while(check_request(pending) && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
+                               /* XXX Would be nice to have a way to poll/select here XXX */
                                usleep(1);
+                       }
                        /* Continue on as normal, our cache should kick in */
                }
        }
@@ -3020,7 +3187,7 @@ static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct
                order = skipped;
                skipped = 0;
                foundcache = 0;
-               build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, avoid, direct);
+               build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, avoid, direct);
        } while (skipped && !foundcache && !dr.trans);
        /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
           do this earlier because we didn't know if we were going to have transactions
@@ -3029,6 +3196,8 @@ static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct
                hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
                abort_request(&dr);
                unregister_request(&dr);
+               close(dr.pfds[0]);
+               close(dr.pfds[1]);
                return 0;
        }
                
@@ -3038,18 +3207,22 @@ static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct
        discover_transactions(&dr);
        /* Wait for transaction to come back */
        gettimeofday(&start, NULL);
-       while(dr.trans && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup))
-               usleep(1);
+       while(dr.trans && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
+               ms = 100;
+               ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
+       }
        if (chan && chan->_softhangup)
                ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
        cancel_request(&dr);
        unregister_request(&dr);
        res = dr.respcount;
        *expiration = dr.expiration;
+       close(dr.pfds[0]);
+       close(dr.pfds[1]);
        return res;
 }
 
-int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number)
+int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
 {
        struct dundi_hint_metadata hmd;
        dundi_eid *avoid[1] = { NULL, };
@@ -3057,7 +3230,7 @@ int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *ch
        int expiration = DUNDI_DEFAULT_CACHE_TIME;
        memset(&hmd, 0, sizeof(hmd));
        hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
-       return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, avoid, direct);
+       return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, avoid, direct);
 }
 
 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[])
@@ -3084,7 +3257,7 @@ static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *d
        if (rooteid)
                dr.root_eid = *rooteid;
        /* Create transactions */
-       build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, avoid, NULL);
+       build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, avoid, NULL);
 
        /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
           do this earlier because we didn't know if we were going to have transactions
@@ -3595,7 +3768,7 @@ static int dundi_helper(struct ast_channel *chan, const char *context, const cha
                if (!data || ast_strlen_zero(data))
                        data = context;
        }
-       res = dundi_lookup(results, MAX_RESULTS, chan, data, exten);
+       res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
        for (x=0;x<res;x++) {
                if (results[x].flags & flag)
                        found++;
@@ -3646,7 +3819,7 @@ static int dundi_exec(struct ast_channel *chan, const char *context, const char
                if (!data || ast_strlen_zero(data))
                        data = context;
        }
-       res = dundi_lookup(results, MAX_RESULTS, chan, data, exten);
+       res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
        if (res > 0) {
                sort_results(results, res);
                for (x=0;x<res;x++) {
@@ -3716,6 +3889,7 @@ static int set_config(char *config_file, struct sockaddr_in* sin)
                ast_log(LOG_WARNING, "Unable to get host name!\n");
        ast_mutex_lock(&peerlock);
        reset_global_eid();
+       global_storehistory = 0;
        strncpy(secretpath, "dundi", sizeof(secretpath) - 1);
        v = ast_variable_browse(cfg, "general");
        while(v) {
@@ -3793,6 +3967,8 @@ static int set_config(char *config_file, struct sockaddr_in* sin)
                        strncpy(email, v->value, sizeof(email) - 1);
                } else if (!strcasecmp(v->name, "phone")) {
                        strncpy(phone, v->value, sizeof(phone) - 1);
+               } else if (!strcasecmp(v->name, "storehistory")) {
+                       global_storehistory = ast_true(v->value);
                }
                v = v->next;
        }
@@ -3827,8 +4003,10 @@ int unload_module(void)
        int res;
        STANDARD_HANGUP_LOCALUSERS;
        ast_cli_unregister(&cli_debug);
+       ast_cli_unregister(&cli_store_history);
        ast_cli_unregister(&cli_flush);
        ast_cli_unregister(&cli_no_debug);
+       ast_cli_unregister(&cli_no_store_history);
        ast_cli_unregister(&cli_show_peers);
        ast_cli_unregister(&cli_show_entityid);
        ast_cli_unregister(&cli_show_trans);
@@ -3875,8 +4053,10 @@ int load_module(void)
        }
 
        ast_cli_register(&cli_debug);
+       ast_cli_register(&cli_store_history);
        ast_cli_register(&cli_flush);
        ast_cli_register(&cli_no_debug);
+       ast_cli_register(&cli_no_store_history);
        ast_cli_register(&cli_show_peers);
        ast_cli_register(&cli_show_entityid);
        ast_cli_register(&cli_show_trans);