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