Use same defines for NetBSD as FreeBSD in pbx_dundi.c (bug 2716)
[asterisk/asterisk.git] / pbx / pbx_dundi.c
1 /*
2  * Distributed Universal Number Discovery (DUNDi)
3  *
4  * Copyright (C) 2004, Digium Inc.
5  *
6  * Written by Mark Spencer <markster@digium.com>
7  *
8  * This program is Free Software distributed under the terms of
9  * of the GNU General Public License.
10  */
11
12 #include <asterisk/file.h>
13 #include <asterisk/logger.h>
14 #include <asterisk/channel.h>
15 #include <asterisk/config.h>
16 #include <asterisk/options.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/module.h>
19 #include <asterisk/frame.h>
20 #include <asterisk/file.h>
21 #include <asterisk/channel_pvt.h>
22 #include <asterisk/cli.h>
23 #include <asterisk/lock.h>
24 #include <asterisk/md5.h>
25 #include <asterisk/dundi.h>
26 #include <asterisk/sched.h>
27 #include <asterisk/io.h>
28 #include <asterisk/utils.h>
29 #include <asterisk/crypto.h>
30 #include <asterisk/astdb.h>
31 #include <asterisk/acl.h>
32 #include <asterisk/aes.h>
33
34 #include "dundi-parser.h"
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
39 #include <sys/socket.h>
40 #include <string.h>
41 #include <errno.h>
42 #if defined(__FreeBSD__) || defined(__NetBSD__)
43 #include <sys/types.h>
44 #include <netinet/in_systm.h>
45 #endif
46 #include <netinet/ip.h>
47 #include <sys/ioctl.h>
48 #include <netinet/in.h>
49 #include <net/if.h>
50 #if defined(__FreeBSD__) || defined(__NetBSD__)
51 #include <net/if_dl.h>
52 #include <ifaddrs.h>
53 #endif
54 #include <zlib.h>
55
56 #define MAX_RESULTS     64
57
58 #define MAX_PACKET_SIZE 8192
59
60 extern char ast_config_AST_KEY_DIR[];
61
62 static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
63
64 static char *app = "DUNDiLookup";
65 static char *synopsis = "Look up a number with DUNDi";
66 static char *descrip = 
67 "DUNDiLookup(number[|context[|options]])\n"
68 "      Looks up a given number in the global context specified or in\n"
69 "the reserved 'e164' context if not specified.  Returns -1 if the channel\n"
70 "is hungup during the lookup or 0 otherwise.  On completion, the variable\n"
71 "${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
72 "of the appropriate technology and destination to access the number. If no\n"
73 "answer was found, and the priority n + 101 exists, execution will continue\n"
74 "at that location.\n";
75
76 #define DUNDI_MODEL_INBOUND             (1 << 0)
77 #define DUNDI_MODEL_OUTBOUND    (1 << 1)
78 #define DUNDI_MODEL_SYMMETRIC   (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
79
80 /* Keep times of last 10 lookups */
81 #define DUNDI_TIMING_HISTORY    10
82
83 #define FLAG_ISREG              (1 << 0)                /* Transaction is register request */
84 #define FLAG_DEAD               (1 << 1)                /* Transaction is dead */
85 #define FLAG_FINAL              (1 << 2)                /* Transaction has final message sent */
86 #define FLAG_ISQUAL             (1 << 3)                /* Transaction is a qualification */
87 #define FLAG_ENCRYPT    (1 << 4)                /* Transaction is encrypted wiht ECX/DCX */
88 #define FLAG_SENDFULLKEY        (1 << 5)                /* Send full key on transaction */
89 #define FLAG_STOREHIST  (1 << 6)                /* Record historic performance */
90
91 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
92
93 #if 0
94 #define DUNDI_SECRET_TIME 15    /* Testing only */
95 #else
96 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
97 #endif
98
99 #define KEY_OUT                 0
100 #define KEY_IN                  1
101
102 static struct io_context *io;
103 static struct sched_context *sched;
104 static int netsocket = -1;
105 static pthread_t netthreadid = AST_PTHREADT_NULL;
106 static 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("%s", data);
253 }
254
255 static void dundi_error_output(const char *data)
256 {
257         ast_log(LOG_WARNING, "%s", 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         int stats=0;
1976         if ((argc < 2) || (argc > 3))
1977                 return RESULT_SHOWUSAGE;
1978         if (argc > 2) {
1979                 if (!strcasecmp(argv[2], "stats"))
1980                         stats = 1;
1981                 else
1982                         return RESULT_SHOWUSAGE;
1983         }
1984         if (stats) {
1985                 /* Flush statistics */
1986                 struct dundi_peer *p;
1987                 int x;
1988                 ast_mutex_lock(&peerlock);
1989                 p = peers;
1990                 while(p) {
1991                         for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
1992                                 if (p->lookups[x])
1993                                         free(p->lookups[x]);
1994                                 p->lookups[x] = NULL;
1995                                 p->lookuptimes[x] = 0;
1996                         }
1997                         p->avgms = 0;
1998                         p = p->next;
1999                 }
2000                 ast_mutex_unlock(&peerlock);
2001         } else {
2002                 ast_db_deltree("dundi/cache", NULL);
2003                 ast_cli(fd, "DUNDi Cache Flushed\n");
2004         }
2005         return RESULT_SUCCESS;
2006 }
2007
2008 static int dundi_no_debug(int fd, int argc, char *argv[])
2009 {
2010         if (argc != 3)
2011                 return RESULT_SHOWUSAGE;
2012         dundidebug = 0;
2013         ast_cli(fd, "DUNDi Debugging Disabled\n");
2014         return RESULT_SUCCESS;
2015 }
2016
2017 static int dundi_no_store_history(int fd, int argc, char *argv[])
2018 {
2019         if (argc != 4)
2020                 return RESULT_SHOWUSAGE;
2021         global_storehistory = 0;
2022         ast_cli(fd, "DUNDi History Storage Disabled\n");
2023         return RESULT_SUCCESS;
2024 }
2025
2026 static char *model2str(int model)
2027 {
2028         switch(model) {
2029         case DUNDI_MODEL_INBOUND:
2030                 return "Inbound";
2031         case DUNDI_MODEL_OUTBOUND:
2032                 return "Outbound";
2033         case DUNDI_MODEL_SYMMETRIC:
2034                 return "Symmetric";
2035         default:
2036                 return "Unknown";
2037         }
2038 }
2039
2040 static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
2041 {
2042         int which=0;
2043         char *ret;
2044         struct dundi_peer *p;
2045         char eid_str[20];
2046         if (pos != rpos)
2047                 return NULL;
2048         ast_mutex_lock(&peerlock);
2049         p = peers;
2050         while(p) {
2051                 if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
2052                         if (++which > state)
2053                                 break;
2054                 }
2055                 p = p->next;
2056         }
2057         if (p) {
2058                 ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
2059         } else
2060                 ret = NULL;
2061         ast_mutex_unlock(&peerlock);
2062         return ret;
2063 }
2064
2065 static char *complete_peer_4(char *line, char *word, int pos, int state)
2066 {
2067         return complete_peer_helper(line, word, pos, state, 3);
2068 }
2069
2070 static int rescomp(const void *a, const void *b)
2071 {
2072         const struct dundi_result *resa, *resb;
2073         resa = a;
2074         resb = b;
2075         if (resa->weight < resb->weight)
2076                 return -1;
2077         if (resa->weight > resb->weight)
2078                 return 1;
2079         return 0;
2080 }
2081
2082 static void sort_results(struct dundi_result *results, int count)
2083 {
2084         qsort(results, count, sizeof(results[0]), rescomp);
2085 }
2086
2087 static int dundi_do_lookup(int fd, int argc, char *argv[])
2088 {
2089         int res;
2090         char tmp[256] = "";
2091         char fs[80] = "";
2092         char *context;
2093         int x;
2094         int bypass = 0;
2095         struct dundi_result dr[MAX_RESULTS];
2096         struct timeval start;
2097         if ((argc < 3) || (argc > 4))
2098                 return RESULT_SHOWUSAGE;
2099         if (argc > 3) {
2100                 if (!strcasecmp(argv[3], "bypass"))
2101                         bypass=1;
2102                 else
2103                         return RESULT_SHOWUSAGE;
2104         }
2105         strncpy(tmp, argv[2], sizeof(tmp) - 1);
2106         context = strchr(tmp, '@');
2107         if (context) {
2108                 *context = '\0';
2109                 context++;
2110         }
2111         gettimeofday(&start, NULL);
2112         res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2113         
2114         if (res < 0) 
2115                 ast_cli(fd, "DUNDi lookup returned error.\n");
2116         else if (!res) 
2117                 ast_cli(fd, "DUNDi lookup returned no results.\n");
2118         else
2119                 sort_results(dr, res);
2120         for (x=0;x<res;x++) {
2121                 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));
2122         }
2123         ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
2124         return RESULT_SUCCESS;
2125 }
2126
2127 static int dundi_do_query(int fd, int argc, char *argv[])
2128 {
2129         int res;
2130         char tmp[256] = "";
2131         char *context;
2132         dundi_eid eid;
2133         struct dundi_entity_info dei;
2134         if ((argc < 3) || (argc > 3))
2135                 return RESULT_SHOWUSAGE;
2136         if (dundi_str_to_eid(&eid, argv[2])) {
2137                 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2138                 return RESULT_SHOWUSAGE;
2139         }
2140         strncpy(tmp, argv[2], sizeof(tmp) - 1);
2141         context = strchr(tmp, '@');
2142         if (context) {
2143                 *context = '\0';
2144                 context++;
2145         }
2146         res = dundi_query_eid(&dei, context, eid);
2147         if (res < 0) 
2148                 ast_cli(fd, "DUNDi Query EID returned error.\n");
2149         else if (!res) 
2150                 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2151         else {
2152                 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2153                 ast_cli(fd, "Department:      %s\n", dei.orgunit);
2154                 ast_cli(fd, "Organization:    %s\n", dei.org);
2155                 ast_cli(fd, "City/Locality:   %s\n", dei.locality);
2156                 ast_cli(fd, "State/Province:  %s\n", dei.stateprov);
2157                 ast_cli(fd, "Country:         %s\n", dei.country);
2158                 ast_cli(fd, "E-mail:          %s\n", dei.email);
2159                 ast_cli(fd, "Phone:           %s\n", dei.phone);
2160                 ast_cli(fd, "IP Address:      %s\n", dei.ipaddr);
2161         }
2162         return RESULT_SUCCESS;
2163 }
2164
2165 static int dundi_show_peer(int fd, int argc, char *argv[])
2166 {
2167         struct dundi_peer *peer;
2168         struct permission *p;
2169         char *order;
2170         char iabuf[INET_ADDRSTRLEN];
2171         char eid_str[20];
2172         int x, cnt;
2173         
2174         if (argc != 4)
2175                 return RESULT_SHOWUSAGE;
2176         ast_mutex_lock(&peerlock);
2177         peer = peers;
2178         while(peer) {
2179                 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2180                         break;
2181                 peer = peer->next;
2182         }
2183         if (peer) {
2184                 switch(peer->order) {
2185                 case 0:
2186                         order = "Primary";
2187                         break;
2188                 case 1:
2189                         order = "Secondary";
2190                         break;
2191                 case 2:
2192                         order = "Tertiary";
2193                         break;
2194                 case 3:
2195                         order = "Quartiary";
2196                         break;
2197                 default:
2198                         order = "Unknown";
2199                 }
2200                 ast_cli(fd, "Peer:    %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2201                 ast_cli(fd, "Model:   %s\n", model2str(peer->model));
2202                 ast_cli(fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2203                 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2204                 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2205                 ast_cli(fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
2206                 ast_cli(fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2207                 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2208                 if (peer->include) {
2209                         ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2210                 }
2211                 p = peer->include;
2212                 while(p) {
2213                         ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2214                         p = p->next;
2215                 }
2216                 if (peer->permit) {
2217                         ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2218                 }
2219                 p = peer->permit;
2220                 while(p) {
2221                         ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2222                         p = p->next;
2223                 }
2224                 cnt = 0;
2225                 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2226                         if (peer->lookups[x]) {
2227                                 if (!cnt)
2228                                         ast_cli(fd, "Last few query times:\n");
2229                                 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2230                                 cnt++;
2231                         }
2232                 }
2233                 if (cnt)
2234                         ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2235         } else
2236                 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2237         ast_mutex_unlock(&peerlock);
2238         return RESULT_SUCCESS;
2239 }
2240
2241 static int dundi_show_peers(int fd, int argc, char *argv[])
2242 {
2243 #define FORMAT2 "%-20.20s %-15.15s     %-10.10s %-8.8s %-15.15s\n"
2244 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2245         struct dundi_peer *peer;
2246         char iabuf[INET_ADDRSTRLEN];
2247         int registeredonly=0;
2248         char avgms[20];
2249         char eid_str[20];
2250         if ((argc != 3) && (argc != 4) && (argc != 5))
2251                 return RESULT_SHOWUSAGE;
2252         if ((argc == 4)) {
2253                 if (!strcasecmp(argv[3], "registered")) {
2254                         registeredonly = 1;
2255                 } else
2256                         return RESULT_SHOWUSAGE;
2257         }
2258         ast_mutex_lock(&peerlock);
2259         ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2260         for (peer = peers;peer;peer = peer->next) {
2261                 char status[20] = "";
2262         int print_line = -1;
2263                 char srch[2000] = "";
2264                 if (registeredonly && !peer->addr.sin_addr.s_addr)
2265                         continue;
2266                 if (peer->maxms) {
2267                         if (peer->lastms < 0)
2268                                 strncpy(status, "UNREACHABLE", sizeof(status) - 1);
2269                         else if (peer->lastms > peer->maxms) 
2270                                 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2271                         else if (peer->lastms) 
2272                                 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2273                         else 
2274                                 strncpy(status, "UNKNOWN", sizeof(status) - 1);
2275                 } else 
2276                         strncpy(status, "Unmonitored", sizeof(status) - 1);
2277                 if (peer->avgms) 
2278                         snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2279                 else
2280                         strcpy(avgms, "Unavail");
2281                 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2282                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2283                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2284
2285                 if (argc == 5) {
2286                   if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2287                         print_line = -1;
2288                    } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2289                         print_line = 1;
2290                    } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2291                         print_line = -1;
2292                    } else {
2293                         print_line = 0;
2294                   }
2295                 }
2296                 
2297         if (print_line) {
2298                         ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2299                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2300                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2301                 }
2302         }
2303         ast_mutex_unlock(&peerlock);
2304         return RESULT_SUCCESS;
2305 #undef FORMAT
2306 #undef FORMAT2
2307 }
2308
2309 static int dundi_show_trans(int fd, int argc, char *argv[])
2310 {
2311 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2312 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2313         struct dundi_transaction *trans;
2314         char iabuf[INET_ADDRSTRLEN];
2315         if (argc != 3)
2316                 return RESULT_SHOWUSAGE;
2317         ast_mutex_lock(&peerlock);
2318         ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2319         for (trans = alltrans;trans;trans = trans->allnext) {
2320                         ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr), 
2321                                         ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2322         }
2323         ast_mutex_unlock(&peerlock);
2324         return RESULT_SUCCESS;
2325 #undef FORMAT
2326 #undef FORMAT2
2327 }
2328
2329 static int dundi_show_entityid(int fd, int argc, char *argv[])
2330 {
2331         char eid_str[20];
2332         if (argc != 3)
2333                 return RESULT_SHOWUSAGE;
2334         ast_mutex_lock(&peerlock);
2335         dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2336         ast_mutex_unlock(&peerlock);
2337         ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2338         return RESULT_SUCCESS;
2339 }
2340
2341 static int dundi_show_requests(int fd, int argc, char *argv[])
2342 {
2343 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2344 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2345         struct dundi_request *req;
2346         char eidstr[20];
2347         if (argc != 3)
2348                 return RESULT_SHOWUSAGE;
2349         ast_mutex_lock(&peerlock);
2350         ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2351         for (req = requests;req;req = req->next) {
2352                         ast_cli(fd, FORMAT, req->number, req->dcontext,
2353                                                 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2354         }
2355         ast_mutex_unlock(&peerlock);
2356         return RESULT_SUCCESS;
2357 #undef FORMAT
2358 #undef FORMAT2
2359 }
2360
2361 /* Grok-a-dial DUNDi */
2362
2363 static int dundi_show_mappings(int fd, int argc, char *argv[])
2364 {
2365 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2366 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2367         struct dundi_mapping *map;
2368         char fs[256];
2369         if (argc != 3)
2370                 return RESULT_SHOWUSAGE;
2371         ast_mutex_lock(&peerlock);
2372         ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2373         for (map = mappings;map;map = map->next) {
2374                         ast_cli(fd, FORMAT, map->dcontext, map->weight, 
2375                                             ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
2376                                                                 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2377         }
2378         ast_mutex_unlock(&peerlock);
2379         return RESULT_SUCCESS;
2380 #undef FORMAT
2381 #undef FORMAT2
2382 }
2383
2384 static char debug_usage[] = 
2385 "Usage: dundi debug\n"
2386 "       Enables dumping of DUNDi packets for debugging purposes\n";
2387
2388 static char no_debug_usage[] = 
2389 "Usage: dundi no debug\n"
2390 "       Disables dumping of DUNDi packets for debugging purposes\n";
2391
2392 static char store_history_usage[] = 
2393 "Usage: dundi store history\n"
2394 "       Enables storing of DUNDi requests and times for debugging\n"
2395 "purposes\n";
2396
2397 static char no_store_history_usage[] = 
2398 "Usage: dundi no store history\n"
2399 "       Disables storing of DUNDi requests and times for debugging\n"
2400 "purposes\n";
2401
2402 static char show_peers_usage[] = 
2403 "Usage: dundi show peers\n"
2404 "       Lists all known DUNDi peers.\n";
2405
2406 static char show_trans_usage[] = 
2407 "Usage: dundi show trans\n"
2408 "       Lists all known DUNDi transactions.\n";
2409
2410 static char show_mappings_usage[] = 
2411 "Usage: dundi show mappings\n"
2412 "       Lists all known DUNDi mappings.\n";
2413
2414 static char show_entityid_usage[] = 
2415 "Usage: dundi show entityid\n"
2416 "       Displays the global entityid for this host.\n";
2417
2418 static char show_peer_usage[] = 
2419 "Usage: dundi show peer [peer]\n"
2420 "       Provide a detailed description of a specifid DUNDi peer.\n";
2421
2422 static char show_requests_usage[] = 
2423 "Usage: dundi show requests\n"
2424 "       Lists all known pending DUNDi requests.\n";
2425
2426 static char lookup_usage[] =
2427 "Usage: dundi lookup <number>[@context] [bypass]\n"
2428 "       Lookup the given number within the given DUNDi context\n"
2429 "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
2430 "keyword is specified.\n";
2431
2432 static char query_usage[] =
2433 "Usage: dundi query <entity>[@context]\n"
2434 "       Attempts to retrieve contact information for a specific\n"
2435 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2436 "e164 if none is specified).\n";
2437
2438 static char flush_usage[] =
2439 "Usage: dundi flush [stats]\n"
2440 "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
2441 "'stats' is present, clears timer statistics instead of normal\n"
2442 "operation.\n";
2443
2444 static struct ast_cli_entry  cli_debug =
2445         { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2446
2447 static struct ast_cli_entry  cli_store_history =
2448         { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2449
2450 static struct ast_cli_entry  cli_no_store_history =
2451         { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2452
2453 static struct ast_cli_entry  cli_flush =
2454         { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2455
2456 static struct ast_cli_entry  cli_no_debug =
2457         { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2458
2459 static struct ast_cli_entry  cli_show_peers =
2460         { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2461
2462 static struct ast_cli_entry  cli_show_trans =
2463         { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2464
2465 static struct ast_cli_entry  cli_show_entityid =
2466         { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2467
2468 static struct ast_cli_entry  cli_show_mappings =
2469         { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2470
2471 static struct ast_cli_entry  cli_show_requests =
2472         { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2473
2474 static struct ast_cli_entry  cli_show_peer =
2475         { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2476
2477 static struct ast_cli_entry  cli_lookup =
2478         { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2479
2480 static struct ast_cli_entry  cli_queryeid =
2481         { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2482
2483 STANDARD_LOCAL_USER;
2484
2485 LOCAL_USER_DECL;
2486
2487 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2488 {
2489         struct dundi_transaction *trans;
2490         int tid;
2491         
2492         /* Don't allow creation of transactions to non-registered peers */
2493         if (p && !p->addr.sin_addr.s_addr)
2494                 return NULL;
2495         tid = get_trans_id();
2496         if (tid < 1)
2497                 return NULL;
2498         trans = malloc(sizeof(struct dundi_transaction));
2499         if (trans) {
2500                 memset(trans, 0, sizeof(struct dundi_transaction));
2501                 if (global_storehistory) {
2502                         gettimeofday(&trans->start, NULL);
2503                         trans->flags |= FLAG_STOREHIST;
2504                 }
2505                 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2506                 trans->autokillid = -1;
2507                 if (p) {
2508                         apply_peer(trans, p);
2509                         if (!p->sentfullkey)
2510                                 trans->flags |= FLAG_SENDFULLKEY;
2511                 }
2512                 trans->strans = tid;
2513                 trans->allnext = alltrans;
2514                 alltrans = trans;
2515         }
2516         return trans;
2517 }
2518
2519 static int dundi_xmit(struct dundi_packet *pack)
2520 {
2521         int res;
2522         char iabuf[INET_ADDRSTRLEN];
2523         if (dundidebug)
2524                 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2525         res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2526         if (res < 0) {
2527                 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
2528                         ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2529                         ntohs(pack->parent->addr.sin_port), strerror(errno));
2530         }
2531         if (res > 0)
2532                 res = 0;
2533         return res;
2534 }
2535
2536 static void destroy_packet(struct dundi_packet *pack, int needfree)
2537 {
2538         struct dundi_packet *prev, *cur;
2539         if (pack->parent) {
2540                 prev = NULL;
2541                 cur = pack->parent->packets;
2542                 while(cur) {
2543                         if (cur == pack) {
2544                                 if (prev)
2545                                         prev->next = cur->next;
2546                                 else
2547                                         pack->parent->packets = cur->next;
2548                                 break;
2549                         }
2550                         prev = cur;
2551                         cur = cur->next;
2552                 }
2553         }
2554         if (pack->retransid > -1)
2555                 ast_sched_del(sched, pack->retransid);
2556         if (needfree)
2557                 free(pack);
2558         else {
2559                 pack->retransid = -1;
2560                 pack->next = NULL;
2561         }
2562 }
2563
2564 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2565 {
2566         struct dundi_transaction *cur, *prev;
2567         struct dundi_peer *peer;
2568         struct timeval tv;
2569         int ms;
2570         int x;
2571         int cnt;
2572         char eid_str[20];
2573         if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2574                 peer = peers;
2575                 while (peer) {
2576                         if (peer->regtrans == trans)
2577                                 peer->regtrans = NULL;
2578                         if (peer->keypending == trans)
2579                                 peer->keypending = NULL;
2580                         if (peer->qualtrans == trans) {
2581                                 if (fromtimeout) {
2582                                         if (peer->lastms > -1)
2583                                                 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2584                                         peer->lastms = -1;
2585                                 } else {
2586                                         gettimeofday(&tv, NULL);
2587                                         ms = (tv.tv_sec - peer->qualtx.tv_sec) * 1000 + 
2588                                                         (tv.tv_usec - peer->qualtx.tv_usec) / 1000;
2589                                         if (ms < 1)
2590                                                 ms = 1;
2591                                         if (ms < peer->maxms) {
2592                                                 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2593                                                         ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2594                                         } else if (peer->lastms < peer->maxms) {
2595                                                 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);
2596                                         }
2597                                         peer->lastms = ms;
2598                                 }
2599                                 peer->qualtrans = NULL;
2600                         }
2601                         if (trans->flags & FLAG_STOREHIST) {
2602                                 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2603                                         if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2604                                                 peer->avgms = 0;
2605                                                 cnt = 0;
2606                                                 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2607                                                         free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2608                                                 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2609                                                         peer->lookuptimes[x] = peer->lookuptimes[x-1];
2610                                                         peer->lookups[x] = peer->lookups[x-1];
2611                                                         if (peer->lookups[x]) {
2612                                                                 peer->avgms += peer->lookuptimes[x];
2613                                                                 cnt++;
2614                                                         }
2615                                                 }
2616                                                 peer->lookuptimes[0] = calc_ms(&trans->start);
2617                                                 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2618                                                 if (peer->lookups[0]) {
2619                                                         sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2620                                                         peer->avgms += peer->lookuptimes[0];
2621                                                         cnt++;
2622                                                 }
2623                                                 if (cnt)
2624                                                         peer->avgms /= cnt;
2625                                         }
2626                                 }
2627                         }
2628                         peer = peer->next;
2629                 }
2630         }
2631         if (trans->parent) {
2632                 /* Unlink from parent if appropriate */
2633                 prev = NULL;
2634                 cur = trans->parent->trans;
2635                 while(cur) {
2636                         if (cur == trans) {
2637                                 if (prev)
2638                                         prev->next = trans->next;
2639                                 else
2640                                         trans->parent->trans = trans->next;
2641                                 break;
2642                         }
2643                         prev = cur;
2644                         cur = cur->next;
2645                 }
2646                 if (!trans->parent->trans) {
2647                         /* Wake up sleeper */
2648                         if (trans->parent->pfds[1] > -1) {
2649                                 write(trans->parent->pfds[1], "killa!", 6);
2650                         }
2651                 }
2652         }
2653         /* Unlink from all trans */
2654         prev = NULL;
2655         cur = alltrans;
2656         while(cur) {
2657                 if (cur == trans) {
2658                         if (prev)
2659                                 prev->allnext = trans->allnext;
2660                         else
2661                                 alltrans = trans->allnext;
2662                         break;
2663                 }
2664                 prev = cur;
2665                 cur = cur->allnext;
2666         }
2667         destroy_packets(trans->packets);
2668         destroy_packets(trans->lasttrans);
2669         trans->packets = NULL;
2670         if (trans->autokillid > -1)
2671                 ast_sched_del(sched, trans->autokillid);
2672         trans->autokillid = -1;
2673         if (trans->thread) {
2674                 /* If used by a thread, mark as dead and be done */
2675                 trans->flags |= FLAG_DEAD;
2676         } else
2677                 free(trans);
2678 }
2679
2680 static int dundi_rexmit(void *data)
2681 {
2682         struct dundi_packet *pack;
2683         char iabuf[INET_ADDRSTRLEN];
2684         int res;
2685         ast_mutex_lock(&peerlock);
2686         pack = data;
2687         if (pack->retrans < 1) {
2688                 pack->retransid = -1;
2689                 if (!(pack->parent->flags & FLAG_ISQUAL))
2690                         ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
2691                                 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr), 
2692                                 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2693                 destroy_trans(pack->parent, 1);
2694                 res = 0;
2695         } else {
2696                 /* Decrement retransmission, try again */
2697                 pack->retrans--;
2698                 dundi_xmit(pack);
2699                 res = 1;
2700         }
2701         ast_mutex_unlock(&peerlock);
2702         return res;
2703 }
2704
2705 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2706 {
2707         struct dundi_packet *pack;
2708         int res;
2709         int len;
2710         char eid_str[20];
2711         len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2712         /* Reserve enough space for encryption */
2713         if (trans->flags & FLAG_ENCRYPT)
2714                 len += 384;
2715         pack = malloc(len);
2716         if (pack) {
2717                 memset(pack, 0, len);
2718                 pack->h = (struct dundi_hdr *)(pack->data);
2719                 if (cmdresp != DUNDI_COMMAND_ACK) {
2720                         pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2721                         pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2722                         pack->next = trans->packets;
2723                         trans->packets = pack;
2724                 }
2725                 pack->parent = trans;
2726                 pack->h->strans = htons(trans->strans);
2727                 pack->h->dtrans = htons(trans->dtrans);
2728                 pack->h->iseqno = trans->iseqno;
2729                 pack->h->oseqno = trans->oseqno;
2730                 pack->h->cmdresp = cmdresp;
2731                 pack->datalen = sizeof(struct dundi_hdr);
2732                 if (ied) {
2733                         memcpy(pack->h->ies, ied->buf, ied->pos);
2734                         pack->datalen += ied->pos;
2735                 } 
2736                 if (final) {
2737                         pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
2738                         trans->flags |= FLAG_FINAL;
2739                 }
2740                 pack->h->cmdflags = flags;
2741                 if (cmdresp != DUNDI_COMMAND_ACK) {
2742                         trans->oseqno++;
2743                         trans->oseqno = trans->oseqno % 256;
2744                 }
2745                 trans->aseqno = trans->iseqno;
2746                 /* If we have their public key, encrypt */
2747                 if (trans->flags & FLAG_ENCRYPT) {
2748                         switch(cmdresp) {
2749                         case DUNDI_COMMAND_REGREQ:
2750                         case DUNDI_COMMAND_REGRESPONSE:
2751                         case DUNDI_COMMAND_DPDISCOVER:
2752                         case DUNDI_COMMAND_DPRESPONSE:
2753                         case DUNDI_COMMAND_EIDQUERY:
2754                         case DUNDI_COMMAND_EIDRESPONSE:
2755                                 if (dundidebug)
2756                                         dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
2757                                 res = dundi_encrypt(trans, pack);
2758                                 break;
2759                         default:
2760                                 res = 0;
2761                         }
2762                 } else 
2763                         res = 0;
2764                 if (!res) 
2765                         res = dundi_xmit(pack);
2766                 if (res)
2767                         ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
2768                                 
2769                 if (cmdresp == DUNDI_COMMAND_ACK)
2770                         free(pack);
2771                 return res;
2772         }
2773         return -1;
2774 }
2775
2776 static int do_autokill(void *data)
2777 {
2778         struct dundi_transaction *trans = data;
2779         char eid_str[20];
2780         ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 
2781                 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
2782         trans->autokillid = -1;
2783         destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
2784         return 0;
2785 }
2786
2787 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
2788 {
2789         struct dundi_peer *p;
2790         if (!dundi_eid_cmp(eid, us)) {
2791                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
2792                 return;
2793         }
2794         ast_mutex_lock(&peerlock);
2795         p = peers;
2796         while(p) {
2797                 if (!dundi_eid_cmp(&p->eid, eid)) {
2798                         if (has_permission(p->include, context))
2799                                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
2800                         else
2801                                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
2802                         break;
2803                 }
2804                 p = p->next;
2805         }
2806         if (!p)
2807                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
2808         ast_mutex_unlock(&peerlock);
2809 }
2810
2811 static int dundi_discover(struct dundi_transaction *trans)
2812 {
2813         struct dundi_ie_data ied;
2814         int x;
2815         if (!trans->parent) {
2816                 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
2817                 return -1;
2818         }
2819         memset(&ied, 0, sizeof(ied));
2820         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
2821         if (!dundi_eid_zero(&trans->us_eid))
2822                 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
2823         for (x=0;x<trans->eidcount;x++)
2824                 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
2825         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
2826         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
2827         dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
2828         if (trans->parent->cbypass)
2829                 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
2830         if (trans->autokilltimeout)
2831                 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
2832         return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
2833 }
2834
2835 static int dundi_query(struct dundi_transaction *trans)
2836 {
2837         struct dundi_ie_data ied;
2838         int x;
2839         if (!trans->parent) {
2840                 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
2841                 return -1;
2842         }
2843         memset(&ied, 0, sizeof(ied));
2844         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
2845         if (!dundi_eid_zero(&trans->us_eid))
2846                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
2847         for (x=0;x<trans->eidcount;x++)
2848                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
2849         dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
2850         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
2851         dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
2852         if (trans->autokilltimeout)
2853                 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
2854         return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
2855 }
2856
2857 static int discover_transactions(struct dundi_request *dr)
2858 {
2859         struct dundi_transaction *trans;
2860         ast_mutex_lock(&peerlock);
2861         trans = dr->trans;
2862         while(trans) {
2863                 dundi_discover(trans);
2864                 trans = trans->next;
2865         }
2866         ast_mutex_unlock(&peerlock);
2867         return 0;
2868 }
2869
2870 static int query_transactions(struct dundi_request *dr)
2871 {
2872         struct dundi_transaction *trans;
2873         ast_mutex_lock(&peerlock);
2874         trans = dr->trans;
2875         while(trans) {
2876                 dundi_query(trans);
2877                 trans = trans->next;
2878         }
2879         ast_mutex_unlock(&peerlock);
2880         return 0;
2881 }
2882
2883 static int optimize_transactions(struct dundi_request *dr, int order)
2884 {
2885         /* Minimize the message propagation through DUNDi by
2886            alerting the network to hops which should be not be considered */
2887         struct dundi_transaction *trans;
2888         struct dundi_peer *peer;
2889         dundi_eid tmp;
2890         int x;
2891         int needpush;
2892         ast_mutex_lock(&peerlock);
2893         trans = dr->trans;
2894         while(trans) {
2895                 /* Pop off the true root */
2896                 if (trans->eidcount) {
2897                         tmp = trans->eids[--trans->eidcount];
2898                         needpush = 1;
2899                 } else {
2900                         tmp = trans->us_eid;
2901                         needpush = 0;
2902                 }
2903
2904                 peer = peers;
2905                 while(peer) {
2906                         if (has_permission(peer->include, dr->dcontext) && 
2907                             dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
2908                                 (peer->order <= order)) {
2909                                 /* For each other transaction, make sure we don't
2910                                    ask this EID about the others if they're not
2911                                    already in the list */
2912                                 if (!dundi_eid_cmp(&tmp, &peer->eid)) 
2913                                         x = -1;
2914                                 else {
2915                                         for (x=0;x<trans->eidcount;x++) {
2916                                                 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
2917                                                         break;
2918                                         }
2919                                 }
2920                                 if (x == trans->eidcount) {
2921                                         /* Nope not in the list, if needed, add us at the end since we're the source */
2922                                         if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
2923                                                 trans->eids[trans->eidcount++] = peer->eid;
2924                                                 /* Need to insert the real root (or us) at the bottom now as
2925                                                    a requirement now.  */
2926                                                 needpush = 1;
2927                                         }
2928                                 }
2929                         }
2930                         peer = peer->next;
2931                 }
2932                 /* If necessary, push the true root back on the end */
2933                 if (needpush)
2934                         trans->eids[trans->eidcount++] = tmp;
2935                 trans = trans->next;
2936         }
2937         ast_mutex_unlock(&peerlock);
2938         return 0;
2939 }
2940
2941 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
2942 {
2943         struct dundi_transaction *trans;
2944         int x;
2945         char eid_str[20];
2946         char eid_str2[20];
2947         /* Ignore if not registered */
2948         if (!p->addr.sin_addr.s_addr)
2949                 return 0;
2950         if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
2951                 return 0;
2952         if (ast_strlen_zero(dr->number))
2953                 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);
2954         else
2955                 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);
2956         trans = create_transaction(p);
2957         if (!trans)
2958                 return -1;
2959         trans->next = dr->trans;
2960         trans->parent = dr;
2961         trans->ttl = ttl;
2962         for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
2963                 trans->eids[x] = *avoid[x];
2964         trans->eidcount = x;
2965         dr->trans = trans;
2966         return 0;
2967 }
2968
2969 static void cancel_request(struct dundi_request *dr)
2970 {
2971         struct dundi_transaction *trans, *next;
2972
2973         ast_mutex_lock(&peerlock);
2974         trans = dr->trans;
2975         
2976         while(trans) {
2977                 next = trans->next;
2978                 /* Orphan transaction from request */
2979                 trans->parent = NULL;
2980                 trans->next = NULL;
2981                 /* Send final cancel */
2982                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
2983                 trans = next;
2984         }
2985         ast_mutex_unlock(&peerlock);
2986 }
2987
2988 static void abort_request(struct dundi_request *dr)
2989 {
2990         ast_mutex_lock(&peerlock);
2991         while(dr->trans) 
2992                 destroy_trans(dr->trans, 0);
2993         ast_mutex_unlock(&peerlock);
2994 }
2995
2996 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[])
2997 {
2998         struct dundi_peer *p;
2999         int x;
3000         int res;
3001         char eid_str[20];
3002         ast_mutex_lock(&peerlock);
3003         p = peers;
3004         while(p) {
3005                 if (has_permission(p->include, dr->dcontext)) {
3006                         if (p->order <= order) {
3007                                 /* Check order first, then check cache, regardless of
3008                                    omissions, this gets us more likely to not have an
3009                                    affected answer. */
3010                                 if(nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration))) {
3011                                         res = 0;
3012                                         /* Make sure we haven't already seen it and that it won't
3013                                            affect our answer */
3014                                         for (x=0;avoid[x];x++) {
3015                                                 if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
3016                                                         /* If not a direct connection, it affects our answer */
3017                                                         if (directs && !directs[x]) 
3018                                                                 dr->hmd->flags &= ~DUNDI_HINT_UNAFFECTED;
3019                                                         break;
3020                                                 }
3021                                         }
3022                                         /* Make sure we can ask */
3023                                         if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
3024                                                 /* Check for a matching or 0 cache entry */
3025                                                 append_transaction(dr, p, ttl, avoid);
3026                                         } else
3027                                                 ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
3028                                 }
3029                                 *foundcache |= res;
3030                         } else if (!*skipped || (p->order < *skipped))
3031                                 *skipped = p->order;
3032                 }
3033                 p = p->next;
3034         }
3035         ast_mutex_unlock(&peerlock);
3036 }
3037
3038 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
3039 {
3040         struct dundi_request *cur;
3041         int res=0;
3042         char eid_str[20];
3043         ast_mutex_lock(&peerlock);
3044         cur = requests;
3045         while(cur) {
3046                 if (option_debug)
3047                         ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
3048                                 dr->dcontext, dr->number);
3049                 if (!strcasecmp(cur->dcontext, dr->dcontext) &&
3050                     !strcasecmp(cur->number, dr->number) &&
3051                         (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
3052                                 ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n", 
3053                                         cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
3054                                 *pending = cur;
3055                         res = 1;
3056                         break;
3057                 }
3058                 cur = cur->next;
3059         }
3060         if (!res) {
3061                 ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n", 
3062                                 dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
3063                 /* Go ahead and link us in since nobody else is searching for this */
3064                 dr->next = requests;
3065                 requests = dr;
3066                 *pending = NULL;
3067         }
3068         ast_mutex_unlock(&peerlock);
3069         return res;
3070 }
3071
3072 static void unregister_request(struct dundi_request *dr)
3073 {
3074         struct dundi_request *cur, *prev;
3075         ast_mutex_lock(&peerlock);
3076         prev = NULL;
3077         cur = requests;
3078         while(cur) {
3079                 if (cur == dr) {
3080                         if (prev)
3081                                 prev->next = cur->next;
3082                         else
3083                                 requests = cur->next;
3084                         break;
3085                 }
3086                 prev = cur;
3087                 cur = cur->next;
3088         }
3089         ast_mutex_unlock(&peerlock);
3090 }
3091
3092 static int check_request(struct dundi_request *dr)
3093 {
3094         struct dundi_request *cur;
3095         int res = 0;
3096         ast_mutex_lock(&peerlock);
3097         cur = requests;
3098         while(cur) {
3099                 if (cur == dr) {
3100                         res = 1;
3101                         break;
3102                 }
3103                 cur = cur->next;
3104         }
3105         ast_mutex_unlock(&peerlock);
3106         return res;
3107 }
3108
3109 static unsigned long avoid_crc32(dundi_eid *avoid[])
3110 {
3111         /* Idea is that we're calculating a checksum which is independent of
3112            the order that the EID's are listed in */
3113         unsigned long acrc32 = 0;
3114         int x;
3115         for (x=0;avoid[x];x++) {
3116                 /* Order doesn't matter */
3117                 if (avoid[x+1]) {
3118                         acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
3119                 }
3120         }
3121         return acrc32;
3122 }
3123
3124 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[])
3125 {
3126         int res;
3127         struct dundi_request dr, *pending;
3128         dundi_eid *rooteid=NULL;
3129         int x;
3130         int ttlms;
3131         int ms;
3132         int foundcache;
3133         int skipped=0;
3134         int order=0;
3135         char eid_str[20];
3136         struct timeval start;
3137         
3138         /* Don't do anthing for a hungup channel */
3139         if (chan && chan->_softhangup)
3140                 return 0;
3141
3142         ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3143
3144         for (x=0;avoid[x];x++)
3145                 rooteid = avoid[x];
3146         /* Now perform real check */
3147         memset(&dr, 0, sizeof(dr));
3148         if (pipe(dr.pfds)) {
3149                 ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
3150                 return -1;
3151         }
3152         dr.dr = result;
3153         dr.hmd = hmd;
3154         dr.maxcount = maxret;
3155         dr.expiration = *expiration;
3156         dr.cbypass = cbypass;
3157         dr.crc32 = avoid_crc32(avoid);
3158         strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
3159         strncpy(dr.number, number, sizeof(dr.number) - 1);
3160         if (rooteid)
3161                 dr.root_eid = *rooteid;
3162         res = register_request(&dr, &pending);
3163         if (res) {
3164                 /* Already a request */
3165                 if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
3166                         /* This is on behalf of someone else.  Go ahead and close this out since
3167                            they'll get their answer anyway. */
3168                         ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3169                                 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
3170                         close(dr.pfds[0]);
3171                         close(dr.pfds[1]);
3172                         return -2;
3173                 } else {
3174                         /* Wait for the cache to populate */
3175                         ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
3176                                 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
3177                         gettimeofday(&start, NULL);
3178                         while(check_request(pending) && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
3179                                 /* XXX Would be nice to have a way to poll/select here XXX */
3180                                 usleep(1);
3181                         }
3182                         /* Continue on as normal, our cache should kick in */
3183                 }
3184         }
3185         /* Create transactions */
3186         do {
3187                 order = skipped;
3188                 skipped = 0;
3189                 foundcache = 0;
3190                 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, avoid, direct);
3191         } while (skipped && !foundcache && !dr.trans);
3192         /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
3193            do this earlier because we didn't know if we were going to have transactions
3194            or not. */
3195         if (!ttl) {
3196                 hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
3197                 abort_request(&dr);
3198                 unregister_request(&dr);
3199                 close(dr.pfds[0]);
3200                 close(dr.pfds[1]);
3201                 return 0;
3202         }
3203                 
3204         /* Optimize transactions */
3205         optimize_transactions(&dr, order);
3206         /* Actually perform transactions */
3207         discover_transactions(&dr);
3208         /* Wait for transaction to come back */
3209         gettimeofday(&start, NULL);
3210         while(dr.trans && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
3211                 ms = 100;
3212                 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3213         }
3214         if (chan && chan->_softhangup)
3215                 ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
3216         cancel_request(&dr);
3217         unregister_request(&dr);
3218         res = dr.respcount;
3219         *expiration = dr.expiration;
3220         close(dr.pfds[0]);
3221         close(dr.pfds[1]);
3222         return res;
3223 }
3224
3225 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
3226 {
3227         struct dundi_hint_metadata hmd;
3228         dundi_eid *avoid[1] = { NULL, };
3229         int direct[1] = { 0, };
3230         int expiration = DUNDI_DEFAULT_CACHE_TIME;
3231         memset(&hmd, 0, sizeof(hmd));
3232         hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
3233         return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, avoid, direct);
3234 }
3235
3236 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[])
3237 {
3238         int res;
3239         struct dundi_request dr;
3240         dundi_eid *rooteid=NULL;
3241         int x;
3242         int ttlms;
3243         int skipped=0;
3244         int foundcache=0;
3245         struct timeval start;
3246         
3247         ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3248
3249         for (x=0;avoid[x];x++)
3250                 rooteid = avoid[x];
3251         /* Now perform real check */
3252         memset(&dr, 0, sizeof(dr));
3253         dr.hmd = hmd;
3254         dr.dei = dei;
3255         strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
3256         memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
3257         if (rooteid)
3258                 dr.root_eid = *rooteid;
3259         /* Create transactions */
3260         build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, avoid, NULL);
3261
3262         /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
3263            do this earlier because we didn't know if we were going to have transactions
3264            or not. */
3265         if (!ttl) {
3266                 hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
3267                 return 0;
3268         }
3269                 
3270         /* Optimize transactions */
3271         optimize_transactions(&dr, 9999);
3272         /* Actually perform transactions */
3273         query_transactions(&dr);
3274         /* Wait for transaction to come back */
3275         gettimeofday(&start, NULL);
3276         while(dr.trans && (calc_ms(&start) < ttlms))
3277                 usleep(1);
3278         res = dr.respcount;
3279         return res;
3280 }
3281
3282 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
3283 {
3284         dundi_eid *avoid[1] = { NULL, };
3285         struct dundi_hint_metadata hmd;
3286         memset(&hmd, 0, sizeof(hmd));
3287         return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
3288 }
3289
3290 static int dundi_lookup_exec(struct ast_channel *chan, void *data)
3291 {
3292         char *tmp;
3293         char *context;
3294         char *opts;
3295         int res = -1;
3296         struct localuser *u;
3297
3298         if (!data || !strlen(data)) {
3299                 ast_log(LOG_WARNING, "DUNDiLookup requires an argument (number)\n");
3300                 return 0;
3301         }
3302         LOCAL_USER_ADD(u);
3303         tmp = ast_strdupa(data);
3304         if (tmp) {
3305                 context = strchr(tmp, '|');
3306                 if (context) {
3307                         *context = '\0';
3308                         context++;
3309                         opts = strchr(context, '|');
3310                         if (opts) {
3311                                 *opts = '\0';
3312                                 opts++;
3313                         }
3314                 } else
3315                         opts = NULL;
3316                 if (!context || !strlen(context))
3317                         context = "e164";
3318                 if (!opts)
3319                         opts = "";