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