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