1bafab2d6c5e1d1a589f26dfb8c5d2065e16c397
[asterisk/asterisk.git] / pbx / pbx_dundi.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Distributed Universal Number Discovery (DUNDi)
22  *
23  */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <sys/socket.h>
31 #include <string.h>
32 #include <errno.h>
33 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
34 #include <sys/types.h>
35 #include <netinet/in_systm.h>
36 #endif
37 #include <netinet/ip.h>
38 #include <sys/ioctl.h>
39 #include <netinet/in.h>
40 #include <net/if.h>
41 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
42 #include <net/if_dl.h>
43 #include <ifaddrs.h>
44 #endif
45 #include <zlib.h>
46
47 #include "asterisk.h"
48
49 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
50
51 #include "asterisk/file.h"
52 #include "asterisk/logger.h"
53 #include "asterisk/channel.h"
54 #include "asterisk/config.h"
55 #include "asterisk/options.h"
56 #include "asterisk/pbx.h"
57 #include "asterisk/module.h"
58 #include "asterisk/frame.h"
59 #include "asterisk/file.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/md5.h"
63 #include "asterisk/dundi.h"
64 #include "asterisk/sched.h"
65 #include "asterisk/io.h"
66 #include "asterisk/utils.h"
67 #include "asterisk/crypto.h"
68 #include "asterisk/astdb.h"
69 #include "asterisk/acl.h"
70 #include "asterisk/aes.h"
71
72 #include "dundi-parser.h"
73
74 #define MAX_RESULTS     64
75
76 #define MAX_PACKET_SIZE 8192
77
78 extern char ast_config_AST_KEY_DIR[];
79
80 static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
81
82 #define DUNDI_MODEL_INBOUND             (1 << 0)
83 #define DUNDI_MODEL_OUTBOUND    (1 << 1)
84 #define DUNDI_MODEL_SYMMETRIC   (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
85
86 /* Keep times of last 10 lookups */
87 #define DUNDI_TIMING_HISTORY    10
88
89 #define FLAG_ISREG              (1 << 0)                /* Transaction is register request */
90 #define FLAG_DEAD               (1 << 1)                /* Transaction is dead */
91 #define FLAG_FINAL              (1 << 2)                /* Transaction has final message sent */
92 #define FLAG_ISQUAL             (1 << 3)                /* Transaction is a qualification */
93 #define FLAG_ENCRYPT    (1 << 4)                /* Transaction is encrypted wiht ECX/DCX */
94 #define FLAG_SENDFULLKEY        (1 << 5)                /* Send full key on transaction */
95 #define FLAG_STOREHIST  (1 << 6)                /* Record historic performance */
96
97 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
98
99 #if 0
100 #define DUNDI_SECRET_TIME 15    /* Testing only */
101 #else
102 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
103 #endif
104
105 #define KEY_OUT                 0
106 #define KEY_IN                  1
107
108 static struct io_context *io;
109 static struct sched_context *sched;
110 static int netsocket = -1;
111 static pthread_t netthreadid = AST_PTHREADT_NULL;
112 static pthread_t precachethreadid = AST_PTHREADT_NULL;
113 static int tos = 0;
114 static int dundidebug = 0;
115 static int authdebug = 0;
116 static int dundi_ttl = DUNDI_DEFAULT_TTL;
117 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
118 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
119 static int global_autokilltimeout = 0;
120 static dundi_eid global_eid;
121 static int default_expiration = 60;
122 static int global_storehistory = 0;
123 static char dept[80];
124 static char org[80];
125 static char locality[80];
126 static char stateprov[80];
127 static char country[80];
128 static char email[80];
129 static char phone[80];
130 static char secretpath[80];
131 static char cursecret[80];
132 static char ipaddr[80];
133 static time_t rotatetime;
134 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
135 struct permission {
136         struct permission *next;
137         int allow;
138         char name[0];
139 };
140
141 struct dundi_packet {
142         struct dundi_hdr *h;
143         struct dundi_packet *next;
144         int datalen;
145         struct dundi_transaction *parent;
146         int retransid;
147         int retrans;
148         unsigned char data[0];
149 };
150
151 struct dundi_hint_metadata {
152         unsigned short flags;
153         char exten[AST_MAX_EXTENSION];
154 };
155
156 struct dundi_precache_queue {
157         struct dundi_precache_queue *next;
158         char *context;
159         time_t expiration;
160         char number[0];
161 };
162
163 struct dundi_request;
164
165 struct dundi_transaction {
166         struct sockaddr_in addr;        /* Other end of transaction */
167         struct timeval start;           /* When this transaction was created */
168         dundi_eid eids[DUNDI_MAX_STACK + 1];
169         int eidcount;                           /* Number of eids in eids */
170         dundi_eid us_eid;                       /* Our EID, to them */
171         dundi_eid them_eid;                     /* Their EID, to us */
172         aes_encrypt_ctx ecx;            /* AES 128 Encryption context */
173         aes_decrypt_ctx dcx;            /* AES 128 Decryption context */
174         unsigned int flags;                             /* Has final packet been sent */
175         int ttl;                                        /* Remaining TTL for queries on this one */
176         int thread;                                     /* We have a calling thread */
177         int retranstimer;                       /* How long to wait before retransmissions */
178         int autokillid;                         /* ID to kill connection if answer doesn't come back fast enough */
179         int autokilltimeout;            /* Recommended timeout for autokill */
180         unsigned short strans;          /* Our transaction identifier */
181         unsigned short dtrans;          /* Their transaction identifer */
182         unsigned char iseqno;           /* Next expected received seqno */
183         unsigned char oiseqno;          /* Last received incoming seqno */
184         unsigned char oseqno;           /* Next transmitted seqno */
185         unsigned char aseqno;           /* Last acknowledge seqno */
186         struct dundi_packet *packets;   /* Packets to be retransmitted */
187         struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
188         struct dundi_transaction *next; /* Next with respect to the parent */
189         struct dundi_request *parent;   /* Parent request (if there is one) */
190         struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
191 } *alltrans;
192
193 struct dundi_request {
194         char dcontext[AST_MAX_EXTENSION];
195         char number[AST_MAX_EXTENSION];
196         dundi_eid query_eid;
197         dundi_eid root_eid;
198         struct dundi_result *dr;
199         struct dundi_entity_info *dei;
200         struct dundi_hint_metadata *hmd;
201         int maxcount;
202         int respcount;
203         int expiration;
204         int cbypass;
205         int pfds[2];
206         unsigned long crc32;                                                    /* CRC-32 of all but root EID's in avoid list */
207         struct dundi_transaction *trans;        /* Transactions */
208         struct dundi_request *next;
209 } *requests;
210
211 static struct dundi_mapping {
212         char dcontext[AST_MAX_EXTENSION];
213         char lcontext[AST_MAX_EXTENSION];
214         int weight;
215         int options;
216         int tech;
217         int dead;
218         char dest[AST_MAX_EXTENSION];
219         struct dundi_mapping *next;
220 } *mappings = NULL;
221
222 static struct dundi_peer {
223         dundi_eid eid;
224         struct sockaddr_in addr;        /* Address of DUNDi peer */
225         struct permission *permit;
226         struct permission *include;
227         struct permission *precachesend;
228         struct permission *precachereceive;
229         dundi_eid us_eid;
230         char inkey[80];
231         char outkey[80];
232         int dead;
233         int registerid;
234         int qualifyid;
235         int sentfullkey;
236         int order;
237         unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
238         unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
239         unsigned long us_keycrc32;      /* CRC-32 of our key */
240         aes_encrypt_ctx us_ecx;         /* Cached AES 128 Encryption context */
241         aes_decrypt_ctx us_dcx;         /* Cached AES 128 Decryption context */
242         unsigned long them_keycrc32;/* CRC-32 of our key */
243         aes_encrypt_ctx them_ecx;       /* Cached AES 128 Encryption context */
244         aes_decrypt_ctx them_dcx;       /* Cached AES 128 Decryption context */
245         time_t keyexpire;                       /* When to expire/recreate key */
246         int registerexpire;
247         int lookuptimes[DUNDI_TIMING_HISTORY];
248         char *lookups[DUNDI_TIMING_HISTORY];
249         int avgms;
250         struct dundi_transaction *regtrans;     /* Registration transaction */
251         struct dundi_transaction *qualtrans;    /* Qualify transaction */
252         struct dundi_transaction *keypending;
253         int model;                                      /* Pull model */
254         int pcmodel;                            /* Push/precache model */
255         int dynamic;                            /* Are we dynamic? */
256         int lastms;                                     /* Last measured latency */
257         int maxms;                                      /* Max permissible latency */
258         struct timeval qualtx;          /* Time of transmit */
259         struct dundi_peer *next;
260 } *peers;
261
262 static struct dundi_precache_queue *pcq;
263
264 AST_MUTEX_DEFINE_STATIC(peerlock);
265 AST_MUTEX_DEFINE_STATIC(pclock);
266
267 static int dundi_xmit(struct dundi_packet *pack);
268
269 static void dundi_debug_output(const char *data)
270 {
271         if (dundidebug)
272                 ast_verbose("%s", data);
273 }
274
275 static void dundi_error_output(const char *data)
276 {
277         ast_log(LOG_WARNING, "%s", data);
278 }
279
280 static int has_permission(struct permission *ps, char *cont)
281 {
282         int res=0;
283         while(ps) {
284                 if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
285                         res = ps->allow;
286                 ps = ps->next;
287         }
288         return res;
289 }
290
291 static char *tech2str(int tech)
292 {
293         switch(tech) {
294         case DUNDI_PROTO_NONE:
295                 return "None";
296         case DUNDI_PROTO_IAX:
297                 return "IAX2";
298         case DUNDI_PROTO_SIP:
299                 return "SIP";
300         case DUNDI_PROTO_H323:
301                 return "H323";
302         default:
303                 return "Unknown";
304         }
305 }
306
307 static int str2tech(char *str)
308 {
309         if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 
310                 return DUNDI_PROTO_IAX;
311         else if (!strcasecmp(str, "SIP"))
312                 return DUNDI_PROTO_SIP;
313         else if (!strcasecmp(str, "H323"))
314                 return DUNDI_PROTO_H323;
315         else
316                 return -1;
317 }
318
319 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
320 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
321 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
322 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
323 {
324         /* Look for an exact match first */
325         struct dundi_transaction *trans;
326         trans = alltrans;
327         while(trans) {
328                 if (!inaddrcmp(&trans->addr, sin) && 
329                      ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
330                           ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
331                           if (hdr->strans)
332                                   trans->dtrans = ntohs(hdr->strans) & 32767;
333                           break;
334                 }
335                 trans = trans->allnext;
336         }
337         if (!trans) {
338                 switch(hdr->cmdresp & 0x7f) {
339                 case DUNDI_COMMAND_DPDISCOVER:
340                 case DUNDI_COMMAND_EIDQUERY:
341                 case DUNDI_COMMAND_PRECACHERQ:
342                 case DUNDI_COMMAND_REGREQ:
343                 case DUNDI_COMMAND_NULL:
344                 case DUNDI_COMMAND_ENCRYPT:
345                         if (hdr->strans) {      
346                                 /* Create new transaction */
347                                 trans = create_transaction(NULL);
348                                 if (trans) {
349                                         memcpy(&trans->addr, sin, sizeof(trans->addr));
350                                         trans->dtrans = ntohs(hdr->strans) & 32767;
351                                 } else
352                                         ast_log(LOG_WARNING, "Out of memory!\n");
353                         }
354                         break;
355                 default:
356                         break;
357                 }
358         }
359         return trans;
360 }
361
362 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
363
364 static int dundi_ack(struct dundi_transaction *trans, int final)
365 {
366         return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
367 }
368 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
369 {
370         struct {
371                 struct dundi_packet pack;
372                 struct dundi_hdr hdr;
373         } tmp;
374         struct dundi_transaction trans;
375         /* Never respond to an INVALID with another INVALID */
376         if (h->cmdresp == DUNDI_COMMAND_INVALID)
377                 return;
378         memset(&tmp, 0, sizeof(tmp));
379         memset(&trans, 0, sizeof(trans));
380         memcpy(&trans.addr, sin, sizeof(trans.addr));
381         tmp.hdr.strans = h->dtrans;
382         tmp.hdr.dtrans = h->strans;
383         tmp.hdr.iseqno = h->oseqno;
384         tmp.hdr.oseqno = h->iseqno;
385         tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
386         tmp.hdr.cmdflags = 0;
387         tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
388         tmp.pack.datalen = sizeof(struct dundi_hdr);
389         tmp.pack.parent = &trans;
390         dundi_xmit(&tmp.pack);
391 }
392
393 static void reset_global_eid(void)
394 {
395 #if defined(SIOCGIFHWADDR)
396         int x,s;
397         char eid_str[20];
398         struct ifreq ifr;
399
400         s = socket(AF_INET, SOCK_STREAM, 0);
401         if (s > 0) {
402                 x = 0;
403                 for(x=0;x<10;x++) {
404                         memset(&ifr, 0, sizeof(ifr));
405                         snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
406                         if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
407                                 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
408                                 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
409                                 close(s);
410                                 return;
411                         }
412         }
413                 close(s);
414         }
415 #else
416 #if defined(ifa_broadaddr) && !defined(SOLARIS)
417         char eid_str[20];
418         struct ifaddrs *ifap;
419         
420         if (getifaddrs(&ifap) == 0) {
421                 struct ifaddrs *p;
422                 for (p = ifap; p; p = p->ifa_next) {
423                         if (p->ifa_addr->sa_family == AF_LINK) {
424                                 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
425                                 memcpy(
426                                         &(global_eid.eid),
427                                         sdp->sdl_data + sdp->sdl_nlen, 6);
428                                 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifap->ifa_name);
429                                 freeifaddrs(ifap);
430                                 return;
431                         }
432                 }
433                 freeifaddrs(ifap);
434         }
435 #endif
436 #endif
437         ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID  You will have to set it manually.\n");
438 }
439
440 static int get_trans_id(void)
441 {
442         struct dundi_transaction *t;
443         int stid = (rand() % 32766) + 1;
444         int tid = stid;
445         do {
446                 t = alltrans;
447                 while(t) {
448                         if (t->strans == tid) 
449                                 break;
450                         t = t->allnext;
451                 }
452                 if (!t)
453                         return tid;
454                 tid = (tid % 32766) + 1;
455         } while (tid != stid);
456         return 0;
457 }
458
459 static int reset_transaction(struct dundi_transaction *trans)
460 {
461         int tid;
462         tid = get_trans_id();
463         if (tid < 1)
464                 return -1;
465         trans->strans = tid;
466         trans->dtrans = 0;
467         trans->iseqno = 0;
468         trans->oiseqno = 0;
469         trans->oseqno = 0;
470         trans->aseqno = 0;
471         ast_clear_flag(trans, FLAG_FINAL);      
472         return 0;
473 }
474
475 static struct dundi_peer *find_peer(dundi_eid *eid)
476 {
477         struct dundi_peer *cur;
478         if (!eid)
479                 eid = &empty_eid;
480         cur = peers;
481         while(cur) {
482                 if (!dundi_eid_cmp(&cur->eid,eid))
483                         return cur;
484                 cur = cur->next;
485         }
486         return NULL;
487 }
488
489 static void build_iv(unsigned char *iv)
490 {
491         /* XXX Would be nice to be more random XXX */
492         unsigned int *fluffy;
493         int x;
494         fluffy = (unsigned int *)(iv);
495         for (x=0;x<4;x++)
496                 fluffy[x] = rand();
497 }
498
499 struct dundi_query_state {
500         dundi_eid *eids[DUNDI_MAX_STACK + 1]; 
501         int directs[DUNDI_MAX_STACK + 1]; 
502         dundi_eid reqeid;
503         char called_context[AST_MAX_EXTENSION];
504         char called_number[AST_MAX_EXTENSION];
505         struct dundi_mapping *maps;
506         int nummaps;
507         int nocache;
508         struct dundi_transaction *trans;
509         void *chal;
510         int challen;
511         int ttl;
512         char fluffy[0];
513 };
514
515 static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd)
516 {
517         struct ast_flags flags = {0};
518         int x;
519         if (!ast_strlen_zero(map->lcontext)) {
520                 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
521                         ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
522                 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
523                         ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
524                 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
525                         ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
526                 if (ast_ignore_pattern(map->lcontext, called_number))
527                         ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
528
529                 /* Clearly we can't say 'don't ask' anymore if we found anything... */
530                 if (ast_test_flag(&flags, AST_FLAGS_ALL)) 
531                         ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
532
533                 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
534                         /* Skip partial answers */
535                         ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
536                 }
537                 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
538                         struct varshead headp;
539                         struct ast_var_t *newvariable;
540                         ast_set_flag(&flags, map->options & 0xffff);
541                         ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
542                         dr[anscnt].techint = map->tech;
543                         dr[anscnt].weight = map->weight;
544                         dr[anscnt].expiration = dundi_cache_time;
545                         ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
546                         dr[anscnt].eid = *us_eid;
547                         dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
548                         if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
549                                 AST_LIST_HEAD_INIT_NOLOCK(&headp);
550                                 newvariable = ast_var_assign("NUMBER", called_number);
551                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
552                                 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
553                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
554                                 newvariable = ast_var_assign("SECRET", cursecret);
555                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
556                                 newvariable = ast_var_assign("IPADDR", ipaddr);
557                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
558                                 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
559                                 while (!AST_LIST_EMPTY(&headp)) {           /* List Deletion. */
560                                         newvariable = AST_LIST_REMOVE_HEAD(&headp, entries);
561                                         ast_var_delete(newvariable);
562                                 }
563                         } else
564                                 dr[anscnt].dest[0] = '\0';
565                         anscnt++;
566                 } else {
567                         /* No answers...  Find the fewest number of digits from the
568                            number for which we have no answer. */
569                         char tmp[AST_MAX_EXTENSION];
570                         for (x=0;x<AST_MAX_EXTENSION;x++) {
571                                 tmp[x] = called_number[x];
572                                 if (!tmp[x])
573                                         break;
574                                 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
575                                         /* Oops found something we can't match.  If this is longer
576                                            than the running hint, we have to consider it */
577                                         if (strlen(tmp) > strlen(hmd->exten)) {
578                                                 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
579                                         }
580                                         break;
581                                 }
582                         }
583                 }
584         }
585         return anscnt;
586 }
587
588 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
589
590 static void *dundi_lookup_thread(void *data)
591 {
592         struct dundi_query_state *st = data;
593         struct dundi_result dr[MAX_RESULTS];
594         struct dundi_ie_data ied;
595         struct dundi_hint_metadata hmd;
596         char eid_str[20];
597         int res, x;
598         int ouranswers=0;
599         int max = 999999;
600         int expiration = dundi_cache_time;
601
602         ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
603                 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
604         memset(&ied, 0, sizeof(ied));
605         memset(&dr, 0, sizeof(dr));
606         memset(&hmd, 0, sizeof(hmd));
607         /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
608         hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
609         for (x=0;x<st->nummaps;x++)
610                 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
611         if (ouranswers < 0)
612                 ouranswers = 0;
613         for (x=0;x<ouranswers;x++) {
614                 if (dr[x].weight < max)
615                         max = dr[x].weight;
616         }
617                 
618         if (max) {
619                 /* If we do not have a canonical result, keep looking */
620                 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs);
621                 if (res > 0) {
622                         /* Append answer in result */
623                         ouranswers += res;
624                 } else {
625                         if ((res < -1) && (!ouranswers))
626                                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
627                 }
628         }
629         ast_mutex_lock(&peerlock);
630         /* Truncate if "don't ask" isn't present */
631         if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
632                 hmd.exten[0] = '\0';
633         if (ast_test_flag(st->trans, FLAG_DEAD)) {
634                 ast_log(LOG_DEBUG, "Our transaction went away!\n");
635                 st->trans->thread = 0;
636                 destroy_trans(st->trans, 0);
637         } else {
638                 for (x=0;x<ouranswers;x++) {
639                         /* Add answers */
640                         if (dr[x].expiration && (expiration > dr[x].expiration))
641                                 expiration = dr[x].expiration;
642                         dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
643                 }
644                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
645                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
646                 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
647                 st->trans->thread = 0;
648         }
649         ast_mutex_unlock(&peerlock);
650         free(st);
651         return NULL;    
652 }
653
654 static void *dundi_precache_thread(void *data)
655 {
656         struct dundi_query_state *st = data;
657         struct dundi_ie_data ied;
658         struct dundi_hint_metadata hmd;
659         char eid_str[20];
660
661         ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 
662                 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
663         memset(&ied, 0, sizeof(ied));
664
665         /* Now produce precache */
666         dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
667
668         ast_mutex_lock(&peerlock);
669         /* Truncate if "don't ask" isn't present */
670         if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
671                 hmd.exten[0] = '\0';
672         if (ast_test_flag(st->trans, FLAG_DEAD)) {
673                 ast_log(LOG_DEBUG, "Our transaction went away!\n");
674                 st->trans->thread = 0;
675                 destroy_trans(st->trans, 0);
676         } else {
677                 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
678                 st->trans->thread = 0;
679         }
680         ast_mutex_unlock(&peerlock);
681         free(st);
682         return NULL;    
683 }
684
685 static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[]);
686
687 static void *dundi_query_thread(void *data)
688 {
689         struct dundi_query_state *st = data;
690         struct dundi_entity_info dei;
691         struct dundi_ie_data ied;
692         struct dundi_hint_metadata hmd;
693         char eid_str[20];
694         int res;
695         ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
696                 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
697         memset(&ied, 0, sizeof(ied));
698         memset(&dei, 0, sizeof(dei));
699         memset(&hmd, 0, sizeof(hmd));
700         if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
701                 /* Ooh, it's us! */
702                 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
703                 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
704                 ast_copy_string(dei.org, org, sizeof(dei.org));
705                 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
706                 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
707                 ast_copy_string(dei.country, country, sizeof(dei.country));
708                 ast_copy_string(dei.email, email, sizeof(dei.email));
709                 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
710                 res = 1;
711         } else {
712                 /* If we do not have a canonical result, keep looking */
713                 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
714         }
715         ast_mutex_lock(&peerlock);
716         if (ast_test_flag(st->trans, FLAG_DEAD)) {
717                 ast_log(LOG_DEBUG, "Our transaction went away!\n");
718                 st->trans->thread = 0;
719                 destroy_trans(st->trans, 0);
720         } else {
721                 if (res) {
722                         dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
723                         dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
724                         dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
725                         dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
726                         dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
727                         dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
728                         dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
729                         if (!ast_strlen_zero(dei.ipaddr))
730                                 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
731                 }
732                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
733                 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
734                 st->trans->thread = 0;
735         }
736         ast_mutex_unlock(&peerlock);
737         free(st);
738         return NULL;    
739 }
740
741 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
742 {
743         struct dundi_query_state *st;
744         int totallen;
745         int x;
746         int skipfirst=0;
747         struct dundi_ie_data ied;
748         char eid_str[20];
749         char *s;
750         pthread_t lookupthread;
751         pthread_attr_t attr;
752         if (ies->eidcount > 1) {
753                 /* Since it is a requirement that the first EID is the authenticating host
754                    and the last EID is the root, it is permissible that the first and last EID
755                    could be the same.  In that case, we should go ahead copy only the "root" section
756                    since we will not need it for authentication. */
757                 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
758                         skipfirst = 1;
759         }
760         totallen = sizeof(struct dundi_query_state);
761         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
762         st = malloc(totallen);
763         if (st) {
764                 memset(st, 0, totallen);
765                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
766                 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
767                 st->trans = trans;
768                 st->ttl = ies->ttl - 1;
769                 if (st->ttl < 0)
770                         st->ttl = 0;
771                 s = st->fluffy;
772                 for (x=skipfirst;ies->eids[x];x++) {
773                         st->eids[x-skipfirst] = (dundi_eid *)s;
774                         *st->eids[x-skipfirst] = *ies->eids[x];
775                         s += sizeof(dundi_eid);
776                 }
777                 ast_log(LOG_DEBUG, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
778                 pthread_attr_init(&attr);
779                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
780                 trans->thread = 1;
781                 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
782                         trans->thread = 0;
783                         ast_log(LOG_WARNING, "Unable to create thread!\n");
784                         free(st);
785                         memset(&ied, 0, sizeof(ied));
786                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
787                         dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
788                         return -1;
789                 }
790         } else {
791                 ast_log(LOG_WARNING, "Out of memory!\n");
792                 memset(&ied, 0, sizeof(ied));
793                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
794                 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
795                 return -1;
796         }
797         return 0;
798 }
799
800 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
801 {
802         int unaffected;
803         char key1[256];
804         char key2[256];
805         char eidpeer_str[20];
806         char eidroot_str[20];
807         char data[80];
808         time_t timeout;
809
810         if (expiration < 0)
811                 expiration = dundi_cache_time;
812
813         /* Only cache hint if "don't ask" is there... */
814         if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))    
815                 return 0;
816
817         unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
818
819         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
820         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
821         snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
822         snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
823
824         time(&timeout);
825         timeout += expiration;
826         snprintf(data, sizeof(data), "%ld|", (long)(timeout));
827         
828         ast_db_put("dundi/cache", key1, data);
829         ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
830         ast_db_put("dundi/cache", key2, data);
831         ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
832         return 0;
833 }
834
835 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
836 {
837         int x;
838         char key1[256];
839         char key2[256];
840         char data[1024];
841         char eidpeer_str[20];
842         char eidroot_str[20];
843         time_t timeout;
844
845         if (expiration < 1)     
846                 expiration = dundi_cache_time;
847
848         /* Keep pushes a little longer, cut pulls a little short */
849         if (push)
850                 expiration += 10;
851         else
852                 expiration -= 10;
853         if (expiration < 1)
854                 expiration = 1;
855         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
856         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
857         snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
858         snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
859         /* Build request string */
860         time(&timeout);
861         timeout += expiration;
862         snprintf(data, sizeof(data), "%ld|", (long)(timeout));
863         for (x=start;x<req->respcount;x++) {
864                 /* Skip anything with an illegal pipe in it */
865                 if (strchr(req->dr[x].dest, '|'))
866                         continue;
867                 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 
868                         req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 
869                         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
870         }
871         ast_db_put("dundi/cache", key1, data);
872         ast_db_put("dundi/cache", key2, data);
873         return 0;
874 }
875
876 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
877 {
878         struct dundi_query_state *st;
879         int totallen;
880         int x,z;
881         struct dundi_ie_data ied;
882         char *s;
883         struct dundi_result dr2[MAX_RESULTS];
884         struct dundi_request dr;
885         struct dundi_hint_metadata hmd;
886
887         struct dundi_mapping *cur;
888         int mapcount;
889         int skipfirst = 0;
890         
891         pthread_t lookupthread;
892         pthread_attr_t attr;
893
894         memset(&dr2, 0, sizeof(dr2));
895         memset(&dr, 0, sizeof(dr));
896         memset(&hmd, 0, sizeof(hmd));
897         
898         /* Forge request structure to hold answers for cache */
899         hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
900         dr.dr = dr2;
901         dr.maxcount = MAX_RESULTS;
902         dr.expiration = dundi_cache_time;
903         dr.hmd = &hmd;
904         dr.pfds[0] = dr.pfds[1] = -1;
905         trans->parent = &dr;
906         ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
907         ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
908         
909         for (x=0;x<ies->anscount;x++) {
910                 if (trans->parent->respcount < trans->parent->maxcount) {
911                         /* Make sure it's not already there */
912                         for (z=0;z<trans->parent->respcount;z++) {
913                                 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
914                                     !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data)) 
915                                                 break;
916                         }
917                         if (z == trans->parent->respcount) {
918                                 /* Copy into parent responses */
919                                 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
920                                 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
921                                 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
922                                 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
923                                 if (ies->expiration > 0)
924                                         trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
925                                 else
926                                         trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
927                                 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
928                                         sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
929                                         &ies->answers[x]->eid);
930                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
931                                         sizeof(trans->parent->dr[trans->parent->respcount].dest));
932                                         ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
933                                         sizeof(trans->parent->dr[trans->parent->respcount].tech));
934                                 trans->parent->respcount++;
935                                 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK); 
936                         } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
937                                 /* Update weight if appropriate */
938                                 trans->parent->dr[z].weight = ies->answers[x]->weight;
939                         }
940                 } else
941                         ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
942                                 trans->parent->number, trans->parent->dcontext);
943
944         }
945         /* Save all the results (if any) we had.  Even if no results, still cache lookup. */
946         cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
947         if (ies->hint)
948                 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
949
950         totallen = sizeof(struct dundi_query_state);
951         /* Count matching map entries */
952         mapcount = 0;
953         cur = mappings;
954         while(cur) {
955                 if (!strcasecmp(cur->dcontext, ccontext))
956                         mapcount++;
957                 cur = cur->next;
958         }
959         
960         /* If no maps, return -1 immediately */
961         if (!mapcount)
962                 return -1;
963
964         if (ies->eidcount > 1) {
965                 /* Since it is a requirement that the first EID is the authenticating host
966                    and the last EID is the root, it is permissible that the first and last EID
967                    could be the same.  In that case, we should go ahead copy only the "root" section
968                    since we will not need it for authentication. */
969                 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
970                         skipfirst = 1;
971         }
972
973         /* Prepare to run a query and then propagate that as necessary */
974         totallen += mapcount * sizeof(struct dundi_mapping);
975         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
976         st = malloc(totallen);
977         if (st) {
978                 memset(st, 0, totallen);
979                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
980                 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
981                 st->trans = trans;
982                 st->ttl = ies->ttl - 1;
983                 st->nocache = ies->cbypass;
984                 if (st->ttl < 0)
985                         st->ttl = 0;
986                 s = st->fluffy;
987                 for (x=skipfirst;ies->eids[x];x++) {
988                         st->eids[x-skipfirst] = (dundi_eid *)s;
989                         *st->eids[x-skipfirst] = *ies->eids[x];
990                         st->directs[x-skipfirst] = ies->eid_direct[x];
991                         s += sizeof(dundi_eid);
992                 }
993                 /* Append mappings */
994                 x = 0;
995                 st->maps = (struct dundi_mapping *)s;
996                 cur = mappings;
997                 while(cur) {
998                         if (!strcasecmp(cur->dcontext, ccontext)) {
999                                 if (x < mapcount) {
1000                                         st->maps[x] = *cur;
1001                                         st->maps[x].next = NULL;
1002                                         x++;
1003                                 }
1004                         }
1005                         cur = cur->next;
1006                 }
1007                 st->nummaps = mapcount;
1008                 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1009                 pthread_attr_init(&attr);
1010                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1011                 trans->thread = 1;
1012                 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1013                         trans->thread = 0;
1014                         ast_log(LOG_WARNING, "Unable to create thread!\n");
1015                         free(st);
1016                         memset(&ied, 0, sizeof(ied));
1017                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1018                         dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1019                         return -1;
1020                 }
1021         } else {
1022                 ast_log(LOG_WARNING, "Out of memory!\n");
1023                 memset(&ied, 0, sizeof(ied));
1024                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1025                 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1026                 return -1;
1027         }
1028         return 0;
1029 }
1030
1031 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1032 {
1033         struct dundi_query_state *st;
1034         int totallen;
1035         int x;
1036         struct dundi_ie_data ied;
1037         char *s;
1038         struct dundi_mapping *cur;
1039         int mapcount;
1040         int skipfirst = 0;
1041         
1042         pthread_t lookupthread;
1043         pthread_attr_t attr;
1044         totallen = sizeof(struct dundi_query_state);
1045         /* Count matching map entries */
1046         mapcount = 0;
1047         cur = mappings;
1048         while(cur) {
1049                 if (!strcasecmp(cur->dcontext, ccontext))
1050                         mapcount++;
1051                 cur = cur->next;
1052         }
1053         /* If no maps, return -1 immediately */
1054         if (!mapcount)
1055                 return -1;
1056
1057         if (ies->eidcount > 1) {
1058                 /* Since it is a requirement that the first EID is the authenticating host
1059                    and the last EID is the root, it is permissible that the first and last EID
1060                    could be the same.  In that case, we should go ahead copy only the "root" section
1061                    since we will not need it for authentication. */
1062                 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1063                         skipfirst = 1;
1064         }
1065
1066         totallen += mapcount * sizeof(struct dundi_mapping);
1067         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1068         st = malloc(totallen);
1069         if (st) {
1070                 memset(st, 0, totallen);
1071                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1072                 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1073                 st->trans = trans;
1074                 st->ttl = ies->ttl - 1;
1075                 st->nocache = ies->cbypass;
1076                 if (st->ttl < 0)
1077                         st->ttl = 0;
1078                 s = st->fluffy;
1079                 for (x=skipfirst;ies->eids[x];x++) {
1080                         st->eids[x-skipfirst] = (dundi_eid *)s;
1081                         *st->eids[x-skipfirst] = *ies->eids[x];
1082                         st->directs[x-skipfirst] = ies->eid_direct[x];
1083                         s += sizeof(dundi_eid);
1084                 }
1085                 /* Append mappings */
1086                 x = 0;
1087                 st->maps = (struct dundi_mapping *)s;
1088                 cur = mappings;
1089                 while(cur) {
1090                         if (!strcasecmp(cur->dcontext, ccontext)) {
1091                                 if (x < mapcount) {
1092                                         st->maps[x] = *cur;
1093                                         st->maps[x].next = NULL;
1094                                         x++;
1095                                 }
1096                         }
1097                         cur = cur->next;
1098                 }
1099                 st->nummaps = mapcount;
1100                 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1101                 pthread_attr_init(&attr);
1102                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1103                 trans->thread = 1;
1104                 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1105                         trans->thread = 0;
1106                         ast_log(LOG_WARNING, "Unable to create thread!\n");
1107                         free(st);
1108                         memset(&ied, 0, sizeof(ied));
1109                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1110                         dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1111                         return -1;
1112                 }
1113         } else {
1114                 ast_log(LOG_WARNING, "Out of memory!\n");
1115                 memset(&ied, 0, sizeof(ied));
1116                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1117                 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1118                 return -1;
1119         }
1120         return 0;
1121 }
1122
1123 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1124 {
1125         char data[1024];
1126         char *ptr, *term, *src;
1127         int tech;
1128         struct ast_flags flags;
1129         int weight;
1130         int length;
1131         int z;
1132         int expiration;
1133         char fs[256];
1134         time_t timeout;
1135         /* Build request string */
1136         if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1137                 ptr = data;
1138                 if (sscanf(ptr, "%d|%n", (int *)&timeout, &length) == 1) {
1139                         expiration = timeout - now;
1140                         if (expiration > 0) {
1141                                 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
1142                                 ptr += length;
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 (sscanf(tmp, "%d", (int *)&expired) == 1) {
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;
2232         char *ret;
2233         struct dundi_peer *p;
2234         char eid_str[20];
2235         if (pos != rpos)
2236                 return NULL;
2237         ast_mutex_lock(&peerlock);
2238         p = peers;
2239         while(p) {
2240                 if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
2241                         if (++which > state)
2242                                 break;
2243                 }
2244                 p = p->next;
2245         }
2246         if (p) {
2247                 ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
2248         } else
2249                 ret = NULL;
2250         ast_mutex_unlock(&peerlock);
2251         return ret;
2252 }
2253
2254 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2255 {
2256         return complete_peer_helper(line, word, pos, state, 3);
2257 }
2258
2259 static int rescomp(const void *a, const void *b)
2260 {
2261         const struct dundi_result *resa, *resb;
2262         resa = a;
2263         resb = b;
2264         if (resa->weight < resb->weight)
2265                 return -1;
2266         if (resa->weight > resb->weight)
2267                 return 1;
2268         return 0;
2269 }
2270
2271 static void sort_results(struct dundi_result *results, int count)
2272 {
2273         qsort(results, count, sizeof(results[0]), rescomp);
2274 }
2275
2276 static int dundi_do_lookup(int fd, int argc, char *argv[])
2277 {
2278         int res;
2279         char tmp[256];
2280         char fs[80] = "";
2281         char *context;
2282         int x;
2283         int bypass = 0;
2284         struct dundi_result dr[MAX_RESULTS];
2285         struct timeval start;
2286         if ((argc < 3) || (argc > 4))
2287                 return RESULT_SHOWUSAGE;
2288         if (argc > 3) {
2289                 if (!strcasecmp(argv[3], "bypass"))
2290                         bypass=1;
2291                 else
2292                         return RESULT_SHOWUSAGE;
2293         }
2294         ast_copy_string(tmp, argv[2], sizeof(tmp));
2295         context = strchr(tmp, '@');
2296         if (context) {
2297                 *context = '\0';
2298                 context++;
2299         }
2300         start = ast_tvnow();
2301         res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2302         
2303         if (res < 0) 
2304                 ast_cli(fd, "DUNDi lookup returned error.\n");
2305         else if (!res) 
2306                 ast_cli(fd, "DUNDi lookup returned no results.\n");
2307         else
2308                 sort_results(dr, res);
2309         for (x=0;x<res;x++) {
2310                 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));
2311                 ast_cli(fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2312         }
2313         ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2314         return RESULT_SUCCESS;
2315 }
2316
2317 static int dundi_do_precache(int fd, int argc, char *argv[])
2318 {
2319         int res;
2320         char tmp[256];
2321         char *context;
2322         struct timeval start;
2323         if ((argc < 3) || (argc > 3))
2324                 return RESULT_SHOWUSAGE;
2325         ast_copy_string(tmp, argv[2], sizeof(tmp));
2326         context = strchr(tmp, '@');
2327         if (context) {
2328                 *context = '\0';
2329                 context++;
2330         }
2331         start = ast_tvnow();
2332         res = dundi_precache(context, tmp);
2333         
2334         if (res < 0) 
2335                 ast_cli(fd, "DUNDi precache returned error.\n");
2336         else if (!res) 
2337                 ast_cli(fd, "DUNDi precache returned no error.\n");
2338         ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2339         return RESULT_SUCCESS;
2340 }
2341
2342 static int dundi_do_query(int fd, int argc, char *argv[])
2343 {
2344         int res;
2345         char tmp[256];
2346         char *context;
2347         dundi_eid eid;
2348         struct dundi_entity_info dei;
2349         if ((argc < 3) || (argc > 3))
2350                 return RESULT_SHOWUSAGE;
2351         if (dundi_str_to_eid(&eid, argv[2])) {
2352                 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2353                 return RESULT_SHOWUSAGE;
2354         }
2355         ast_copy_string(tmp, argv[2], sizeof(tmp));
2356         context = strchr(tmp, '@');
2357         if (context) {
2358                 *context = '\0';
2359                 context++;
2360         }
2361         res = dundi_query_eid(&dei, context, eid);
2362         if (res < 0) 
2363                 ast_cli(fd, "DUNDi Query EID returned error.\n");
2364         else if (!res) 
2365                 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2366         else {
2367                 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2368                 ast_cli(fd, "Department:      %s\n", dei.orgunit);
2369                 ast_cli(fd, "Organization:    %s\n", dei.org);
2370                 ast_cli(fd, "City/Locality:   %s\n", dei.locality);
2371                 ast_cli(fd, "State/Province:  %s\n", dei.stateprov);
2372                 ast_cli(fd, "Country:         %s\n", dei.country);
2373                 ast_cli(fd, "E-mail:          %s\n", dei.email);
2374                 ast_cli(fd, "Phone:           %s\n", dei.phone);
2375                 ast_cli(fd, "IP Address:      %s\n", dei.ipaddr);
2376         }
2377         return RESULT_SUCCESS;
2378 }
2379
2380 static int dundi_show_peer(int fd, int argc, char *argv[])
2381 {
2382         struct dundi_peer *peer;
2383         struct permission *p;
2384         char *order;
2385         char iabuf[INET_ADDRSTRLEN];
2386         char eid_str[20];
2387         int x, cnt;
2388         
2389         if (argc != 4)
2390                 return RESULT_SHOWUSAGE;
2391         ast_mutex_lock(&peerlock);
2392         peer = peers;
2393         while(peer) {
2394                 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2395                         break;
2396                 peer = peer->next;
2397         }
2398         if (peer) {
2399                 switch(peer->order) {
2400                 case 0:
2401                         order = "Primary";
2402                         break;
2403                 case 1:
2404                         order = "Secondary";
2405                         break;
2406                 case 2:
2407                         order = "Tertiary";
2408                         break;
2409                 case 3:
2410                         order = "Quartiary";
2411                         break;
2412                 default:
2413                         order = "Unknown";
2414                 }
2415                 ast_cli(fd, "Peer:    %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2416                 ast_cli(fd, "Model:   %s\n", model2str(peer->model));
2417                 ast_cli(fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2418                 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2419                 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2420                 ast_cli(fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
2421                 ast_cli(fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2422                 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2423                 if (peer->include) {
2424                         ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2425                 }
2426                 p = peer->include;
2427                 while(p) {
2428                         ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2429                         p = p->next;
2430                 }
2431                 if (peer->permit) {
2432                         ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2433                 }
2434                 p = peer->permit;
2435                 while(p) {
2436                         ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2437                         p = p->next;
2438                 }
2439                 cnt = 0;
2440                 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2441                         if (peer->lookups[x]) {
2442                                 if (!cnt)
2443                                         ast_cli(fd, "Last few query times:\n");
2444                                 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2445                                 cnt++;
2446                         }
2447                 }
2448                 if (cnt)
2449                         ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2450         } else
2451                 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2452         ast_mutex_unlock(&peerlock);
2453         return RESULT_SUCCESS;
2454 }
2455
2456 static int dundi_show_peers(int fd, int argc, char *argv[])
2457 {
2458 #define FORMAT2 "%-20.20s %-15.15s     %-10.10s %-8.8s %-15.15s\n"
2459 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2460         struct dundi_peer *peer;
2461         char iabuf[INET_ADDRSTRLEN];
2462         int registeredonly=0;
2463         char avgms[20];
2464         char eid_str[20];
2465         int online_peers = 0;
2466         int offline_peers = 0;
2467         int unmonitored_peers = 0;
2468         int total_peers = 0;
2469
2470         if ((argc != 3) && (argc != 4) && (argc != 5))
2471                 return RESULT_SHOWUSAGE;
2472         if ((argc == 4)) {
2473                 if (!strcasecmp(argv[3], "registered")) {
2474                         registeredonly = 1;
2475                 } else
2476                         return RESULT_SHOWUSAGE;
2477         }
2478         ast_mutex_lock(&peerlock);
2479         ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2480         for (peer = peers;peer;peer = peer->next) {
2481                 char status[20];
2482                 int print_line = -1;
2483                 char srch[2000];
2484                 total_peers++;
2485                 if (registeredonly && !peer->addr.sin_addr.s_addr)
2486                         continue;
2487                 if (peer->maxms) {
2488                         if (peer->lastms < 0) {
2489                                 strcpy(status, "UNREACHABLE");
2490                                 offline_peers++;
2491                         }
2492                         else if (peer->lastms > peer->maxms) {
2493                                 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2494                                 offline_peers++;
2495                         }
2496                         else if (peer->lastms) {
2497                                 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2498                                 online_peers++;
2499                         }
2500                         else {
2501                                 strcpy(status, "UNKNOWN");
2502                                 offline_peers++;
2503                         }
2504                 } else {
2505                         strcpy(status, "Unmonitored");
2506                         unmonitored_peers++;
2507                 }
2508                 if (peer->avgms) 
2509                         snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2510                 else
2511                         strcpy(avgms, "Unavail");
2512                 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2513                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2514                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2515
2516                 if (argc == 5) {
2517                   if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2518                         print_line = -1;
2519                    } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2520                         print_line = 1;
2521                    } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2522                         print_line = -1;
2523                    } else {
2524                         print_line = 0;
2525                   }
2526                 }
2527                 
2528         if (print_line) {
2529                         ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2530                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2531                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2532                 }
2533         }
2534         ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2535         ast_mutex_unlock(&peerlock);
2536         return RESULT_SUCCESS;
2537 #undef FORMAT
2538 #undef FORMAT2
2539 }
2540
2541 static int dundi_show_trans(int fd, int argc, char *argv[])
2542 {
2543 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2544 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2545         struct dundi_transaction *trans;
2546         char iabuf[INET_ADDRSTRLEN];
2547         if (argc != 3)
2548                 return RESULT_SHOWUSAGE;
2549         ast_mutex_lock(&peerlock);
2550         ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2551         for (trans = alltrans;trans;trans = trans->allnext) {
2552                         ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr), 
2553                                         ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2554         }
2555         ast_mutex_unlock(&peerlock);
2556         return RESULT_SUCCESS;
2557 #undef FORMAT
2558 #undef FORMAT2
2559 }
2560
2561 static int dundi_show_entityid(int fd, int argc, char *argv[])
2562 {
2563         char eid_str[20];
2564         if (argc != 3)
2565                 return RESULT_SHOWUSAGE;
2566         ast_mutex_lock(&peerlock);
2567         dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2568         ast_mutex_unlock(&peerlock);
2569         ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2570         return RESULT_SUCCESS;
2571 }
2572
2573 static int dundi_show_requests(int fd, int argc, char *argv[])
2574 {
2575 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2576 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2577         struct dundi_request *req;
2578         char eidstr[20];
2579         if (argc != 3)
2580                 return RESULT_SHOWUSAGE;
2581         ast_mutex_lock(&peerlock);
2582         ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2583         for (req = requests;req;req = req->next) {
2584                         ast_cli(fd, FORMAT, req->number, req->dcontext,
2585                                                 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2586         }
2587         ast_mutex_unlock(&peerlock);
2588         return RESULT_SUCCESS;
2589 #undef FORMAT
2590 #undef FORMAT2
2591 }
2592
2593 /* Grok-a-dial DUNDi */
2594
2595 static int dundi_show_mappings(int fd, int argc, char *argv[])
2596 {
2597 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2598 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2599         struct dundi_mapping *map;
2600         char fs[256];
2601         if (argc != 3)
2602                 return RESULT_SHOWUSAGE;
2603         ast_mutex_lock(&peerlock);
2604         ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2605         for (map = mappings;map;map = map->next) {
2606                         ast_cli(fd, FORMAT, map->dcontext, map->weight, 
2607                                             ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
2608                                                                 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2609         }
2610         ast_mutex_unlock(&peerlock);
2611         return RESULT_SUCCESS;
2612 #undef FORMAT
2613 #undef FORMAT2
2614 }
2615
2616 static int dundi_show_precache(int fd, int argc, char *argv[])
2617 {
2618 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2619 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2620         struct dundi_precache_queue *qe;
2621         int h,m,s;
2622         time_t now;
2623         
2624         if (argc != 3)
2625                 return RESULT_SHOWUSAGE;
2626         time(&now);
2627         ast_mutex_lock(&pclock);
2628         ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2629         for (qe = pcq;qe;qe = qe->next) {
2630                 s = qe->expiration - now;
2631                 h = s / 3600;
2632                 s = s % 3600;
2633                 m = s / 60;
2634                 s = s % 60;
2635                 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2636         }
2637         ast_mutex_unlock(&pclock);
2638         return RESULT_SUCCESS;
2639 #undef FORMAT
2640 #undef FORMAT2
2641 }
2642
2643 static char debug_usage[] = 
2644 "Usage: dundi debug\n"
2645 "       Enables dumping of DUNDi packets for debugging purposes\n";
2646
2647 static char no_debug_usage[] = 
2648 "Usage: dundi no debug\n"
2649 "       Disables dumping of DUNDi packets for debugging purposes\n";
2650
2651 static char store_history_usage[] = 
2652 "Usage: dundi store history\n"
2653 "       Enables storing of DUNDi requests and times for debugging\n"
2654 "purposes\n";
2655
2656 static char no_store_history_usage[] = 
2657 "Usage: dundi no store history\n"
2658 "       Disables storing of DUNDi requests and times for debugging\n"
2659 "purposes\n";
2660
2661 static char show_peers_usage[] = 
2662 "Usage: dundi show peers\n"
2663 "       Lists all known DUNDi peers.\n";
2664
2665 static char show_trans_usage[] = 
2666 "Usage: dundi show trans\n"
2667 "       Lists all known DUNDi transactions.\n";
2668
2669 static char show_mappings_usage[] = 
2670 "Usage: dundi show mappings\n"
2671 "       Lists all known DUNDi mappings.\n";
2672
2673 static char show_precache_usage[] = 
2674 "Usage: dundi show precache\n"
2675 "       Lists all known DUNDi scheduled precache updates.\n";
2676
2677 static char show_entityid_usage[] = 
2678 "Usage: dundi show entityid\n"
2679 "       Displays the global entityid for this host.\n";
2680
2681 static char show_peer_usage[] = 
2682 "Usage: dundi show peer [peer]\n"
2683 "       Provide a detailed description of a specifid DUNDi peer.\n";
2684
2685 static char show_requests_usage[] = 
2686 "Usage: dundi show requests\n"
2687 "       Lists all known pending DUNDi requests.\n";
2688
2689 static char lookup_usage[] =
2690 "Usage: dundi lookup <number>[@context] [bypass]\n"
2691 "       Lookup the given number within the given DUNDi context\n"
2692 "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
2693 "keyword is specified.\n";
2694
2695 static char precache_usage[] =
2696 "Usage: dundi precache <number>[@context]\n"
2697 "       Lookup the given number within the given DUNDi context\n"
2698 "(or e164 if none is specified) and precaches the results to any\n"
2699 "upstream DUNDi push servers.\n";
2700
2701 static char query_usage[] =
2702 "Usage: dundi query <entity>[@context]\n"
2703 "       Attempts to retrieve contact information for a specific\n"
2704 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2705 "e164 if none is specified).\n";
2706
2707 static char flush_usage[] =
2708 "Usage: dundi flush [stats]\n"
2709 "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
2710 "'stats' is present, clears timer statistics instead of normal\n"
2711 "operation.\n";
2712
2713 static struct ast_cli_entry  cli_debug =
2714         { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2715
2716 static struct ast_cli_entry  cli_store_history =
2717         { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2718
2719 static struct ast_cli_entry  cli_no_store_history =
2720         { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2721
2722 static struct ast_cli_entry  cli_flush =
2723         { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2724
2725 static struct ast_cli_entry  cli_no_debug =
2726         { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2727
2728 static struct ast_cli_entry  cli_show_peers =
2729         { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2730
2731 static struct ast_cli_entry  cli_show_trans =
2732         { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2733
2734 static struct ast_cli_entry  cli_show_entityid =
2735         { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2736
2737 static struct ast_cli_entry  cli_show_mappings =
2738         { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2739
2740 static struct ast_cli_entry  cli_show_precache =
2741         { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2742
2743 static struct ast_cli_entry  cli_show_requests =
2744         { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2745
2746 static struct ast_cli_entry  cli_show_peer =
2747         { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2748
2749 static struct ast_cli_entry  cli_lookup =
2750         { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2751
2752 static struct ast_cli_entry  cli_precache =
2753         { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2754
2755 static struct ast_cli_entry  cli_queryeid =
2756         { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2757
2758 LOCAL_USER_DECL;
2759
2760 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2761 {
2762         struct dundi_transaction *trans;
2763         int tid;
2764         
2765         /* Don't allow creation of transactions to non-registered peers */
2766         if (p && !p->addr.sin_addr.s_addr)
2767                 return NULL;
2768         tid = get_trans_id();
2769         if (tid < 1)
2770                 return NULL;
2771         trans = malloc(sizeof(struct dundi_transaction));
2772         if (trans) {
2773                 memset(trans, 0, sizeof(struct dundi_transaction));
2774                 if (global_storehistory) {
2775                         trans->start = ast_tvnow();
2776                         ast_set_flag(trans, FLAG_STOREHIST);
2777                 }
2778                 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2779                 trans->autokillid = -1;
2780                 if (p) {
2781                         apply_peer(trans, p);
2782                         if (!p->sentfullkey)
2783                                 ast_set_flag(trans, FLAG_SENDFULLKEY);
2784                 }
2785                 trans->strans = tid;
2786                 trans->allnext = alltrans;
2787                 alltrans = trans;
2788         }
2789         return trans;
2790 }
2791
2792 static int dundi_xmit(struct dundi_packet *pack)
2793 {
2794         int res;
2795         char iabuf[INET_ADDRSTRLEN];
2796         if (dundidebug)
2797                 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2798         res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2799         if (res < 0) {
2800                 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
2801                         ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2802                         ntohs(pack->parent->addr.sin_port), strerror(errno));
2803         }
2804         if (res > 0)
2805                 res = 0;
2806         return res;
2807 }
2808
2809 static void destroy_packet(struct dundi_packet *pack, int needfree)
2810 {
2811         struct dundi_packet *prev, *cur;
2812         if (pack->parent) {
2813                 prev = NULL;
2814                 cur = pack->parent->packets;
2815                 while(cur) {
2816                         if (cur == pack) {
2817                                 if (prev)
2818                                         prev->next = cur->next;
2819                                 else
2820                                         pack->parent->packets = cur->next;
2821                                 break;
2822                         }
2823                         prev = cur;
2824                         cur = cur->next;
2825                 }
2826         }
2827         if (pack->retransid > -1)
2828                 ast_sched_del(sched, pack->retransid);
2829         if (needfree)
2830                 free(pack);
2831         else {
2832                 pack->retransid = -1;
2833                 pack->next = NULL;
2834         }
2835 }
2836
2837 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2838 {
2839         struct dundi_transaction *cur, *prev;
2840         struct dundi_peer *peer;
2841         int ms;
2842         int x;
2843         int cnt;
2844         char eid_str[20];
2845         if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2846                 peer = peers;
2847                 while (peer) {
2848                         if (peer->regtrans == trans)
2849                                 peer->regtrans = NULL;
2850                         if (peer->keypending == trans)
2851                                 peer->keypending = NULL;
2852                         if (peer->qualtrans == trans) {
2853                                 if (fromtimeout) {
2854                                         if (peer->lastms > -1)
2855                                                 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2856                                         peer->lastms = -1;
2857                                 } else {
2858                                         ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2859                                         if (ms < 1)
2860                                                 ms = 1;
2861                                         if (ms < peer->maxms) {
2862                                                 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2863                                                         ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2864                                         } else if (peer->lastms < peer->maxms) {
2865                                                 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);
2866                                         }
2867                                         peer->lastms = ms;
2868                                 }
2869                                 peer->qualtrans = NULL;
2870                         }
2871                         if (ast_test_flag(trans, FLAG_STOREHIST)) {
2872                                 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2873                                         if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2874                                                 peer->avgms = 0;
2875                                                 cnt = 0;
2876                                                 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2877                                                         free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2878                                                 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2879                                                         peer->lookuptimes[x] = peer->lookuptimes[x-1];
2880                                                         peer->lookups[x] = peer->lookups[x-1];
2881                                                         if (peer->lookups[x]) {
2882                                                                 peer->avgms += peer->lookuptimes[x];
2883                                                                 cnt++;
2884                                                         }
2885                                                 }
2886                                                 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2887                                                 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2888                                                 if (peer->lookups[0]) {
2889                                                         sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2890                                                         peer->avgms += peer->lookuptimes[0];
2891                                                         cnt++;
2892                                                 }
2893                                                 if (cnt)
2894                                                         peer->avgms /= cnt;
2895                                         }
2896                                 }
2897                         }
2898                         peer = peer->next;
2899                 }
2900         }
2901         if (trans->parent) {
2902                 /* Unlink from parent if appropriate */
2903                 prev = NULL;
2904                 cur = trans->parent->trans;
2905                 while(cur) {
2906                         if (cur == trans) {
2907                                 if (prev)
2908                                         prev->next = trans->next;
2909                                 else
2910                                         trans->parent->trans = trans->next;
2911                                 break;
2912                         }
2913                         prev = cur;
2914                         cur = cur->next;
2915                 }
2916                 if (!trans->parent->trans) {
2917                         /* Wake up sleeper */
2918                         if (trans->parent->pfds[1] > -1) {
2919                                 write(trans->parent->pfds[1], "killa!", 6);
2920                         }
2921                 }
2922         }
2923         /* Unlink from all trans */
2924         prev = NULL;
2925         cur = alltrans;
2926         while(cur) {
2927                 if (cur == trans) {
2928                         if (prev)
2929                                 prev->allnext = trans->allnext;
2930                         else
2931                                 alltrans = trans->allnext;
2932                         break;
2933                 }
2934                 prev = cur;
2935                 cur = cur->allnext;
2936         }
2937         destroy_packets(trans->packets);
2938         destroy_packets(trans->lasttrans);
2939         trans->packets = NULL;
2940         trans->lasttrans = NULL;
2941         if (trans->autokillid > -1)
2942                 ast_sched_del(sched, trans->autokillid);
2943         trans->autokillid = -1;
2944         if (trans->thread) {
2945                 /* If used by a thread, mark as dead and be done */
2946                 ast_set_flag(trans, FLAG_DEAD);
2947         } else
2948                 free(trans);
2949 }
2950
2951 static int dundi_rexmit(void *data)
2952 {
2953         struct dundi_packet *pack;
2954         char iabuf[INET_ADDRSTRLEN];
2955         int res;
2956         ast_mutex_lock(&peerlock);
2957         pack = data;
2958         if (pack->retrans < 1) {
2959                 pack->retransid = -1;
2960                 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2961                         ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
2962                                 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr), 
2963                                 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2964                 destroy_trans(pack->parent, 1);
2965                 res = 0;
2966         } else {
2967                 /* Decrement retransmission, try again */
2968                 pack->retrans--;
2969                 dundi_xmit(pack);
2970                 res = 1;
2971         }
2972         ast_mutex_unlock(&peerlock);
2973         return res;
2974 }
2975
2976 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2977 {
2978         struct dundi_packet *pack;
2979         int res;
2980         int len;
2981         char eid_str[20];
2982         len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2983         /* Reserve enough space for encryption */
2984         if (ast_test_flag(trans, FLAG_ENCRYPT))
2985                 len += 384;
2986         pack = malloc(len);
2987         if (pack) {
2988                 memset(pack, 0, len);
2989                 pack->h = (struct dundi_hdr *)(pack->data);
2990                 if (cmdresp != DUNDI_COMMAND_ACK) {
2991                         pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2992                         pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2993                         pack->next = trans->packets;
2994                         trans->packets = pack;
2995                 }
2996                 pack->parent = trans;
2997                 pack->h->strans = htons(trans->strans);
2998                 pack->h->dtrans = htons(trans->dtrans);
2999                 pack->h->iseqno = trans->iseqno;
3000                 pack->h->oseqno = trans->oseqno;
3001                 pack->h->cmdresp = cmdresp;
3002                 pack->datalen = sizeof(struct dundi_hdr);
3003                 if (ied) {
3004                         memcpy(pack->h->ies, ied->buf, ied->pos);
3005                         pack->datalen += ied->pos;
3006                 } 
3007                 if (final) {
3008                         pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3009                         ast_set_flag(trans, FLAG_FINAL);
3010                 }
3011                 pack->h->cmdflags = flags;
3012                 if (cmdresp != DUNDI_COMMAND_ACK) {
3013                         trans->oseqno++;
3014                         trans->oseqno = trans->oseqno % 256;
3015                 }
3016                 trans->aseqno = trans->iseqno;
3017                 /* If we have their public key, encrypt */
3018                 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3019                         switch(cmdresp) {
3020                         case DUNDI_COMMAND_REGREQ:
3021                         case DUNDI_COMMAND_REGRESPONSE:
3022                         case DUNDI_COMMAND_DPDISCOVER:
3023                         case DUNDI_COMMAND_DPRESPONSE:
3024                         case DUNDI_COMMAND_EIDQUERY:
3025                         case DUNDI_COMMAND_EIDRESPONSE:
3026                         case DUNDI_COMMAND_PRECACHERQ:
3027                         case DUNDI_COMMAND_PRECACHERP:
3028                                 if (dundidebug)
3029                                         dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3030                                 res = dundi_encrypt(trans, pack);
3031                                 break;
3032                         default:
3033                                 res = 0;
3034                         }
3035                 } else 
3036                         res = 0;
3037                 if (!res) 
3038                         res = dundi_xmit(pack);
3039                 if (res)
3040                         ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3041                                 
3042                 if (cmdresp == DUNDI_COMMAND_ACK)
3043                         free(pack);
3044                 return res;
3045         }
3046         return -1;
3047 }
3048
3049 static int do_autokill(void *data)
3050 {
3051         struct dundi_transaction *trans = data;
3052         char eid_str[20];
3053         ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 
3054                 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3055         trans->autokillid = -1;
3056         destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3057         return 0;
3058 }
3059
3060 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3061 {
3062         struct dundi_peer *p;
3063         if (!dundi_eid_cmp(eid, us)) {
3064                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3065                 return;
3066         }
3067         ast_mutex_lock(&peerlock);
3068         p = peers;
3069         while(p) {
3070                 if (!dundi_eid_cmp(&p->eid, eid)) {
3071                         if (has_permission(p->include, context))
3072                                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3073                         else
3074                                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3075                         break;
3076                 }
3077                 p = p->next;
3078         }
3079         if (!p)
3080                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3081         ast_mutex_unlock(&peerlock);
3082 }
3083
3084 static int dundi_discover(struct dundi_transaction *trans)
3085 {
3086         struct dundi_ie_data ied;
3087         int x;
3088         if (!trans->parent) {
3089                 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3090                 return -1;
3091         }
3092         memset(&ied, 0, sizeof(ied));
3093         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3094         if (!dundi_eid_zero(&trans->us_eid))
3095                 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3096         for (x=0;x<trans->eidcount;x++)
3097                 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3098         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3099         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3100         dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3101         if (trans->parent->cbypass)
3102                 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3103         if (trans->autokilltimeout)
3104                 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3105         return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3106 }
3107
3108 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3109 {
3110         struct dundi_ie_data ied;
3111         int x, res;
3112         int max = 999999;
3113         int expiration = dundi_cache_time;
3114         int ouranswers=0;
3115         dundi_eid *avoid[1] = { NULL, };
3116         int direct[1] = { 0, };
3117         struct dundi_result dr[MAX_RESULTS];
3118         struct dundi_hint_metadata hmd;
3119         if (!trans->parent) {
3120                 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3121                 return -1;
3122         }
3123         memset(&hmd, 0, sizeof(hmd));
3124         memset(&dr, 0, sizeof(dr));
3125         /* Look up the answers we're going to include */
3126         for (x=0;x<mapcount;x++)
3127                 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3128         if (ouranswers < 0)
3129                 ouranswers = 0;
3130         for (x=0;x<ouranswers;x++) {
3131                 if (dr[x].weight < max)
3132                         max = dr[x].weight;
3133         }
3134         if (max) {
3135                 /* If we do not have a canonical result, keep looking */
3136                 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);
3137                 if (res > 0) {
3138                         /* Append answer in result */
3139                         ouranswers += res;
3140                 }
3141         }
3142         
3143         if (ouranswers > 0) {
3144                 *foundanswers += ouranswers;
3145                 memset(&ied, 0, sizeof(ied));
3146                 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3147                 if (!dundi_eid_zero(&trans->us_eid))
3148                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3149                 for (x=0;x<trans->eidcount;x++)
3150                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3151                 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3152                 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3153                 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3154                 for (x=0;x<ouranswers;x++) {
3155                         /* Add answers */
3156                         if (dr[x].expiration && (expiration > dr[x].expiration))
3157                                 expiration = dr[x].expiration;
3158                         dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3159                 }
3160                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3161                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3162                 if (trans->autokilltimeout)
3163                         trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3164                 if (expiration < *minexp)
3165                         *minexp = expiration;
3166                 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3167         } else {
3168                 /* Oops, nothing to send... */
3169                 destroy_trans(trans, 0);
3170                 return 0;
3171         }
3172 }
3173
3174 static int dundi_query(struct dundi_transaction *trans)
3175 {
3176         struct dundi_ie_data ied;
3177         int x;
3178         if (!trans->parent) {
3179                 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3180                 return -1;
3181         }
3182         memset(&ied, 0, sizeof(ied));
3183         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3184         if (!dundi_eid_zero(&trans->us_eid))
3185                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3186         for (x=0;x<trans->eidcount;x++)
3187                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3188         dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3189         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3190         dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3191         if (trans->autokilltimeout)
3192                 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3193         return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3194 }
3195
3196 static int discover_transactions(struct dundi_request *dr)
3197 {
3198         struct dundi_transaction *trans;
3199         ast_mutex_lock(&peerlock);
3200         trans = dr->trans;
3201         while(trans) {
3202                 dundi_discover(trans);
3203                 trans = trans->next;
3204         }
3205         ast_mutex_unlock(&peerlock);
3206         return 0;
3207 }
3208
3209 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3210 {
3211         struct dundi_transaction *trans, *transn;
3212         /* Mark all as "in thread" so they don't disappear */
3213         ast_mutex_lock(&peerlock);
3214         trans = dr->trans;
3215         while(trans) {
3216                 if (trans->thread)
3217                         ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3218                 trans->thread = 1;
3219                 trans = trans->next;
3220         }
3221         ast_mutex_unlock(&peerlock);
3222
3223         trans = dr->trans;
3224         while(trans) {
3225                 if (!ast_test_flag(trans, FLAG_DEAD))
3226                         precache_trans(trans, maps, mapcount, expiration, foundanswers);
3227                 trans = trans->next;
3228         }
3229
3230         /* Cleanup any that got destroyed in the mean time */
3231         ast_mutex_lock(&peerlock);
3232         trans = dr->trans;
3233         while(trans) {
3234                 transn = trans->next;
3235                 trans->thread = 0;
3236                 if (ast_test_flag(trans, FLAG_DEAD)) {
3237                         ast_log(LOG_DEBUG, "Our transaction went away!\n");
3238                         destroy_trans(trans, 0);
3239                 }
3240                 trans = transn;
3241         }
3242         ast_mutex_unlock(&peerlock);
3243         return 0;
3244 }
3245
3246 static int query_transactions(struct dundi_request *dr)
3247 {
3248         struct dundi_transaction *trans;
3249         ast_mutex_lock(&peerlock);
3250         trans = dr->trans;
3251         while(trans) {
3252                 dundi_query(trans);
3253                 trans = trans->next;
3254         }
3255         ast_mutex_unlock(&peerlock);
3256         return 0;
3257 }
3258
3259 static int optimize_transactions(struct dundi_request *dr, int order)
3260 {
3261         /* Minimize the message propagation through DUNDi by
3262            alerting the network to hops which should be not be considered */
3263         struct dundi_transaction *trans;
3264         struct dundi_peer *peer;
3265         dundi_eid tmp;
3266         int x;
3267         int needpush;
3268         ast_mutex_lock(&peerlock);
3269         trans = dr->trans;
3270         while(trans) {
3271                 /* Pop off the true root */
3272                 if (trans->eidcount) {
3273                         tmp = trans->eids[--trans->eidcount];
3274                         needpush = 1;
3275                 } else {
3276                         tmp = trans->us_eid;
3277                         needpush = 0;
3278                 }
3279
3280                 peer = peers;
3281                 while(peer) {
3282                         if (has_permission(peer->include, dr->dcontext) && 
3283                             dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3284                                 (peer->order <= order)) {
3285                                 /* For each other transaction, make sure we don't
3286                                    ask this EID about the others if they're not
3287                                    already in the list */
3288                                 if (!dundi_eid_cmp(&tmp, &peer->eid)) 
3289                                         x = -1;
3290                                 else {
3291                                         for (x=0;x<trans->eidcount;x++) {
3292                                                 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3293                                                         break;
3294                                         }
3295                                 }
3296                                 if (x == trans->eidcount) {
3297                                         /* Nope not in the list, if needed, add us at the end since we're the source */
3298                                         if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3299                                                 trans->eids[trans->eidcount++] = peer->eid;
3300                                                 /* Need to insert the real root (or us) at the bottom now as
3301                                                    a requirement now.  */
3302                                                 needpush = 1;
3303                                         }
3304                                 }
3305                         }
3306                         peer = peer->next;
3307                 }
3308                 /* If necessary, push the true root back on the end */
3309                 if (needpush)
3310                         trans->eids[trans->eidcount++] = tmp;
3311                 trans = trans->next;
3312         }
3313         ast_mutex_unlock(&peerlock);
3314         return 0;
3315 }
3316
3317 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3318 {
3319         struct dundi_transaction *trans;
3320         int x;
3321         char eid_str[20];
3322         char eid_str2[20];
3323         /* Ignore if not registered */
3324         if (!p->addr.sin_addr.s_addr)
3325                 return 0;
3326         if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3327                 return 0;
3328         if (ast_strlen_zero(dr->number))
3329                 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);
3330         else
3331                 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);
3332         trans = create_transaction(p);
3333         if (!trans)
3334                 return -1;
3335         trans->next = dr->trans;
3336         trans->parent = dr;
3337         trans->ttl = ttl;
3338         for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
3339                 trans->eids[x] = *avoid[x];
3340         trans->eidcount = x;
3341         dr->trans = trans;
3342         return 0;
3343 }
3344
3345 static void cancel_request(struct dundi_request *dr)
3346 {
3347         struct dundi_transaction *trans, *next;
3348
3349         ast_mutex_lock(&peerlock);
3350         trans = dr->trans;