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