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