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