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