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