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