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