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