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