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