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