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