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