make datastore creation and destruction a generic API since it is not really channel...
[asterisk/asterisk.git] / pbx / pbx_dundi.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Distributed Universal Number Discovery (DUNDi)
22  */
23
24 /*** MODULEINFO
25         <depend>zlib</depend>
26         <use>crypto</use>
27  ***/
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/network.h"
34 #include <sys/ioctl.h>
35 #include <zlib.h>
36 #include <sys/signal.h>
37 #include <pthread.h>
38 #include <net/if.h>
39
40 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__) 
41 #include <net/if_dl.h>
42 #include <ifaddrs.h>
43 #endif
44
45 #include "asterisk/file.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/config.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/module.h"
51 #include "asterisk/frame.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/lock.h"
54 #include "asterisk/md5.h"
55 #include "asterisk/dundi.h"
56 #include "asterisk/sched.h"
57 #include "asterisk/io.h"
58 #include "asterisk/utils.h"
59 #include "asterisk/netsock.h"
60 #include "asterisk/crypto.h"
61 #include "asterisk/astdb.h"
62 #include "asterisk/acl.h"
63 #include "asterisk/aes.h"
64 #include "asterisk/app.h"
65
66 #include "dundi-parser.h"
67
68 #define MAX_RESULTS     64
69
70 #define MAX_PACKET_SIZE 8192
71
72 #define MAX_WEIGHT 59999
73
74 #define DUNDI_MODEL_INBOUND             (1 << 0)
75 #define DUNDI_MODEL_OUTBOUND    (1 << 1)
76 #define DUNDI_MODEL_SYMMETRIC   (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
77
78 /*! Keep times of last 10 lookups */
79 #define DUNDI_TIMING_HISTORY    10
80
81 enum {
82         FLAG_ISREG =       (1 << 0),  /*!< Transaction is register request */
83         FLAG_DEAD =        (1 << 1),  /*!< Transaction is dead */
84         FLAG_FINAL =       (1 << 2),  /*!< Transaction has final message sent */
85         FLAG_ISQUAL =      (1 << 3),  /*!< Transaction is a qualification */
86         FLAG_ENCRYPT =     (1 << 4),  /*!< Transaction is encrypted wiht ECX/DCX */
87         FLAG_SENDFULLKEY = (1 << 5),  /*!< Send full key on transaction */
88         FLAG_STOREHIST =   (1 << 6),  /*!< Record historic performance */
89 };
90
91 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
92
93 #if 0
94 #define DUNDI_SECRET_TIME 15    /* Testing only */
95 #else
96 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
97 #endif
98
99 static struct io_context *io;
100 static struct sched_context *sched;
101 static int netsocket = -1;
102 static pthread_t netthreadid = AST_PTHREADT_NULL;
103 static pthread_t precachethreadid = AST_PTHREADT_NULL;
104 static unsigned int tos = 0;
105 static int dundidebug = 0;
106 static int authdebug = 0;
107 static int dundi_ttl = DUNDI_DEFAULT_TTL;
108 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
109 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
110 static int global_autokilltimeout = 0;
111 static dundi_eid global_eid;
112 static int default_expiration = 60;
113 static int global_storehistory = 0;
114 static char dept[80];
115 static char org[80];
116 static char locality[80];
117 static char stateprov[80];
118 static char country[80];
119 static char email[80];
120 static char phone[80];
121 static char secretpath[80];
122 static char cursecret[80];
123 static char ipaddr[80];
124 static time_t rotatetime;
125 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
126 static int dundi_shutdown = 0;
127
128 struct permission {
129         AST_LIST_ENTRY(permission) list;
130         int allow;
131         char name[0];
132 };
133
134 struct dundi_packet {
135         AST_LIST_ENTRY(dundi_packet) list;
136         struct dundi_hdr *h;
137         int datalen;
138         struct dundi_transaction *parent;
139         int retransid;
140         int retrans;
141         unsigned char data[0];
142 };
143
144 struct dundi_hint_metadata {
145         unsigned short flags;
146         char exten[AST_MAX_EXTENSION];
147 };
148
149 struct dundi_precache_queue {
150         AST_LIST_ENTRY(dundi_precache_queue) list;
151         char *context;
152         time_t expiration;
153         char number[0];
154 };
155
156 struct dundi_request;
157
158 struct dundi_transaction {
159         struct sockaddr_in addr;                       /*!< Other end of transaction */
160         struct timeval start;                          /*!< When this transaction was created */
161         dundi_eid eids[DUNDI_MAX_STACK + 1];
162         int eidcount;                                  /*!< Number of eids in eids */
163         dundi_eid us_eid;                              /*!< Our EID, to them */
164         dundi_eid them_eid;                            /*!< Their EID, to us */
165         ast_aes_encrypt_key ecx;                       /*!< AES 128 Encryption context */
166         ast_aes_decrypt_key dcx;                       /*!< AES 128 Decryption context */
167         unsigned int flags;                            /*!< Has final packet been sent */
168         int ttl;                                       /*!< Remaining TTL for queries on this one */
169         int thread;                                    /*!< We have a calling thread */
170         int retranstimer;                              /*!< How long to wait before retransmissions */
171         int autokillid;                                /*!< ID to kill connection if answer doesn't come back fast enough */
172         int autokilltimeout;                           /*!< Recommended timeout for autokill */
173         unsigned short strans;                         /*!< Our transaction identifier */
174         unsigned short dtrans;                         /*!< Their transaction identifer */
175         unsigned char iseqno;                          /*!< Next expected received seqno */
176         unsigned char oiseqno;                         /*!< Last received incoming seqno */
177         unsigned char oseqno;                          /*!< Next transmitted seqno */
178         unsigned char aseqno;                          /*!< Last acknowledge seqno */
179         AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets;  /*!< Packets to be retransmitted */
180         struct packetlist lasttrans;                   /*!< Last transmitted / ACK'd packet */
181         struct dundi_request *parent;                  /*!< Parent request (if there is one) */
182         AST_LIST_ENTRY(dundi_transaction) parentlist;  /*!< Next with respect to the parent */
183         AST_LIST_ENTRY(dundi_transaction) all;         /*!< Next with respect to all DUNDi transactions */
184 };
185
186 struct dundi_request {
187         char dcontext[AST_MAX_EXTENSION];
188         char number[AST_MAX_EXTENSION];
189         dundi_eid query_eid;
190         dundi_eid root_eid;
191         struct dundi_result *dr;
192         struct dundi_entity_info *dei;
193         struct dundi_hint_metadata *hmd;
194         int maxcount;
195         int respcount;
196         int expiration;
197         int cbypass;
198         int pfds[2];
199         unsigned long crc32;                              /*!< CRC-32 of all but root EID's in avoid list */
200         AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans;  /*!< Transactions */
201         AST_LIST_ENTRY(dundi_request) list;
202 };
203
204 struct dundi_mapping {
205         char dcontext[AST_MAX_EXTENSION];
206         char lcontext[AST_MAX_EXTENSION];
207         int _weight;
208         char *weightstr;
209         int options;
210         int tech;
211         int dead;
212         char dest[AST_MAX_EXTENSION];
213         AST_LIST_ENTRY(dundi_mapping) list;
214 };
215
216 struct dundi_peer {
217         dundi_eid eid;
218         struct sockaddr_in addr;               /*!< Address of DUNDi peer */
219         AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
220         struct permissionlist include;
221         dundi_eid us_eid;
222         char inkey[80];
223         char outkey[80];
224         int dead;
225         int registerid;
226         int qualifyid;
227         int sentfullkey;
228         int order;
229         unsigned char txenckey[256];           /*!< Transmitted encrypted key + sig */
230         unsigned char rxenckey[256];           /*!< Cache received encrypted key + sig */
231         unsigned long us_keycrc32;             /*!< CRC-32 of our key */
232         ast_aes_encrypt_key us_ecx;            /*!< Cached AES 128 Encryption context */
233         ast_aes_decrypt_key us_dcx;            /*!< Cached AES 128 Decryption context */
234         unsigned long them_keycrc32;           /*!< CRC-32 of our key */
235         ast_aes_encrypt_key them_ecx;          /*!< Cached AES 128 Encryption context */
236         ast_aes_decrypt_key them_dcx;          /*!< Cached AES 128 Decryption context */
237         time_t keyexpire;                      /*!< When to expire/recreate key */
238         int registerexpire;
239         int lookuptimes[DUNDI_TIMING_HISTORY];
240         char *lookups[DUNDI_TIMING_HISTORY];
241         int avgms;
242         struct dundi_transaction *regtrans;    /*!< Registration transaction */
243         struct dundi_transaction *qualtrans;   /*!< Qualify transaction */
244         int model;                             /*!< Pull model */
245         int pcmodel;                           /*!< Push/precache model */
246         /*! Dynamic peers register with us */
247         unsigned int dynamic:1;
248         int lastms;                            /*!< Last measured latency */
249         int maxms;                             /*!< Max permissible latency */
250         struct timeval qualtx;                 /*!< Time of transmit */
251         AST_LIST_ENTRY(dundi_peer) list;
252 };
253
254 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
255 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
256 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
257 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
258 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
259
260 /*!
261  * \brief Wildcard peer
262  *
263  * This peer is created if the [*] entry is specified in dundi.conf
264  */
265 static struct dundi_peer *any_peer;
266
267 static int dundi_xmit(struct dundi_packet *pack);
268
269 static void dundi_debug_output(const char *data)
270 {
271         if (dundidebug)
272                 ast_verbose("%s", data);
273 }
274
275 static void dundi_error_output(const char *data)
276 {
277         ast_log(LOG_WARNING, "%s", data);
278 }
279
280 static int has_permission(struct permissionlist *permlist, char *cont)
281 {
282         struct permission *perm;
283         int res = 0;
284
285         AST_LIST_TRAVERSE(permlist, perm, list) {
286                 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
287                         res = perm->allow;
288         }
289
290         return res;
291 }
292
293 static char *tech2str(int tech)
294 {
295         switch(tech) {
296         case DUNDI_PROTO_NONE:
297                 return "None";
298         case DUNDI_PROTO_IAX:
299                 return "IAX2";
300         case DUNDI_PROTO_SIP:
301                 return "SIP";
302         case DUNDI_PROTO_H323:
303                 return "H323";
304         default:
305                 return "Unknown";
306         }
307 }
308
309 static int str2tech(char *str)
310 {
311         if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 
312                 return DUNDI_PROTO_IAX;
313         else if (!strcasecmp(str, "SIP"))
314                 return DUNDI_PROTO_SIP;
315         else if (!strcasecmp(str, "H323"))
316                 return DUNDI_PROTO_H323;
317         else
318                 return -1;
319 }
320
321 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[]);
322 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
323 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
324 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
325 {
326         struct dundi_transaction *trans;
327
328         /* Look for an exact match first */
329         AST_LIST_TRAVERSE(&alltrans, trans, all) {
330                 if (!inaddrcmp(&trans->addr, sin) && 
331                      ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
332                           ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
333                           if (hdr->strans)
334                                   trans->dtrans = ntohs(hdr->strans) & 32767;
335                           return trans;
336                 }
337         }
338         
339         switch(hdr->cmdresp & 0x7f) {
340         case DUNDI_COMMAND_DPDISCOVER:
341         case DUNDI_COMMAND_EIDQUERY:
342         case DUNDI_COMMAND_PRECACHERQ:
343         case DUNDI_COMMAND_REGREQ:
344         case DUNDI_COMMAND_NULL:
345         case DUNDI_COMMAND_ENCRYPT:
346                 if (!hdr->strans)
347                         break;
348                 /* Create new transaction */
349                 if (!(trans = create_transaction(NULL)))
350                         break;
351                 memcpy(&trans->addr, sin, sizeof(trans->addr));
352                 trans->dtrans = ntohs(hdr->strans) & 32767;
353         default:
354                 break;
355         }
356         
357         return trans;
358 }
359
360 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
361
362 static int dundi_ack(struct dundi_transaction *trans, int final)
363 {
364         return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
365 }
366 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
367 {
368         struct {
369                 struct dundi_packet pack;
370                 struct dundi_hdr hdr;
371         } tmp;
372         struct dundi_transaction trans;
373         /* Never respond to an INVALID with another INVALID */
374         if (h->cmdresp == DUNDI_COMMAND_INVALID)
375                 return;
376         memset(&tmp, 0, sizeof(tmp));
377         memset(&trans, 0, sizeof(trans));
378         memcpy(&trans.addr, sin, sizeof(trans.addr));
379         tmp.hdr.strans = h->dtrans;
380         tmp.hdr.dtrans = h->strans;
381         tmp.hdr.iseqno = h->oseqno;
382         tmp.hdr.oseqno = h->iseqno;
383         tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
384         tmp.hdr.cmdflags = 0;
385         tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
386         tmp.pack.datalen = sizeof(struct dundi_hdr);
387         tmp.pack.parent = &trans;
388         dundi_xmit(&tmp.pack);
389 }
390
391 static int get_trans_id(void)
392 {
393         struct dundi_transaction *t;
394         int stid = (ast_random() % 32766) + 1;
395         int tid = stid;
396
397         do {
398                 AST_LIST_TRAVERSE(&alltrans, t, all) {
399                         if (t->strans == tid) 
400                                 break;
401                 }
402                 if (!t)
403                         return tid;
404                 tid = (tid % 32766) + 1;
405         } while (tid != stid);
406
407         return 0;
408 }
409
410 static int reset_transaction(struct dundi_transaction *trans)
411 {
412         int tid;
413         tid = get_trans_id();
414         if (tid < 1)
415                 return -1;
416         trans->strans = tid;
417         trans->dtrans = 0;
418         trans->iseqno = 0;
419         trans->oiseqno = 0;
420         trans->oseqno = 0;
421         trans->aseqno = 0;
422         ast_clear_flag(trans, FLAG_FINAL);      
423         return 0;
424 }
425
426 static struct dundi_peer *find_peer(dundi_eid *eid)
427 {
428         struct dundi_peer *cur = NULL;
429
430         if (!eid)
431                 eid = &empty_eid;
432         
433         AST_LIST_TRAVERSE(&peers, cur, list) {
434                 if (!ast_eid_cmp(&cur->eid,eid))
435                         break;
436         }
437
438         if (!cur && any_peer)
439                 cur = any_peer;
440
441         return cur;
442 }
443
444 static void build_iv(unsigned char *iv)
445 {
446         /* XXX Would be nice to be more random XXX */
447         unsigned int *fluffy;
448         int x;
449         fluffy = (unsigned int *)(iv);
450         for (x=0;x<4;x++)
451                 fluffy[x] = ast_random();
452 }
453
454 struct dundi_query_state {
455         dundi_eid *eids[DUNDI_MAX_STACK + 1]; 
456         int directs[DUNDI_MAX_STACK + 1]; 
457         dundi_eid reqeid;
458         char called_context[AST_MAX_EXTENSION];
459         char called_number[AST_MAX_EXTENSION];
460         struct dundi_mapping *maps;
461         int nummaps;
462         int nocache;
463         struct dundi_transaction *trans;
464         void *chal;
465         int challen;
466         int ttl;
467         char fluffy[0];
468 };
469
470 static int get_mapping_weight(struct dundi_mapping *map)
471 {
472         char buf[32];
473
474         buf[0] = 0;
475         if (map->weightstr) {
476                 pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
477                 if (sscanf(buf, "%d", &map->_weight) != 1)
478                         map->_weight = MAX_WEIGHT;
479         }
480
481         return map->_weight;
482 }
483
484 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)
485 {
486         struct ast_flags flags = {0};
487         int x;
488         if (!ast_strlen_zero(map->lcontext)) {
489                 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
490                         ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
491                 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
492                         ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
493                 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
494                         ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
495                 if (ast_ignore_pattern(map->lcontext, called_number))
496                         ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
497
498                 /* Clearly we can't say 'don't ask' anymore if we found anything... */
499                 if (ast_test_flag(&flags, AST_FLAGS_ALL)) 
500                         ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
501
502                 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
503                         /* Skip partial answers */
504                         ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
505                 }
506                 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
507                         struct varshead headp;
508                         struct ast_var_t *newvariable;
509                         ast_set_flag(&flags, map->options & 0xffff);
510                         ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
511                         dr[anscnt].techint = map->tech;
512                         dr[anscnt].weight = get_mapping_weight(map);
513                         dr[anscnt].expiration = dundi_cache_time;
514                         ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
515                         dr[anscnt].eid = *us_eid;
516                         ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
517                         if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
518                                 AST_LIST_HEAD_INIT_NOLOCK(&headp);
519                                 newvariable = ast_var_assign("NUMBER", called_number);
520                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
521                                 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
522                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
523                                 newvariable = ast_var_assign("SECRET", cursecret);
524                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
525                                 newvariable = ast_var_assign("IPADDR", ipaddr);
526                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
527                                 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
528                                 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
529                                         ast_var_delete(newvariable);
530                         } else
531                                 dr[anscnt].dest[0] = '\0';
532                         anscnt++;
533                 } else {
534                         /* No answers...  Find the fewest number of digits from the
535                            number for which we have no answer. */
536                         char tmp[AST_MAX_EXTENSION + 1] = "";
537                         for (x = 0; x < (sizeof(tmp) - 1); x++) {
538                                 tmp[x] = called_number[x];
539                                 if (!tmp[x])
540                                         break;
541                                 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
542                                         /* Oops found something we can't match.  If this is longer
543                                            than the running hint, we have to consider it */
544                                         if (strlen(tmp) > strlen(hmd->exten)) {
545                                                 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
546                                         }
547                                         break;
548                                 }
549                         }
550                 }
551         }
552         return anscnt;
553 }
554
555 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
556
557 static void *dundi_lookup_thread(void *data)
558 {
559         struct dundi_query_state *st = data;
560         struct dundi_result dr[MAX_RESULTS];
561         struct dundi_ie_data ied;
562         struct dundi_hint_metadata hmd;
563         char eid_str[20];
564         int res, x;
565         int ouranswers=0;
566         int max = 999999;
567         int expiration = dundi_cache_time;
568
569         ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
570                         st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
571         memset(&ied, 0, sizeof(ied));
572         memset(&dr, 0, sizeof(dr));
573         memset(&hmd, 0, sizeof(hmd));
574         /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
575         hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
576         for (x=0;x<st->nummaps;x++)
577                 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
578         if (ouranswers < 0)
579                 ouranswers = 0;
580         for (x=0;x<ouranswers;x++) {
581                 if (dr[x].weight < max)
582                         max = dr[x].weight;
583         }
584                 
585         if (max) {
586                 /* If we do not have a canonical result, keep looking */
587                 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);
588                 if (res > 0) {
589                         /* Append answer in result */
590                         ouranswers += res;
591                 } else {
592                         if ((res < -1) && (!ouranswers))
593                                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
594                 }
595         }
596         AST_LIST_LOCK(&peers);
597         /* Truncate if "don't ask" isn't present */
598         if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
599                 hmd.exten[0] = '\0';
600         if (ast_test_flag(st->trans, FLAG_DEAD)) {
601                 ast_debug(1, "Our transaction went away!\n");
602                 st->trans->thread = 0;
603                 destroy_trans(st->trans, 0);
604         } else {
605                 for (x=0;x<ouranswers;x++) {
606                         /* Add answers */
607                         if (dr[x].expiration && (expiration > dr[x].expiration))
608                                 expiration = dr[x].expiration;
609                         dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
610                 }
611                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
612                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
613                 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
614                 st->trans->thread = 0;
615         }
616         AST_LIST_UNLOCK(&peers);
617         ast_free(st);
618         return NULL;    
619 }
620
621 static void *dundi_precache_thread(void *data)
622 {
623         struct dundi_query_state *st = data;
624         struct dundi_ie_data ied;
625         struct dundi_hint_metadata hmd;
626         char eid_str[20];
627
628         ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 
629                 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
630         memset(&ied, 0, sizeof(ied));
631
632         /* Now produce precache */
633         dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
634
635         AST_LIST_LOCK(&peers);
636         /* Truncate if "don't ask" isn't present */
637         if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
638                 hmd.exten[0] = '\0';
639         if (ast_test_flag(st->trans, FLAG_DEAD)) {
640                 ast_debug(1, "Our transaction went away!\n");
641                 st->trans->thread = 0;
642                 destroy_trans(st->trans, 0);
643         } else {
644                 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
645                 st->trans->thread = 0;
646         }
647         AST_LIST_UNLOCK(&peers);
648         ast_free(st);
649         return NULL;    
650 }
651
652 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[]);
653
654 static void *dundi_query_thread(void *data)
655 {
656         struct dundi_query_state *st = data;
657         struct dundi_entity_info dei;
658         struct dundi_ie_data ied;
659         struct dundi_hint_metadata hmd;
660         char eid_str[20];
661         int res;
662
663         ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
664                 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
665         memset(&ied, 0, sizeof(ied));
666         memset(&dei, 0, sizeof(dei));
667         memset(&hmd, 0, sizeof(hmd));
668         if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
669                 /* Ooh, it's us! */
670                 ast_debug(1, "Neat, someone look for us!\n");
671                 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
672                 ast_copy_string(dei.org, org, sizeof(dei.org));
673                 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
674                 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
675                 ast_copy_string(dei.country, country, sizeof(dei.country));
676                 ast_copy_string(dei.email, email, sizeof(dei.email));
677                 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
678                 res = 1;
679         } else {
680                 /* If we do not have a canonical result, keep looking */
681                 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
682         }
683         AST_LIST_LOCK(&peers);
684         if (ast_test_flag(st->trans, FLAG_DEAD)) {
685                 ast_debug(1, "Our transaction went away!\n");
686                 st->trans->thread = 0;
687                 destroy_trans(st->trans, 0);
688         } else {
689                 if (res) {
690                         dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
691                         dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
692                         dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
693                         dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
694                         dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
695                         dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
696                         dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
697                         if (!ast_strlen_zero(dei.ipaddr))
698                                 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
699                 }
700                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
701                 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
702                 st->trans->thread = 0;
703         }
704         AST_LIST_UNLOCK(&peers);
705         ast_free(st);
706         return NULL;    
707 }
708
709 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
710 {
711         struct dundi_query_state *st;
712         int totallen;
713         int x;
714         int skipfirst=0;
715         char eid_str[20];
716         char *s;
717         pthread_t lookupthread;
718
719         if (ies->eidcount > 1) {
720                 /* Since it is a requirement that the first EID is the authenticating host
721                    and the last EID is the root, it is permissible that the first and last EID
722                    could be the same.  In that case, we should go ahead copy only the "root" section
723                    since we will not need it for authentication. */
724                 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
725                         skipfirst = 1;
726         }
727         totallen = sizeof(struct dundi_query_state);
728         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
729         st = ast_calloc(1, totallen);
730         if (st) {
731                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
732                 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
733                 st->trans = trans;
734                 st->ttl = ies->ttl - 1;
735                 if (st->ttl < 0)
736                         st->ttl = 0;
737                 s = st->fluffy;
738                 for (x=skipfirst;ies->eids[x];x++) {
739                         st->eids[x-skipfirst] = (dundi_eid *)s;
740                         *st->eids[x-skipfirst] = *ies->eids[x];
741                         s += sizeof(dundi_eid);
742                 }
743                 ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
744
745                 trans->thread = 1;
746                 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
747                         struct dundi_ie_data ied = { 0, };
748                         trans->thread = 0;
749                         ast_log(LOG_WARNING, "Unable to create thread!\n");
750                         ast_free(st);
751                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
752                         dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
753                         return -1;
754                 }
755         } else {
756                 struct dundi_ie_data ied = { 0, };
757                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
758                 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
759                 return -1;
760         }
761         return 0;
762 }
763
764 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
765 {
766         int unaffected;
767         char key1[256];
768         char key2[256];
769         char eidpeer_str[20];
770         char eidroot_str[20];
771         char data[80];
772         time_t timeout;
773
774         if (expiration < 0)
775                 expiration = dundi_cache_time;
776
777         /* Only cache hint if "don't ask" is there... */
778         if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))    
779                 return 0;
780
781         unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
782
783         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
784         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
785         snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
786         snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
787
788         time(&timeout);
789         timeout += expiration;
790         snprintf(data, sizeof(data), "%ld|", (long)(timeout));
791         
792         ast_db_put("dundi/cache", key1, data);
793         ast_debug(1, "Caching hint at '%s'\n", key1);
794         ast_db_put("dundi/cache", key2, data);
795         ast_debug(1, "Caching hint at '%s'\n", key2);
796         return 0;
797 }
798
799 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
800 {
801         int x;
802         char key1[256];
803         char key2[256];
804         char data[1024];
805         char eidpeer_str[20];
806         char eidroot_str[20];
807         time_t timeout;
808
809         if (expiration < 1)     
810                 expiration = dundi_cache_time;
811
812         /* Keep pushes a little longer, cut pulls a little short */
813         if (push)
814                 expiration += 10;
815         else
816                 expiration -= 10;
817         if (expiration < 1)
818                 expiration = 1;
819         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
820         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
821         snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
822         snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
823         /* Build request string */
824         time(&timeout);
825         timeout += expiration;
826         snprintf(data, sizeof(data), "%ld|", (long)(timeout));
827         for (x=start;x<req->respcount;x++) {
828                 /* Skip anything with an illegal pipe in it */
829                 if (strchr(req->dr[x].dest, '|'))
830                         continue;
831                 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 
832                         req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 
833                         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
834         }
835         ast_db_put("dundi/cache", key1, data);
836         ast_db_put("dundi/cache", key2, data);
837         return 0;
838 }
839
840 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
841 {
842         struct dundi_query_state *st;
843         int totallen;
844         int x,z;
845         struct dundi_ie_data ied;
846         char *s;
847         struct dundi_result dr2[MAX_RESULTS];
848         struct dundi_request dr;
849         struct dundi_hint_metadata hmd;
850
851         struct dundi_mapping *cur;
852         int mapcount;
853         int skipfirst = 0;
854         
855         pthread_t lookupthread;
856
857         memset(&dr2, 0, sizeof(dr2));
858         memset(&dr, 0, sizeof(dr));
859         memset(&hmd, 0, sizeof(hmd));
860         
861         /* Forge request structure to hold answers for cache */
862         hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
863         dr.dr = dr2;
864         dr.maxcount = MAX_RESULTS;
865         dr.expiration = dundi_cache_time;
866         dr.hmd = &hmd;
867         dr.pfds[0] = dr.pfds[1] = -1;
868         trans->parent = &dr;
869         ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
870         ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
871         
872         for (x=0;x<ies->anscount;x++) {
873                 if (trans->parent->respcount < trans->parent->maxcount) {
874                         /* Make sure it's not already there */
875                         for (z=0;z<trans->parent->respcount;z++) {
876                                 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
877                                     !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data)) 
878                                                 break;
879                         }
880                         if (z == trans->parent->respcount) {
881                                 /* Copy into parent responses */
882                                 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
883                                 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
884                                 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
885                                 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
886                                 if (ies->expiration > 0)
887                                         trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
888                                 else
889                                         trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
890                                 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
891                                         sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
892                                         &ies->answers[x]->eid);
893                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
894                                         sizeof(trans->parent->dr[trans->parent->respcount].dest));
895                                         ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
896                                         sizeof(trans->parent->dr[trans->parent->respcount].tech));
897                                 trans->parent->respcount++;
898                                 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK); 
899                         } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
900                                 /* Update weight if appropriate */
901                                 trans->parent->dr[z].weight = ies->answers[x]->weight;
902                         }
903                 } else
904                         ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
905                                 trans->parent->number, trans->parent->dcontext);
906
907         }
908         /* Save all the results (if any) we had.  Even if no results, still cache lookup. */
909         cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
910         if (ies->hint)
911                 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
912
913         totallen = sizeof(struct dundi_query_state);
914         /* Count matching map entries */
915         mapcount = 0;
916         AST_LIST_TRAVERSE(&mappings, cur, list) {
917                 if (!strcasecmp(cur->dcontext, ccontext))
918                         mapcount++;
919         }
920         
921         /* If no maps, return -1 immediately */
922         if (!mapcount)
923                 return -1;
924
925         if (ies->eidcount > 1) {
926                 /* Since it is a requirement that the first EID is the authenticating host
927                    and the last EID is the root, it is permissible that the first and last EID
928                    could be the same.  In that case, we should go ahead copy only the "root" section
929                    since we will not need it for authentication. */
930                 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
931                         skipfirst = 1;
932         }
933
934         /* Prepare to run a query and then propagate that as necessary */
935         totallen += mapcount * sizeof(struct dundi_mapping);
936         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
937         st = ast_calloc(1, totallen);
938         if (st) {
939                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
940                 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
941                 st->trans = trans;
942                 st->ttl = ies->ttl - 1;
943                 st->nocache = ies->cbypass;
944                 if (st->ttl < 0)
945                         st->ttl = 0;
946                 s = st->fluffy;
947                 for (x=skipfirst;ies->eids[x];x++) {
948                         st->eids[x-skipfirst] = (dundi_eid *)s;
949                         *st->eids[x-skipfirst] = *ies->eids[x];
950                         st->directs[x-skipfirst] = ies->eid_direct[x];
951                         s += sizeof(dundi_eid);
952                 }
953                 /* Append mappings */
954                 x = 0;
955                 st->maps = (struct dundi_mapping *)s;
956                 AST_LIST_TRAVERSE(&mappings, cur, list) {
957                         if (!strcasecmp(cur->dcontext, ccontext)) {
958                                 if (x < mapcount) {
959                                         st->maps[x] = *cur;
960                                         st->maps[x].list.next = NULL;
961                                         x++;
962                                 }
963                         }
964                 }
965                 st->nummaps = mapcount;
966                 ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
967                 trans->thread = 1;
968                 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
969                         trans->thread = 0;
970                         ast_log(LOG_WARNING, "Unable to create thread!\n");
971                         ast_free(st);
972                         memset(&ied, 0, sizeof(ied));
973                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
974                         dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
975                         return -1;
976                 }
977         } else {
978                 ast_log(LOG_WARNING, "Out of memory!\n");
979                 memset(&ied, 0, sizeof(ied));
980                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
981                 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
982                 return -1;
983         }
984         return 0;
985 }
986
987 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
988 {
989         struct dundi_query_state *st;
990         int totallen;
991         int x;
992         struct dundi_ie_data ied;
993         char *s;
994         struct dundi_mapping *cur;
995         int mapcount = 0;
996         int skipfirst = 0;
997         
998         pthread_t lookupthread;
999         totallen = sizeof(struct dundi_query_state);
1000         /* Count matching map entries */
1001         AST_LIST_TRAVERSE(&mappings, cur, list) {
1002                 if (!strcasecmp(cur->dcontext, ccontext))
1003                         mapcount++;
1004         }
1005         /* If no maps, return -1 immediately */
1006         if (!mapcount)
1007                 return -1;
1008
1009         if (ies->eidcount > 1) {
1010                 /* Since it is a requirement that the first EID is the authenticating host
1011                    and the last EID is the root, it is permissible that the first and last EID
1012                    could be the same.  In that case, we should go ahead copy only the "root" section
1013                    since we will not need it for authentication. */
1014                 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1015                         skipfirst = 1;
1016         }
1017
1018         totallen += mapcount * sizeof(struct dundi_mapping);
1019         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1020         st = ast_calloc(1, totallen);
1021         if (st) {
1022                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1023                 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1024                 st->trans = trans;
1025                 st->ttl = ies->ttl - 1;
1026                 st->nocache = ies->cbypass;
1027                 if (st->ttl < 0)
1028                         st->ttl = 0;
1029                 s = st->fluffy;
1030                 for (x=skipfirst;ies->eids[x];x++) {
1031                         st->eids[x-skipfirst] = (dundi_eid *)s;
1032                         *st->eids[x-skipfirst] = *ies->eids[x];
1033                         st->directs[x-skipfirst] = ies->eid_direct[x];
1034                         s += sizeof(dundi_eid);
1035                 }
1036                 /* Append mappings */
1037                 x = 0;
1038                 st->maps = (struct dundi_mapping *)s;
1039                 AST_LIST_TRAVERSE(&mappings, cur, list) {
1040                         if (!strcasecmp(cur->dcontext, ccontext)) {
1041                                 if (x < mapcount) {
1042                                         st->maps[x] = *cur;
1043                                         st->maps[x].list.next = NULL;
1044                                         x++;
1045                                 }
1046                         }
1047                 }
1048                 st->nummaps = mapcount;
1049                 ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1050                 trans->thread = 1;
1051                 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1052                         trans->thread = 0;
1053                         ast_log(LOG_WARNING, "Unable to create thread!\n");
1054                         ast_free(st);
1055                         memset(&ied, 0, sizeof(ied));
1056                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1057                         dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1058                         return -1;
1059                 }
1060         } else {
1061                 ast_log(LOG_WARNING, "Out of memory!\n");
1062                 memset(&ied, 0, sizeof(ied));
1063                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1064                 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1065                 return -1;
1066         }
1067         return 0;
1068 }
1069
1070 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1071 {
1072         char data[1024];
1073         char *ptr, *term, *src;
1074         int tech;
1075         struct ast_flags flags;
1076         int weight;
1077         int length;
1078         int z;
1079         char fs[256];
1080
1081         /* Build request string */
1082         if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1083                 time_t timeout;
1084                 ptr = data;
1085                 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1086                         int expiration = timeout - now;
1087                         if (expiration > 0) {
1088                                 ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
1089                                 ptr += length + 1;
1090                                 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1091                                         ptr += length;
1092                                         term = strchr(ptr, '|');
1093                                         if (term) {
1094                                                 *term = '\0';
1095                                                 src = strrchr(ptr, '/');
1096                                                 if (src) {
1097                                                         *src = '\0';
1098                                                         src++;
1099                                                 } else
1100                                                         src = "";
1101                                                 ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n", 
1102                                                         tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1103                                                 /* Make sure it's not already there */
1104                                                 for (z=0;z<req->respcount;z++) {
1105                                                         if ((req->dr[z].techint == tech) &&
1106                                                             !strcmp(req->dr[z].dest, ptr)) 
1107                                                                         break;
1108                                                 }
1109                                                 if (z == req->respcount) {
1110                                                         /* Copy into parent responses */
1111                                                         ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);      
1112                                                         req->dr[req->respcount].weight = weight;
1113                                                         req->dr[req->respcount].techint = tech;
1114                                                         req->dr[req->respcount].expiration = expiration;
1115                                                         dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1116                                                         ast_eid_to_str(req->dr[req->respcount].eid_str, 
1117                                                                 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1118                                                         ast_copy_string(req->dr[req->respcount].dest, ptr,
1119                                                                 sizeof(req->dr[req->respcount].dest));
1120                                                         ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1121                                                                 sizeof(req->dr[req->respcount].tech));
1122                                                         req->respcount++;
1123                                                         ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);   
1124                                                 } else if (req->dr[z].weight > weight)
1125                                                         req->dr[z].weight = weight;
1126                                                 ptr = term + 1;
1127                                         }
1128                                 }
1129                                 /* We found *something* cached */
1130                                 if (expiration < *lowexpiration)
1131                                         *lowexpiration = expiration;
1132                                 return 1;
1133                         } else 
1134                                 ast_db_del("dundi/cache", key);
1135                 } else 
1136                         ast_db_del("dundi/cache", key);
1137         }
1138                 
1139         return 0;
1140 }
1141
1142 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1143 {
1144         char key[256];
1145         char eid_str[20];
1146         char eidroot_str[20];
1147         time_t now;
1148         int res=0;
1149         int res2=0;
1150         char eid_str_full[20];
1151         char tmp[256]="";
1152         int x;
1153
1154         time(&now);
1155         dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1156         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1157         ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1158         snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1159         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1160         snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1161         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1162         snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1163         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1164         x = 0;
1165         if (!req->respcount) {
1166                 while(!res2) {
1167                         /* Look and see if we have a hint that would preclude us from looking at this
1168                            peer for this number. */
1169                         if (!(tmp[x] = req->number[x])) 
1170                                 break;
1171                         x++;
1172                         /* Check for hints */
1173                         snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1174                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1175                         snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1176                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1177                         snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1178                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1179                         if (res2) {
1180                                 if (strlen(tmp) > strlen(req->hmd->exten)) {
1181                                         /* Update meta data if appropriate */
1182                                         ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1183                                 }
1184                         }
1185                 }
1186                 res |= res2;
1187         }
1188
1189         return res;
1190 }
1191
1192 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1193
1194 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1195 {
1196         if (!trans->addr.sin_addr.s_addr)
1197                 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1198         trans->us_eid = p->us_eid;
1199         trans->them_eid = p->eid;
1200         /* Enable encryption if appropriate */
1201         if (!ast_strlen_zero(p->inkey))
1202                 ast_set_flag(trans, FLAG_ENCRYPT);      
1203         if (p->maxms) {
1204                 trans->autokilltimeout = p->maxms;
1205                 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1206                 if (p->lastms > 1) {
1207                         trans->retranstimer = p->lastms * 2;
1208                         /* Keep it from being silly */
1209                         if (trans->retranstimer < 150)
1210                                 trans->retranstimer = 150;
1211                 }
1212                 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1213                         trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1214         } else
1215                 trans->autokilltimeout = global_autokilltimeout;
1216 }
1217
1218 /*! \note Called with the peers list already locked */
1219 static int do_register_expire(const void *data)
1220 {
1221         struct dundi_peer *peer = (struct dundi_peer *)data;
1222         char eid_str[20];
1223         ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1224         peer->registerexpire = -1;
1225         peer->lastms = 0;
1226         memset(&peer->addr, 0, sizeof(peer->addr));
1227         return 0;
1228 }
1229
1230 static int update_key(struct dundi_peer *peer)
1231 {
1232         unsigned char key[16];
1233         struct ast_key *ekey, *skey;
1234         char eid_str[20];
1235         int res;
1236         if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1237                 build_iv(key);
1238                 ast_aes_encrypt_key(key, &peer->us_ecx);
1239                 ast_aes_decrypt_key(key, &peer->us_dcx);
1240                 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1241                 if (!ekey) {
1242                         ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1243                                 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1244                         return -1;
1245                 }
1246                 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1247                 if (!skey) {
1248                         ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1249                                 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1250                         return -1;
1251                 }
1252                 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1253                         ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1254                         return -1;
1255                 }
1256                 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1257                         ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1258                         return -1;
1259                 }
1260                 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1261                 peer->sentfullkey = 0;
1262                 /* Looks good */
1263                 time(&peer->keyexpire);
1264                 peer->keyexpire += dundi_key_ttl;
1265         }
1266         return 0;
1267 }
1268
1269 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx) 
1270 {
1271         unsigned char curblock[16];
1272         int x;
1273         memcpy(curblock, iv, sizeof(curblock));
1274         while(len > 0) {
1275                 for (x=0;x<16;x++)
1276                         curblock[x] ^= src[x];
1277                 ast_aes_encrypt(curblock, dst, ecx);
1278                 memcpy(curblock, dst, sizeof(curblock)); 
1279                 dst += 16;
1280                 src += 16;
1281                 len -= 16;
1282         }
1283         return 0;
1284 }
1285 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx) 
1286 {
1287         unsigned char lastblock[16];
1288         int x;
1289         memcpy(lastblock, iv, sizeof(lastblock));
1290         while(len > 0) {
1291                 ast_aes_decrypt(src, dst, dcx);
1292                 for (x=0;x<16;x++)
1293                         dst[x] ^= lastblock[x];
1294                 memcpy(lastblock, src, sizeof(lastblock));
1295                 dst += 16;
1296                 src += 16;
1297                 len -= 16;
1298         }
1299         return 0;
1300 }
1301
1302 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)
1303 {
1304         int space = *dstlen;
1305         unsigned long bytes;
1306         struct dundi_hdr *h;
1307         unsigned char *decrypt_space;
1308         decrypt_space = alloca(srclen);
1309         if (!decrypt_space)
1310                 return NULL;
1311         decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1312         /* Setup header */
1313         h = (struct dundi_hdr *)dst;
1314         *h = *ohdr;
1315         bytes = space - 6;
1316         if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1317                 ast_debug(1, "Ouch, uncompress failed :(\n");
1318                 return NULL;
1319         }
1320         /* Update length */
1321         *dstlen = bytes + 6;
1322         /* Return new header */
1323         return h;
1324 }
1325
1326 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1327 {
1328         unsigned char *compress_space;
1329         int len;
1330         int res;
1331         unsigned long bytes;
1332         struct dundi_ie_data ied;
1333         struct dundi_peer *peer;
1334         unsigned char iv[16];
1335         len = pack->datalen + pack->datalen / 100 + 42;
1336         compress_space = alloca(len);
1337         if (compress_space) {
1338                 memset(compress_space, 0, len);
1339                 /* We care about everthing save the first 6 bytes of header */
1340                 bytes = len;
1341                 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1342                 if (res != Z_OK) {
1343                         ast_debug(1, "Ouch, compression failed!\n");
1344                         return -1;
1345                 }
1346                 memset(&ied, 0, sizeof(ied));
1347                 /* Say who we are */
1348                 if (!pack->h->iseqno && !pack->h->oseqno) {
1349                         /* Need the key in the first copy */
1350                         if (!(peer = find_peer(&trans->them_eid))) 
1351                                 return -1;
1352                         if (update_key(peer))
1353                                 return -1;
1354                         if (!peer->sentfullkey)
1355                                 ast_set_flag(trans, FLAG_SENDFULLKEY);  
1356                         /* Append key data */
1357                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1358                         if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1359                                 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1360                                 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1361                         } else {
1362                                 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1363                         }
1364                         /* Setup contexts */
1365                         trans->ecx = peer->us_ecx;
1366                         trans->dcx = peer->us_dcx;
1367
1368                         /* We've sent the full key */
1369                         peer->sentfullkey = 1;
1370                 }
1371                 /* Build initialization vector */
1372                 build_iv(iv);
1373                 /* Add the field, rounded up to 16 bytes */
1374                 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1375                 /* Copy the data */
1376                 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1377                         ast_log(LOG_NOTICE, "Final packet too large!\n");
1378                         return -1;
1379                 }
1380                 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1381                 ied.pos += ((bytes + 15) / 16) * 16;
1382                 /* Reconstruct header */
1383                 pack->datalen = sizeof(struct dundi_hdr);
1384                 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1385                 pack->h->cmdflags = 0;
1386                 memcpy(pack->h->ies, ied.buf, ied.pos);
1387                 pack->datalen += ied.pos;
1388                 return 0;
1389         }
1390         return -1;
1391 }
1392
1393 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1394 {
1395         unsigned char dst[128];
1396         int res;
1397         struct ast_key *key, *skey;
1398         char eid_str[20];
1399         ast_debug(1, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1400         if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1401                 /* A match */
1402                 return 1;
1403         } else if (!newkey || !newsig)
1404                 return 0;
1405         if (!memcmp(peer->rxenckey, newkey, 128) &&
1406             !memcmp(peer->rxenckey + 128, newsig, 128)) {
1407                 /* By definition, a match */
1408                 return 1;
1409         }
1410         /* Decrypt key */
1411         key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1412         if (!key) {
1413                 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1414                         peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1415                 return -1;
1416         }
1417
1418         skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1419         if (!skey) {
1420                 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1421                         peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1422                 return -1;
1423         }
1424
1425         /* First check signature */
1426         res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1427         if (res) 
1428                 return 0;
1429
1430         res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1431         if (res != 16) {
1432                 if (res >= 0)
1433                         ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1434                 return 0;
1435         }
1436         /* Decrypted, passes signature */
1437         ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
1438         memcpy(peer->rxenckey, newkey, 128);
1439         memcpy(peer->rxenckey + 128, newsig, 128);
1440         peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1441         ast_aes_decrypt_key(dst, &peer->them_dcx);
1442         ast_aes_encrypt_key(dst, &peer->them_ecx);
1443         return 1;
1444 }
1445
1446 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1447 {
1448         struct permission *cur, *perm;
1449
1450         memcpy(peer_dst, peer_src, sizeof(*peer_dst));
1451         
1452         memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1453         memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1454
1455         AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1456                 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1457                         continue;
1458
1459                 perm->allow = cur->allow;
1460                 strcpy(perm->name, cur->name);
1461
1462                 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1463         }
1464
1465         AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1466                 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1467                         continue;
1468
1469                 perm->allow = cur->allow;
1470                 strcpy(perm->name, cur->name);
1471
1472                 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1473         }
1474 }
1475
1476 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1477 {
1478         /* Handle canonical command / response */
1479         int final = hdr->cmdresp & 0x80;
1480         int cmd = hdr->cmdresp & 0x7f;
1481         int x,y,z;
1482         int resp;
1483         int res;
1484         int authpass=0;
1485         unsigned char *bufcpy;
1486 #ifdef LOW_MEMORY
1487         struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
1488 #else
1489         struct dundi_ie_data _ied = {
1490                 .pos = 0,       
1491         };
1492         struct dundi_ie_data *ied = &_ied;
1493 #endif
1494         struct dundi_ies ies = {
1495                 .eidcount = 0,  
1496         };
1497         struct dundi_peer *peer = NULL;
1498         char eid_str[20];
1499         char eid_str2[20];
1500         int retval = -1;
1501
1502         if (!ied) {
1503                 return -1;
1504         }
1505
1506         if (datalen) {
1507                 bufcpy = alloca(datalen);
1508                 if (!bufcpy) {
1509                         goto return_cleanup;
1510                 }
1511                 /* Make a copy for parsing */
1512                 memcpy(bufcpy, hdr->ies, datalen);
1513                 ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1514                 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1515                         ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1516                         goto return_cleanup;
1517                 }
1518         }
1519         switch(cmd) {
1520         case DUNDI_COMMAND_DPDISCOVER:
1521         case DUNDI_COMMAND_EIDQUERY:
1522         case DUNDI_COMMAND_PRECACHERQ:
1523                 if (cmd == DUNDI_COMMAND_EIDQUERY)
1524                         resp = DUNDI_COMMAND_EIDRESPONSE;
1525                 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1526                         resp = DUNDI_COMMAND_PRECACHERP;
1527                 else
1528                         resp = DUNDI_COMMAND_DPRESPONSE;
1529                 /* A dialplan or entity discover -- qualify by highest level entity */
1530                 peer = find_peer(ies.eids[0]);
1531                 if (!peer) {
1532                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1533                         dundi_send(trans, resp, 0, 1, ied);
1534                 } else {
1535                         int hasauth = 0;
1536                         trans->us_eid = peer->us_eid;
1537                         if (strlen(peer->inkey)) {
1538                                 hasauth = encrypted;
1539                         } else 
1540                                 hasauth = 1;
1541                         if (hasauth) {
1542                                 /* Okay we're authentiated and all, now we check if they're authorized */
1543                                 if (!ies.called_context)
1544                                         ies.called_context = "e164";
1545                                 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1546                                         res = dundi_answer_entity(trans, &ies, ies.called_context);
1547                                 } else {
1548                                         if (ast_strlen_zero(ies.called_number)) {
1549                                                 /* They're not permitted to access that context */
1550                                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1551                                                 dundi_send(trans, resp, 0, 1, ied);
1552                                         } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) && 
1553                                                    (peer->model & DUNDI_MODEL_INBOUND) && 
1554                                                            has_permission(&peer->permit, ies.called_context)) {
1555                                                 res = dundi_answer_query(trans, &ies, ies.called_context);
1556                                                 if (res < 0) {
1557                                                         /* There is no such dundi context */
1558                                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1559                                                         dundi_send(trans, resp, 0, 1, ied);
1560                                                 }
1561                                         } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) && 
1562                                                    (peer->pcmodel & DUNDI_MODEL_INBOUND) && 
1563                                                            has_permission(&peer->include, ies.called_context)) {
1564                                                 res = dundi_prop_precache(trans, &ies, ies.called_context);
1565                                                 if (res < 0) {
1566                                                         /* There is no such dundi context */
1567                                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1568                                                         dundi_send(trans, resp, 0, 1, ied);
1569                                                 }
1570                                         } else {
1571                                                 /* They're not permitted to access that context */
1572                                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1573                                                 dundi_send(trans, resp, 0, 1, ied);
1574                                         }
1575                                 }
1576                         } else {
1577                                 /* They're not permitted to access that context */
1578                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1579                                 dundi_send(trans, resp, 0, 1, ied);
1580                         }
1581                 }
1582                 break;
1583         case DUNDI_COMMAND_REGREQ:
1584                 /* A register request -- should only have one entity */
1585                 peer = find_peer(ies.eids[0]);
1586                 
1587                 /* if the peer is not found and we have a valid 'any_peer' setting */
1588                 if (any_peer && peer == any_peer) {
1589                         /* copy any_peer into a new peer object */
1590                         peer = ast_calloc(1, sizeof(*peer));
1591                         if (peer) {
1592                                 deep_copy_peer(peer, any_peer);
1593
1594                                 /* set EID to remote EID */
1595                                 peer->eid = *ies.eids[0];
1596
1597                                 AST_LIST_LOCK(&peers);
1598                                 AST_LIST_INSERT_HEAD(&peers, peer, list);
1599                                 AST_LIST_UNLOCK(&peers);
1600                         }
1601                 }
1602
1603                 if (!peer || !peer->dynamic) {
1604                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1605                         dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1606                 } else {
1607                         int hasauth = 0;
1608                         trans->us_eid = peer->us_eid;
1609                         if (!ast_strlen_zero(peer->inkey)) {
1610                                 hasauth = encrypted;
1611                         } else
1612                                 hasauth = 1;
1613                         if (hasauth) {
1614                                 int expire = default_expiration;
1615                                 char data[256];
1616                                 int needqual = 0;
1617                                 AST_SCHED_DEL(sched, peer->registerexpire);
1618                                 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1619                                 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr), 
1620                                         ntohs(trans->addr.sin_port), expire);
1621                                 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1622                                 if (inaddrcmp(&peer->addr, &trans->addr)) {
1623                                         ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
1624                                                         ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
1625                                                         ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1626                                         needqual = 1;
1627                                 }
1628                                         
1629                                 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1630                                 dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
1631                                 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1632                                 if (needqual)
1633                                         qualify_peer(peer, 1);
1634                         }
1635                 }
1636                 break;
1637         case DUNDI_COMMAND_DPRESPONSE:
1638                 /* A dialplan response, lets see what we got... */
1639                 if (ies.cause < 1) {
1640                         /* Success of some sort */
1641                         ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1642                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1643                                 authpass = encrypted;
1644                         } else 
1645                                 authpass = 1;
1646                         if (authpass) {
1647                                 /* Pass back up answers */
1648                                 if (trans->parent && trans->parent->dr) {
1649                                         y = trans->parent->respcount;
1650                                         for (x=0;x<ies.anscount;x++) {
1651                                                 if (trans->parent->respcount < trans->parent->maxcount) {
1652                                                         /* Make sure it's not already there */
1653                                                         for (z=0;z<trans->parent->respcount;z++) {
1654                                                                 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1655                                                                     !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data)) 
1656                                                                                 break;
1657                                                         }
1658                                                         if (z == trans->parent->respcount) {
1659                                                                 /* Copy into parent responses */
1660                                                                 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1661                                                                 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1662                                                                 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1663                                                                 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1664                                                                 if (ies.expiration > 0)
1665                                                                         trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1666                                                                 else
1667                                                                         trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1668                                                                 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
1669                                                                         sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1670                                                                         &ies.answers[x]->eid);
1671                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1672                                                                         sizeof(trans->parent->dr[trans->parent->respcount].dest));
1673                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1674                                                                         sizeof(trans->parent->dr[trans->parent->respcount].tech));
1675                                                                 trans->parent->respcount++;
1676                                                                 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1677                                                         } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1678                                                                 /* Update weight if appropriate */
1679                                                                 trans->parent->dr[z].weight = ies.answers[x]->weight;
1680                                                         }
1681                                                 } else
1682                                                         ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1683                                                                 trans->parent->number, trans->parent->dcontext);
1684                                         }
1685                                         /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
1686                                            the cache know if this request was unaffected by our entity list. */
1687                                         cache_save(&trans->them_eid, trans->parent, y, 
1688                                                         ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1689                                         if (ies.hint) {
1690                                                 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1691                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1692                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1693                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) { 
1694                                                         if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1695                                                                 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data, 
1696                                                                         sizeof(trans->parent->hmd->exten));
1697                                                         }
1698                                                 } else {
1699                                                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1700                                                 }
1701                                         }
1702                                         if (ies.expiration > 0) {
1703                                                 if (trans->parent->expiration > ies.expiration) {
1704                                                         trans->parent->expiration = ies.expiration;
1705                                                 }
1706                                         }
1707                                 }
1708                                 /* Close connection if not final */
1709                                 if (!final) 
1710                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1711                         }
1712                         
1713                 } else {
1714                         /* Auth failure, check for data */
1715                         if (!final) {
1716                                 /* Cancel if they didn't already */
1717                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1718                         }
1719                 }
1720                 break;
1721         case DUNDI_COMMAND_EIDRESPONSE:
1722                 /* A dialplan response, lets see what we got... */
1723                 if (ies.cause < 1) {
1724                         /* Success of some sort */
1725                         ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
1726                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1727                                 authpass = encrypted;
1728                         } else 
1729                                 authpass = 1;
1730                         if (authpass) {
1731                                 /* Pass back up answers */
1732                                 if (trans->parent && trans->parent->dei && ies.q_org) {
1733                                         if (!trans->parent->respcount) {
1734                                                 trans->parent->respcount++;
1735                                                 if (ies.q_dept)
1736                                                         ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1737                                                 if (ies.q_org)
1738                                                         ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1739                                                 if (ies.q_locality)
1740                                                         ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1741                                                 if (ies.q_stateprov)
1742                                                         ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1743                                                 if (ies.q_country)
1744                                                         ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1745                                                 if (ies.q_email)
1746                                                         ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1747                                                 if (ies.q_phone)
1748                                                         ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1749                                                 if (ies.q_ipaddr)
1750                                                         ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1751                                                 if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1752                                                         /* If it's them, update our address */
1753                                                         ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1754                                                 }
1755                                         }
1756                                         if (ies.hint) {
1757                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1758                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1759                                         }
1760                                 }
1761                                 /* Close connection if not final */
1762                                 if (!final) 
1763                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1764                         }
1765                         
1766                 } else {
1767                         /* Auth failure, check for data */
1768                         if (!final) {
1769                                 /* Cancel if they didn't already */
1770                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1771                         }
1772                 }
1773                 break;
1774         case DUNDI_COMMAND_REGRESPONSE:
1775                 /* A dialplan response, lets see what we got... */
1776                 if (ies.cause < 1) {
1777                         int hasauth;
1778                         /* Success of some sort */
1779                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1780                                 hasauth = encrypted;
1781                         } else 
1782                                 hasauth = 1;
1783                         
1784                         if (!hasauth) {
1785                                 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1786                                 if (!final) {
1787                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1788                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
1789                                 }
1790                         } else {
1791                                 ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
1792                                                 ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1793                                 /* Close connection if not final */
1794                                 if (!final) 
1795                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1796                         }
1797                 } else {
1798                         /* Auth failure, cancel if they didn't for some reason */
1799                         if (!final) {
1800                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1801                         }
1802                 }
1803                 break;
1804         case DUNDI_COMMAND_INVALID:
1805         case DUNDI_COMMAND_NULL:
1806         case DUNDI_COMMAND_PRECACHERP:
1807                 /* Do nothing special */
1808                 if (!final) 
1809                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1810                 break;
1811         case DUNDI_COMMAND_ENCREJ:
1812                 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1813                         /* No really, it's over at this point */
1814                         if (!final) 
1815                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1816                 } else {
1817                         /* Send with full key */
1818                         ast_set_flag(trans, FLAG_SENDFULLKEY);
1819                         if (final) {
1820                                 /* Ooops, we got a final message, start by sending ACK... */
1821                                 dundi_ack(trans, hdr->cmdresp & 0x80);
1822                                 trans->aseqno = trans->iseqno;
1823                                 /* Now, we gotta create a new transaction */
1824                                 if (!reset_transaction(trans)) {
1825                                         /* Make sure handle_frame doesn't destroy us */
1826                                         hdr->cmdresp &= 0x7f;
1827                                         /* Parse the message we transmitted */
1828                                         memset(&ies, 0, sizeof(ies));
1829                                         dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1830                                         /* Reconstruct outgoing encrypted packet */
1831                                         memset(ied, 0, sizeof(*ied));
1832                                         dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
1833                                         dundi_ie_append_raw(ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1834                                         dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1835                                         if (ies.encblock) 
1836                                                 dundi_ie_append_encdata(ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1837                                         dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
1838                                         peer->sentfullkey = 1;
1839                                 }
1840                         }
1841                 }
1842                 break;
1843         case DUNDI_COMMAND_ENCRYPT:
1844                 if (!encrypted) {
1845                         /* No nested encryption! */
1846                         if ((trans->iseqno == 1) && !trans->oseqno) {
1847                                 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || 
1848                                         ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || 
1849                                         (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1850                                         if (!final) {
1851                                                 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1852                                         }
1853                                         break;
1854                                 }
1855                                 apply_peer(trans, peer);
1856                                 /* Key passed, use new contexts for this session */
1857                                 trans->ecx = peer->them_ecx;
1858                                 trans->dcx = peer->them_dcx;
1859                         }
1860                         if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1861                                 struct dundi_hdr *dhdr;
1862                                 unsigned char decoded[MAX_PACKET_SIZE];
1863                                 int ddatalen;
1864                                 ddatalen = sizeof(decoded);
1865                                 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1866                                 if (dhdr) {
1867                                         /* Handle decrypted response */
1868                                         if (dundidebug)
1869                                                 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1870                                         handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1871                                         /* Carry back final flag */
1872                                         hdr->cmdresp |= dhdr->cmdresp & 0x80;
1873                                         break;
1874                                 } else {
1875                                         ast_debug(1, "Ouch, decrypt failed :(\n");
1876                                 }
1877                         }
1878                 }
1879                 if (!final) {
1880                         /* Turn off encryption */
1881                         ast_clear_flag(trans, FLAG_ENCRYPT);
1882                         dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1883                 }
1884                 break;
1885         default:
1886                 /* Send unknown command if we don't know it, with final flag IFF it's the
1887                    first command in the dialog and only if we haven't recieved final notification */
1888                 if (!final) {
1889                         dundi_ie_append_byte(ied, DUNDI_IE_UNKNOWN, cmd);
1890                         dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
1891                 }
1892         }
1893
1894         retval = 0;
1895
1896 return_cleanup:
1897 #ifdef LOW_MEMORY
1898         ast_free(ied);
1899 #endif
1900         return retval;
1901 }
1902
1903 static void destroy_packet(struct dundi_packet *pack, int needfree);
1904 static void destroy_packets(struct packetlist *p)
1905 {
1906         struct dundi_packet *pack;
1907         
1908         while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1909                 AST_SCHED_DEL(sched, pack->retransid);
1910                 ast_free(pack);
1911         }
1912 }
1913
1914
1915 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1916 {
1917         struct dundi_packet *pack;
1918
1919         /* Ack transmitted packet corresponding to iseqno */
1920         AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1921                 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1922                         destroy_packet(pack, 0);
1923                         if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1924                                 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1925                                 destroy_packets(&trans->lasttrans);
1926                         }
1927                         AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1928                         AST_SCHED_DEL(sched, trans->autokillid);
1929                         return 1;
1930                 }
1931         }
1932
1933         return 0;
1934 }
1935
1936 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1937 {
1938         struct dundi_transaction *trans;
1939         trans = find_transaction(h, sin);
1940         if (!trans) {
1941                 dundi_reject(h, sin);
1942                 return 0;
1943         }
1944         /* Got a transaction, see where this header fits in */
1945         if (h->oseqno == trans->iseqno) {
1946                 /* Just what we were looking for...  Anything but ack increments iseqno */
1947                 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1948                         /* If final, we're done */
1949                         destroy_trans(trans, 0);
1950                         return 0;
1951                 }
1952                 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1953                         trans->oiseqno = trans->iseqno;
1954                         trans->iseqno++;
1955                         handle_command_response(trans, h, datalen, 0);
1956                 }
1957                 if (trans->aseqno != trans->iseqno) {
1958                         dundi_ack(trans, h->cmdresp & 0x80);
1959                         trans->aseqno = trans->iseqno;
1960                 }
1961                 /* Delete any saved last transmissions */
1962                 destroy_packets(&trans->lasttrans);
1963                 if (h->cmdresp & 0x80) {
1964                         /* Final -- destroy now */
1965                         destroy_trans(trans, 0);
1966                 }
1967         } else if (h->oseqno == trans->oiseqno) {
1968                 /* Last incoming sequence number -- send ACK without processing */
1969                 dundi_ack(trans, 0);
1970         } else {
1971                 /* Out of window -- simply drop */
1972                 ast_debug(1, "Dropping packet out of window!\n");
1973         }
1974         return 0;
1975 }
1976
1977 static int socket_read(int *id, int fd, short events, void *cbdata)
1978 {
1979         struct sockaddr_in sin;
1980         int res;
1981         struct dundi_hdr *h;
1982         char buf[MAX_PACKET_SIZE];
1983         socklen_t len = sizeof(sin);
1984         
1985         res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1986         if (res < 0) {
1987                 if (errno != ECONNREFUSED)
1988                         ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1989                 return 1;
1990         }
1991         if (res < sizeof(struct dundi_hdr)) {
1992                 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1993                 return 1;
1994         }
1995         buf[res] = '\0';
1996         h = (struct dundi_hdr *) buf;
1997         if (dundidebug)
1998                 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1999         AST_LIST_LOCK(&peers);
2000         handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2001         AST_LIST_UNLOCK(&peers);
2002         return 1;
2003 }
2004
2005 static void build_secret(char *secret, int seclen)
2006 {
2007         unsigned char tmp[16];
2008         char *s;
2009         build_iv(tmp);
2010         secret[0] = '\0';
2011         ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2012         /* Eliminate potential bad characters */
2013         while((s = strchr(secret, ';'))) *s = '+';
2014         while((s = strchr(secret, '/'))) *s = '+';
2015         while((s = strchr(secret, ':'))) *s = '+';
2016         while((s = strchr(secret, '@'))) *s = '+';
2017 }
2018
2019
2020 static void save_secret(const char *newkey, const char *oldkey)
2021 {
2022         char tmp[256];
2023         if (oldkey)
2024                 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2025         else
2026                 snprintf(tmp, sizeof(tmp), "%s", newkey);
2027         rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2028         ast_db_put(secretpath, "secret", tmp);
2029         snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2030         ast_db_put(secretpath, "secretexpiry", tmp);
2031 }
2032
2033 static void load_password(void)
2034 {
2035         char *current=NULL;
2036         char *last=NULL;
2037         char tmp[256];
2038         time_t expired;
2039         
2040         ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2041         if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2042                 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2043                 current = strchr(tmp, ';');
2044                 if (!current)
2045                         current = tmp;
2046                 else {
2047                         *current = '\0';
2048                         current++;
2049                 };
2050                 if ((time(NULL) - expired) < 0) {
2051                         if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2052                                 expired = time(NULL) + DUNDI_SECRET_TIME;
2053                 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2054                         last = current;
2055                         current = NULL;
2056                 } else {
2057                         last = NULL;
2058                         current = NULL;
2059                 }
2060         }
2061         if (current) {
2062                 /* Current key is still valid, just setup rotatation properly */
2063                 ast_copy_string(cursecret, current, sizeof(cursecret));
2064                 rotatetime = expired;
2065         } else {
2066                 /* Current key is out of date, rotate or eliminate all together */
2067                 build_secret(cursecret, sizeof(cursecret));
2068                 save_secret(cursecret, last);
2069         }
2070 }
2071
2072 static void check_password(void)
2073 {
2074         char oldsecret[80];
2075         time_t now;
2076         
2077         time(&now);     
2078 #if 0
2079         printf("%ld/%ld\n", now, rotatetime);
2080 #endif
2081         if ((now - rotatetime) >= 0) {
2082                 /* Time to rotate keys */
2083                 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2084                 build_secret(cursecret, sizeof(cursecret));
2085                 save_secret(cursecret, oldsecret);
2086         }
2087 }
2088
2089 static void *network_thread(void *ignore)
2090 {
2091         /* Our job is simple: Send queued messages, retrying if necessary.  Read frames 
2092            from the network, and queue them for delivery to the channels */
2093         int res;
2094         /* Establish I/O callback for socket read */
2095         ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2096         
2097         while (!dundi_shutdown) {
2098                 res = ast_sched_wait(sched);
2099                 if ((res > 1000) || (res < 0))
2100                         res = 1000;
2101                 res = ast_io_wait(io, res);
2102                 if (res >= 0) {
2103                         AST_LIST_LOCK(&peers);
2104                         ast_sched_runq(sched);
2105                         AST_LIST_UNLOCK(&peers);
2106                 }
2107                 check_password();
2108         }
2109
2110         netthreadid = AST_PTHREADT_NULL;
2111         
2112         return NULL;
2113 }
2114
2115 static void *process_precache(void *ign)
2116 {
2117         struct dundi_precache_queue *qe;
2118         time_t now;
2119         char context[256];
2120         char number[256];
2121         int run;
2122
2123         while (!dundi_shutdown) {
2124                 time(&now);
2125                 run = 0;
2126                 AST_LIST_LOCK(&pcq);
2127                 if ((qe = AST_LIST_FIRST(&pcq))) {
2128                         if (!qe->expiration) {
2129                                 /* Gone...  Remove... */
2130                                 AST_LIST_REMOVE_HEAD(&pcq, list);
2131                                 ast_free(qe);
2132                         } else if (qe->expiration < now) {
2133                                 /* Process this entry */
2134                                 qe->expiration = 0;
2135                                 ast_copy_string(context, qe->context, sizeof(context));
2136                                 ast_copy_string(number, qe->number, sizeof(number));
2137                                 run = 1;
2138                         }
2139                 }
2140                 AST_LIST_UNLOCK(&pcq);
2141                 if (run) {
2142                         dundi_precache(context, number);
2143                 } else
2144                         sleep(1);
2145         }
2146
2147         precachethreadid = AST_PTHREADT_NULL;
2148
2149         return NULL;
2150 }
2151
2152 static int start_network_thread(void)
2153 {
2154         ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2155         ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2156         return 0;
2157 }
2158
2159 static char *dundi_do_debug_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2160 {
2161         switch (cmd) {
2162         case CLI_INIT:
2163                 e->command = "dundi [no] debug";
2164                 e->usage = 
2165                         "Usage: dundi [no] debug\n"
2166                         "       Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2167                 return NULL;
2168         case CLI_GENERATE:
2169                 return NULL;
2170         }
2171         if (a->argc < 2 || a->argc > 3)
2172                 return CLI_SHOWUSAGE;
2173         if (a->argc == 2) {
2174                 dundidebug = 1;
2175                 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2176         } else {
2177                 dundidebug = 0;
2178                 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2179         }
2180         return CLI_SUCCESS;
2181 }
2182
2183 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2184 {
2185         switch (cmd) {
2186         case CLI_INIT:
2187                 e->command = "dundi set debug {on|off}";
2188                 e->usage = 
2189                         "Usage: dundi set debug {on|off}\n"
2190                         "       Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2191                 return NULL;
2192         case CLI_GENERATE:
2193                 return NULL;
2194         }
2195
2196         if (a->argc != e->args)
2197                 return CLI_SHOWUSAGE;
2198
2199         if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2200                 dundidebug = 1;
2201                 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2202         } else {
2203                 dundidebug = 0;
2204                 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2205         }
2206         return CLI_SUCCESS;
2207 }
2208
2209 static char *dundi_do_store_history_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2210 {
2211         switch (cmd) {
2212         case CLI_INIT:
2213                 e->command = "dundi [no] store history";
2214                 e->usage = 
2215                         "Usage: dundi [no] store history\n"
2216                         "       Enables/Disables storing of DUNDi requests and times for debugging\n"
2217                         "purposes\n";
2218                 return NULL;
2219         case CLI_GENERATE:
2220                 return NULL;
2221         }
2222         if (a->argc < 3 || a->argc > 4)
2223                 return CLI_SHOWUSAGE;
2224         
2225         if (a->argc == 3) {
2226                 global_storehistory = 1;
2227                 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2228         } else {
2229                 global_storehistory = 0;
2230                 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2231         }
2232         return CLI_SUCCESS;
2233 }
2234
2235 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2236 {
2237         switch (cmd) {
2238         case CLI_INIT:
2239                 e->command = "dundi store history {on|off}";
2240                 e->usage = 
2241                         "Usage: dundi store history {on|off}\n"
2242                         "       Enables/Disables storing of DUNDi requests and times for debugging\n"
2243                         "purposes\n";
2244                 return NULL;
2245         case CLI_GENERATE:
2246                 return NULL;
2247         }
2248
2249         if (a->argc != e->args)
2250                 return CLI_SHOWUSAGE;
2251         
2252         if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2253                 global_storehistory = 1;
2254                 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2255         } else {
2256                 global_storehistory = 0;
2257                 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2258         }
2259         return CLI_SUCCESS;
2260 }
2261
2262 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2263 {
2264         int stats = 0;
2265         switch (cmd) {
2266         case CLI_INIT:
2267                 e->command = "dundi flush [stats]";
2268                 e->usage = 
2269                         "Usage: dundi flush [stats]\n"
2270                         "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
2271                         "'stats' is present, clears timer statistics instead of normal\n"
2272                         "operation.\n";
2273                 return NULL;
2274         case CLI_GENERATE:
2275                 return NULL;
2276         }
2277         if ((a->argc < 2) || (a->argc > 3))
2278                 return CLI_SHOWUSAGE;
2279         if (a->argc > 2) {
2280                 if (!strcasecmp(a->argv[2], "stats"))
2281                         stats = 1;
2282                 else
2283                         return CLI_SHOWUSAGE;
2284         }
2285         if (stats) {
2286                 /* Flush statistics */
2287                 struct dundi_peer *p;
2288                 int x;
2289                 AST_LIST_LOCK(&peers);
2290                 AST_LIST_TRAVERSE(&peers, p, list) {
2291                         for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2292                                 if (p->lookups[x])
2293                                         ast_free(p->lookups[x]);
2294                                 p->lookups[x] = NULL;
2295                                 p->lookuptimes[x] = 0;
2296                         }
2297                         p->avgms = 0;
2298                 }
2299                 AST_LIST_UNLOCK(&peers);
2300         } else {
2301                 ast_db_deltree("dundi/cache", NULL);
2302                 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2303         }
2304         return CLI_SUCCESS;
2305 }
2306
2307 static char *model2str(int model)
2308 {
2309         switch(model) {
2310         case DUNDI_MODEL_INBOUND:
2311                 return "Inbound";
2312         case DUNDI_MODEL_OUTBOUND:
2313                 return "Outbound";
2314         case DUNDI_MODEL_SYMMETRIC:
2315                 return "Symmetric";
2316         default:
2317                 return "Unknown";
2318         }
2319 }
2320
2321 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2322 {
2323         int which=0, len;
2324         char *ret = NULL;
2325         struct dundi_peer *p;
2326         char eid_str[20];
2327
2328         if (pos != rpos)
2329                 return NULL;
2330         AST_LIST_LOCK(&peers);
2331         len = strlen(word);
2332         AST_LIST_TRAVERSE(&peers, p, list) {
2333                 const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2334                 if (!strncasecmp(word, s, len) && ++which > state) {
2335                         ret = ast_strdup(s);
2336                         break;
2337                 }
2338         }
2339         AST_LIST_UNLOCK(&peers);
2340         return ret;
2341 }
2342
2343 static int rescomp(const void *a, const void *b)
2344 {
2345         const struct dundi_result *resa, *resb;
2346         resa = a;
2347         resb = b;
2348         if (resa->weight < resb->weight)
2349                 return -1;
2350         if (resa->weight > resb->weight)
2351                 return 1;
2352         return 0;
2353 }
2354
2355 static void sort_results(struct dundi_result *results, int count)
2356 {
2357         qsort(results, count, sizeof(results[0]), rescomp);
2358 }
2359
2360 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2361 {
2362         int res;
2363         char tmp[256];
2364         char fs[80] = "";
2365         char *context;
2366         int x;
2367         int bypass = 0;
2368         struct dundi_result dr[MAX_RESULTS];
2369         struct timeval start;
2370         switch (cmd) {
2371         case CLI_INIT:
2372                 e->command = "dundi lookup";
2373                 e->usage =
2374                         "Usage: dundi lookup <number>[@context] [bypass]\n"
2375                         "       Lookup the given number within the given DUNDi context\n"
2376                         "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
2377                         "keyword is specified.\n";
2378                 return NULL;
2379         case CLI_GENERATE:
2380                 return NULL;
2381         }
2382
2383         if ((a->argc < 3) || (a->argc > 4))
2384                 return CLI_SHOWUSAGE;
2385         if (a->argc > 3) {
2386                 if (!strcasecmp(a->argv[3], "bypass"))
2387                         bypass=1;
2388                 else
2389                         return CLI_SHOWUSAGE;
2390         }
2391         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2392         context = strchr(tmp, '@');
2393         if (context) {
2394                 *context = '\0';
2395                 context++;
2396         }
2397         start = ast_tvnow();
2398         res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2399         
2400         if (res < 0) 
2401                 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2402         else if (!res) 
2403                 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2404         else
2405                 sort_results(dr, res);
2406         for (x=0;x<res;x++) {
2407                 ast_cli(a->fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
2408                 ast_cli(a->fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2409         }
2410         ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2411         return CLI_SUCCESS;
2412 }
2413
2414 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2415 {
2416         int res;
2417         char tmp[256];
2418         char *context;
2419         struct timeval start;
2420         switch (cmd) {
2421         case CLI_INIT:
2422                 e->command = "dundi precache";
2423                 e->usage = 
2424                         "Usage: dundi precache <number>[@context]\n"
2425                         "       Lookup the given number within the given DUNDi context\n"
2426                         "(or e164 if none is specified) and precaches the results to any\n"
2427                         "upstream DUNDi push servers.\n";
2428                 return NULL;
2429         case CLI_GENERATE:
2430                 return NULL;
2431         }
2432         if ((a->argc < 3) || (a->argc > 3))
2433                 return CLI_SHOWUSAGE;
2434         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2435         context = strchr(tmp, '@');
2436         if (context) {
2437                 *context = '\0';
2438                 context++;
2439         }
2440         start = ast_tvnow();
2441         res = dundi_precache(context, tmp);
2442         
2443         if (res < 0) 
2444                 ast_cli(a->fd, "DUNDi precache returned error.\n");
2445         else if (!res) 
2446                 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2447         ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2448         return CLI_SUCCESS;
2449 }
2450
2451 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2452 {
2453         int res;
2454         char tmp[256];
2455         char *context;
2456         dundi_eid eid;
2457         struct dundi_entity_info dei;
2458         switch (cmd) {
2459         case CLI_INIT:
2460                 e->command = "dundi query";
2461                 e->usage = 
2462                         "Usage: dundi query <entity>[@context]\n"
2463                         "       Attempts to retrieve contact information for a specific\n"
2464                         "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2465                         "e164 if none is specified).\n";
2466                 return NULL;
2467         case CLI_GENERATE:
2468                 return NULL;
2469         }
2470         if ((a->argc < 3) || (a->argc > 3))
2471                 return CLI_SHOWUSAGE;
2472         if (ast_str_to_eid(&eid, a->argv[2])) {
2473                 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2474                 return CLI_SHOWUSAGE;
2475         }
2476         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2477         context = strchr(tmp, '@');
2478         if (context) {
2479                 *context = '\0';
2480                 context++;
2481         }
2482         res = dundi_query_eid(&dei, context, eid);
2483         if (res < 0) 
2484                 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2485         else if (!res) 
2486                 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2487         else {
2488                 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2489                 ast_cli(a->fd, "Department:      %s\n", dei.orgunit);
2490                 ast_cli(a->fd, "Organization:    %s\n", dei.org);
2491                 ast_cli(a->fd, "City/Locality:   %s\n", dei.locality);
2492                 ast_cli(a->fd, "State/Province:  %s\n", dei.stateprov);
2493                 ast_cli(a->fd, "Country:         %s\n", dei.country);
2494                 ast_cli(a->fd, "E-mail:          %s\n", dei.email);
2495                 ast_cli(a->fd, "Phone:           %s\n", dei.phone);
2496                 ast_cli(a->fd, "IP Address:      %s\n", dei.ipaddr);
2497         }
2498         return CLI_SUCCESS;
2499 }
2500
2501 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2502 {
2503         struct dundi_peer *peer;
2504         struct permission *p;
2505         char *order;
2506         char eid_str[20];
2507         int x, cnt;
2508         switch (cmd) {
2509         case CLI_INIT:
2510                 e->command = "dundi show peer";
2511                 e->usage =
2512                         "Usage: dundi show peer [peer]\n"
2513                         "       Provide a detailed description of a specifid DUNDi peer.\n";
2514                 return NULL;
2515         case CLI_GENERATE:
2516                 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2517         }
2518         if (a->argc != 4)
2519                 return CLI_SHOWUSAGE;
2520         AST_LIST_LOCK(&peers);
2521         AST_LIST_TRAVERSE(&peers, peer, list) {
2522                 if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2523                         break;
2524         }
2525         if (peer) {
2526                 switch(peer->order) {
2527                 case 0:
2528                         order = "Primary";
2529                         break;
2530                 case 1:
2531                         order = "Secondary";
2532                         break;
2533                 case 2:
2534                         order = "Tertiary";
2535                         break;
2536                 case 3:
2537                         order = "Quartiary";
2538                         break;
2539                 default:
2540                         order = "Unknown";
2541                 }
2542                 ast_cli(a->fd, "Peer:    %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2543                 ast_cli(a->fd, "Model:   %s\n", model2str(peer->model));
2544                 ast_cli(a->fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2545                 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2546                 ast_cli(a->fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
2547                 ast_cli(a->fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2548                 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2549                 if (!AST_LIST_EMPTY(&peer->include))
2550                         ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2551                 AST_LIST_TRAVERSE(&peer->include, p, list)
2552                         ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2553                 if (!AST_LIST_EMPTY(&peer->permit))
2554                         ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2555                 AST_LIST_TRAVERSE(&peer->permit, p, list)
2556                         ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2557                 cnt = 0;
2558                 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2559                         if (peer->lookups[x]) {
2560                                 if (!cnt)
2561                                         ast_cli(a->fd, "Last few query times:\n");
2562                                 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2563                                 cnt++;
2564                         }
2565                 }
2566                 if (cnt)
2567                         ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2568         } else
2569                 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2570         AST_LIST_UNLOCK(&peers);
2571         return CLI_SUCCESS;
2572 }
2573
2574 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2575 {
2576 #define FORMAT2 "%-20.20s %-15.15s     %-10.10s %-8.8s %-15.15s\n"
2577 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2578         struct dundi_peer *peer;
2579         int registeredonly=0;
2580         char avgms[20];
2581         char eid_str[20];
2582         int online_peers = 0;
2583         int offline_peers = 0;
2584         int unmonitored_peers = 0;
2585         int total_peers = 0;
2586         switch (cmd) {
2587         case CLI_INIT:
2588                 e->command = "dundi show peers [registered|include|exclude|begin]";
2589                 e->usage = 
2590                         "Usage: dundi show peers [registered|include|exclude|begin]\n"
2591                         "       Lists all known DUNDi peers.\n"
2592                         "       If 'registered' is present, only registered peers are shown.\n";
2593                 return NULL;
2594         case CLI_GENERATE:
2595                 return NULL;
2596         }
2597
2598         if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5))
2599                 return CLI_SHOWUSAGE;
2600         if ((a->argc == 4)) {
2601                 if (!strcasecmp(a->argv[3], "registered")) {
2602                         registeredonly = 1;
2603                 } else
2604                         return CLI_SHOWUSAGE;
2605         }
2606         AST_LIST_LOCK(&peers);
2607         ast_cli(a->fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2608         AST_LIST_TRAVERSE(&peers, peer, list) {
2609                 char status[20];
2610                 int print_line = -1;
2611                 char srch[2000];
2612                 total_peers++;
2613                 if (registeredonly && !peer->addr.sin_addr.s_addr)
2614                         continue;
2615                 if (peer->maxms) {
2616                         if (peer->lastms < 0) {
2617                                 strcpy(status, "UNREACHABLE");
2618                                 offline_peers++;
2619                         }
2620                         else if (peer->lastms > peer->maxms) {
2621                                 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2622                                 offline_peers++;
2623                         }
2624                         else if (peer->lastms) {
2625                                 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2626                                 online_peers++;
2627                         }
2628                         else {
2629                                 strcpy(status, "UNKNOWN");
2630                                 offline_peers++;
2631                         }
2632                 } else {
2633                         strcpy(status, "Unmonitored");
2634                         unmonitored_peers++;
2635                 }
2636                 if (peer->avgms) 
2637                         snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2638                 else
2639                         strcpy(avgms, "Unavail");
2640                 snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2641                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2642                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2643
2644                 if (a->argc == 5) {
2645                   if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2646                         print_line = -1;
2647                    } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2648                         print_line = 1;
2649                    } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2650                         print_line = -1;
2651                    } else {
2652                         print_line = 0;
2653                   }
2654                 }
2655                 
2656         if (print_line) {
2657                         ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2658                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2659                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2660                 }
2661         }
2662         ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2663         AST_LIST_UNLOCK(&peers);
2664         return CLI_SUCCESS;
2665 #undef FORMAT
2666 #undef FORMAT2
2667 }
2668
2669 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2670 {
2671 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2672 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2673         struct dundi_transaction *trans;
2674         switch (cmd) {
2675         case CLI_INIT:
2676                 e->command = "dundi show trans";
2677                 e->usage = 
2678                         "Usage: dundi show trans\n"
2679                         "       Lists all known DUNDi transactions.\n";
2680                 return NULL;
2681         case CLI_GENERATE:
2682                 return NULL;
2683         }
2684         if (a->argc != 3)
2685                 return CLI_SHOWUSAGE;
2686         AST_LIST_LOCK(&peers);
2687         ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2688         AST_LIST_TRAVERSE(&alltrans, trans, all) {
2689                 ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr), 
2690                         ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2691         }
2692         AST_LIST_UNLOCK(&peers);
2693         return CLI_SUCCESS;
2694 #undef FORMAT
2695 #undef FORMAT2
2696 }
2697
2698 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2699 {
2700         char eid_str[20];
2701         switch (cmd) {
2702         case CLI_INIT:
2703                 e->command = "dundi show entityid";
2704                 e->usage =
2705                         "Usage: dundi show entityid\n"
2706                         "       Displays the global entityid for this host.\n";
2707                 return NULL;
2708         case CLI_GENERATE:
2709                 return NULL;
2710         }
2711         if (a->argc != 3)
2712                 return CLI_SHOWUSAGE;
2713         AST_LIST_LOCK(&peers);
2714         ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2715         AST_LIST_UNLOCK(&peers);
2716         ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2717         return CLI_SUCCESS;
2718 }
2719
2720 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2721 {
2722 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2723 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2724         struct dundi_request *req;
2725         char eidstr[20];
2726         switch (cmd) {
2727         case CLI_INIT:
2728                 e->command = "dundi show requests";
2729                 e->usage = 
2730                         "Usage: dundi show requests\n"
2731                         "       Lists all known pending DUNDi requests.\n";
2732                 return NULL;
2733         case CLI_GENERATE:
2734                 return NULL;
2735         }
2736         if (a->argc != 3)
2737                 return CLI_SHOWUSAGE;
2738         AST_LIST_LOCK(&peers);
2739         ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2740         AST_LIST_TRAVERSE(&requests, req, list) {
2741                 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2742                         dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2743         }
2744         AST_LIST_UNLOCK(&peers);
2745         return CLI_SUCCESS;
2746 #undef FORMAT
2747 #undef FORMAT2
2748 }
2749
2750 /* Grok-a-dial DUNDi */
2751
2752 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2753 {
2754 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2755 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2756         struct dundi_mapping *map;
2757         char fs[256];
2758         char weight[8];
2759         switch (cmd) {
2760         case CLI_INIT:
2761                 e->command = "dundi show mappings";
2762                 e->usage = 
2763                         "Usage: dundi show mappings\n"
2764                         "       Lists all known DUNDi mappings.\n";
2765                 return NULL;
2766         case CLI_GENERATE:
2767                 return NULL;
2768         }
2769         if (a->argc != 3)
2770                 return CLI_SHOWUSAGE;
2771         AST_LIST_LOCK(&peers);
2772         ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2773         AST_LIST_TRAVERSE(&mappings, map, list) {
2774                 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map));
2775                 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2776                         ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
2777                         dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2778         }
2779         AST_LIST_UNLOCK(&peers);
2780         return CLI_SUCCESS;
2781 #undef FORMAT
2782 #undef FORMAT2
2783 }
2784
2785 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2786 {
2787 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2788 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2789         struct dundi_precache_queue *qe;
2790         int h,m,s;
2791         time_t now;
2792         switch (cmd) {
2793         case CLI_INIT:
2794                 e->command = "dundi show precache";
2795                 e->usage = 
2796                         "Usage: dundi show precache\n"
2797                         "       Lists all known DUNDi scheduled precache updates.\n";
2798                 return NULL;
2799         case CLI_GENERATE:
2800                 return NULL;
2801         }
2802         if (a->argc != 3)
2803                 return CLI_SHOWUSAGE;
2804         time(&now);
2805         ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2806         AST_LIST_LOCK(&pcq);
2807         AST_LIST_TRAVERSE(&pcq, qe, list) {
2808                 s = qe->expiration - now;
2809                 h = s / 3600;
2810                 s = s % 3600;
2811                 m = s / 60;
2812                 s = s % 60;
2813                 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2814         }
2815         AST_LIST_UNLOCK(&pcq);
2816         
2817         return CLI_SUCCESS;
2818 #undef FORMAT
2819 #undef FORMAT2
2820 }
2821
2822 static struct ast_cli_entry cli_dundi_do_debug_deprecated = AST_CLI_DEFINE(dundi_do_debug_deprecated, "Enable/Disable DUNDi debugging");
2823 static struct ast_cli_entry cli_dundi_do_store_history_deprecated = AST_CLI_DEFINE(dundi_do_store_history_deprecated, "Enable/Disable DUNDi historic records");
2824 static struct ast_cli_entry cli_dundi[] = {
2825         AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging", .deprecate_cmd = &cli_dundi_do_debug_deprecated),
2826         AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records", .deprecate_cmd = &cli_dundi_do_store_history_deprecated),
2827         AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
2828         AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
2829         AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
2830         AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
2831         AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
2832         AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
2833         AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
2834         AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
2835         AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
2836         AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
2837         AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
2838 };
2839
2840 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2841 {
2842         struct dundi_transaction *trans;
2843         int tid;
2844         
2845         /* Don't allow creation of transactions to non-registered peers */
2846         if (p && !p->addr.sin_addr.s_addr)
2847                 return NULL;
2848         tid = get_trans_id();
2849         if (tid < 1)
2850                 return NULL;
2851         if (!(trans = ast_calloc(1, sizeof(*trans))))
2852                 return NULL;
2853
2854         if (global_storehistory) {
2855                 trans->start = ast_tvnow();
2856                 ast_set_flag(trans, FLAG_STOREHIST);
2857         }
2858         trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2859         trans->autokillid = -1;
2860         if (p) {
2861                 apply_peer(trans, p);
2862                 if (!p->sentfullkey)
2863                         ast_set_flag(trans, FLAG_SENDFULLKEY);
2864         }
2865         trans->strans = tid;
2866         AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2867         
2868         return trans;
2869 }
2870
2871 static int dundi_xmit(struct dundi_packet *pack)
2872 {
2873         int res;
2874         if (dundidebug)
2875                 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2876         res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2877         if (res < 0) {
2878                 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
2879                         ast_inet_ntoa(pack->parent->addr.sin_addr),
2880                         ntohs(pack->parent->addr.sin_port), strerror(errno));
2881         }
2882         if (res > 0)
2883                 res = 0;
2884         return res;
2885 }
2886
2887 static void destroy_packet(struct dundi_packet *pack, int needfree)
2888 {
2889         if (pack->parent)
2890                 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2891         AST_SCHED_DEL(sched, pack->retransid);
2892         if (needfree)
2893                 ast_free(pack);
2894 }
2895
2896 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2897 {
2898         struct dundi_peer *peer;
2899         int ms;
2900         int x;
2901         int cnt;
2902         char eid_str[20];
2903         if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2904                 AST_LIST_TRAVERSE(&peers, peer, list) {
2905                         if (peer->regtrans == trans)
2906                                 peer->regtrans = NULL;
2907                         if (peer->qualtrans == trans) {
2908                                 if (fromtimeout) {
2909                                         if (peer->lastms > -1)
2910                                                 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2911                                         peer->lastms = -1;
2912                                 } else {
2913                                         ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2914                                         if (ms < 1)
2915                                                 ms = 1;
2916                                         if (ms < peer->maxms) {
2917                                                 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2918                                                         ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2919                                         } else if (peer->lastms < peer->maxms) {
2920                                                 ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
2921                                         }
2922                                         peer->lastms = ms;
2923                                 }
2924                                 peer->qualtrans = NULL;
2925                         }
2926                         if (ast_test_flag(trans, FLAG_STOREHIST)) {
2927                                 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2928                                         if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
2929                                                 peer->avgms = 0;
2930                                                 cnt = 0;
2931                                                 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2932                                                         ast_free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2933            &nbs