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