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