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