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