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