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