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