Asterisk - The Open Source Telephony Project GIT-master-3dae2cf
pbx_dundi.c
Go to the documentation of this file.
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/*! \li \ref pbx_dundi.c uses configuration file \ref dundi.conf
25 * \addtogroup configuration_file Configuration Files
26 */
27
28/*!
29 * \page dundi.conf dundi.conf
30 * \verbinclude dundi.conf.sample
31 */
32
33/*** MODULEINFO
34 <depend>zlib</depend>
35 <use type="module">res_crypto</use>
36 <use type="external">crypto</use>
37 <support_level>extended</support_level>
38 ***/
39
40#include "asterisk.h"
41
42#include "asterisk/network.h"
43#include <sys/ioctl.h>
44#include <zlib.h>
45#include <signal.h>
46#include <pthread.h>
47#include <net/if.h>
48
49#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
50#include <net/if_dl.h>
51#include <ifaddrs.h>
52#include <signal.h>
53#endif
54
55#include "asterisk/file.h"
56#include "asterisk/logger.h"
57#include "asterisk/channel.h"
58#include "asterisk/config.h"
59#include "asterisk/pbx.h"
60#include "asterisk/module.h"
61#include "asterisk/frame.h"
62#include "asterisk/cli.h"
63#include "asterisk/lock.h"
64#include "asterisk/md5.h"
65#include "asterisk/dundi.h"
66#include "asterisk/sched.h"
67#include "asterisk/io.h"
68#include "asterisk/utils.h"
69#include "asterisk/netsock2.h"
70#include "asterisk/crypto.h"
71#include "asterisk/astdb.h"
72#include "asterisk/acl.h"
73#include "asterisk/app.h"
74
75#include "dundi-parser.h"
76
77/*** DOCUMENTATION
78 <function name="DUNDILOOKUP" language="en_US">
79 <synopsis>
80 Do a DUNDi lookup of a phone number.
81 </synopsis>
82 <syntax>
83 <parameter name="number" required="true"/>
84 <parameter name="context">
85 <para>If not specified the default will be <literal>e164</literal>.</para>
86 </parameter>
87 <parameter name="options">
88 <optionlist>
89 <option name="b">
90 <para>Bypass the internal DUNDi cache</para>
91 </option>
92 </optionlist>
93 </parameter>
94 </syntax>
95 <description>
96 <para>This will do a DUNDi lookup of the given phone number.</para>
97 <para>This function will return the Technology/Resource found in the first result
98 in the DUNDi lookup. If no results were found, the result will be blank.</para>
99 </description>
100 </function>
101
102
103 <function name="DUNDIQUERY" language="en_US">
104 <synopsis>
105 Initiate a DUNDi query.
106 </synopsis>
107 <syntax>
108 <parameter name="number" required="true"/>
109 <parameter name="context">
110 <para>If not specified the default will be <literal>e164</literal>.</para>
111 </parameter>
112 <parameter name="options">
113 <optionlist>
114 <option name="b">
115 <para>Bypass the internal DUNDi cache</para>
116 </option>
117 </optionlist>
118 </parameter>
119 </syntax>
120 <description>
121 <para>This will do a DUNDi lookup of the given phone number.</para>
122 <para>The result of this function will be a numeric ID that can be used to retrieve
123 the results with the <literal>DUNDIRESULT</literal> function.</para>
124 </description>
125 </function>
126
127 <function name="DUNDIRESULT" language="en_US">
128 <synopsis>
129 Retrieve results from a DUNDIQUERY.
130 </synopsis>
131 <syntax>
132 <parameter name="id" required="true">
133 <para>The identifier returned by the <literal>DUNDIQUERY</literal> function.</para>
134 </parameter>
135 <parameter name="resultnum">
136 <optionlist>
137 <option name="number">
138 <para>The number of the result that you want to retrieve, this starts at <literal>1</literal></para>
139 </option>
140 <option name="getnum">
141 <para>The total number of results that are available.</para>
142 </option>
143 </optionlist>
144 </parameter>
145 </syntax>
146 <description>
147 <para>This function will retrieve results from a previous use\n"
148 of the <literal>DUNDIQUERY</literal> function.</para>
149 </description>
150 </function>
151 ***/
152
153#define MAX_RESULTS 64
154
155#define MAX_PACKET_SIZE 8192
156
157#define MAX_WEIGHT 59999
158
159#define DUNDI_MODEL_INBOUND (1 << 0)
160#define DUNDI_MODEL_OUTBOUND (1 << 1)
161#define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
162
163/*! Keep times of last 10 lookups */
164#define DUNDI_TIMING_HISTORY 10
165
166enum {
167 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
168 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
169 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
170 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
171 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted with ECX/DCX */
172 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
173 FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
174};
175
176#define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
177
178#if 0
179#define DUNDI_SECRET_TIME 15 /* Testing only */
180#else
181#define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
182#endif
183
184static struct io_context *io;
186static int netsocket = -1; /* Socket for bindaddr if only one bindaddr. Otherwise the IPv4 socket when bindaddr2 given. */
187static int netsocket2 = -1; /* IPv6 socket when bindaddr2 given. */
191static unsigned int tos = 0;
192static int dundidebug = 0;
193static int authdebug = 0;
199static int default_expiration = 60;
200static int global_storehistory = 0;
201static char dept[80];
202static char org[80];
203static char locality[80];
204static char stateprov[80];
205static char country[80];
206static char email[80];
207static char phone[80];
208static char secretpath[80];
209static char cursecret[80];
210static char ipaddr[80];
213static time_t rotatetime;
214static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
215static int dundi_shutdown = 0;
216
219 int allow;
220 char name[0];
221};
222
225 struct dundi_hdr *h;
230 unsigned char data[0];
231};
232
234 unsigned short flags;
236};
237
240 char *context;
242 char number[0];
243};
244
245struct dundi_request;
246
248 struct ast_sockaddr addr; /*!< Other end of transaction */
249 struct timeval start; /*!< When this transaction was created */
251 int eidcount; /*!< Number of eids in eids */
252 dundi_eid us_eid; /*!< Our EID, to them */
253 dundi_eid them_eid; /*!< Their EID, to us */
254 ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
255 ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
256 unsigned int flags; /*!< Has final packet been sent */
257 int ttl; /*!< Remaining TTL for queries on this one */
258 int thread; /*!< We have a calling thread */
259 int retranstimer; /*!< How long to wait before retransmissions */
260 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
261 int autokilltimeout; /*!< Recommended timeout for autokill */
262 unsigned short strans; /*!< Our transaction identifier */
263 unsigned short dtrans; /*!< Their transaction identifer */
264 unsigned char iseqno; /*!< Next expected received seqno */
265 unsigned char oiseqno; /*!< Last received incoming seqno */
266 unsigned char oseqno; /*!< Next transmitted seqno */
267 unsigned char aseqno; /*!< Last acknowledge seqno */
268 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
269 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
270 struct dundi_request *parent; /*!< Parent request (if there is one) */
271 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
272 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
273};
274
287 int pfds[2];
288 uint32_t crc32; /*!< CRC-32 of all but root EID's in avoid list */
291};
292
299 int tech;
300 int dead;
301 char dest[512];
303};
304
307 struct ast_sockaddr addr; /*!< Address of DUNDi peer */
311 char inkey[80];
312 char outkey[80];
313 int dead;
317 int order;
318 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
319 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
320 uint32_t us_keycrc32; /*!< CRC-32 of our key */
321 ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
322 ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
323 uint32_t them_keycrc32; /*!< CRC-32 of our key */
324 ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
325 ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
326 time_t keyexpire; /*!< When to expire/recreate key */
330 int avgms;
331 struct dundi_transaction *regtrans; /*!< Registration transaction */
332 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
333 int model; /*!< Pull model */
334 int pcmodel; /*!< Push/precache model */
335 /*! Dynamic peers register with us */
336 unsigned int dynamic:1;
337 int lastms; /*!< Last measured latency */
338 int maxms; /*!< Max permissible latency */
339 struct timeval qualtx; /*!< Time of transmit */
341};
342
348
349/*!
350 * \brief Wildcard peer
351 *
352 * This peer is created if the [*] entry is specified in dundi.conf
353 */
354static struct dundi_peer *any_peer;
355
356static int dundi_xmit(struct dundi_packet *pack);
357
358static void dundi_debug_output(const char *data)
359{
360 if (dundidebug)
361 ast_verbose("%s", data);
362}
363
364static void dundi_error_output(const char *data)
365{
366 ast_log(LOG_WARNING, "%s", data);
367}
368
369static int has_permission(struct permissionlist *permlist, char *cont)
370{
371 struct permission *perm;
372 int res = 0;
373
374 AST_LIST_TRAVERSE(permlist, perm, list) {
375 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
376 res = perm->allow;
377 }
378
379 return res;
380}
381
382static char *tech2str(int tech)
383{
384 switch(tech) {
385 case DUNDI_PROTO_NONE:
386 return "None";
387 case DUNDI_PROTO_IAX:
388 return "IAX2";
389 case DUNDI_PROTO_SIP:
390 return "SIP";
391 case DUNDI_PROTO_H323:
392 return "H323";
394 return "PJSIP";
395 default:
396 return "Unknown";
397 }
398}
399
400static int str2tech(const char *str)
401{
402 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
403 return DUNDI_PROTO_IAX;
404 else if (!strcasecmp(str, "SIP"))
405 return DUNDI_PROTO_SIP;
406 else if (!strcasecmp(str, "H323"))
407 return DUNDI_PROTO_H323;
408 else if (!strcasecmp(str, "PJSIP"))
409 return DUNDI_PROTO_PJSIP;
410 else
411 return -1;
412}
413
414static 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[]);
415static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
416static struct dundi_transaction *create_transaction(struct dundi_peer *p);
417
418static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct ast_sockaddr *sin)
419{
420 struct dundi_transaction *trans;
421
422 /* Look for an exact match first */
423 AST_LIST_TRAVERSE(&alltrans, trans, all) {
424 if (!ast_sockaddr_cmp(&trans->addr, sin) &&
425 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
426 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
427 if (hdr->strans) {
428 trans->dtrans = ntohs(hdr->strans) & 32767;
429 }
430 return trans;
431 }
432 }
433
434 switch(hdr->cmdresp & 0x7f) {
441 if (!hdr->strans)
442 break;
443 /* Create new transaction */
444 if (!(trans = create_transaction(NULL)))
445 break;
446 ast_sockaddr_copy(&trans->addr, sin);
447 trans->dtrans = ntohs(hdr->strans) & 32767;
448 default:
449 break;
450 }
451
452 return trans;
453}
454
455static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
456
457static int dundi_ack(struct dundi_transaction *trans, int final)
458{
459 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
460}
461static void dundi_reject(struct dundi_hdr *h, struct ast_sockaddr *sin)
462{
463 struct {
464 struct dundi_packet pack;
465 struct dundi_hdr hdr;
466 } tmp;
467 struct dundi_transaction trans;
468 /* Never respond to an INVALID with another INVALID */
470 return;
471 memset(&tmp, 0, sizeof(tmp));
472 memset(&trans, 0, sizeof(trans));
473 ast_sockaddr_copy(&trans.addr, sin);
474 tmp.hdr.strans = h->dtrans;
475 tmp.hdr.dtrans = h->strans;
476 tmp.hdr.iseqno = h->oseqno;
477 tmp.hdr.oseqno = h->iseqno;
478 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
479 tmp.hdr.cmdflags = 0;
480 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
481 tmp.pack.datalen = sizeof(struct dundi_hdr);
482 tmp.pack.parent = &trans;
483 dundi_xmit(&tmp.pack);
484}
485
486static int get_trans_id(void)
487{
488 struct dundi_transaction *t;
489 int stid = (ast_random() % 32766) + 1;
490 int tid = stid;
491
492 do {
494 if (t->strans == tid)
495 break;
496 }
497 if (!t)
498 return tid;
499 tid = (tid % 32766) + 1;
500 } while (tid != stid);
501
502 return 0;
503}
504
505static int reset_transaction(struct dundi_transaction *trans)
506{
507 int tid;
508 tid = get_trans_id();
509 if (tid < 1)
510 return -1;
511 trans->strans = tid;
512 trans->dtrans = 0;
513 trans->iseqno = 0;
514 trans->oiseqno = 0;
515 trans->oseqno = 0;
516 trans->aseqno = 0;
518 return 0;
519}
520
522{
523 struct dundi_peer *cur = NULL;
524
525 if (!eid)
526 eid = &empty_eid;
527
529 if (!ast_eid_cmp(&cur->eid,eid))
530 break;
531 }
532
533 if (!cur && any_peer)
534 cur = any_peer;
535
536 return cur;
537}
538
539static void build_iv(unsigned char *iv)
540{
541 /* XXX Would be nice to be more random XXX */
542 unsigned int *fluffy;
543 int x;
544 fluffy = (unsigned int *)(iv);
545 for (x=0;x<4;x++)
546 fluffy[x] = ast_random();
547}
548
559 void *chal;
561 int ttl;
562 char fluffy[0];
563};
564
565static int get_mapping_weight(struct dundi_mapping *map, struct varshead *headp)
566{
567 char buf[32];
568
569 buf[0] = 0;
570 if (map->weightstr) {
571 if (headp) {
572 pbx_substitute_variables_varshead(headp, map->weightstr, buf, sizeof(buf) - 1);
573 } else {
575 }
576
577 if (sscanf(buf, "%30d", &map->_weight) != 1)
578 map->_weight = MAX_WEIGHT;
579 }
580
581 return map->_weight;
582}
583
584static 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)
585{
586 struct ast_flags flags = {0};
587 int x;
588 if (!ast_strlen_zero(map->lcontext)) {
589 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
591 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
593 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
595 if (ast_ignore_pattern(map->lcontext, called_number))
597
598 /* Clearly we can't say 'don't ask' anymore if we found anything... */
601
603 /* Skip partial answers */
605 }
607 struct varshead headp;
608 struct ast_var_t *newvariable;
609 ast_set_flag(&flags, map->options & 0xffff);
610 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
611 dr[anscnt].techint = map->tech;
612 dr[anscnt].expiration = dundi_cache_time;
613 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
614 dr[anscnt].eid = *us_eid;
615 ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
616 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
618 if ((newvariable = ast_var_assign("NUMBER", called_number))) {
619 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
620 }
621 if ((newvariable = ast_var_assign("EID", dr[anscnt].eid_str))) {
622 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
623 }
624 if ((newvariable = ast_var_assign("SECRET", cursecret))) {
625 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
626 }
627 if ((newvariable = ast_var_assign("IPADDR", ipaddr))) {
628 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
629 }
630 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
631 dr[anscnt].weight = get_mapping_weight(map, &headp);
632 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
633 ast_var_delete(newvariable);
634 } else {
635 dr[anscnt].dest[0] = '\0';
636 dr[anscnt].weight = get_mapping_weight(map, NULL);
637 }
638 anscnt++;
639 } else {
640 /* No answers... Find the fewest number of digits from the
641 number for which we have no answer. */
642 char tmp[AST_MAX_EXTENSION + 1] = "";
643 for (x = 0; x < (sizeof(tmp) - 1); x++) {
644 tmp[x] = called_number[x];
645 if (!tmp[x])
646 break;
647 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
648 /* Oops found something we can't match. If this is longer
649 than the running hint, we have to consider it */
650 if (strlen(tmp) > strlen(hmd->exten)) {
651 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
652 }
653 break;
654 }
655 }
656 }
657 }
658 return anscnt;
659}
660
661static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
662
663static void *dundi_lookup_thread(void *data)
664{
665 struct dundi_query_state *st = data;
667 struct dundi_ie_data ied;
668 struct dundi_hint_metadata hmd;
669 char eid_str[20];
670 int res, x;
671 int ouranswers=0;
672 int max = 999999;
673 int expiration = dundi_cache_time;
674
675 ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
676 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
677 memset(&ied, 0, sizeof(ied));
678 memset(&dr, 0, sizeof(dr));
679 memset(&hmd, 0, sizeof(hmd));
680 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
682 for (x=0;x<st->nummaps;x++)
683 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
684 if (ouranswers < 0)
685 ouranswers = 0;
686 for (x=0;x<ouranswers;x++) {
687 if (dr[x].weight < max)
688 max = dr[x].weight;
689 }
690
691 if (max) {
692 /* If we do not have a canonical result, keep looking */
693 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);
694 if (res > 0) {
695 /* Append answer in result */
696 ouranswers += res;
697 } else {
698 if ((res < -1) && (!ouranswers))
699 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
700 }
701 }
703 /* Truncate if "don't ask" isn't present */
705 hmd.exten[0] = '\0';
706 if (ast_test_flag(st->trans, FLAG_DEAD)) {
707 ast_debug(1, "Our transaction went away!\n");
708 st->trans->thread = 0;
709 destroy_trans(st->trans, 0);
710 } else {
711 for (x=0;x<ouranswers;x++) {
712 /* Add answers */
713 if (dr[x].expiration && (expiration > dr[x].expiration))
714 expiration = dr[x].expiration;
715 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
716 }
719 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
720 st->trans->thread = 0;
721 }
723 ast_free(st);
724 return NULL;
725}
726
727static void *dundi_precache_thread(void *data)
728{
729 struct dundi_query_state *st = data;
730 struct dundi_ie_data ied;
731 struct dundi_hint_metadata hmd = {0};
732 char eid_str[20];
733
734 ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
735 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
736 memset(&ied, 0, sizeof(ied));
737
738 /* Now produce precache */
740
742 /* Truncate if "don't ask" isn't present */
744 hmd.exten[0] = '\0';
745 if (ast_test_flag(st->trans, FLAG_DEAD)) {
746 ast_debug(1, "Our transaction went away!\n");
747 st->trans->thread = 0;
748 destroy_trans(st->trans, 0);
749 } else {
750 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
751 st->trans->thread = 0;
752 }
754 ast_free(st);
755 return NULL;
756}
757
758static 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[]);
759
760static void *dundi_query_thread(void *data)
761{
762 struct dundi_query_state *st = data;
763 struct dundi_entity_info dei;
764 struct dundi_ie_data ied;
765 struct dundi_hint_metadata hmd;
766 char eid_str[20];
767 int res;
768
769 ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
770 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
771 memset(&ied, 0, sizeof(ied));
772 memset(&dei, 0, sizeof(dei));
773 memset(&hmd, 0, sizeof(hmd));
774 if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
775 /* Ooh, it's us! */
776 ast_debug(1, "Neat, someone look for us!\n");
777 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
778 ast_copy_string(dei.org, org, sizeof(dei.org));
779 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
780 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
781 ast_copy_string(dei.country, country, sizeof(dei.country));
782 ast_copy_string(dei.email, email, sizeof(dei.email));
783 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
784 res = 1;
785 } else {
786 /* If we do not have a canonical result, keep looking */
787 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
788 }
790 if (ast_test_flag(st->trans, FLAG_DEAD)) {
791 ast_debug(1, "Our transaction went away!\n");
792 st->trans->thread = 0;
793 destroy_trans(st->trans, 0);
794 } else {
795 if (res) {
803 if (!ast_strlen_zero(dei.ipaddr))
805 }
808 st->trans->thread = 0;
809 }
811 ast_free(st);
812 return NULL;
813}
814
815static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
816{
817 struct dundi_query_state *st;
818 int totallen;
819 int x;
820 int skipfirst=0;
821 char eid_str[20];
822 char *s;
823 pthread_t lookupthread;
824
825 if (ies->eidcount > 1) {
826 /* Since it is a requirement that the first EID is the authenticating host
827 and the last EID is the root, it is permissible that the first and last EID
828 could be the same. In that case, we should go ahead copy only the "root" section
829 since we will not need it for authentication. */
830 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
831 skipfirst = 1;
832 }
833 totallen = sizeof(struct dundi_query_state);
834 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
835 st = ast_calloc(1, totallen);
836 if (st) {
838 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
839 st->trans = trans;
840 st->ttl = ies->ttl - 1;
841 if (st->ttl < 0)
842 st->ttl = 0;
843 s = st->fluffy;
844 for (x=skipfirst;ies->eids[x];x++) {
845 st->eids[x-skipfirst] = (dundi_eid *)s;
846 *st->eids[x-skipfirst] = *ies->eids[x];
847 s += sizeof(dundi_eid);
848 }
849 ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
850
851 trans->thread = 1;
852 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
853 struct dundi_ie_data ied = { 0, };
854 trans->thread = 0;
855 ast_log(LOG_WARNING, "Unable to create thread!\n");
856 ast_free(st);
858 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
859 return -1;
860 }
861 } else {
862 struct dundi_ie_data ied = { 0, };
864 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
865 return -1;
866 }
867 return 0;
868}
869
870static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
871{
872 int unaffected;
873 char key1[256];
874 char key2[256];
875 char eidpeer_str[20];
876 char eidroot_str[20];
877 char data[80];
878 time_t timeout;
879
880 if (expiration < 0)
881 expiration = dundi_cache_time;
882
883 /* Only cache hint if "don't ask" is there... */
884 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
885 return 0;
886
887 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
888
889 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
890 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
891 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08x", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
892 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
893
894 time(&timeout);
895 timeout += expiration;
896 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
897
898 ast_db_put("dundi/cache", key1, data);
899 ast_debug(1, "Caching hint at '%s'\n", key1);
900 ast_db_put("dundi/cache", key2, data);
901 ast_debug(1, "Caching hint at '%s'\n", key2);
902 return 0;
903}
904
905static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
906{
907 int x;
908 char key1[256];
909 char key2[256];
910 char data[1024];
911 char eidpeer_str[20];
912 char eidroot_str[20];
913 time_t timeout;
914
915 if (expiration < 1)
916 expiration = dundi_cache_time;
917
918 /* Keep pushes a little longer, cut pulls a little short */
919 if (push)
920 expiration += 10;
921 else
922 expiration -= 10;
923 if (expiration < 1)
924 expiration = 1;
925 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
926 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
927 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08x", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
928 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
929 /* Build request string */
930 time(&timeout);
931 timeout += expiration;
932 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
933 for (x=start;x<req->respcount;x++) {
934 /* Skip anything with an illegal pipe in it */
935 if (strchr(req->dr[x].dest, '|'))
936 continue;
937 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%u/%d/%d/%s/%s|",
938 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
939 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
940 }
941 ast_db_put("dundi/cache", key1, data);
942 ast_db_put("dundi/cache", key2, data);
943 return 0;
944}
945
946static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
947{
948 struct dundi_query_state *st;
949 int totallen;
950 int x,z;
951 struct dundi_ie_data ied;
952 char *s;
953 struct dundi_result dr2[MAX_RESULTS];
954 struct dundi_request dr;
955 struct dundi_hint_metadata hmd;
956
957 struct dundi_mapping *cur;
958 int mapcount;
959 int skipfirst = 0;
960
961 pthread_t lookupthread;
962
963 memset(&dr2, 0, sizeof(dr2));
964 memset(&dr, 0, sizeof(dr));
965 memset(&hmd, 0, sizeof(hmd));
966
967 /* Forge request structure to hold answers for cache */
969 dr.dr = dr2;
970 dr.maxcount = MAX_RESULTS;
971 dr.expiration = dundi_cache_time;
972 dr.hmd = &hmd;
973 dr.pfds[0] = dr.pfds[1] = -1;
974 trans->parent = &dr;
975 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
976 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
977
978 for (x=0;x<ies->anscount;x++) {
979 if (trans->parent->respcount < trans->parent->maxcount) {
980 /* Make sure it's not already there */
981 for (z=0;z<trans->parent->respcount;z++) {
982 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
983 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
984 break;
985 }
986 if (z == trans->parent->respcount) {
987 /* Copy into parent responses */
988 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
989 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
990 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
991 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
992 if (ies->expiration > 0)
993 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
994 else
997 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
998 &ies->answers[x]->eid);
999 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
1000 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1002 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1003 trans->parent->respcount++;
1005 } else if (trans->parent->dr[z].weight > ntohs(ies->answers[x]->weight)) {
1006 /* Update weight if appropriate */
1007 trans->parent->dr[z].weight = ntohs(ies->answers[x]->weight);
1008 }
1009 } else
1010 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
1011 trans->parent->number, trans->parent->dcontext);
1012
1013 }
1014 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
1015 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
1016 if (ies->hint)
1017 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
1018
1019 totallen = sizeof(struct dundi_query_state);
1020 /* Count matching map entries */
1021 mapcount = 0;
1022 AST_LIST_TRAVERSE(&mappings, cur, list) {
1023 if (!strcasecmp(cur->dcontext, ccontext))
1024 mapcount++;
1025 }
1026
1027 /* If no maps, return -1 immediately */
1028 if (!mapcount)
1029 return -1;
1030
1031 if (ies->eidcount > 1) {
1032 /* Since it is a requirement that the first EID is the authenticating host
1033 and the last EID is the root, it is permissible that the first and last EID
1034 could be the same. In that case, we should go ahead copy only the "root" section
1035 since we will not need it for authentication. */
1036 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1037 skipfirst = 1;
1038 }
1039
1040 /* Prepare to run a query and then propagate that as necessary */
1041 totallen += mapcount * sizeof(struct dundi_mapping);
1042 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1043 st = ast_calloc(1, totallen);
1044 if (st) {
1045 ast_copy_string(st->called_context, dr.dcontext, sizeof(st->called_context));
1047 st->trans = trans;
1048 st->ttl = ies->ttl - 1;
1049 st->nocache = ies->cbypass;
1050 if (st->ttl < 0)
1051 st->ttl = 0;
1052 s = st->fluffy;
1053 for (x=skipfirst;ies->eids[x];x++) {
1054 st->eids[x-skipfirst] = (dundi_eid *)s;
1055 *st->eids[x-skipfirst] = *ies->eids[x];
1056 st->directs[x-skipfirst] = ies->eid_direct[x];
1057 s += sizeof(dundi_eid);
1058 }
1059 /* Append mappings */
1060 x = 0;
1061 st->maps = (struct dundi_mapping *)s;
1063 if (!strcasecmp(cur->dcontext, ccontext)) {
1064 if (x < mapcount) {
1065 st->maps[x] = *cur;
1066 st->maps[x].list.next = NULL;
1067 x++;
1068 }
1069 }
1070 }
1071 st->nummaps = mapcount;
1072 ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1073 trans->thread = 1;
1074 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1075 trans->thread = 0;
1076 ast_log(LOG_WARNING, "Unable to create thread!\n");
1077 ast_free(st);
1078 memset(&ied, 0, sizeof(ied));
1080 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1081 return -1;
1082 }
1083 } else {
1084 ast_log(LOG_WARNING, "Out of memory!\n");
1085 memset(&ied, 0, sizeof(ied));
1087 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1088 return -1;
1089 }
1090 return 0;
1091}
1092
1093static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1094{
1095 struct dundi_query_state *st;
1096 int totallen;
1097 int x;
1098 struct dundi_ie_data ied;
1099 char *s;
1100 struct dundi_mapping *cur;
1101 int mapcount = 0;
1102 int skipfirst = 0;
1103
1104 pthread_t lookupthread;
1105 totallen = sizeof(struct dundi_query_state);
1106 /* Count matching map entries */
1107 AST_LIST_TRAVERSE(&mappings, cur, list) {
1108 if (!strcasecmp(cur->dcontext, ccontext))
1109 mapcount++;
1110 }
1111 /* If no maps, return -1 immediately */
1112 if (!mapcount)
1113 return -1;
1114
1115 if (ies->eidcount > 1) {
1116 /* Since it is a requirement that the first EID is the authenticating host
1117 and the last EID is the root, it is permissible that the first and last EID
1118 could be the same. In that case, we should go ahead copy only the "root" section
1119 since we will not need it for authentication. */
1120 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1121 skipfirst = 1;
1122 }
1123
1124 totallen += mapcount * sizeof(struct dundi_mapping);
1125 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1126 st = ast_calloc(1, totallen);
1127 if (st) {
1130 st->trans = trans;
1131 st->ttl = ies->ttl - 1;
1132 st->nocache = ies->cbypass;
1133 if (st->ttl < 0)
1134 st->ttl = 0;
1135 s = st->fluffy;
1136 for (x=skipfirst;ies->eids[x];x++) {
1137 st->eids[x-skipfirst] = (dundi_eid *)s;
1138 *st->eids[x-skipfirst] = *ies->eids[x];
1139 st->directs[x-skipfirst] = ies->eid_direct[x];
1140 s += sizeof(dundi_eid);
1141 }
1142 /* Append mappings */
1143 x = 0;
1144 st->maps = (struct dundi_mapping *)s;
1146 if (!strcasecmp(cur->dcontext, ccontext)) {
1147 if (x < mapcount) {
1148 st->maps[x] = *cur;
1149 st->maps[x].list.next = NULL;
1150 x++;
1151 }
1152 }
1153 }
1154 st->nummaps = mapcount;
1155 ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1156 trans->thread = 1;
1157 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1158 trans->thread = 0;
1159 ast_log(LOG_WARNING, "Unable to create thread!\n");
1160 ast_free(st);
1161 memset(&ied, 0, sizeof(ied));
1163 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1164 return -1;
1165 }
1166 } else {
1167 ast_log(LOG_WARNING, "Out of memory!\n");
1168 memset(&ied, 0, sizeof(ied));
1170 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1171 return -1;
1172 }
1173 return 0;
1174}
1175
1176static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1177{
1178 char data[1024];
1179 char *ptr, *term, *src;
1180 int tech;
1181 struct ast_flags flags;
1182 int weight;
1183 int length;
1184 int z;
1185 char fs[256];
1186
1187 /* Build request string */
1188 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1189 time_t timeout;
1190 ptr = data;
1191 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1192 int expiration = timeout - now;
1193 if (expiration > 0) {
1194 ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
1195 ptr += length + 1;
1196 while((sscanf(ptr, "%30d/%30d/%30d/%n", (int *)&(flags.flags), &weight, &tech, &length) == 3)) {
1197 ptr += length;
1198 term = strchr(ptr, '|');
1199 if (term) {
1200 *term = '\0';
1201 src = strrchr(ptr, '/');
1202 if (src) {
1203 *src = '\0';
1204 src++;
1205 } else
1206 src = "";
1207 ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1208 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1209 /* Make sure it's not already there */
1210 for (z=0;z<req->respcount;z++) {
1211 if ((req->dr[z].techint == tech) &&
1212 !strcmp(req->dr[z].dest, ptr))
1213 break;
1214 }
1215 if (z == req->respcount) {
1216 /* Copy into parent responses */
1217 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1218 req->dr[req->respcount].weight = weight;
1219 req->dr[req->respcount].techint = tech;
1220 req->dr[req->respcount].expiration = expiration;
1221 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1223 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1224 ast_copy_string(req->dr[req->respcount].dest, ptr,
1225 sizeof(req->dr[req->respcount].dest));
1226 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1227 sizeof(req->dr[req->respcount].tech));
1228 req->respcount++;
1230 } else if (req->dr[z].weight > weight)
1231 req->dr[z].weight = weight;
1232 ptr = term + 1;
1233 }
1234 }
1235 /* We found *something* cached */
1236 if (expiration < *lowexpiration)
1237 *lowexpiration = expiration;
1238 return 1;
1239 } else
1240 ast_db_del("dundi/cache", key);
1241 } else
1242 ast_db_del("dundi/cache", key);
1243 }
1244
1245 return 0;
1246}
1247
1248static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc, int *lowexpiration)
1249{
1250 char eid_str[20];
1251 char eidroot_str[20];
1252 time_t now;
1253 int res=0;
1254 int res2=0;
1255 char eid_str_full[20];
1256 char tmp[256]="";
1257 /* Enough space for largest value that can be stored in key. */
1258 char key[sizeof(eid_str) + sizeof(tmp) + sizeof(req->dcontext) + sizeof(eidroot_str) + sizeof("hint////r")];
1259 int x;
1260
1261 time(&now);
1262 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1263 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1264 ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1265 snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, crc);
1266 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1267 snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, (unsigned)0);
1268 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1269 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1270 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1271 x = 0;
1272 if (!req->respcount) {
1273 while(!res2) {
1274 /* Look and see if we have a hint that would preclude us from looking at this
1275 peer for this number. */
1276 if (!(tmp[x] = req->number[x]))
1277 break;
1278 x++;
1279 /* Check for hints */
1280 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, crc);
1281 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1282 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, (unsigned)0);
1283 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1284 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1285 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1286 if (res2) {
1287 if (strlen(tmp) > strlen(req->hmd->exten)) {
1288 /* Update meta data if appropriate */
1289 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1290 }
1291 }
1292 }
1293 res |= res2;
1294 }
1295
1296 return res;
1297}
1298
1299static void qualify_peer(struct dundi_peer *peer, int schedonly);
1300
1301static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1302{
1303 if (ast_sockaddr_isnull(&trans->addr)) {
1304 ast_sockaddr_copy(&trans->addr, &p->addr);
1305 }
1306 trans->us_eid = p->us_eid;
1307 trans->them_eid = p->eid;
1308 /* Enable encryption if appropriate */
1309 if (!ast_strlen_zero(p->inkey))
1310 ast_set_flag(trans, FLAG_ENCRYPT);
1311 if (p->maxms) {
1312 trans->autokilltimeout = p->maxms;
1314 if (p->lastms > 1) {
1315 trans->retranstimer = p->lastms * 2;
1316 /* Keep it from being silly */
1317 if (trans->retranstimer < 150)
1318 trans->retranstimer = 150;
1319 }
1322 } else
1324}
1325
1326/*! \note Called with the peers list already locked */
1327static int do_register_expire(const void *data)
1328{
1329 struct dundi_peer *peer = (struct dundi_peer *)data;
1330 char eid_str[20];
1331
1332 ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1333 ast_db_del("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid));
1334 peer->registerexpire = -1;
1335 peer->lastms = 0;
1336 ast_sockaddr_setnull(&peer->addr);
1337 return 0;
1338}
1339
1340static int update_key(struct dundi_peer *peer)
1341{
1342 unsigned char key[16];
1343 struct ast_key *ekey, *skey;
1344 char eid_str[20];
1345 int res;
1346 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1347 build_iv(key);
1348 ast_aes_set_encrypt_key(key, &peer->us_ecx);
1349 ast_aes_set_decrypt_key(key, &peer->us_dcx);
1350 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1351 if (!ekey) {
1352 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1353 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1354 return -1;
1355 }
1356 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1357 if (!skey) {
1358 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1359 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1360 return -1;
1361 }
1362 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1363 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1364 return -1;
1365 }
1366 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1367 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1368 return -1;
1369 }
1370 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1371 peer->sentfullkey = 0;
1372 /* Looks good */
1373 time(&peer->keyexpire);
1374 peer->keyexpire += dundi_key_ttl;
1375 }
1376 return 0;
1377}
1378
1379static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1380{
1381 unsigned char curblock[16];
1382 int x;
1383 memcpy(curblock, iv, sizeof(curblock));
1384 while(len > 0) {
1385 for (x=0;x<16;x++)
1386 curblock[x] ^= src[x];
1387 ast_aes_encrypt(curblock, dst, ecx);
1388 memcpy(curblock, dst, sizeof(curblock));
1389 dst += 16;
1390 src += 16;
1391 len -= 16;
1392 }
1393 return 0;
1394}
1395static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1396{
1397 unsigned char lastblock[16];
1398 int x;
1399 memcpy(lastblock, iv, sizeof(lastblock));
1400 while(len > 0) {
1401 ast_aes_decrypt(src, dst, dcx);
1402 for (x=0;x<16;x++)
1403 dst[x] ^= lastblock[x];
1404 memcpy(lastblock, src, sizeof(lastblock));
1405 dst += 16;
1406 src += 16;
1407 len -= 16;
1408 }
1409 return 0;
1410}
1411
1412static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
1413{
1414 int space = *dstlen;
1415 unsigned long bytes;
1416 struct dundi_hdr *h;
1417 unsigned char *decrypt_space;
1418 decrypt_space = ast_alloca(srclen);
1419 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1420 /* Setup header */
1421 h = (struct dundi_hdr *)dst;
1422 *h = *ohdr;
1423 bytes = space - 6;
1424 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1425 ast_debug(1, "Ouch, uncompress failed :(\n");
1426 return NULL;
1427 }
1428 /* Update length */
1429 *dstlen = bytes + 6;
1430 /* Return new header */
1431 return h;
1432}
1433
1434static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1435{
1436 unsigned char *compress_space;
1437 int len;
1438 int res;
1439 unsigned long bytes;
1440 struct dundi_ie_data ied;
1441 struct dundi_peer *peer;
1442 unsigned char iv[16];
1443 len = pack->datalen + pack->datalen / 100 + 42;
1444 compress_space = ast_alloca(len);
1445 memset(compress_space, 0, len);
1446 /* We care about everthing save the first 6 bytes of header */
1447 bytes = len;
1448 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1449 if (res != Z_OK) {
1450 ast_debug(1, "Ouch, compression failed!\n");
1451 return -1;
1452 }
1453 memset(&ied, 0, sizeof(ied));
1454 /* Say who we are */
1455 if (!pack->h->iseqno && !pack->h->oseqno) {
1456 /* Need the key in the first copy */
1457 if (!(peer = find_peer(&trans->them_eid)))
1458 return -1;
1459 if (update_key(peer))
1460 return -1;
1461 if (!peer->sentfullkey)
1463 /* Append key data */
1464 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1465 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1467 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1468 } else {
1470 }
1471 /* Setup contexts */
1472 trans->ecx = peer->us_ecx;
1473 trans->dcx = peer->us_dcx;
1474
1475 /* We've sent the full key */
1476 peer->sentfullkey = 1;
1477 }
1478 /* Build initialization vector */
1479 build_iv(iv);
1480 /* Add the field, rounded up to 16 bytes */
1481 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1482 /* Copy the data */
1483 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1484 ast_log(LOG_NOTICE, "Final packet too large!\n");
1485 return -1;
1486 }
1487 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1488 ied.pos += ((bytes + 15) / 16) * 16;
1489 /* Reconstruct header */
1490 pack->datalen = sizeof(struct dundi_hdr);
1492 pack->h->cmdflags = 0;
1493 memcpy(pack->h->ies, ied.buf, ied.pos);
1494 pack->datalen += ied.pos;
1495 return 0;
1496}
1497
1498static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
1499{
1500 unsigned char dst[128];
1501 int res;
1502 struct ast_key *key, *skey;
1503 char eid_str[20];
1504 ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
1505 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1506 /* A match */
1507 return 1;
1508 } else if (!newkey || !newsig)
1509 return 0;
1510 if (!memcmp(peer->rxenckey, newkey, 128) &&
1511 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1512 /* By definition, a match */
1513 return 1;
1514 }
1515 /* Decrypt key */
1516 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1517 if (!key) {
1518 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1519 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1520 return -1;
1521 }
1522
1523 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1524 if (!skey) {
1525 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1526 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1527 return -1;
1528 }
1529
1530 /* First check signature */
1531 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1532 if (res)
1533 return 0;
1534
1535 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1536 if (res != 16) {
1537 if (res >= 0)
1538 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1539 return 0;
1540 }
1541 /* Decrypted, passes signature */
1542 ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
1543 memcpy(peer->rxenckey, newkey, 128);
1544 memcpy(peer->rxenckey + 128, newsig, 128);
1545 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1546 ast_aes_set_decrypt_key(dst, &peer->them_dcx);
1547 ast_aes_set_encrypt_key(dst, &peer->them_ecx);
1548 return 1;
1549}
1550
1551static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1552{
1553 struct permission *cur, *perm;
1554
1555 *peer_dst = *peer_src;
1556 AST_LIST_NEXT(peer_dst, list) = NULL;
1557
1558 /* Scheduled items cannot go with the copy */
1559 peer_dst->registerid = -1;
1560 peer_dst->qualifyid = -1;
1561 peer_dst->registerexpire = -1;
1562
1563 /* Transactions and lookup history cannot go with the copy either */
1564 peer_dst->regtrans = NULL;
1565 peer_dst->qualtrans = NULL;
1566 memset(&peer_dst->lookups, 0, sizeof(peer_dst->lookups));
1567
1568 memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1569 memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1570
1571 AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1572 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1573 continue;
1574
1575 perm->allow = cur->allow;
1576 strcpy(perm->name, cur->name);
1577
1578 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1579 }
1580
1581 AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1582 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1583 continue;
1584
1585 perm->allow = cur->allow;
1586 strcpy(perm->name, cur->name);
1587
1588 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1589 }
1590}
1591
1592static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1593{
1594 /* Handle canonical command / response */
1595 int final = hdr->cmdresp & 0x80;
1596 int cmd = hdr->cmdresp & 0x7f;
1597 int x,y,z;
1598 int resp;
1599 int res;
1600 int authpass=0;
1601 unsigned char *bufcpy;
1602#ifdef LOW_MEMORY
1603 struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
1604#else
1605 struct dundi_ie_data _ied = {
1606 .pos = 0,
1607 };
1608 struct dundi_ie_data *ied = &_ied;
1609#endif
1610 struct dundi_ies ies = {
1611 .eidcount = 0,
1612 };
1613 struct dundi_peer *peer = NULL;
1614 char eid_str[20];
1615 char eid_str2[20];
1616 int retval = -1;
1617
1618 if (!ied) {
1619 return -1;
1620 }
1621
1622 if (datalen) {
1623 bufcpy = ast_alloca(datalen);
1624 /* Make a copy for parsing */
1625 memcpy(bufcpy, hdr->ies, datalen);
1626 ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1627 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1628 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1629 goto return_cleanup;
1630 }
1631 }
1632 switch(cmd) {
1636 if (cmd == DUNDI_COMMAND_EIDQUERY)
1638 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1640 else
1642 /* A dialplan or entity discover -- qualify by highest level entity */
1643 peer = find_peer(ies.eids[0]);
1644 if (!peer) {
1646 dundi_send(trans, resp, 0, 1, ied);
1647 } else {
1648 int hasauth = 0;
1649 trans->us_eid = peer->us_eid;
1650 if (strlen(peer->inkey)) {
1651 hasauth = encrypted;
1652 } else
1653 hasauth = 1;
1654 if (hasauth) {
1655 /* Okay we're authentiated and all, now we check if they're authorized */
1656 if (!ies.called_context)
1657 ies.called_context = "e164";
1658 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1659 res = dundi_answer_entity(trans, &ies, ies.called_context);
1660 } else {
1661 if (ast_strlen_zero(ies.called_number)) {
1662 /* They're not permitted to access that context */
1663 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1664 dundi_send(trans, resp, 0, 1, ied);
1665 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1666 (peer->model & DUNDI_MODEL_INBOUND) &&
1667 has_permission(&peer->permit, ies.called_context)) {
1668 res = dundi_answer_query(trans, &ies, ies.called_context);
1669 if (res < 0) {
1670 /* There is no such dundi context */
1671 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1672 dundi_send(trans, resp, 0, 1, ied);
1673 }
1674 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1675 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1676 has_permission(&peer->include, ies.called_context)) {
1677 res = dundi_prop_precache(trans, &ies, ies.called_context);
1678 if (res < 0) {
1679 /* There is no such dundi context */
1680 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1681 dundi_send(trans, resp, 0, 1, ied);
1682 }
1683 } else {
1684 /* They're not permitted to access that context */
1685 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1686 dundi_send(trans, resp, 0, 1, ied);
1687 }
1688 }
1689 } else {
1690 /* They're not permitted to access that context */
1691 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1692 dundi_send(trans, resp, 0, 1, ied);
1693 }
1694 }
1695 break;
1697 /* A register request -- should only have one entity */
1698 peer = find_peer(ies.eids[0]);
1699
1700 /* if the peer is not found and we have a valid 'any_peer' setting */
1701 if (any_peer && peer == any_peer) {
1702 /* copy any_peer into a new peer object */
1703 peer = ast_calloc(1, sizeof(*peer));
1704 if (peer) {
1705 deep_copy_peer(peer, any_peer);
1706
1707 /* set EID to remote EID */
1708 peer->eid = *ies.eids[0];
1709
1713 }
1714 }
1715
1716 if (!peer || !peer->dynamic) {
1718 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1719 } else {
1720 int hasauth = 0;
1721 trans->us_eid = peer->us_eid;
1722 if (!ast_strlen_zero(peer->inkey)) {
1723 hasauth = encrypted;
1724 } else
1725 hasauth = 1;
1726 if (hasauth) {
1727 int expire = default_expiration;
1728 char data[256];
1729 int needqual = 0;
1731 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1732 snprintf(data, sizeof(data), "%s:%d", ast_sockaddr_stringify(&trans->addr), expire);
1733 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1734 if (ast_sockaddr_cmp(&peer->addr, &trans->addr)) {
1735 ast_verb(3, "Registered DUNDi peer '%s' at '%s'\n",
1736 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1737 ast_sockaddr_stringify(&trans->addr));
1738 needqual = 1;
1739 }
1740
1741 ast_sockaddr_copy(&peer->addr, &trans->addr);
1743 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1744 if (needqual)
1745 qualify_peer(peer, 1);
1746 }
1747 }
1748 break;
1750 /* A dialplan response, lets see what we got... */
1751 if (ies.cause < 1) {
1752 /* Success of some sort */
1753 ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1754 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1755 authpass = encrypted;
1756 } else
1757 authpass = 1;
1758 if (authpass) {
1759 /* Pass back up answers */
1760 if (trans->parent && trans->parent->dr) {
1761 y = trans->parent->respcount;
1762 for (x=0;x<ies.anscount;x++) {
1763 if (trans->parent->respcount < trans->parent->maxcount) {
1764 /* Make sure it's not already there */
1765 for (z=0;z<trans->parent->respcount;z++) {
1766 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1767 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1768 break;
1769 }
1770 if (z == trans->parent->respcount) {
1771 /* Copy into parent responses */
1772 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1773 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1774 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1775 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1776 if (ies.expiration > 0)
1777 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1778 else
1781 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1782 &ies.answers[x]->eid);
1783 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1784 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1786 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1787 trans->parent->respcount++;
1789 } else if (trans->parent->dr[z].weight > ntohs(ies.answers[x]->weight)) {
1790 /* Update weight if appropriate */
1791 trans->parent->dr[z].weight = ntohs(ies.answers[x]->weight);
1792 }
1793 } else
1794 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1795 trans->parent->number, trans->parent->dcontext);
1796 }
1797 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1798 the cache know if this request was unaffected by our entity list. */
1799 cache_save(&trans->them_eid, trans->parent, y,
1800 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1801 if (ies.hint) {
1802 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1806 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1807 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1808 sizeof(trans->parent->hmd->exten));
1809 }
1810 } else {
1812 }
1813 }
1814 if (ies.expiration > 0) {
1815 if (trans->parent->expiration > ies.expiration) {
1816 trans->parent->expiration = ies.expiration;
1817 }
1818 }
1819 }
1820 /* Close connection if not final */
1821 if (!final)
1822 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1823 }
1824
1825 } else {
1826 /* Auth failure, check for data */
1827 if (!final) {
1828 /* Cancel if they didn't already */
1829 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1830 }
1831 }
1832 break;
1834 /* A dialplan response, lets see what we got... */
1835 if (ies.cause < 1) {
1836 /* Success of some sort */
1837 ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
1838 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1839 authpass = encrypted;
1840 } else
1841 authpass = 1;
1842 if (authpass) {
1843 /* Pass back up answers */
1844 if (trans->parent && trans->parent->dei && ies.q_org) {
1845 if (!trans->parent->respcount) {
1846 trans->parent->respcount++;
1847 if (ies.q_dept)
1848 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1849 if (ies.q_org)
1850 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1851 if (ies.q_locality)
1852 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1853 if (ies.q_stateprov)
1854 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1855 if (ies.q_country)
1856 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1857 if (ies.q_email)
1858 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1859 if (ies.q_phone)
1860 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1861 if (ies.q_ipaddr)
1862 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1863 if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1864 /* If it's them, update our address */
1865 ast_copy_string(trans->parent->dei->ipaddr, ast_sockaddr_stringify_addr(&trans->addr), sizeof(trans->parent->dei->ipaddr));
1866 }
1867 }
1868 if (ies.hint) {
1871 }
1872 }
1873 /* Close connection if not final */
1874 if (!final)
1875 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1876 }
1877
1878 } else {
1879 /* Auth failure, check for data */
1880 if (!final) {
1881 /* Cancel if they didn't already */
1882 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1883 }
1884 }
1885 break;
1887 /* A dialplan response, lets see what we got... */
1888 if (ies.cause < 1) {
1889 int hasauth;
1890 /* Success of some sort */
1891 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1892 hasauth = encrypted;
1893 } else
1894 hasauth = 1;
1895
1896 if (!hasauth) {
1897 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1898 if (!final) {
1899 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1900 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
1901 }
1902 } else {
1903 ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
1904 ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1905 /* Close connection if not final */
1906 if (!final)
1907 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1908 }
1909 } else {
1910 /* Auth failure, cancel if they didn't for some reason */
1911 if (!final) {
1912 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1913 }
1914 }
1915 break;
1917 case DUNDI_COMMAND_NULL:
1919 /* Do nothing special */
1920 if (!final)
1921 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1922 break;
1924 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1925 /* No really, it's over at this point */
1926 if (!final)
1927 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1928 } else {
1929 /* Send with full key */
1931 if (final) {
1932 /* Ooops, we got a final message, start by sending ACK... */
1933 dundi_ack(trans, hdr->cmdresp & 0x80);
1934 trans->aseqno = trans->iseqno;
1935 /* Now, we gotta create a new transaction */
1936 if (!reset_transaction(trans)) {
1937 /* Make sure handle_frame doesn't destroy us */
1938 hdr->cmdresp &= 0x7f;
1939 /* Parse the message we transmitted */
1940 memset(&ies, 0, sizeof(ies));
1941 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1942 /* Reconstruct outgoing encrypted packet */
1943 memset(ied, 0, sizeof(*ied));
1946 dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1947 if (ies.encblock)
1949 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
1950 peer->sentfullkey = 1;
1951 }
1952 }
1953 }
1954 break;
1956 if (!encrypted) {
1957 /* No nested encryption! */
1958 if ((trans->iseqno == 1) && !trans->oseqno) {
1959 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1960 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1961 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1962 if (!final) {
1963 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1964 }
1965 break;
1966 }
1967 apply_peer(trans, peer);
1968 /* Key passed, use new contexts for this session */
1969 trans->ecx = peer->them_ecx;
1970 trans->dcx = peer->them_dcx;
1971 }
1972 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1973 struct dundi_hdr *dhdr;
1974 unsigned char decoded[MAX_PACKET_SIZE];
1975 int ddatalen;
1976 ddatalen = sizeof(decoded);
1977 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1978 if (dhdr) {
1979 /* Handle decrypted response */
1980 if (dundidebug)
1981 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1982 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1983 /* Carry back final flag */
1984 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1985 break;
1986 } else {
1987 ast_debug(1, "Ouch, decrypt failed :(\n");
1988 }
1989 }
1990 }
1991 if (!final) {
1992 /* Turn off encryption */
1994 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1995 }
1996 break;
1997 default:
1998 /* Send unknown command if we don't know it, with final flag IFF it's the
1999 first command in the dialog and only if we haven't received final notification */
2000 if (!final) {
2002 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
2003 }
2004 }
2005
2006 retval = 0;
2007
2008return_cleanup:
2009#ifdef LOW_MEMORY
2010 ast_free(ied);
2011#endif
2012 return retval;
2013}
2014
2015static void destroy_packet(struct dundi_packet *pack, int needfree);
2016static void destroy_packets(struct packetlist *p)
2017{
2018 struct dundi_packet *pack;
2019
2020 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
2022 ast_free(pack);
2023 }
2024}
2025
2026
2027static int ack_trans(struct dundi_transaction *trans, int iseqno)
2028{
2029 struct dundi_packet *pack;
2030
2031 /* Ack transmitted packet corresponding to iseqno */
2032 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
2033 if ((pack->h->oseqno + 1) % 255 == iseqno) {
2034 destroy_packet(pack, 0);
2035 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
2036 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
2037 destroy_packets(&trans->lasttrans);
2038 }
2039 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
2041 return 1;
2042 }
2043 }
2044
2045 return 0;
2046}
2047
2048static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datalen)
2049{
2050 struct dundi_transaction *trans;
2051 trans = find_transaction(h, sin);
2052 if (!trans) {
2053 dundi_reject(h, sin);
2054 return 0;
2055 }
2056 /* Got a transaction, see where this header fits in */
2057 if (h->oseqno == trans->iseqno) {
2058 /* Just what we were looking for... Anything but ack increments iseqno */
2059 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2060 /* If final, we're done */
2061 destroy_trans(trans, 0);
2062 return 0;
2063 }
2064 if (h->cmdresp != DUNDI_COMMAND_ACK) {
2065 trans->oiseqno = trans->iseqno;
2066 trans->iseqno++;
2067 handle_command_response(trans, h, datalen, 0);
2068 }
2069 if (trans->aseqno != trans->iseqno) {
2070 dundi_ack(trans, h->cmdresp & 0x80);
2071 trans->aseqno = trans->iseqno;
2072 }
2073 /* Delete any saved last transmissions */
2074 destroy_packets(&trans->lasttrans);
2075 if (h->cmdresp & 0x80) {
2076 /* Final -- destroy now */
2077 destroy_trans(trans, 0);
2078 }
2079 } else if (h->oseqno == trans->oiseqno) {
2080 /* Last incoming sequence number -- send ACK without processing */
2081 dundi_ack(trans, 0);
2082 } else {
2083 /* Out of window -- simply drop */
2084 ast_debug(1, "Dropping packet out of window!\n");
2085 }
2086 return 0;
2087}
2088
2089static int socket_read(int *id, int fd, short events, void *sock)
2090{
2091 struct ast_sockaddr sin;
2092 int res;
2093 struct dundi_hdr *h;
2094 char buf[MAX_PACKET_SIZE];
2095
2096 res = ast_recvfrom(*((int *)sock), buf, sizeof(buf), 0, &sin);
2097 if (res < 0) {
2098 if (errno != ECONNREFUSED)
2099 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2100 return 1;
2101 }
2102 if (res < sizeof(struct dundi_hdr)) {
2103 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2104 return 1;
2105 }
2106 buf[res] = '\0';
2107 h = (struct dundi_hdr *) buf;
2108 if (dundidebug)
2109 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2111 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2113 return 1;
2114}
2115
2116static void build_secret(char *secret, int seclen)
2117{
2118 unsigned char tmp[16];
2119 char *s;
2120 build_iv(tmp);
2121 secret[0] = '\0';
2122 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2123 /* Eliminate potential bad characters */
2124 while((s = strchr(secret, ';'))) *s = '+';
2125 while((s = strchr(secret, '/'))) *s = '+';
2126 while((s = strchr(secret, ':'))) *s = '+';
2127 while((s = strchr(secret, '@'))) *s = '+';
2128}
2129
2130
2131static void save_secret(const char *newkey, const char *oldkey)
2132{
2133 char tmp[350];
2134 if (oldkey)
2135 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2136 else
2137 snprintf(tmp, sizeof(tmp), "%s", newkey);
2139 ast_db_put(secretpath, "secret", tmp);
2140 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2141 ast_db_put(secretpath, "secretexpiry", tmp);
2142}
2143
2144static void load_password(void)
2145{
2146 char *current=NULL;
2147 char *last=NULL;
2148 char tmp[256];
2149 time_t expired;
2150
2151 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2152 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2153 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2154 current = strchr(tmp, ';');
2155 if (!current)
2156 current = tmp;
2157 else {
2158 *current = '\0';
2159 current++;
2160 };
2161 if ((time(NULL) - expired) < 0) {
2162 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2163 expired = time(NULL) + DUNDI_SECRET_TIME;
2164 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2165 last = current;
2166 current = NULL;
2167 } else {
2168 last = NULL;
2169 current = NULL;
2170 }
2171 }
2172 if (current) {
2173 /* Current key is still valid, just setup rotation properly */
2175 rotatetime = expired;
2176 } else {
2177 /* Current key is out of date, rotate or eliminate all together */
2180 }
2181}
2182
2183static void check_password(void)
2184{
2185 char oldsecret[80];
2186 time_t now;
2187
2188 time(&now);
2189#if 0
2190 printf("%ld/%ld\n", now, rotatetime);
2191#endif
2192 if ((now - rotatetime) >= 0) {
2193 /* Time to rotate keys */
2194 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2196 save_secret(cursecret, oldsecret);
2197 }
2198}
2199
2200static void *network_thread(void *ignore)
2201{
2202 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2203 from the network, and queue them for delivery to the channels */
2204 int res;
2205 /* Establish I/O callback for socket read */
2206 int *socket_read_id = ast_io_add(io, netsocket, socket_read, AST_IO_IN, &netsocket);
2207 int *socket_read_id2 = NULL;
2208 if (netsocket2 >= 0) {
2209 socket_read_id2 = ast_io_add(io, netsocket2, socket_read, AST_IO_IN, &netsocket2);
2210 }
2211
2212 while (!dundi_shutdown) {
2213 res = ast_sched_wait(sched);
2214 if ((res > 1000) || (res < 0))
2215 res = 1000;
2216 res = ast_io_wait(io, res);
2217 if (res >= 0) {
2221 }
2223 }
2224
2225 ast_io_remove(io, socket_read_id);
2226
2227 if (socket_read_id2) {
2228 ast_io_remove(io, socket_read_id2);
2229 }
2230
2231 return NULL;
2232}
2233
2234static void *process_clearcache(void *ignore)
2235{
2236 struct ast_db_entry *db_entry, *db_tree;
2237 int striplen = sizeof("/dundi/cache");
2238 time_t now;
2239
2240 while (!dundi_shutdown) {
2241 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2242
2243 time(&now);
2244
2245 db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
2246 for (; db_entry; db_entry = db_entry->next) {
2247 time_t expiry;
2248
2249 if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
2250 if (expiry < now) {
2251 ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
2252 ast_db_del("dundi/cache", db_entry->key + striplen);
2253 }
2254 }
2255 }
2256 ast_db_freetree(db_tree);
2257
2258 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2259 pthread_testcancel();
2260 sleep(60);
2261 pthread_testcancel();
2262 }
2263
2264 return NULL;
2265}
2266
2267static void *process_precache(void *ign)
2268{
2269 struct dundi_precache_queue *qe;
2270 time_t now;
2271 char context[256];
2272 char number[256];
2273 int run;
2274
2275 while (!dundi_shutdown) {
2276 time(&now);
2277 run = 0;
2279 if ((qe = AST_LIST_FIRST(&pcq))) {
2280 if (!qe->expiration) {
2281 /* Gone... Remove... */
2283 ast_free(qe);
2284 } else if (qe->expiration < now) {
2285 /* Process this entry */
2286 qe->expiration = 0;
2287 ast_copy_string(context, qe->context, sizeof(context));
2288 ast_copy_string(number, qe->number, sizeof(number));
2289 run = 1;
2290 }
2291 }
2293 if (run) {
2295 } else
2296 sleep(1);
2297 }
2298
2299 return NULL;
2300}
2301
2302static int start_network_thread(void)
2303{
2307 return 0;
2308}
2309
2310static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2311{
2312 switch (cmd) {
2313 case CLI_INIT:
2314 e->command = "dundi set debug {on|off}";
2315 e->usage =
2316 "Usage: dundi set debug {on|off}\n"
2317 " Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2318 return NULL;
2319 case CLI_GENERATE:
2320 return NULL;
2321 }
2322
2323 if (a->argc != e->args) {
2324 return CLI_SHOWUSAGE;
2325 }
2326 if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2327 dundidebug = 1;
2328 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2329 } else {
2330 dundidebug = 0;
2331 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2332 }
2333 return CLI_SUCCESS;
2334}
2335
2336static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2337{
2338 switch (cmd) {
2339 case CLI_INIT:
2340 e->command = "dundi store history {on|off}";
2341 e->usage =
2342 "Usage: dundi store history {on|off}\n"
2343 " Enables/Disables storing of DUNDi requests and times for debugging\n"
2344 "purposes\n";
2345 return NULL;
2346 case CLI_GENERATE:
2347 return NULL;
2348 }
2349
2350 if (a->argc != e->args) {
2351 return CLI_SHOWUSAGE;
2352 }
2353 if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2355 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2356 } else {
2358 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2359 }
2360 return CLI_SUCCESS;
2361}
2362
2363static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2364{
2365 int stats = 0;
2366 switch (cmd) {
2367 case CLI_INIT:
2368 e->command = "dundi flush [stats]";
2369 e->usage =
2370 "Usage: dundi flush [stats]\n"
2371 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2372 "'stats' is present, clears timer statistics instead of normal\n"
2373 "operation.\n";
2374 return NULL;
2375 case CLI_GENERATE:
2376 return NULL;
2377 }
2378 if ((a->argc < 2) || (a->argc > 3)) {
2379 return CLI_SHOWUSAGE;
2380 }
2381 if (a->argc > 2) {
2382 if (!strcasecmp(a->argv[2], "stats")) {
2383 stats = 1;
2384 } else {
2385 return CLI_SHOWUSAGE;
2386 }
2387 }
2388 if (stats) {
2389 /* Flush statistics */
2390 struct dundi_peer *p;
2391 int x;
2394 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2395 ast_free(p->lookups[x]);
2396 p->lookups[x] = NULL;
2397 p->lookuptimes[x] = 0;
2398 }
2399 p->avgms = 0;
2400 }
2402 } else {
2403 ast_db_deltree("dundi/cache", NULL);
2404 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2405 }
2406 return CLI_SUCCESS;
2407}
2408
2409static char *model2str(int model)
2410{
2411 switch(model) {
2413 return "Inbound";
2415 return "Outbound";
2417 return "Symmetric";
2418 default:
2419 return "Unknown";
2420 }
2421}
2422
2423static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2424{
2425 int which=0, len;
2426 char *ret = NULL;
2427 struct dundi_peer *p;
2428 char eid_str[20];
2429
2430 if (pos != rpos)
2431 return NULL;
2433 len = strlen(word);
2435 const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2436 if (!strncasecmp(word, s, len) && ++which > state) {
2437 ret = ast_strdup(s);
2438 break;
2439 }
2440 }
2442 return ret;
2443}
2444
2445static int rescomp(const void *a, const void *b)
2446{
2447 const struct dundi_result *resa, *resb;
2448 resa = a;
2449 resb = b;
2450 if (resa->weight < resb->weight)
2451 return -1;
2452 if (resa->weight > resb->weight)
2453 return 1;
2454 return 0;
2455}
2456
2457static void sort_results(struct dundi_result *results, int count)
2458{
2459 qsort(results, count, sizeof(results[0]), rescomp);
2460}
2461
2462static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2463{
2464 int res;
2465 char tmp[256];
2466 char fs[80] = "";
2467 char *context;
2468 int x;
2469 int bypass = 0;
2470 struct dundi_result dr[MAX_RESULTS];
2471 struct timeval start;
2472 switch (cmd) {
2473 case CLI_INIT:
2474 e->command = "dundi lookup";
2475 e->usage =
2476 "Usage: dundi lookup <number>[@context] [bypass]\n"
2477 " Lookup the given number within the given DUNDi context\n"
2478 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2479 "keyword is specified.\n";
2480 return NULL;
2481 case CLI_GENERATE:
2482 return NULL;
2483 }
2484
2485 if ((a->argc < 3) || (a->argc > 4)) {
2486 return CLI_SHOWUSAGE;
2487 }
2488 if (a->argc > 3) {
2489 if (!strcasecmp(a->argv[3], "bypass")) {
2490 bypass=1;
2491 } else {
2492 return CLI_SHOWUSAGE;
2493 }
2494 }
2495 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2496 context = strchr(tmp, '@');
2497 if (context) {
2498 *context = '\0';
2499 context++;
2500 }
2501 start = ast_tvnow();
2502 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2503
2504 if (res < 0)
2505 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2506 else if (!res)
2507 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2508 else
2509 sort_results(dr, res);
2510 for (x=0;x<res;x++) {
2511 ast_cli(a->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));
2512 ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2513 }
2514 ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2515 return CLI_SUCCESS;
2516}
2517
2518static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2519{
2520 int res;
2521 char tmp[256];
2522 char *context;
2523 struct timeval start;
2524 switch (cmd) {
2525 case CLI_INIT:
2526 e->command = "dundi precache";
2527 e->usage =
2528 "Usage: dundi precache <number>[@context]\n"
2529 " Lookup the given number within the given DUNDi context\n"
2530 "(or e164 if none is specified) and precaches the results to any\n"
2531 "upstream DUNDi push servers.\n";
2532 return NULL;
2533 case CLI_GENERATE:
2534 return NULL;
2535 }
2536 if ((a->argc < 3) || (a->argc > 3)) {
2537 return CLI_SHOWUSAGE;
2538 }
2539 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2540 context = strchr(tmp, '@');
2541 if (context) {
2542 *context = '\0';
2543 context++;
2544 }
2545 start = ast_tvnow();
2546 res = dundi_precache(context, tmp);
2547
2548 if (res < 0)
2549 ast_cli(a->fd, "DUNDi precache returned error.\n");
2550 else if (!res)
2551 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2552 ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2553 return CLI_SUCCESS;
2554}
2555
2556static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2557{
2558 int res;
2559 char tmp[256];
2560 char *context;
2561 dundi_eid eid;
2562 struct dundi_entity_info dei;
2563 switch (cmd) {
2564 case CLI_INIT:
2565 e->command = "dundi query";
2566 e->usage =
2567 "Usage: dundi query <entity>[@context]\n"
2568 " Attempts to retrieve contact information for a specific\n"
2569 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2570 "e164 if none is specified).\n";
2571 return NULL;
2572 case CLI_GENERATE:
2573 return NULL;
2574 }
2575 if ((a->argc < 3) || (a->argc > 3)) {
2576 return CLI_SHOWUSAGE;
2577 }
2578 if (ast_str_to_eid(&eid, a->argv[2])) {
2579 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2580 return CLI_SHOWUSAGE;
2581 }
2582 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2583 context = strchr(tmp, '@');
2584 if (context) {
2585 *context = '\0';
2586 context++;
2587 }
2588 res = dundi_query_eid(&dei, context, eid);
2589 if (res < 0)
2590 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2591 else if (!res)
2592 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2593 else {
2594 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2595 ast_cli(a->fd, "Department: %s\n", dei.orgunit);
2596 ast_cli(a->fd, "Organization: %s\n", dei.org);
2597 ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
2598 ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
2599 ast_cli(a->fd, "Country: %s\n", dei.country);
2600 ast_cli(a->fd, "E-mail: %s\n", dei.email);
2601 ast_cli(a->fd, "Phone: %s\n", dei.phone);
2602 ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
2603 }
2604 return CLI_SUCCESS;
2605}
2606
2607static char *dundi_sockaddr_stringify_host(const struct ast_sockaddr *addr)
2608{
2609 if (ast_sockaddr_isnull(addr)) {
2610 return "(Unspecified)";
2611 }
2612 return ast_sockaddr_stringify_host(addr);
2613}
2614
2615static uint16_t dundi_sockaddr_port(const struct ast_sockaddr *addr)
2616{
2617 /*
2618 * Test to avoid a debug message complaining about addr
2619 * not being an IPv4 or IPv6 address.
2620 */
2621 if (ast_sockaddr_isnull(addr)) {
2622 return 0;
2623 }
2624 return ast_sockaddr_port(addr);
2625}
2626
2627static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2628{
2629 struct dundi_peer *peer;
2630 struct permission *p;
2631 char *order;
2632 char eid_str[20];
2633 int x, cnt;
2634 switch (cmd) {
2635 case CLI_INIT:
2636 e->command = "dundi show peer";
2637 e->usage =
2638 "Usage: dundi show peer [peer]\n"
2639 " Provide a detailed description of a specifid DUNDi peer.\n";
2640 return NULL;
2641 case CLI_GENERATE:
2642 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2643 }
2644 if (a->argc != 4) {
2645 return CLI_SHOWUSAGE;
2646 }
2648 AST_LIST_TRAVERSE(&peers, peer, list) {
2649 if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2650 break;
2651 }
2652 if (peer) {
2653 switch(peer->order) {
2654 case 0:
2655 order = "Primary";
2656 break;
2657 case 1:
2658 order = "Secondary";
2659 break;
2660 case 2:
2661 order = "Tertiary";
2662 break;
2663 case 3:
2664 order = "Quartiary";
2665 break;
2666 default:
2667 order = "Unknown";
2668 }
2669 ast_cli(a->fd, "Peer: %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2670 ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
2671 ast_cli(a->fd, "Order: %s\n", order);
2672 ast_cli(a->fd, "Host: %s\n", ast_sockaddr_isnull(&peer->addr) ? "<Unspecified>" : ast_sockaddr_stringify_host(&peer->addr));
2673 ast_cli(a->fd, "Port: %d\n", dundi_sockaddr_port(&peer->addr));
2674 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2675 ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2676 ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2677 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2678 if (!AST_LIST_EMPTY(&peer->include))
2679 ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2680 AST_LIST_TRAVERSE(&peer->include, p, list)
2681 ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2682 if (!AST_LIST_EMPTY(&peer->permit))
2683 ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2684 AST_LIST_TRAVERSE(&peer->permit, p, list)
2685 ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2686 cnt = 0;
2687 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2688 if (peer->lookups[x]) {
2689 if (!cnt)
2690 ast_cli(a->fd, "Last few query times:\n");
2691 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2692 cnt++;
2693 }
2694 }
2695 if (cnt)
2696 ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2697 } else
2698 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2700 return CLI_SUCCESS;
2701}
2702
2703static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2704{
2705#define FORMAT2 "%-20.20s %-41s %-6.6s %-10.10s %-8.8s %-15.15s\n"
2706#define FORMAT "%-20.20s %-41s %s %-6d %-10.10s %-8.8s %-15.15s\n"
2707 struct dundi_peer *peer;
2708 int registeredonly=0;
2709 char avgms[20];
2710 char eid_str[20];
2711 int online_peers = 0;
2712 int offline_peers = 0;
2713 int unmonitored_peers = 0;
2714 int total_peers = 0;
2715 switch (cmd) {
2716 case CLI_INIT:
2717 e->command = "dundi show peers [registered|include|exclude|begin]";
2718 e->usage =
2719 "Usage: dundi show peers [registered|include|exclude|begin]\n"
2720 " Lists all known DUNDi peers.\n"
2721 " If 'registered' is present, only registered peers are shown.\n";
2722 return NULL;
2723 case CLI_GENERATE:
2724 return NULL;
2725 }
2726
2727 if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
2728 return CLI_SHOWUSAGE;
2729 }
2730 if ((a->argc == 4)) {
2731 if (!strcasecmp(a->argv[3], "registered")) {
2732 registeredonly = 1;
2733 } else {
2734 return CLI_SHOWUSAGE;
2735 }
2736 }
2738 ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
2739 AST_LIST_TRAVERSE(&peers, peer, list) {
2740 char status[64];
2741 int print_line = -1;
2742 char srch[2000];
2743
2744 total_peers++;
2745 if (registeredonly && ast_sockaddr_isnull(&peer->addr)) {
2746 continue;
2747 }
2748 if (peer->maxms) {
2749 if (peer->lastms < 0) {
2750 strcpy(status, "UNREACHABLE");
2751 offline_peers++;
2752 }
2753 else if (peer->lastms > peer->maxms) {
2754 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2755 offline_peers++;
2756 }
2757 else if (peer->lastms) {
2758 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2759 online_peers++;
2760 }
2761 else {
2762 strcpy(status, "UNKNOWN");
2763 offline_peers++;
2764 }
2765 } else {
2766 strcpy(status, "Unmonitored");
2767 unmonitored_peers++;
2768 }
2769 if (peer->avgms)
2770 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2771 else
2772 strcpy(avgms, "Unavail");
2773 snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str),
2774 &peer->eid), dundi_sockaddr_stringify_host(&peer->addr),
2775 peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2776
2777 if (a->argc == 5) {
2778 if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2779 print_line = -1;
2780 } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2781 print_line = 1;
2782 } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2783 print_line = -1;
2784 } else {
2785 print_line = 0;
2786 }
2787 }
2788
2789 if (print_line) {
2790 ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2792 peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2793 }
2794 }
2795 ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2797 return CLI_SUCCESS;
2798#undef FORMAT
2799#undef FORMAT2
2800}
2801
2802static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2803{
2804#define FORMAT2 "%-47s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2805#define FORMAT "%-41s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2806 struct dundi_transaction *trans;
2807 switch (cmd) {
2808 case CLI_INIT:
2809 e->command = "dundi show trans";
2810 e->usage =
2811 "Usage: dundi show trans\n"
2812 " Lists all known DUNDi transactions.\n";
2813 return NULL;
2814 case CLI_GENERATE:
2815 return NULL;
2816 }
2817 if (a->argc != 3) {
2818 return CLI_SHOWUSAGE;
2819 }
2821 ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2822 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2824 ast_sockaddr_port(&trans->addr), trans->strans, trans->dtrans,
2825 trans->oseqno, trans->iseqno, trans->aseqno);
2826 }
2828 return CLI_SUCCESS;
2829#undef FORMAT
2830#undef FORMAT2
2831}
2832
2833static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2834{
2835 char eid_str[20];
2836 switch (cmd) {
2837 case CLI_INIT:
2838 e->command = "dundi show entityid";
2839 e->usage =
2840 "Usage: dundi show entityid\n"
2841 " Displays the global entityid for this host.\n";
2842 return NULL;
2843 case CLI_GENERATE:
2844 return NULL;
2845 }
2846 if (a->argc != 3) {
2847 return CLI_SHOWUSAGE;
2848 }
2850 ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2852 ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2853 return CLI_SUCCESS;
2854}
2855
2856static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2857{
2858#define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2859#define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2860 struct dundi_request *req;
2861 char eidstr[20];
2862 switch (cmd) {
2863 case CLI_INIT:
2864 e->command = "dundi show requests";
2865 e->usage =
2866 "Usage: dundi show requests\n"
2867 " Lists all known pending DUNDi requests.\n";
2868 return NULL;
2869 case CLI_GENERATE:
2870 return NULL;
2871 }
2872 if (a->argc != 3) {
2873 return CLI_SHOWUSAGE;
2874 }
2876 ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2878 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2879 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2880 }
2882 return CLI_SUCCESS;
2883#undef FORMAT
2884#undef FORMAT2
2885}
2886
2887/* Grok-a-dial DUNDi */
2888
2889static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2890{
2891#define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2892#define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2893 struct dundi_mapping *map;
2894 char fs[256];
2895 char weight[8];
2896 switch (cmd) {
2897 case CLI_INIT:
2898 e->command = "dundi show mappings";
2899 e->usage =
2900 "Usage: dundi show mappings\n"
2901 " Lists all known DUNDi mappings.\n";
2902 return NULL;
2903 case CLI_GENERATE:
2904 return NULL;
2905 }
2906 if (a->argc != 3) {
2907 return CLI_SHOWUSAGE;
2908 }
2910 ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2912 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map, NULL));
2913 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2914 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2915 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2916 }
2918 return CLI_SUCCESS;
2919#undef FORMAT
2920#undef FORMAT2
2921}
2922
2923static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2924{
2925#define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2926#define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2927 struct dundi_precache_queue *qe;
2928 int h,m,s;
2929 time_t now;
2930 switch (cmd) {
2931 case CLI_INIT:
2932 e->command = "dundi show precache";
2933 e->usage =
2934 "Usage: dundi show precache\n"
2935 " Lists all known DUNDi scheduled precache updates.\n";
2936 return NULL;
2937 case CLI_GENERATE:
2938 return NULL;
2939 }
2940 if (a->argc != 3) {
2941 return CLI_SHOWUSAGE;
2942 }
2943 time(&now);
2944 ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2946 AST_LIST_TRAVERSE(&pcq, qe, list) {
2947 s = qe->expiration - now;
2948 h = s / 3600;
2949 s = s % 3600;
2950 m = s / 60;
2951 s = s % 60;
2952 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2953 }
2955
2956 return CLI_SUCCESS;
2957#undef FORMAT
2958#undef FORMAT2
2959}
2960
2961static char *dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2962{
2963#define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s %-7s %s\n"
2964#define FORMAT "%-12.12s %-16.16s %6d sec %-18s %-7d %s/%s (%s)\n"
2965 struct ast_db_entry *db_tree, *db_entry;
2966 int cnt = 0;
2967 time_t ts, now;
2968 dundi_eid src_eid;
2969 char src_eid_str[20];
2970 int expiry, tech, weight;
2971 struct ast_flags flags;
2972 char fs[256];
2973 int length;
2974 char *ptr, *term, *src, *number, *context, *dst;
2975
2976 switch (cmd) {
2977 case CLI_INIT:
2978 e->command = "dundi show cache";
2979 e->usage =
2980 "Usage: dundi show cache\n"
2981 " Lists all DUNDi cache entries.\n";
2982 return NULL;
2983 case CLI_GENERATE:
2984 return NULL;
2985 }
2986
2987 if (a->argc != 3) {
2988 return CLI_SHOWUSAGE;
2989 }
2990
2991 time(&now);
2992 db_tree = ast_db_gettree("dundi/cache", NULL);
2993 ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration", "From", "Weight", "Destination (Flags)");
2994 for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
2995 char *rest;
2996
2997 if ((strncmp(db_entry->key, "/dundi/cache/hint/", 18) == 0) || ast_get_time_t(db_entry->data, &ts, 0, &length)) {
2998 continue;
2999 }
3000
3001 expiry = ts - now;
3002
3003 if (expiry <= 0) {
3004 continue;
3005 }
3006
3007 ptr = db_entry->key + sizeof("/dundi/cache");
3008 strtok_r(ptr, "/", &rest);
3009 number = strtok_r(NULL, "/", &rest);
3010 context = strtok_r(NULL, "/", &rest);
3011 ptr = strtok_r(NULL, "/", &rest);
3012
3013 if (*ptr != 'e') {
3014 continue;
3015 }
3016
3017 ptr = db_entry->data + length + 1;
3018
3019 if ((sscanf(ptr, "%30u/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) != 3)) {
3020 continue;
3021 }
3022
3023 ptr += length;
3024 dst = ptr;
3025 term = strchr(ptr, '|');
3026
3027 if (!term) {
3028 continue;
3029 }
3030
3031 /* Ok, at this point we know we aren't going to skp the entry, so we go ahead and increment the count. */
3032 cnt++;
3033
3034 *term = '\0';
3035 src = strrchr(ptr, '/');
3036 dundi_eid_zero(&src_eid);
3037
3038 if (src) {
3039 *src = '\0';
3040 src++;
3041 dundi_str_short_to_eid(&src_eid, src);
3042 ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3043 }
3044
3045 ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str, weight, tech2str(tech), dst, dundi_flags2str(fs, sizeof(fs), flags.flags));
3046 }
3047
3048 ast_cli(a->fd, "Number of entries: %d\n", cnt);
3049 ast_db_freetree(db_tree);
3050
3051 return CLI_SUCCESS;
3052#undef FORMAT
3053#undef FORMAT2
3054}
3055
3056static char *dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3057{
3058#define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s\n"
3059#define FORMAT "%-12.12s %-16.16s %6d sec %-18s\n"
3060 struct ast_db_entry *db_tree, *db_entry;
3061 int cnt = 0;
3062 time_t ts, now;
3063 dundi_eid src_eid;
3064 char src_eid_str[20];
3065 int expiry;
3066 int length;
3067 char *ptr, *src, *number, *context;
3068
3069 switch (cmd) {
3070 case CLI_INIT:
3071 e->command = "dundi show hints";
3072 e->usage =
3073 "Usage: dundi show hints\n"
3074 " Lists all DUNDi 'DONTASK' hints in the cache.\n";
3075 return NULL;
3076 case CLI_GENERATE:
3077 return NULL;
3078 }
3079
3080 if (a->argc != 3) {
3081 return CLI_SHOWUSAGE;
3082 }
3083
3084 time(&now);
3085 db_tree = ast_db_gettree("dundi/cache/hint", NULL);
3086 ast_cli(a->fd, FORMAT2, "Prefix", "Context", "Expiration", "From");
3087
3088 for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
3089 char *rest = NULL;
3090
3091 if (ast_get_time_t(db_entry->data, &ts, 0, &length)) {
3092 continue;
3093 }
3094
3095 expiry = ts - now;
3096
3097 if (expiry <= 0) {
3098 continue;
3099 }
3100
3101 ptr = db_entry->key + sizeof("/dundi/cache/hint");
3102 src = strtok_r(ptr, "/", &rest);
3103 number = strtok_r(NULL, "/", &rest);
3104 context = strtok_r(NULL, "/", &rest);
3105 ptr = strtok_r(NULL, "/", &rest);
3106
3107 if (*ptr != 'e') {
3108 continue;
3109 }
3110
3111 cnt++;
3112 dundi_str_short_to_eid(&src_eid, src);
3113 ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3114 ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str);
3115 }
3116
3117 ast_cli(a->fd, "Number of entries: %d\n", cnt);
3118 ast_db_freetree(db_tree);
3119
3120 return CLI_SUCCESS;
3121#undef FORMAT
3122#undef FORMAT2
3123}
3124
3125static struct ast_cli_entry cli_dundi[] = {
3126 AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging"),
3127 AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records"),
3128 AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
3129 AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
3130 AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
3131 AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
3132 AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
3133 AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
3134 AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
3135 AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
3136 AST_CLI_DEFINE(dundi_show_cache, "Show DUNDi cache"),
3137 AST_CLI_DEFINE(dundi_show_hints, "Show DUNDi hints in the cache"),
3138 AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
3139 AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
3140 AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
3141};
3142
3144{
3145 struct dundi_transaction *trans;
3146 int tid;
3147
3148 /* Don't allow creation of transactions to non-registered peers */
3149 if (p && ast_sockaddr_isnull(&p->addr)) {
3150 return NULL;
3151 }
3152 tid = get_trans_id();
3153 if (tid < 1)
3154 return NULL;
3155 if (!(trans = ast_calloc(1, sizeof(*trans))))
3156 return NULL;
3157
3158 if (global_storehistory) {
3159 trans->start = ast_tvnow();
3161 }
3163 trans->autokillid = -1;
3164 if (p) {
3165 apply_peer(trans, p);
3166 if (!p->sentfullkey)
3168 }
3169 trans->strans = tid;
3171
3172 return trans;
3173}
3174
3175static int dundi_xmit(struct dundi_packet *pack)
3176{
3177 int res;
3178 if (dundidebug)
3179 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
3180
3181 if (netsocket2 < 0) {
3182 res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
3183 } else {
3184 if (ast_sockaddr_is_ipv4(&pack->parent->addr)) {
3185 res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
3186 } else {
3187 res = ast_sendto(netsocket2, pack->data, pack->datalen, 0, &pack->parent->addr);
3188 }
3189 }
3190
3191 if (res < 0) {
3192 ast_log(LOG_WARNING, "Failed to transmit to '%s': %s\n",
3193 ast_sockaddr_stringify(&pack->parent->addr), strerror(errno));
3194 }
3195 if (res > 0)
3196 res = 0;
3197 return res;
3198}
3199
3200static void destroy_packet(struct dundi_packet *pack, int needfree)
3201{
3202 if (pack->parent)
3203 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
3205 if (needfree)
3206 ast_free(pack);
3207}
3208
3209static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
3210{
3211 struct dundi_peer *peer;
3212 int ms;
3213 int x;
3214 int cnt;
3215 char eid_str[20];
3217 AST_LIST_TRAVERSE(&peers, peer, list) {
3218 if (peer->regtrans == trans)
3219 peer->regtrans = NULL;
3220 if (peer->qualtrans == trans) {
3221 if (fromtimeout) {
3222 if (peer->lastms > -1)
3223 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3224 peer->lastms = -1;
3225 } else {
3226 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
3227 if (ms < 1)
3228 ms = 1;
3229 if (ms < peer->maxms) {
3230 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
3231 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3232 } else if (peer->lastms < peer->maxms) {
3233 ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
3234 }
3235 peer->lastms = ms;
3236 }
3237 peer->qualtrans = NULL;
3238 }
3239 if (ast_test_flag(trans, FLAG_STOREHIST)) {
3240 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
3241 if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
3242 peer->avgms = 0;
3243 cnt = 0;
3245 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
3246 peer->lookuptimes[x] = peer->lookuptimes[x-1];
3247 peer->lookups[x] = peer->lookups[x-1];
3248 if (peer->lookups[x]) {
3249 peer->avgms += peer->lookuptimes[x];
3250 cnt++;
3251 }
3252 }
3253 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
3254 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
3255 if (peer->lookups[0]) {
3256 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
3257 peer->avgms += peer->lookuptimes[0];
3258 cnt++;
3259 }
3260 if (cnt)
3261 peer->avgms /= cnt;
3262 }
3263 }
3264 }
3265 }
3266 }
3267 if (trans->parent) {
3268 /* Unlink from parent if appropriate */
3269 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
3270 if (AST_LIST_EMPTY(&trans->parent->trans)) {
3271 /* Wake up sleeper */
3272 if (trans->parent->pfds[1] > -1) {
3273 if (write(trans->parent->pfds[1], "killa!", 6) < 0) {
3274 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
3275 }
3276 }
3277 }
3278 }
3279 /* Unlink from all trans */
3280 AST_LIST_REMOVE(&alltrans, trans, all);
3281 destroy_packets(&trans->packets);
3282 destroy_packets(&trans->lasttrans);
3284 if (trans->thread) {
3285 /* If used by a thread, mark as dead and be done */
3286 ast_set_flag(trans, FLAG_DEAD);
3287 } else
3288 ast_free(trans);
3289}
3290
3291static int dundi_rexmit(const void *data)
3292{
3293 struct dundi_packet *pack = (struct dundi_packet *)data;
3294 int res;
3296 if (pack->retrans < 1) {
3297 pack->retransid = -1;
3298 if (!ast_test_flag(pack->parent, FLAG_ISQUAL)) {
3299 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s' msg %d on call %d\n",
3300 ast_sockaddr_stringify(&pack->parent->addr), pack->h->oseqno, ntohs(pack->h->strans));
3301 }
3302 destroy_trans(pack->parent, 1);
3303 res = 0;
3304 } else {
3305 /* Decrement retransmission, try again */
3306 pack->retrans--;
3307 dundi_xmit(pack);
3308 res = 1;
3309 }
3311 return res;
3312}
3313
3314static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
3315{
3316 struct dundi_packet *pack;
3317 int res;
3318 int len;
3319 char eid_str[20];
3320 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
3321 /* Reserve enough space for encryption */
3322 if (ast_test_flag(trans, FLAG_ENCRYPT))
3323 len += 384;
3324 pack = ast_calloc(1, len);
3325 if (pack) {
3326 pack->h = (struct dundi_hdr *)(pack->data);
3327 pack->retransid = -1;
3328 if (cmdresp != DUNDI_COMMAND_ACK) {
3329 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
3330 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3331 AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
3332 }
3333 pack->parent = trans;
3334 pack->h->strans = htons(trans->strans);
3335 pack->h->dtrans = htons(trans->dtrans);
3336 pack->h->iseqno = trans->iseqno;
3337 pack->h->oseqno = trans->oseqno;
3338 pack->h->cmdresp = cmdresp;
3339 pack->datalen = sizeof(struct dundi_hdr);
3340 if (ied) {
3341 memcpy(pack->h->ies, ied->buf, ied->pos);
3342 pack->datalen += ied->pos;
3343 }
3344 if (final) {
3345 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3346 ast_set_flag(trans, FLAG_FINAL);
3347 }
3348 pack->h->cmdflags = flags;
3349 if (cmdresp != DUNDI_COMMAND_ACK) {
3350 trans->oseqno++;
3351 trans->oseqno = trans->oseqno % 256;
3352 }
3353 trans->aseqno = trans->iseqno;
3354 /* If we have their public key, encrypt */
3355 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3356 switch(cmdresp) {
3365 if (dundidebug)
3366 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3367 res = dundi_encrypt(trans, pack);
3368 break;
3369 default:
3370 res = 0;
3371 }
3372 } else
3373 res = 0;
3374 if (!res)
3375 res = dundi_xmit(pack);
3376 if (res)
3377 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3378
3380 ast_free(pack);
3381 return res;
3382 }
3383 return -1;
3384}
3385
3386static int do_autokill(const void *data)
3387{
3388 struct dundi_transaction *trans = (struct dundi_transaction *)data;
3389 char eid_str[20];
3390 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3391 ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3392 trans->autokillid = -1;
3393 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3394 return 0;
3395}
3396
3398{
3399 struct dundi_peer *p;
3400 if (!ast_eid_cmp(eid, us)) {
3402 return;
3403 }
3406 if (!ast_eid_cmp(&p->eid, eid)) {
3407 if (has_permission(&p->include, context))
3409 else
3411 break;
3412 }
3413 }
3414 if (!p)
3417}
3418
3419static int dundi_discover(struct dundi_transaction *trans)
3420{
3421 struct dundi_ie_data ied;
3422 int x;
3423 if (!trans->parent) {
3424 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3425 return -1;
3426 }
3427 memset(&ied, 0, sizeof(ied));
3429 if (!dundi_eid_zero(&trans->us_eid))
3431 for (x=0;x<trans->eidcount;x++)
3432 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3435 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3436 if (trans->parent->cbypass)
3438 if (trans->autokilltimeout)
3439 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3440 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3441}
3442
3443static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3444{
3445 struct dundi_ie_data ied;
3446 int x, res;
3447 int max = 999999;
3448 int expiration = dundi_cache_time;
3449 int ouranswers=0;
3450 dundi_eid *avoid[1] = { NULL, };
3451 int direct[1] = { 0, };
3452 struct dundi_result dr[MAX_RESULTS];
3453 struct dundi_hint_metadata hmd;
3454 if (!trans->parent) {
3455 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3456 return -1;
3457 }
3458 memset(&hmd, 0, sizeof(hmd));
3459 memset(&dr, 0, sizeof(dr));
3460 /* Look up the answers we're going to include */
3461 for (x=0;x<mapcount;x++)
3462 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3463 if (ouranswers < 0)
3464 ouranswers = 0;
3465 for (x=0;x<ouranswers;x++) {
3466 if (dr[x].weight < max)
3467 max = dr[x].weight;
3468 }
3469 if (max) {
3470 /* If we do not have a canonical result, keep looking */
3471 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
3472 if (res > 0) {
3473 /* Append answer in result */
3474 ouranswers += res;
3475 }
3476 }
3477
3478 if (ouranswers > 0) {
3479 *foundanswers += ouranswers;
3480 memset(&ied, 0, sizeof(ied));
3482 if (!dundi_eid_zero(&trans->us_eid))
3483 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3484 for (x=0;x<trans->eidcount;x++)
3485 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3488 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3489 for (x=0;x<ouranswers;x++) {
3490 /* Add answers */
3491 if (dr[x].expiration && (expiration > dr[x].expiration))
3492 expiration = dr[x].expiration;
3493 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3494 }
3496 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3497 if (trans->autokilltimeout)
3498 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3499 if (expiration < *minexp)
3500 *minexp = expiration;
3501 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3502 } else {
3503 /* Oops, nothing to send... */
3504 destroy_trans(trans, 0);
3505 return 0;
3506 }
3507}
3508
3509static int dundi_query(struct dundi_transaction *trans)
3510{
3511 struct dundi_ie_data ied;
3512 int x;
3513 if (!trans->parent) {
3514 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3515 return -1;
3516 }
3517 memset(&ied, 0, sizeof(ied));
3519 if (!dundi_eid_zero(&trans->us_eid))
3520 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3521 for (x=0;x<trans->eidcount;x++)
3522 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3525 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3526 if (trans->autokilltimeout)
3527 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3528 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3529}
3530
3532{
3533 struct dundi_transaction *trans;
3535 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3536 dundi_discover(trans);
3537 }
3539 return 0;
3540}
3541
3542static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3543{
3544 struct dundi_transaction *trans;
3545
3546 /* Mark all as "in thread" so they don't disappear */
3548 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3549 if (trans->thread)
3550 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3551 trans->thread = 1;
3552 }
3554
3555 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3556 if (!ast_test_flag(trans, FLAG_DEAD))
3557 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3558 }
3559
3560 /* Cleanup any that got destroyed in the mean time */
3562 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
3563 trans->thread = 0;
3564 if (ast_test_flag(trans, FLAG_DEAD)) {
3565 ast_debug(1, "Our transaction went away!\n");
3566 /* This is going to remove the transaction from the dundi_request's list, as well
3567 * as the global transactions list */
3568 destroy_trans(trans, 0);
3569 }
3570 }
3573
3574 return 0;
3575}
3576
3578{
3579 struct dundi_transaction *trans;
3580
3582 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3583 dundi_query(trans);
3584 }
3586
3587 return 0;
3588}
3589
3591{
3592 /* Minimize the message propagation through DUNDi by
3593 alerting the network to hops which should be not be considered */
3594 struct dundi_transaction *trans;
3595 struct dundi_peer *peer;
3596 dundi_eid tmp;
3597 int x;
3598 int needpush;
3599
3601 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3602 /* Pop off the true root */
3603 if (trans->eidcount) {
3604 tmp = trans->eids[--trans->eidcount];
3605 needpush = 1;
3606 } else {
3607 tmp = trans->us_eid;
3608 needpush = 0;
3609 }
3610
3611 AST_LIST_TRAVERSE(&peers, peer, list) {
3612 if (ast_eid_cmp(&peer->eid, &empty_eid) && /* peer's eid is not empty (in case of dynamic peers) */
3613 (peer->lastms > -1) && /* peer is reachable */
3614 has_permission(&peer->include, dr->dcontext) && /* peer has destination context */
3615 ast_eid_cmp(&peer->eid, &trans->them_eid) && /* peer is not transaction endpoint */
3616 (peer->order <= order)) {
3617 /* For each other transaction, make sure we don't
3618 ask this EID about the others if they're not
3619 already in the list */
3620 if (!ast_eid_cmp(&tmp, &peer->eid))
3621 x = -1;
3622 else {
3623 for (x=0;x<trans->eidcount;x++) {
3624 if (!ast_eid_cmp(&trans->eids[x], &peer->eid))
3625 break;
3626 }
3627 }
3628 if (x == trans->eidcount) {
3629 /* Nope not in the list, if needed, add us at the end since we're the source */
3630 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3631 trans->eids[trans->eidcount++] = peer->eid;
3632 /* Need to insert the real root (or us) at the bottom now as
3633 a requirement now. */
3634 needpush = 1;
3635 }
3636 }
3637 }
3638 }
3639 /* If necessary, push the true root back on the end */
3640 if (needpush)
3641 trans->eids[trans->eidcount++] = tmp;
3642 }
3644
3645 return 0;
3646}
3647
3648static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3649{
3650 struct dundi_transaction *trans;
3651 int x;
3652 char eid_str[20];
3653 char eid_str2[20];
3654
3655 /* Ignore if not registered */
3656 if (ast_sockaddr_isnull(&p->addr)) {
3657 return 0;
3658 }
3659 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3660 return 0;
3661
3662 if (ast_strlen_zero(dr->number))
3663 ast_debug(1, "Will query peer '%s' for '%s' (context '%s')\n", ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid), ast_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
3664 else
3665 ast_debug(1, "Will query peer '%s' for '%s@%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
3666
3667 trans = create_transaction(p);
3668 if (!trans)
3669 return -1;
3670 trans->parent = dr;
3671 trans->ttl = ttl;
3672 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3673 trans->eids[x] = *avoid[x];
3674 trans->eidcount = x;
3675 AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
3676
3677 return 0;
3678}
3679
3680static void cancel_request(struct dundi_request *dr)
3681{
3682 struct dundi_transaction *trans;
3683
3685 while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
3686 /* Orphan transaction from request */
3687 trans->parent = NULL;
3688 /* Send final cancel */
3689 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3690 }
3692}
3693
3694static void abort_request(struct dundi_request *dr)
3695{
3696 struct dundi_transaction *trans;
3697
3699 while ((trans = AST_LIST_FIRST(&dr->trans))) {
3700 /* This will remove the transaction from the list */
3701 destroy_trans(trans, 0);
3702 }
3704}
3705
3706static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
3707{
3708 struct dundi_peer *p;
3709 int x;
3710 int res;
3711 int pass;
3712 int allowconnect;
3713 char eid_str[20];
3716 if (modeselect == 1) {
3717 /* Send the precache to push upstreams only! */
3718 pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
3719 allowconnect = 1;
3720 } else {
3721 /* Normal lookup / EID query */
3722 pass = has_permission(&p->include, dr->dcontext);
3723 allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
3724 }
3725 if (skip) {
3726 if (!ast_eid_cmp(skip, &p->eid))
3727 pass = 0;
3728 }
3729 if (pass) {
3730 if (p->order <= order) {
3731 /* Check order first, then check cache, regardless of
3732 omissions, this gets us more likely to not have an
3733 affected answer. */
3734 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
3735 res = 0;
3736 /* Make sure we haven't already seen it and that it won't
3737 affect our answer */
3738 for (x=0;avoid[x];x++) {
3739 if (!ast_eid_cmp(avoid[x], &p->eid) || !ast_eid_cmp(avoid[x], &p->us_eid)) {
3740 /* If not a direct connection, it affects our answer */
3741 if (directs && !directs[x])
3743 break;
3744 }
3745 }
3746 /* Make sure we can ask */
3747 if (allowconnect) {
3748 if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
3749 /* Check for a matching or 0 cache entry */
3750 append_transaction(dr, p, ttl, avoid);
3751 } else {
3752 ast_debug(1, "Avoiding '%s' in transaction\n", ast_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
3753 }
3754 }
3755 }
3756 *foundcache |= res;
3757 } else if (!*skipped || (p->order < *skipped))
3758 *skipped = p->order;
3759 }
3760 }
3762}
3763
3764static int register_request(struct dundi_request *dr, struct dundi_request **pending)
3765{
3766 struct dundi_request *cur;
3767 int res=0;
3768 char eid_str[20];
3771 ast_debug(1, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
3772 dr->dcontext, dr->number);
3773 if (!strcasecmp(cur->dcontext, dr->dcontext) &&
3774 !strcasecmp(cur->number, dr->number) &&
3775 (!ast_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
3776 ast_debug(1, "Found existing query for '%s@%s' for '%s' crc '%08x'\n",
3777 cur->dcontext, cur->number, ast_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
3778 *pending = cur;
3779 res = 1;
3780 break;
3781 }
3782 }
3783 if (!res) {
3784 ast_debug(1, "Registering request for '%s@%s' on behalf of '%s' crc '%08x'\n",
3785 dr->number, dr->dcontext, ast_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
3786 /* Go ahead and link us in since nobody else is searching for this */
3788 *pending = NULL;
3789 }
3791 return res;
3792}
3793
3795{
3799}
3800
3801static int check_request(struct dundi_request *dr)
3802{
3803 struct dundi_request *cur;
3804
3807 if (cur == dr)
3808 break;
3809 }
3811
3812 return cur ? 1 : 0;
3813}
3814
3815static unsigned long avoid_crc32(dundi_eid *avoid[])
3816{
3817 /* Idea is that we're calculating a checksum which is independent of
3818 the order that the EID's are listed in */
3819 uint32_t acrc32 = 0;
3820 int x;
3821 for (x=0;avoid[x];x++) {
3822 /* Order doesn't matter */
3823 if (avoid[x+1]) {
3824 acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
3825 }
3826 }
3827 return acrc32;
3828}
3829
3830static 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 *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
3831{
3832 int res;
3833 struct dundi_request dr, *pending;
3834 dundi_eid *rooteid=NULL;
3835 int x;
3836 int ttlms;
3837 int ms;
3838 int foundcache;
3839 int skipped=0;
3840 int order=0;
3841 char eid_str[20];
3842 struct timeval start;
3843
3844 /* Don't do anthing for a hungup channel */
3845 if (chan && ast_check_hangup(chan))
3846 return 0;
3847
3848 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3849
3850 for (x=0;avoid[x];x++)
3851 rooteid = avoid[x];
3852 /* Now perform real check */
3853 memset(&dr, 0, sizeof(dr));
3854 if (pipe(