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