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