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