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