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