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