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