The Eurostar Commit! (it's amazing how much work you can get done on a 150 minute...
[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 iabuf[INET_ADDRSTRLEN];
1596                                 char data[256];
1597                                 int needqual = 0;
1598                                 if (peer->registerexpire > -1)
1599                                         ast_sched_del(sched, peer->registerexpire);
1600                                 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1601                                 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1602                                 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1603                                 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1604                                 if (inaddrcmp(&peer->addr, &trans->addr)) {
1605                                         if (option_verbose > 2)
1606                                                 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), iabuf, ntohs(trans->addr.sin_port));
1607                                         needqual = 1;
1608                                 }
1609                                         
1610                                 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1611                                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1612                                 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1613                                 if (needqual)
1614                                         qualify_peer(peer, 1);
1615                         }
1616                 }
1617                 break;
1618         case DUNDI_COMMAND_DPRESPONSE:
1619                 /* A dialplan response, lets see what we got... */
1620                 if (ies.cause < 1) {
1621                         /* Success of some sort */
1622                         ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1623                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1624                                 authpass = encrypted;
1625                         } else 
1626                                 authpass = 1;
1627                         if (authpass) {
1628                                 /* Pass back up answers */
1629                                 if (trans->parent && trans->parent->dr) {
1630                                         y = trans->parent->respcount;
1631                                         for (x=0;x<ies.anscount;x++) {
1632                                                 if (trans->parent->respcount < trans->parent->maxcount) {
1633                                                         /* Make sure it's not already there */
1634                                                         for (z=0;z<trans->parent->respcount;z++) {
1635                                                                 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1636                                                                     !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data)) 
1637                                                                                 break;
1638                                                         }
1639                                                         if (z == trans->parent->respcount) {
1640                                                                 /* Copy into parent responses */
1641                                                                 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1642                                                                 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1643                                                                 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1644                                                                 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1645                                                                 if (ies.expiration > 0)
1646                                                                         trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1647                                                                 else
1648                                                                         trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1649                                                                 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
1650                                                                         sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1651                                                                         &ies.answers[x]->eid);
1652                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1653                                                                         sizeof(trans->parent->dr[trans->parent->respcount].dest));
1654                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1655                                                                         sizeof(trans->parent->dr[trans->parent->respcount].tech));
1656                                                                 trans->parent->respcount++;
1657                                                                 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1658                                                         } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1659                                                                 /* Update weight if appropriate */
1660                                                                 trans->parent->dr[z].weight = ies.answers[x]->weight;
1661                                                         }
1662                                                 } else
1663                                                         ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1664                                                                 trans->parent->number, trans->parent->dcontext);
1665                                         }
1666                                         /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
1667                                            the cache know if this request was unaffected by our entity list. */
1668                                         cache_save(&trans->them_eid, trans->parent, y, 
1669                                                         ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1670                                         if (ies.hint) {
1671                                                 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1672                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1673                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1674                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) { 
1675                                                         if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1676                                                                 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data, 
1677                                                                         sizeof(trans->parent->hmd->exten));
1678                                                         }
1679                                                 } else {
1680                                                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1681                                                 }
1682                                         }
1683                                         if (ies.expiration > 0) {
1684                                                 if (trans->parent->expiration > ies.expiration) {
1685                                                         trans->parent->expiration = ies.expiration;
1686                                                 }
1687                                         }
1688                                 }
1689                                 /* Close connection if not final */
1690                                 if (!final) 
1691                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1692                         }
1693                         
1694                 } else {
1695                         /* Auth failure, check for data */
1696                         if (!final) {
1697                                 /* Cancel if they didn't already */
1698                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1699                         }
1700                 }
1701                 break;
1702         case DUNDI_COMMAND_EIDRESPONSE:
1703                 /* A dialplan response, lets see what we got... */
1704                 if (ies.cause < 1) {
1705                         /* Success of some sort */
1706                         ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1707                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1708                                 authpass = encrypted;
1709                         } else 
1710                                 authpass = 1;
1711                         if (authpass) {
1712                                 /* Pass back up answers */
1713                                 if (trans->parent && trans->parent->dei && ies.q_org) {
1714                                         if (!trans->parent->respcount) {
1715                                                 trans->parent->respcount++;
1716                                                 if (ies.q_dept)
1717                                                         ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1718                                                 if (ies.q_org)
1719                                                         ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1720                                                 if (ies.q_locality)
1721                                                         ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1722                                                 if (ies.q_stateprov)
1723                                                         ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1724                                                 if (ies.q_country)
1725                                                         ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1726                                                 if (ies.q_email)
1727                                                         ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1728                                                 if (ies.q_phone)
1729                                                         ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1730                                                 if (ies.q_ipaddr)
1731                                                         ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1732                                                 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1733                                                         /* If it's them, update our address */
1734                                                         ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1735                                                                 trans->addr.sin_addr);
1736                                                 }
1737                                         }
1738                                         if (ies.hint) {
1739                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1740                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1741                                         }
1742                                 }
1743                                 /* Close connection if not final */
1744                                 if (!final) 
1745                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1746                         }
1747                         
1748                 } else {
1749                         /* Auth failure, check for data */
1750                         if (!final) {
1751                                 /* Cancel if they didn't already */
1752                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1753                         }
1754                 }
1755                 break;
1756         case DUNDI_COMMAND_REGRESPONSE:
1757                 /* A dialplan response, lets see what we got... */
1758                 if (ies.cause < 1) {
1759                         int hasauth;
1760                         /* Success of some sort */
1761                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1762                                 hasauth = encrypted;
1763                         } else 
1764                                 hasauth = 1;
1765                         
1766                         if (!hasauth) {
1767                                 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1768                                 if (!final) {
1769                                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1770                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1771                                 }
1772                         } else {
1773                                 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),
1774                                                         dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1775                                 /* Close connection if not final */
1776                                 if (!final) 
1777                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1778                         }
1779                 } else {
1780                         /* Auth failure, cancel if they didn't for some reason */
1781                         if (!final) {
1782                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1783                         }
1784                 }
1785                 break;
1786         case DUNDI_COMMAND_INVALID:
1787         case DUNDI_COMMAND_NULL:
1788         case DUNDI_COMMAND_PRECACHERP:
1789                 /* Do nothing special */
1790                 if (!final) 
1791                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1792                 break;
1793         case DUNDI_COMMAND_ENCREJ:
1794                 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1795                         /* No really, it's over at this point */
1796                         if (!final) 
1797                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1798                 } else {
1799                         /* Send with full key */
1800                         ast_set_flag(trans, FLAG_SENDFULLKEY);
1801                         if (final) {
1802                                 /* Ooops, we got a final message, start by sending ACK... */
1803                                 dundi_ack(trans, hdr->cmdresp & 0x80);
1804                                 trans->aseqno = trans->iseqno;
1805                                 /* Now, we gotta create a new transaction */
1806                                 if (!reset_transaction(trans)) {
1807                                         /* Make sure handle_frame doesn't destroy us */
1808                                         hdr->cmdresp &= 0x7f;
1809                                         /* Parse the message we transmitted */
1810                                         memset(&ies, 0, sizeof(ies));
1811                                         dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1812                                         /* Reconstruct outgoing encrypted packet */
1813                                         memset(&ied, 0, sizeof(ied));
1814                                         dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1815                                         dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1816                                         dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1817                                         if (ies.encblock) 
1818                                                 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1819                                         dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
1820                                         peer->sentfullkey = 1;
1821                                 }
1822                         }
1823                 }
1824                 break;
1825         case DUNDI_COMMAND_ENCRYPT:
1826                 if (!encrypted) {
1827                         /* No nested encryption! */
1828                         if ((trans->iseqno == 1) && !trans->oseqno) {
1829                                 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || 
1830                                         ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || 
1831                                         (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1832                                         if (!final) {
1833                                                 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1834                                         }
1835                                         break;
1836                                 }
1837                                 apply_peer(trans, peer);
1838                                 /* Key passed, use new contexts for this session */
1839                                 trans->ecx = peer->them_ecx;
1840                                 trans->dcx = peer->them_dcx;
1841                         }
1842                         if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1843                                 struct dundi_hdr *dhdr;
1844                                 unsigned char decoded[MAX_PACKET_SIZE];
1845                                 int ddatalen;
1846                                 ddatalen = sizeof(decoded);
1847                                 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1848                                 if (dhdr) {
1849                                         /* Handle decrypted response */
1850                                         if (dundidebug)
1851                                                 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1852                                         handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1853                                         /* Carry back final flag */
1854                                         hdr->cmdresp |= dhdr->cmdresp & 0x80;
1855                                         break;
1856                                 } else
1857                                         ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1858                         }
1859                 }
1860                 if (!final) {
1861                         /* Turn off encryption */
1862                         ast_clear_flag(trans, FLAG_ENCRYPT);
1863                         dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1864                 }
1865                 break;
1866         default:
1867                 /* Send unknown command if we don't know it, with final flag IFF it's the
1868                    first command in the dialog and only if we haven't recieved final notification */
1869                 if (!final) {
1870                         dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1871                         dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1872                 }
1873         }
1874         return 0;
1875 }
1876
1877 static void destroy_packet(struct dundi_packet *pack, int needfree);
1878 static void destroy_packets(struct packetlist *p)
1879 {
1880         struct dundi_packet *pack;
1881         
1882         while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1883                 if (pack->retransid > -1)
1884                         ast_sched_del(sched, pack->retransid);
1885                 free(pack);
1886         }
1887 }
1888
1889
1890 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1891 {
1892         struct dundi_packet *pack;
1893
1894         /* Ack transmitted packet corresponding to iseqno */
1895         AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1896                 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1897                         destroy_packet(pack, 0);
1898                         if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1899                                 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1900                                 destroy_packets(&trans->lasttrans);
1901                         }
1902                         AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1903                         if (trans->autokillid > -1)
1904                                 ast_sched_del(sched, trans->autokillid);
1905                         trans->autokillid = -1;
1906                         return 1;
1907                 }
1908         }
1909
1910         return 0;
1911 }
1912
1913 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1914 {
1915         struct dundi_transaction *trans;
1916         trans = find_transaction(h, sin);
1917         if (!trans) {
1918                 dundi_reject(h, sin);
1919                 return 0;
1920         }
1921         /* Got a transaction, see where this header fits in */
1922         if (h->oseqno == trans->iseqno) {
1923                 /* Just what we were looking for...  Anything but ack increments iseqno */
1924                 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1925                         /* If final, we're done */
1926                         destroy_trans(trans, 0);
1927                         return 0;
1928                 }
1929                 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1930                         trans->oiseqno = trans->iseqno;
1931                         trans->iseqno++;
1932                         handle_command_response(trans, h, datalen, 0);
1933                 }
1934                 if (trans->aseqno != trans->iseqno) {
1935                         dundi_ack(trans, h->cmdresp & 0x80);
1936                         trans->aseqno = trans->iseqno;
1937                 }
1938                 /* Delete any saved last transmissions */
1939                 destroy_packets(&trans->lasttrans);
1940                 if (h->cmdresp & 0x80) {
1941                         /* Final -- destroy now */
1942                         destroy_trans(trans, 0);
1943                 }
1944         } else if (h->oseqno == trans->oiseqno) {
1945                 /* Last incoming sequence number -- send ACK without processing */
1946                 dundi_ack(trans, 0);
1947         } else {
1948                 /* Out of window -- simply drop */
1949                 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1950         }
1951         return 0;
1952 }
1953
1954 static int socket_read(int *id, int fd, short events, void *cbdata)
1955 {
1956         struct sockaddr_in sin;
1957         int res;
1958         struct dundi_hdr *h;
1959         char buf[MAX_PACKET_SIZE];
1960         socklen_t len;
1961         len = sizeof(sin);
1962         res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1963         if (res < 0) {
1964                 if (errno != ECONNREFUSED)
1965                         ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1966                 return 1;
1967         }
1968         if (res < sizeof(struct dundi_hdr)) {
1969                 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1970                 return 1;
1971         }
1972         buf[res] = '\0';
1973         h = (struct dundi_hdr *)buf;
1974         if (dundidebug)
1975                 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1976         AST_LIST_LOCK(&peers);
1977         handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
1978         AST_LIST_UNLOCK(&peers);
1979         return 1;
1980 }
1981
1982 static void build_secret(char *secret, int seclen)
1983 {
1984         unsigned char tmp[16];
1985         char *s;
1986         build_iv(tmp);
1987         secret[0] = '\0';
1988         ast_base64encode(secret, tmp, sizeof(tmp), seclen);
1989         /* Eliminate potential bad characters */
1990         while((s = strchr(secret, ';'))) *s = '+';
1991         while((s = strchr(secret, '/'))) *s = '+';
1992         while((s = strchr(secret, ':'))) *s = '+';
1993         while((s = strchr(secret, '@'))) *s = '+';
1994 }
1995
1996
1997 static void save_secret(const char *newkey, const char *oldkey)
1998 {
1999         char tmp[256];
2000         if (oldkey)
2001                 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2002         else
2003                 snprintf(tmp, sizeof(tmp), "%s", newkey);
2004         rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2005         ast_db_put(secretpath, "secret", tmp);
2006         snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2007         ast_db_put(secretpath, "secretexpiry", tmp);
2008 }
2009
2010 static void load_password(void)
2011 {
2012         char *current=NULL;
2013         char *last=NULL;
2014         char tmp[256];
2015         time_t expired;
2016         
2017         ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2018         if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2019                 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2020                 current = strchr(tmp, ';');
2021                 if (!current)
2022                         current = tmp;
2023                 else {
2024                         *current = '\0';
2025                         current++;
2026                 };
2027                 if ((time(NULL) - expired) < 0) {
2028                         if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2029                                 expired = time(NULL) + DUNDI_SECRET_TIME;
2030                 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2031                         last = current;
2032                         current = NULL;
2033                 } else {
2034                         last = NULL;
2035                         current = NULL;
2036                 }
2037         }
2038         if (current) {
2039                 /* Current key is still valid, just setup rotatation properly */
2040                 ast_copy_string(cursecret, current, sizeof(cursecret));
2041                 rotatetime = expired;
2042         } else {
2043                 /* Current key is out of date, rotate or eliminate all together */
2044                 build_secret(cursecret, sizeof(cursecret));
2045                 save_secret(cursecret, last);
2046         }
2047 }
2048
2049 static void check_password(void)
2050 {
2051         char oldsecret[80];
2052         time_t now;
2053         
2054         time(&now);     
2055 #if 0
2056         printf("%ld/%ld\n", now, rotatetime);
2057 #endif
2058         if ((now - rotatetime) >= 0) {
2059                 /* Time to rotate keys */
2060                 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2061                 build_secret(cursecret, sizeof(cursecret));
2062                 save_secret(cursecret, oldsecret);
2063         }
2064 }
2065
2066 static void *network_thread(void *ignore)
2067 {
2068         /* Our job is simple: Send queued messages, retrying if necessary.  Read frames 
2069            from the network, and queue them for delivery to the channels */
2070         int res;
2071         /* Establish I/O callback for socket read */
2072         ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2073         for(;;) {
2074                 res = ast_sched_wait(sched);
2075                 if ((res > 1000) || (res < 0))
2076                         res = 1000;
2077                 res = ast_io_wait(io, res);
2078                 if (res >= 0) {
2079                         AST_LIST_LOCK(&peers);
2080                         ast_sched_runq(sched);
2081                         AST_LIST_UNLOCK(&peers);
2082                 }
2083                 check_password();
2084         }
2085         return NULL;
2086 }
2087
2088 static void *process_precache(void *ign)
2089 {
2090         struct dundi_precache_queue *qe;
2091         time_t now;
2092         char context[256];
2093         char number[256];
2094         int run;
2095
2096         for (;;) {
2097                 time(&now);
2098                 run = 0;
2099                 AST_LIST_LOCK(&pcq);
2100                 if ((qe = AST_LIST_FIRST(&pcq))) {
2101                         if (!qe->expiration) {
2102                                 /* Gone...  Remove... */
2103                                 AST_LIST_REMOVE_HEAD(&pcq, list);
2104                                 free(qe);
2105                         } else if (qe->expiration < now) {
2106                                 /* Process this entry */
2107                                 qe->expiration = 0;
2108                                 ast_copy_string(context, qe->context, sizeof(context));
2109                                 ast_copy_string(number, qe->number, sizeof(number));
2110                                 run = 1;
2111                         }
2112                 }
2113                 AST_LIST_UNLOCK(&pcq);
2114                 if (run) {
2115                         dundi_precache(context, number);
2116                 } else
2117                         sleep(1);
2118         }
2119
2120         return NULL;
2121 }
2122
2123 static int start_network_thread(void)
2124 {
2125         ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
2126         ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
2127         return 0;
2128 }
2129
2130 static int dundi_do_debug(int fd, int argc, char *argv[])
2131 {
2132         if (argc != 2)
2133                 return RESULT_SHOWUSAGE;
2134         dundidebug = 1;
2135         ast_cli(fd, "DUNDi Debugging Enabled\n");
2136         return RESULT_SUCCESS;
2137 }
2138
2139 static int dundi_do_store_history(int fd, int argc, char *argv[])
2140 {
2141         if (argc != 3)
2142                 return RESULT_SHOWUSAGE;
2143         global_storehistory = 1;
2144         ast_cli(fd, "DUNDi History Storage Enabled\n");
2145         return RESULT_SUCCESS;
2146 }
2147
2148 static int dundi_flush(int fd, int argc, char *argv[])
2149 {
2150         int stats = 0;
2151         if ((argc < 2) || (argc > 3))
2152                 return RESULT_SHOWUSAGE;
2153         if (argc > 2) {
2154                 if (!strcasecmp(argv[2], "stats"))
2155                         stats = 1;
2156                 else
2157                         return RESULT_SHOWUSAGE;
2158         }
2159         if (stats) {
2160                 /* Flush statistics */
2161                 struct dundi_peer *p;
2162                 int x;
2163                 AST_LIST_LOCK(&peers);
2164                 AST_LIST_TRAVERSE(&peers, p, list) {
2165                         for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2166                                 if (p->lookups[x])
2167                                         free(p->lookups[x]);
2168                                 p->lookups[x] = NULL;
2169                                 p->lookuptimes[x] = 0;
2170                         }
2171                         p->avgms = 0;
2172                 }
2173                 AST_LIST_UNLOCK(&peers);
2174         } else {
2175                 ast_db_deltree("dundi/cache", NULL);
2176                 ast_cli(fd, "DUNDi Cache Flushed\n");
2177         }
2178         return RESULT_SUCCESS;
2179 }
2180
2181 static int dundi_no_debug(int fd, int argc, char *argv[])
2182 {
2183         if (argc != 3)
2184                 return RESULT_SHOWUSAGE;
2185         dundidebug = 0;
2186         ast_cli(fd, "DUNDi Debugging Disabled\n");
2187         return RESULT_SUCCESS;
2188 }
2189
2190 static int dundi_no_store_history(int fd, int argc, char *argv[])
2191 {
2192         if (argc != 4)
2193                 return RESULT_SHOWUSAGE;
2194         global_storehistory = 0;
2195         ast_cli(fd, "DUNDi History Storage Disabled\n");
2196         return RESULT_SUCCESS;
2197 }
2198
2199 static char *model2str(int model)
2200 {
2201         switch(model) {
2202         case DUNDI_MODEL_INBOUND:
2203                 return "Inbound";
2204         case DUNDI_MODEL_OUTBOUND:
2205                 return "Outbound";
2206         case DUNDI_MODEL_SYMMETRIC:
2207                 return "Symmetric";
2208         default:
2209                 return "Unknown";
2210         }
2211 }
2212
2213 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2214 {
2215         int which=0, len;
2216         char *ret = NULL;
2217         struct dundi_peer *p;
2218         char eid_str[20];
2219
2220         if (pos != rpos)
2221                 return NULL;
2222         AST_LIST_LOCK(&peers);
2223         len = strlen(word);
2224         AST_LIST_TRAVERSE(&peers, p, list) {
2225                 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2226                 if (!strncasecmp(word, s, len) && ++which > state)
2227                         ret = ast_strdup(s);
2228         }
2229         AST_LIST_UNLOCK(&peers);
2230         return ret;
2231 }
2232
2233 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2234 {
2235         return complete_peer_helper(line, word, pos, state, 3);
2236 }
2237
2238 static int rescomp(const void *a, const void *b)
2239 {
2240         const struct dundi_result *resa, *resb;
2241         resa = a;
2242         resb = b;
2243         if (resa->weight < resb->weight)
2244                 return -1;
2245         if (resa->weight > resb->weight)
2246                 return 1;
2247         return 0;
2248 }
2249
2250 static void sort_results(struct dundi_result *results, int count)
2251 {
2252         qsort(results, count, sizeof(results[0]), rescomp);
2253 }
2254
2255 static int dundi_do_lookup(int fd, int argc, char *argv[])
2256 {
2257         int res;
2258         char tmp[256];
2259         char fs[80] = "";
2260         char *context;
2261         int x;
2262         int bypass = 0;
2263         struct dundi_result dr[MAX_RESULTS];
2264         struct timeval start;
2265         if ((argc < 3) || (argc > 4))
2266                 return RESULT_SHOWUSAGE;
2267         if (argc > 3) {
2268                 if (!strcasecmp(argv[3], "bypass"))
2269                         bypass=1;
2270                 else
2271                         return RESULT_SHOWUSAGE;
2272         }
2273         ast_copy_string(tmp, argv[2], sizeof(tmp));
2274         context = strchr(tmp, '@');
2275         if (context) {
2276                 *context = '\0';
2277                 context++;
2278         }
2279         start = ast_tvnow();
2280         res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2281         
2282         if (res < 0) 
2283                 ast_cli(fd, "DUNDi lookup returned error.\n");
2284         else if (!res) 
2285                 ast_cli(fd, "DUNDi lookup returned no results.\n");
2286         else
2287                 sort_results(dr, res);
2288         for (x=0;x<res;x++) {
2289                 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));
2290                 ast_cli(fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2291         }
2292         ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2293         return RESULT_SUCCESS;
2294 }
2295
2296 static int dundi_do_precache(int fd, int argc, char *argv[])
2297 {
2298         int res;
2299         char tmp[256];
2300         char *context;
2301         struct timeval start;
2302         if ((argc < 3) || (argc > 3))
2303                 return RESULT_SHOWUSAGE;
2304         ast_copy_string(tmp, argv[2], sizeof(tmp));
2305         context = strchr(tmp, '@');
2306         if (context) {
2307                 *context = '\0';
2308                 context++;
2309         }
2310         start = ast_tvnow();
2311         res = dundi_precache(context, tmp);
2312         
2313         if (res < 0) 
2314                 ast_cli(fd, "DUNDi precache returned error.\n");
2315         else if (!res) 
2316                 ast_cli(fd, "DUNDi precache returned no error.\n");
2317         ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2318         return RESULT_SUCCESS;
2319 }
2320
2321 static int dundi_do_query(int fd, int argc, char *argv[])
2322 {
2323         int res;
2324         char tmp[256];
2325         char *context;
2326         dundi_eid eid;
2327         struct dundi_entity_info dei;
2328         if ((argc < 3) || (argc > 3))
2329                 return RESULT_SHOWUSAGE;
2330         if (dundi_str_to_eid(&eid, argv[2])) {
2331                 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2332                 return RESULT_SHOWUSAGE;
2333         }
2334         ast_copy_string(tmp, argv[2], sizeof(tmp));
2335         context = strchr(tmp, '@');
2336         if (context) {
2337                 *context = '\0';
2338                 context++;
2339         }
2340         res = dundi_query_eid(&dei, context, eid);
2341         if (res < 0) 
2342                 ast_cli(fd, "DUNDi Query EID returned error.\n");
2343         else if (!res) 
2344                 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2345         else {
2346                 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2347                 ast_cli(fd, "Department:      %s\n", dei.orgunit);
2348                 ast_cli(fd, "Organization:    %s\n", dei.org);
2349                 ast_cli(fd, "City/Locality:   %s\n", dei.locality);
2350                 ast_cli(fd, "State/Province:  %s\n", dei.stateprov);
2351                 ast_cli(fd, "Country:         %s\n", dei.country);
2352                 ast_cli(fd, "E-mail:          %s\n", dei.email);
2353                 ast_cli(fd, "Phone:           %s\n", dei.phone);
2354                 ast_cli(fd, "IP Address:      %s\n", dei.ipaddr);
2355         }
2356         return RESULT_SUCCESS;
2357 }
2358
2359 static int dundi_show_peer(int fd, int argc, char *argv[])
2360 {
2361         struct dundi_peer *peer;
2362         struct permission *p;
2363         char *order;
2364         char iabuf[INET_ADDRSTRLEN];
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(iabuf, sizeof(iabuf), 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         char iabuf[INET_ADDRSTRLEN];
2430         int registeredonly=0;
2431         char avgms[20];
2432         char eid_str[20];
2433         int online_peers = 0;
2434         int offline_peers = 0;
2435         int unmonitored_peers = 0;
2436         int total_peers = 0;
2437
2438         if ((argc != 3) && (argc != 4) && (argc != 5))
2439                 return RESULT_SHOWUSAGE;
2440         if ((argc == 4)) {
2441                 if (!strcasecmp(argv[3], "registered")) {
2442                         registeredonly = 1;
2443                 } else
2444                         return RESULT_SHOWUSAGE;
2445         }
2446         AST_LIST_LOCK(&peers);
2447         ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2448         AST_LIST_TRAVERSE(&peers, peer, list) {
2449                 char status[20];
2450                 int print_line = -1;
2451                 char srch[2000];
2452                 total_peers++;
2453                 if (registeredonly && !peer->addr.sin_addr.s_addr)
2454                         continue;
2455                 if (peer->maxms) {
2456                         if (peer->lastms < 0) {
2457                                 strcpy(status, "UNREACHABLE");
2458                                 offline_peers++;
2459                         }
2460                         else if (peer->lastms > peer->maxms) {
2461                                 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2462                                 offline_peers++;
2463                         }
2464                         else if (peer->lastms) {
2465                                 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2466                                 online_peers++;
2467                         }
2468                         else {
2469                                 strcpy(status, "UNKNOWN");
2470                                 offline_peers++;
2471                         }
2472                 } else {
2473                         strcpy(status, "Unmonitored");
2474                         unmonitored_peers++;
2475                 }
2476                 if (peer->avgms) 
2477                         snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2478                 else
2479                         strcpy(avgms, "Unavail");
2480                 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2481                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2482                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2483
2484                 if (argc == 5) {
2485                   if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2486                         print_line = -1;
2487                    } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2488                         print_line = 1;
2489                    } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2490                         print_line = -1;
2491                    } else {
2492                         print_line = 0;
2493                   }
2494                 }
2495                 
2496         if (print_line) {
2497                         ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
2498                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2499                                         peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2500                 }
2501         }
2502         ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2503         AST_LIST_UNLOCK(&peers);
2504         return RESULT_SUCCESS;
2505 #undef FORMAT
2506 #undef FORMAT2
2507 }
2508
2509 static int dundi_show_trans(int fd, int argc, char *argv[])
2510 {
2511 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2512 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2513         struct dundi_transaction *trans;
2514         char iabuf[INET_ADDRSTRLEN];
2515         if (argc != 3)
2516                 return RESULT_SHOWUSAGE;
2517         AST_LIST_LOCK(&peers);
2518         ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2519         AST_LIST_TRAVERSE(&alltrans, trans, all) {
2520                 ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr), 
2521                         ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2522         }
2523         AST_LIST_UNLOCK(&peers);
2524         return RESULT_SUCCESS;
2525 #undef FORMAT
2526 #undef FORMAT2
2527 }
2528
2529 static int dundi_show_entityid(int fd, int argc, char *argv[])
2530 {
2531         char eid_str[20];
2532         if (argc != 3)
2533                 return RESULT_SHOWUSAGE;
2534         AST_LIST_LOCK(&peers);
2535         dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2536         AST_LIST_UNLOCK(&peers);
2537         ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2538         return RESULT_SUCCESS;
2539 }
2540
2541 static int dundi_show_requests(int fd, int argc, char *argv[])
2542 {
2543 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2544 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2545         struct dundi_request *req;
2546         char eidstr[20];
2547         if (argc != 3)
2548                 return RESULT_SHOWUSAGE;
2549         AST_LIST_LOCK(&peers);
2550         ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2551         AST_LIST_TRAVERSE(&requests, req, list) {
2552                 ast_cli(fd, FORMAT, req->number, req->dcontext,
2553                         dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2554         }
2555         AST_LIST_UNLOCK(&peers);
2556         return RESULT_SUCCESS;
2557 #undef FORMAT
2558 #undef FORMAT2
2559 }
2560
2561 /* Grok-a-dial DUNDi */
2562
2563 static int dundi_show_mappings(int fd, int argc, char *argv[])
2564 {
2565 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2566 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2567         struct dundi_mapping *map;
2568         char fs[256];
2569         if (argc != 3)
2570                 return RESULT_SHOWUSAGE;
2571         AST_LIST_LOCK(&peers);
2572         ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2573         AST_LIST_TRAVERSE(&mappings, map, list) {
2574                 ast_cli(fd, FORMAT, map->dcontext, map->weight, 
2575                         ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
2576                         dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2577         }
2578         AST_LIST_UNLOCK(&peers);
2579         return RESULT_SUCCESS;
2580 #undef FORMAT
2581 #undef FORMAT2
2582 }
2583
2584 static int dundi_show_precache(int fd, int argc, char *argv[])
2585 {
2586 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2587 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2588         struct dundi_precache_queue *qe;
2589         int h,m,s;
2590         time_t now;
2591         
2592         if (argc != 3)
2593                 return RESULT_SHOWUSAGE;
2594         time(&now);
2595         ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2596         AST_LIST_LOCK(&pcq);
2597         AST_LIST_TRAVERSE(&pcq, qe, list) {
2598                 s = qe->expiration - now;
2599                 h = s / 3600;
2600                 s = s % 3600;
2601                 m = s / 60;
2602                 s = s % 60;
2603                 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2604         }
2605         AST_LIST_UNLOCK(&pcq);
2606         
2607         return RESULT_SUCCESS;
2608 #undef FORMAT
2609 #undef FORMAT2
2610 }
2611
2612 static char debug_usage[] = 
2613 "Usage: dundi debug\n"
2614 "       Enables dumping of DUNDi packets for debugging purposes\n";
2615
2616 static char no_debug_usage[] = 
2617 "Usage: dundi no debug\n"
2618 "       Disables dumping of DUNDi packets for debugging purposes\n";
2619
2620 static char store_history_usage[] = 
2621 "Usage: dundi store history\n"
2622 "       Enables storing of DUNDi requests and times for debugging\n"
2623 "purposes\n";
2624
2625 static char no_store_history_usage[] = 
2626 "Usage: dundi no store history\n"
2627 "       Disables storing of DUNDi requests and times for debugging\n"
2628 "purposes\n";
2629
2630 static char show_peers_usage[] = 
2631 "Usage: dundi show peers\n"
2632 "       Lists all known DUNDi peers.\n";
2633
2634 static char show_trans_usage[] = 
2635 "Usage: dundi show trans\n"
2636 "       Lists all known DUNDi transactions.\n";
2637
2638 static char show_mappings_usage[] = 
2639 "Usage: dundi show mappings\n"
2640 "       Lists all known DUNDi mappings.\n";
2641
2642 static char show_precache_usage[] = 
2643 "Usage: dundi show precache\n"
2644 "       Lists all known DUNDi scheduled precache updates.\n";
2645
2646 static char show_entityid_usage[] = 
2647 "Usage: dundi show entityid\n"
2648 "       Displays the global entityid for this host.\n";
2649
2650 static char show_peer_usage[] = 
2651 "Usage: dundi show peer [peer]\n"
2652 "       Provide a detailed description of a specifid DUNDi peer.\n";
2653
2654 static char show_requests_usage[] = 
2655 "Usage: dundi show requests\n"
2656 "       Lists all known pending DUNDi requests.\n";
2657
2658 static char lookup_usage[] =
2659 "Usage: dundi lookup <number>[@context] [bypass]\n"
2660 "       Lookup the given number within the given DUNDi context\n"
2661 "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
2662 "keyword is specified.\n";
2663
2664 static char precache_usage[] =
2665 "Usage: dundi precache <number>[@context]\n"
2666 "       Lookup the given number within the given DUNDi context\n"
2667 "(or e164 if none is specified) and precaches the results to any\n"
2668 "upstream DUNDi push servers.\n";
2669
2670 static char query_usage[] =
2671 "Usage: dundi query <entity>[@context]\n"
2672 "       Attempts to retrieve contact information for a specific\n"
2673 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2674 "e164 if none is specified).\n";
2675
2676 static char flush_usage[] =
2677 "Usage: dundi flush [stats]\n"
2678 "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
2679 "'stats' is present, clears timer statistics instead of normal\n"
2680 "operation.\n";
2681
2682 static struct ast_cli_entry  cli_debug =
2683         { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2684
2685 static struct ast_cli_entry  cli_store_history =
2686         { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2687
2688 static struct ast_cli_entry  cli_no_store_history =
2689         { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2690
2691 static struct ast_cli_entry  cli_flush =
2692         { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2693
2694 static struct ast_cli_entry  cli_no_debug =
2695         { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2696
2697 static struct ast_cli_entry  cli_show_peers =
2698         { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2699
2700 static struct ast_cli_entry  cli_show_trans =
2701         { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2702
2703 static struct ast_cli_entry  cli_show_entityid =
2704         { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2705
2706 static struct ast_cli_entry  cli_show_mappings =
2707         { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2708
2709 static struct ast_cli_entry  cli_show_precache =
2710         { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2711
2712 static struct ast_cli_entry  cli_show_requests =
2713         { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2714
2715 static struct ast_cli_entry  cli_show_peer =
2716         { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2717
2718 static struct ast_cli_entry  cli_lookup =
2719         { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2720
2721 static struct ast_cli_entry  cli_precache =
2722         { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2723
2724 static struct ast_cli_entry  cli_queryeid =
2725         { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2726
2727 LOCAL_USER_DECL;
2728
2729 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2730 {
2731         struct dundi_transaction *trans;
2732         int tid;
2733         
2734         /* Don't allow creation of transactions to non-registered peers */
2735         if (p && !p->addr.sin_addr.s_addr)
2736                 return NULL;
2737         tid = get_trans_id();
2738         if (tid < 1)
2739                 return NULL;
2740         trans = ast_calloc(1, sizeof(*trans));
2741         if (trans) {
2742                 if (global_storehistory) {
2743                         trans->start = ast_tvnow();
2744                         ast_set_flag(trans, FLAG_STOREHIST);
2745                 }
2746                 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2747                 trans->autokillid = -1;
2748                 if (p) {
2749                         apply_peer(trans, p);
2750                         if (!p->sentfullkey)
2751                                 ast_set_flag(trans, FLAG_SENDFULLKEY);
2752                 }
2753                 trans->strans = tid;
2754                 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2755         }
2756         return trans;
2757 }
2758
2759 static int dundi_xmit(struct dundi_packet *pack)
2760 {
2761         int res;
2762         char iabuf[INET_ADDRSTRLEN];
2763         if (dundidebug)
2764                 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2765         res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2766         if (res < 0) {
2767                 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
2768                         ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2769                         ntohs(pack->parent->addr.sin_port), strerror(errno));
2770         }
2771         if (res > 0)
2772                 res = 0;
2773         return res;
2774 }
2775
2776 static void destroy_packet(struct dundi_packet *pack, int needfree)
2777 {
2778         if (pack->parent)
2779                 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2780         if (pack->retransid > -1)
2781                 ast_sched_del(sched, pack->retransid);
2782         if (needfree)
2783                 free(pack);
2784         else
2785                 pack->retransid = -1;
2786 }
2787
2788 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2789 {
2790         struct dundi_peer *peer;
2791         int ms;
2792         int x;
2793         int cnt;
2794         char eid_str[20];
2795         if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2796                 AST_LIST_TRAVERSE(&peers, peer, list) {
2797                         if (peer->regtrans == trans)
2798                                 peer->regtrans = NULL;
2799                         if (peer->qualtrans == trans) {
2800                                 if (fromtimeout) {
2801                                         if (peer->lastms > -1)
2802                                                 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2803                                         peer->lastms = -1;
2804                                 } else {
2805                                         ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2806                                         if (ms < 1)
2807                                                 ms = 1;
2808                                         if (ms < peer->maxms) {
2809                                                 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2810                                                         ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2811                                         } else if (peer->lastms < peer->maxms) {
2812                                                 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);
2813                                         }
2814                                         peer->lastms = ms;
2815                                 }
2816                                 peer->qualtrans = NULL;
2817                         }
2818                         if (ast_test_flag(trans, FLAG_STOREHIST)) {
2819                                 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2820                                         if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2821                                                 peer->avgms = 0;
2822                                                 cnt = 0;
2823                                                 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2824                                                         free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2825                                                 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2826                                                         peer->lookuptimes[x] = peer->lookuptimes[x-1];
2827                                                         peer->lookups[x] = peer->lookups[x-1];
2828                                                         if (peer->lookups[x]) {
2829                                                                 peer->avgms += peer->lookuptimes[x];
2830                                                                 cnt++;
2831                                                         }
2832                                                 }
2833                                                 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2834                                                 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2835                                                 if (peer->lookups[0]) {
2836                                                         sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2837                                                         peer->avgms += peer->lookuptimes[0];
2838                                                         cnt++;
2839                                                 }
2840                                                 if (cnt)
2841                                                         peer->avgms /= cnt;
2842                                         }
2843                                 }
2844                         }
2845                 }
2846         }
2847         if (trans->parent) {
2848                 /* Unlink from parent if appropriate */
2849                 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
2850                 if (AST_LIST_EMPTY(&trans->parent->trans)) {
2851                         /* Wake up sleeper */
2852                         if (trans->parent->pfds[1] > -1) {
2853                                 write(trans->parent->pfds[1], "killa!", 6);
2854                         }
2855                 }
2856         }
2857         /* Unlink from all trans */
2858         AST_LIST_REMOVE(&alltrans, trans, all);
2859         destroy_packets(&trans->packets);
2860         destroy_packets(&trans->lasttrans);
2861         if (trans->autokillid > -1)
2862                 ast_sched_del(sched, trans->autokillid);
2863         trans->autokillid = -1;
2864         if (trans->thread) {
2865                 /* If used by a thread, mark as dead and be done */
2866                 ast_set_flag(trans, FLAG_DEAD);
2867         } else
2868                 free(trans);
2869 }
2870
2871 static int dundi_rexmit(void *data)
2872 {
2873         struct dundi_packet *pack;
2874         char iabuf[INET_ADDRSTRLEN];
2875         int res;
2876         AST_LIST_LOCK(&peers);
2877         pack = data;
2878         if (pack->retrans < 1) {
2879                 pack->retransid = -1;
2880                 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2881                         ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
2882                                 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr), 
2883                                 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2884                 destroy_trans(pack->parent, 1);
2885                 res = 0;
2886         } else {
2887                 /* Decrement retransmission, try again */
2888                 pack->retrans--;
2889                 dundi_xmit(pack);
2890                 res = 1;
2891         }
2892         AST_LIST_UNLOCK(&peers);
2893         return res;
2894 }
2895
2896 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2897 {
2898         struct dundi_packet *pack;
2899         int res;
2900         int len;
2901         char eid_str[20];
2902         len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2903         /* Reserve enough space for encryption */
2904         if (ast_test_flag(trans, FLAG_ENCRYPT))
2905                 len += 384;
2906         pack = ast_calloc(1, len);
2907         if (pack) {
2908                 pack->h = (struct dundi_hdr *)(pack->data);
2909                 if (cmdresp != DUNDI_COMMAND_ACK) {
2910                         pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2911                         pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2912                         AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
2913                 }
2914                 pack->parent = trans;
2915                 pack->h->strans = htons(trans->strans);
2916                 pack->h->dtrans = htons(trans->dtrans);
2917                 pack->h->iseqno = trans->iseqno;
2918                 pack->h->oseqno = trans->oseqno;
2919                 pack->h->cmdresp = cmdresp;
2920                 pack->datalen = sizeof(struct dundi_hdr);
2921                 if (ied) {
2922                         memcpy(pack->h->ies, ied->buf, ied->pos);
2923                         pack->datalen += ied->pos;
2924                 } 
2925