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