Asterisk - The Open Source Telephony Project GIT-master-2de1a68
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(dr.pfds)) {
3855 ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
3856 return -1;
3857 }
3858 dr.dr = result;
3859 dr.hmd = hmd;
3860 dr.maxcount = maxret;
3861 dr.expiration = *expiration;
3862 dr.cbypass = cbypass;
3863 dr.crc32 = avoid_crc32(avoid);
3864 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
3865 ast_copy_string(dr.number, number, sizeof(dr.number));
3866 if (rooteid)
3867 dr.root_eid = *rooteid;
3868 res = register_request(&dr, &pending);
3869 if (res) {
3870 /* Already a request */
3871 if (rooteid && !ast_eid_cmp(&dr.root_eid, &pending->root_eid)) {
3872 /* This is on behalf of someone else. Go ahead and close this out since
3873 they'll get their answer anyway. */
3874 ast_debug(1, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3875 dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
3876 close(dr.pfds[0]);
3877 close(dr.pfds[1]);
3878 return -2;
3879 } else {
3880 /* Wait for the cache to populate */
3881 ast_debug(1, "Waiting for similar request for '%s@%s' for '%s'\n",
3882 dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
3883 start = ast_tvnow();
3884 while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
3885 /* XXX Would be nice to have a way to poll/select here XXX */
3886 /* XXX this is a busy wait loop!!! */
3887 usleep(1);
3888 }
3889 /* Continue on as normal, our cache should kick in */
3890 }
3891 }
3892 /* Create transactions */
3893 do {
3894 order = skipped;
3895 skipped = 0;
3896 foundcache = 0;
3897 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
3898 } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
3899 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3900 do this earlier because we didn't know if we were going to have transactions
3901 or not. */
3902 if (!ttl) {
3904 abort_request(&dr);
3906 close(dr.pfds[0]);
3907 close(dr.pfds[1]);
3908 return 0;
3909 }
3910
3911 /* Optimize transactions */
3913 /* Actually perform transactions */
3915 /* Wait for transaction to come back */
3916 start = ast_tvnow();
3917 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
3918 ms = 100;
3919 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3920 }
3921 if (chan && ast_check_hangup(chan))
3922 ast_debug(1, "Hrm, '%s' hungup before their query for %s@%s finished\n", ast_channel_name(chan), dr.number, dr.dcontext);
3925 res = dr.respcount;
3926 *expiration = dr.expiration;
3927 close(dr.pfds[0]);
3928 close(dr.pfds[1]);
3929 return res;
3930}
3931
3932int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
3933{
3934 struct dundi_hint_metadata hmd;
3935 dundi_eid *avoid[1] = { NULL, };
3936 int direct[1] = { 0, };
3937 int expiration = dundi_cache_time;
3938 memset(&hmd, 0, sizeof(hmd));
3940 return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
3941}
3942
3943static void reschedule_precache(const char *number, const char *context, int expiration)
3944{
3945 struct dundi_precache_queue *qe, *prev;
3946
3949 if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
3951 break;
3952 }
3953 }
3955 if (!qe) {
3956 int len = sizeof(*qe);
3957 int num_len = strlen(number) + 1;
3958 int context_len = strlen(context) + 1;
3959 if (!(qe = ast_calloc(1, len + num_len + context_len))) {
3961 return;
3962 }
3963 strcpy(qe->number, number);
3964 qe->context = qe->number + num_len + 1;
3965 ast_copy_string(qe->context, context, context_len);
3966 }
3967 time(&qe->expiration);
3968 qe->expiration += expiration;
3969 if ((prev = AST_LIST_FIRST(&pcq))) {
3970 while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
3971 prev = AST_LIST_NEXT(prev, list);
3972 AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
3973 } else
3976}
3977
3978static void dundi_precache_full(void)
3979{
3980 struct dundi_mapping *cur;
3981 struct ast_context *con;
3982 struct ast_exten *e;
3983
3984 AST_LIST_TRAVERSE(&mappings, cur, list) {
3985 ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
3987 con = NULL;
3988 while ((con = ast_walk_contexts(con))) {
3989 if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
3990 continue;
3991 /* Found the match, now queue them all up */
3992 ast_rdlock_context(con);
3993 e = NULL;
3994 while ((e = ast_walk_context_extensions(con, e)))
3996 ast_unlock_context(con);
3997 }
3999 }
4000}
4001
4002static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
4003{
4004 struct dundi_request dr;
4005 struct dundi_hint_metadata hmd;
4006 struct dundi_result dr2[MAX_RESULTS];
4007 struct timeval start;
4008 struct dundi_mapping *maps = NULL, *cur;
4009 int nummaps = 0;
4010 int foundanswers;
4011 int foundcache, skipped, ttlms, ms;
4012 if (!context)
4013 context = "e164";
4014 ast_debug(1, "Precache internal (%s@%s)!\n", number, context);
4015
4018 if (!strcasecmp(cur->dcontext, context))
4019 nummaps++;
4020 }
4021 if (nummaps) {
4022 maps = ast_alloca(nummaps * sizeof(*maps));
4023 nummaps = 0;
4025 if (!strcasecmp(cur->dcontext, context))
4026 maps[nummaps++] = *cur;
4027 }
4028 }
4030 if (!nummaps) {
4031 return -1;
4032 }
4033 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
4034 memset(&dr2, 0, sizeof(dr2));
4035 memset(&dr, 0, sizeof(dr));
4036 memset(&hmd, 0, sizeof(hmd));
4037 dr.dr = dr2;
4038 ast_copy_string(dr.number, number, sizeof(dr.number));
4039 ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
4040 dr.maxcount = MAX_RESULTS;
4041 dr.expiration = dundi_cache_time;
4042 dr.hmd = &hmd;
4043 dr.pfds[0] = dr.pfds[1] = -1;
4044 if (pipe(dr.pfds) < 0) {
4045 ast_log(LOG_WARNING, "pipe() failed: %s\n", strerror(errno));
4046 return -1;
4047 }
4048 build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
4050 foundanswers = 0;
4051 precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
4052 if (foundanswers) {
4053 if (dr.expiration > 0)
4054 reschedule_precache(dr.number, dr.dcontext, dr.expiration);
4055 else
4056 ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
4057 }
4058 start = ast_tvnow();
4059 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
4060 if (dr.pfds[0] > -1) {
4061 ms = 100;
4062 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
4063 } else
4064 usleep(1);
4065 }
4067 if (dr.pfds[0] > -1) {
4068 close(dr.pfds[0]);
4069 close(dr.pfds[1]);
4070 }
4071 return 0;
4072}
4073
4074int dundi_precache(const char *context, const char *number)
4075{
4076 dundi_eid *avoid[1] = { NULL, };
4078}
4079
4080static 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[])
4081{
4082 int res;
4083 struct dundi_request dr;
4084 dundi_eid *rooteid=NULL;
4085 int x;
4086 int ttlms;
4087 int skipped=0;
4088 int foundcache=0;
4089 struct timeval start;
4090
4091 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
4092
4093 for (x=0;avoid[x];x++)
4094 rooteid = avoid[x];
4095 /* Now perform real check */
4096 memset(&dr, 0, sizeof(dr));
4097 dr.hmd = hmd;
4098 dr.dei = dei;
4099 dr.pfds[0] = dr.pfds[1] = -1;
4100 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
4101 memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
4102 if (rooteid)
4103 dr.root_eid = *rooteid;
4104 /* Create transactions */
4105 build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
4106
4107 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
4108 do this earlier because we didn't know if we were going to have transactions
4109 or not. */
4110 if (!ttl) {
4112 return 0;
4113 }
4114
4115 /* Optimize transactions */
4116 optimize_transactions(&dr, 9999);
4117 /* Actually perform transactions */
4119 /* Wait for transaction to come back */
4120 start = ast_tvnow();
4121 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
4122 usleep(1);
4123 res = dr.respcount;
4124 return res;
4125}
4126
4127int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
4128{
4129 dundi_eid *avoid[1] = { NULL, };
4130 struct dundi_hint_metadata hmd;
4131 memset(&hmd, 0, sizeof(hmd));
4132 return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
4133}
4134
4135enum {
4137};
4138
4142
4143static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
4144{
4145 int results;
4146 int x;
4147 struct dundi_result dr[MAX_RESULTS];
4152 );
4153 char *parse;
4154 struct ast_flags opts = { 0, };
4155
4156 buf[0] = '\0';
4157
4158 if (ast_strlen_zero(num)) {
4159 ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
4160 return -1;
4161 }
4162
4163 parse = ast_strdupa(num);
4164
4166
4167 if (!ast_strlen_zero(args.options)) {
4169 }
4170 if (ast_strlen_zero(args.context)) {
4171 args.context = "e164";
4172 }
4173
4174 results = dundi_lookup(dr, MAX_RESULTS, NULL, args.context, args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4175 if (results > 0) {
4176 sort_results(dr, results);
4177 for (x = 0; x < results; x++) {
4179 snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
4180 break;
4181 }
4182 }
4183 }
4184
4185 return 0;
4186}
4187
4188/*! DUNDILOOKUP
4189 * \ingroup functions
4190*/
4191
4193 .name = "DUNDILOOKUP",
4194 .read = dundifunc_read,
4195};
4196
4197static unsigned int dundi_result_id;
4198
4202 unsigned int id;
4203};
4204
4205static void drds_destroy(struct dundi_result_datastore *drds)
4206{
4207 ast_free(drds);
4208}
4209
4210static void drds_destroy_cb(void *data)
4211{
4212 struct dundi_result_datastore *drds = data;
4213 drds_destroy(drds);
4214}
4215
4217 .type = "DUNDIQUERY",
4218 .destroy = drds_destroy_cb,
4219};
4220
4221static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4222{
4227 );
4228 struct ast_flags opts = { 0, };
4229 char *parse;
4230 struct dundi_result_datastore *drds;
4231 struct ast_datastore *datastore;
4232
4233 if (ast_strlen_zero(data)) {
4234 ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
4235 return -1;
4236 }
4237
4238 if (!chan) {
4239 ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
4240 return -1;
4241 }
4242
4243 parse = ast_strdupa(data);
4244
4246
4247 if (!ast_strlen_zero(args.options))
4249
4250 if (ast_strlen_zero(args.context))
4251 args.context = "e164";
4252
4253 if (!(drds = ast_calloc(1, sizeof(*drds)))) {
4254 return -1;
4255 }
4256
4257 drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1);
4258 snprintf(buf, len, "%u", drds->id);
4259
4260 if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
4261 drds_destroy(drds);
4262 return -1;
4263 }
4264
4265 datastore->data = drds;
4266
4267 drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
4268 args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4269
4270 if (drds->num_results > 0)
4271 sort_results(drds->results, drds->num_results);
4272
4273 ast_channel_lock(chan);
4274 ast_channel_datastore_add(chan, datastore);
4275 ast_channel_unlock(chan);
4276
4277 return 0;
4278}
4279
4281 .name = "DUNDIQUERY",
4282 .read = dundi_query_read,
4283};
4284
4285static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4286{
4288 AST_APP_ARG(id);
4289 AST_APP_ARG(resultnum);
4290 );
4291 char *parse;
4292 unsigned int num;
4293 struct dundi_result_datastore *drds;
4294 struct ast_datastore *datastore;
4295 int res = -1;
4296
4297 if (ast_strlen_zero(data)) {
4298 ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
4299 goto finish;
4300 }
4301
4302 if (!chan) {
4303 ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n");
4304 goto finish;
4305 }
4306
4307 parse = ast_strdupa(data);
4308
4310
4311 if (ast_strlen_zero(args.id)) {
4312 ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n");
4313 goto finish;
4314 }
4315
4316 if (ast_strlen_zero(args.resultnum)) {
4317 ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n");
4318 goto finish;
4319 }
4320
4321 ast_channel_lock(chan);
4323 ast_channel_unlock(chan);
4324
4325 if (!datastore) {
4326 ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id);
4327 goto finish;
4328 }
4329
4330 drds = datastore->data;
4331
4332 if (!strcasecmp(args.resultnum, "getnum")) {
4333 snprintf(buf, len, "%d", drds->num_results < 0 ? 0 : drds->num_results);
4334 res = 0;
4335 goto finish;
4336 }
4337
4338 if (sscanf(args.resultnum, "%30u", &num) != 1) {
4339 ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n",
4340 args.resultnum);
4341 goto finish;
4342 }
4343
4344 if (num && drds->num_results > 0 && num <= drds->num_results) {
4345 snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
4346 res = 0;
4347 } else
4348 ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
4349
4350finish:
4351 return res;
4352}
4353
4355 .name = "DUNDIRESULT",
4356 .read = dundi_result_read,
4357};
4358
4359static void mark_peers(void)
4360{
4361 struct dundi_peer *peer;
4363 AST_LIST_TRAVERSE(&peers, peer, list) {
4364 peer->dead = 1;
4365 }
4367}
4368
4369static void mark_mappings(void)
4370{
4371 struct dundi_mapping *map;
4372
4375 map->dead = 1;
4376 }
4378}
4379
4380static void destroy_permissions(struct permissionlist *permlist)
4381{
4382 struct permission *perm;
4383
4384 while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
4385 ast_free(perm);
4386}
4387
4388static void destroy_peer(struct dundi_peer *peer)
4389{
4390 int idx;
4391
4394 if (peer->regtrans) {
4395 destroy_trans(peer->regtrans, 0);
4396 }
4398 if (peer->qualtrans) {
4399 destroy_trans(peer->qualtrans, 0);
4400 }
4403
4404 /* Release lookup history */
4405 for (idx = 0; idx < ARRAY_LEN(peer->lookups); ++idx) {
4406 ast_free(peer->lookups[idx]);
4407 }
4408
4409 ast_free(peer);
4410}
4411
4412static void destroy_map(struct dundi_mapping *map)
4413{
4414 ast_free(map->weightstr);
4415 ast_free(map);
4416}
4417
4418static void prune_peers(void)
4419{
4420 struct dundi_peer *peer;
4421
4424 if (peer->dead) {
4426 destroy_peer(peer);
4427 }
4428 }
4431}
4432
4433static void prune_mappings(void)
4434{
4435 struct dundi_mapping *map;
4436
4439 if (map->dead) {
4441 destroy_map(map);
4442 }
4443 }
4446}
4447
4448static void append_permission(struct permissionlist *permlist, const char *s, int allow)
4449{
4450 struct permission *perm;
4451
4452 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
4453 return;
4454
4455 strcpy(perm->name, s);
4456 perm->allow = allow;
4457
4458 AST_LIST_INSERT_TAIL(permlist, perm, list);
4459}
4460
4461#define MAX_OPTS 128
4462
4463static void build_mapping(const char *name, const char *value)
4464{
4465 char *t, *fields[MAX_OPTS];
4466 struct dundi_mapping *map;
4467 int x;
4468 int y;
4469
4470 t = ast_strdupa(value);
4471
4473 /* Find a double match */
4474 if (!strcasecmp(map->dcontext, name) &&
4475 (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
4476 (!value[strlen(map->lcontext)] ||
4477 (value[strlen(map->lcontext)] == ','))))
4478 break;
4479 }
4480 if (!map) {
4481 if (!(map = ast_calloc(1, sizeof(*map))))
4482 return;
4484 map->dead = 1;
4485 }
4486 map->options = 0;
4487 memset(fields, 0, sizeof(fields));
4488 x = 0;
4489 while (t && x < MAX_OPTS) {
4490 fields[x++] = t;
4491 t = strchr(t, ',');
4492 if (t) {
4493 *t = '\0';
4494 t++;
4495 }
4496 } /* Russell was here, arrrr! */
4497 if ((x == 1) && ast_strlen_zero(fields[0])) {
4498 /* Placeholder mapping */
4499 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4500 map->dead = 0;
4501 } else if (x >= 4) {
4502 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4503 ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
4504 if ((sscanf(fields[1], "%30d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
4505 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4506 if ((map->tech = str2tech(fields[2])))
4507 map->dead = 0;
4508 } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') {
4509 map->weightstr = ast_strdup(fields[1]);
4510 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4511 if ((map->tech = str2tech(fields[2])))
4512 map->dead = 0;
4513 } else {
4514 ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
4515 }
4516 for (y = 4;y < x; y++) {
4517 if (!strcasecmp(fields[y], "nounsolicited"))
4519 else if (!strcasecmp(fields[y], "nocomunsolicit"))
4521 else if (!strcasecmp(fields[y], "residential"))
4523 else if (!strcasecmp(fields[y], "commercial"))
4525 else if (!strcasecmp(fields[y], "mobile"))
4526 map->options |= DUNDI_FLAG_MOBILE;
4527 else if (!strcasecmp(fields[y], "nopartial"))
4529 else
4530 ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
4531 }
4532 } else
4533 ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
4534}
4535
4536/*! \note Called with the peers list already locked */
4537static int do_register(const void *data)
4538{
4539 struct dundi_ie_data ied;
4540 struct dundi_peer *peer = (struct dundi_peer *)data;
4541 char eid_str[20];
4542 char eid_str2[20];
4543 ast_debug(1, "Register us as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), ast_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid));
4545 /* Destroy old transaction if there is one */
4546 if (peer->regtrans)
4547 destroy_trans(peer->regtrans, 0);
4548 peer->regtrans = create_transaction(peer);
4549 if (peer->regtrans) {
4551 memset(&ied, 0, sizeof(ied));
4555 dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
4556
4557 } else
4558 ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4559
4560 return 0;
4561}
4562
4563static int do_qualify(const void *data)
4564{
4565 struct dundi_peer *peer = (struct dundi_peer *)data;
4566 peer->qualifyid = -1;
4567 qualify_peer(peer, 0);
4568 return 0;
4569}
4570
4571static void qualify_peer(struct dundi_peer *peer, int schedonly)
4572{
4573 int when;
4575 if (peer->qualtrans)
4576 destroy_trans(peer->qualtrans, 0);
4577 peer->qualtrans = NULL;
4578 if (peer->maxms > 0) {
4579 when = 60000;
4580 if (peer->lastms < 0)
4581 when = 10000;
4582 if (schedonly)
4583 when = 5000;
4584 peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
4585 if (!schedonly)
4586 peer->qualtrans = create_transaction(peer);
4587 if (peer->qualtrans) {
4588 peer->qualtx = ast_tvnow();
4591 }
4592 }
4593}
4594static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
4595{
4596 char data[256];
4597 char *c;
4598 int port, expire;
4599 char eid_str[20];
4600 ast_eid_to_str(eid_str, sizeof(eid_str), eid);
4601 if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
4602 /*
4603 * data is in the form:
4604 * IPv6 address: [ffff:ffff::ffff:ffff]:port:expire
4605 * IPv4 address: a.b.c.d:port:expire
4606 */
4607 c = data;
4608 if (*c == '[') {
4609 /* Need to skip over the IPv6 address. */
4610 c = strchr(c, ']');
4611 }
4612 if (c) {
4613 c = strchr(c, ':');
4614 }
4615 if (c) {
4616 *c = '\0';
4617 c++;
4618 if (sscanf(c, "%5d:%30d", &port, &expire) == 2) {
4619 /* Got it! */
4620 struct ast_sockaddr *addrs;
4621
4622 if (ast_sockaddr_resolve(&addrs, data, PARSE_PORT_FORBID, AF_UNSPEC) > 0){
4623 ast_sockaddr_copy(&peer->addr, &addrs[0]);
4624 ast_free(addrs);
4625 }
4626 ast_sockaddr_set_port(&peer->addr, port);
4627 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
4628 }
4629 }
4630 }
4631}
4632
4633
4634static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
4635{
4636 struct dundi_peer *peer;
4637 dundi_eid testeid;
4638 int needregister=0;
4639 char eid_str[20];
4640 int port = 0;
4641
4643 AST_LIST_TRAVERSE(&peers, peer, list) {
4644 if (!ast_eid_cmp(&peer->eid, eid)) {
4645 break;
4646 }
4647 }
4648 if (!peer) {
4649 /* Add us into the list */
4650 if (!(peer = ast_calloc(1, sizeof(*peer)))) {
4652 return;
4653 }
4654 peer->registerid = -1;
4655 peer->registerexpire = -1;
4656 peer->qualifyid = -1;
4657 populate_addr(peer, eid);
4659 }
4660 peer->dead = 0;
4661 peer->eid = *eid;
4662 peer->us_eid = global_eid;
4666 for (; v; v = v->next) {
4667 if (!strcasecmp(v->name, "inkey")) {
4668 ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
4669 } else if (!strcasecmp(v->name, "outkey")) {
4670 ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
4671 } else if (!strcasecmp(v->name, "port")) {
4672 port = atoi(v->value);
4673 } else if (!strcasecmp(v->name, "host")) {
4674 if (!strcasecmp(v->value, "dynamic")) {
4675 peer->dynamic = 1;
4676 } else {
4677 struct ast_sockaddr *addrs;
4678
4679 if (ast_sockaddr_resolve(&addrs, v->value, PARSE_PORT_FORBID, AF_UNSPEC) > 0) {
4680 ast_sockaddr_copy(&peer->addr, &addrs[0]);
4681 peer->dynamic = 0;
4682 ast_free(addrs);
4683 } else {
4684 ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
4685 peer->dead = 1;
4686 }
4687 }
4688 } else if (!strcasecmp(v->name, "ustothem")) {
4689 if (!ast_str_to_eid(&testeid, v->value))
4690 peer->us_eid = testeid;
4691 else
4692 ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
4693 } else if (!strcasecmp(v->name, "include")) {
4694 append_permission(&peer->include, v->value, 1);
4695 } else if (!strcasecmp(v->name, "permit")) {
4696 append_permission(&peer->permit, v->value, 1);
4697 } else if (!strcasecmp(v->name, "noinclude")) {
4698 append_permission(&peer->include, v->value, 0);
4699 } else if (!strcasecmp(v->name, "deny")) {
4700 append_permission(&peer->permit, v->value, 0);
4701 } else if (!strcasecmp(v->name, "register")) {
4702 needregister = ast_true(v->value);
4703 } else if (!strcasecmp(v->name, "order")) {
4704 if (!strcasecmp(v->value, "primary"))
4705 peer->order = 0;
4706 else if (!strcasecmp(v->value, "secondary"))
4707 peer->order = 1;
4708 else if (!strcasecmp(v->value, "tertiary"))
4709 peer->order = 2;
4710 else if (!strcasecmp(v->value, "quartiary"))
4711 peer->order = 3;
4712 else {
4713 ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno);
4714 }
4715 } else if (!strcasecmp(v->name, "qualify")) {
4716 if (!strcasecmp(v->value, "no")) {
4717 peer->maxms = 0;
4718 } else if (!strcasecmp(v->value, "yes")) {
4719 peer->maxms = DEFAULT_MAXMS;
4720 } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
4721 ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4722 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
4723 peer->maxms = 0;
4724 }
4725 } else if (!strcasecmp(v->name, "model")) {
4726 if (!strcasecmp(v->value, "inbound"))
4727 peer->model = DUNDI_MODEL_INBOUND;
4728 else if (!strcasecmp(v->value, "outbound"))
4730 else if (!strcasecmp(v->value, "symmetric"))
4732 else if (!strcasecmp(v->value, "none"))
4733 peer->model = 0;
4734 else {
4735 ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4736 v->value, v->lineno);
4737 }
4738 } else if (!strcasecmp(v->name, "precache")) {
4739 if (!strcasecmp(v->value, "inbound"))
4741 else if (!strcasecmp(v->value, "outbound"))
4743 else if (!strcasecmp(v->value, "symmetric"))
4745 else if (!strcasecmp(v->value, "none"))
4746 peer->pcmodel = 0;
4747 else {
4748 ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4749 v->value, v->lineno);
4750 }
4751 }
4752 }
4753
4754 if (!ast_sockaddr_isnull(&peer->addr)) {
4755 ast_sockaddr_set_port(&peer->addr, (0 < port) ? port : DUNDI_PORT);
4756 }
4757
4758 (*globalpcmode) |= peer->pcmodel;
4759 if (!peer->model && !peer->pcmodel) {
4760 ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
4761 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4762 peer->dead = 1;
4763 } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4764 ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4765 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4766 peer->dead = 1;
4767 } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4768 ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4769 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4770 peer->dead = 1;
4771 } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4772 ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4773 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4774 } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4775 ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n",
4776 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4777 } else {
4778 if (ast_eid_cmp(&peer->eid, &empty_eid)) {
4779 /* Schedule any items for explicitly configured peers. */
4780 if (needregister) {
4781 peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
4782 }
4783 qualify_peer(peer, 1);
4784 }
4785 }
4787}
4788
4789static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
4790{
4791 struct dundi_result results[MAX_RESULTS];
4792 int res;
4793 int x;
4794 int found = 0;
4795 if (ast_strlen_zero(data))
4796 data = context;
4797
4798 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4799 for (x=0;x<res;x++) {
4800 if (ast_test_flag(results + x, flag))
4801 found++;
4802 }
4803 if (found >= priority)
4804 return 1;
4805 return 0;
4806}
4807
4808static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4809{
4810 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
4811}
4812
4813static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4814{
4815 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
4816}
4817
4818static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4819{
4820 struct dundi_result results[MAX_RESULTS];
4821 int res;
4822 int x=0;
4823 char req[1024];
4824 const char *dundiargs;
4825
4826 if (ast_strlen_zero(data))
4827 data = context;
4828
4829 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4830 if (res > 0) {
4831 sort_results(results, res);
4832 for (x=0;x<res;x++) {
4833 if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
4834 if (!--priority)
4835 break;
4836 }
4837 }
4838 }
4839 if (x < res) {
4840 /* Got a hit! */
4841 dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
4842 /* Backwards compatibility with lookups using chan_sip even if we don't have it anymore:
4843 * At a protocol level, "SIP" will always be specified, but depending on our configuration,
4844 * we will use the user-specified channel driver (from dundi.conf) to complete the call.
4845 */
4846 if (!strcasecmp(results[x].tech, "SIP") || !strcasecmp(results[x].tech, "PJSIP")) {
4847 /* Only "SIP" is a valid technology for a DUNDi peer to communicate.
4848 * But if they tell use to use "PJSIP" instead, just interpret it as if they said "SIP" instead. */
4849 if (strcasecmp(results[x].tech, "SIP")) {
4850 ast_log(LOG_WARNING, "%s cannot be specified by DUNDi peers (peer should use SIP for DUNDi lookups instead)\n", results[x].tech);
4851 }
4852 /* Use whatever we're configured to use for SIP protocol calls. */
4853 results[x].techint = outgoing_sip_tech;
4854 ast_copy_string(results[x].tech, tech2str(outgoing_sip_tech), sizeof(results[x].tech));
4855 }
4856 /* PJSIP requires an endpoint to be specified explicitly. */
4858 char *number, *ip = ast_strdupa(results[x].dest);
4860 ast_log(LOG_WARNING, "PJSIP calls require an endpoint to be specified explicitly (use the pjsip_outgoing_endpoint option in dundi.conf)\n");
4861 return -1;
4862 }
4863 /* Take IP/number and turn it into sip:number@IP */
4864 if (ast_strlen_zero(ip)) {
4865 ast_log(LOG_WARNING, "PJSIP destination is empty?\n");
4866 return -1;
4867 }
4868 number = strsep(&ip, "/");
4869 snprintf(req, sizeof(req), "%s/%s/sip:%s@%s,,%s", results[x].tech, pjsip_outgoing_endpoint, S_OR(number, ""), ip, S_OR(dundiargs, ""));
4870 ast_debug(1, "Finalized PJSIP Dial: %s\n", req);
4871 } else { /* SIP, or something else. */
4872 snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest, S_OR(dundiargs, ""));
4873 }
4874 res = ast_pbx_exec_application(chan, "Dial", req);
4875 } else {
4876 res = -1;
4877 }
4878 return res;
4879}
4880
4881static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4882{
4883 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
4884}
4885
4886static struct ast_switch dundi_switch = {
4887 .name = "DUNDi",
4888 .description = "DUNDi Discovered Dialplan Switch",
4889 .exists = dundi_exists,
4890 .canmatch = dundi_canmatch,
4891 .exec = dundi_exec,
4892 .matchmore = dundi_matchmore,
4893};
4894
4895static int get_ipaddress(char *ip, size_t size, const char *str, int family)
4896{
4897 struct ast_sockaddr *addrs;
4898
4899 if (!ast_sockaddr_resolve(&addrs, str, 0, family)) {
4900 return -1;
4901 }
4902
4903 ast_copy_string(ip, ast_sockaddr_stringify_host(&addrs[0]), size);
4904 ast_free(addrs);
4905
4906 return 0;
4907}
4908
4909static void set_host_ipaddr(struct ast_sockaddr *sin)
4910{
4911 char hn[MAXHOSTNAMELEN];
4912 struct addrinfo hints;
4913 int family;
4914
4915 memset(&hints, 0, sizeof(hints));
4916
4917 if (ast_sockaddr_is_ipv6(sin)) {
4918 family = AF_INET6;
4919 } else {
4920 family = AF_INET;
4921 }
4922
4923 if (gethostname(hn, sizeof(hn) - 1) < 0) {
4924 ast_log(LOG_WARNING, "Unable to get host name!\n");
4925 return;
4926 }
4927
4928 get_ipaddress(ipaddr, sizeof(ipaddr), hn, family);
4929}
4930
4931static int set_config(char *config_file, struct ast_sockaddr *sin, int reload, struct ast_sockaddr *sin2)
4932{
4933 struct ast_config *cfg;
4934 struct ast_variable *v;
4935 char *cat;
4936 int x;
4937 struct ast_flags config_flags = { 0 };
4938 static int last_port = 0;
4939 int port = 0;
4940 int globalpcmodel = 0;
4941 dundi_eid testeid;
4942 char bind_addr[80]={0,};
4943 char bind_addr2[80]={0,};
4944
4945 if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
4946 ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
4947 return -1;
4948 }
4949
4952 any_peer = NULL;
4953 outgoing_sip_tech = DUNDI_PROTO_PJSIP; /* Default for new versions */
4954
4956
4958 ast_log(LOG_WARNING, "Entity ID is not set.\n");
4959 }
4960 memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
4961
4963 ast_copy_string(secretpath, "dundi", sizeof(secretpath));
4964 v = ast_variable_browse(cfg, "general");
4965 while(v) {
4966 if (!strcasecmp(v->name, "port")){
4967 port = atoi(v->value);
4968 } else if (!strcasecmp(v->name, "bindaddr")) {
4969 if (get_ipaddress(bind_addr, sizeof(bind_addr), v->value, AF_UNSPEC) == 0) {
4970 if (!ast_sockaddr_parse(sin, bind_addr, 0)) {
4971 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4972 }
4973 }
4974 } else if (!strcasecmp(v->name, "bindaddr2")) {
4975 if (get_ipaddress(bind_addr2, sizeof(bind_addr2), v->value, AF_UNSPEC) == 0) {
4976 if (!ast_sockaddr_parse(sin2, bind_addr2, 0)) {
4977 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4978 }
4979 }
4980 } else if (!strcasecmp(v->name, "authdebug")) {
4981 authdebug = ast_true(v->value);
4982 } else if (!strcasecmp(v->name, "ttl")) {
4983 if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
4984 dundi_ttl = x;
4985 } else {
4986 ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4988 }
4989 } else if (!strcasecmp(v->name, "autokill")) {
4990 if (sscanf(v->value, "%30d", &x) == 1) {
4991 if (x >= 0)
4993 else
4994 ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
4995 } else if (ast_true(v->value)) {
4997 } else {
4999 }
5000 } else if (!strcasecmp(v->name, "entityid")) {
5001 if (!ast_str_to_eid(&testeid, v->value))
5002 global_eid = testeid;
5003 else
5004 ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
5005 } else if (!strcasecmp(v->name, "tos")) {
5006 if (ast_str2tos(v->value, &tos))
5007 ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
5008 } else if (!strcasecmp(v->name, "department")) {
5009 ast_copy_string(dept, v->value, sizeof(dept));
5010 } else if (!strcasecmp(v->name, "organization")) {
5011 ast_copy_string(org, v->value, sizeof(org));
5012 } else if (!strcasecmp(v->name, "locality")) {
5013 ast_copy_string(locality, v->value, sizeof(locality));
5014 } else if (!strcasecmp(v->name, "stateprov")) {
5016 } else if (!strcasecmp(v->name, "country")) {
5017 ast_copy_string(country, v->value, sizeof(country));
5018 } else if (!strcasecmp(v->name, "email")) {
5019 ast_copy_string(email, v->value, sizeof(email));
5020 } else if (!strcasecmp(v->name, "phone")) {
5021 ast_copy_string(phone, v->value, sizeof(phone));
5022 } else if (!strcasecmp(v->name, "storehistory")) {
5024 } else if (!strcasecmp(v->name, "outgoing_sip_tech")) {
5025 int outgoing_tech = str2tech(v->value);
5026 if (outgoing_tech != DUNDI_PROTO_SIP && outgoing_tech != DUNDI_PROTO_PJSIP) {
5027 ast_log(LOG_WARNING, "outgoing_sip_tech must be SIP or PJSIP\n");
5028 } else {
5029 outgoing_sip_tech = outgoing_tech;
5030 }
5031 } else if (!strcasecmp(v->name, "pjsip_outgoing_endpoint")) {
5033 } else if (!strcasecmp(v->name, "cachetime")) {
5034 if ((sscanf(v->value, "%30d", &x) == 1)) {
5035 dundi_cache_time = x;
5036 } else {
5037 ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
5039 }
5040 }
5041 v = v->next;
5042 }
5043
5044 if (port == 0) {
5045 port = DUNDI_PORT;
5046 }
5047
5048 if (ast_sockaddr_isnull(sin)) {
5049 sprintf(bind_addr, "0.0.0.0:%d", port);
5050 ast_sockaddr_parse(sin, bind_addr, 0);
5051 } else {
5052 ast_sockaddr_set_port(sin, port);
5053 }
5054
5055 if (last_port == 0) {
5056 last_port = port;
5057 } else if (last_port != port) {
5058 ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
5059 }
5060
5061 set_host_ipaddr(sin);
5062
5063 if (!ast_sockaddr_isnull(sin2)) {
5064 ast_sockaddr_set_port(sin2, port);
5065 }
5066
5068
5069 mark_mappings();
5070 v = ast_variable_browse(cfg, "mappings");
5071 while (v) {
5073 build_mapping(v->name, v->value);
5075 v = v->next;
5076 }
5078
5079 mark_peers();
5080 cat = ast_category_browse(cfg, NULL);
5081 while(cat) {
5082 if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
5083 /* Entries */
5084 if (!ast_str_to_eid(&testeid, cat))
5085 build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
5086 else if (!strcasecmp(cat, "*")) {
5087 build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
5089 } else
5090 ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
5091 }
5092 cat = ast_category_browse(cfg, cat);
5093 }
5094 prune_peers();
5095
5096 ast_config_destroy(cfg);
5097 load_password();
5098 if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
5100 return 0;
5101}
5102
5103static int unload_module(void)
5104{
5110
5111 /* Stop all currently running threads */
5112 dundi_shutdown = 1;
5114 pthread_kill(netthreadid, SIGURG);
5115 pthread_join(netthreadid, NULL);
5117 }
5119 pthread_kill(precachethreadid, SIGURG);
5120 pthread_join(precachethreadid, NULL);
5122 }
5124 pthread_cancel(clearcachethreadid);
5125 pthread_join(clearcachethreadid, NULL);
5127 }
5128
5129 if (netsocket >= 0) {
5130 close(netsocket);
5131 }
5132
5133 if (netsocket2 >= 0) {
5134 close(netsocket2);
5135 }
5136
5137 mark_mappings();
5139 mark_peers();
5140 prune_peers();
5141
5142 if (-1 < netsocket) {
5143 close(netsocket);
5144 netsocket = -1;
5145 }
5146 if (io) {
5148 io = NULL;
5149 }
5150
5151 if (sched) {
5153 sched = NULL;
5154 }
5155
5156 return 0;
5157}
5158
5159static int reload(void)
5160{
5161 struct ast_sockaddr sin;
5162 struct ast_sockaddr sin2;
5163
5165 ast_sockaddr_setnull(&sin2);
5166
5167 if (set_config("dundi.conf", &sin, 1, &sin2))
5169
5171}
5172
5173static int load_module(void)
5174{
5175 struct ast_sockaddr sin;
5176 struct ast_sockaddr sin2;
5177
5180
5181 /* Make a UDP socket */
5184
5185 if (!io || !sched) {
5186 goto declined;
5187 }
5188
5190 ast_sockaddr_setnull(&sin2);
5191
5192 if (set_config("dundi.conf", &sin, 0, &sin2)) {
5193 goto declined;
5194 }
5195
5196 if (!ast_sockaddr_isnull(&sin2)) {
5197 if ((ast_sockaddr_is_ipv4(&sin) == ast_sockaddr_is_ipv4(&sin2)) || (ast_sockaddr_is_ipv6(&sin) == ast_sockaddr_is_ipv6(&sin2))) {
5198 ast_log(LOG_ERROR, "bindaddr & bindaddr2 should be different IP protocols.\n");
5199 goto declined;
5200 }
5201
5202 /*bind netsocket to ipv4, netsocket2 to ipv6 */
5203
5204 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5205 netsocket2 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5206 if (netsocket < 0 || netsocket2 < 0) {
5207 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
5208 goto declined;
5209 }
5210 if (ast_sockaddr_is_ipv4(&sin)) {
5211 if (ast_bind(netsocket, &sin)) {
5212 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5213 ast_sockaddr_stringify(&sin), strerror(errno));
5214 goto declined;
5215 }
5216 if (ast_bind(netsocket2, &sin2)) {
5217 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5218 ast_sockaddr_stringify(&sin2), strerror(errno));
5219 goto declined;
5220 }
5221 } else {
5222 if (ast_bind(netsocket, &sin2)) {
5223 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5224 ast_sockaddr_stringify(&sin2), strerror(errno));
5225 goto declined;
5226 }
5227 if (ast_bind(netsocket2, &sin)) {
5228 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5229 ast_sockaddr_stringify(&sin), strerror(errno));
5230 goto declined;
5231 }
5232 }
5233 ast_set_qos(netsocket, tos, 0, "DUNDi");
5234 ast_set_qos(netsocket2, tos, 0, "DUNDi");
5235 } else {
5236 if (ast_sockaddr_is_ipv6(&sin)) {
5237 netsocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5238 } else {
5239 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5240 }
5241 if (netsocket < 0) {
5242 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
5243 goto declined;
5244 }
5245 if (ast_bind(netsocket, &sin)) {
5246 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5247 ast_sockaddr_stringify(&sin), strerror(errno));
5248 goto declined;
5249 }
5250 ast_set_qos(netsocket, tos, 0, "DUNDi");
5251 }
5252
5253 if (start_network_thread()) {
5254 ast_log(LOG_ERROR, "Unable to start network thread\n");
5255 goto declined;
5256 }
5257
5260 ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
5264
5265 ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin));
5266 if (!ast_sockaddr_isnull(&sin2))
5267 ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin2));
5268
5270
5271declined:
5272 unload_module();
5274}
5275
5276AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
5277 .support_level = AST_MODULE_SUPPORT_EXTENDED,
5278 .load = load_module,
5279 .unload = unload_module,
5280 .reload = reload,
5281 .optional_modules = "res_crypto",
Access Control of various sorts.
int ast_str2tos(const char *value, unsigned int *tos)
Convert a string to the appropriate TOS value.
Definition: acl.c:966
integer order
Definition: analys.c:66
static const struct adsi_event events[]
Definition: app_adsiprog.c:85
char weight
jack_status_t status
Definition: app_jack.c:146
const char * str
Definition: app_jack.c:147
struct sla_ringing_trunk * last
Definition: app_sla.c:332
if(!yyg->yy_init)
Definition: ast_expr2f.c:854
Persistent data storage (akin to *doze registry)
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:342
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:427
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:476
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition: main/db.c:610
int ast_db_deltree(const char *family, const char *keytree)
Delete one or more entries in astdb.
Definition: main/db.c:536
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:677
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
static int priority
static const char config_file[]
Definition: cdr_odbc.c:54
static PGresult * result
Definition: cel_pgsql.c:84
#define DEFAULT_MAXMS
Definition: chan_iax2.c:397
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
#define ast_channel_lock(chan)
Definition: channel.h:2922
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
Waits for input on an fd.
Definition: channel.c:2980
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
#define ast_channel_unlock(chan)
Definition: channel.h:2923
#define AST_MAX_EXTENSION
Definition: channel.h:134
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
#define ast_var_assign(name, value)
Definition: chanvars.h:40
void ast_var_delete(struct ast_var_t *var)
Definition: extconf.c:2471
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
short word
Provide cryptographic signature routines.
int ast_aes_set_encrypt_key(const unsigned char *key, ast_aes_encrypt_key *ctx)
Set an encryption key.
Definition: res_crypto.c:700
struct ast_key * ast_key_get(const char *kname, int ktype)
Retrieve a key.
Definition: res_crypto.c:149
#define AST_KEY_PUBLIC
Definition: crypto.h:46
int ast_check_signature_bin(struct ast_key *key, const char *msg, int msglen, const unsigned char *dsig)
Check the authenticity of a message signature using a given public key.
Definition: res_crypto.c:634
#define AST_KEY_PRIVATE
Definition: crypto.h:47
int ast_aes_set_decrypt_key(const unsigned char *key, ast_aes_decrypt_key *ctx)
Set a decryption key.
Definition: res_crypto.c:709
int ast_sign_bin(struct ast_key *key, const char *msg, int msglen, unsigned char *dsig)
Sign a message signature using a given private key.
Definition: res_crypto.c:390
int ast_aes_encrypt(const unsigned char *in, unsigned char *out, const ast_aes_encrypt_key *key)
AES encrypt data.
Definition: res_crypto.c:749
int ast_aes_decrypt(const unsigned char *in, unsigned char *out, const ast_aes_decrypt_key *key)
AES decrypt data.
Definition: res_crypto.c:790
int ast_encrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
Encrypt a message using a given private key.
Definition: res_crypto.c:549
int ast_decrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
Decrypt a message using a given private key.
Definition: res_crypto.c:472
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value)
Definition: dundi-parser.c:593
int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
Definition: dundi-parser.c:541
void dundi_set_output(void(*func)(const char *))
Definition: dundi-parser.c:627
int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
Definition: dundi-parser.c:486
int dundi_str_short_to_eid(dundi_eid *eid, const char *s)
Definition: dundi-parser.c:70
int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, char *data)
Definition: dundi-parser.c:561
int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
Definition: dundi-parser.c:612
int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
Definition: dundi-parser.c:617
int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value)
Definition: dundi-parser.c:600
int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie)
Definition: dundi-parser.c:622
char * dundi_flags2str(char *buf, int bufsiz, int flags)
Definition: dundi-parser.c:248
int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
Definition: dundi-parser.c:637
void dundi_showframe(struct dundi_hdr *fhi, int rx, struct ast_sockaddr *sin, int datalen)
Definition: dundi-parser.c:434
int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, char *str)
Definition: dundi-parser.c:607
char * dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
Definition: dundi-parser.c:54
int dundi_eid_zero(dundi_eid *eid)
Definition: dundi-parser.c:82
int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, char *data)
Definition: dundi-parser.c:501
void dundi_set_error(void(*func)(const char *))
Definition: dundi-parser.c:632
int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, char *data)
Definition: dundi-parser.c:520
#define DUNDI_MAX_STACK
Definition: dundi-parser.h:18
Distributed Universal Number Discovery (DUNDi) See also.
#define DUNDI_IE_COUNTRY
Definition: dundi.h:205
#define DUNDI_IE_VERSION
Definition: dundi.h:190
#define DUNDI_DEFAULT_VERSION
Definition: dundi.h:219
#define DUNDI_IE_CACHEBYPASS
Definition: dundi.h:209
#define DUNDI_IE_ENCDATA
Definition: dundi.h:195
struct ast_eid dundi_eid
Definition: dundi.h:32
#define DUNDI_COMMAND_NULL
Definition: dundi.h:170
#define DUNDI_PORT
Definition: dundi.h:30
#define DUNDI_IE_IPADDR
Definition: dundi.h:208
#define DUNDI_FLUFF_TIME
Definition: dundi.h:213
#define DUNDI_IE_DEPARTMENT
Definition: dundi.h:201
#define DUNDI_IE_ORGANIZATION
Definition: dundi.h:202
#define DUNDI_IE_TTL
Definition: dundi.h:189
#define DUNDI_COMMAND_PRECACHERQ
Definition: dundi.h:166
#define DUNDI_IE_LOCALITY
Definition: dundi.h:203
#define DUNDI_IE_SIGNATURE
Definition: dundi.h:197
#define DUNDI_IE_EXPIRATION
Definition: dundi.h:191
#define DUNDI_TTL_TIME
Definition: dundi.h:214
#define DUNDI_IE_HINT
Definition: dundi.h:199
#define DUNDI_COMMAND_PRECACHERP
Definition: dundi.h:167
#define DUNDI_IE_CALLED_CONTEXT
Definition: dundi.h:185
#define DUNDI_IE_CAUSE
Definition: dundi.h:193
#define DUNDI_COMMAND_ENCREJ
Definition: dundi.h:175
#define DUNDI_DEFAULT_TTL
Definition: dundi.h:218
#define DUNDI_COMMAND_EIDQUERY
Definition: dundi.h:164
#define DUNDI_IE_PHONE
Definition: dundi.h:207
#define DUNDI_IE_REQEID
Definition: dundi.h:194
#define DUNDI_IE_CALLED_NUMBER
Definition: dundi.h:186
#define DUNDI_COMMAND_DPRESPONSE
Definition: dundi.h:163
#define DUNDI_IE_EID_DIRECT
Definition: dundi.h:187
#define DUNDI_DEFAULT_RETRANS
Definition: dundi.h:216
#define DUNDI_IE_EID
Definition: dundi.h:184
#define DUNDI_IE_SHAREDKEY
Definition: dundi.h:196
#define DUNDI_IE_UNKNOWN
Definition: dundi.h:192
#define DUNDI_COMMAND_CANCEL
Definition: dundi.h:173
#define DUNDI_COMMAND_INVALID
Definition: dundi.h:168
#define DUNDI_DEFAULT_CACHE_TIME
Definition: dundi.h:220
#define DUNDI_COMMAND_DPDISCOVER
Definition: dundi.h:162
#define DUNDI_DEFAULT_RETRANS_TIMER
Definition: dundi.h:217
#define DUNDI_COMMAND_EIDRESPONSE
Definition: dundi.h:165
#define DUNDI_IE_KEYCRC32
Definition: dundi.h:198
#define DUNDI_COMMAND_ACK
Definition: dundi.h:161
@ DUNDI_PROTO_NONE
Definition: dundi.h:55
@ DUNDI_PROTO_PJSIP
Definition: dundi.h:63
@ DUNDI_PROTO_SIP
Definition: dundi.h:59
@ DUNDI_PROTO_IAX
Definition: dundi.h:57
@ DUNDI_PROTO_H323
Definition: dundi.h:61
#define DUNDI_IE_EMAIL
Definition: dundi.h:206
#define DUNDI_COMMAND_UNKNOWN
Definition: dundi.h:169
#define DUNDI_DEFAULT_KEY_EXPIRE
Definition: dundi.h:221
@ DUNDI_FLAG_MOBILE
Definition: dundi.h:82
@ DUNDI_FLAG_COMMERCIAL
Definition: dundi.h:80
@ DUNDI_FLAG_NOCOMUNSOLICIT
Definition: dundi.h:86
@ DUNDI_FLAG_CANMATCH
Definition: dundi.h:74
@ DUNDI_FLAG_MATCHMORE
Definition: dundi.h:72
@ DUNDI_FLAG_RESIDENTIAL
Definition: dundi.h:78
@ DUNDI_FLAG_EXISTS
Definition: dundi.h:70
@ DUNDI_FLAG_IGNOREPAT
Definition: dundi.h:76
@ DUNDI_FLAG_NOUNSOLICITED
Definition: dundi.h:84
#define DUNDI_COMMAND_FINAL
Definition: dundi.h:159
@ DUNDI_CAUSE_GENERAL
Definition: dundi.h:121
@ DUNDI_CAUSE_DUPLICATE
Definition: dundi.h:127
@ DUNDI_CAUSE_NOAUTH
Definition: dundi.h:125
#define DUNDI_COMMAND_REGRESPONSE
Definition: dundi.h:172
#define DUNDI_COMMAND_ENCRYPT
Definition: dundi.h:174
#define DUNDI_COMMAND_REGREQ
Definition: dundi.h:171
#define DUNDI_IE_STATE_PROV
Definition: dundi.h:204
@ DUNDI_HINT_DONT_ASK
Definition: dundi.h:94
@ DUNDI_HINT_UNAFFECTED
Definition: dundi.h:96
@ DUNDI_HINT_TTL_EXPIRED
Definition: dundi.h:92
#define DUNDI_IE_ANSWER
Definition: dundi.h:188
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void ast_verbose(const char *fmt,...)
Definition: extconf.c:2206
long int flag
Definition: f2c.h:83
#define max(a, b)
Definition: f2c.h:198
Generic File Format Support. Should be included by clients of the file handling routines....
static const char name[]
Definition: format_mp3.c:68
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static struct ast_custom_function dundi_function
Definition: pbx_dundi.c:4192
static ENTRY retval
Definition: hsearch.c:50
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define END_OPTIONS
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define BEGIN_OPTIONS
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3056
char * strsep(char **str, const char *delims)
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
Asterisk internal frame definitions.
Support for logging to various files, console and syslog Configuration in file logger....
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
I/O Management (derived from Cheops-NG)
#define AST_IO_IN
Definition: io.h:34
struct io_context * io_context_create(void)
Creates a context Create a context for I/O operations Basically mallocs an IO structure and sets up s...
Definition: io.c:81
int * ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
Adds an IO context.
Definition: io.c:162
void io_context_destroy(struct io_context *ioc)
Destroys a context.
Definition: io.c:107
int ast_io_wait(struct io_context *ioc, int howlong)
Waits for IO.
Definition: io.c:278
int ast_io_remove(struct io_context *ioc, int *id)
Removes an IO context.
Definition: io.c:245
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:291
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:346
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_INSERT_AFTER(head, listelm, elm, field)
Inserts a list entry after a given entry.
Definition: linkedlists.h:695
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
Asterisk locking-related definitions:
#define AST_PTHREADT_NULL
Definition: lock.h:66
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
size_t current
Definition: main/cli.c:113
int errno
MD5 digest functions.
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:315
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_FAILURE
Module could not be loaded properly.
Definition: module.h:102
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
def ignore(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint')
Definition: sip_to_pjsip.py:48
Network socket handling.
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
static void ast_sockaddr_copy(struct ast_sockaddr *dst, const struct ast_sockaddr *src)
Copies the data from one ast_sockaddr to another.
Definition: netsock2.h:167
int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
Determine if this is an IPv6 address.
Definition: netsock2.c:524
int ast_bind(int sockfd, const struct ast_sockaddr *addr)
Wrapper around bind(2) that uses struct ast_sockaddr.
Definition: netsock2.c:590
static char * ast_sockaddr_stringify_host(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only, suitable for a URL (with brack...
Definition: netsock2.h:327
ssize_t ast_sendto(int sockfd, const void *buf, size_t len, int flags, const struct ast_sockaddr *dest_addr)
Wrapper around sendto(2) that uses ast_sockaddr.
Definition: netsock2.c:614
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition: netsock2.c:280
int ast_set_qos(int sockfd, int tos, int cos, const char *desc)
Set type of service.
Definition: netsock2.c:621
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
static int ast_sockaddr_isnull(const struct ast_sockaddr *addr)
Checks if the ast_sockaddr is null. "null" in this sense essentially means uninitialized,...
Definition: netsock2.h:127
ssize_t ast_recvfrom(int sockfd, void *buf, size_t len, int flags, struct ast_sockaddr *src_addr)
Wrapper around recvfrom(2) that uses struct ast_sockaddr.
Definition: netsock2.c:606
int ast_sockaddr_cmp(const struct ast_sockaddr *a, const struct ast_sockaddr *b)
Compares two ast_sockaddr structures.
Definition: netsock2.c:388
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
Definition: netsock2.h:286
static void ast_sockaddr_setnull(struct ast_sockaddr *addr)
Sets address addr to null.
Definition: netsock2.h:138
int ast_sockaddr_is_ipv4(const struct ast_sockaddr *addr)
Determine if the address is an IPv4 address.
Definition: netsock2.c:497
Wrapper for network related headers, masking differences between various operating systems....
#define MAXHOSTNAMELEN
Definition: network.h:69
Core PBX routines and definitions.
struct ast_context * ast_walk_contexts(struct ast_context *con)
Definition: extconf.c:4024
void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
void ast_unregister_switch(struct ast_switch *sw)
Unregister an alternative switch.
Definition: pbx_switch.c:76
int ast_register_switch(struct ast_switch *sw)
Register an alternative dialplan switch.
Definition: pbx_switch.c:58
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4175
int ast_pbx_exec_application(struct ast_channel *chan, const char *app_name, const char *app_args)
Execute an application.
Definition: pbx_app.c:501
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8491
const char * ast_get_context_name(struct ast_context *con)
Definition: ael_main.c:421
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
Definition: pbx.c:4190
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_rdlock_contexts(void)
Read locks the context list.
Definition: pbx.c:8468
int ast_ignore_pattern(const char *context, const char *pattern)
Checks to see if a number should be ignored.
Definition: pbx.c:6879
struct ast_exten * ast_walk_context_extensions(struct ast_context *con, struct ast_exten *priority)
Definition: ael_main.c:427
int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks to see if adding anything to this extension might match something. (exists ^ canmatch)
Definition: pbx.c:4195
int ast_unlock_contexts(void)
Unlocks contexts.
Definition: pbx.c:8473
const char * ast_get_extension_name(struct ast_exten *exten)
Definition: pbx.c:8509
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
Definition: pbx.c:8486
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition: ael_main.c:211
static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
Definition: pbx_dundi.c:870
static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc, int *lowexpiration)
Definition: pbx_dundi.c:1248
static int socket_read(int *id, int fd, short events, void *sock)
Definition: pbx_dundi.c:2089
static char * dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2310
static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
Definition: pbx_dundi.c:1379
static char country[80]
Definition: pbx_dundi.c:205
static int reset_transaction(struct dundi_transaction *trans)
Definition: pbx_dundi.c:505
#define MAX_RESULTS
Definition: pbx_dundi.c:153
static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
Definition: pbx_dundi.c:3209
static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
Definition: pbx_dundi.c:1301
static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:1093
static struct dundi_transaction * create_transaction(struct dundi_peer *p)
Definition: pbx_dundi.c:3143
static void load_password(void)
Definition: pbx_dundi.c:2144
static void check_password(void)
Definition: pbx_dundi.c:2183
#define DUNDI_MODEL_SYMMETRIC
Definition: pbx_dundi.c:161
static void reschedule_precache(const char *number, const char *context, int expiration)
Definition: pbx_dundi.c:3943
static struct ast_switch dundi_switch
Definition: pbx_dundi.c:4886
static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
Definition: pbx_dundi.c:1395
static dundi_eid empty_eid
Definition: pbx_dundi.c:214
static char * dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:3056
static int dundi_ttl
Definition: pbx_dundi.c:194
static void cancel_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3680
static char phone[80]
Definition: pbx_dundi.c:207
static unsigned int tos
Definition: pbx_dundi.c:191
static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
Definition: pbx_dundi.c:1176
int dundi_precache(const char *context, const char *number)
Pre-cache to push upstream peers.
Definition: pbx_dundi.c:4074
static void drds_destroy(struct dundi_result_datastore *drds)
Definition: pbx_dundi.c:4205
static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
Definition: pbx_dundi.c:1592
static int netsocket
Definition: pbx_dundi.c:186
static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
Definition: pbx_dundi.c:4002
static int dundidebug
Definition: pbx_dundi.c:192
static struct ast_sched_context * sched
Definition: pbx_dundi.c:185
static int dundi_key_ttl
Definition: pbx_dundi.c:195
static int optimize_transactions(struct dundi_request *dr, int order)
Definition: pbx_dundi.c:3590
static int netsocket2
Definition: pbx_dundi.c:187
static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
Definition: pbx_dundi.c:3314
static char * dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2336
static char * dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2627
static char * dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2856
static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:946
static void build_secret(char *secret, int seclen)
Definition: pbx_dundi.c:2116
#define DUNDI_FLAG_INTERNAL_NOPARTIAL
Definition: pbx_dundi.c:176
static char locality[80]
Definition: pbx_dundi.c:203
static struct ast_custom_function dundi_query_function
Definition: pbx_dundi.c:4280
static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
Definition: pbx_dundi.c:1434
static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
Definition: pbx_dundi.c:4143
static int do_register_expire(const void *data)
Definition: pbx_dundi.c:1327
static int global_autokilltimeout
Definition: pbx_dundi.c:197
static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
Definition: pbx_dundi.c:3648
static int get_trans_id(void)
Definition: pbx_dundi.c:486
static int has_permission(struct permissionlist *permlist, char *cont)
Definition: pbx_dundi.c:369
static void save_secret(const char *newkey, const char *oldkey)
Definition: pbx_dundi.c:2131
static int outgoing_sip_tech
Definition: pbx_dundi.c:211
static int check_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3801
static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
Definition: pbx_dundi.c:1498
static char cursecret[80]
Definition: pbx_dundi.c:209
static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_dundi.c:4818
static char * dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2462
static void prune_peers(void)
Definition: pbx_dundi.c:4418
int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
Lookup the given number in the given dundi context. Lookup number in a given dundi context (if unspec...
Definition: pbx_dundi.c:3932
static struct dundi_peer * find_peer(dundi_eid *eid)
Definition: pbx_dundi.c:521
#define DUNDI_MODEL_INBOUND
Definition: pbx_dundi.c:159
static void build_iv(unsigned char *iv)
Definition: pbx_dundi.c:539
static int query_transactions(struct dundi_request *dr)
Definition: pbx_dundi.c:3577
static void dundi_debug_output(const char *data)
Definition: pbx_dundi.c:358
static unsigned long avoid_crc32(dundi_eid *avoid[])
Definition: pbx_dundi.c:3815
static void mark_mappings(void)
Definition: pbx_dundi.c:4369
static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[])
Definition: pbx_dundi.c:4080
static void * dundi_query_thread(void *data)
Definition: pbx_dundi.c:760
static int global_storehistory
Definition: pbx_dundi.c:200
static time_t rotatetime
Definition: pbx_dundi.c:213
static int set_config(char *config_file, struct ast_sockaddr *sin, int reload, struct ast_sockaddr *sin2)
Definition: pbx_dundi.c:4931
static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
Definition: pbx_dundi.c:905
static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
Definition: pbx_dundi.c:3397
static char * dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2833
static int dundi_ack(struct dundi_transaction *trans, int final)
Definition: pbx_dundi.c:457
static int dundi_cache_time
Definition: pbx_dundi.c:196
static struct dundi_hdr * dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
Definition: pbx_dundi.c:1412
static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
Definition: pbx_dundi.c:4594
static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: pbx_dundi.c:4285
static void destroy_packets(struct packetlist *p)
Definition: pbx_dundi.c:2016
static void sort_results(struct dundi_result *results, int count)
Definition: pbx_dundi.c:2457
static void append_permission(struct permissionlist *permlist, const char *s, int allow)
Definition: pbx_dundi.c:4448
static struct ast_custom_function dundi_result_function
Definition: pbx_dundi.c:4354
static const struct ast_app_option dundi_query_opts[128]
Definition: pbx_dundi.c:4141
static void dundi_error_output(const char *data)
Definition: pbx_dundi.c:364
static unsigned int dundi_result_id
Definition: pbx_dundi.c:4197
static int register_request(struct dundi_request *dr, struct dundi_request **pending)
Definition: pbx_dundi.c:3764
@ OPT_BYPASS_CACHE
Definition: pbx_dundi.c:4136
static int dundi_discover(struct dundi_transaction *trans)
Definition: pbx_dundi.c:3419
static int get_ipaddress(char *ip, size_t size, const char *str, int family)
Definition: pbx_dundi.c:4895
static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
Definition: pbx_dundi.c:3830
static const struct ast_datastore_info dundi_result_datastore_info
Definition: pbx_dundi.c:4216
static pthread_t precachethreadid
Definition: pbx_dundi.c:189
#define MAX_PACKET_SIZE
Definition: pbx_dundi.c:155
static char * dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2518
static int ack_trans(struct dundi_transaction *trans, int iseqno)
Definition: pbx_dundi.c:2027
static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
Definition: pbx_dundi.c:4789
static void set_host_ipaddr(struct ast_sockaddr *sin)
Definition: pbx_dundi.c:4909
static char * dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2961
static void destroy_packet(struct dundi_packet *pack, int needfree)
Definition: pbx_dundi.c:3200
static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_dundi.c:4808
static 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[])
Definition: pbx_dundi.c:3706
static int authdebug
Definition: pbx_dundi.c:193
static int rescomp(const void *a, const void *b)
Definition: pbx_dundi.c:2445
#define MAX_WEIGHT
Definition: pbx_dundi.c:157
static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: pbx_dundi.c:4221
static void mark_peers(void)
Definition: pbx_dundi.c:4359
static uint16_t dundi_sockaddr_port(const struct ast_sockaddr *addr)
Definition: pbx_dundi.c:2615
static void destroy_permissions(struct permissionlist *permlist)
Definition: pbx_dundi.c:4380
static struct ast_cli_entry cli_dundi[]
Definition: pbx_dundi.c:3125
static char * tech2str(int tech)
Definition: pbx_dundi.c:382
static void prune_mappings(void)
Definition: pbx_dundi.c:4433
static char * dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2556
static void qualify_peer(struct dundi_peer *peer, int schedonly)
Definition: pbx_dundi.c:4571
#define DUNDI_MODEL_OUTBOUND
Definition: pbx_dundi.c:160
static int dundi_shutdown
Definition: pbx_dundi.c:215
static int dundi_query(struct dundi_transaction *trans)
Definition: pbx_dundi.c:3509
static pthread_t netthreadid
Definition: pbx_dundi.c:188
@ FLAG_ISQUAL
Definition: pbx_dundi.c:170
@ FLAG_DEAD
Definition: pbx_dundi.c:168
@ FLAG_SENDFULLKEY
Definition: pbx_dundi.c:172
@ FLAG_FINAL
Definition: pbx_dundi.c:169
@ FLAG_ISREG
Definition: pbx_dundi.c:167
@ FLAG_ENCRYPT
Definition: pbx_dundi.c:171
@ FLAG_STOREHIST
Definition: pbx_dundi.c:173
static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
Definition: pbx_dundi.c:3542
static char email[80]
Definition: pbx_dundi.c:206
static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
Definition: pbx_dundi.c:3443
static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd)
Definition: pbx_dundi.c:584
static int default_expiration
Definition: pbx_dundi.c:199
static int get_mapping_weight(struct dundi_mapping *map, struct varshead *headp)
Definition: pbx_dundi.c:565
static int do_autokill(const void *data)
Definition: pbx_dundi.c:3386
static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
Definition: pbx_dundi.c:1551
int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
Retrieve information on a specific EID.
Definition: pbx_dundi.c:4127
static void * network_thread(void *ignore)
Definition: pbx_dundi.c:2200
static char * dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2802
static struct io_context * io
Definition: pbx_dundi.c:184
static char ipaddr[80]
Definition: pbx_dundi.c:210
static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
Definition: pbx_dundi.c:4634
static void unregister_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3794
#define MAX_OPTS
Definition: pbx_dundi.c:4461
static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_dundi.c:4813
static int update_key(struct dundi_peer *peer)
Definition: pbx_dundi.c:1340
#define DUNDI_SECRET_TIME
Definition: pbx_dundi.c:181
static int load_module(void)
Definition: pbx_dundi.c:5173
static void build_mapping(const char *name, const char *value)
Definition: pbx_dundi.c:4463
static void abort_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3694
static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:815
static char * dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2889
static char * complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
Definition: pbx_dundi.c:2423
static char stateprov[80]
Definition: pbx_dundi.c:204
#define FORMAT
static struct dundi_peer * any_peer
Wildcard peer.
Definition: pbx_dundi.c:354
static char * dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2703
static int str2tech(const char *str)
Definition: pbx_dundi.c:400
static int dundi_xmit(struct dundi_packet *pack)
Definition: pbx_dundi.c:3175
#define FORMAT2
static int unload_module(void)
Definition: pbx_dundi.c:5103
static int reload(void)
Definition: pbx_dundi.c:5159
static void * process_precache(void *ign)
Definition: pbx_dundi.c:2267
static int dundi_rexmit(const void *data)
Definition: pbx_dundi.c:3291
static void dundi_reject(struct dundi_hdr *h, struct ast_sockaddr *sin)
Definition: pbx_dundi.c:461
static char org[80]
Definition: pbx_dundi.c:202
static void * dundi_lookup_thread(void *data)
Definition: pbx_dundi.c:663
static char * dundi_sockaddr_stringify_host(const struct ast_sockaddr *addr)
Definition: pbx_dundi.c:2607
static char pjsip_outgoing_endpoint[80]
Definition: pbx_dundi.c:212
static char * dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2363
static char dept[80]
Definition: pbx_dundi.c:201
static struct dundi_transaction * find_transaction(struct dundi_hdr *hdr, struct ast_sockaddr *sin)
Definition: pbx_dundi.c:418
static char * model2str(int model)
Definition: pbx_dundi.c:2409
static void destroy_map(struct dundi_mapping *map)
Definition: pbx_dundi.c:4412
static int do_qualify(const void *data)
Definition: pbx_dundi.c:4563
#define DUNDI_TIMING_HISTORY
Definition: pbx_dundi.c:164
static void destroy_peer(struct dundi_peer *peer)
Definition: pbx_dundi.c:4388
static void * process_clearcache(void *ignore)
Definition: pbx_dundi.c:2234
static int start_network_thread(void)
Definition: pbx_dundi.c:2302
static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datalen)
Definition: pbx_dundi.c:2048
static int do_register(const void *data)
Definition: pbx_dundi.c:4537
static void dundi_precache_full(void)
Definition: pbx_dundi.c:3978
static void drds_destroy_cb(void *data)
Definition: pbx_dundi.c:4210
static char * dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2923
static pthread_t clearcachethreadid
Definition: pbx_dundi.c:190
static void * dundi_precache_thread(void *data)
Definition: pbx_dundi.c:727
static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_dundi.c:4881
static int discover_transactions(struct dundi_request *dr)
Definition: pbx_dundi.c:3531
static dundi_eid global_eid
Definition: pbx_dundi.c:198
static char secretpath[80]
Definition: pbx_dundi.c:208
static char pass[512]
#define NULL
Definition: resample.c:96
Scheduler Routines (derived from cheops)
#define AST_SCHED_DEL(sched, id)
Remove a scheduler entry.
Definition: sched.h:46
int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event.
Definition: sched.c:567
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:271
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
Definition: sched.c:786
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:238
int ast_sched_wait(struct ast_sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place.
Definition: sched.c:433
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
Parse a time (integer) string.
Definition: utils.c:2446
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Definition: crypto.h:39
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
ast_context: An extension context
Definition: pbx.c:284
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
Definition: astdb.h:31
struct ast_db_entry * next
Definition: astdb.h:32
char * key
Definition: astdb.h:33
char data[0]
Definition: astdb.h:34
An Entity ID is essentially a MAC address, brief and unique.
Definition: utils.h:813
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition: pbx.c:237
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Socket address structure.
Definition: netsock2.h:97
const char * name
Definition: pbx.h:162
struct ast_var_t::@211 entries
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
unsigned short flags
Definition: dundi.h:107
unsigned short weight
Definition: dundi.h:108
unsigned char protocol
Definition: dundi.h:106
unsigned char data[0]
Definition: dundi.h:109
dundi_eid eid
Definition: dundi.h:105
unsigned char iv[16]
Definition: dundi.h:100
unsigned char encdata[0]
Definition: dundi.h:101
char country[80]
Definition: dundi.h:239
char phone[80]
Definition: dundi.h:245
char orgunit[80]
Definition: dundi.h:243
char locality[80]
Definition: dundi.h:241
char email[80]
Definition: dundi.h:244
char ipaddr[80]
Definition: dundi.h:246
char stateprov[80]
Definition: dundi.h:240
char org[80]
Definition: dundi.h:242
unsigned short strans
Definition: dundi.h:35
unsigned char ies[0]
Definition: dundi.h:41
unsigned char cmdflags
Definition: dundi.h:40
unsigned char cmdresp
Definition: dundi.h:39
unsigned char iseqno
Definition: dundi.h:37
unsigned short dtrans
Definition: dundi.h:36
unsigned char oseqno
Definition: dundi.h:38
unsigned short flags
Definition: pbx_dundi.c:234
char exten[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:235
unsigned char data[0]
Definition: dundi.h:114
unsigned char buf[8192]
Definition: dundi-parser.h:56
struct dundi_answer * answers[DUNDI_MAX_ANSWERS+1]
Definition: dundi-parser.h:28
char * q_org
Definition: dundi-parser.h:38
dundi_eid * reqeid
Definition: dundi-parser.h:24
dundi_eid * eids[DUNDI_MAX_STACK+1]
Definition: dundi-parser.h:22
char * called_context
Definition: dundi-parser.h:26
int eid_direct[DUNDI_MAX_STACK+1]
Definition: dundi-parser.h:23
char * q_country
Definition: dundi-parser.h:41
char * q_phone
Definition: dundi-parser.h:43
char * q_locality
Definition: dundi-parser.h:39
char * q_ipaddr
Definition: dundi-parser.h:44
char * called_number
Definition: dundi-parser.h:27
int eidcount
Definition: dundi-parser.h:25
unsigned long keycrc32
Definition: dundi-parser.h:48
unsigned char * encsharedkey
Definition: dundi-parser.h:46
struct dundi_hint * hint
Definition: dundi-parser.h:29
int anscount
Definition: dundi-parser.h:30
char * q_email
Definition: dundi-parser.h:42
unsigned char * encsig
Definition: dundi-parser.h:47
char * q_dept
Definition: dundi-parser.h:37
int expiration
Definition: dundi-parser.h:33
char * q_stateprov
Definition: dundi-parser.h:40
struct dundi_encblock * encblock
Definition: dundi-parser.h:49
char lcontext[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:295
char * weightstr
Definition: pbx_dundi.c:297
struct dundi_mapping::@417 list
char dest[512]
Definition: pbx_dundi.c:301
char dcontext[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:294
struct dundi_mapping * next
Definition: pbx_dundi.c:302
struct dundi_hdr * h
Definition: pbx_dundi.c:225
struct dundi_packet::@411 list
unsigned char data[0]
Definition: pbx_dundi.c:230
struct dundi_transaction * parent
Definition: pbx_dundi.c:227
int lookuptimes[DUNDI_TIMING_HISTORY]
Definition: pbx_dundi.c:328
unsigned char txenckey[256]
Definition: pbx_dundi.c:318
char outkey[80]
Definition: pbx_dundi.c:312
struct ast_sockaddr addr
Definition: pbx_dundi.c:307
char * lookups[DUNDI_TIMING_HISTORY]
Definition: pbx_dundi.c:329
uint32_t them_keycrc32
Definition: pbx_dundi.c:323
dundi_eid us_eid
Definition: pbx_dundi.c:310
unsigned int dynamic
Definition: pbx_dundi.c:336
unsigned char rxenckey[256]
Definition: pbx_dundi.c:319
uint32_t us_keycrc32
Definition: pbx_dundi.c:320
struct timeval qualtx
Definition: pbx_dundi.c:339
ast_aes_encrypt_key us_ecx
Definition: pbx_dundi.c:321
struct dundi_peer::permissionlist permit
int lastms
Definition: pbx_dundi.c:337
int qualifyid
Definition: pbx_dundi.c:315
struct dundi_transaction * regtrans
Definition: pbx_dundi.c:331
time_t keyexpire
Definition: pbx_dundi.c:326
ast_aes_decrypt_key them_dcx
Definition: pbx_dundi.c:325
char inkey[80]
Definition: pbx_dundi.c:311
int sentfullkey
Definition: pbx_dundi.c:316
struct dundi_peer::@418 list
struct dundi_transaction * qualtrans
Definition: pbx_dundi.c:332
int registerid
Definition: pbx_dundi.c:314
ast_aes_encrypt_key them_ecx
Definition: pbx_dundi.c:324
int pcmodel
Definition: pbx_dundi.c:334
struct permissionlist include
Definition: pbx_dundi.c:309
int registerexpire
Definition: pbx_dundi.c:327
ast_aes_decrypt_key us_dcx
Definition: pbx_dundi.c:322
dundi_eid eid
Definition: pbx_dundi.c:306
struct dundi_precache_queue::@412 list
char called_context[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:553
struct dundi_mapping * maps
Definition: pbx_dundi.c:555
int directs[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:551
dundi_eid * eids[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:550
char called_number[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:554
dundi_eid reqeid
Definition: pbx_dundi.c:552
struct dundi_transaction * trans
Definition: pbx_dundi.c:558
int pfds[2]
Definition: pbx_dundi.c:287
struct dundi_entity_info * dei
Definition: pbx_dundi.c:281
struct dundi_request::@415 trans
uint32_t crc32
Definition: pbx_dundi.c:288
struct dundi_hint_metadata * hmd
Definition: pbx_dundi.c:282
struct dundi_result * dr
Definition: pbx_dundi.c:280
struct dundi_request::@416 list
dundi_eid query_eid
Definition: pbx_dundi.c:278
char number[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:277
dundi_eid root_eid
Definition: pbx_dundi.c:279
char dcontext[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:276
struct dundi_result results[MAX_RESULTS]
Definition: pbx_dundi.c:4200
char dest[256]
Definition: dundi.h:235
char eid_str[20]
Definition: dundi.h:233
int techint
Definition: dundi.h:231
char tech[10]
Definition: dundi.h:234
int weight
Definition: dundi.h:229
unsigned int flags
Definition: dundi.h:228
int expiration
Definition: dundi.h:230
dundi_eid eid
Definition: dundi.h:232
unsigned short strans
Definition: pbx_dundi.c:262
struct ast_sockaddr addr
Definition: pbx_dundi.c:248
unsigned char oiseqno
Definition: pbx_dundi.c:265
dundi_eid us_eid
Definition: pbx_dundi.c:252
dundi_eid them_eid
Definition: pbx_dundi.c:253
struct dundi_transaction::packetlist packets
struct dundi_transaction::@414 all
unsigned char iseqno
Definition: pbx_dundi.c:264
struct packetlist lasttrans
Definition: pbx_dundi.c:269
struct dundi_transaction::@413 parentlist
dundi_eid eids[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:250
unsigned short dtrans
Definition: pbx_dundi.c:263
unsigned char oseqno
Definition: pbx_dundi.c:266
unsigned char aseqno
Definition: pbx_dundi.c:267
ast_aes_decrypt_key dcx
Definition: pbx_dundi.c:255
unsigned int flags
Definition: pbx_dundi.c:256
struct dundi_request * parent
Definition: pbx_dundi.c:270
struct timeval start
Definition: pbx_dundi.c:249
ast_aes_encrypt_key ecx
Definition: pbx_dundi.c:254
Global IO variables are now in a struct in order to be made threadsafe.
Definition: io.c:71
Number structure.
Definition: app_followme.c:154
Definition: pbx_dundi.c:344
struct permission::@410 list
char name[0]
Definition: pbx_dundi.c:220
Definition: sched.c:76
int value
Definition: syslog.c:37
static float dr[4]
Definition: tdd.c:58
const char * args
static struct test_options options
static struct test_val b
static struct test_val a
static struct test_val c
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define ast_set_flag_nonstd(p, flag)
Definition: utils.h:176
#define ast_clear_flag_nonstd(p, flag)
Definition: utils.h:180
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
Definition: utils.c:3094
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: utils.c:406
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:592
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: utils.c:2839
#define ast_clear_flag(p, flag)
Definition: utils.h:77
long int ast_random(void)
Definition: utils.c:2312
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:588
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
Definition: utils.c:3099
#define ast_test_flag_nonstd(p, flag)
Definition: utils.h:173
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:666
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
struct ast_eid ast_eid_default
Global EID.
Definition: options.c:93
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
Definition: utils.c:3077
#define AST_FLAGS_ALL
Definition: utils.h:196