as discussed with Mark a few weeks ago, the 'newstack' argument
[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         char fs[256];
1133
1134         /* Build request string */
1135         if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1136                 time_t timeout;
1137                 ptr = data;
1138                 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1139                         int expiration = timeout - now;
1140                         if (expiration > 0) {
1141                                 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1142                                 ptr += length + 1;
1143                                 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1144                                         ptr += length;
1145                                         term = strchr(ptr, '|');
1146                                         if (term) {
1147                                                 *term = '\0';
1148                                                 src = strrchr(ptr, '/');
1149                                                 if (src) {
1150                                                         *src = '\0';
1151                                                         src++;
1152                                                 } else
1153                                                         src = "";
1154                                                 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n", 
1155                                                         tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1156                                                 /* Make sure it's not already there */
1157                                                 for (z=0;z<req->respcount;z++) {
1158                                                         if ((req->dr[z].techint == tech) &&
1159                                                             !strcmp(req->dr[z].dest, ptr)) 
1160                                                                         break;
1161                                                 }
1162                                                 if (z == req->respcount) {
1163                                                         /* Copy into parent responses */
1164                                                         ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);      
1165                                                         req->dr[req->respcount].weight = weight;
1166                                                         req->dr[req->respcount].techint = tech;
1167                                                         req->dr[req->respcount].expiration = expiration;
1168                                                         dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1169                                                         dundi_eid_to_str(req->dr[req->respcount].eid_str, 
1170                                                                 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1171                                                         ast_copy_string(req->dr[req->respcount].dest, ptr,
1172                                                                 sizeof(req->dr[req->respcount].dest));
1173                                                         ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1174                                                                 sizeof(req->dr[req->respcount].tech));
1175                                                         req->respcount++;
1176                                                         ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);   
1177                                                 } else if (req->dr[z].weight > weight)
1178                                                         req->dr[z].weight = weight;
1179                                                 ptr = term + 1;
1180                                         }
1181                                 }
1182                                 /* We found *something* cached */
1183                                 if (expiration < *lowexpiration)
1184                                         *lowexpiration = expiration;
1185                                 return 1;
1186                         } else 
1187                                 ast_db_del("dundi/cache", key);
1188                 } else 
1189                         ast_db_del("dundi/cache", key);
1190         }
1191                 
1192         return 0;
1193 }
1194
1195 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1196 {
1197         char key[256];
1198         char eid_str[20];
1199         char eidroot_str[20];
1200         time_t now;
1201         int res=0;
1202         int res2=0;
1203         char eid_str_full[20];
1204         char tmp[256]="";
1205         int x;
1206
1207         time(&now);
1208         dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1209         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1210         dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1211         snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1212         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1213         snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1214         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1215         snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1216         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1217         x = 0;
1218         if (!req->respcount) {
1219                 while(!res2) {
1220                         /* Look and see if we have a hint that would preclude us from looking at this
1221                            peer for this number. */
1222                         if (!(tmp[x] = req->number[x])) 
1223                                 break;
1224                         x++;
1225                         /* Check for hints */
1226                         snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1227                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1228                         snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1229                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1230                         snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1231                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1232                         if (res2) {
1233                                 if (strlen(tmp) > strlen(req->hmd->exten)) {
1234                                         /* Update meta data if appropriate */
1235                                         ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1236                                 }
1237                         }
1238                 }
1239                 res |= res2;
1240         }
1241
1242         return res;
1243 }
1244
1245 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1246
1247 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1248 {
1249         if (!trans->addr.sin_addr.s_addr)
1250                 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1251         trans->us_eid = p->us_eid;
1252         trans->them_eid = p->eid;
1253         /* Enable encryption if appropriate */
1254         if (!ast_strlen_zero(p->inkey))
1255                 ast_set_flag(trans, FLAG_ENCRYPT);      
1256         if (p->maxms) {
1257                 trans->autokilltimeout = p->maxms;
1258                 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1259                 if (p->lastms > 1) {
1260                         trans->retranstimer = p->lastms * 2;
1261                         /* Keep it from being silly */
1262                         if (trans->retranstimer < 150)
1263                                 trans->retranstimer = 150;
1264                 }
1265                 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1266                         trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1267         } else
1268                 trans->autokilltimeout = global_autokilltimeout;
1269 }
1270
1271 static int do_register_expire(void *data)
1272 {
1273         struct dundi_peer *peer = data;
1274         char eid_str[20];
1275         /* Called with peerlock already held */
1276         ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1277         peer->registerexpire = -1;
1278         peer->lastms = 0;
1279         memset(&peer->addr, 0, sizeof(peer->addr));
1280         return 0;
1281 }
1282
1283 static int update_key(struct dundi_peer *peer)
1284 {
1285         unsigned char key[16];
1286         struct ast_key *ekey, *skey;
1287         char eid_str[20];
1288         int res;
1289         if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1290                 build_iv(key);
1291                 aes_encrypt_key128(key, &peer->us_ecx);
1292                 aes_decrypt_key128(key, &peer->us_dcx);
1293                 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1294                 if (!ekey) {
1295                         ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1296                                 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1297                         return -1;
1298                 }
1299                 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1300                 if (!skey) {
1301                         ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1302                                 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1303                         return -1;
1304                 }
1305                 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1306                         ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1307                         return -1;
1308                 }
1309                 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1310                         ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1311                         return -1;
1312                 }
1313                 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1314                 peer->sentfullkey = 0;
1315                 /* Looks good */
1316                 time(&peer->keyexpire);
1317                 peer->keyexpire += dundi_key_ttl;
1318         }
1319         return 0;
1320 }
1321
1322 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx) 
1323 {
1324         unsigned char curblock[16];
1325         int x;
1326         memcpy(curblock, iv, sizeof(curblock));
1327         while(len > 0) {
1328                 for (x=0;x<16;x++)
1329                         curblock[x] ^= src[x];
1330                 aes_encrypt(curblock, dst, ecx);
1331                 memcpy(curblock, dst, sizeof(curblock)); 
1332                 dst += 16;
1333                 src += 16;
1334                 len -= 16;
1335         }
1336         return 0;
1337 }
1338 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx) 
1339 {
1340         unsigned char lastblock[16];
1341         int x;
1342         memcpy(lastblock, iv, sizeof(lastblock));
1343         while(len > 0) {
1344                 aes_decrypt(src, dst, dcx);
1345                 for (x=0;x<16;x++)
1346                         dst[x] ^= lastblock[x];
1347                 memcpy(lastblock, src, sizeof(lastblock));
1348                 dst += 16;
1349                 src += 16;
1350                 len -= 16;
1351         }
1352         return 0;
1353 }
1354
1355 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)
1356 {
1357         int space = *dstlen;
1358         unsigned long bytes;
1359         struct dundi_hdr *h;
1360         unsigned char *decrypt_space;
1361         decrypt_space = alloca(srclen);
1362         if (!decrypt_space)
1363                 return NULL;
1364         decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1365         /* Setup header */
1366         h = (struct dundi_hdr *)dst;
1367         *h = *ohdr;
1368         bytes = space - 6;
1369         if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1370                 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1371                 return NULL;
1372         }
1373         /* Update length */
1374         *dstlen = bytes + 6;
1375         /* Return new header */
1376         return h;
1377 }
1378
1379 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1380 {
1381         unsigned char *compress_space;
1382         int len;
1383         int res;
1384         unsigned long bytes;
1385         struct dundi_ie_data ied;
1386         struct dundi_peer *peer;
1387         unsigned char iv[16];
1388         len = pack->datalen + pack->datalen / 100 + 42;
1389         compress_space = alloca(len);
1390         if (compress_space) {
1391                 memset(compress_space, 0, len);
1392                 /* We care about everthing save the first 6 bytes of header */
1393                 bytes = len;
1394                 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1395                 if (res != Z_OK) {
1396                         ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1397                         return -1;
1398                 }
1399                 memset(&ied, 0, sizeof(ied));
1400                 /* Say who we are */
1401                 if (!pack->h->iseqno && !pack->h->oseqno) {
1402                         /* Need the key in the first copy */
1403                         if (!(peer = find_peer(&trans->them_eid))) 
1404                                 return -1;
1405                         if (update_key(peer))
1406                                 return -1;
1407                         if (!peer->sentfullkey)
1408                                 ast_set_flag(trans, FLAG_SENDFULLKEY);  
1409                         /* Append key data */
1410                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1411                         if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1412                                 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1413                                 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1414                         } else {
1415                                 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1416                         }
1417                         /* Setup contexts */
1418                         trans->ecx = peer->us_ecx;
1419                         trans->dcx = peer->us_dcx;
1420
1421                         /* We've sent the full key */
1422                         peer->sentfullkey = 1;
1423                 }
1424                 /* Build initialization vector */
1425                 build_iv(iv);
1426                 /* Add the field, rounded up to 16 bytes */
1427                 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1428                 /* Copy the data */
1429                 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1430                         ast_log(LOG_NOTICE, "Final packet too large!\n");
1431                         return -1;
1432                 }
1433                 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1434                 ied.pos += ((bytes + 15) / 16) * 16;
1435                 /* Reconstruct header */
1436                 pack->datalen = sizeof(struct dundi_hdr);
1437                 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1438                 pack->h->cmdflags = 0;
1439                 memcpy(pack->h->ies, ied.buf, ied.pos);
1440                 pack->datalen += ied.pos;
1441                 return 0;
1442         }
1443         return -1;
1444 }
1445
1446 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1447 {
1448         unsigned char dst[128];
1449         int res;
1450         struct ast_key *key, *skey;
1451         char eid_str[20];
1452         if (option_debug)
1453                 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1454         if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1455                 /* A match */
1456                 return 1;
1457         } else if (!newkey || !newsig)
1458                 return 0;
1459         if (!memcmp(peer->rxenckey, newkey, 128) &&
1460             !memcmp(peer->rxenckey + 128, newsig, 128)) {
1461                 /* By definition, a match */
1462                 return 1;
1463         }
1464         /* Decrypt key */
1465         key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1466         if (!key) {
1467                 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1468                         peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1469                 return -1;
1470         }
1471
1472         skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1473         if (!skey) {
1474                 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1475                         peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1476                 return -1;
1477         }
1478
1479         /* First check signature */
1480         res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1481         if (res) 
1482                 return 0;
1483
1484         res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1485         if (res != 16) {
1486                 if (res >= 0)
1487                         ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1488                 return 0;
1489         }
1490         /* Decrypted, passes signature */
1491         ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1492         memcpy(peer->rxenckey, newkey, 128);
1493         memcpy(peer->rxenckey + 128, newsig, 128);
1494         peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1495         aes_decrypt_key128(dst, &peer->them_dcx);
1496         aes_encrypt_key128(dst, &peer->them_ecx);
1497         return 1;
1498 }
1499
1500 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1501 {
1502         /* Handle canonical command / response */
1503         int final = hdr->cmdresp & 0x80;
1504         int cmd = hdr->cmdresp & 0x7f;
1505         int x,y,z;
1506         int resp;
1507         int res;
1508         int authpass=0;
1509         unsigned char *bufcpy;
1510         struct dundi_ie_data ied;
1511         struct dundi_ies ies;
1512         struct dundi_peer *peer;
1513         char eid_str[20];
1514         char eid_str2[20];
1515         memset(&ied, 0, sizeof(ied));
1516         memset(&ies, 0, sizeof(ies));
1517         if (datalen) {
1518                 bufcpy = alloca(datalen);
1519                 if (!bufcpy)
1520                         return -1;
1521                 /* Make a copy for parsing */
1522                 memcpy(bufcpy, hdr->ies, datalen);
1523                 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1524                 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1525                         ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1526                         return -1;
1527                 }
1528         }
1529         switch(cmd) {
1530         case DUNDI_COMMAND_DPDISCOVER:
1531         case DUNDI_COMMAND_EIDQUERY:
1532         case DUNDI_COMMAND_PRECACHERQ:
1533                 if (cmd == DUNDI_COMMAND_EIDQUERY)
1534                         resp = DUNDI_COMMAND_EIDRESPONSE;
1535                 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1536                         resp = DUNDI_COMMAND_PRECACHERP;
1537                 else
1538                         resp = DUNDI_COMMAND_DPRESPONSE;
1539                 /* A dialplan or entity discover -- qualify by highest level entity */
1540                 peer = find_peer(ies.eids[0]);
1541                 if (!peer) {
1542                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1543                         dundi_send(trans, resp, 0, 1, &ied);
1544                 } else {
1545                         int hasauth = 0;
1546                         trans->us_eid = peer->us_eid;
1547                         if (strlen(peer->inkey)) {
1548                                 hasauth = encrypted;
1549                         } else 
1550                                 hasauth = 1;
1551                         if (hasauth) {
1552                                 /* Okay we're authentiated and all, now we check if they're authorized */
1553                                 if (!ies.called_context)
1554                                         ies.called_context = "e164";
1555                                 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1556                                         res = dundi_answer_entity(trans, &ies, ies.called_context);
1557                                 } else {
1558                                         if (ast_strlen_zero(ies.called_number)) {
1559                                                 /* They're not permitted to access that context */
1560                                                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1561                                                 dundi_send(trans, resp, 0, 1, &ied);
1562                                         } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) && 
1563                                                    (peer->model & DUNDI_MODEL_INBOUND) && 
1564                                                            has_permission(peer->permit, ies.called_context)) {
1565                                                 res = dundi_answer_query(trans, &ies, ies.called_context);
1566                                                 if (res < 0) {
1567                                                         /* There is no such dundi context */
1568                                                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1569                                                         dundi_send(trans, resp, 0, 1, &ied);
1570                                                 }
1571                                         } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) && 
1572                                                    (peer->pcmodel & DUNDI_MODEL_INBOUND) && 
1573                                                            has_permission(peer->include, ies.called_context)) {
1574                                                 res = dundi_prop_precache(trans, &ies, ies.called_context);
1575                                                 if (res < 0) {
1576                                                         /* There is no such dundi context */
1577                                                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1578                                                         dundi_send(trans, resp, 0, 1, &ied);
1579                                                 }
1580                                         } else {
1581                                                 /* They're not permitted to access that context */
1582                                                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1583                                                 dundi_send(trans, resp, 0, 1, &ied);
1584                                         }
1585                                 }
1586                         } else {
1587                                 /* They're not permitted to access that context */
1588                                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1589                                 dundi_send(trans, resp, 0, 1, &ied);
1590                         }
1591                 }
1592                 break;
1593         case DUNDI_COMMAND_REGREQ:
1594                 /* A register request -- should only have one entity */
1595                 peer = find_peer(ies.eids[0]);
1596                 if (!peer || !peer->dynamic) {
1597                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1598                         dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1599                 } else {
1600                         int hasauth = 0;
1601                         trans->us_eid = peer->us_eid;
1602                         if (!ast_strlen_zero(peer->inkey)) {
1603                                 hasauth = encrypted;
1604                         } else
1605                                 hasauth = 1;
1606                         if (hasauth) {
1607                                 int expire = default_expiration;
1608                                 char iabuf[INET_ADDRSTRLEN];
1609                                 char data[256];
1610                                 int needqual = 0;
1611                                 if (peer->registerexpire > -1)
1612                                         ast_sched_del(sched, peer->registerexpire);
1613                                 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1614                                 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1615                                 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1616                                 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1617                                 if (inaddrcmp(&peer->addr, &trans->addr)) {
1618                                         if (option_verbose > 2)
1619                                                 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));
1620                                         needqual = 1;
1621                                 }
1622                                         
1623                                 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1624                                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1625                                 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1626                                 if (needqual)
1627                                         qualify_peer(peer, 1);
1628                         }
1629                 }
1630                 break;
1631         case DUNDI_COMMAND_DPRESPONSE:
1632                 /* A dialplan response, lets see what we got... */
1633                 if (ies.cause < 1) {
1634                         /* Success of some sort */
1635                         ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1636                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1637                                 authpass = encrypted;
1638                         } else 
1639                                 authpass = 1;
1640                         if (authpass) {
1641                                 /* Pass back up answers */
1642                                 if (trans->parent && trans->parent->dr) {
1643                                         y = trans->parent->respcount;
1644                                         for (x=0;x<ies.anscount;x++) {
1645                                                 if (trans->parent->respcount < trans->parent->maxcount) {
1646                                                         /* Make sure it's not already there */
1647                                                         for (z=0;z<trans->parent->respcount;z++) {
1648                                                                 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1649                                                                     !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data)) 
1650                                                                                 break;
1651                                                         }
1652                                                         if (z == trans->parent->respcount) {
1653                                                                 /* Copy into parent responses */
1654                                                                 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1655                                                                 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1656                                                                 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1657                                                                 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1658                                                                 if (ies.expiration > 0)
1659                                                                         trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1660                                                                 else
1661                                                                         trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1662                                                                 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
1663                                                                         sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1664                                                                         &ies.answers[x]->eid);
1665                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1666                                                                         sizeof(trans->parent->dr[trans->parent->respcount].dest));
1667                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1668                                                                         sizeof(trans->parent->dr[trans->parent->respcount].tech));
1669                                                                 trans->parent->respcount++;
1670                                                                 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1671                                                         } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1672                                                                 /* Update weight if appropriate */
1673                                                                 trans->parent->dr[z].weight = ies.answers[x]->weight;
1674                                                         }
1675                                                 } else
1676                                                         ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1677                                                                 trans->parent->number, trans->parent->dcontext);
1678                                         }
1679                                         /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
1680                                            the cache know if this request was unaffected by our entity list. */
1681                                         cache_save(&trans->them_eid, trans->parent, y, 
1682                                                         ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1683                                         if (ies.hint) {
1684                                                 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1685                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1686                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1687                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) { 
1688                                                         if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1689                                                                 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data, 
1690                                                                         sizeof(trans->parent->hmd->exten));
1691                                                         }
1692                                                 } else {
1693                                                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1694                                                 }
1695                                         }
1696                                         if (ies.expiration > 0) {
1697                                                 if (trans->parent->expiration > ies.expiration) {
1698                                                         trans->parent->expiration = ies.expiration;
1699                                                 }
1700                                         }
1701                                 }
1702                                 /* Close connection if not final */
1703                                 if (!final) 
1704                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1705                         }
1706                         
1707                 } else {
1708                         /* Auth failure, check for data */
1709                         if (!final) {
1710                                 /* Cancel if they didn't already */
1711                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1712                         }
1713                 }
1714                 break;
1715         case DUNDI_COMMAND_EIDRESPONSE:
1716                 /* A dialplan response, lets see what we got... */
1717                 if (ies.cause < 1) {
1718                         /* Success of some sort */
1719                         ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1720                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1721                                 authpass = encrypted;
1722                         } else 
1723                                 authpass = 1;
1724                         if (authpass) {
1725                                 /* Pass back up answers */
1726                                 if (trans->parent && trans->parent->dei && ies.q_org) {
1727                                         if (!trans->parent->respcount) {
1728                                                 trans->parent->respcount++;
1729                                                 if (ies.q_dept)
1730                                                         ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1731                                                 if (ies.q_org)
1732                                                         ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1733                                                 if (ies.q_locality)
1734                                                         ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1735                                                 if (ies.q_stateprov)
1736                                                         ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1737                                                 if (ies.q_country)
1738                                                         ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1739                                                 if (ies.q_email)
1740                                                         ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1741                                                 if (ies.q_phone)
1742                                                         ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1743                                                 if (ies.q_ipaddr)
1744                                                         ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1745                                                 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1746                                                         /* If it's them, update our address */
1747                                                         ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1748                                                                 trans->addr.sin_addr);
1749                                                 }
1750                                         }
1751                                         if (ies.hint) {
1752                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1753                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1754                                         }
1755                                 }
1756                                 /* Close connection if not final */
1757                                 if (!final) 
1758                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1759                         }
1760                         
1761                 } else {
1762                         /* Auth failure, check for data */
1763                         if (!final) {
1764                                 /* Cancel if they didn't already */
1765                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1766                         }
1767                 }
1768                 break;
1769         case DUNDI_COMMAND_REGRESPONSE:
1770                 /* A dialplan response, lets see what we got... */
1771                 if (ies.cause < 1) {
1772                         int hasauth;
1773                         /* Success of some sort */
1774                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1775                                 hasauth = encrypted;
1776                         } else 
1777                                 hasauth = 1;
1778                         
1779                         if (!hasauth) {
1780                                 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1781                                 if (!final) {
1782                                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1783                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1784                                 }
1785                         } else {
1786                                 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),
1787                                                         dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1788                                 /* Close connection if not final */
1789                                 if (!final) 
1790                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1791                         }
1792                 } else {
1793                         /* Auth failure, cancel if they didn't for some reason */
1794                         if (!final) {
1795                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1796                         }
1797                 }
1798                 break;
1799         case DUNDI_COMMAND_INVALID:
1800         case DUNDI_COMMAND_NULL:
1801         case DUNDI_COMMAND_PRECACHERP:
1802                 /* Do nothing special */
1803                 if (!final) 
1804                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1805                 break;
1806         case DUNDI_COMMAND_ENCREJ:
1807                 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
1808                         /* No really, it's over at this point */
1809                         if (!final) 
1810                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1811                 } else {
1812                         /* Send with full key */
1813                         ast_set_flag(trans, FLAG_SENDFULLKEY);
1814                         if (final) {
1815                                 /* Ooops, we got a final message, start by sending ACK... */
1816                                 dundi_ack(trans, hdr->cmdresp & 0x80);
1817                                 trans->aseqno = trans->iseqno;
1818                                 /* Now, we gotta create a new transaction */
1819                                 if (!reset_transaction(trans)) {
1820                                         /* Make sure handle_frame doesn't destroy us */
1821                                         hdr->cmdresp &= 0x7f;
1822                                         /* Parse the message we transmitted */
1823                                         memset(&ies, 0, sizeof(ies));
1824                                         dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
1825                                         /* Reconstruct outgoing encrypted packet */
1826                                         memset(&ied, 0, sizeof(ied));
1827                                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1828                                         dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1829                                         dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1830                                         if (ies.encblock) 
1831                                                 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1832                                         dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
1833                                         peer->sentfullkey = 1;
1834                                 }
1835                         }
1836                 }
1837                 break;
1838         case DUNDI_COMMAND_ENCRYPT:
1839                 if (!encrypted) {
1840                         /* No nested encryption! */
1841                         if ((trans->iseqno == 1) && !trans->oseqno) {
1842                                 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || 
1843                                         ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || 
1844                                         (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1845                                         if (!final) {
1846                                                 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1847                                         }
1848                                         break;
1849                                 }
1850                                 apply_peer(trans, peer);
1851                                 /* Key passed, use new contexts for this session */
1852                                 trans->ecx = peer->them_ecx;
1853                                 trans->dcx = peer->them_dcx;
1854                         }
1855                         if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1856                                 struct dundi_hdr *dhdr;
1857                                 unsigned char decoded[MAX_PACKET_SIZE];
1858                                 int ddatalen;
1859                                 ddatalen = sizeof(decoded);
1860                                 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1861                                 if (dhdr) {
1862                                         /* Handle decrypted response */
1863                                         if (dundidebug)
1864                                                 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1865                                         handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1866                                         /* Carry back final flag */
1867                                         hdr->cmdresp |= dhdr->cmdresp & 0x80;
1868                                         break;
1869                                 } else
1870                                         ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1871                         }
1872                 }
1873                 if (!final) {
1874                         /* Turn off encryption */
1875                         ast_clear_flag(trans, FLAG_ENCRYPT);
1876                         dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1877                 }
1878                 break;
1879         default:
1880                 /* Send unknown command if we don't know it, with final flag IFF it's the
1881                    first command in the dialog and only if we haven't recieved final notification */
1882                 if (!final) {
1883                         dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1884                         dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1885                 }
1886         }
1887         return 0;
1888 }
1889
1890 static void destroy_packet(struct dundi_packet *pack, int needfree);
1891 static void destroy_packets(struct dundi_packet *p)
1892 {
1893         struct dundi_packet *prev;
1894         while(p) {
1895                 prev = p;
1896                 p = p->next;
1897                 if (prev->retransid > -1)
1898                         ast_sched_del(sched, prev->retransid);
1899                 free(prev);
1900         }
1901 }
1902
1903
1904 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1905 {
1906         /* Ack transmitted packet corresponding to iseqno */
1907         struct dundi_packet *pack;
1908         pack = trans->packets;
1909         while(pack) {
1910                 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1911                         destroy_packet(pack, 0);
1912                         if (trans->lasttrans) {
1913                                 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1914                                 destroy_packets(trans->lasttrans);
1915                         }
1916                         trans->lasttrans = pack;
1917                         if (trans->autokillid > -1)
1918                                 ast_sched_del(sched, trans->autokillid);
1919                         trans->autokillid = -1;
1920                         return 1;
1921                 }
1922                 pack = pack->next;
1923         }
1924         return 0;
1925 }
1926
1927 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1928 {
1929         struct dundi_transaction *trans;
1930         trans = find_transaction(h, sin);
1931         if (!trans) {
1932                 dundi_reject(h, sin);
1933                 return 0;
1934         }
1935         /* Got a transaction, see where this header fits in */
1936         if (h->oseqno == trans->iseqno) {
1937                 /* Just what we were looking for...  Anything but ack increments iseqno */
1938                 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1939                         /* If final, we're done */
1940                         destroy_trans(trans, 0);
1941                         return 0;
1942                 }
1943                 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1944                         trans->oiseqno = trans->iseqno;
1945                         trans->iseqno++;
1946                         handle_command_response(trans, h, datalen, 0);
1947                 }
1948                 if (trans->aseqno != trans->iseqno) {
1949                         dundi_ack(trans, h->cmdresp & 0x80);
1950                         trans->aseqno = trans->iseqno;
1951                 }
1952                 /* Delete any saved last transmissions */
1953                 destroy_packets(trans->lasttrans);
1954                 trans->lasttrans = NULL;
1955                 if (h->cmdresp & 0x80) {
1956                         /* Final -- destroy now */
1957                         destroy_trans(trans, 0);
1958                 }
1959         } else if (h->oseqno == trans->oiseqno) {
1960                 /* Last incoming sequence number -- send ACK without processing */
1961                 dundi_ack(trans, 0);
1962         } else {
1963                 /* Out of window -- simply drop */
1964                 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1965         }
1966         return 0;
1967 }
1968
1969 static int socket_read(int *id, int fd, short events, void *cbdata)
1970 {
1971         struct sockaddr_in sin;
1972         int res;
1973         struct dundi_hdr *h;
1974         char buf[MAX_PACKET_SIZE];
1975         socklen_t len;
1976         len = sizeof(sin);
1977         res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1978         if (res < 0) {
1979                 if (errno != ECONNREFUSED)
1980                         ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1981                 return 1;
1982         }
1983         if (res < sizeof(struct dundi_hdr)) {
1984                 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1985                 return 1;
1986         }
1987         buf[res] = '\0';
1988         h = (struct dundi_hdr *)buf;
1989         if (dundidebug)
1990                 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1991         ast_mutex_lock(&peerlock);
1992         handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
1993         ast_mutex_unlock(&peerlock);
1994         return 1;
1995 }
1996
1997 static void build_secret(char *secret, int seclen)
1998 {
1999         unsigned char tmp[16];
2000         char *s;
2001         build_iv(tmp);
2002         secret[0] = '\0';
2003         ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2004         /* Eliminate potential bad characters */
2005         while((s = strchr(secret, ';'))) *s = '+';
2006         while((s = strchr(secret, '/'))) *s = '+';
2007         while((s = strchr(secret, ':'))) *s = '+';
2008         while((s = strchr(secret, '@'))) *s = '+';
2009 }
2010
2011
2012 static void save_secret(const char *newkey, const char *oldkey)
2013 {
2014         char tmp[256];
2015         if (oldkey)
2016                 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2017         else
2018                 snprintf(tmp, sizeof(tmp), "%s", newkey);
2019         rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2020         ast_db_put(secretpath, "secret", tmp);
2021         snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2022         ast_db_put(secretpath, "secretexpiry", tmp);
2023 }
2024
2025 static void load_password(void)
2026 {
2027         char *current=NULL;
2028         char *last=NULL;
2029         char tmp[256];
2030         time_t expired;
2031         
2032         ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2033         if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2034                 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2035                 current = strchr(tmp, ';');
2036                 if (!current)
2037                         current = tmp;
2038                 else {
2039                         *current = '\0';
2040                         current++;
2041                 };
2042                 if ((time(NULL) - expired) < 0) {
2043                         if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2044                                 expired = time(NULL) + DUNDI_SECRET_TIME;
2045                 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2046                         last = current;
2047                         current = NULL;
2048                 } else {
2049                         last = NULL;
2050                         current = NULL;
2051                 }
2052         }
2053         if (current) {
2054                 /* Current key is still valid, just setup rotatation properly */
2055                 ast_copy_string(cursecret, current, sizeof(cursecret));
2056                 rotatetime = expired;
2057         } else {
2058                 /* Current key is out of date, rotate or eliminate all together */
2059                 build_secret(cursecret, sizeof(cursecret));
2060                 save_secret(cursecret, last);
2061         }
2062 }
2063
2064 static void check_password(void)
2065 {
2066         char oldsecret[80];
2067         time_t now;
2068         
2069         time(&now);     
2070 #if 0
2071         printf("%ld/%ld\n", now, rotatetime);
2072 #endif
2073         if ((now - rotatetime) >= 0) {
2074                 /* Time to rotate keys */
2075                 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2076                 build_secret(cursecret, sizeof(cursecret));
2077                 save_secret(cursecret, oldsecret);
2078         }
2079 }
2080
2081 static void *network_thread(void *ignore)
2082 {
2083         /* Our job is simple: Send queued messages, retrying if necessary.  Read frames 
2084            from the network, and queue them for delivery to the channels */
2085         int res;
2086         /* Establish I/O callback for socket read */
2087         ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2088         for(;;) {
2089                 res = ast_sched_wait(sched);
2090                 if ((res > 1000) || (res < 0))
2091                         res = 1000;
2092                 res = ast_io_wait(io, res);
2093                 if (res >= 0) {
2094                         ast_mutex_lock(&peerlock);
2095                         ast_sched_runq(sched);
2096                         ast_mutex_unlock(&peerlock);
2097                 }
2098                 check_password();
2099         }
2100         return NULL;
2101 }
2102
2103 static void *process_precache(void *ign)
2104 {
2105         struct dundi_precache_queue *qe;
2106         time_t now;
2107         char context[256];
2108         char number[256];
2109         int run;
2110         for (;;) {
2111                 time(&now);
2112                 run = 0;
2113                 ast_mutex_lock(&pclock);
2114                 if (pcq) {
2115                         if (!pcq->expiration) {
2116                                 /* Gone...  Remove... */
2117                                 qe = pcq;
2118                                 pcq = pcq->next;
2119                                 free(qe);
2120                         } else if (pcq->expiration < now) {
2121                                 /* Process this entry */
2122                                 pcq->expiration = 0;
2123                                 ast_copy_string(context, pcq->context, sizeof(context));
2124                                 ast_copy_string(number, pcq->number, sizeof(number));
2125                                 run = 1;
2126                         }
2127                 }
2128                 ast_mutex_unlock(&pclock);
2129                 if (run) {
2130                         dundi_precache(context, number);
2131                 } else
2132                         sleep(1);
2133         }
2134         return NULL;
2135 }
2136
2137 static int start_network_thread(void)
2138 {
2139         ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
2140         ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
2141         return 0;
2142 }
2143
2144 static int dundi_do_debug(int fd, int argc, char *argv[])
2145 {
2146         if (argc != 2)
2147                 return RESULT_SHOWUSAGE;
2148         dundidebug = 1;
2149         ast_cli(fd, "DUNDi Debugging Enabled\n");
2150         return RESULT_SUCCESS;
2151 }
2152
2153 static int dundi_do_store_history(int fd, int argc, char *argv[])
2154 {
2155         if (argc != 3)
2156                 return RESULT_SHOWUSAGE;
2157         global_storehistory = 1;
2158         ast_cli(fd, "DUNDi History Storage Enabled\n");
2159         return RESULT_SUCCESS;
2160 }
2161
2162 static int dundi_flush(int fd, int argc, char *argv[])
2163 {
2164         int stats=0;
2165         if ((argc < 2) || (argc > 3))
2166                 return RESULT_SHOWUSAGE;
2167         if (argc > 2) {
2168                 if (!strcasecmp(argv[2], "stats"))
2169                         stats = 1;
2170                 else
2171                         return RESULT_SHOWUSAGE;
2172         }
2173         if (stats) {
2174                 /* Flush statistics */
2175                 struct dundi_peer *p;
2176                 int x;
2177                 ast_mutex_lock(&peerlock);
2178                 p = peers;
2179                 while(p) {
2180                         for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2181                                 if (p->lookups[x])
2182                                         free(p->lookups[x]);
2183                                 p->lookups[x] = NULL;
2184                                 p->lookuptimes[x] = 0;
2185                         }
2186                         p->avgms = 0;
2187                         p = p->next;
2188                 }
2189                 ast_mutex_unlock(&peerlock);
2190         } else {
2191                 ast_db_deltree("dundi/cache", NULL);
2192                 ast_cli(fd, "DUNDi Cache Flushed\n");
2193         }
2194         return RESULT_SUCCESS;
2195 }
2196
2197 static int dundi_no_debug(int fd, int argc, char *argv[])
2198 {
2199         if (argc != 3)
2200                 return RESULT_SHOWUSAGE;
2201         dundidebug = 0;
2202         ast_cli(fd, "DUNDi Debugging Disabled\n");
2203         return RESULT_SUCCESS;
2204 }
2205
2206 static int dundi_no_store_history(int fd, int argc, char *argv[])
2207 {
2208         if (argc != 4)
2209                 return RESULT_SHOWUSAGE;
2210         global_storehistory = 0;
2211         ast_cli(fd, "DUNDi History Storage Disabled\n");
2212         return RESULT_SUCCESS;
2213 }
2214
2215 static char *model2str(int model)
2216 {
2217         switch(model) {
2218         case DUNDI_MODEL_INBOUND:
2219                 return "Inbound";
2220         case DUNDI_MODEL_OUTBOUND:
2221                 return "Outbound";
2222         case DUNDI_MODEL_SYMMETRIC:
2223                 return "Symmetric";
2224         default:
2225                 return "Unknown";
2226         }
2227 }
2228
2229 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2230 {
2231         int which=0, len;
2232         char *ret = NULL;
2233         struct dundi_peer *p;
2234         char eid_str[20];
2235
2236         if (pos != rpos)
2237                 return NULL;
2238         ast_mutex_lock(&peerlock);
2239         len = strlen(word);
2240         for (p = peers; !ret && p; p = p->next) {
2241                 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2242                 if (!strncasecmp(word, s, len) && ++which > state)
2243                         ret = ast_strdup(s);
2244         }
2245         ast_mutex_unlock(&peerlock);
2246         return ret;
2247 }
2248
2249 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2250 {
2251         return complete_peer_helper(line, word, pos, state, 3);
2252 }
2253
2254 static int rescomp(const void *a, const void *b)
2255 {
2256         const struct dundi_result *resa, *resb;
2257         resa = a;
2258         resb = b;
2259         if (resa->weight < resb->weight)
2260                 return -1;
2261         if (resa->weight > resb->weight)
2262                 return 1;
2263         return 0;
2264 }
2265
2266 static void sort_results(struct dundi_result *results, int count)
2267 {
2268         qsort(results, count, sizeof(results[0]), rescomp);
2269 }
2270
2271 static int dundi_do_lookup(int fd, int argc, char *argv[])
2272 {
2273         int res;
2274         char tmp[256];
2275         char fs[80] = "";
2276         char *context;
2277         int x;
2278         int bypass = 0;
2279         struct dundi_result dr[MAX_RESULTS];
2280         struct timeval start;
2281         if ((argc < 3) || (argc > 4))
2282                 return RESULT_SHOWUSAGE;
2283         if (argc > 3) {
2284                 if (!strcasecmp(argv[3], "bypass"))
2285                         bypass=1;
2286                 else
2287                         return RESULT_SHOWUSAGE;
2288         }
2289         ast_copy_string(tmp, argv[2], sizeof(tmp));
2290         context = strchr(tmp, '@');
2291         if (context) {
2292                 *context = '\0';
2293                 context++;
2294         }
2295         start = ast_tvnow();
2296         res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2297         
2298         if (res < 0) 
2299                 ast_cli(fd, "DUNDi lookup returned error.\n");
2300         else if (!res) 
2301                 ast_cli(fd, "DUNDi lookup returned no results.\n");
2302         else
2303                 sort_results(dr, res);
2304         for (x=0;x<res;x++) {
2305                 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));
2306                 ast_cli(fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2307         }
2308         ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2309         return RESULT_SUCCESS;
2310 }
2311
2312 static int dundi_do_precache(int fd, int argc, char *argv[])
2313 {
2314         int res;
2315         char tmp[256];
2316         char *context;
2317         struct timeval start;
2318         if ((argc < 3) || (argc > 3))
2319                 return RESULT_SHOWUSAGE;
2320         ast_copy_string(tmp, argv[2], sizeof(tmp));
2321         context = strchr(tmp, '@');
2322         if (context) {
2323                 *context = '\0';
2324                 context++;
2325         }
2326         start = ast_tvnow();
2327         res = dundi_precache(context, tmp);
2328         
2329         if (res < 0) 
2330                 ast_cli(fd, "DUNDi precache returned error.\n");
2331         else if (!res) 
2332                 ast_cli(fd, "DUNDi precache returned no error.\n");
2333         ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2334         return RESULT_SUCCESS;
2335 }
2336
2337 static int dundi_do_query(int fd, int argc, char *argv[])
2338 {
2339         int res;
2340         char tmp[256];
2341         char *context;
2342         dundi_eid eid;
2343         struct dundi_entity_info dei;
2344         if ((argc < 3) || (argc > 3))
2345                 return RESULT_SHOWUSAGE;
2346         if (dundi_str_to_eid(&eid, argv[2])) {
2347                 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2348                 return RESULT_SHOWUSAGE;
2349         }
2350         ast_copy_string(tmp, argv[2], sizeof(tmp));
2351         context = strchr(tmp, '@');
2352         if (context) {
2353                 *context = '\0';
2354                 context++;
2355         }
2356         res = dundi_query_eid(&dei, context, eid);
2357         if (res < 0) 
2358                 ast_cli(fd, "DUNDi Query EID returned error.\n");
2359         else if (!res) 
2360                 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2361         else {
2362                 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2363                 ast_cli(fd, "Department:      %s\n", dei.orgunit);
2364                 ast_cli(fd, "Organization:    %s\n", dei.org);
2365                 ast_cli(fd, "City/Locality:   %s\n", dei.locality);
2366                 ast_cli(fd, "State/Province:  %s\n", dei.stateprov);
2367                 ast_cli(fd, "Country:         %s\n", dei.country);
2368                 ast_cli(fd, "E-mail:          %s\n", dei.email);
2369                 ast_cli(fd, "Phone:           %s\n", dei.phone);
2370                 ast_cli(fd, "IP Address:      %s\n", dei.ipaddr);
2371         }
2372         return RESULT_SUCCESS;
2373 }
2374
2375 static int dundi_show_peer(int fd, int argc, char *argv[])
2376 {
2377         struct dundi_peer *peer;
2378         struct permission *p;
2379         char *order;
2380         char iabuf[INET_ADDRSTRLEN];
2381         char eid_str[20];
2382         int x, cnt;
2383         
2384         if (argc != 4)
2385                 return RESULT_SHOWUSAGE;
2386         ast_mutex_lock(&peerlock);
2387         peer = peers;
2388         while(peer) {
2389                 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2390                         break;
2391                 peer = peer->next;
2392         }
2393         if (peer) {
2394                 switch(peer->order) {
2395                 case 0:
2396                         order = "Primary";
2397                         break;
2398                 case 1:
2399                         order = "Secondary";
2400                         break;
2401                 case 2:
2402                         order = "Tertiary";
2403                         break;
2404                 case 3:
2405                         order = "Quartiary";
2406                         break;
2407                 default:
2408                         order = "Unknown";
2409                 }
2410                 ast_cli(fd, "Peer:    %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2411                 ast_cli(fd, "Model:   %s\n", model2str(peer->model));
2412                 ast_cli(fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2413                 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2414                 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2415                 ast_cli(fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
2416                 ast_cli(fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2417                 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2418                 if (peer->include) {
2419                         ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2420                 }
2421                 p = peer->include;
2422                 while(p) {
2423                         ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2424                         p = p->next;
2425                 }
2426                 if (peer->permit) {
2427                         ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2428                 }
2429                 p = peer->permit;
2430                 while(p) {
2431                         ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2432                         p = p->next;
2433                 }
2434                 cnt = 0;
2435                 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2436                         if (peer->lookups[x]) {
2437                                 if (!cnt)
2438                                         ast_cli(fd, "Last few query times:\n");
2439                                 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2440                                 cnt++;
2441                         }
2442                 }
2443                 if (cnt)
2444                         ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2445         } else
2446                 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2447         ast_mutex_unlock(&peerlock);
2448         return RESULT_SUCCESS;
2449 }
2450
2451 static int dundi_show_peers(int fd, int argc, char *argv[])
2452 {
2453 #define FORMAT2 "%-20.20s %-15.15s     %-10.10s %-8.8s %-15.15s\n"
2454 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2455         struct dundi_peer *peer;
2456         char iabuf[INET_ADDRSTRLEN];
2457         int registeredonly=0;
2458         char avgms[20];
2459         char eid_str[20];
2460         int online_peers = 0;
2461         int offline_peers = 0;
2462         int unmonitored_peers = 0;
2463         int total_peers = 0;
2464
2465         if ((argc != 3) && (argc != 4) && (argc != 5))
2466                 return RESULT_SHOWUSAGE;
2467         if ((argc == 4)) {
2468                 if (!strcasecmp(argv[3], "registered")) {
2469                         registeredonly = 1;
2470                 } else
2471                         return RESULT_SHOWUSAGE;
2472         }
2473         ast_mutex_lock(&peerlock);
2474         ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2475         for (peer = peers;peer;peer = peer->next) {
2476                 char status[20];
2477                 int print_line = -1;
2478                 char srch[2000];
2479                 total_peers++;
2480                 if (registeredonly && !peer->addr.sin_addr.s_addr)
2481                         continue;
2482                 if (peer->maxms) {
2483                         if (peer->lastms < 0) {
2484                                 strcpy(status, "UNREACHABLE");
2485                                 offline_peers++;
2486                         }
2487                         else if (peer->lastms > peer->maxms) {
2488                                 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2489                                 offline_peers++;
2490                         }
2491                         else if (peer->lastms) {
2492                                 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2493                                 online_peers++;
2494                         }
2495                         else {
2496                                 strcpy(status, "UNKNOWN");
2497                                 offline_peers++;
2498                         }
2499                 } else {
2500                         strcpy(status, "Unmonitored");
2501                         unmonitored_peers++;
2502                 }
2503                 if (peer->avgms) 
2504                         snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2505                 else
2506                         strcpy(avgms, "Unavail");
2507                 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2508                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2509                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2510
2511                 if (argc == 5) {
2512                   if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2513                         print_line = -1;
2514                    } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2515                         print_line = 1;
2516                    } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2517                         print_line = -1;
2518                    } else {
2519                         print_line = 0;
2520                   }
2521                 }
2522                 
2523         if (print_line) {
2524                         ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2525                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2526                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2527                 }
2528         }
2529         ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2530         ast_mutex_unlock(&peerlock);
2531         return RESULT_SUCCESS;
2532 #undef FORMAT
2533 #undef FORMAT2
2534 }
2535
2536 static int dundi_show_trans(int fd, int argc, char *argv[])
2537 {
2538 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2539 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2540         struct dundi_transaction *trans;
2541         char iabuf[INET_ADDRSTRLEN];
2542         if (argc != 3)
2543                 return RESULT_SHOWUSAGE;
2544         ast_mutex_lock(&peerlock);
2545         ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2546         for (trans = alltrans;trans;trans = trans->allnext) {
2547                         ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr), 
2548                                         ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2549         }
2550         ast_mutex_unlock(&peerlock);
2551         return RESULT_SUCCESS;
2552 #undef FORMAT
2553 #undef FORMAT2
2554 }
2555
2556 static int dundi_show_entityid(int fd, int argc, char *argv[])
2557 {
2558         char eid_str[20];
2559         if (argc != 3)
2560                 return RESULT_SHOWUSAGE;
2561         ast_mutex_lock(&peerlock);
2562         dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2563         ast_mutex_unlock(&peerlock);
2564         ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2565         return RESULT_SUCCESS;
2566 }
2567
2568 static int dundi_show_requests(int fd, int argc, char *argv[])
2569 {
2570 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2571 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2572         struct dundi_request *req;
2573         char eidstr[20];
2574         if (argc != 3)
2575                 return RESULT_SHOWUSAGE;
2576         ast_mutex_lock(&peerlock);
2577         ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2578         for (req = requests;req;req = req->next) {
2579                         ast_cli(fd, FORMAT, req->number, req->dcontext,
2580                                                 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2581         }
2582         ast_mutex_unlock(&peerlock);
2583         return RESULT_SUCCESS;
2584 #undef FORMAT
2585 #undef FORMAT2
2586 }
2587
2588 /* Grok-a-dial DUNDi */
2589
2590 static int dundi_show_mappings(int fd, int argc, char *argv[])
2591 {
2592 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2593 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2594         struct dundi_mapping *map;
2595         char fs[256];
2596         if (argc != 3)
2597                 return RESULT_SHOWUSAGE;
2598         ast_mutex_lock(&peerlock);
2599         ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2600         for (map = mappings;map;map = map->next) {
2601                         ast_cli(fd, FORMAT, map->dcontext, map->weight, 
2602                                             ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
2603                                                                 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2604         }
2605         ast_mutex_unlock(&peerlock);
2606         return RESULT_SUCCESS;
2607 #undef FORMAT
2608 #undef FORMAT2
2609 }
2610
2611 static int dundi_show_precache(int fd, int argc, char *argv[])
2612 {
2613 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2614 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2615         struct dundi_precache_queue *qe;
2616         int h,m,s;
2617         time_t now;
2618         
2619         if (argc != 3)
2620                 return RESULT_SHOWUSAGE;
2621         time(&now);
2622         ast_mutex_lock(&pclock);
2623         ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2624         for (qe = pcq;qe;qe = qe->next) {
2625                 s = qe->expiration - now;
2626                 h = s / 3600;
2627                 s = s % 3600;
2628                 m = s / 60;
2629                 s = s % 60;
2630                 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2631         }
2632         ast_mutex_unlock(&pclock);
2633         return RESULT_SUCCESS;
2634 #undef FORMAT
2635 #undef FORMAT2
2636 }
2637
2638 static char debug_usage[] = 
2639 "Usage: dundi debug\n"
2640 "       Enables dumping of DUNDi packets for debugging purposes\n";
2641
2642 static char no_debug_usage[] = 
2643 "Usage: dundi no debug\n"
2644 "       Disables dumping of DUNDi packets for debugging purposes\n";
2645
2646 static char store_history_usage[] = 
2647 "Usage: dundi store history\n"
2648 "       Enables storing of DUNDi requests and times for debugging\n"
2649 "purposes\n";
2650
2651 static char no_store_history_usage[] = 
2652 "Usage: dundi no store history\n"
2653 "       Disables storing of DUNDi requests and times for debugging\n"
2654 "purposes\n";
2655
2656 static char show_peers_usage[] = 
2657 "Usage: dundi show peers\n"
2658 "       Lists all known DUNDi peers.\n";
2659
2660 static char show_trans_usage[] = 
2661 "Usage: dundi show trans\n"
2662 "       Lists all known DUNDi transactions.\n";
2663
2664 static char show_mappings_usage[] = 
2665 "Usage: dundi show mappings\n"
2666 "       Lists all known DUNDi mappings.\n";
2667
2668 static char show_precache_usage[] = 
2669 "Usage: dundi show precache\n"
2670 "       Lists all known DUNDi scheduled precache updates.\n";
2671
2672 static char show_entityid_usage[] = 
2673 "Usage: dundi show entityid\n"
2674 "       Displays the global entityid for this host.\n";
2675
2676 static char show_peer_usage[] = 
2677 "Usage: dundi show peer [peer]\n"
2678 "       Provide a detailed description of a specifid DUNDi peer.\n";
2679
2680 static char show_requests_usage[] = 
2681 "Usage: dundi show requests\n"
2682 "       Lists all known pending DUNDi requests.\n";
2683
2684 static char lookup_usage[] =
2685 "Usage: dundi lookup <number>[@context] [bypass]\n"
2686 "       Lookup the given number within the given DUNDi context\n"
2687 "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
2688 "keyword is specified.\n";
2689
2690 static char precache_usage[] =
2691 "Usage: dundi precache <number>[@context]\n"
2692 "       Lookup the given number within the given DUNDi context\n"
2693 "(or e164 if none is specified) and precaches the results to any\n"
2694 "upstream DUNDi push servers.\n";
2695
2696 static char query_usage[] =
2697 "Usage: dundi query <entity>[@context]\n"
2698 "       Attempts to retrieve contact information for a specific\n"
2699 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2700 "e164 if none is specified).\n";
2701
2702 static char flush_usage[] =
2703 "Usage: dundi flush [stats]\n"
2704 "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
2705 "'stats' is present, clears timer statistics instead of normal\n"
2706 "operation.\n";
2707
2708 static struct ast_cli_entry  cli_debug =
2709         { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2710
2711 static struct ast_cli_entry  cli_store_history =
2712         { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2713
2714 static struct ast_cli_entry  cli_no_store_history =
2715         { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2716
2717 static struct ast_cli_entry  cli_flush =
2718         { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2719
2720 static struct ast_cli_entry  cli_no_debug =
2721         { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2722
2723 static struct ast_cli_entry  cli_show_peers =
2724         { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2725
2726 static struct ast_cli_entry  cli_show_trans =
2727         { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2728
2729 static struct ast_cli_entry  cli_show_entityid =
2730         { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2731
2732 static struct ast_cli_entry  cli_show_mappings =
2733         { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2734
2735 static struct ast_cli_entry  cli_show_precache =
2736         { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2737
2738 static struct ast_cli_entry  cli_show_requests =
2739         { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2740
2741 static struct ast_cli_entry  cli_show_peer =
2742         { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2743
2744 static struct ast_cli_entry  cli_lookup =
2745         { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2746
2747 static struct ast_cli_entry  cli_precache =
2748         { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2749
2750 static struct ast_cli_entry  cli_queryeid =
2751         { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2752
2753 LOCAL_USER_DECL;
2754
2755 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2756 {
2757         struct dundi_transaction *trans;
2758         int tid;
2759         
2760         /* Don't allow creation of transactions to non-registered peers */
2761         if (p && !p->addr.sin_addr.s_addr)
2762                 return NULL;
2763         tid = get_trans_id();
2764         if (tid < 1)
2765                 return NULL;
2766         trans = malloc(sizeof(struct dundi_transaction));
2767         if (trans) {
2768                 memset(trans, 0, sizeof(struct dundi_transaction));
2769                 if (global_storehistory) {
2770                         trans->start = ast_tvnow();
2771                         ast_set_flag(trans, FLAG_STOREHIST);
2772                 }
2773                 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2774                 trans->autokillid = -1;
2775                 if (p) {
2776                         apply_peer(trans, p);
2777                         if (!p->sentfullkey)
2778                                 ast_set_flag(trans, FLAG_SENDFULLKEY);
2779                 }
2780                 trans->strans = tid;
2781                 trans->allnext = alltrans;
2782                 alltrans = trans;
2783         }
2784         return trans;
2785 }
2786
2787 static int dundi_xmit(struct dundi_packet *pack)
2788 {
2789         int res;
2790         char iabuf[INET_ADDRSTRLEN];
2791         if (dundidebug)
2792                 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2793         res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2794         if (res < 0) {
2795                 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
2796                         ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2797                         ntohs(pack->parent->addr.sin_port), strerror(errno));
2798         }
2799         if (res > 0)
2800                 res = 0;
2801         return res;
2802 }
2803
2804 static void destroy_packet(struct dundi_packet *pack, int needfree)
2805 {
2806         struct dundi_packet *prev, *cur;
2807         if (pack->parent) {
2808                 prev = NULL;
2809                 cur = pack->parent->packets;
2810                 while(cur) {
2811                         if (cur == pack) {
2812                                 if (prev)
2813                                         prev->next = cur->next;
2814                                 else
2815                                         pack->parent->packets = cur->next;
2816                                 break;
2817                         }
2818                         prev = cur;
2819                         cur = cur->next;
2820                 }
2821         }
2822         if (pack->retransid > -1)
2823                 ast_sched_del(sched, pack->retransid);
2824         if (needfree)
2825                 free(pack);
2826         else {
2827                 pack->retransid = -1;
2828                 pack->next = NULL;
2829         }
2830 }
2831
2832 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2833 {
2834         struct dundi_transaction *cur, *prev;
2835         struct dundi_peer *peer;
2836         int ms;
2837         int x;
2838         int cnt;
2839         char eid_str[20];
2840         if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2841                 peer = peers;
2842                 while (peer) {
2843                         if (peer->regtrans == trans)
2844                                 peer->regtrans = NULL;
2845                         if (peer->keypending == trans)
2846                                 peer->keypending = NULL;
2847                         if (peer->qualtrans == trans) {
2848                                 if (fromtimeout) {
2849                                         if (peer->lastms > -1)
2850                                                 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2851                                         peer->lastms = -1;
2852                                 } else {
2853                                         ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2854                                         if (ms < 1)
2855                                                 ms = 1;
2856                                         if (ms < peer->maxms) {
2857                                                 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2858                                                         ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2859                                         } else if (peer->lastms < peer->maxms) {
2860                                                 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);
2861                                         }
2862                                         peer->lastms = ms;
2863                                 }
2864                                 peer->qualtrans = NULL;
2865                         }
2866                         if (ast_test_flag(trans, FLAG_STOREHIST)) {
2867                                 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2868                                         if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2869                                                 peer->avgms = 0;
2870                                                 cnt = 0;
2871                                                 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2872                                                         free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2873                                                 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2874                                                         peer->lookuptimes[x] = peer->lookuptimes[x-1];
2875                                                         peer->lookups[x] = peer->lookups[x-1];
2876                                                         if (peer->lookups[x]) {
2877                                                                 peer->avgms += peer->lookuptimes[x];
2878                                                                 cnt++;
2879                                                         }
2880                                                 }
2881                                                 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2882                                                 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2883                                                 if (peer->lookups[0]) {
2884                                                         sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2885                                                         peer->avgms += peer->lookuptimes[0];
2886                                                         cnt++;
2887                                                 }
2888                                                 if (cnt)
2889                                                         peer->avgms /= cnt;
2890                                         }
2891                                 }
2892                         }
2893                         peer = peer->next;
2894                 }
2895         }
2896         if (trans->parent) {
2897                 /* Unlink from parent if appropriate */
2898                 prev = NULL;
2899                 cur = trans->parent->trans;
2900                 while(cur) {
2901                         if (cur == trans) {
2902                                 if (prev)
2903                                         prev->next = trans->next;
2904                                 else
2905                                         trans->parent->trans = trans->next;
2906                                 break;
2907                         }
2908                         prev = cur;
2909                         cur = cur->next;
2910                 }
2911                 if (!trans->parent->trans) {
2912                         /* Wake up sleeper */
2913                         if (trans->parent->pfds[1] > -1) {
2914                                 write(trans->parent->pfds[1], "killa!", 6);
2915                         }
2916                 }
2917         }
2918         /* Unlink from all trans */
2919         prev = NULL;
2920         cur = alltrans;
2921         while(cur) {
2922                 if (cur == trans) {
2923                         if (prev)
2924                                 prev->allnext = trans->allnext;
2925                         else
2926                                 alltrans = trans->allnext;
2927                         break;
2928                 }
2929                 prev = cur;
2930                 cur = cur->allnext;
2931         }
2932         destroy_packets(trans->packets);
2933         destroy_packets(trans->lasttrans);
2934         trans->packets = NULL;
2935         trans->lasttrans = NULL;
2936         if (trans->autokillid > -1)
2937                 ast_sched_del(sched, trans->autokillid);
2938         trans->autokillid = -1;
2939         if (trans->thread) {
2940                 /* If used by a thread, mark as dead and be done */
2941                 ast_set_flag(trans, FLAG_DEAD);
2942         } else
2943                 free(trans);
2944 }
2945
2946 static int dundi_rexmit(void *data)
2947 {
2948         struct dundi_packet *pack;
2949         char iabuf[INET_ADDRSTRLEN];
2950         int res;
2951         ast_mutex_lock(&peerlock);
2952         pack = data;
2953         if (pack->retrans < 1) {
2954                 pack->retransid = -1;
2955                 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2956                         ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
2957                                 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr), 
2958                                 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2959                 destroy_trans(pack->parent, 1);
2960                 res = 0;
2961         } else {
2962                 /* Decrement retransmission, try again */
2963                 pack->retrans--;
2964                 dundi_xmit(pack);
2965                 res = 1;
2966         }
2967         ast_mutex_unlock(&peerlock);
2968         return res;
2969 }
2970
2971 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2972 {
2973         struct dundi_packet *pack;
2974         int res;
2975         int len;
2976         char eid_str[20];
2977         len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2978         /* Reserve enough space for encryption */
2979         if (ast_test_flag(trans, FLAG_ENCRYPT))
2980                 len += 384;
2981         pack = malloc(len);
2982         if (pack) {
2983                 memset(pack, 0, len);
2984                 pack->h = (struct dundi_hdr *)(pack->data);
2985                 if (cmdresp != DUNDI_COMMAND_ACK) {
2986                         pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2987                         pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2988                         pack->next = trans->packets;
2989                         trans->packets = pack;
2990                 }
2991                 pack->parent = trans;
2992                 pack->h->strans = htons(trans->strans);
2993                 pack->h->dtrans = htons(trans->dtrans);
2994                 pack->h->iseqno = trans->iseqno;
2995                 pack->h->oseqno = trans->oseqno;
2996                 pack->h->cmdresp = cmdresp;
2997                 pack->datalen = sizeof(struct dundi_hdr);
2998                 if (ied) {
2999                         memcpy(pack->h->ies, ied->buf, ied->pos);
3000                         pack->datalen += ied->pos;
3001                 } 
3002                 if (final) {
3003                         pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3004                         ast_set_flag(trans, FLAG_FINAL);
3005                 }
3006                 pack->h->cmdflags = flags;
3007                 if (cmdresp != DUNDI_COMMAND_ACK) {
3008                         trans->oseqno++;
3009                         trans->oseqno = trans->oseqno % 256;
3010                 }
3011                 trans->aseqno = trans->iseqno;
3012                 /* If we have their public key, encrypt */
3013                 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3014                         switch(cmdresp) {
3015                         case DUNDI_COMMAND_REGREQ:
3016                         case DUNDI_COMMAND_REGRESPONSE:
3017                         case DUNDI_COMMAND_DPDISCOVER:
3018                         case DUNDI_COMMAND_DPRESPONSE:
3019                         case DUNDI_COMMAND_EIDQUERY:
3020                         case DUNDI_COMMAND_EIDRESPONSE:
3021                         case DUNDI_COMMAND_PRECACHERQ:
3022                         case DUNDI_COMMAND_PRECACHERP:
3023                                 if (dundidebug)
3024                                         dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3025                                 res = dundi_encrypt(trans, pack);
3026                                 break;
3027                         default:
3028                                 res = 0;
3029                         }
3030                 } else 
3031                         res = 0;
3032                 if (!res) 
3033                         res = dundi_xmit(pack);
3034                 if (res)
3035                         ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3036                                 
3037                 if (cmdresp == DUNDI_COMMAND_ACK)
3038                         free(pack);
3039                 return res;
3040         }
3041         return -1;
3042 }
3043
3044 static int do_autokill(void *data)
3045 {
3046         struct dundi_transaction *trans = data;
3047         char eid_str[20];
3048         ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 
3049                 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3050         trans->autokillid = -1;
3051         destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3052         return 0;
3053 }
3054
3055 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3056 {
3057         struct dundi_peer *p;
3058         if (!dundi_eid_cmp(eid, us)) {
3059                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3060                 return;
3061         }
3062         ast_mutex_lock(&peerlock);
3063         p = peers;
3064         while(p) {
3065                 if (!dundi_eid_cmp(&p->eid, eid)) {
3066                         if (has_permission(p->include, context))
3067                                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3068                         else
3069                                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3070                         break;
3071                 }
3072                 p = p->next;
3073         }
3074         if (!p)
3075                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3076         ast_mutex_unlock(&peerlock);
3077 }
3078
3079 static int dundi_discover(struct dundi_transaction *trans)
3080 {
3081         struct dundi_ie_data ied;
3082         int x;
3083         if (!trans->parent) {
3084                 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3085                 return -1;
3086         }
3087         memset(&ied, 0, sizeof(ied));
3088         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3089         if (!dundi_eid_zero(&trans->us_eid))
3090                 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3091         for (x=0;x<trans->eidcount;x++)
3092                 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3093         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3094         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3095         dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3096         if (trans->parent->cbypass)
3097                 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3098         if (trans->autokilltimeout)
3099                 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3100         return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3101 }
3102
3103 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3104 {
3105         struct dundi_ie_data ied;
3106         int x, res;
3107         int max = 999999;
3108         int expiration = dundi_cache_time;
3109         int ouranswers=0;
3110         dundi_eid *avoid[1] = { NULL, };
3111         int direct[1] = { 0, };
3112         struct dundi_result dr[MAX_RESULTS];
3113         struct dundi_hint_metadata hmd;
3114         if (!trans->parent) {
3115                 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3116                 return -1;
3117         }
3118         memset(&hmd, 0, sizeof(hmd));
3119         memset(&dr, 0, sizeof(dr));
3120         /* Look up the answers we're going to include */
3121         for (x=0;x<mapcount;x++)
3122                 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3123         if (ouranswers < 0)
3124                 ouranswers = 0;
3125         for (x=0;x<ouranswers;x++) {
3126                 if (dr[x].weight < max)
3127                         max = dr[x].weight;
3128         }
3129         if (max) {
3130                 /* If we do not have a canonical result, keep looking */
3131                 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
3132                 if (res > 0) {
3133                         /* Append answer in result */
3134                         ouranswers += res;
3135                 }
3136         }
3137         
3138         if (ouranswers > 0) {
3139                 *foundanswers += ouranswers;
3140                 memset(&ied, 0, sizeof(ied));
3141                 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3142                 if (!dundi_eid_zero(&trans->us_eid))
3143                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3144                 for (x=0;x<trans->eidcount;x++)
3145                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3146                 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3147                 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3148                 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3149                 for (x=0;x<ouranswers;x++) {
3150                         /* Add answers */
3151                         if (dr[x].expiration && (expiration > dr[x].expiration))
3152                                 expiration = dr[x].expiration;
3153                         dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3154                 }
3155                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3156                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3157                 if (trans->autokilltimeout)
3158                         trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3159                 if (expiration < *minexp)
3160                         *minexp = expiration;
3161                 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3162         } else {
3163                 /* Oops, nothing to send... */
3164                 destroy_trans(trans, 0);
3165                 return 0;
3166         }
3167 }
3168
3169 static int dundi_query(struct dundi_transaction *trans)
3170 {
3171         struct dundi_ie_data ied;
3172         int x;
3173         if (!trans->parent) {
3174                 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3175                 return -1;
3176         }
3177         memset(&ied, 0, sizeof(ied));
3178         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3179         if (!dundi_eid_zero(&trans->us_eid))
3180                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3181         for (x=0;x<trans->eidcount;x++)
3182                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3183         dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3184         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3185         dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3186         if (trans->autokilltimeout)
3187                 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3188         return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3189 }
3190
3191 static int discover_transactions(struct dundi_request *dr)
3192 {
3193         struct dundi_transaction *trans;
3194         ast_mutex_lock(&peerlock);
3195         trans = dr->trans;
3196         while(trans) {
3197                 dundi_discover(trans);
3198                 trans = trans->next;
3199         }
3200         ast_mutex_unlock(&peerlock);
3201         return 0;
3202 }
3203
3204 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3205 {
3206         struct dundi_transaction *trans, *transn;
3207         /* Mark all as "in thread" so they don't disappear */
3208         ast_mutex_lock(&peerlock);
3209         trans = dr->trans;
3210         while(trans) {
3211                 if (trans->thread)
3212                         ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3213                 trans->thread = 1;
3214                 trans = trans->next;
3215         }
3216         ast_mutex_unlock(&peerlock);
3217
3218         trans = dr->trans;
3219         while(trans) {
3220                 if (!ast_test_flag(trans, FLAG_DEAD))
3221                         precache_trans(trans, maps, mapcount, expiration, foundanswers);
3222                 trans = trans->next;
3223         }
3224
3225         /* Cleanup any that got destroyed in the mean time */
3226         ast_mutex_lock(&peerlock);
3227         trans = dr->trans;
3228         while(trans) {
3229                 transn = trans->next;
3230                 trans->thread = 0;
3231                 if (ast_test_flag(trans, FLAG_DEAD)) {
3232                         ast_log(LOG_DEBUG, "Our transaction went away!\n");
3233                         destroy_trans(trans, 0);
3234                 }
3235                 trans = transn;
3236         }
3237         ast_mutex_unlock(&peerlock);
3238         return 0;
3239 }
3240
3241 static int query_transactions(struct dundi_request *dr)
3242 {
3243         struct dundi_transaction *trans;
3244         ast_mutex_lock(&peerlock);
3245         trans = dr->trans;
3246         while(trans) {
3247                 dundi_query(trans);
3248                 trans = trans->next;
3249         }
3250         ast_mutex_unlock(&peerlock);
3251         return 0;
3252 }
3253
3254 static int optimize_transactions(struct dundi_request *dr, int order)
3255 {
3256         /* Minimize the message propagation through DUNDi by
3257            alerting the network to hops which should be not be considered */
3258         struct dundi_transaction *trans;
3259         struct dundi_peer *peer;
3260         dundi_eid tmp;
3261         int x;
3262         int needpush;
3263         ast_mutex_lock(&peerlock);
3264         trans = dr->trans;
3265         while(trans) {
3266                 /* Pop off the true root */
3267                 if (trans->eidcount) {
3268                         tmp = trans->eids[--trans->eidcount];
3269                         needpush = 1;
3270                 } else {
3271                         tmp = trans->us_eid;
3272                         needpush = 0;
3273                 }
3274
3275                 peer = peers;
3276                 while(peer) {
3277                         if (has_permission(peer->include, dr->dcontext) && 
3278                             dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3279                                 (peer->order <= order)) {
3280                                 /* For each other transaction, make sure we don't
3281                                    ask this EID about the others if they're not
3282                                    already in the list */
3283                                 if (!dundi_eid_cmp(&tmp, &peer->eid)) 
3284                                         x = -1;
3285                                 else {
3286                                         for (x=0;x<trans->eidcount;x++) {
3287                                                 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3288                                                         break;
3289                                         }
3290                                 }
3291                                 if (x == trans->eidcount) {
3292                                         /* Nope not in the list, if needed, add us at the end since we're the source */
3293                                         if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3294                                                 trans->eids[trans->eidcount++] = peer->eid;
3295                                                 /* Need to insert the real root (or us) at the bottom now as
3296                                                    a requirement now.  */
3297                                                 needpush = 1;
3298                                         }
3299                                 }
3300                         }
3301                         peer = peer->next;
3302                 }
3303                 /* If necessary, push the true root back on the end */
3304                 if (needpush)
3305                         trans->eids[trans->eidcount++] = tmp;
3306                 trans = trans->next;
3307         }
3308         ast_mutex_unlock(&peerlock);
3309         return 0;
3310 }
3311
3312 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3313 {
3314         struct dundi_transaction *trans;
3315         int x;
3316         char eid_str[20];
3317         char eid_str2[20];
3318         /* Ignore if not registered */
3319         if (!p->addr.sin_addr.s_addr)
3320                 return 0;
3321         if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3322                 return 0;
3323         if (ast_strlen_zero(dr->number))
3324                 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
3325         else
3326                 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
3327         trans = create_transaction(p);
3328         if (!trans)
3329                 return -1;
3330         trans->next = dr->trans;
3331         trans->parent = dr;
3332         trans->ttl = ttl;
3333         for