b37c4ed290346c216ff1593430bae52ae4a8564b
[asterisk/asterisk.git] / pbx / pbx_dundi.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Distributed Universal Number Discovery (DUNDi)
22  *
23  */
24
25 /*** MODULEINFO
26         <depend>zlib</depend>
27         <use>crypto</use>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <sys/socket.h>
40 #include <string.h>
41 #include <errno.h>
42 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
43 #include <sys/types.h>
44 #include <netinet/in_systm.h>
45 #endif
46 #include <netinet/ip.h>
47 #include <sys/ioctl.h>
48 #include <netinet/in.h>
49 #include <net/if.h>
50 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
51 #include <net/if_dl.h>
52 #include <ifaddrs.h>
53 #endif
54 #include <zlib.h>
55 #include <sys/signal.h>
56 #include <pthread.h>
57
58 #include "asterisk/file.h"
59 #include "asterisk/logger.h"
60 #include "asterisk/channel.h"
61 #include "asterisk/config.h"
62 #include "asterisk/options.h"
63 #include "asterisk/pbx.h"
64 #include "asterisk/module.h"
65 #include "asterisk/frame.h"
66 #include "asterisk/file.h"
67 #include "asterisk/cli.h"
68 #include "asterisk/lock.h"
69 #include "asterisk/md5.h"
70 #include "asterisk/dundi.h"
71 #include "asterisk/sched.h"
72 #include "asterisk/io.h"
73 #include "asterisk/utils.h"
74 #include "asterisk/netsock.h"
75 #include "asterisk/crypto.h"
76 #include "asterisk/astdb.h"
77 #include "asterisk/acl.h"
78 #include "asterisk/aes.h"
79 #include "asterisk/app.h"
80
81 #include "dundi-parser.h"
82
83 #define MAX_RESULTS     64
84
85 #define MAX_PACKET_SIZE 8192
86
87 #define MAX_WEIGHT 59999
88
89 #define DUNDI_MODEL_INBOUND             (1 << 0)
90 #define DUNDI_MODEL_OUTBOUND    (1 << 1)
91 #define DUNDI_MODEL_SYMMETRIC   (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
92
93 /*! Keep times of last 10 lookups */
94 #define DUNDI_TIMING_HISTORY    10
95
96 enum {
97         FLAG_ISREG =       (1 << 0),  /*!< Transaction is register request */
98         FLAG_DEAD =        (1 << 1),  /*!< Transaction is dead */
99         FLAG_FINAL =       (1 << 2),  /*!< Transaction has final message sent */
100         FLAG_ISQUAL =      (1 << 3),  /*!< Transaction is a qualification */
101         FLAG_ENCRYPT =     (1 << 4),  /*!< Transaction is encrypted wiht ECX/DCX */
102         FLAG_SENDFULLKEY = (1 << 5),  /*!< Send full key on transaction */
103         FLAG_STOREHIST =   (1 << 6),  /*!< Record historic performance */
104 };
105
106 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
107
108 #if 0
109 #define DUNDI_SECRET_TIME 15    /* Testing only */
110 #else
111 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
112 #endif
113
114 static struct io_context *io;
115 static struct sched_context *sched;
116 static int netsocket = -1;
117 static pthread_t netthreadid = AST_PTHREADT_NULL;
118 static pthread_t precachethreadid = AST_PTHREADT_NULL;
119 static int tos = 0;
120 static int dundidebug = 0;
121 static int authdebug = 0;
122 static int dundi_ttl = DUNDI_DEFAULT_TTL;
123 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
124 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
125 static int global_autokilltimeout = 0;
126 static dundi_eid global_eid;
127 static int default_expiration = 60;
128 static int global_storehistory = 0;
129 static char dept[80];
130 static char org[80];
131 static char locality[80];
132 static char stateprov[80];
133 static char country[80];
134 static char email[80];
135 static char phone[80];
136 static char secretpath[80];
137 static char cursecret[80];
138 static char ipaddr[80];
139 static time_t rotatetime;
140 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
141 static int dundi_shutdown = 0;
142
143 struct permission {
144         AST_LIST_ENTRY(permission) list;
145         int allow;
146         char name[0];
147 };
148
149 struct dundi_packet {
150         AST_LIST_ENTRY(dundi_packet) list;
151         struct dundi_hdr *h;
152         int datalen;
153         struct dundi_transaction *parent;
154         int retransid;
155         int retrans;
156         unsigned char data[0];
157 };
158
159 struct dundi_hint_metadata {
160         unsigned short flags;
161         char exten[AST_MAX_EXTENSION];
162 };
163
164 struct dundi_precache_queue {
165         AST_LIST_ENTRY(dundi_precache_queue) list;
166         char *context;
167         time_t expiration;
168         char number[0];
169 };
170
171 struct dundi_request;
172
173 struct dundi_transaction {
174         struct sockaddr_in addr;                       /*!< Other end of transaction */
175         struct timeval start;                          /*!< When this transaction was created */
176         dundi_eid eids[DUNDI_MAX_STACK + 1];
177         int eidcount;                                  /*!< Number of eids in eids */
178         dundi_eid us_eid;                              /*!< Our EID, to them */
179         dundi_eid them_eid;                            /*!< Their EID, to us */
180         ast_aes_encrypt_key ecx;                       /*!< AES 128 Encryption context */
181         ast_aes_decrypt_key dcx;                       /*!< AES 128 Decryption context */
182         unsigned int flags;                            /*!< Has final packet been sent */
183         int ttl;                                       /*!< Remaining TTL for queries on this one */
184         int thread;                                    /*!< We have a calling thread */
185         int retranstimer;                              /*!< How long to wait before retransmissions */
186         int autokillid;                                /*!< ID to kill connection if answer doesn't come back fast enough */
187         int autokilltimeout;                           /*!< Recommended timeout for autokill */
188         unsigned short strans;                         /*!< Our transaction identifier */
189         unsigned short dtrans;                         /*!< Their transaction identifer */
190         unsigned char iseqno;                          /*!< Next expected received seqno */
191         unsigned char oiseqno;                         /*!< Last received incoming seqno */
192         unsigned char oseqno;                          /*!< Next transmitted seqno */
193         unsigned char aseqno;                          /*!< Last acknowledge seqno */
194         AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets;  /*!< Packets to be retransmitted */
195         struct packetlist lasttrans;                   /*!< Last transmitted / ACK'd packet */
196         struct dundi_request *parent;                  /*!< Parent request (if there is one) */
197         AST_LIST_ENTRY(dundi_transaction) parentlist;  /*!< Next with respect to the parent */
198         AST_LIST_ENTRY(dundi_transaction) all;         /*!< Next with respect to all DUNDi transactions */
199 };
200
201 struct dundi_request {
202         char dcontext[AST_MAX_EXTENSION];
203         char number[AST_MAX_EXTENSION];
204         dundi_eid query_eid;
205         dundi_eid root_eid;
206         struct dundi_result *dr;
207         struct dundi_entity_info *dei;
208         struct dundi_hint_metadata *hmd;
209         int maxcount;
210         int respcount;
211         int expiration;
212         int cbypass;
213         int pfds[2];
214         unsigned long crc32;                              /*!< CRC-32 of all but root EID's in avoid list */
215         AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans;  /*!< Transactions */
216         AST_LIST_ENTRY(dundi_request) list;
217 };
218
219 struct dundi_mapping {
220         char dcontext[AST_MAX_EXTENSION];
221         char lcontext[AST_MAX_EXTENSION];
222         int _weight;
223         char *weightstr;
224         int options;
225         int tech;
226         int dead;
227         char dest[AST_MAX_EXTENSION];
228         AST_LIST_ENTRY(dundi_mapping) list;
229 };
230
231 struct dundi_peer {
232         dundi_eid eid;
233         struct sockaddr_in addr;               /*!< Address of DUNDi peer */
234         AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
235         struct permissionlist include;
236         dundi_eid us_eid;
237         char inkey[80];
238         char outkey[80];
239         int dead;
240         int registerid;
241         int qualifyid;
242         int sentfullkey;
243         int order;
244         unsigned char txenckey[256];           /*!< Transmitted encrypted key + sig */
245         unsigned char rxenckey[256];           /*!< Cache received encrypted key + sig */
246         unsigned long us_keycrc32;             /*!< CRC-32 of our key */
247         ast_aes_encrypt_key us_ecx;            /*!< Cached AES 128 Encryption context */
248         ast_aes_decrypt_key us_dcx;            /*!< Cached AES 128 Decryption context */
249         unsigned long them_keycrc32;           /*!< CRC-32 of our key */
250         ast_aes_encrypt_key them_ecx;          /*!< Cached AES 128 Encryption context */
251         ast_aes_decrypt_key them_dcx;          /*!< Cached AES 128 Decryption context */
252         time_t keyexpire;                      /*!< When to expire/recreate key */
253         int registerexpire;
254         int lookuptimes[DUNDI_TIMING_HISTORY];
255         char *lookups[DUNDI_TIMING_HISTORY];
256         int avgms;
257         struct dundi_transaction *regtrans;    /*!< Registration transaction */
258         struct dundi_transaction *qualtrans;   /*!< Qualify transaction */
259         int model;                             /*!< Pull model */
260         int pcmodel;                           /*!< Push/precache model */
261         int dynamic;                           /*!< Are we dynamic? */
262         int lastms;                            /*!< Last measured latency */
263         int maxms;                             /*!< Max permissible latency */
264         struct timeval qualtx;                 /*!< Time of transmit */
265         AST_LIST_ENTRY(dundi_peer) list;
266 };
267
268 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
269 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
270 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
271 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
272 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
273
274 static int dundi_xmit(struct dundi_packet *pack);
275
276 static void dundi_debug_output(const char *data)
277 {
278         if (dundidebug)
279                 ast_verbose("%s", data);
280 }
281
282 static void dundi_error_output(const char *data)
283 {
284         ast_log(LOG_WARNING, "%s", data);
285 }
286
287 static int has_permission(struct permissionlist *permlist, char *cont)
288 {
289         struct permission *perm;
290         int res = 0;
291
292         AST_LIST_TRAVERSE(permlist, perm, list) {
293                 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
294                         res = perm->allow;
295         }
296
297         return res;
298 }
299
300 static char *tech2str(int tech)
301 {
302         switch(tech) {
303         case DUNDI_PROTO_NONE:
304                 return "None";
305         case DUNDI_PROTO_IAX:
306                 return "IAX2";
307         case DUNDI_PROTO_SIP:
308                 return "SIP";
309         case DUNDI_PROTO_H323:
310                 return "H323";
311         default:
312                 return "Unknown";
313         }
314 }
315
316 static int str2tech(char *str)
317 {
318         if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 
319                 return DUNDI_PROTO_IAX;
320         else if (!strcasecmp(str, "SIP"))
321                 return DUNDI_PROTO_SIP;
322         else if (!strcasecmp(str, "H323"))
323                 return DUNDI_PROTO_H323;
324         else
325                 return -1;
326 }
327
328 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
329 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
330 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
331 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
332 {
333         struct dundi_transaction *trans;
334
335         /* Look for an exact match first */
336         AST_LIST_TRAVERSE(&alltrans, trans, all) {
337                 if (!inaddrcmp(&trans->addr, sin) && 
338                      ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
339                           ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
340                           if (hdr->strans)
341                                   trans->dtrans = ntohs(hdr->strans) & 32767;
342                           return trans;
343                 }
344         }
345         
346         switch(hdr->cmdresp & 0x7f) {
347         case DUNDI_COMMAND_DPDISCOVER:
348         case DUNDI_COMMAND_EIDQUERY:
349         case DUNDI_COMMAND_PRECACHERQ:
350         case DUNDI_COMMAND_REGREQ:
351         case DUNDI_COMMAND_NULL:
352         case DUNDI_COMMAND_ENCRYPT:
353                 if (!hdr->strans)
354                         break;
355                 /* Create new transaction */
356                 if (!(trans = create_transaction(NULL)))
357                         break;
358                 memcpy(&trans->addr, sin, sizeof(trans->addr));
359                 trans->dtrans = ntohs(hdr->strans) & 32767;
360         default:
361                 break;
362         }
363         
364         return trans;
365 }
366
367 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
368
369 static int dundi_ack(struct dundi_transaction *trans, int final)
370 {
371         return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
372 }
373 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
374 {
375         struct {
376                 struct dundi_packet pack;
377                 struct dundi_hdr hdr;
378         } tmp;
379         struct dundi_transaction trans;
380         /* Never respond to an INVALID with another INVALID */
381         if (h->cmdresp == DUNDI_COMMAND_INVALID)
382                 return;
383         memset(&tmp, 0, sizeof(tmp));
384         memset(&trans, 0, sizeof(trans));
385         memcpy(&trans.addr, sin, sizeof(trans.addr));
386         tmp.hdr.strans = h->dtrans;
387         tmp.hdr.dtrans = h->strans;
388         tmp.hdr.iseqno = h->oseqno;
389         tmp.hdr.oseqno = h->iseqno;
390         tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
391         tmp.hdr.cmdflags = 0;
392         tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
393         tmp.pack.datalen = sizeof(struct dundi_hdr);
394         tmp.pack.parent = &trans;
395         dundi_xmit(&tmp.pack);
396 }
397
398 static void reset_global_eid(void)
399 {
400 #if defined(SIOCGIFHWADDR)
401         int s, x = 0;
402         char eid_str[20];
403         struct ifreq ifr;
404
405         s = socket(AF_INET, SOCK_STREAM, 0);
406         if (s < 0)
407                 return;
408         for (x = 0; x < 10; x++) {
409                 memset(&ifr, 0, sizeof(ifr));
410                 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
411                 if (ioctl(s, SIOCGIFHWADDR, &ifr))
412                         continue;
413                 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
414                 if (option_debug) {
415                         ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", 
416                                 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
417                 }
418                 break;
419         }
420         close(s);
421 #else
422 #if defined(ifa_broadaddr) && !defined(SOLARIS)
423         char eid_str[20];
424         struct ifaddrs *ifap;
425         
426         if (getifaddrs(&ifap) == 0) {
427                 struct ifaddrs *p;
428                 for (p = ifap; p; p = p->ifa_next) {
429                         if (p->ifa_addr->sa_family == AF_LINK) {
430                                 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
431                                 memcpy(
432                                         &(global_eid.eid),
433                                         sdp->sdl_data + sdp->sdl_nlen, 6);
434                                 if (option_debug)
435                                         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);
436                                 freeifaddrs(ifap);
437                                 return;
438                         }
439                 }
440                 freeifaddrs(ifap);
441         }
442 #endif
443 #endif
444         ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID  You will have to set it manually.\n");
445 }
446
447 static int get_trans_id(void)
448 {
449         struct dundi_transaction *t;
450         int stid = (ast_random() % 32766) + 1;
451         int tid = stid;
452
453         do {
454                 AST_LIST_TRAVERSE(&alltrans, t, all) {
455                         if (t->strans == tid) 
456                                 break;
457                 }
458                 if (!t)
459                         return tid;
460                 tid = (tid % 32766) + 1;
461         } while (tid != stid);
462
463         return 0;
464 }
465
466 static int reset_transaction(struct dundi_transaction *trans)
467 {
468         int tid;
469         tid = get_trans_id();
470         if (tid < 1)
471                 return -1;
472         trans->strans = tid;
473         trans->dtrans = 0;
474         trans->iseqno = 0;
475         trans->oiseqno = 0;
476         trans->oseqno = 0;
477         trans->aseqno = 0;
478         ast_clear_flag(trans, FLAG_FINAL);      
479         return 0;
480 }
481
482 static struct dundi_peer *find_peer(dundi_eid *eid)
483 {
484         struct dundi_peer *cur = NULL;
485
486         if (!eid)
487                 eid = &empty_eid;
488         
489         AST_LIST_TRAVERSE(&peers, cur, list) {
490                 if (!dundi_eid_cmp(&cur->eid,eid))
491                         break;
492         }
493
494         return cur;
495 }
496
497 static void build_iv(unsigned char *iv)
498 {
499         /* XXX Would be nice to be more random XXX */
500         unsigned int *fluffy;
501         int x;
502         fluffy = (unsigned int *)(iv);
503         for (x=0;x<4;x++)
504                 fluffy[x] = ast_random();
505 }
506
507 struct dundi_query_state {
508         dundi_eid *eids[DUNDI_MAX_STACK + 1]; 
509         int directs[DUNDI_MAX_STACK + 1]; 
510         dundi_eid reqeid;
511         char called_context[AST_MAX_EXTENSION];
512         char called_number[AST_MAX_EXTENSION];
513         struct dundi_mapping *maps;
514         int nummaps;
515         int nocache;
516         struct dundi_transaction *trans;
517         void *chal;
518         int challen;
519         int ttl;
520         char fluffy[0];
521 };
522
523 static int get_mapping_weight(struct dundi_mapping *map)
524 {
525         char buf[32] = "";
526
527         if (map->weightstr) {
528                 pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
529                 if (sscanf(buf, "%d", &map->_weight) != 1)
530                         map->_weight = MAX_WEIGHT;
531         }
532
533         return map->_weight;
534 }
535
536 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)
537 {
538         struct ast_flags flags = {0};
539         int x;
540         if (!ast_strlen_zero(map->lcontext)) {
541                 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
542                         ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
543                 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
544                         ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
545                 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
546                         ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
547                 if (ast_ignore_pattern(map->lcontext, called_number))
548                         ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
549
550                 /* Clearly we can't say 'don't ask' anymore if we found anything... */
551                 if (ast_test_flag(&flags, AST_FLAGS_ALL)) 
552                         ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
553
554                 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
555                         /* Skip partial answers */
556                         ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
557                 }
558                 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
559                         struct varshead headp;
560                         struct ast_var_t *newvariable;
561                         ast_set_flag(&flags, map->options & 0xffff);
562                         ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
563                         dr[anscnt].techint = map->tech;
564                         dr[anscnt].weight = get_mapping_weight(map);
565                         dr[anscnt].expiration = dundi_cache_time;
566                         ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
567                         dr[anscnt].eid = *us_eid;
568                         dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
569                         if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
570                                 AST_LIST_HEAD_INIT_NOLOCK(&headp);
571                                 newvariable = ast_var_assign("NUMBER", called_number);
572                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
573                                 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
574                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
575                                 newvariable = ast_var_assign("SECRET", cursecret);
576                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
577                                 newvariable = ast_var_assign("IPADDR", ipaddr);
578                                 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
579                                 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
580                                 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
581                                         ast_var_delete(newvariable);
582                         } else
583                                 dr[anscnt].dest[0] = '\0';
584                         anscnt++;
585                 } else {
586                         /* No answers...  Find the fewest number of digits from the
587                            number for which we have no answer. */
588                         char tmp[AST_MAX_EXTENSION];
589                         for (x=0;x<AST_MAX_EXTENSION;x++) {
590                                 tmp[x] = called_number[x];
591                                 if (!tmp[x])
592                                         break;
593                                 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
594                                         /* Oops found something we can't match.  If this is longer
595                                            than the running hint, we have to consider it */
596                                         if (strlen(tmp) > strlen(hmd->exten)) {
597                                                 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
598                                         }
599                                         break;
600                                 }
601                         }
602                 }
603         }
604         return anscnt;
605 }
606
607 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
608
609 static void *dundi_lookup_thread(void *data)
610 {
611         struct dundi_query_state *st = data;
612         struct dundi_result dr[MAX_RESULTS];
613         struct dundi_ie_data ied;
614         struct dundi_hint_metadata hmd;
615         char eid_str[20];
616         int res, x;
617         int ouranswers=0;
618         int max = 999999;
619         int expiration = dundi_cache_time;
620
621         if (option_debug)
622                 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
623                         st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
624         memset(&ied, 0, sizeof(ied));
625         memset(&dr, 0, sizeof(dr));
626         memset(&hmd, 0, sizeof(hmd));
627         /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
628         hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
629         for (x=0;x<st->nummaps;x++)
630                 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
631         if (ouranswers < 0)
632                 ouranswers = 0;
633         for (x=0;x<ouranswers;x++) {
634                 if (dr[x].weight < max)
635                         max = dr[x].weight;
636         }
637                 
638         if (max) {
639                 /* If we do not have a canonical result, keep looking */
640                 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);
641                 if (res > 0) {
642                         /* Append answer in result */
643                         ouranswers += res;
644                 } else {
645                         if ((res < -1) && (!ouranswers))
646                                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
647                 }
648         }
649         AST_LIST_LOCK(&peers);
650         /* Truncate if "don't ask" isn't present */
651         if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
652                 hmd.exten[0] = '\0';
653         if (ast_test_flag(st->trans, FLAG_DEAD)) {
654                 if (option_debug)
655                         ast_log(LOG_DEBUG, "Our transaction went away!\n");
656                 st->trans->thread = 0;
657                 destroy_trans(st->trans, 0);
658         } else {
659                 for (x=0;x<ouranswers;x++) {
660                         /* Add answers */
661                         if (dr[x].expiration && (expiration > dr[x].expiration))
662                                 expiration = dr[x].expiration;
663                         dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
664                 }
665                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
666                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
667                 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
668                 st->trans->thread = 0;
669         }
670         AST_LIST_UNLOCK(&peers);
671         free(st);
672         return NULL;    
673 }
674
675 static void *dundi_precache_thread(void *data)
676 {
677         struct dundi_query_state *st = data;
678         struct dundi_ie_data ied;
679         struct dundi_hint_metadata hmd;
680         char eid_str[20];
681
682         if (option_debug)
683                 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 
684                         st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
685         memset(&ied, 0, sizeof(ied));
686
687         /* Now produce precache */
688         dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
689
690         AST_LIST_LOCK(&peers);
691         /* Truncate if "don't ask" isn't present */
692         if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
693                 hmd.exten[0] = '\0';
694         if (ast_test_flag(st->trans, FLAG_DEAD)) {
695                 if (option_debug)
696                         ast_log(LOG_DEBUG, "Our transaction went away!\n");
697                 st->trans->thread = 0;
698                 destroy_trans(st->trans, 0);
699         } else {
700                 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
701                 st->trans->thread = 0;
702         }
703         AST_LIST_UNLOCK(&peers);
704         free(st);
705         return NULL;    
706 }
707
708 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[]);
709
710 static void *dundi_query_thread(void *data)
711 {
712         struct dundi_query_state *st = data;
713         struct dundi_entity_info dei;
714         struct dundi_ie_data ied;
715         struct dundi_hint_metadata hmd;
716         char eid_str[20];
717         int res;
718
719         if (option_debug)
720                 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
721                         st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
722         memset(&ied, 0, sizeof(ied));
723         memset(&dei, 0, sizeof(dei));
724         memset(&hmd, 0, sizeof(hmd));
725         if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
726                 /* Ooh, it's us! */
727                 if (option_debug)
728                         ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
729                 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
730                 ast_copy_string(dei.org, org, sizeof(dei.org));
731                 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
732                 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
733                 ast_copy_string(dei.country, country, sizeof(dei.country));
734                 ast_copy_string(dei.email, email, sizeof(dei.email));
735                 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
736                 res = 1;
737         } else {
738                 /* If we do not have a canonical result, keep looking */
739                 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
740         }
741         AST_LIST_LOCK(&peers);
742         if (ast_test_flag(st->trans, FLAG_DEAD)) {
743                 if (option_debug)
744                         ast_log(LOG_DEBUG, "Our transaction went away!\n");
745                 st->trans->thread = 0;
746                 destroy_trans(st->trans, 0);
747         } else {
748                 if (res) {
749                         dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
750                         dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
751                         dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
752                         dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
753                         dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
754                         dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
755                         dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
756                         if (!ast_strlen_zero(dei.ipaddr))
757                                 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
758                 }
759                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
760                 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
761                 st->trans->thread = 0;
762         }
763         AST_LIST_UNLOCK(&peers);
764         free(st);
765         return NULL;    
766 }
767
768 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
769 {
770         struct dundi_query_state *st;
771         int totallen;
772         int x;
773         int skipfirst=0;
774         struct dundi_ie_data ied;
775         char eid_str[20];
776         char *s;
777         pthread_t lookupthread;
778
779         if (ies->eidcount > 1) {
780                 /* Since it is a requirement that the first EID is the authenticating host
781                    and the last EID is the root, it is permissible that the first and last EID
782                    could be the same.  In that case, we should go ahead copy only the "root" section
783                    since we will not need it for authentication. */
784                 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
785                         skipfirst = 1;
786         }
787         totallen = sizeof(struct dundi_query_state);
788         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
789         st = ast_calloc(1, totallen);
790         if (st) {
791                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
792                 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
793                 st->trans = trans;
794                 st->ttl = ies->ttl - 1;
795                 if (st->ttl < 0)
796                         st->ttl = 0;
797                 s = st->fluffy;
798                 for (x=skipfirst;ies->eids[x];x++) {
799                         st->eids[x-skipfirst] = (dundi_eid *)s;
800                         *st->eids[x-skipfirst] = *ies->eids[x];
801                         s += sizeof(dundi_eid);
802                 }
803                 if (option_debug)
804                         ast_log(LOG_DEBUG, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
805
806                 trans->thread = 1;
807                 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
808                         trans->thread = 0;
809                         ast_log(LOG_WARNING, "Unable to create thread!\n");
810                         free(st);
811                         memset(&ied, 0, sizeof(ied));
812                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
813                         dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
814                         return -1;
815                 }
816         } else {
817                 ast_log(LOG_WARNING, "Out of memory!\n");
818                 memset(&ied, 0, sizeof(ied));
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                         ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
2939                                 ast_inet_ntoa(pack->parent->addr.sin_addr), 
2940                                 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2941                 destroy_trans(pack->parent, 1);
2942                 res = 0;
2943         } else {
2944                 /* Decrement retransmission, try again */
2945                 pack->retrans--;
2946                 dundi_xmit(pack);
2947                 res = 1;
2948         }
2949         AST_LIST_UNLOCK(&peers);
2950         return res;
2951 }
2952
2953 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2954 {
2955         struct dundi_packet *pack;
2956         int res;
2957         int len;
2958         char eid_str[20];
2959         len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2960         /* Reserve enough space for encryption */
2961         if (ast_test_flag(trans, FLAG_ENCRYPT))
2962                 len += 384;
2963         pack = ast_calloc(1, len);
2964         if (pack) {
2965                 pack->h = (struct dundi_hdr *)(pack->data);
2966                 if (cmdresp != DUNDI_COMMAND_ACK) {
2967                         pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2968                         pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2969                         AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
2970                 }
2971                 pack->parent = trans;
2972                 pack->h->strans = htons(trans->strans);
2973                 pack->h->dtrans = htons(trans->dtrans);
2974                 pack->h->iseqno = trans->iseqno;
2975                 pack->h->oseqno = trans->oseqno;
2976                 pack->h->cmdresp = cmdresp;
2977                 pack->datalen = sizeof(struct dundi_hdr);
2978                 if (ied) {
2979                         memcpy(pack->h->ies, ied->buf, ied->pos);
2980                         pack->datalen += ied->pos;
2981                 } 
2982                 if (final) {
2983                         pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
2984                         ast_set_flag(trans, FLAG_FINAL);
2985                 }
2986                 pack->h->cmdflags = flags;
2987                 if (cmdresp != DUNDI_COMMAND_ACK) {
2988                         trans->oseqno++;
2989                         trans->oseqno = trans->oseqno % 256;
2990                 }
2991                 trans->aseqno = trans->iseqno;
2992                 /* If we have their public key, encrypt */
2993                 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
2994                         switch(cmdresp) {
2995                         case DUNDI_COMMAND_REGREQ:
2996                         case DUNDI_COMMAND_REGRESPONSE:
2997                         case DUNDI_COMMAND_DPDISCOVER:
2998                         case DUNDI_COMMAND_DPRESPONSE:
2999                         case DUNDI_COMMAND_EIDQUERY:
3000                         case DUNDI_COMMAND_EIDRESPONSE:
3001                         case DUNDI_COMMAND_PRECACHERQ:
3002                         case DUNDI_COMMAND_PRECACHERP:
3003                                 if (dundidebug)
3004                                         dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3005                                 res = dundi_encrypt(trans, pack);
3006                                 break;
3007                         default:
3008                                 res = 0;
3009                         }
3010                 } else 
3011                         res = 0;
3012                 if (!res) 
3013                         res = dundi_xmit(pack);
3014                 if (res)
3015                         ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3016                                 
3017                 if (cmdresp == DUNDI_COMMAND_ACK)
3018                         free(pack);
3019                 return res;
3020         }
3021         return -1;
3022 }
3023
3024 static int do_autokill(void *data)
3025 {
3026         struct dundi_transaction *trans = data;
3027         char eid_str[20];
3028         ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 
3029                 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3030         trans->autokillid = -1;
3031         destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3032         return 0;
3033 }
3034
3035 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3036 {
3037         struct dundi_peer *p;
3038         if (!dundi_eid_cmp(eid, us)) {
3039                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3040                 return;
3041         }
3042         AST_LIST_LOCK(&peers);
3043         AST_LIST_TRAVERSE(&peers, p, list) {
3044                 if (!dundi_eid_cmp(&p->eid, eid)) {
3045                         if (has_permission(&p->include, context))
3046                                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3047                         else
3048                                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3049                         break;
3050                 }
3051         }
3052         if (!p)
3053                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3054         AST_LIST_UNLOCK(&peers);
3055 }
3056
3057 static int dundi_discover(struct dundi_transaction *trans)
3058 {
3059         struct dundi_ie_data ied;
3060         int x;
3061         if (!trans->parent) {
3062                 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3063                 return -1;
3064         }
3065         memset(&ied, 0, sizeof(ied));
3066         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3067         if (!dundi_eid_zero(&trans->us_eid))
3068                 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3069         for (x=0;x<trans->eidcount;x++)
3070                 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3071         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3072         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3073         dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3074         if (trans->parent->cbypass)
3075                 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3076         if (trans->autokilltimeout)
3077                 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3078         return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3079 }
3080
3081 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3082 {
3083         struct dundi_ie_data ied;
3084         int x, res;
3085         int max = 999999;
3086         int expiration = dundi_cache_time;
3087         int ouranswers=0;
3088         dundi_eid *avoid[1] = { NULL, };
3089         int direct[1] = { 0, };
3090         struct dundi_result dr[MAX_RESULTS];
3091         struct dundi_hint_metadata hmd;
3092         if (!trans->parent) {
3093                 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3094                 return -1;
3095         }
3096         memset(&hmd, 0, sizeof(hmd));
3097         memset(&dr, 0, sizeof(dr));
3098         /* Look up the answers we're going to include */
3099         for (x=0;x<mapcount;x++)
3100                 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3101         if (ouranswers < 0)
3102                 ouranswers = 0;
3103         for (x=0;x<ouranswers;x++) {
3104                 if (dr[x].weight < max)
3105                         max = dr[x].weight;
3106         }
3107         if (max) {
3108                 /* If we do not have a canonical result, keep looking */
3109                 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
3110                 if (res > 0) {
3111                         /* Append answer in result */
3112                         ouranswers += res;
3113                 }
3114         }
3115         
3116         if (ouranswers > 0) {
3117                 *foundanswers += ouranswers;
3118                 memset(&ied, 0, sizeof(ied));
3119                 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3120                 if (!dundi_eid_zero(&trans->us_eid))
3121                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3122                 for (x=0;x<trans->eidcount;x++)
3123                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3124                 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3125                 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3126                 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3127                 for (x=0;x<ouranswers;x++) {
3128                         /* Add answers */
3129                         if (dr[x].expiration && (expiration > dr[x].expiration))
3130                                 expiration = dr[x].expiration;
3131                         dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3132                 }
3133                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3134                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3135                 if (trans->autokilltimeout)
3136                         trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3137                 if (expiration < *minexp)
3138                         *minexp = expiration;
3139                 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3140         } else {
3141                 /* Oops, nothing to send... */
3142                 destroy_trans(trans, 0);
3143                 return 0;
3144         }
3145 }
3146
3147 static int dundi_query(struct dundi_transaction *trans)
3148 {
3149         struct dundi_ie_data ied;
3150         int x;
3151         if (!trans->parent) {
3152                 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3153                 return -1;
3154         }
3155         memset(&ied, 0, sizeof(ied));
3156         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3157         if (!dundi_eid_zero(&trans->us_eid))
3158                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3159         for (x=0;x<trans->eidcount;x++)
3160                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3161         dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3162         dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3163         dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3164         if (trans->autokilltimeout)
3165                 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3166         return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3167 }
3168
3169 static int discover_transactions(struct dundi_request *dr)
3170 {
3171         struct dundi_transaction *trans;
3172         AST_LIST_LOCK(&peers);
3173         AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3174                 dundi_discover(trans);
3175         }
3176         AST_LIST_UNLOCK(&peers);
3177         return 0;
3178 }
3179
3180 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3181 {
3182         struct dundi_transaction *trans;
3183
3184         /* Mark all as "in thread" so they don't disappear */
3185         AST_LIST_LOCK(&peers);
3186         AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3187                 if (trans->thread)
3188                         ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3189                 trans->thread = 1;
3190         }
3191         AST_LIST_UNLOCK(&peers);
3192
3193         AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3194                 if (!ast_test_flag(trans, FLAG_DEAD))
3195                         precache_trans(trans, maps, mapcount, expiration, foundanswers);
3196         }
3197
3198         /* Cleanup any that got destroyed in the mean time */
3199         AST_LIST_LOCK(&peers);
3200         AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
3201                 trans->thread = 0;
3202                 if (ast_test_flag(trans, FLAG_DEAD)) {
3203                         if (option_debug)
3204                                 ast_log(LOG_DEBUG, "Our transaction went away!\n");
3205                         /* This is going to remove the transaction from the dundi_request's list, as well
3206                          * as the global transactions list */
3207                         destroy_trans(trans, 0);
3208                 }
3209         }
3210         AST_LIST_TRAVERSE_SAFE_END
3211         AST_LIST_UNLOCK(&peers);
3212
3213         return 0;
3214 }
3215
3216 static int query_transactions(struct dundi_request *dr)
3217 {
3218         struct dundi_transaction *trans;
3219
3220         AST_LIST_LOCK(&peers);
3221         AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3222                 dundi_query(trans);
3223         }
3224         AST_LIST_UNLOCK(&peers);
3225
3226         return 0;
3227 }
3228
3229 static int optimize_transactions(struct dundi_request *dr, int order)
3230 {
3231         /* Minimize the message propagation through DUNDi by
3232            alerting the network to hops which should be not be considered */
3233         struct dundi_transaction *trans;
3234         struct dundi_peer *peer;
3235         dundi_eid tmp;
3236         int x;
3237         int needpush;
3238
3239         AST_LIST_LOCK(&peers);
3240         AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3241                 /* Pop off the true root */
3242                 if (trans->eidcount) {
3243                         tmp = trans->eids[--trans->eidcount];
3244                         needpush = 1;
3245                 } else {
3246                         tmp = trans->us_eid;
3247                         needpush = 0;
3248                 }
3249
3250                 AST_LIST_TRAVERSE(&peers, peer, list) {
3251                         if (has_permission(&peer->include, dr->dcontext) && 
3252                             dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3253                                 (peer->order <= order)) {
3254                                 /* For each other transaction, make sure we don't
3255                                    ask this EID about the others if they're not
3256                                    already in the list */
3257                                 if (!dundi_eid_cmp(&tmp, &peer->eid)) 
3258                                         x = -1;
3259                                 else {
3260                                         for (x=0;x<trans->eidcount;x++) {
3261                                                 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3262                                                         break;
3263                                         }
3264                                 }
3265                                 if (x == trans->eidcount) {
3266                                         /* Nope not in the list, if needed, add us at the end since we're the source */
3267                                         if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3268                                                 trans->eids[trans->eidcount++] = peer->eid;
3269                                                 /* Need to insert the real root (or us) at the bottom now as
3270                                                    a requirement now.  */
3271                                                 needpush = 1;
3272                                         }
3273                                 }
3274                         }
3275                 }
3276                 /* If necessary, push the true root back on the end */
3277                 if (needpush)
3278                         trans->eids[trans->eidcount++] = tmp;
3279         }
3280         AST_LIST_UNLOCK(&peers);
3281
3282         return 0;
3283 }
3284
3285 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3286 {
3287         struct dundi_transaction *trans;
3288         int x;
3289         char eid_str[20];
3290         char eid_str2[20];
3291
3292         /* Ignore if not registered */
3293         if (!p->addr.sin_addr.s_addr)
3294                 return 0;
3295         if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3296                 return 0;
3297
3298         if (option_debug) {
3299                 if (ast_strlen_zero(dr->number))
3300                         ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
3301                 else
3302                         ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
3303         }
3304
3305         trans = create_transaction(p);
3306         if (!trans)
3307                 return -1;
3308         trans->parent = dr;
3309         trans->ttl = ttl;
3310         for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3311                 trans->eids[x] = *avoid[x];
3312         trans->eidcount = x;
3313         AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
3314         
3315         return 0;
3316 }
3317
3318 static void cancel_request(struct dundi_request *dr)
3319 {
3320         struct dundi_transaction *trans;
3321
3322         AST_LIST_LOCK(&peers);
3323         while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
3324                 /* Orphan transaction from request */
3325                 trans->parent = NULL;
3326                 /* Send final cancel */
3327                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3328         }
3329         AST_LIST_UNLOCK(&peers);
3330 }
3331
3332 static void abort_request(struct dundi_request *dr)
3333 {
3334         struct dundi_transaction *trans;
3335
3336         AST_LIST_LOCK(&peers);
3337         while ((trans = AST_LIST_FIRST(&dr->trans))) {
3338                 /* This will remove the transaction from the list */
3339                 destroy_trans(trans, 0);
3340         }
3341         AST_LIST_UNLOCK(&peers);
3342 }
3343
3344 static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
3345 {
3346         struct dundi_peer *p;
3347         int x;
3348         int res;
3349         int pass;
3350         int allowconnect;
3351         char eid_str[20];
3352         AST_LIST_LOCK(&peers);
3353         AST_LIST_TRAVERSE(&peers, p, list) {
3354                 if (modeselect == 1) {
3355                         /* Send the precache to push upstreams only! */
3356                         pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
3357                         allowconnect = 1;
3358                 } else {
3359                         /* Normal lookup / EID query */
3360                         pass = has_permission(&p->include, dr->dcontext);
3361                         allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
3362                 }
3363                 if (skip) {
3364                         if (!dundi_eid_cmp(skip, &p->eid))
3365                                 pass = 0;
3366                 }
3367                 if (pass) {
3368                         if (p->order <= order) {
3369                                 /* Check order first, then check cache, regardless of
3370                                    omissions, this gets us more likely to not have an
3371                                    affected answer. */
3372                                 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr-&g