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