Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
80 <version>1.8.0</version>
81 </since>
82 <synopsis>
83 Do a DUNDi lookup of a phone number.
84 </synopsis>
85 <syntax>
86 <parameter name="number" required="true"/>
87 <parameter name="context">
88 <para>If not specified the default will be <literal>e164</literal>.</para>
89 </parameter>
90 <parameter name="options">
91 <optionlist>
92 <option name="b">
93 <para>Bypass the internal DUNDi cache</para>
94 </option>
95 </optionlist>
96 </parameter>
97 </syntax>
98 <description>
99 <para>This will do a DUNDi lookup of the given phone number.</para>
100 <para>This function will return the Technology/Resource found in the first result
101 in the DUNDi lookup. If no results were found, the result will be blank.</para>
102 </description>
103 </function>
104
105
106 <function name="DUNDIQUERY" language="en_US">
107 <since>
108 <version>1.8.0</version>
109 </since>
110 <synopsis>
111 Initiate a DUNDi query.
112 </synopsis>
113 <syntax>
114 <parameter name="number" required="true"/>
115 <parameter name="context">
116 <para>If not specified the default will be <literal>e164</literal>.</para>
117 </parameter>
118 <parameter name="options">
119 <optionlist>
120 <option name="b">
121 <para>Bypass the internal DUNDi cache</para>
122 </option>
123 </optionlist>
124 </parameter>
125 </syntax>
126 <description>
127 <para>This will do a DUNDi lookup of the given phone number.</para>
128 <para>The result of this function will be a numeric ID that can be used to retrieve
129 the results with the <literal>DUNDIRESULT</literal> function.</para>
130 </description>
131 </function>
132
133 <function name="DUNDIRESULT" language="en_US">
134 <since>
135 <version>1.8.0</version>
136 </since>
137 <synopsis>
138 Retrieve results from a DUNDIQUERY.
139 </synopsis>
140 <syntax>
141 <parameter name="id" required="true">
142 <para>The identifier returned by the <literal>DUNDIQUERY</literal> function.</para>
143 </parameter>
144 <parameter name="resultnum">
145 <optionlist>
146 <option name="number">
147 <para>The number of the result that you want to retrieve, this starts at <literal>1</literal></para>
148 </option>
149 <option name="getnum">
150 <para>The total number of results that are available.</para>
151 </option>
152 </optionlist>
153 </parameter>
154 </syntax>
155 <description>
156 <para>This function will retrieve results from a previous use\n"
157 of the <literal>DUNDIQUERY</literal> function.</para>
158 </description>
159 </function>
160 ***/
161
162#define MAX_RESULTS 64
163
164#define MAX_PACKET_SIZE 8192
165
166#define MAX_WEIGHT 59999
167
168#define DUNDI_MODEL_INBOUND (1 << 0)
169#define DUNDI_MODEL_OUTBOUND (1 << 1)
170#define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
171
172/*! Keep times of last 10 lookups */
173#define DUNDI_TIMING_HISTORY 10
174
175enum {
176 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
177 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
178 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
179 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
180 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted with ECX/DCX */
181 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
182 FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
183};
184
185#define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
186
187#if 0
188#define DUNDI_SECRET_TIME 15 /* Testing only */
189#else
190#define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
191#endif
192
193static struct io_context *io;
195static int netsocket = -1; /* Socket for bindaddr if only one bindaddr. Otherwise the IPv4 socket when bindaddr2 given. */
196static int netsocket2 = -1; /* IPv6 socket when bindaddr2 given. */
200static unsigned int tos = 0;
201static int dundidebug = 0;
202static int authdebug = 0;
208static int default_expiration = 60;
209static int global_storehistory = 0;
210static char dept[80];
211static char org[80];
212static char locality[80];
213static char stateprov[80];
214static char country[80];
215static char email[80];
216static char phone[80];
217static char secretpath[80];
218static char cursecret[80];
219static char ipaddr[80];
222static time_t rotatetime;
223static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
224static int dundi_shutdown = 0;
225
228 int allow;
229 char name[0];
230};
231
234 struct dundi_hdr *h;
239 unsigned char data[0];
240};
241
243 unsigned short flags;
245};
246
249 char *context;
251 char number[0];
252};
253
254struct dundi_request;
255
257 struct ast_sockaddr addr; /*!< Other end of transaction */
258 struct timeval start; /*!< When this transaction was created */
260 int eidcount; /*!< Number of eids in eids */
261 dundi_eid us_eid; /*!< Our EID, to them */
262 dundi_eid them_eid; /*!< Their EID, to us */
263 ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
264 ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
265 unsigned int flags; /*!< Has final packet been sent */
266 int ttl; /*!< Remaining TTL for queries on this one */
267 int thread; /*!< We have a calling thread */
268 int retranstimer; /*!< How long to wait before retransmissions */
269 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
270 int autokilltimeout; /*!< Recommended timeout for autokill */
271 unsigned short strans; /*!< Our transaction identifier */
272 unsigned short dtrans; /*!< Their transaction identifer */
273 unsigned char iseqno; /*!< Next expected received seqno */
274 unsigned char oiseqno; /*!< Last received incoming seqno */
275 unsigned char oseqno; /*!< Next transmitted seqno */
276 unsigned char aseqno; /*!< Last acknowledge seqno */
277 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
278 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
279 struct dundi_request *parent; /*!< Parent request (if there is one) */
280 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
281 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
282};
283
296 int pfds[2];
297 uint32_t crc32; /*!< CRC-32 of all but root EID's in avoid list */
300};
301
308 int tech;
309 int dead;
310 char dest[512];
312};
313
316 struct ast_sockaddr addr; /*!< Address of DUNDi peer */
320 char inkey[80];
321 char outkey[80];
322 int dead;
326 int order;
327 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
328 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
329 uint32_t us_keycrc32; /*!< CRC-32 of our key */
330 ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
331 ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
332 uint32_t them_keycrc32; /*!< CRC-32 of our key */
333 ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
334 ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
335 time_t keyexpire; /*!< When to expire/recreate key */
339 int avgms;
340 struct dundi_transaction *regtrans; /*!< Registration transaction */
341 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
342 int model; /*!< Pull model */
343 int pcmodel; /*!< Push/precache model */
344 /*! Dynamic peers register with us */
345 unsigned int dynamic:1;
346 int lastms; /*!< Last measured latency */
347 int maxms; /*!< Max permissible latency */
348 struct timeval qualtx; /*!< Time of transmit */
350};
351
357
358/*!
359 * \brief Wildcard peer
360 *
361 * This peer is created if the [*] entry is specified in dundi.conf
362 */
363static struct dundi_peer *any_peer;
364
365static int dundi_xmit(struct dundi_packet *pack);
366
367static void dundi_debug_output(const char *data)
368{
369 if (dundidebug)
370 ast_verbose("%s", data);
371}
372
373static void dundi_error_output(const char *data)
374{
375 ast_log(LOG_WARNING, "%s", data);
376}
377
378static int has_permission(struct permissionlist *permlist, char *cont)
379{
380 struct permission *perm;
381 int res = 0;
382
383 AST_LIST_TRAVERSE(permlist, perm, list) {
384 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
385 res = perm->allow;
386 }
387
388 return res;
389}
390
391static char *tech2str(int tech)
392{
393 switch(tech) {
394 case DUNDI_PROTO_NONE:
395 return "None";
396 case DUNDI_PROTO_IAX:
397 return "IAX2";
398 case DUNDI_PROTO_SIP:
399 return "SIP";
400 case DUNDI_PROTO_H323:
401 return "H323";
403 return "PJSIP";
404 default:
405 return "Unknown";
406 }
407}
408
409static int str2tech(const char *str)
410{
411 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
412 return DUNDI_PROTO_IAX;
413 else if (!strcasecmp(str, "SIP"))
414 return DUNDI_PROTO_SIP;
415 else if (!strcasecmp(str, "H323"))
416 return DUNDI_PROTO_H323;
417 else if (!strcasecmp(str, "PJSIP"))
418 return DUNDI_PROTO_PJSIP;
419 else
420 return -1;
421}
422
423static 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[]);
424static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
425static struct dundi_transaction *create_transaction(struct dundi_peer *p);
426
427static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct ast_sockaddr *sin)
428{
429 struct dundi_transaction *trans;
430
431 /* Look for an exact match first */
432 AST_LIST_TRAVERSE(&alltrans, trans, all) {
433 if (!ast_sockaddr_cmp(&trans->addr, sin) &&
434 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
435 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
436 if (hdr->strans) {
437 trans->dtrans = ntohs(hdr->strans) & 32767;
438 }
439 return trans;
440 }
441 }
442
443 switch(hdr->cmdresp & 0x7f) {
450 if (!hdr->strans)
451 break;
452 /* Create new transaction */
453 if (!(trans = create_transaction(NULL)))
454 break;
455 ast_sockaddr_copy(&trans->addr, sin);
456 trans->dtrans = ntohs(hdr->strans) & 32767;
457 default:
458 break;
459 }
460
461 return trans;
462}
463
464static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
465
466static int dundi_ack(struct dundi_transaction *trans, int final)
467{
468 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
469}
470static void dundi_reject(struct dundi_hdr *h, struct ast_sockaddr *sin)
471{
472 struct {
473 struct dundi_packet pack;
474 struct dundi_hdr hdr;
475 } tmp;
476 struct dundi_transaction trans;
477 /* Never respond to an INVALID with another INVALID */
479 return;
480 memset(&tmp, 0, sizeof(tmp));
481 memset(&trans, 0, sizeof(trans));
482 ast_sockaddr_copy(&trans.addr, sin);
483 tmp.hdr.strans = h->dtrans;
484 tmp.hdr.dtrans = h->strans;
485 tmp.hdr.iseqno = h->oseqno;
486 tmp.hdr.oseqno = h->iseqno;
487 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
488 tmp.hdr.cmdflags = 0;
489 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
490 tmp.pack.datalen = sizeof(struct dundi_hdr);
491 tmp.pack.parent = &trans;
492 dundi_xmit(&tmp.pack);
493}
494
495static int get_trans_id(void)
496{
497 struct dundi_transaction *t;
498 int stid = (ast_random() % 32766) + 1;
499 int tid = stid;
500
501 do {
503 if (t->strans == tid)
504 break;
505 }
506 if (!t)
507 return tid;
508 tid = (tid % 32766) + 1;
509 } while (tid != stid);
510
511 return 0;
512}
513
514static int reset_transaction(struct dundi_transaction *trans)
515{
516 int tid;
517 tid = get_trans_id();
518 if (tid < 1)
519 return -1;
520 trans->strans = tid;
521 trans->dtrans = 0;
522 trans->iseqno = 0;
523 trans->oiseqno = 0;
524 trans->oseqno = 0;
525 trans->aseqno = 0;
527 return 0;
528}
529
531{
532 struct dundi_peer *cur = NULL;
533
534 if (!eid)
535 eid = &empty_eid;
536
538 if (!ast_eid_cmp(&cur->eid,eid))
539 break;
540 }
541
542 if (!cur && any_peer)
543 cur = any_peer;
544
545 return cur;
546}
547
548static void build_iv(unsigned char *iv)
549{
550 /* XXX Would be nice to be more random XXX */
551 unsigned int *fluffy;
552 int x;
553 fluffy = (unsigned int *)(iv);
554 for (x=0;x<4;x++)
555 fluffy[x] = ast_random();
556}
557
568 void *chal;
570 int ttl;
571 char fluffy[0];
572};
573
574static int get_mapping_weight(struct dundi_mapping *map, struct varshead *headp)
575{
576 char buf[32];
577
578 buf[0] = 0;
579 if (map->weightstr) {
580 if (headp) {
581 pbx_substitute_variables_varshead(headp, map->weightstr, buf, sizeof(buf) - 1);
582 } else {
584 }
585
586 if (sscanf(buf, "%30d", &map->_weight) != 1)
587 map->_weight = MAX_WEIGHT;
588 }
589
590 return map->_weight;
591}
592
593static 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)
594{
595 struct ast_flags flags = {0};
596 int x;
597 if (!ast_strlen_zero(map->lcontext)) {
598 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
600 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
602 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
604 if (ast_ignore_pattern(map->lcontext, called_number))
606
607 /* Clearly we can't say 'don't ask' anymore if we found anything... */
610
612 /* Skip partial answers */
614 }
616 struct varshead headp;
617 struct ast_var_t *newvariable;
618 ast_set_flag(&flags, map->options & 0xffff);
619 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
620 dr[anscnt].techint = map->tech;
621 dr[anscnt].expiration = dundi_cache_time;
622 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
623 dr[anscnt].eid = *us_eid;
624 ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
625 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
627 if ((newvariable = ast_var_assign("NUMBER", called_number))) {
628 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
629 }
630 if ((newvariable = ast_var_assign("EID", dr[anscnt].eid_str))) {
631 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
632 }
633 if ((newvariable = ast_var_assign("SECRET", cursecret))) {
634 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
635 }
636 if ((newvariable = ast_var_assign("IPADDR", ipaddr))) {
637 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
638 }
639 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
640 dr[anscnt].weight = get_mapping_weight(map, &headp);
641 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
642 ast_var_delete(newvariable);
643 } else {
644 dr[anscnt].dest[0] = '\0';
645 dr[anscnt].weight = get_mapping_weight(map, NULL);
646 }
647 anscnt++;
648 } else {
649 /* No answers... Find the fewest number of digits from the
650 number for which we have no answer. */
651 char tmp[AST_MAX_EXTENSION + 1] = "";
652 for (x = 0; x < (sizeof(tmp) - 1); x++) {
653 tmp[x] = called_number[x];
654 if (!tmp[x])
655 break;
656 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
657 /* Oops found something we can't match. If this is longer
658 than the running hint, we have to consider it */
659 if (strlen(tmp) > strlen(hmd->exten)) {
660 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
661 }
662 break;
663 }
664 }
665 }
666 }
667 return anscnt;
668}
669
670static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
671
672static void *dundi_lookup_thread(void *data)
673{
674 struct dundi_query_state *st = data;
676 struct dundi_ie_data ied;
677 struct dundi_hint_metadata hmd;
678 char eid_str[20];
679 int res, x;
680 int ouranswers=0;
681 int max = 999999;
682 int expiration = dundi_cache_time;
683
684 ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
685 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
686 memset(&ied, 0, sizeof(ied));
687 memset(&dr, 0, sizeof(dr));
688 memset(&hmd, 0, sizeof(hmd));
689 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
691 for (x=0;x<st->nummaps;x++)
692 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
693 if (ouranswers < 0)
694 ouranswers = 0;
695 for (x=0;x<ouranswers;x++) {
696 if (dr[x].weight < max)
697 max = dr[x].weight;
698 }
699
700 if (max) {
701 /* If we do not have a canonical result, keep looking */
702 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);
703 if (res > 0) {
704 /* Append answer in result */
705 ouranswers += res;
706 } else {
707 if ((res < -1) && (!ouranswers))
708 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
709 }
710 }
712 /* Truncate if "don't ask" isn't present */
714 hmd.exten[0] = '\0';
715 if (ast_test_flag(st->trans, FLAG_DEAD)) {
716 ast_debug(1, "Our transaction went away!\n");
717 st->trans->thread = 0;
718 destroy_trans(st->trans, 0);
719 } else {
720 for (x=0;x<ouranswers;x++) {
721 /* Add answers */
722 if (dr[x].expiration && (expiration > dr[x].expiration))
723 expiration = dr[x].expiration;
724 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
725 }
728 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
729 st->trans->thread = 0;
730 }
732 ast_free(st);
733 return NULL;
734}
735
736static void *dundi_precache_thread(void *data)
737{
738 struct dundi_query_state *st = data;
739 struct dundi_ie_data ied;
740 struct dundi_hint_metadata hmd = {0};
741 char eid_str[20];
742
743 ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
744 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
745 memset(&ied, 0, sizeof(ied));
746
747 /* Now produce precache */
749
751 /* Truncate if "don't ask" isn't present */
753 hmd.exten[0] = '\0';
754 if (ast_test_flag(st->trans, FLAG_DEAD)) {
755 ast_debug(1, "Our transaction went away!\n");
756 st->trans->thread = 0;
757 destroy_trans(st->trans, 0);
758 } else {
759 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
760 st->trans->thread = 0;
761 }
763 ast_free(st);
764 return NULL;
765}
766
767static 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[]);
768
769static void *dundi_query_thread(void *data)
770{
771 struct dundi_query_state *st = data;
772 struct dundi_entity_info dei;
773 struct dundi_ie_data ied;
774 struct dundi_hint_metadata hmd;
775 char eid_str[20];
776 int res;
777
778 ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
779 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
780 memset(&ied, 0, sizeof(ied));
781 memset(&dei, 0, sizeof(dei));
782 memset(&hmd, 0, sizeof(hmd));
783 if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
784 /* Ooh, it's us! */
785 ast_debug(1, "Neat, someone look for us!\n");
786 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
787 ast_copy_string(dei.org, org, sizeof(dei.org));
788 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
789 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
790 ast_copy_string(dei.country, country, sizeof(dei.country));
791 ast_copy_string(dei.email, email, sizeof(dei.email));
792 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
793 res = 1;
794 } else {
795 /* If we do not have a canonical result, keep looking */
796 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
797 }
799 if (ast_test_flag(st->trans, FLAG_DEAD)) {
800 ast_debug(1, "Our transaction went away!\n");
801 st->trans->thread = 0;
802 destroy_trans(st->trans, 0);
803 } else {
804 if (res) {
812 if (!ast_strlen_zero(dei.ipaddr))
814 }
817 st->trans->thread = 0;
818 }
820 ast_free(st);
821 return NULL;
822}
823
824static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
825{
826 struct dundi_query_state *st;
827 int totallen;
828 int x;
829 int skipfirst=0;
830 char eid_str[20];
831 char *s;
832 pthread_t lookupthread;
833
834 if (ies->eidcount > 1) {
835 /* Since it is a requirement that the first EID is the authenticating host
836 and the last EID is the root, it is permissible that the first and last EID
837 could be the same. In that case, we should go ahead copy only the "root" section
838 since we will not need it for authentication. */
839 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
840 skipfirst = 1;
841 }
842 totallen = sizeof(struct dundi_query_state);
843 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
844 st = ast_calloc(1, totallen);
845 if (st) {
847 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
848 st->trans = trans;
849 st->ttl = ies->ttl - 1;
850 if (st->ttl < 0)
851 st->ttl = 0;
852 s = st->fluffy;
853 for (x=skipfirst;ies->eids[x];x++) {
854 st->eids[x-skipfirst] = (dundi_eid *)s;
855 *st->eids[x-skipfirst] = *ies->eids[x];
856 s += sizeof(dundi_eid);
857 }
858 ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
859
860 trans->thread = 1;
861 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
862 struct dundi_ie_data ied = { 0, };
863 trans->thread = 0;
864 ast_log(LOG_WARNING, "Unable to create thread!\n");
865 ast_free(st);
867 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
868 return -1;
869 }
870 } else {
871 struct dundi_ie_data ied = { 0, };
873 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
874 return -1;
875 }
876 return 0;
877}
878
879static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
880{
881 int unaffected;
882 char key1[256];
883 char key2[256];
884 char eidpeer_str[20];
885 char eidroot_str[20];
886 char data[80];
887 time_t timeout;
888
889 if (expiration < 0)
890 expiration = dundi_cache_time;
891
892 /* Only cache hint if "don't ask" is there... */
893 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
894 return 0;
895
896 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
897
898 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
899 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
900 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08x", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
901 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
902
903 time(&timeout);
904 timeout += expiration;
905 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
906
907 ast_db_put("dundi/cache", key1, data);
908 ast_debug(1, "Caching hint at '%s'\n", key1);
909 ast_db_put("dundi/cache", key2, data);
910 ast_debug(1, "Caching hint at '%s'\n", key2);
911 return 0;
912}
913
914static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
915{
916 int x;
917 char key1[256];
918 char key2[256];
919 char data[1024];
920 char eidpeer_str[20];
921 char eidroot_str[20];
922 time_t timeout;
923
924 if (expiration < 1)
925 expiration = dundi_cache_time;
926
927 /* Keep pushes a little longer, cut pulls a little short */
928 if (push)
929 expiration += 10;
930 else
931 expiration -= 10;
932 if (expiration < 1)
933 expiration = 1;
934 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
935 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
936 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08x", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
937 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
938 /* Build request string */
939 time(&timeout);
940 timeout += expiration;
941 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
942 for (x=start;x<req->respcount;x++) {
943 /* Skip anything with an illegal pipe in it */
944 if (strchr(req->dr[x].dest, '|'))
945 continue;
946 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%u/%d/%d/%s/%s|",
947 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
948 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
949 }
950 ast_db_put("dundi/cache", key1, data);
951 ast_db_put("dundi/cache", key2, data);
952 return 0;
953}
954
955static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
956{
957 struct dundi_query_state *st;
958 int totallen;
959 int x,z;
960 struct dundi_ie_data ied;
961 char *s;
962 struct dundi_result dr2[MAX_RESULTS];
963 struct dundi_request dr;
964 struct dundi_hint_metadata hmd;
965
966 struct dundi_mapping *cur;
967 int mapcount;
968 int skipfirst = 0;
969
970 pthread_t lookupthread;
971
972 memset(&dr2, 0, sizeof(dr2));
973 memset(&dr, 0, sizeof(dr));
974 memset(&hmd, 0, sizeof(hmd));
975
976 /* Forge request structure to hold answers for cache */
978 dr.dr = dr2;
979 dr.maxcount = MAX_RESULTS;
980 dr.expiration = dundi_cache_time;
981 dr.hmd = &hmd;
982 dr.pfds[0] = dr.pfds[1] = -1;
983 trans->parent = &dr;
984 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
985 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
986
987 for (x=0;x<ies->anscount;x++) {
988 if (trans->parent->respcount < trans->parent->maxcount) {
989 /* Make sure it's not already there */
990 for (z=0;z<trans->parent->respcount;z++) {
991 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
992 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
993 break;
994 }
995 if (z == trans->parent->respcount) {
996 /* Copy into parent responses */
997 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
998 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
999 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
1000 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
1001 if (ies->expiration > 0)
1002 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
1003 else
1006 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1007 &ies->answers[x]->eid);
1008 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
1009 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1011 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1012 trans->parent->respcount++;
1014 } else if (trans->parent->dr[z].weight > ntohs(ies->answers[x]->weight)) {
1015 /* Update weight if appropriate */
1016 trans->parent->dr[z].weight = ntohs(ies->answers[x]->weight);
1017 }
1018 } else
1019 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
1020 trans->parent->number, trans->parent->dcontext);
1021
1022 }
1023 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
1024 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
1025 if (ies->hint)
1026 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
1027
1028 totallen = sizeof(struct dundi_query_state);
1029 /* Count matching map entries */
1030 mapcount = 0;
1031 AST_LIST_TRAVERSE(&mappings, cur, list) {
1032 if (!strcasecmp(cur->dcontext, ccontext))
1033 mapcount++;
1034 }
1035
1036 /* If no maps, return -1 immediately */
1037 if (!mapcount)
1038 return -1;
1039
1040 if (ies->eidcount > 1) {
1041 /* Since it is a requirement that the first EID is the authenticating host
1042 and the last EID is the root, it is permissible that the first and last EID
1043 could be the same. In that case, we should go ahead copy only the "root" section
1044 since we will not need it for authentication. */
1045 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1046 skipfirst = 1;
1047 }
1048
1049 /* Prepare to run a query and then propagate that as necessary */
1050 totallen += mapcount * sizeof(struct dundi_mapping);
1051 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1052 st = ast_calloc(1, totallen);
1053 if (st) {
1054 ast_copy_string(st->called_context, dr.dcontext, sizeof(st->called_context));
1056 st->trans = trans;
1057 st->ttl = ies->ttl - 1;
1058 st->nocache = ies->cbypass;
1059 if (st->ttl < 0)
1060 st->ttl = 0;
1061 s = st->fluffy;
1062 for (x=skipfirst;ies->eids[x];x++) {
1063 st->eids[x-skipfirst] = (dundi_eid *)s;
1064 *st->eids[x-skipfirst] = *ies->eids[x];
1065 st->directs[x-skipfirst] = ies->eid_direct[x];
1066 s += sizeof(dundi_eid);
1067 }
1068 /* Append mappings */
1069 x = 0;
1070 st->maps = (struct dundi_mapping *)s;
1072 if (!strcasecmp(cur->dcontext, ccontext)) {
1073 if (x < mapcount) {
1074 st->maps[x] = *cur;
1075 st->maps[x].list.next = NULL;
1076 x++;
1077 }
1078 }
1079 }
1080 st->nummaps = mapcount;
1081 ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1082 trans->thread = 1;
1083 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1084 trans->thread = 0;
1085 ast_log(LOG_WARNING, "Unable to create thread!\n");
1086 ast_free(st);
1087 memset(&ied, 0, sizeof(ied));
1089 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1090 return -1;
1091 }
1092 } else {
1093 ast_log(LOG_WARNING, "Out of memory!\n");
1094 memset(&ied, 0, sizeof(ied));
1096 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1097 return -1;
1098 }
1099 return 0;
1100}
1101
1102static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1103{
1104 struct dundi_query_state *st;
1105 int totallen;
1106 int x;
1107 struct dundi_ie_data ied;
1108 char *s;
1109 struct dundi_mapping *cur;
1110 int mapcount = 0;
1111 int skipfirst = 0;
1112
1113 pthread_t lookupthread;
1114 totallen = sizeof(struct dundi_query_state);
1115 /* Count matching map entries */
1116 AST_LIST_TRAVERSE(&mappings, cur, list) {
1117 if (!strcasecmp(cur->dcontext, ccontext))
1118 mapcount++;
1119 }
1120 /* If no maps, return -1 immediately */
1121 if (!mapcount)
1122 return -1;
1123
1124 if (ies->eidcount > 1) {
1125 /* Since it is a requirement that the first EID is the authenticating host
1126 and the last EID is the root, it is permissible that the first and last EID
1127 could be the same. In that case, we should go ahead copy only the "root" section
1128 since we will not need it for authentication. */
1129 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1130 skipfirst = 1;
1131 }
1132
1133 totallen += mapcount * sizeof(struct dundi_mapping);
1134 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1135 st = ast_calloc(1, totallen);
1136 if (st) {
1139 st->trans = trans;
1140 st->ttl = ies->ttl - 1;
1141 st->nocache = ies->cbypass;
1142 if (st->ttl < 0)
1143 st->ttl = 0;
1144 s = st->fluffy;
1145 for (x=skipfirst;ies->eids[x];x++) {
1146 st->eids[x-skipfirst] = (dundi_eid *)s;
1147 *st->eids[x-skipfirst] = *ies->eids[x];
1148 st->directs[x-skipfirst] = ies->eid_direct[x];
1149 s += sizeof(dundi_eid);
1150 }
1151 /* Append mappings */
1152 x = 0;
1153 st->maps = (struct dundi_mapping *)s;
1155 if (!strcasecmp(cur->dcontext, ccontext)) {
1156 if (x < mapcount) {
1157 st->maps[x] = *cur;
1158 st->maps[x].list.next = NULL;
1159 x++;
1160 }
1161 }
1162 }
1163 st->nummaps = mapcount;
1164 ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1165 trans->thread = 1;
1166 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1167 trans->thread = 0;
1168 ast_log(LOG_WARNING, "Unable to create thread!\n");
1169 ast_free(st);
1170 memset(&ied, 0, sizeof(ied));
1172 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1173 return -1;
1174 }
1175 } else {
1176 ast_log(LOG_WARNING, "Out of memory!\n");
1177 memset(&ied, 0, sizeof(ied));
1179 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1180 return -1;
1181 }
1182 return 0;
1183}
1184
1185static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1186{
1187 char data[1024];
1188 char *ptr, *term, *src;
1189 int tech;
1190 struct ast_flags flags;
1191 int weight;
1192 int length;
1193 int z;
1194 char fs[256];
1195
1196 /* Build request string */
1197 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1198 time_t timeout;
1199 ptr = data;
1200 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1201 int expiration = timeout - now;
1202 if (expiration > 0) {
1203 ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
1204 ptr += length + 1;
1205 while((sscanf(ptr, "%30d/%30d/%30d/%n", (int *)&(flags.flags), &weight, &tech, &length) == 3)) {
1206 ptr += length;
1207 term = strchr(ptr, '|');
1208 if (term) {
1209 *term = '\0';
1210 src = strrchr(ptr, '/');
1211 if (src) {
1212 *src = '\0';
1213 src++;
1214 } else
1215 src = "";
1216 ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1217 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1218 /* Make sure it's not already there */
1219 for (z=0;z<req->respcount;z++) {
1220 if ((req->dr[z].techint == tech) &&
1221 !strcmp(req->dr[z].dest, ptr))
1222 break;
1223 }
1224 if (z == req->respcount) {
1225 /* Copy into parent responses */
1226 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1227 req->dr[req->respcount].weight = weight;
1228 req->dr[req->respcount].techint = tech;
1229 req->dr[req->respcount].expiration = expiration;
1230 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1232 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1233 ast_copy_string(req->dr[req->respcount].dest, ptr,
1234 sizeof(req->dr[req->respcount].dest));
1235 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1236 sizeof(req->dr[req->respcount].tech));
1237 req->respcount++;
1239 } else if (req->dr[z].weight > weight)
1240 req->dr[z].weight = weight;
1241 ptr = term + 1;
1242 }
1243 }
1244 /* We found *something* cached */
1245 if (expiration < *lowexpiration)
1246 *lowexpiration = expiration;
1247 return 1;
1248 } else
1249 ast_db_del("dundi/cache", key);
1250 } else
1251 ast_db_del("dundi/cache", key);
1252 }
1253
1254 return 0;
1255}
1256
1257static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc, int *lowexpiration)
1258{
1259 char eid_str[20];
1260 char eidroot_str[20];
1261 time_t now;
1262 int res=0;
1263 int res2=0;
1264 char eid_str_full[20];
1265 char tmp[256]="";
1266 /* Enough space for largest value that can be stored in key. */
1267 char key[sizeof(eid_str) + sizeof(tmp) + sizeof(req->dcontext) + sizeof(eidroot_str) + sizeof("hint////r")];
1268 int x;
1269
1270 time(&now);
1271 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1272 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1273 ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1274 snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, crc);
1275 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1276 snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, (unsigned)0);
1277 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1278 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1279 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1280 x = 0;
1281 if (!req->respcount) {
1282 while(!res2) {
1283 /* Look and see if we have a hint that would preclude us from looking at this
1284 peer for this number. */
1285 if (!(tmp[x] = req->number[x]))
1286 break;
1287 x++;
1288 /* Check for hints */
1289 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, crc);
1290 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1291 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, (unsigned)0);
1292 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1293 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1294 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1295 if (res2) {
1296 if (strlen(tmp) > strlen(req->hmd->exten)) {
1297 /* Update meta data if appropriate */
1298 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1299 }
1300 }
1301 }
1302 res |= res2;
1303 }
1304
1305 return res;
1306}
1307
1308static void qualify_peer(struct dundi_peer *peer, int schedonly);
1309
1310static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1311{
1312 if (ast_sockaddr_isnull(&trans->addr)) {
1313 ast_sockaddr_copy(&trans->addr, &p->addr);
1314 }
1315 trans->us_eid = p->us_eid;
1316 trans->them_eid = p->eid;
1317 /* Enable encryption if appropriate */
1318 if (!ast_strlen_zero(p->inkey))
1319 ast_set_flag(trans, FLAG_ENCRYPT);
1320 if (p->maxms) {
1321 trans->autokilltimeout = p->maxms;
1323 if (p->lastms > 1) {
1324 trans->retranstimer = p->lastms * 2;
1325 /* Keep it from being silly */
1326 if (trans->retranstimer < 150)
1327 trans->retranstimer = 150;
1328 }
1331 } else
1333}
1334
1335/*! \note Called with the peers list already locked */
1336static int do_register_expire(const void *data)
1337{
1338 struct dundi_peer *peer = (struct dundi_peer *)data;
1339 char eid_str[20];
1340
1341 ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1342 ast_db_del("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid));
1343 peer->registerexpire = -1;
1344 peer->lastms = 0;
1345 ast_sockaddr_setnull(&peer->addr);
1346 return 0;
1347}
1348
1349static int update_key(struct dundi_peer *peer)
1350{
1351 unsigned char key[16];
1352 struct ast_key *ekey, *skey;
1353 char eid_str[20];
1354 int res;
1355 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1356 build_iv(key);
1357 ast_aes_set_encrypt_key(key, &peer->us_ecx);
1358 ast_aes_set_decrypt_key(key, &peer->us_dcx);
1359 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1360 if (!ekey) {
1361 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1362 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1363 return -1;
1364 }
1365 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1366 if (!skey) {
1367 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1368 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1369 return -1;
1370 }
1371 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1372 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1373 return -1;
1374 }
1375 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1376 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1377 return -1;
1378 }
1379 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1380 peer->sentfullkey = 0;
1381 /* Looks good */
1382 time(&peer->keyexpire);
1383 peer->keyexpire += dundi_key_ttl;
1384 }
1385 return 0;
1386}
1387
1388static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1389{
1390 unsigned char curblock[16];
1391 int x;
1392 memcpy(curblock, iv, sizeof(curblock));
1393 while(len > 0) {
1394 for (x=0;x<16;x++)
1395 curblock[x] ^= src[x];
1396 ast_aes_encrypt(curblock, dst, ecx);
1397 memcpy(curblock, dst, sizeof(curblock));
1398 dst += 16;
1399 src += 16;
1400 len -= 16;
1401 }
1402 return 0;
1403}
1404static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1405{
1406 unsigned char lastblock[16];
1407 int x;
1408 memcpy(lastblock, iv, sizeof(lastblock));
1409 while(len > 0) {
1410 ast_aes_decrypt(src, dst, dcx);
1411 for (x=0;x<16;x++)
1412 dst[x] ^= lastblock[x];
1413 memcpy(lastblock, src, sizeof(lastblock));
1414 dst += 16;
1415 src += 16;
1416 len -= 16;
1417 }
1418 return 0;
1419}
1420
1421static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
1422{
1423 int space = *dstlen;
1424 unsigned long bytes;
1425 struct dundi_hdr *h;
1426 unsigned char *decrypt_space;
1427 decrypt_space = ast_alloca(srclen);
1428 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1429 /* Setup header */
1430 h = (struct dundi_hdr *)dst;
1431 *h = *ohdr;
1432 bytes = space - 6;
1433 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1434 ast_debug(1, "Ouch, uncompress failed :(\n");
1435 return NULL;
1436 }
1437 /* Update length */
1438 *dstlen = bytes + 6;
1439 /* Return new header */
1440 return h;
1441}
1442
1443static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1444{
1445 unsigned char *compress_space;
1446 int len;
1447 int res;
1448 unsigned long bytes;
1449 struct dundi_ie_data ied;
1450 struct dundi_peer *peer;
1451 unsigned char iv[16];
1452 len = pack->datalen + pack->datalen / 100 + 42;
1453 compress_space = ast_alloca(len);
1454 memset(compress_space, 0, len);
1455 /* We care about everthing save the first 6 bytes of header */
1456 bytes = len;
1457 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1458 if (res != Z_OK) {
1459 ast_debug(1, "Ouch, compression failed!\n");
1460 return -1;
1461 }
1462 memset(&ied, 0, sizeof(ied));
1463 /* Say who we are */
1464 if (!pack->h->iseqno && !pack->h->oseqno) {
1465 /* Need the key in the first copy */
1466 if (!(peer = find_peer(&trans->them_eid)))
1467 return -1;
1468 if (update_key(peer))
1469 return -1;
1470 if (!peer->sentfullkey)
1472 /* Append key data */
1473 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1474 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1476 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1477 } else {
1479 }
1480 /* Setup contexts */
1481 trans->ecx = peer->us_ecx;
1482 trans->dcx = peer->us_dcx;
1483
1484 /* We've sent the full key */
1485 peer->sentfullkey = 1;
1486 }
1487 /* Build initialization vector */
1488 build_iv(iv);
1489 /* Add the field, rounded up to 16 bytes */
1490 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1491 /* Copy the data */
1492 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1493 ast_log(LOG_NOTICE, "Final packet too large!\n");
1494 return -1;
1495 }
1496 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1497 ied.pos += ((bytes + 15) / 16) * 16;
1498 /* Reconstruct header */
1499 pack->datalen = sizeof(struct dundi_hdr);
1501 pack->h->cmdflags = 0;
1502 memcpy(pack->h->ies, ied.buf, ied.pos);
1503 pack->datalen += ied.pos;
1504 return 0;
1505}
1506
1507static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
1508{
1509 unsigned char dst[128];
1510 int res;
1511 struct ast_key *key, *skey;
1512 char eid_str[20];
1513 ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
1514 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1515 /* A match */
1516 return 1;
1517 } else if (!newkey || !newsig)
1518 return 0;
1519 if (!memcmp(peer->rxenckey, newkey, 128) &&
1520 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1521 /* By definition, a match */
1522 return 1;
1523 }
1524 /* Decrypt key */
1525 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1526 if (!key) {
1527 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1528 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1529 return -1;
1530 }
1531
1532 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1533 if (!skey) {
1534 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1535 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1536 return -1;
1537 }
1538
1539 /* First check signature */
1540 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1541 if (res)
1542 return 0;
1543
1544 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1545 if (res != 16) {
1546 if (res >= 0)
1547 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1548 return 0;
1549 }
1550 /* Decrypted, passes signature */
1551 ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
1552 memcpy(peer->rxenckey, newkey, 128);
1553 memcpy(peer->rxenckey + 128, newsig, 128);
1554 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1555 ast_aes_set_decrypt_key(dst, &peer->them_dcx);
1556 ast_aes_set_encrypt_key(dst, &peer->them_ecx);
1557 return 1;
1558}
1559
1560static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1561{
1562 struct permission *cur, *perm;
1563
1564 *peer_dst = *peer_src;
1565 AST_LIST_NEXT(peer_dst, list) = NULL;
1566
1567 /* Scheduled items cannot go with the copy */
1568 peer_dst->registerid = -1;
1569 peer_dst->qualifyid = -1;
1570 peer_dst->registerexpire = -1;
1571
1572 /* Transactions and lookup history cannot go with the copy either */
1573 peer_dst->regtrans = NULL;
1574 peer_dst->qualtrans = NULL;
1575 memset(&peer_dst->lookups, 0, sizeof(peer_dst->lookups));
1576
1577 memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1578 memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1579
1580 AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1581 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1582 continue;
1583
1584 perm->allow = cur->allow;
1585 strcpy(perm->name, cur->name);
1586
1587 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1588 }
1589
1590 AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1591 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1592 continue;
1593
1594 perm->allow = cur->allow;
1595 strcpy(perm->name, cur->name);
1596
1597 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1598 }
1599}
1600
1601static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1602{
1603 /* Handle canonical command / response */
1604 int final = hdr->cmdresp & 0x80;
1605 int cmd = hdr->cmdresp & 0x7f;
1606 int x,y,z;
1607 int resp;
1608 int res;
1609 int authpass=0;
1610 unsigned char *bufcpy;
1611#ifdef LOW_MEMORY
1612 struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
1613#else
1614 struct dundi_ie_data _ied = {
1615 .pos = 0,
1616 };
1617 struct dundi_ie_data *ied = &_ied;
1618#endif
1619 struct dundi_ies ies = {
1620 .eidcount = 0,
1621 };
1622 struct dundi_peer *peer = NULL;
1623 char eid_str[20];
1624 char eid_str2[20];
1625 int retval = -1;
1626
1627 if (!ied) {
1628 return -1;
1629 }
1630
1631 if (datalen) {
1632 bufcpy = ast_alloca(datalen);
1633 /* Make a copy for parsing */
1634 memcpy(bufcpy, hdr->ies, datalen);
1635 ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1636 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1637 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1638 goto return_cleanup;
1639 }
1640 }
1641 switch(cmd) {
1645 if (cmd == DUNDI_COMMAND_EIDQUERY)
1647 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1649 else
1651 /* A dialplan or entity discover -- qualify by highest level entity */
1652 peer = find_peer(ies.eids[0]);
1653 if (!peer) {
1655 dundi_send(trans, resp, 0, 1, ied);
1656 } else {
1657 int hasauth = 0;
1658 trans->us_eid = peer->us_eid;
1659 if (strlen(peer->inkey)) {
1660 hasauth = encrypted;
1661 } else
1662 hasauth = 1;
1663 if (hasauth) {
1664 /* Okay we're authentiated and all, now we check if they're authorized */
1665 if (!ies.called_context)
1666 ies.called_context = "e164";
1667 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1668 res = dundi_answer_entity(trans, &ies, ies.called_context);
1669 } else {
1670 if (ast_strlen_zero(ies.called_number)) {
1671 /* They're not permitted to access that context */
1672 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1673 dundi_send(trans, resp, 0, 1, ied);
1674 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1675 (peer->model & DUNDI_MODEL_INBOUND) &&
1676 has_permission(&peer->permit, ies.called_context)) {
1677 res = dundi_answer_query(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 if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1684 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1685 has_permission(&peer->include, ies.called_context)) {
1686 res = dundi_prop_precache(trans, &ies, ies.called_context);
1687 if (res < 0) {
1688 /* There is no such dundi context */
1689 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1690 dundi_send(trans, resp, 0, 1, ied);
1691 }
1692 } else {
1693 /* They're not permitted to access that context */
1694 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1695 dundi_send(trans, resp, 0, 1, ied);
1696 }
1697 }
1698 } else {
1699 /* They're not permitted to access that context */
1700 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1701 dundi_send(trans, resp, 0, 1, ied);
1702 }
1703 }
1704 break;
1706 /* A register request -- should only have one entity */
1707 peer = find_peer(ies.eids[0]);
1708
1709 /* if the peer is not found and we have a valid 'any_peer' setting */
1710 if (any_peer && peer == any_peer) {
1711 /* copy any_peer into a new peer object */
1712 peer = ast_calloc(1, sizeof(*peer));
1713 if (peer) {
1714 deep_copy_peer(peer, any_peer);
1715
1716 /* set EID to remote EID */
1717 peer->eid = *ies.eids[0];
1718
1722 }
1723 }
1724
1725 if (!peer || !peer->dynamic) {
1727 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1728 } else {
1729 int hasauth = 0;
1730 trans->us_eid = peer->us_eid;
1731 if (!ast_strlen_zero(peer->inkey)) {
1732 hasauth = encrypted;
1733 } else
1734 hasauth = 1;
1735 if (hasauth) {
1736 int expire = default_expiration;
1737 char data[256];
1738 int needqual = 0;
1740 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1741 snprintf(data, sizeof(data), "%s:%d", ast_sockaddr_stringify(&trans->addr), expire);
1742 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1743 if (ast_sockaddr_cmp(&peer->addr, &trans->addr)) {
1744 ast_verb(3, "Registered DUNDi peer '%s' at '%s'\n",
1745 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1746 ast_sockaddr_stringify(&trans->addr));
1747 needqual = 1;
1748 }
1749
1750 ast_sockaddr_copy(&peer->addr, &trans->addr);
1752 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1753 if (needqual)
1754 qualify_peer(peer, 1);
1755 }
1756 }
1757 break;
1759 /* A dialplan response, lets see what we got... */
1760 if (ies.cause < 1) {
1761 /* Success of some sort */
1762 ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1763 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1764 authpass = encrypted;
1765 } else
1766 authpass = 1;
1767 if (authpass) {
1768 /* Pass back up answers */
1769 if (trans->parent && trans->parent->dr) {
1770 y = trans->parent->respcount;
1771 for (x=0;x<ies.anscount;x++) {
1772 if (trans->parent->respcount < trans->parent->maxcount) {
1773 /* Make sure it's not already there */
1774 for (z=0;z<trans->parent->respcount;z++) {
1775 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1776 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1777 break;
1778 }
1779 if (z == trans->parent->respcount) {
1780 /* Copy into parent responses */
1781 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1782 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1783 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1784 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1785 if (ies.expiration > 0)
1786 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1787 else
1790 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1791 &ies.answers[x]->eid);
1792 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1793 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1795 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1796 trans->parent->respcount++;
1798 } else if (trans->parent->dr[z].weight > ntohs(ies.answers[x]->weight)) {
1799 /* Update weight if appropriate */
1800 trans->parent->dr[z].weight = ntohs(ies.answers[x]->weight);
1801 }
1802 } else
1803 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1804 trans->parent->number, trans->parent->dcontext);
1805 }
1806 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1807 the cache know if this request was unaffected by our entity list. */
1808 cache_save(&trans->them_eid, trans->parent, y,
1809 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1810 if (ies.hint) {
1811 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1815 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1816 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1817 sizeof(trans->parent->hmd->exten));
1818 }
1819 } else {
1821 }
1822 }
1823 if (ies.expiration > 0) {
1824 if (trans->parent->expiration > ies.expiration) {
1825 trans->parent->expiration = ies.expiration;
1826 }
1827 }
1828 }
1829 /* Close connection if not final */
1830 if (!final)
1831 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1832 }
1833
1834 } else {
1835 /* Auth failure, check for data */
1836 if (!final) {
1837 /* Cancel if they didn't already */
1838 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1839 }
1840 }
1841 break;
1843 /* A dialplan response, lets see what we got... */
1844 if (ies.cause < 1) {
1845 /* Success of some sort */
1846 ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
1847 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1848 authpass = encrypted;
1849 } else
1850 authpass = 1;
1851 if (authpass) {
1852 /* Pass back up answers */
1853 if (trans->parent && trans->parent->dei && ies.q_org) {
1854 if (!trans->parent->respcount) {
1855 trans->parent->respcount++;
1856 if (ies.q_dept)
1857 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1858 if (ies.q_org)
1859 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1860 if (ies.q_locality)
1861 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1862 if (ies.q_stateprov)
1863 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1864 if (ies.q_country)
1865 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1866 if (ies.q_email)
1867 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1868 if (ies.q_phone)
1869 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1870 if (ies.q_ipaddr)
1871 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1872 if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1873 /* If it's them, update our address */
1874 ast_copy_string(trans->parent->dei->ipaddr, ast_sockaddr_stringify_addr(&trans->addr), sizeof(trans->parent->dei->ipaddr));
1875 }
1876 }
1877 if (ies.hint) {
1880 }
1881 }
1882 /* Close connection if not final */
1883 if (!final)
1884 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1885 }
1886
1887 } else {
1888 /* Auth failure, check for data */
1889 if (!final) {
1890 /* Cancel if they didn't already */
1891 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1892 }
1893 }
1894 break;
1896 /* A dialplan response, lets see what we got... */
1897 if (ies.cause < 1) {
1898 int hasauth;
1899 /* Success of some sort */
1900 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1901 hasauth = encrypted;
1902 } else
1903 hasauth = 1;
1904
1905 if (!hasauth) {
1906 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1907 if (!final) {
1908 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1909 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
1910 }
1911 } else {
1912 ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
1913 ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1914 /* Close connection if not final */
1915 if (!final)
1916 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1917 }
1918 } else {
1919 /* Auth failure, cancel if they didn't for some reason */
1920 if (!final) {
1921 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1922 }
1923 }
1924 break;
1926 case DUNDI_COMMAND_NULL:
1928 /* Do nothing special */
1929 if (!final)
1930 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1931 break;
1933 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1934 /* No really, it's over at this point */
1935 if (!final)
1936 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1937 } else {
1938 /* Send with full key */
1940 if (final) {
1941 /* Ooops, we got a final message, start by sending ACK... */
1942 dundi_ack(trans, hdr->cmdresp & 0x80);
1943 trans->aseqno = trans->iseqno;
1944 /* Now, we gotta create a new transaction */
1945 if (!reset_transaction(trans)) {
1946 /* Make sure handle_frame doesn't destroy us */
1947 hdr->cmdresp &= 0x7f;
1948 /* Parse the message we transmitted */
1949 memset(&ies, 0, sizeof(ies));
1950 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1951 /* Reconstruct outgoing encrypted packet */
1952 memset(ied, 0, sizeof(*ied));
1955 dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1956 if (ies.encblock)
1958 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
1959 peer->sentfullkey = 1;
1960 }
1961 }
1962 }
1963 break;
1965 if (!encrypted) {
1966 /* No nested encryption! */
1967 if ((trans->iseqno == 1) && !trans->oseqno) {
1968 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1969 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1970 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1971 if (!final) {
1972 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1973 }
1974 break;
1975 }
1976 apply_peer(trans, peer);
1977 /* Key passed, use new contexts for this session */
1978 trans->ecx = peer->them_ecx;
1979 trans->dcx = peer->them_dcx;
1980 }
1981 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1982 struct dundi_hdr *dhdr;
1983 unsigned char decoded[MAX_PACKET_SIZE];
1984 int ddatalen;
1985 ddatalen = sizeof(decoded);
1986 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1987 if (dhdr) {
1988 /* Handle decrypted response */
1989 if (dundidebug)
1990 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1991 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1992 /* Carry back final flag */
1993 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1994 break;
1995 } else {
1996 ast_debug(1, "Ouch, decrypt failed :(\n");
1997 }
1998 }
1999 }
2000 if (!final) {
2001 /* Turn off encryption */
2003 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
2004 }
2005 break;
2006 default:
2007 /* Send unknown command if we don't know it, with final flag IFF it's the
2008 first command in the dialog and only if we haven't received final notification */
2009 if (!final) {
2011 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
2012 }
2013 }
2014
2015 retval = 0;
2016
2017return_cleanup:
2018#ifdef LOW_MEMORY
2019 ast_free(ied);
2020#endif
2021 return retval;
2022}
2023
2024static void destroy_packet(struct dundi_packet *pack, int needfree);
2025static void destroy_packets(struct packetlist *p)
2026{
2027 struct dundi_packet *pack;
2028
2029 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
2031 ast_free(pack);
2032 }
2033}
2034
2035
2036static int ack_trans(struct dundi_transaction *trans, int iseqno)
2037{
2038 struct dundi_packet *pack;
2039
2040 /* Ack transmitted packet corresponding to iseqno */
2041 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
2042 if ((pack->h->oseqno + 1) % 255 == iseqno) {
2043 destroy_packet(pack, 0);
2044 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
2045 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
2046 destroy_packets(&trans->lasttrans);
2047 }
2048 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
2050 return 1;
2051 }
2052 }
2053
2054 return 0;
2055}
2056
2057static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datalen)
2058{
2059 struct dundi_transaction *trans;
2060 trans = find_transaction(h, sin);
2061 if (!trans) {
2062 dundi_reject(h, sin);
2063 return 0;
2064 }
2065 /* Got a transaction, see where this header fits in */
2066 if (h->oseqno == trans->iseqno) {
2067 /* Just what we were looking for... Anything but ack increments iseqno */
2068 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2069 /* If final, we're done */
2070 destroy_trans(trans, 0);
2071 return 0;
2072 }
2073 if (h->cmdresp != DUNDI_COMMAND_ACK) {
2074 trans->oiseqno = trans->iseqno;
2075 trans->iseqno++;
2076 handle_command_response(trans, h, datalen, 0);
2077 }
2078 if (trans->aseqno != trans->iseqno) {
2079 dundi_ack(trans, h->cmdresp & 0x80);
2080 trans->aseqno = trans->iseqno;
2081 }
2082 /* Delete any saved last transmissions */
2083 destroy_packets(&trans->lasttrans);
2084 if (h->cmdresp & 0x80) {
2085 /* Final -- destroy now */
2086 destroy_trans(trans, 0);
2087 }
2088 } else if (h->oseqno == trans->oiseqno) {
2089 /* Last incoming sequence number -- send ACK without processing */
2090 dundi_ack(trans, 0);
2091 } else {
2092 /* Out of window -- simply drop */
2093 ast_debug(1, "Dropping packet out of window!\n");
2094 }
2095 return 0;
2096}
2097
2098static int socket_read(int *id, int fd, short events, void *sock)
2099{
2100 struct ast_sockaddr sin;
2101 int res;
2102 struct dundi_hdr *h;
2103 char buf[MAX_PACKET_SIZE];
2104
2105 res = ast_recvfrom(*((int *)sock), buf, sizeof(buf), 0, &sin);
2106 if (res < 0) {
2107 if (errno != ECONNREFUSED)
2108 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2109 return 1;
2110 }
2111 if (res < sizeof(struct dundi_hdr)) {
2112 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2113 return 1;
2114 }
2115 buf[res] = '\0';
2116 h = (struct dundi_hdr *) buf;
2117 if (dundidebug)
2118 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2120 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2122 return 1;
2123}
2124
2125static void build_secret(char *secret, int seclen)
2126{
2127 unsigned char tmp[16];
2128 char *s;
2129 build_iv(tmp);
2130 secret[0] = '\0';
2131 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2132 /* Eliminate potential bad characters */
2133 while((s = strchr(secret, ';'))) *s = '+';
2134 while((s = strchr(secret, '/'))) *s = '+';
2135 while((s = strchr(secret, ':'))) *s = '+';
2136 while((s = strchr(secret, '@'))) *s = '+';
2137}
2138
2139
2140static void save_secret(const char *newkey, const char *oldkey)
2141{
2142 char tmp[350];
2143 if (oldkey)
2144 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2145 else
2146 snprintf(tmp, sizeof(tmp), "%s", newkey);
2148 ast_db_put(secretpath, "secret", tmp);
2149 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2150 ast_db_put(secretpath, "secretexpiry", tmp);
2151}
2152
2153static void load_password(void)
2154{
2155 char *current=NULL;
2156 char *last=NULL;
2157 char tmp[256];
2158 time_t expired;
2159
2160 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2161 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2162 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2163 current = strchr(tmp, ';');
2164 if (!current)
2165 current = tmp;
2166 else {
2167 *current = '\0';
2168 current++;
2169 };
2170 if ((time(NULL) - expired) < 0) {
2171 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2172 expired = time(NULL) + DUNDI_SECRET_TIME;
2173 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2174 last = current;
2175 current = NULL;
2176 } else {
2177 last = NULL;
2178 current = NULL;
2179 }
2180 }
2181 if (current) {
2182 /* Current key is still valid, just setup rotation properly */
2184 rotatetime = expired;
2185 } else {
2186 /* Current key is out of date, rotate or eliminate all together */
2189 }
2190}
2191
2192static void check_password(void)
2193{
2194 char oldsecret[80];
2195 time_t now;
2196
2197 time(&now);
2198#if 0
2199 printf("%ld/%ld\n", now, rotatetime);
2200#endif
2201 if ((now - rotatetime) >= 0) {
2202 /* Time to rotate keys */
2203 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2205 save_secret(cursecret, oldsecret);
2206 }
2207}
2208
2209static void *network_thread(void *ignore)
2210{
2211 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2212 from the network, and queue them for delivery to the channels */
2213 int res;
2214 /* Establish I/O callback for socket read */
2215 int *socket_read_id = ast_io_add(io, netsocket, socket_read, AST_IO_IN, &netsocket);
2216 int *socket_read_id2 = NULL;
2217 if (netsocket2 >= 0) {
2218 socket_read_id2 = ast_io_add(io, netsocket2, socket_read, AST_IO_IN, &netsocket2);
2219 }
2220
2221 while (!dundi_shutdown) {
2222 res = ast_sched_wait(sched);
2223 if ((res > 1000) || (res < 0))
2224 res = 1000;
2225 res = ast_io_wait(io, res);
2226 if (res >= 0) {
2230 }
2232 }
2233
2234 ast_io_remove(io, socket_read_id);
2235
2236 if (socket_read_id2) {
2237 ast_io_remove(io, socket_read_id2);
2238 }
2239
2240 return NULL;
2241}
2242
2243static void *process_clearcache(void *ignore)
2244{
2245 struct ast_db_entry *db_entry, *db_tree;
2246 int striplen = sizeof("/dundi/cache");
2247 time_t now;
2248
2249 while (!dundi_shutdown) {
2250 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2251
2252 time(&now);
2253
2254 db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
2255 for (; db_entry; db_entry = db_entry->next) {
2256 time_t expiry;
2257
2258 if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
2259 if (expiry < now) {
2260 ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
2261 ast_db_del("dundi/cache", db_entry->key + striplen);
2262 }
2263 }
2264 }
2265 ast_db_freetree(db_tree);
2266
2267 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2268 pthread_testcancel();
2269 sleep(60);
2270 pthread_testcancel();
2271 }
2272
2273 return NULL;
2274}
2275
2276static void *process_precache(void *ign)
2277{
2278 struct dundi_precache_queue *qe;
2279 time_t now;
2280 char context[256];
2281 char number[256];
2282 int run;
2283
2284 while (!dundi_shutdown) {
2285 time(&now);
2286 run = 0;
2288 if ((qe = AST_LIST_FIRST(&pcq))) {
2289 if (!qe->expiration) {
2290 /* Gone... Remove... */
2292 ast_free(qe);
2293 } else if (qe->expiration < now) {
2294 /* Process this entry */
2295 qe->expiration = 0;
2296 ast_copy_string(context, qe->context, sizeof(context));
2297 ast_copy_string(number, qe->number, sizeof(number));
2298 run = 1;
2299 }
2300 }
2302 if (run) {
2304 } else
2305 sleep(1);
2306 }
2307
2308 return NULL;
2309}
2310
2311static int start_network_thread(void)
2312{
2316 return 0;
2317}
2318
2319static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2320{
2321 switch (cmd) {
2322 case CLI_INIT:
2323 e->command = "dundi set debug {on|off}";
2324 e->usage =
2325 "Usage: dundi set debug {on|off}\n"
2326 " Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2327 return NULL;
2328 case CLI_GENERATE:
2329 return NULL;
2330 }
2331
2332 if (a->argc != e->args) {
2333 return CLI_SHOWUSAGE;
2334 }
2335 if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2336 dundidebug = 1;
2337 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2338 } else {
2339 dundidebug = 0;
2340 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2341 }
2342 return CLI_SUCCESS;
2343}
2344
2345static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2346{
2347 switch (cmd) {
2348 case CLI_INIT:
2349 e->command = "dundi store history {on|off}";
2350 e->usage =
2351 "Usage: dundi store history {on|off}\n"
2352 " Enables/Disables storing of DUNDi requests and times for debugging\n"
2353 "purposes\n";
2354 return NULL;
2355 case CLI_GENERATE:
2356 return NULL;
2357 }
2358
2359 if (a->argc != e->args) {
2360 return CLI_SHOWUSAGE;
2361 }
2362 if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2364 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2365 } else {
2367 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2368 }
2369 return CLI_SUCCESS;
2370}
2371
2372static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2373{
2374 int stats = 0;
2375 switch (cmd) {
2376 case CLI_INIT:
2377 e->command = "dundi flush [stats]";
2378 e->usage =
2379 "Usage: dundi flush [stats]\n"
2380 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2381 "'stats' is present, clears timer statistics instead of normal\n"
2382 "operation.\n";
2383 return NULL;
2384 case CLI_GENERATE:
2385 return NULL;
2386 }
2387 if ((a->argc < 2) || (a->argc > 3)) {
2388 return CLI_SHOWUSAGE;
2389 }
2390 if (a->argc > 2) {
2391 if (!strcasecmp(a->argv[2], "stats")) {
2392 stats = 1;
2393 } else {
2394 return CLI_SHOWUSAGE;
2395 }
2396 }
2397 if (stats) {
2398 /* Flush statistics */
2399 struct dundi_peer *p;
2400 int x;
2403 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2404 ast_free(p->lookups[x]);
2405 p->lookups[x] = NULL;
2406 p->lookuptimes[x] = 0;
2407 }
2408 p->avgms = 0;
2409 }
2411 } else {
2412 ast_db_deltree("dundi/cache", NULL);
2413 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2414 }
2415 return CLI_SUCCESS;
2416}
2417
2418static char *model2str(int model)
2419{
2420 switch(model) {
2422 return "Inbound";
2424 return "Outbound";
2426 return "Symmetric";
2427 default:
2428 return "Unknown";
2429 }
2430}
2431
2432static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2433{
2434 int which=0, len;
2435 char *ret = NULL;
2436 struct dundi_peer *p;
2437 char eid_str[20];
2438
2439 if (pos != rpos)
2440 return NULL;
2442 len = strlen(word);
2444 const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2445 if (!strncasecmp(word, s, len) && ++which > state) {
2446 ret = ast_strdup(s);
2447 break;
2448 }
2449 }
2451 return ret;
2452}
2453
2454static int rescomp(const void *a, const void *b)
2455{
2456 const struct dundi_result *resa, *resb;
2457 resa = a;
2458 resb = b;
2459 if (resa->weight < resb->weight)
2460 return -1;
2461 if (resa->weight > resb->weight)
2462 return 1;
2463 return 0;
2464}
2465
2466static void sort_results(struct dundi_result *results, int count)
2467{
2468 qsort(results, count, sizeof(results[0]), rescomp);
2469}
2470
2471static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2472{
2473 int res;
2474 char tmp[256];
2475 char fs[80] = "";
2476 char *context;
2477 int x;
2478 int bypass = 0;
2479 struct dundi_result dr[MAX_RESULTS];
2480 struct timeval start;
2481 switch (cmd) {
2482 case CLI_INIT:
2483 e->command = "dundi lookup";
2484 e->usage =
2485 "Usage: dundi lookup <number>[@context] [bypass]\n"
2486 " Lookup the given number within the given DUNDi context\n"
2487 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2488 "keyword is specified.\n";
2489 return NULL;
2490 case CLI_GENERATE:
2491 return NULL;
2492 }
2493
2494 if ((a->argc < 3) || (a->argc > 4)) {
2495 return CLI_SHOWUSAGE;
2496 }
2497 if (a->argc > 3) {
2498 if (!strcasecmp(a->argv[3], "bypass")) {
2499 bypass=1;
2500 } else {
2501 return CLI_SHOWUSAGE;
2502 }
2503 }
2504 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2505 context = strchr(tmp, '@');
2506 if (context) {
2507 *context = '\0';
2508 context++;
2509 }
2510 start = ast_tvnow();
2511 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2512
2513 if (res < 0)
2514 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2515 else if (!res)
2516 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2517 else
2518 sort_results(dr, res);
2519 for (x=0;x<res;x++) {
2520 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));
2521 ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2522 }
2523 ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2524 return CLI_SUCCESS;
2525}
2526
2527static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2528{
2529 int res;
2530 char tmp[256];
2531 char *context;
2532 struct timeval start;
2533 switch (cmd) {
2534 case CLI_INIT:
2535 e->command = "dundi precache";
2536 e->usage =
2537 "Usage: dundi precache <number>[@context]\n"
2538 " Lookup the given number within the given DUNDi context\n"
2539 "(or e164 if none is specified) and precaches the results to any\n"
2540 "upstream DUNDi push servers.\n";
2541 return NULL;
2542 case CLI_GENERATE:
2543 return NULL;
2544 }
2545 if ((a->argc < 3) || (a->argc > 3)) {
2546 return CLI_SHOWUSAGE;
2547 }
2548 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2549 context = strchr(tmp, '@');
2550 if (context) {
2551 *context = '\0';
2552 context++;
2553 }
2554 start = ast_tvnow();
2555 res = dundi_precache(context, tmp);
2556
2557 if (res < 0)
2558 ast_cli(a->fd, "DUNDi precache returned error.\n");
2559 else if (!res)
2560 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2561 ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2562 return CLI_SUCCESS;
2563}
2564
2565static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2566{
2567 int res;
2568 char tmp[256];
2569 char *context;
2570 dundi_eid eid;
2571 struct dundi_entity_info dei;
2572 switch (cmd) {
2573 case CLI_INIT:
2574 e->command = "dundi query";
2575 e->usage =
2576 "Usage: dundi query <entity>[@context]\n"
2577 " Attempts to retrieve contact information for a specific\n"
2578 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2579 "e164 if none is specified).\n";
2580 return NULL;
2581 case CLI_GENERATE:
2582 return NULL;
2583 }
2584 if ((a->argc < 3) || (a->argc > 3)) {
2585 return CLI_SHOWUSAGE;
2586 }
2587 if (ast_str_to_eid(&eid, a->argv[2])) {
2588 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2589 return CLI_SHOWUSAGE;
2590 }
2591 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2592 context = strchr(tmp, '@');
2593 if (context) {
2594 *context = '\0';
2595 context++;
2596 }
2597 res = dundi_query_eid(&dei, context, eid);
2598 if (res < 0)
2599 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2600 else if (!res)
2601 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2602 else {
2603 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2604 ast_cli(a->fd, "Department: %s\n", dei.orgunit);
2605 ast_cli(a->fd, "Organization: %s\n", dei.org);
2606 ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
2607 ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
2608 ast_cli(a->fd, "Country: %s\n", dei.country);
2609 ast_cli(a->fd, "E-mail: %s\n", dei.email);
2610 ast_cli(a->fd, "Phone: %s\n", dei.phone);
2611 ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
2612 }
2613 return CLI_SUCCESS;
2614}
2615
2616static char *dundi_sockaddr_stringify_host(const struct ast_sockaddr *addr)
2617{
2618 if (ast_sockaddr_isnull(addr)) {
2619 return "(Unspecified)";
2620 }
2621 return ast_sockaddr_stringify_host(addr);
2622}
2623
2624static uint16_t dundi_sockaddr_port(const struct ast_sockaddr *addr)
2625{
2626 /*
2627 * Test to avoid a debug message complaining about addr
2628 * not being an IPv4 or IPv6 address.
2629 */
2630 if (ast_sockaddr_isnull(addr)) {
2631 return 0;
2632 }
2633 return ast_sockaddr_port(addr);
2634}
2635
2636static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2637{
2638 struct dundi_peer *peer;
2639 struct permission *p;
2640 char *order;
2641 char eid_str[20];
2642 int x, cnt;
2643 switch (cmd) {
2644 case CLI_INIT:
2645 e->command = "dundi show peer";
2646 e->usage =
2647 "Usage: dundi show peer [peer]\n"
2648 " Provide a detailed description of a specifid DUNDi peer.\n";
2649 return NULL;
2650 case CLI_GENERATE:
2651 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2652 }
2653 if (a->argc != 4) {
2654 return CLI_SHOWUSAGE;
2655 }
2657 AST_LIST_TRAVERSE(&peers, peer, list) {
2658 if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2659 break;
2660 }
2661 if (peer) {
2662 switch(peer->order) {
2663 case 0:
2664 order = "Primary";
2665 break;
2666 case 1:
2667 order = "Secondary";
2668 break;
2669 case 2:
2670 order = "Tertiary";
2671 break;
2672 case 3:
2673 order = "Quartiary";
2674 break;
2675 default:
2676 order = "Unknown";
2677 }
2678 ast_cli(a->fd, "Peer: %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2679 ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
2680 ast_cli(a->fd, "Order: %s\n", order);
2681 ast_cli(a->fd, "Host: %s\n", ast_sockaddr_isnull(&peer->addr) ? "<Unspecified>" : ast_sockaddr_stringify_host(&peer->addr));
2682 ast_cli(a->fd, "Port: %d\n", dundi_sockaddr_port(&peer->addr));
2683 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2684 ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2685 ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2686 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2687 if (!AST_LIST_EMPTY(&peer->include))
2688 ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2689 AST_LIST_TRAVERSE(&peer->include, p, list)
2690 ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2691 if (!AST_LIST_EMPTY(&peer->permit))
2692 ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2693 AST_LIST_TRAVERSE(&peer->permit, p, list)
2694 ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2695 cnt = 0;
2696 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2697 if (peer->lookups[x]) {
2698 if (!cnt)
2699 ast_cli(a->fd, "Last few query times:\n");
2700 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2701 cnt++;
2702 }
2703 }
2704 if (cnt)
2705 ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2706 } else
2707 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2709 return CLI_SUCCESS;
2710}
2711
2712static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2713{
2714#define FORMAT2 "%-20.20s %-41s %-6.6s %-10.10s %-8.8s %-15.15s\n"
2715#define FORMAT "%-20.20s %-41s %s %-6d %-10.10s %-8.8s %-15.15s\n"
2716 struct dundi_peer *peer;
2717 int registeredonly=0;
2718 char avgms[20];
2719 char eid_str[20];
2720 int online_peers = 0;
2721 int offline_peers = 0;
2722 int unmonitored_peers = 0;
2723 int total_peers = 0;
2724 switch (cmd) {
2725 case CLI_INIT:
2726 e->command = "dundi show peers [registered|include|exclude|begin]";
2727 e->usage =
2728 "Usage: dundi show peers [registered|include|exclude|begin]\n"
2729 " Lists all known DUNDi peers.\n"
2730 " If 'registered' is present, only registered peers are shown.\n";
2731 return NULL;
2732 case CLI_GENERATE:
2733 return NULL;
2734 }
2735
2736 if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
2737 return CLI_SHOWUSAGE;
2738 }
2739 if ((a->argc == 4)) {
2740 if (!strcasecmp(a->argv[3], "registered")) {
2741 registeredonly = 1;
2742 } else {
2743 return CLI_SHOWUSAGE;
2744 }
2745 }
2747 ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
2748 AST_LIST_TRAVERSE(&peers, peer, list) {
2749 char status[64];
2750 int print_line = -1;
2751 char srch[2000];
2752
2753 total_peers++;
2754 if (registeredonly && ast_sockaddr_isnull(&peer->addr)) {
2755 continue;
2756 }
2757 if (peer->maxms) {
2758 if (peer->lastms < 0) {
2759 strcpy(status, "UNREACHABLE");
2760 offline_peers++;
2761 }
2762 else if (peer->lastms > peer->maxms) {
2763 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2764 offline_peers++;
2765 }
2766 else if (peer->lastms) {
2767 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2768 online_peers++;
2769 }
2770 else {
2771 strcpy(status, "UNKNOWN");
2772 offline_peers++;
2773 }
2774 } else {
2775 strcpy(status, "Unmonitored");
2776 unmonitored_peers++;
2777 }
2778 if (peer->avgms)
2779 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2780 else
2781 strcpy(avgms, "Unavail");
2782 snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str),
2783 &peer->eid), dundi_sockaddr_stringify_host(&peer->addr),
2784 peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2785
2786 if (a->argc == 5) {
2787 if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2788 print_line = -1;
2789 } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2790 print_line = 1;
2791 } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2792 print_line = -1;
2793 } else {
2794 print_line = 0;
2795 }
2796 }
2797
2798 if (print_line) {
2799 ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2801 peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2802 }
2803 }
2804 ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2806 return CLI_SUCCESS;
2807#undef FORMAT
2808#undef FORMAT2
2809}
2810
2811static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2812{
2813#define FORMAT2 "%-47s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2814#define FORMAT "%-41s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2815 struct dundi_transaction *trans;
2816 switch (cmd) {
2817 case CLI_INIT:
2818 e->command = "dundi show trans";
2819 e->usage =
2820 "Usage: dundi show trans\n"
2821 " Lists all known DUNDi transactions.\n";
2822 return NULL;
2823 case CLI_GENERATE:
2824 return NULL;
2825 }
2826 if (a->argc != 3) {
2827 return CLI_SHOWUSAGE;
2828 }
2830 ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2831 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2833 ast_sockaddr_port(&trans->addr), trans->strans, trans->dtrans,
2834 trans->oseqno, trans->iseqno, trans->aseqno);
2835 }
2837 return CLI_SUCCESS;
2838#undef FORMAT
2839#undef FORMAT2
2840}
2841
2842static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2843{
2844 char eid_str[20];
2845 switch (cmd) {
2846 case CLI_INIT:
2847 e->command = "dundi show entityid";
2848 e->usage =
2849 "Usage: dundi show entityid\n"
2850 " Displays the global entityid for this host.\n";
2851 return NULL;
2852 case CLI_GENERATE:
2853 return NULL;
2854 }
2855 if (a->argc != 3) {
2856 return CLI_SHOWUSAGE;
2857 }
2859 ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2861 ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2862 return CLI_SUCCESS;
2863}
2864
2865static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2866{
2867#define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2868#define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2869 struct dundi_request *req;
2870 char eidstr[20];
2871 switch (cmd) {
2872 case CLI_INIT:
2873 e->command = "dundi show requests";
2874 e->usage =
2875 "Usage: dundi show requests\n"
2876 " Lists all known pending DUNDi requests.\n";
2877 return NULL;
2878 case CLI_GENERATE:
2879 return NULL;
2880 }
2881 if (a->argc != 3) {
2882 return CLI_SHOWUSAGE;
2883 }
2885 ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2887 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2888 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2889 }
2891 return CLI_SUCCESS;
2892#undef FORMAT
2893#undef FORMAT2
2894}
2895
2896/* Grok-a-dial DUNDi */
2897
2898static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2899{
2900#define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2901#define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2902 struct dundi_mapping *map;
2903 char fs[256];
2904 char weight[8];
2905 switch (cmd) {
2906 case CLI_INIT:
2907 e->command = "dundi show mappings";
2908 e->usage =
2909 "Usage: dundi show mappings\n"
2910 " Lists all known DUNDi mappings.\n";
2911 return NULL;
2912 case CLI_GENERATE:
2913 return NULL;
2914 }
2915 if (a->argc != 3) {
2916 return CLI_SHOWUSAGE;
2917 }
2919 ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2921 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map, NULL));
2922 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2923 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2924 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2925 }
2927 return CLI_SUCCESS;
2928#undef FORMAT
2929#undef FORMAT2
2930}
2931
2932static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2933{
2934#define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2935#define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2936 struct dundi_precache_queue *qe;
2937 int h,m,s;
2938 time_t now;
2939 switch (cmd) {
2940 case CLI_INIT:
2941 e->command = "dundi show precache";
2942 e->usage =
2943 "Usage: dundi show precache\n"
2944 " Lists all known DUNDi scheduled precache updates.\n";
2945 return NULL;
2946 case CLI_GENERATE:
2947 return NULL;
2948 }
2949 if (a->argc != 3) {
2950 return CLI_SHOWUSAGE;
2951 }
2952 time(&now);
2953 ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2955 AST_LIST_TRAVERSE(&pcq, qe, list) {
2956 s = qe->expiration - now;
2957 h = s / 3600;
2958 s = s % 3600;
2959 m = s / 60;
2960 s = s % 60;
2961 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2962 }
2964
2965 return CLI_SUCCESS;
2966#undef FORMAT
2967#undef FORMAT2
2968}
2969
2970static char *dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2971{
2972#define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s %-7s %s\n"
2973#define FORMAT "%-12.12s %-16.16s %6d sec %-18s %-7d %s/%s (%s)\n"
2974 struct ast_db_entry *db_tree, *db_entry;
2975 int cnt = 0;
2976 time_t ts, now;
2977 dundi_eid src_eid;
2978 char src_eid_str[20];
2979 int expiry, tech, weight;
2980 struct ast_flags flags;
2981 char fs[256];
2982 int length;
2983 char *ptr, *term, *src, *number, *context, *dst;
2984
2985 switch (cmd) {
2986 case CLI_INIT:
2987 e->command = "dundi show cache";
2988 e->usage =
2989 "Usage: dundi show cache\n"
2990 " Lists all DUNDi cache entries.\n";
2991 return NULL;
2992 case CLI_GENERATE:
2993 return NULL;
2994 }
2995
2996 if (a->argc != 3) {
2997 return CLI_SHOWUSAGE;
2998 }
2999
3000 time(&now);
3001 db_tree = ast_db_gettree("dundi/cache", NULL);
3002 ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration", "From", "Weight", "Destination (Flags)");
3003 for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
3004 char *rest;
3005
3006 if ((strncmp(db_entry->key, "/dundi/cache/hint/", 18) == 0) || ast_get_time_t(db_entry->data, &ts, 0, &length)) {
3007 continue;
3008 }
3009
3010 expiry = ts - now;
3011
3012 if (expiry <= 0) {
3013 continue;
3014 }
3015
3016 ptr = db_entry->key + sizeof("/dundi/cache");
3017 strtok_r(ptr, "/", &rest);
3018 number = strtok_r(NULL, "/", &rest);
3019 context = strtok_r(NULL, "/", &rest);
3020 ptr = strtok_r(NULL, "/", &rest);
3021
3022 if (*ptr != 'e') {
3023 continue;
3024 }
3025
3026 ptr = db_entry->data + length + 1;
3027
3028 if ((sscanf(ptr, "%30u/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) != 3)) {
3029 continue;
3030 }
3031
3032 ptr += length;
3033 dst = ptr;
3034 term = strchr(ptr, '|');
3035
3036 if (!term) {
3037 continue;
3038 }
3039
3040 /* Ok, at this point we know we aren't going to skp the entry, so we go ahead and increment the count. */
3041 cnt++;
3042
3043 *term = '\0';
3044 src = strrchr(ptr, '/');
3045 dundi_eid_zero(&src_eid);
3046
3047 if (src) {
3048 *src = '\0';
3049 src++;
3050 dundi_str_short_to_eid(&src_eid, src);
3051 ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3052 }
3053
3054 ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str, weight, tech2str(tech), dst, dundi_flags2str(fs, sizeof(fs), flags.flags));
3055 }
3056
3057 ast_cli(a->fd, "Number of entries: %d\n", cnt);
3058 ast_db_freetree(db_tree);
3059
3060 return CLI_SUCCESS;
3061#undef FORMAT
3062#undef FORMAT2
3063}
3064
3065static char *dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3066{
3067#define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s\n"
3068#define FORMAT "%-12.12s %-16.16s %6d sec %-18s\n"
3069 struct ast_db_entry *db_tree, *db_entry;
3070 int cnt = 0;
3071 time_t ts, now;
3072 dundi_eid src_eid;
3073 char src_eid_str[20];
3074 int expiry;
3075 int length;
3076 char *ptr, *src, *number, *context;
3077
3078 switch (cmd) {
3079 case CLI_INIT:
3080 e->command = "dundi show hints";
3081 e->usage =
3082 "Usage: dundi show hints\n"
3083 " Lists all DUNDi 'DONTASK' hints in the cache.\n";
3084 return NULL;
3085 case CLI_GENERATE:
3086 return NULL;
3087 }
3088
3089 if (a->argc != 3) {
3090 return CLI_SHOWUSAGE;
3091 }
3092
3093 time(&now);
3094 db_tree = ast_db_gettree("dundi/cache/hint", NULL);
3095 ast_cli(a->fd, FORMAT2, "Prefix", "Context", "Expiration", "From");
3096
3097 for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
3098 char *rest = NULL;
3099
3100 if (ast_get_time_t(db_entry->data, &ts, 0, &length)) {
3101 continue;
3102 }
3103
3104 expiry = ts - now;
3105
3106 if (expiry <= 0) {
3107 continue;
3108 }
3109
3110 ptr = db_entry->key + sizeof("/dundi/cache/hint");
3111 src = strtok_r(ptr, "/", &rest);
3112 number = strtok_r(NULL, "/", &rest);
3113 context = strtok_r(NULL, "/", &rest);
3114 ptr = strtok_r(NULL, "/", &rest);
3115
3116 if (*ptr != 'e') {
3117 continue;
3118 }
3119
3120 cnt++;
3121 dundi_str_short_to_eid(&src_eid, src);
3122 ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3123 ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str);
3124 }
3125
3126 ast_cli(a->fd, "Number of entries: %d\n", cnt);
3127 ast_db_freetree(db_tree);
3128
3129 return CLI_SUCCESS;
3130#undef FORMAT
3131#undef FORMAT2
3132}
3133
3134static struct ast_cli_entry cli_dundi[] = {
3135 AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging"),
3136 AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records"),
3137 AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
3138 AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
3139 AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
3140 AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
3141 AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
3142 AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
3143 AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
3144 AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
3145 AST_CLI_DEFINE(dundi_show_cache, "Show DUNDi cache"),
3146 AST_CLI_DEFINE(dundi_show_hints, "Show DUNDi hints in the cache"),
3147 AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
3148 AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
3149 AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
3150};
3151
3153{
3154 struct dundi_transaction *trans;
3155 int tid;
3156
3157 /* Don't allow creation of transactions to non-registered peers */
3158 if (p && ast_sockaddr_isnull(&p->addr)) {
3159 return NULL;
3160 }
3161 tid = get_trans_id();
3162 if (tid < 1)
3163 return NULL;
3164 if (!(trans = ast_calloc(1, sizeof(*trans))))
3165 return NULL;
3166
3167 if (global_storehistory) {
3168 trans->start = ast_tvnow();
3170 }
3172 trans->autokillid = -1;
3173 if (p) {
3174 apply_peer(trans, p);
3175 if (!p->sentfullkey)
3177 }
3178 trans->strans = tid;
3180
3181 return trans;
3182}
3183
3184static int dundi_xmit(struct dundi_packet *pack)
3185{
3186 int res;
3187 if (dundidebug)
3188 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
3189
3190 if (netsocket2 < 0) {
3191 res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
3192 } else {
3193 if (ast_sockaddr_is_ipv4(&pack->parent->addr)) {
3194 res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
3195 } else {
3196 res = ast_sendto(netsocket2, pack->data, pack->datalen, 0, &pack->parent->addr);
3197 }
3198 }
3199
3200 if (res < 0) {
3201 ast_log(LOG_WARNING, "Failed to transmit to '%s': %s\n",
3202 ast_sockaddr_stringify(&pack->parent->addr), strerror(errno));
3203 }
3204 if (res > 0)
3205 res = 0;
3206 return res;
3207}
3208
3209static void destroy_packet(struct dundi_packet *pack, int needfree)
3210{
3211 if (pack->parent)
3212 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
3214 if (needfree)
3215 ast_free(pack);
3216}
3217
3218static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
3219{
3220 struct dundi_peer *peer;
3221 int ms;
3222 int x;
3223 int cnt;
3224 char eid_str[20];
3226 AST_LIST_TRAVERSE(&peers, peer, list) {
3227 if (peer->regtrans == trans)
3228 peer->regtrans = NULL;
3229 if (peer->qualtrans == trans) {
3230 if (fromtimeout) {
3231 if (peer->lastms > -1)
3232 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3233 peer->lastms = -1;
3234 } else {
3235 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
3236 if (ms < 1)
3237 ms = 1;
3238 if (ms < peer->maxms) {
3239 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
3240 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3241 } else if (peer->lastms < peer->maxms) {
3242 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);
3243 }
3244 peer->lastms = ms;
3245 }
3246 peer->qualtrans = NULL;
3247 }
3248 if (ast_test_flag(trans, FLAG_STOREHIST)) {
3249 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
3250 if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
3251 peer->avgms = 0;
3252 cnt = 0;
3254 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
3255 peer->lookuptimes[x] = peer->lookuptimes[x-1];
3256 peer->lookups[x] = peer->lookups[x-1];
3257 if (peer->lookups[x]) {
3258 peer->avgms += peer->lookuptimes[x];
3259 cnt++;
3260 }
3261 }
3262 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
3263 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
3264 if (peer->lookups[0]) {
3265 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
3266 peer->avgms += peer->lookuptimes[0];
3267 cnt++;
3268 }
3269 if (cnt)
3270 peer->avgms /= cnt;
3271 }
3272 }
3273 }
3274 }
3275 }
3276 if (trans->parent) {
3277 /* Unlink from parent if appropriate */
3278 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
3279 if (AST_LIST_EMPTY(&trans->parent->trans)) {
3280 /* Wake up sleeper */
3281 if (trans->parent->pfds[1] > -1) {
3282 if (write(trans->parent->pfds[1], "killa!", 6) < 0) {
3283 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
3284 }
3285 }
3286 }
3287 }
3288 /* Unlink from all trans */
3289 AST_LIST_REMOVE(&alltrans, trans, all);
3290 destroy_packets(&trans->packets);
3291 destroy_packets(&trans->lasttrans);
3293 if (trans->thread) {
3294 /* If used by a thread, mark as dead and be done */
3295 ast_set_flag(trans, FLAG_DEAD);
3296 } else
3297 ast_free(trans);
3298}
3299
3300static int dundi_rexmit(const void *data)
3301{
3302 struct dundi_packet *pack = (struct dundi_packet *)data;
3303 int res;
3305 if (pack->retrans < 1) {
3306 pack->retransid = -1;
3307 if (!ast_test_flag(pack->parent, FLAG_ISQUAL)) {
3308 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s' msg %d on call %d\n",
3309 ast_sockaddr_stringify(&pack->parent->addr), pack->h->oseqno, ntohs(pack->h->strans));
3310 }
3311 destroy_trans(pack->parent, 1);
3312 res = 0;
3313 } else {
3314 /* Decrement retransmission, try again */
3315 pack->retrans--;
3316 dundi_xmit(pack);
3317 res = 1;
3318 }
3320 return res;
3321}
3322
3323static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
3324{
3325 struct dundi_packet *pack;
3326 int res;
3327 int len;
3328 char eid_str[20];
3329 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
3330 /* Reserve enough space for encryption */
3331 if (ast_test_flag(trans, FLAG_ENCRYPT))
3332 len += 384;
3333 pack = ast_calloc(1, len);
3334 if (pack) {
3335 pack->h = (struct dundi_hdr *)(pack->data);
3336 pack->retransid = -1;
3337 if (cmdresp != DUNDI_COMMAND_ACK) {
3338 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
3339 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3340 AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
3341 }
3342 pack->parent = trans;
3343 pack->h->strans = htons(trans->strans);
3344 pack->h->dtrans = htons(trans->dtrans);
3345 pack->h->iseqno = trans->iseqno;
3346 pack->h->oseqno = trans->oseqno;
3347 pack->h->cmdresp = cmdresp;
3348 pack->datalen = sizeof(struct dundi_hdr);
3349 if (ied) {
3350 memcpy(pack->h->ies, ied->buf, ied->pos);
3351 pack->datalen += ied->pos;
3352 }
3353 if (final) {
3354 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3355 ast_set_flag(trans, FLAG_FINAL);
3356 }
3357 pack->h->cmdflags = flags;
3358 if (cmdresp != DUNDI_COMMAND_ACK) {
3359 trans->oseqno++;
3360 trans->oseqno = trans->oseqno % 256;
3361 }
3362 trans->aseqno = trans->iseqno;
3363 /* If we have their public key, encrypt */
3364 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3365 switch(cmdresp) {
3374 if (dundidebug)
3375 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3376 res = dundi_encrypt(trans, pack);
3377 break;
3378 default:
3379 res = 0;
3380 }
3381 } else
3382 res = 0;
3383 if (!res)
3384 res = dundi_xmit(pack);
3385 if (res)
3386 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3387
3389 ast_free(pack);
3390 return res;
3391 }
3392 return -1;
3393}
3394
3395static int do_autokill(const void *data)
3396{
3397 struct dundi_transaction *trans = (struct dundi_transaction *)data;
3398 char eid_str[20];
3399 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3400 ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3401 trans->autokillid = -1;
3402 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3403 return 0;
3404}
3405
3407{
3408 struct dundi_peer *p;
3409 if (!ast_eid_cmp(eid, us)) {
3411 return;
3412 }
3415 if (!ast_eid_cmp(&p->eid, eid)) {
3416 if (has_permission(&p->include, context))
3418 else
3420 break;
3421 }
3422 }
3423 if (!p)
3426}
3427
3428static int dundi_discover(struct dundi_transaction *trans)
3429{
3430 struct dundi_ie_data ied;
3431 int x;
3432 if (!trans->parent) {
3433 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3434 return -1;
3435 }
3436 memset(&ied, 0, sizeof(ied));
3438 if (!dundi_eid_zero(&trans->us_eid))
3440 for (x=0;x<trans->eidcount;x++)
3441 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3444 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3445 if (trans->parent->cbypass)
3447 if (trans->autokilltimeout)
3448 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3449 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3450}
3451
3452static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3453{
3454 struct dundi_ie_data ied;
3455 int x, res;
3456 int max = 999999;
3457 int expiration = dundi_cache_time;
3458 int ouranswers=0;
3459 dundi_eid *avoid[1] = { NULL, };
3460 int direct[1] = { 0, };
3461 struct dundi_result dr[MAX_RESULTS];
3462 struct dundi_hint_metadata hmd;
3463 if (!trans->parent) {
3464 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3465 return -1;
3466 }
3467 memset(&hmd, 0, sizeof(hmd));
3468 memset(&dr, 0, sizeof(dr));
3469 /* Look up the answers we're going to include */
3470 for (x=0;x<mapcount;x++)
3471 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3472 if (ouranswers < 0)
3473 ouranswers = 0;
3474 for (x=0;x<ouranswers;x++) {
3475 if (dr[x].weight < max)
3476 max = dr[x].weight;
3477 }
3478 if (max) {
3479 /* If we do not have a canonical result, keep looking */
3480 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);
3481 if (res > 0) {
3482 /* Append answer in result */
3483 ouranswers += res;
3484 }
3485 }
3486
3487 if (ouranswers > 0) {
3488 *foundanswers += ouranswers;
3489 memset(&ied, 0, sizeof(ied));
3491 if (!dundi_eid_zero(&trans->us_eid))
3492 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3493 for (x=0;x<trans->eidcount;x++)
3494 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3497 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3498 for (x=0;x<ouranswers;x++) {
3499 /* Add answers */
3500 if (dr[x].expiration && (expiration > dr[x].expiration))
3501 expiration = dr[x].expiration;
3502 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3503 }
3505 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3506 if (trans->autokilltimeout)
3507 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3508 if (expiration < *minexp)
3509 *minexp = expiration;
3510 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3511 } else {
3512 /* Oops, nothing to send... */
3513 destroy_trans(trans, 0);
3514 return 0;
3515 }
3516}
3517
3518static int dundi_query(struct dundi_transaction *trans)
3519{
3520 struct dundi_ie_data ied;
3521 int x;
3522 if (!trans->parent) {
3523 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3524 return -1;
3525 }
3526 memset(&ied, 0, sizeof(ied));
3528 if (!dundi_eid_zero(&trans->us_eid))
3529 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3530 for (x=0;x<trans->eidcount;x++)
3531 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3534 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3535 if (trans->autokilltimeout)
3536 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3537 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3538}
3539
3541{
3542 struct dundi_transaction *trans;
3544 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3545 dundi_discover(trans);
3546 }
3548 return 0;
3549}
3550
3551static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3552{
3553 struct dundi_transaction *trans;
3554
3555 /* Mark all as "in thread" so they don't disappear */
3557 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3558 if (trans->thread)
3559 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3560 trans->thread = 1;
3561 }
3563
3564 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3565 if (!ast_test_flag(trans, FLAG_DEAD))
3566 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3567 }
3568
3569 /* Cleanup any that got destroyed in the mean time */
3571 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
3572 trans->thread = 0;
3573 if (ast_test_flag(trans, FLAG_DEAD)) {
3574 ast_debug(1, "Our transaction went away!\n");
3575 /* This is going to remove the transaction from the dundi_request's list, as well
3576 * as the global transactions list */
3577 destroy_trans(trans, 0);
3578 }
3579 }
3582
3583 return 0;
3584}
3585
3587{
3588 struct dundi_transaction *trans;
3589
3591 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3592 dundi_query(trans);
3593 }
3595
3596 return 0;
3597}
3598
3600{
3601 /* Minimize the message propagation through DUNDi by
3602 alerting the network to hops which should be not be considered */
3603 struct dundi_transaction *trans;
3604 struct dundi_peer *peer;
3605 dundi_eid tmp;
3606 int x;
3607 int needpush;
3608
3610 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3611 /* Pop off the true root */
3612 if (trans->eidcount) {
3613 tmp = trans->eids[--trans->eidcount];
3614 needpush = 1;
3615 } else {
3616 tmp = trans->us_eid;
3617 needpush = 0;
3618 }
3619
3620 AST_LIST_TRAVERSE(&peers, peer, list) {
3621 if (ast_eid_cmp(&peer->eid, &empty_eid) && /* peer's eid is not empty (in case of dynamic peers) */
3622 (peer->lastms > -1) && /* peer is reachable */
3623 has_permission(&peer->include, dr->dcontext) && /* peer has destination context */
3624 ast_eid_cmp(&peer->eid, &trans->them_eid) && /* peer is not transaction endpoint */
3625 (peer->order <= order)) {
3626 /* For each other transaction, make sure we don't
3627 ask this EID about the others if they're not
3628 already in the list */
3629 if (!ast_eid_cmp(&tmp, &peer->eid))
3630 x = -1;
3631 else {
3632 for (x=0;x<trans->eidcount;x++) {
3633 if (!ast_eid_cmp(&trans->eids[x], &peer->eid))
3634 break;
3635 }
3636 }
3637 if (x == trans->eidcount) {
3638 /* Nope not in the list, if needed, add us at the end since we're the source */
3639 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3640 trans->eids[trans->eidcount++] = peer->eid;
3641 /* Need to insert the real root (or us) at the bottom now as
3642 a requirement now. */
3643 needpush = 1;
3644 }
3645 }
3646 }
3647 }
3648 /* If necessary, push the true root back on the end */
3649 if (needpush)
3650 trans->eids[trans->eidcount++] = tmp;
3651 }
3653
3654 return 0;
3655}
3656
3657static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3658{
3659 struct dundi_transaction *trans;
3660 int x;
3661 char eid_str[20];
3662 char eid_str2[20];
3663
3664 /* Ignore if not registered */
3665 if (ast_sockaddr_isnull(&p->addr)) {
3666 return 0;
3667 }
3668 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3669 return 0;
3670
3671 if (ast_strlen_zero(dr->number))
3672 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);
3673 else
3674 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);
3675
3676 trans = create_transaction(p);
3677 if (!trans)
3678 return -1;
3679 trans->parent = dr;
3680 trans->ttl = ttl;
3681 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3682 trans->eids[x] = *avoid[x];
3683 trans->eidcount = x;
3684 AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
3685
3686 return 0;
3687}
3688
3689static void cancel_request(struct dundi_request *dr)
3690{
3691 struct dundi_transaction *trans;
3692
3694 while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
3695 /* Orphan transaction from request */
3696 trans->parent = NULL;
3697 /* Send final cancel */
3698 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3699 }
3701}
3702
3703static void abort_request(struct dundi_request *dr)
3704{
3705 struct dundi_transaction *trans;
3706
3708 while ((trans = AST_LIST_FIRST(&dr->trans))) {
3709 /* This will remove the transaction from the list */
3710 destroy_trans(trans, 0);
3711 }
3713}
3714
3715static 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[])
3716{
3717 struct dundi_peer *p;
3718 int x;
3719 int res;
3720 int pass;
3721 int allowconnect;
3722 char eid_str[20];
3725 if (modeselect == 1) {
3726 /* Send the precache to push upstreams only! */
3727 pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
3728 allowconnect = 1;
3729 } else {
3730 /* Normal lookup / EID query */
3731 pass = has_permission(&p->include, dr->dcontext);
3732 allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
3733 }
3734 if (skip) {
3735 if (!ast_eid_cmp(skip, &p->eid))
3736 pass = 0;
3737 }
3738 if (pass) {
3739 if (p->order <= order) {
3740 /* Check order first, then check cache, regardless of
3741 omissions, this gets us more likely to not have an
3742 affected answer. */
3743 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
3744 res = 0;
3745 /* Make sure we haven't already seen it and that it won't
3746 affect our answer */
3747 for (x=0;avoid[x];x++) {
3748 if (!ast_eid_cmp(avoid[x], &p->eid) || !ast_eid_cmp(avoid[x], &p->us_eid)) {
3749 /* If not a direct connection, it affects our answer */
3750 if (directs && !directs[x])
3752 break;
3753 }
3754 }
3755 /* Make sure we can ask */
3756 if (allowconnect) {
3757 if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
3758 /* Check for a matching or 0 cache entry */
3759 append_transaction(dr, p, ttl, avoid);
3760 } else {
3761 ast_debug(1, "Avoiding '%s' in transaction\n", ast_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
3762 }
3763 }
3764 }
3765 *foundcache |= res;
3766 } else if (!*skipped || (p->order < *skipped))
3767 *skipped = p->order;
3768 }
3769 }
3771}
3772
3773static int register_request(struct dundi_request *dr, struct dundi_request **pending)
3774{
3775 struct dundi_request *cur;
3776 int res=0;
3777 char eid_str[20];
3780 ast_debug(1, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
3781 dr->dcontext, dr->number);
3782 if (!strcasecmp(cur->dcontext, dr->dcontext) &&
3783 !strcasecmp(cur->number, dr->number) &&
3784 (!ast_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
3785 ast_debug(1, "Found existing query for '%s@%s' for '%s' crc '%08x'\n",
3786 cur->dcontext, cur->number, ast_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
3787 *pending = cur;
3788 res = 1;
3789 break;
3790 }
3791 }
3792 if (!res) {
3793 ast_debug(1, "Registering request for '%s@%s' on behalf of '%s' crc '%08x'\n",
3794 dr->number, dr->dcontext, ast_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
3795 /* Go ahead and link us in since nobody else is searching for this */
3797 *pending = NULL;
3798 }
3800 return res;
3801}
3802
3804{
3808}
3809
3810static int check_request(struct dundi_request *dr)
3811{
3812 struct dundi_request *cur;
3813
3816 if (cur == dr)
3817 break;
3818 }
3820
3821 return cur ? 1 : 0;
3822}
3823
3824static unsigned long avoid_crc32(dundi_eid *avoid[])
3825{
3826 /* Idea is that we're calculating a checksum which is independent of
3827 the order that the EID's are listed in */
3828 uint32_t acrc32 = 0;
3829 int x;
3830 for (x=0;avoid[x];x++) {
3831 /* Order doesn't matter */
3832 if (avoid[x+1]) {
3833 acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
3834 }
3835 }
3836 return acrc32;
3837}
3838
3839static 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[])
3840{
3841 int res;
3842 struct dundi_request dr, *pending;
3843 dundi_eid *rooteid=NULL;
3844 int x;
3845 int ttlms;
3846 int ms;
3847 int foundcache;
3848 int skipped=0;
3849 int order=0;
3850 char eid_str[20];
3851 struct timeval start;
3852
3853 /* Don't do anthing for a hungup channel */
3854 if (chan && ast_check_hangup(chan))
3855 return 0;
3856
3857 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3858
3859 for (x=0;avoid[x];x++)
3860 rooteid = avoid[x];
3861 /* Now perform real check */
3862 memset(&dr, 0, sizeof(dr));
3863 if (pipe(dr.pfds)) {
3864 ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
3865 return -1;
3866 }
3867 dr.dr = result;
3868 dr.hmd = hmd;
3869 dr.maxcount = maxret;
3870 dr.expiration = *expiration;
3871 dr.cbypass = cbypass;
3872 dr.crc32 = avoid_crc32(avoid);
3873 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
3874 ast_copy_string(dr.number, number, sizeof(dr.number));
3875 if (rooteid)
3876 dr.root_eid = *rooteid;
3877 res = register_request(&dr, &pending);
3878 if (res) {
3879 /* Already a request */
3880 if (rooteid && !ast_eid_cmp(&dr.root_eid, &pending->root_eid)) {
3881 /* This is on behalf of someone else. Go ahead and close this out since
3882 they'll get their answer anyway. */
3883 ast_debug(1, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3884 dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
3885 close(dr.pfds[0]);
3886 close(dr.pfds[1]);
3887 return -2;
3888 } else {
3889 /* Wait for the cache to populate */
3890 ast_debug(1, "Waiting for similar request for '%s@%s' for '%s'\n",
3891 dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
3892 start = ast_tvnow();
3893 while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
3894 /* XXX Would be nice to have a way to poll/select here XXX */
3895 /* XXX this is a busy wait loop!!! */
3896 usleep(1);
3897 }
3898 /* Continue on as normal, our cache should kick in */
3899 }
3900 }
3901 /* Create transactions */
3902 do {
3903 order = skipped;
3904 skipped = 0;
3905 foundcache = 0;
3906 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
3907 } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
3908 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3909 do this earlier because we didn't know if we were going to have transactions
3910 or not. */
3911 if (!ttl) {
3913 abort_request(&dr);
3915 close(dr.pfds[0]);
3916 close(dr.pfds[1]);
3917 return 0;
3918 }
3919
3920 /* Optimize transactions */
3922 /* Actually perform transactions */
3924 /* Wait for transaction to come back */
3925 start = ast_tvnow();
3926 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
3927 ms = 100;
3928 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3929 }
3930 if (chan && ast_check_hangup(chan))
3931 ast_debug(1, "Hrm, '%s' hungup before their query for %s@%s finished\n", ast_channel_name(chan), dr.number, dr.dcontext);
3934 res = dr.respcount;
3935 *expiration = dr.expiration;
3936 close(dr.pfds[0]);
3937 close(dr.pfds[1]);
3938 return res;
3939}
3940
3941int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
3942{
3943 struct dundi_hint_metadata hmd;
3944 dundi_eid *avoid[1] = { NULL, };
3945 int direct[1] = { 0, };
3946 int expiration = dundi_cache_time;
3947 memset(&hmd, 0, sizeof(hmd));
3949 return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
3950}
3951
3952static void reschedule_precache(const char *number, const char *context, int expiration)
3953{
3954 struct dundi_precache_queue *qe, *prev;
3955
3958 if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
3960 break;
3961 }
3962 }
3964 if (!qe) {
3965 int len = sizeof(*qe);
3966 int num_len = strlen(number) + 1;
3967 int context_len = strlen(context) + 1;
3968 if (!(qe = ast_calloc(1, len + num_len + context_len))) {
3970 return;
3971 }
3972 strcpy(qe->number, number);
3973 qe->context = qe->number + num_len + 1;
3974 ast_copy_string(qe->context, context, context_len);
3975 }
3976 time(&qe->expiration);
3977 qe->expiration += expiration;
3978 if ((prev = AST_LIST_FIRST(&pcq))) {
3979 while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
3980 prev = AST_LIST_NEXT(prev, list);
3981 AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
3982 } else
3985}
3986
3987static void dundi_precache_full(void)
3988{
3989 struct dundi_mapping *cur;
3990 struct ast_context *con;
3991 struct ast_exten *e;
3992
3993 AST_LIST_TRAVERSE(&mappings, cur, list) {
3994 ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
3996 con = NULL;
3997 while ((con = ast_walk_contexts(con))) {
3998 if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
3999 continue;
4000 /* Found the match, now queue them all up */
4001 ast_rdlock_context(con);
4002 e = NULL;
4003 while ((e = ast_walk_context_extensions(con, e)))
4005 ast_unlock_context(con);
4006 }
4008 }
4009}
4010
4011static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
4012{
4013 struct dundi_request dr;
4014 struct dundi_hint_metadata hmd;
4015 struct dundi_result dr2[MAX_RESULTS];
4016 struct timeval start;
4017 struct dundi_mapping *maps = NULL, *cur;
4018 int nummaps = 0;
4019 int foundanswers;
4020 int foundcache, skipped, ttlms, ms;
4021 if (!context)
4022 context = "e164";
4023 ast_debug(1, "Precache internal (%s@%s)!\n", number, context);
4024
4027 if (!strcasecmp(cur->dcontext, context))
4028 nummaps++;
4029 }
4030 if (nummaps) {
4031 maps = ast_alloca(nummaps * sizeof(*maps));
4032 nummaps = 0;
4034 if (!strcasecmp(cur->dcontext, context))
4035 maps[nummaps++] = *cur;
4036 }
4037 }
4039 if (!nummaps) {
4040 return -1;
4041 }
4042 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
4043 memset(&dr2, 0, sizeof(dr2));
4044 memset(&dr, 0, sizeof(dr));
4045 memset(&hmd, 0, sizeof(hmd));
4046 dr.dr = dr2;
4047 ast_copy_string(dr.number, number, sizeof(dr.number));
4048 ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
4049 dr.maxcount = MAX_RESULTS;
4050 dr.expiration = dundi_cache_time;
4051 dr.hmd = &hmd;
4052 dr.pfds[0] = dr.pfds[1] = -1;
4053 if (pipe(dr.pfds) < 0) {
4054 ast_log(LOG_WARNING, "pipe() failed: %s\n", strerror(errno));
4055 return -1;
4056 }
4057 build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
4059 foundanswers = 0;
4060 precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
4061 if (foundanswers) {
4062 if (dr.expiration > 0)
4063 reschedule_precache(dr.number, dr.dcontext, dr.expiration);
4064 else
4065 ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
4066 }
4067 start = ast_tvnow();
4068 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
4069 if (dr.pfds[0] > -1) {
4070 ms = 100;
4071 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
4072 } else
4073 usleep(1);
4074 }
4076 if (dr.pfds[0] > -1) {
4077 close(dr.pfds[0]);
4078 close(dr.pfds[1]);
4079 }
4080 return 0;
4081}
4082
4083int dundi_precache(const char *context, const char *number)
4084{
4085 dundi_eid *avoid[1] = { NULL, };
4087}
4088
4089static 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[])
4090{
4091 int res;
4092 struct dundi_request dr;
4093 dundi_eid *rooteid=NULL;
4094 int x;
4095 int ttlms;
4096 int skipped=0;
4097 int foundcache=0;
4098 struct timeval start;
4099
4100 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
4101
4102 for (x=0;avoid[x];x++)
4103 rooteid = avoid[x];
4104 /* Now perform real check */
4105 memset(&dr, 0, sizeof(dr));
4106 dr.hmd = hmd;
4107 dr.dei = dei;
4108 dr.pfds[0] = dr.pfds[1] = -1;
4109 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
4110 memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
4111 if (rooteid)
4112 dr.root_eid = *rooteid;
4113 /* Create transactions */
4114 build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
4115
4116 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
4117 do this earlier because we didn't know if we were going to have transactions
4118 or not. */
4119 if (!ttl) {
4121 return 0;
4122 }
4123
4124 /* Optimize transactions */
4125 optimize_transactions(&dr, 9999);
4126 /* Actually perform transactions */
4128 /* Wait for transaction to come back */
4129 start = ast_tvnow();
4130 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
4131 usleep(1);
4132 res = dr.respcount;
4133 return res;
4134}
4135
4136int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
4137{
4138 dundi_eid *avoid[1] = { NULL, };
4139 struct dundi_hint_metadata hmd;
4140 memset(&hmd, 0, sizeof(hmd));
4141 return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
4142}
4143
4144enum {
4146};
4147
4151
4152static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
4153{
4154 int results;
4155 int x;
4156 struct dundi_result dr[MAX_RESULTS];
4161 );
4162 char *parse;
4163 struct ast_flags opts = { 0, };
4164
4165 buf[0] = '\0';
4166
4167 if (ast_strlen_zero(num)) {
4168 ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
4169 return -1;
4170 }
4171
4172 parse = ast_strdupa(num);
4173
4175
4176 if (!ast_strlen_zero(args.options)) {
4178 }
4179 if (ast_strlen_zero(args.context)) {
4180 args.context = "e164";
4181 }
4182
4183 results = dundi_lookup(dr, MAX_RESULTS, NULL, args.context, args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4184 if (results > 0) {
4185 sort_results(dr, results);
4186 for (x = 0; x < results; x++) {
4188 snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
4189 break;
4190 }
4191 }
4192 }
4193
4194 return 0;
4195}
4196
4197/*! DUNDILOOKUP
4198 * \ingroup functions
4199*/
4200
4202 .name = "DUNDILOOKUP",
4203 .read = dundifunc_read,
4204};
4205
4206static unsigned int dundi_result_id;
4207
4211 unsigned int id;
4212};
4213
4214static void drds_destroy(struct dundi_result_datastore *drds)
4215{
4216 ast_free(drds);
4217}
4218
4219static void drds_destroy_cb(void *data)
4220{
4221 struct dundi_result_datastore *drds = data;
4222 drds_destroy(drds);
4223}
4224
4226 .type = "DUNDIQUERY",
4227 .destroy = drds_destroy_cb,
4228};
4229
4230static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4231{
4236 );
4237 struct ast_flags opts = { 0, };
4238 char *parse;
4239 struct dundi_result_datastore *drds;
4240 struct ast_datastore *datastore;
4241
4242 if (ast_strlen_zero(data)) {
4243 ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
4244 return -1;
4245 }
4246
4247 if (!chan) {
4248 ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
4249 return -1;
4250 }
4251
4252 parse = ast_strdupa(data);
4253
4255
4256 if (!ast_strlen_zero(args.options))
4258
4259 if (ast_strlen_zero(args.context))
4260 args.context = "e164";
4261
4262 if (!(drds = ast_calloc(1, sizeof(*drds)))) {
4263 return -1;
4264 }
4265
4266 drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1);
4267 snprintf(buf, len, "%u", drds->id);
4268
4269 if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
4270 drds_destroy(drds);
4271 return -1;
4272 }
4273
4274 datastore->data = drds;
4275
4276 drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
4277 args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4278
4279 if (drds->num_results > 0)
4280 sort_results(drds->results, drds->num_results);
4281
4282 ast_channel_lock(chan);
4283 ast_channel_datastore_add(chan, datastore);
4284 ast_channel_unlock(chan);
4285
4286 return 0;
4287}
4288
4290 .name = "DUNDIQUERY",
4291 .read = dundi_query_read,
4292};
4293
4294static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4295{
4297 AST_APP_ARG(id);
4298 AST_APP_ARG(resultnum);
4299 );
4300 char *parse;
4301 unsigned int num;
4302 struct dundi_result_datastore *drds;
4303 struct ast_datastore *datastore;
4304 int res = -1;
4305
4306 if (ast_strlen_zero(data)) {
4307 ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
4308 goto finish;
4309 }
4310
4311 if (!chan) {
4312 ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n");
4313 goto finish;
4314 }
4315
4316 parse = ast_strdupa(data);
4317
4319
4320 if (ast_strlen_zero(args.id)) {
4321 ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n");
4322 goto finish;
4323 }
4324
4325 if (ast_strlen_zero(args.resultnum)) {
4326 ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n");
4327 goto finish;
4328 }
4329
4330 ast_channel_lock(chan);
4332 ast_channel_unlock(chan);
4333
4334 if (!datastore) {
4335 ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id);
4336 goto finish;
4337 }
4338
4339 drds = datastore->data;
4340
4341 if (!strcasecmp(args.resultnum, "getnum")) {
4342 snprintf(buf, len, "%d", drds->num_results < 0 ? 0 : drds->num_results);
4343 res = 0;
4344 goto finish;
4345 }
4346
4347 if (sscanf(args.resultnum, "%30u", &num) != 1) {
4348 ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n",
4349 args.resultnum);
4350 goto finish;
4351 }
4352
4353 if (num && drds->num_results > 0 && num <= drds->num_results) {
4354 snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
4355 res = 0;
4356 } else
4357 ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
4358
4359finish:
4360 return res;
4361}
4362
4364 .name = "DUNDIRESULT",
4365 .read = dundi_result_read,
4366};
4367
4368static void mark_peers(void)
4369{
4370 struct dundi_peer *peer;
4372 AST_LIST_TRAVERSE(&peers, peer, list) {
4373 peer->dead = 1;
4374 }
4376}
4377
4378static void mark_mappings(void)
4379{
4380 struct dundi_mapping *map;
4381
4384 map->dead = 1;
4385 }
4387}
4388
4389static void destroy_permissions(struct permissionlist *permlist)
4390{
4391 struct permission *perm;
4392
4393 while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
4394 ast_free(perm);
4395}
4396
4397static void destroy_peer(struct dundi_peer *peer)
4398{
4399 int idx;
4400
4403 if (peer->regtrans) {
4404 destroy_trans(peer->regtrans, 0);
4405 }
4407 if (peer->qualtrans) {
4408 destroy_trans(peer->qualtrans, 0);
4409 }
4412
4413 /* Release lookup history */
4414 for (idx = 0; idx < ARRAY_LEN(peer->lookups); ++idx) {
4415 ast_free(peer->lookups[idx]);
4416 }
4417
4418 ast_free(peer);
4419}
4420
4421static void destroy_map(struct dundi_mapping *map)
4422{
4423 ast_free(map->weightstr);
4424 ast_free(map);
4425}
4426
4427static void prune_peers(void)
4428{
4429 struct dundi_peer *peer;
4430
4433 if (peer->dead) {
4435 destroy_peer(peer);
4436 }
4437 }
4440}
4441
4442static void prune_mappings(void)
4443{
4444 struct dundi_mapping *map;
4445
4448 if (map->dead) {
4450 destroy_map(map);
4451 }
4452 }
4455}
4456
4457static void append_permission(struct permissionlist *permlist, const char *s, int allow)
4458{
4459 struct permission *perm;
4460
4461 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
4462 return;
4463
4464 strcpy(perm->name, s);
4465 perm->allow = allow;
4466
4467 AST_LIST_INSERT_TAIL(permlist, perm, list);
4468}
4469
4470#define MAX_OPTS 128
4471
4472static void build_mapping(const char *name, const char *value)
4473{
4474 char *t, *fields[MAX_OPTS];
4475 struct dundi_mapping *map;
4476 int x;
4477 int y;
4478
4479 t = ast_strdupa(value);
4480
4482 /* Find a double match */
4483 if (!strcasecmp(map->dcontext, name) &&
4484 (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
4485 (!value[strlen(map->lcontext)] ||
4486 (value[strlen(map->lcontext)] == ','))))
4487 break;
4488 }
4489 if (!map) {
4490 if (!(map = ast_calloc(1, sizeof(*map))))
4491 return;
4493 map->dead = 1;
4494 }
4495 map->options = 0;
4496 memset(fields, 0, sizeof(fields));
4497 x = 0;
4498 while (t && x < MAX_OPTS) {
4499 fields[x++] = t;
4500 t = strchr(t, ',');
4501 if (t) {
4502 *t = '\0';
4503 t++;
4504 }
4505 } /* Russell was here, arrrr! */
4506 if ((x == 1) && ast_strlen_zero(fields[0])) {
4507 /* Placeholder mapping */
4508 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4509 map->dead = 0;
4510 } else if (x >= 4) {
4511 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4512 ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
4513 if ((sscanf(fields[1], "%30d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
4514 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4515 if ((map->tech = str2tech(fields[2])))
4516 map->dead = 0;
4517 } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') {
4518 map->weightstr = ast_strdup(fields[1]);
4519 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4520 if ((map->tech = str2tech(fields[2])))
4521 map->dead = 0;
4522 } else {
4523 ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
4524 }
4525 for (y = 4;y < x; y++) {
4526 if (!strcasecmp(fields[y], "nounsolicited"))
4528 else if (!strcasecmp(fields[y], "nocomunsolicit"))
4530 else if (!strcasecmp(fields[y], "residential"))
4532 else if (!strcasecmp(fields[y], "commercial"))
4534 else if (!strcasecmp(fields[y], "mobile"))
4535 map->options |= DUNDI_FLAG_MOBILE;
4536 else if (!strcasecmp(fields[y], "nopartial"))
4538 else
4539 ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
4540 }
4541 } else
4542 ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
4543}
4544
4545/*! \note Called with the peers list already locked */
4546static int do_register(const void *data)
4547{
4548 struct dundi_ie_data ied;
4549 struct dundi_peer *peer = (struct dundi_peer *)data;
4550 char eid_str[20];
4551 char eid_str2[20];
4552 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));
4554 /* Destroy old transaction if there is one */
4555 if (peer->regtrans)
4556 destroy_trans(peer->regtrans, 0);
4557 peer->regtrans = create_transaction(peer);
4558 if (peer->regtrans) {
4560 memset(&ied, 0, sizeof(ied));
4564 dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
4565
4566 } else
4567 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));
4568
4569 return 0;
4570}
4571
4572static int do_qualify(const void *data)
4573{
4574 struct dundi_peer *peer = (struct dundi_peer *)data;
4575 peer->qualifyid = -1;
4576 qualify_peer(peer, 0);
4577 return 0;
4578}
4579
4580static void qualify_peer(struct dundi_peer *peer, int schedonly)
4581{
4582 int when;
4584 if (peer->qualtrans)
4585 destroy_trans(peer->qualtrans, 0);
4586 peer->qualtrans = NULL;
4587 if (peer->maxms > 0) {
4588 when = 60000;
4589 if (peer->lastms < 0)
4590 when = 10000;
4591 if (schedonly)
4592 when = 5000;
4593 peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
4594 if (!schedonly)
4595 peer->qualtrans = create_transaction(peer);
4596 if (peer->qualtrans) {
4597 peer->qualtx = ast_tvnow();
4600 }
4601 }
4602}
4603static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
4604{
4605 char data[256];
4606 char *c;
4607 int port, expire;
4608 char eid_str[20];
4609 ast_eid_to_str(eid_str, sizeof(eid_str), eid);
4610 if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
4611 /*
4612 * data is in the form:
4613 * IPv6 address: [ffff:ffff::ffff:ffff]:port:expire
4614 * IPv4 address: a.b.c.d:port:expire
4615 */
4616 c = data;
4617 if (*c == '[') {
4618 /* Need to skip over the IPv6 address. */
4619 c = strchr(c, ']');
4620 }
4621 if (c) {
4622 c = strchr(c, ':');
4623 }
4624 if (c) {
4625 *c = '\0';
4626 c++;
4627 if (sscanf(c, "%5d:%30d", &port, &expire) == 2) {
4628 /* Got it! */
4629 struct ast_sockaddr *addrs;
4630
4631 if (ast_sockaddr_resolve(&addrs, data, PARSE_PORT_FORBID, AF_UNSPEC) > 0){
4632 ast_sockaddr_copy(&peer->addr, &addrs[0]);
4633 ast_free(addrs);
4634 }
4635 ast_sockaddr_set_port(&peer->addr, port);
4636 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
4637 }
4638 }
4639 }
4640}
4641
4642
4643static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
4644{
4645 struct dundi_peer *peer;
4646 dundi_eid testeid;
4647 int needregister=0;
4648 char eid_str[20];
4649 int port = 0;
4650
4652 AST_LIST_TRAVERSE(&peers, peer, list) {
4653 if (!ast_eid_cmp(&peer->eid, eid)) {
4654 break;
4655 }
4656 }
4657 if (!peer) {
4658 /* Add us into the list */
4659 if (!(peer = ast_calloc(1, sizeof(*peer)))) {
4661 return;
4662 }
4663 peer->registerid = -1;
4664 peer->registerexpire = -1;
4665 peer->qualifyid = -1;
4666 populate_addr(peer, eid);
4668 }
4669 peer->dead = 0;
4670 peer->eid = *eid;
4671 peer->us_eid = global_eid;
4675 for (; v; v = v->next) {
4676 if (!strcasecmp(v->name, "inkey")) {
4677 ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
4678 } else if (!strcasecmp(v->name, "outkey")) {
4679 ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
4680 } else if (!strcasecmp(v->name, "port")) {
4681 port = atoi(v->value);
4682 } else if (!strcasecmp(v->name, "host")) {
4683 if (!strcasecmp(v->value, "dynamic")) {
4684 peer->dynamic = 1;
4685 } else {
4686 struct ast_sockaddr *addrs;
4687
4688 if (ast_sockaddr_resolve(&addrs, v->value, PARSE_PORT_FORBID, AF_UNSPEC) > 0) {
4689 ast_sockaddr_copy(&peer->addr, &addrs[0]);
4690 peer->dynamic = 0;
4691 ast_free(addrs);
4692 } else {
4693 ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
4694 peer->dead = 1;
4695 }
4696 }
4697 } else if (!strcasecmp(v->name, "ustothem")) {
4698 if (!ast_str_to_eid(&testeid, v->value))
4699 peer->us_eid = testeid;
4700 else
4701 ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
4702 } else if (!strcasecmp(v->name, "include")) {
4703 append_permission(&peer->include, v->value, 1);
4704 } else if (!strcasecmp(v->name, "permit")) {
4705 append_permission(&peer->permit, v->value, 1);
4706 } else if (!strcasecmp(v->name, "noinclude")) {
4707 append_permission(&peer->include, v->value, 0);
4708 } else if (!strcasecmp(v->name, "deny")) {
4709 append_permission(&peer->permit, v->value, 0);
4710 } else if (!strcasecmp(v->name, "register")) {
4711 needregister = ast_true(v->value);
4712 } else if (!strcasecmp(v->name, "order")) {
4713 if (!strcasecmp(v->value, "primary"))
4714 peer->order = 0;
4715 else if (!strcasecmp(v->value, "secondary"))
4716 peer->order = 1;
4717 else if (!strcasecmp(v->value, "tertiary"))
4718 peer->order = 2;
4719 else if (!strcasecmp(v->value, "quartiary"))
4720 peer->order = 3;
4721 else {
4722 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);
4723 }
4724 } else if (!strcasecmp(v->name, "qualify")) {
4725 if (!strcasecmp(v->value, "no")) {
4726 peer->maxms = 0;
4727 } else if (!strcasecmp(v->value, "yes")) {
4728 peer->maxms = DEFAULT_MAXMS;
4729 } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
4730 ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4731 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
4732 peer->maxms = 0;
4733 }
4734 } else if (!strcasecmp(v->name, "model")) {
4735 if (!strcasecmp(v->value, "inbound"))
4736 peer->model = DUNDI_MODEL_INBOUND;
4737 else if (!strcasecmp(v->value, "outbound"))
4739 else if (!strcasecmp(v->value, "symmetric"))
4741 else if (!strcasecmp(v->value, "none"))
4742 peer->model = 0;
4743 else {
4744 ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4745 v->value, v->lineno);
4746 }
4747 } else if (!strcasecmp(v->name, "precache")) {
4748 if (!strcasecmp(v->value, "inbound"))
4750 else if (!strcasecmp(v->value, "outbound"))
4752 else if (!strcasecmp(v->value, "symmetric"))
4754 else if (!strcasecmp(v->value, "none"))
4755 peer->pcmodel = 0;
4756 else {
4757 ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4758 v->value, v->lineno);
4759 }
4760 }
4761 }
4762
4763 if (!ast_sockaddr_isnull(&peer->addr)) {
4764 ast_sockaddr_set_port(&peer->addr, (0 < port) ? port : DUNDI_PORT);
4765 }
4766
4767 (*globalpcmode) |= peer->pcmodel;
4768 if (!peer->model && !peer->pcmodel) {
4769 ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
4770 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4771 peer->dead = 1;
4772 } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4773 ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4774 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4775 peer->dead = 1;
4776 } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4777 ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4778 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4779 peer->dead = 1;
4780 } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4781 ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4782 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4783 } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4784 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",
4785 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4786 } else {
4787 if (ast_eid_cmp(&peer->eid, &empty_eid)) {
4788 /* Schedule any items for explicitly configured peers. */
4789 if (needregister) {
4790 peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
4791 }
4792 qualify_peer(peer, 1);
4793 }
4794 }
4796}
4797
4798static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
4799{
4800 struct dundi_result results[MAX_RESULTS];
4801 int res;
4802 int x;
4803 int found = 0;
4804 if (ast_strlen_zero(data))
4805 data = context;
4806
4807 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4808 for (x=0;x<res;x++) {
4809 if (ast_test_flag(results + x, flag))
4810 found++;
4811 }
4812 if (found >= priority)
4813 return 1;
4814 return 0;
4815}
4816
4817static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4818{
4819 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
4820}
4821
4822static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4823{
4824 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
4825}
4826
4827static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4828{
4829 struct dundi_result results[MAX_RESULTS];
4830 int res;
4831 int x=0;
4832 char req[1024];
4833 const char *dundiargs;
4834
4835 if (ast_strlen_zero(data))
4836 data = context;
4837
4838 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4839 if (res > 0) {
4840 sort_results(results, res);
4841 for (x=0;x<res;x++) {
4842 if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
4843 if (!--priority)
4844 break;
4845 }
4846 }
4847 }
4848 if (x < res) {
4849 /* Got a hit! */
4850 dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
4851 /* Backwards compatibility with lookups using chan_sip even if we don't have it anymore:
4852 * At a protocol level, "SIP" will always be specified, but depending on our configuration,
4853 * we will use the user-specified channel driver (from dundi.conf) to complete the call.
4854 */
4855 if (!strcasecmp(results[x].tech, "SIP") || !strcasecmp(results[x].tech, "PJSIP")) {
4856 /* Only "SIP" is a valid technology for a DUNDi peer to communicate.
4857 * But if they tell use to use "PJSIP" instead, just interpret it as if they said "SIP" instead. */
4858 if (strcasecmp(results[x].tech, "SIP")) {
4859 ast_log(LOG_WARNING, "%s cannot be specified by DUNDi peers (peer should use SIP for DUNDi lookups instead)\n", results[x].tech);
4860 }
4861 /* Use whatever we're configured to use for SIP protocol calls. */
4862 results[x].techint = outgoing_sip_tech;
4863 ast_copy_string(results[x].tech, tech2str(outgoing_sip_tech), sizeof(results[x].tech));
4864 }
4865 /* PJSIP requires an endpoint to be specified explicitly. */
4867 char *number, *ip = ast_strdupa(results[x].dest);
4869 ast_log(LOG_WARNING, "PJSIP calls require an endpoint to be specified explicitly (use the pjsip_outgoing_endpoint option in dundi.conf)\n");
4870 return -1;
4871 }
4872 /* Take IP/number and turn it into sip:number@IP */
4873 if (ast_strlen_zero(ip)) {
4874 ast_log(LOG_WARNING, "PJSIP destination is empty?\n");
4875 return -1;
4876 }
4877 number = strsep(&ip, "/");
4878 snprintf(req, sizeof(req), "%s/%s/sip:%s@%s,,%s", results[x].tech, pjsip_outgoing_endpoint, S_OR(number, ""), ip, S_OR(dundiargs, ""));
4879 ast_debug(1, "Finalized PJSIP Dial: %s\n", req);
4880 } else { /* SIP, or something else. */
4881 snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest, S_OR(dundiargs, ""));
4882 }
4883 res = ast_pbx_exec_application(chan, "Dial", req);
4884 } else {
4885 res = -1;
4886 }
4887 return res;
4888}
4889
4890static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4891{
4892 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
4893}
4894
4895static struct ast_switch dundi_switch = {
4896 .name = "DUNDi",
4897 .description = "DUNDi Discovered Dialplan Switch",
4898 .exists = dundi_exists,
4899 .canmatch = dundi_canmatch,
4900 .exec = dundi_exec,
4901 .matchmore = dundi_matchmore,
4902};
4903
4904static int get_ipaddress(char *ip, size_t size, const char *str, int family)
4905{
4906 struct ast_sockaddr *addrs;
4907
4908 if (!ast_sockaddr_resolve(&addrs, str, 0, family)) {
4909 return -1;
4910 }
4911
4912 ast_copy_string(ip, ast_sockaddr_stringify_host(&addrs[0]), size);
4913 ast_free(addrs);
4914
4915 return 0;
4916}
4917
4918static void set_host_ipaddr(struct ast_sockaddr *sin)
4919{
4920 char hn[MAXHOSTNAMELEN];
4921 struct addrinfo hints;
4922 int family;
4923
4924 memset(&hints, 0, sizeof(hints));
4925
4926 if (ast_sockaddr_is_ipv6(sin)) {
4927 family = AF_INET6;
4928 } else {
4929 family = AF_INET;
4930 }
4931
4932 if (gethostname(hn, sizeof(hn) - 1) < 0) {
4933 ast_log(LOG_WARNING, "Unable to get host name!\n");
4934 return;
4935 }
4936
4937 get_ipaddress(ipaddr, sizeof(ipaddr), hn, family);
4938}
4939
4940static int set_config(char *config_file, struct ast_sockaddr *sin, int reload, struct ast_sockaddr *sin2)
4941{
4942 struct ast_config *cfg;
4943 struct ast_variable *v;
4944 char *cat;
4945 int x;
4946 struct ast_flags config_flags = { 0 };
4947 static int last_port = 0;
4948 int port = 0;
4949 int globalpcmodel = 0;
4950 dundi_eid testeid;
4951 char bind_addr[80]={0,};
4952 char bind_addr2[80]={0,};
4953
4954 if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
4955 ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
4956 return -1;
4957 }
4958
4961 any_peer = NULL;
4962 outgoing_sip_tech = DUNDI_PROTO_PJSIP; /* Default for new versions */
4963
4965
4967 ast_log(LOG_WARNING, "Entity ID is not set.\n");
4968 }
4969 memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
4970
4972 ast_copy_string(secretpath, "dundi", sizeof(secretpath));
4973 v = ast_variable_browse(cfg, "general");
4974 while(v) {
4975 if (!strcasecmp(v->name, "port")){
4976 port = atoi(v->value);
4977 } else if (!strcasecmp(v->name, "bindaddr")) {
4978 if (get_ipaddress(bind_addr, sizeof(bind_addr), v->value, AF_UNSPEC) == 0) {
4979 if (!ast_sockaddr_parse(sin, bind_addr, 0)) {
4980 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4981 }
4982 }
4983 } else if (!strcasecmp(v->name, "bindaddr2")) {
4984 if (get_ipaddress(bind_addr2, sizeof(bind_addr2), v->value, AF_UNSPEC) == 0) {
4985 if (!ast_sockaddr_parse(sin2, bind_addr2, 0)) {
4986 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4987 }
4988 }
4989 } else if (!strcasecmp(v->name, "authdebug")) {
4990 authdebug = ast_true(v->value);
4991 } else if (!strcasecmp(v->name, "ttl")) {
4992 if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
4993 dundi_ttl = x;
4994 } else {
4995 ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4997 }
4998 } else if (!strcasecmp(v->name, "autokill")) {
4999 if (sscanf(v->value, "%30d", &x) == 1) {
5000 if (x >= 0)
5002 else
5003 ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
5004 } else if (ast_true(v->value)) {
5006 } else {
5008 }
5009 } else if (!strcasecmp(v->name, "entityid")) {
5010 if (!ast_str_to_eid(&testeid, v->value))
5011 global_eid = testeid;
5012 else
5013 ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
5014 } else if (!strcasecmp(v->name, "tos")) {
5015 if (ast_str2tos(v->value, &tos))
5016 ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
5017 } else if (!strcasecmp(v->name, "department")) {
5018 ast_copy_string(dept, v->value, sizeof(dept));
5019 } else if (!strcasecmp(v->name, "organization")) {
5020 ast_copy_string(org, v->value, sizeof(org));
5021 } else if (!strcasecmp(v->name, "locality")) {
5022 ast_copy_string(locality, v->value, sizeof(locality));
5023 } else if (!strcasecmp(v->name, "stateprov")) {
5025 } else if (!strcasecmp(v->name, "country")) {
5026 ast_copy_string(country, v->value, sizeof(country));
5027 } else if (!strcasecmp(v->name, "email")) {
5028 ast_copy_string(email, v->value, sizeof(email));
5029 } else if (!strcasecmp(v->name, "phone")) {
5030 ast_copy_string(phone, v->value, sizeof(phone));
5031 } else if (!strcasecmp(v->name, "storehistory")) {
5033 } else if (!strcasecmp(v->name, "outgoing_sip_tech")) {
5034 int outgoing_tech = str2tech(v->value);
5035 if (outgoing_tech != DUNDI_PROTO_SIP && outgoing_tech != DUNDI_PROTO_PJSIP) {
5036 ast_log(LOG_WARNING, "outgoing_sip_tech must be SIP or PJSIP\n");
5037 } else {
5038 outgoing_sip_tech = outgoing_tech;
5039 }
5040 } else if (!strcasecmp(v->name, "pjsip_outgoing_endpoint")) {
5042 } else if (!strcasecmp(v->name, "cachetime")) {
5043 if ((sscanf(v->value, "%30d", &x) == 1)) {
5044 dundi_cache_time = x;
5045 } else {
5046 ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
5048 }
5049 }
5050 v = v->next;
5051 }
5052
5053 if (port == 0) {
5054 port = DUNDI_PORT;
5055 }
5056
5057 if (ast_sockaddr_isnull(sin)) {
5058 sprintf(bind_addr, "0.0.0.0:%d", port);
5059 ast_sockaddr_parse(sin, bind_addr, 0);
5060 } else {
5061 ast_sockaddr_set_port(sin, port);
5062 }
5063
5064 if (last_port == 0) {
5065 last_port = port;
5066 } else if (last_port != port) {
5067 ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
5068 }
5069
5070 set_host_ipaddr(sin);
5071
5072 if (!ast_sockaddr_isnull(sin2)) {
5073 ast_sockaddr_set_port(sin2, port);
5074 }
5075
5077
5078 mark_mappings();
5079 v = ast_variable_browse(cfg, "mappings");
5080 while (v) {
5082 build_mapping(v->name, v->value);
5084 v = v->next;
5085 }
5087
5088 mark_peers();
5089 cat = ast_category_browse(cfg, NULL);
5090 while(cat) {
5091 if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
5092 /* Entries */
5093 if (!ast_str_to_eid(&testeid, cat))
5094 build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
5095 else if (!strcasecmp(cat, "*")) {
5096 build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
5098 } else
5099 ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
5100 }
5101 cat = ast_category_browse(cfg, cat);
5102 }
5103 prune_peers();
5104
5105 ast_config_destroy(cfg);
5106 load_password();
5107 if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
5109 return 0;
5110}
5111
5112static int unload_module(void)
5113{
5119
5120 /* Stop all currently running threads */
5121 dundi_shutdown = 1;
5123 pthread_kill(netthreadid, SIGURG);
5124 pthread_join(netthreadid, NULL);
5126 }
5128 pthread_kill(precachethreadid, SIGURG);
5129 pthread_join(precachethreadid, NULL);
5131 }
5133 pthread_cancel(clearcachethreadid);
5134 pthread_join(clearcachethreadid, NULL);
5136 }
5137
5138 if (netsocket >= 0) {
5139 close(netsocket);
5140 }
5141
5142 if (netsocket2 >= 0) {
5143 close(netsocket2);
5144 }
5145
5146 mark_mappings();
5148 mark_peers();
5149 prune_peers();
5150
5151 if (-1 < netsocket) {
5152 close(netsocket);
5153 netsocket = -1;
5154 }
5155 if (io) {
5157 io = NULL;
5158 }
5159
5160 if (sched) {
5162 sched = NULL;
5163 }
5164
5165 return 0;
5166}
5167
5168static int reload(void)
5169{
5170 struct ast_sockaddr sin;
5171 struct ast_sockaddr sin2;
5172
5174 ast_sockaddr_setnull(&sin2);
5175
5176 if (set_config("dundi.conf", &sin, 1, &sin2))
5178
5180}
5181
5182static int load_module(void)
5183{
5184 struct ast_sockaddr sin;
5185 struct ast_sockaddr sin2;
5186
5189
5190 /* Make a UDP socket */
5193
5194 if (!io || !sched) {
5195 goto declined;
5196 }
5197
5199 ast_sockaddr_setnull(&sin2);
5200
5201 if (set_config("dundi.conf", &sin, 0, &sin2)) {
5202 goto declined;
5203 }
5204
5205 if (!ast_sockaddr_isnull(&sin2)) {
5206 if ((ast_sockaddr_is_ipv4(&sin) == ast_sockaddr_is_ipv4(&sin2)) || (ast_sockaddr_is_ipv6(&sin) == ast_sockaddr_is_ipv6(&sin2))) {
5207 ast_log(LOG_ERROR, "bindaddr & bindaddr2 should be different IP protocols.\n");
5208 goto declined;
5209 }
5210
5211 /*bind netsocket to ipv4, netsocket2 to ipv6 */
5212
5213 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5214 netsocket2 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5215 if (netsocket < 0 || netsocket2 < 0) {
5216 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
5217 goto declined;
5218 }
5219 if (ast_sockaddr_is_ipv4(&sin)) {
5220 if (ast_bind(netsocket, &sin)) {
5221 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5222 ast_sockaddr_stringify(&sin), strerror(errno));
5223 goto declined;
5224 }
5225 if (ast_bind(netsocket2, &sin2)) {
5226 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5227 ast_sockaddr_stringify(&sin2), strerror(errno));
5228 goto declined;
5229 }
5230 } else {
5231 if (ast_bind(netsocket, &sin2)) {
5232 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5233 ast_sockaddr_stringify(&sin2), strerror(errno));
5234 goto declined;
5235 }
5236 if (ast_bind(netsocket2, &sin)) {
5237 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5238 ast_sockaddr_stringify(&sin), strerror(errno));
5239 goto declined;
5240 }
5241 }
5242 ast_set_qos(netsocket, tos, 0, "DUNDi");
5243 ast_set_qos(netsocket2, tos, 0, "DUNDi");
5244 } else {
5245 if (ast_sockaddr_is_ipv6(&sin)) {
5246 netsocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5247 } else {
5248 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5249 }
5250 if (netsocket < 0) {
5251 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
5252 goto declined;
5253 }
5254 if (ast_bind(netsocket, &sin)) {
5255 ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5256 ast_sockaddr_stringify(&sin), strerror(errno));
5257 goto declined;
5258 }
5259 ast_set_qos(netsocket, tos, 0, "DUNDi");
5260 }
5261
5262 if (start_network_thread()) {
5263 ast_log(LOG_ERROR, "Unable to start network thread\n");
5264 goto declined;
5265 }
5266
5269 ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
5273
5274 ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin));
5275 if (!ast_sockaddr_isnull(&sin2))
5276 ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin2));
5277
5279
5280declined:
5281 unload_module();
5283}
5284
5285AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
5286 .support_level = AST_MODULE_SUPPORT_EXTENDED,
5287 .load = load_module,
5288 .unload = unload_module,
5289 .reload = reload,
5290 .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:983
integer order
Definition: analys.c:66
static const struct adsi_event events[]
Definition: app_adsiprog.c:88
char weight
jack_status_t status
Definition: app_jack.c:149
const char * str
Definition: app_jack.c:150
struct sla_ringing_trunk * last
Definition: app_sla.c:338
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: db.c:335
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: db.c:421
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: db.c:472
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition: db.c:635
int ast_db_deltree(const char *family, const char *keytree)
Delete one or more entries in astdb.
Definition: db.c:559
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: db.c:695
char * strsep(char **str, const char *delims)
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 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:418
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:2414
#define ast_channel_lock(chan)
Definition: channel.h:2970
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
Waits for input on an fd.
Definition: channel.c:3008
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:2971
#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:2428
#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
@ DUNDI_CAUSE_GENERAL
Definition: dundi.h:121
@ DUNDI_CAUSE_DUPLICATE
Definition: dundi.h:127
@ DUNDI_CAUSE_NOAUTH
Definition: dundi.h:125
#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
#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
#define DUNDI_COMMAND_FINAL
Definition: dundi.h:159
@ 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_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_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_ANSWER
Definition: dundi.h:188
@ DUNDI_HINT_DONT_ASK
Definition: dundi.h:94
@ DUNDI_HINT_UNAFFECTED
Definition: dundi.h:96
@ DUNDI_HINT_TTL_EXPIRED
Definition: dundi.h:92
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:4201
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:3066
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:70
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:761
size_t current
Definition: main/cli.c:113
int errno
MD5 digest functions.
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:329
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ 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:4190
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:8506
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:1559
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:4205
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:8483
int ast_ignore_pattern(const char *context, const char *pattern)
Checks to see if a number should be ignored.
Definition: pbx.c:6894
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:4210
int ast_unlock_contexts(void)
Unlocks contexts.
Definition: pbx.c:8488
const char * ast_get_extension_name(struct ast_exten *exten)
Definition: pbx.c:8524
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
Definition: pbx.c:8501
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:879
static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc, int *lowexpiration)
Definition: pbx_dundi.c:1257
static int socket_read(int *id, int fd, short events, void *sock)
Definition: pbx_dundi.c:2098
static char * dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2319
static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
Definition: pbx_dundi.c:1388
static char country[80]
Definition: pbx_dundi.c:214
static int reset_transaction(struct dundi_transaction *trans)
Definition: pbx_dundi.c:514
#define MAX_RESULTS
Definition: pbx_dundi.c:162
static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
Definition: pbx_dundi.c:3218
static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
Definition: pbx_dundi.c:1310
static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:1102
static struct dundi_transaction * create_transaction(struct dundi_peer *p)
Definition: pbx_dundi.c:3152
static void load_password(void)
Definition: pbx_dundi.c:2153
static void check_password(void)
Definition: pbx_dundi.c:2192
#define DUNDI_MODEL_SYMMETRIC
Definition: pbx_dundi.c:170
static void reschedule_precache(const char *number, const char *context, int expiration)
Definition: pbx_dundi.c:3952
static struct ast_switch dundi_switch
Definition: pbx_dundi.c:4895
static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
Definition: pbx_dundi.c:1404
static dundi_eid empty_eid
Definition: pbx_dundi.c:223
static char * dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:3065
static int dundi_ttl
Definition: pbx_dundi.c:203
static void cancel_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3689
static char phone[80]
Definition: pbx_dundi.c:216
static unsigned int tos
Definition: pbx_dundi.c:200
static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
Definition: pbx_dundi.c:1185
int dundi_precache(const char *context, const char *number)
Pre-cache to push upstream peers.
Definition: pbx_dundi.c:4083
static void drds_destroy(struct dundi_result_datastore *drds)
Definition: pbx_dundi.c:4214
static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
Definition: pbx_dundi.c:1601
static int netsocket
Definition: pbx_dundi.c:195
static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
Definition: pbx_dundi.c:4011
static int dundidebug
Definition: pbx_dundi.c:201
static struct ast_sched_context * sched
Definition: pbx_dundi.c:194
static int dundi_key_ttl
Definition: pbx_dundi.c:204
static int optimize_transactions(struct dundi_request *dr, int order)
Definition: pbx_dundi.c:3599
static int netsocket2
Definition: pbx_dundi.c:196
static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
Definition: pbx_dundi.c:3323
static char * dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2345
static char * dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2636
static char * dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2865
static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:955
static void build_secret(char *secret, int seclen)
Definition: pbx_dundi.c:2125
#define DUNDI_FLAG_INTERNAL_NOPARTIAL
Definition: pbx_dundi.c:185
static char locality[80]
Definition: pbx_dundi.c:212
static struct ast_custom_function dundi_query_function
Definition: pbx_dundi.c:4289
static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
Definition: pbx_dundi.c:1443
static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
Definition: pbx_dundi.c:4152
static int do_register_expire(const void *data)
Definition: pbx_dundi.c:1336
static int global_autokilltimeout
Definition: pbx_dundi.c:206
static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
Definition: pbx_dundi.c:3657
static int get_trans_id(void)
Definition: pbx_dundi.c:495
static int has_permission(struct permissionlist *permlist, char *cont)
Definition: pbx_dundi.c:378
static void save_secret(const char *newkey, const char *oldkey)
Definition: pbx_dundi.c:2140
static int outgoing_sip_tech
Definition: pbx_dundi.c:220
static int check_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3810
static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
Definition: pbx_dundi.c:1507
static char cursecret[80]
Definition: pbx_dundi.c:218
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:4827
static char * dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2471
static void prune_peers(void)
Definition: pbx_dundi.c:4427
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:3941
static struct dundi_peer * find_peer(dundi_eid *eid)
Definition: pbx_dundi.c:530
#define DUNDI_MODEL_INBOUND
Definition: pbx_dundi.c:168
static void build_iv(unsigned char *iv)
Definition: pbx_dundi.c:548
static int query_transactions(struct dundi_request *dr)
Definition: pbx_dundi.c:3586
static void dundi_debug_output(const char *data)
Definition: pbx_dundi.c:367
static unsigned long avoid_crc32(dundi_eid *avoid[])
Definition: pbx_dundi.c:3824
static void mark_mappings(void)
Definition: pbx_dundi.c:4378
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:4089
static void * dundi_query_thread(void *data)
Definition: pbx_dundi.c:769
static int global_storehistory
Definition: pbx_dundi.c:209
static time_t rotatetime
Definition: pbx_dundi.c:222
static int set_config(char *config_file, struct ast_sockaddr *sin, int reload, struct ast_sockaddr *sin2)
Definition: pbx_dundi.c:4940
static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
Definition: pbx_dundi.c:914
static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
Definition: pbx_dundi.c:3406
static char * dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2842
static int dundi_ack(struct dundi_transaction *trans, int final)
Definition: pbx_dundi.c:466
static int dundi_cache_time
Definition: pbx_dundi.c:205
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:1421
static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
Definition: pbx_dundi.c:4603
static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: pbx_dundi.c:4294
static void destroy_packets(struct packetlist *p)
Definition: pbx_dundi.c:2025
static void sort_results(struct dundi_result *results, int count)
Definition: pbx_dundi.c:2466
static void append_permission(struct permissionlist *permlist, const char *s, int allow)
Definition: pbx_dundi.c:4457
static struct ast_custom_function dundi_result_function
Definition: pbx_dundi.c:4363
static const struct ast_app_option dundi_query_opts[128]
Definition: pbx_dundi.c:4150
static void dundi_error_output(const char *data)
Definition: pbx_dundi.c:373
static unsigned int dundi_result_id
Definition: pbx_dundi.c:4206
static int register_request(struct dundi_request *dr, struct dundi_request **pending)
Definition: pbx_dundi.c:3773
static int dundi_discover(struct dundi_transaction *trans)
Definition: pbx_dundi.c:3428
static int get_ipaddress(char *ip, size_t size, const char *str, int family)
Definition: pbx_dundi.c:4904
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:3839
static const struct ast_datastore_info dundi_result_datastore_info
Definition: pbx_dundi.c:4225
static pthread_t precachethreadid
Definition: pbx_dundi.c:198
#define MAX_PACKET_SIZE
Definition: pbx_dundi.c:164
static char * dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2527
static int ack_trans(struct dundi_transaction *trans, int iseqno)
Definition: pbx_dundi.c:2036
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:4798
@ OPT_BYPASS_CACHE
Definition: pbx_dundi.c:4145
static void set_host_ipaddr(struct ast_sockaddr *sin)
Definition: pbx_dundi.c:4918
static char * dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2970
static void destroy_packet(struct dundi_packet *pack, int needfree)
Definition: pbx_dundi.c:3209
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:4817
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:3715
static int authdebug
Definition: pbx_dundi.c:202
static int rescomp(const void *a, const void *b)
Definition: pbx_dundi.c:2454
#define MAX_WEIGHT
Definition: pbx_dundi.c:166
static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: pbx_dundi.c:4230
static void mark_peers(void)
Definition: pbx_dundi.c:4368
static uint16_t dundi_sockaddr_port(const struct ast_sockaddr *addr)
Definition: pbx_dundi.c:2624
static void destroy_permissions(struct permissionlist *permlist)
Definition: pbx_dundi.c:4389
static struct ast_cli_entry cli_dundi[]
Definition: pbx_dundi.c:3134
static char * tech2str(int tech)
Definition: pbx_dundi.c:391
static void prune_mappings(void)
Definition: pbx_dundi.c:4442
static char * dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2565
static void qualify_peer(struct dundi_peer *peer, int schedonly)
Definition: pbx_dundi.c:4580
#define DUNDI_MODEL_OUTBOUND
Definition: pbx_dundi.c:169
static int dundi_shutdown
Definition: pbx_dundi.c:224
static int dundi_query(struct dundi_transaction *trans)
Definition: pbx_dundi.c:3518
static pthread_t netthreadid
Definition: pbx_dundi.c:197
static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
Definition: pbx_dundi.c:3551
static char email[80]
Definition: pbx_dundi.c:215
static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
Definition: pbx_dundi.c:3452
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:593
static int default_expiration
Definition: pbx_dundi.c:208
static int get_mapping_weight(struct dundi_mapping *map, struct varshead *headp)
Definition: pbx_dundi.c:574
static int do_autokill(const void *data)
Definition: pbx_dundi.c:3395
static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
Definition: pbx_dundi.c:1560
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:4136
static void * network_thread(void *ignore)
Definition: pbx_dundi.c:2209
static char * dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2811
static struct io_context * io
Definition: pbx_dundi.c:193
static char ipaddr[80]
Definition: pbx_dundi.c:219
static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
Definition: pbx_dundi.c:4643
static void unregister_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3803
#define MAX_OPTS
Definition: pbx_dundi.c:4470
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:4822
static int update_key(struct dundi_peer *peer)
Definition: pbx_dundi.c:1349
#define DUNDI_SECRET_TIME
Definition: pbx_dundi.c:190
static int load_module(void)
Definition: pbx_dundi.c:5182
static void build_mapping(const char *name, const char *value)
Definition: pbx_dundi.c:4472
static void abort_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3703
static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:824
static char * dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2898
static char * complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
Definition: pbx_dundi.c:2432
static char stateprov[80]
Definition: pbx_dundi.c:213
#define FORMAT
static struct dundi_peer * any_peer
Wildcard peer.
Definition: pbx_dundi.c:363
static char * dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2712
static int str2tech(const char *str)
Definition: pbx_dundi.c:409
static int dundi_xmit(struct dundi_packet *pack)
Definition: pbx_dundi.c:3184
#define FORMAT2
static int unload_module(void)
Definition: pbx_dundi.c:5112
static int reload(void)
Definition: pbx_dundi.c:5168
static void * process_precache(void *ign)
Definition: pbx_dundi.c:2276
static int dundi_rexmit(const void *data)
Definition: pbx_dundi.c:3300
static void dundi_reject(struct dundi_hdr *h, struct ast_sockaddr *sin)
Definition: pbx_dundi.c:470
static char org[80]
Definition: pbx_dundi.c:211
static void * dundi_lookup_thread(void *data)
Definition: pbx_dundi.c:672
static char * dundi_sockaddr_stringify_host(const struct ast_sockaddr *addr)
Definition: pbx_dundi.c:2616
static char pjsip_outgoing_endpoint[80]
Definition: pbx_dundi.c:221
static char * dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2372
@ FLAG_ISQUAL
Definition: pbx_dundi.c:179
@ FLAG_DEAD
Definition: pbx_dundi.c:177
@ FLAG_SENDFULLKEY
Definition: pbx_dundi.c:181
@ FLAG_FINAL
Definition: pbx_dundi.c:178
@ FLAG_ISREG
Definition: pbx_dundi.c:176
@ FLAG_ENCRYPT
Definition: pbx_dundi.c:180
@ FLAG_STOREHIST
Definition: pbx_dundi.c:182
static char dept[80]
Definition: pbx_dundi.c:210
static struct dundi_transaction * find_transaction(struct dundi_hdr *hdr, struct ast_sockaddr *sin)
Definition: pbx_dundi.c:427
static char * model2str(int model)
Definition: pbx_dundi.c:2418
static void destroy_map(struct dundi_mapping *map)
Definition: pbx_dundi.c:4421
static int do_qualify(const void *data)
Definition: pbx_dundi.c:4572
#define DUNDI_TIMING_HISTORY
Definition: pbx_dundi.c:173
static void destroy_peer(struct dundi_peer *peer)
Definition: pbx_dundi.c:4397
static void * process_clearcache(void *ignore)
Definition: pbx_dundi.c:2243
static int start_network_thread(void)
Definition: pbx_dundi.c:2311
static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datalen)
Definition: pbx_dundi.c:2057
static int do_register(const void *data)
Definition: pbx_dundi.c:4546
static void dundi_precache_full(void)
Definition: pbx_dundi.c:3987
static void drds_destroy_cb(void *data)
Definition: pbx_dundi.c:4219
static char * dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2932
static pthread_t clearcachethreadid
Definition: pbx_dundi.c:199
static void * dundi_precache_thread(void *data)
Definition: pbx_dundi.c:736
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:4890
static int discover_transactions(struct dundi_request *dr)
Definition: pbx_dundi.c:3540
static dundi_eid global_eid
Definition: pbx_dundi.c:207
static char secretpath[80]
Definition: pbx_dundi.c:217
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:299
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:252
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:163
struct ast_var_t::@213 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:243
char exten[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:244
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:304
char * weightstr
Definition: pbx_dundi.c:306
char dest[512]
Definition: pbx_dundi.c:310
struct dundi_mapping::@420 list
char dcontext[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:303
struct dundi_mapping * next
Definition: pbx_dundi.c:311
struct dundi_hdr * h
Definition: pbx_dundi.c:234
struct dundi_packet::@414 list
unsigned char data[0]
Definition: pbx_dundi.c:239
struct dundi_transaction * parent
Definition: pbx_dundi.c:236
int lookuptimes[DUNDI_TIMING_HISTORY]
Definition: pbx_dundi.c:337
unsigned char txenckey[256]
Definition: pbx_dundi.c:327
char outkey[80]
Definition: pbx_dundi.c:321
struct ast_sockaddr addr
Definition: pbx_dundi.c:316
char * lookups[DUNDI_TIMING_HISTORY]
Definition: pbx_dundi.c:338
uint32_t them_keycrc32
Definition: pbx_dundi.c:332
dundi_eid us_eid
Definition: pbx_dundi.c:319
struct dundi_peer::@421 list
unsigned int dynamic
Definition: pbx_dundi.c:345
unsigned char rxenckey[256]
Definition: pbx_dundi.c:328
uint32_t us_keycrc32
Definition: pbx_dundi.c:329
struct timeval qualtx
Definition: pbx_dundi.c:348
ast_aes_encrypt_key us_ecx
Definition: pbx_dundi.c:330
struct dundi_peer::permissionlist permit
int lastms
Definition: pbx_dundi.c:346
int qualifyid
Definition: pbx_dundi.c:324
struct dundi_transaction * regtrans
Definition: pbx_dundi.c:340
time_t keyexpire
Definition: pbx_dundi.c:335
ast_aes_decrypt_key them_dcx
Definition: pbx_dundi.c:334
char inkey[80]
Definition: pbx_dundi.c:320
int sentfullkey
Definition: pbx_dundi.c:325
struct dundi_transaction * qualtrans
Definition: pbx_dundi.c:341
int registerid
Definition: pbx_dundi.c:323
ast_aes_encrypt_key them_ecx
Definition: pbx_dundi.c:333
int pcmodel
Definition: pbx_dundi.c:343
struct permissionlist include
Definition: pbx_dundi.c:318
int registerexpire
Definition: pbx_dundi.c:336
ast_aes_decrypt_key us_dcx
Definition: pbx_dundi.c:331
dundi_eid eid
Definition: pbx_dundi.c:315
struct dundi_precache_queue::@415 list
char called_context[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:562
struct dundi_mapping * maps
Definition: pbx_dundi.c:564
int directs[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:560
dundi_eid * eids[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:559
char called_number[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:563
dundi_eid reqeid
Definition: pbx_dundi.c:561
struct dundi_transaction * trans
Definition: pbx_dundi.c:567
int pfds[2]
Definition: pbx_dundi.c:296
struct dundi_entity_info * dei
Definition: pbx_dundi.c:290
struct dundi_request::@418 trans
uint32_t crc32
Definition: pbx_dundi.c:297
struct dundi_hint_metadata * hmd
Definition: pbx_dundi.c:291
struct dundi_result * dr
Definition: pbx_dundi.c:289
struct dundi_request::@419 list
dundi_eid query_eid
Definition: pbx_dundi.c:287
char number[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:286
dundi_eid root_eid
Definition: pbx_dundi.c:288
char dcontext[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:285
struct dundi_result results[MAX_RESULTS]
Definition: pbx_dundi.c:4209
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:271
struct ast_sockaddr addr
Definition: pbx_dundi.c:257
unsigned char oiseqno
Definition: pbx_dundi.c:274
dundi_eid us_eid
Definition: pbx_dundi.c:261
dundi_eid them_eid
Definition: pbx_dundi.c:262
struct dundi_transaction::packetlist packets
unsigned char iseqno
Definition: pbx_dundi.c:273
struct packetlist lasttrans
Definition: pbx_dundi.c:278
dundi_eid eids[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:259
unsigned short dtrans
Definition: pbx_dundi.c:272
struct dundi_transaction::@417 all
unsigned char oseqno
Definition: pbx_dundi.c:275
unsigned char aseqno
Definition: pbx_dundi.c:276
ast_aes_decrypt_key dcx
Definition: pbx_dundi.c:264
unsigned int flags
Definition: pbx_dundi.c:265
struct dundi_transaction::@416 parentlist
struct dundi_request * parent
Definition: pbx_dundi.c:279
struct timeval start
Definition: pbx_dundi.c:258
ast_aes_encrypt_key ecx
Definition: pbx_dundi.c:263
Global IO variables are now in a struct in order to be made threadsafe.
Definition: io.c:71
Number structure.
Definition: app_followme.c:157
Definition: pbx_dundi.c:353
struct permission::@413 list
char name[0]
Definition: pbx_dundi.c:229
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