2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief Distributed Universal Number Discovery (DUNDi)
30 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <sys/socket.h>
40 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
41 #include <sys/types.h>
42 #include <netinet/in_systm.h>
44 #include <netinet/ip.h>
45 #include <sys/ioctl.h>
46 #include <netinet/in.h>
48 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
49 #include <net/if_dl.h>
53 #include <sys/signal.h>
56 #include "asterisk/file.h"
57 #include "asterisk/logger.h"
58 #include "asterisk/channel.h"
59 #include "asterisk/config.h"
60 #include "asterisk/options.h"
61 #include "asterisk/pbx.h"
62 #include "asterisk/module.h"
63 #include "asterisk/frame.h"
64 #include "asterisk/file.h"
65 #include "asterisk/cli.h"
66 #include "asterisk/lock.h"
67 #include "asterisk/md5.h"
68 #include "asterisk/dundi.h"
69 #include "asterisk/sched.h"
70 #include "asterisk/io.h"
71 #include "asterisk/utils.h"
72 #include "asterisk/crypto.h"
73 #include "asterisk/astdb.h"
74 #include "asterisk/acl.h"
75 #include "asterisk/aes.h"
77 #include "dundi-parser.h"
79 #define MAX_RESULTS 64
81 #define MAX_PACKET_SIZE 8192
83 #define DUNDI_MODEL_INBOUND (1 << 0)
84 #define DUNDI_MODEL_OUTBOUND (1 << 1)
85 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
87 /*! Keep times of last 10 lookups */
88 #define DUNDI_TIMING_HISTORY 10
90 #define FLAG_ISREG (1 << 0) /*!< Transaction is register request */
91 #define FLAG_DEAD (1 << 1) /*!< Transaction is dead */
92 #define FLAG_FINAL (1 << 2) /*!< Transaction has final message sent */
93 #define FLAG_ISQUAL (1 << 3) /*!< Transaction is a qualification */
94 #define FLAG_ENCRYPT (1 << 4) /*!< Transaction is encrypted wiht ECX/DCX */
95 #define FLAG_SENDFULLKEY (1 << 5) /*!< Send full key on transaction */
96 #define FLAG_STOREHIST (1 << 6) /*!< Record historic performance */
98 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
101 #define DUNDI_SECRET_TIME 15 /* Testing only */
103 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
109 static struct io_context
*io
;
110 static struct sched_context
*sched
;
111 static int netsocket
= -1;
112 static pthread_t netthreadid
= AST_PTHREADT_NULL
;
113 static pthread_t precachethreadid
= AST_PTHREADT_NULL
;
115 static int dundidebug
= 0;
116 static int authdebug
= 0;
117 static int dundi_ttl
= DUNDI_DEFAULT_TTL
;
118 static int dundi_key_ttl
= DUNDI_DEFAULT_KEY_EXPIRE
;
119 static int dundi_cache_time
= DUNDI_DEFAULT_CACHE_TIME
;
120 static int global_autokilltimeout
= 0;
121 static dundi_eid global_eid
;
122 static int default_expiration
= 60;
123 static int global_storehistory
= 0;
124 static char dept
[80];
126 static char locality
[80];
127 static char stateprov
[80];
128 static char country
[80];
129 static char email
[80];
130 static char phone
[80];
131 static char secretpath
[80];
132 static char cursecret
[80];
133 static char ipaddr
[80];
134 static time_t rotatetime
;
135 static dundi_eid empty_eid
= { { 0, 0, 0, 0, 0, 0 } };
136 static int dundi_shutdown
= 0;
139 AST_LIST_ENTRY(permission
) list
;
144 struct dundi_packet
{
145 AST_LIST_ENTRY(dundi_packet
) list
;
148 struct dundi_transaction
*parent
;
151 unsigned char data
[0];
154 struct dundi_hint_metadata
{
155 unsigned short flags
;
156 char exten
[AST_MAX_EXTENSION
];
159 struct dundi_precache_queue
{
160 AST_LIST_ENTRY(dundi_precache_queue
) list
;
166 struct dundi_request
;
168 struct dundi_transaction
{
169 struct sockaddr_in addr
; /*!< Other end of transaction */
170 struct timeval start
; /*!< When this transaction was created */
171 dundi_eid eids
[DUNDI_MAX_STACK
+ 1];
172 int eidcount
; /*!< Number of eids in eids */
173 dundi_eid us_eid
; /*!< Our EID, to them */
174 dundi_eid them_eid
; /*!< Their EID, to us */
175 aes_encrypt_ctx ecx
; /*!< AES 128 Encryption context */
176 aes_decrypt_ctx dcx
; /*!< AES 128 Decryption context */
177 unsigned int flags
; /*!< Has final packet been sent */
178 int ttl
; /*!< Remaining TTL for queries on this one */
179 int thread
; /*!< We have a calling thread */
180 int retranstimer
; /*!< How long to wait before retransmissions */
181 int autokillid
; /*!< ID to kill connection if answer doesn't come back fast enough */
182 int autokilltimeout
; /*!< Recommended timeout for autokill */
183 unsigned short strans
; /*!< Our transaction identifier */
184 unsigned short dtrans
; /*!< Their transaction identifer */
185 unsigned char iseqno
; /*!< Next expected received seqno */
186 unsigned char oiseqno
; /*!< Last received incoming seqno */
187 unsigned char oseqno
; /*!< Next transmitted seqno */
188 unsigned char aseqno
; /*!< Last acknowledge seqno */
189 AST_LIST_HEAD_NOLOCK(packetlist
, dundi_packet
) packets
; /*!< Packets to be retransmitted */
190 struct packetlist lasttrans
; /*!< Last transmitted / ACK'd packet */
191 struct dundi_request
*parent
; /*!< Parent request (if there is one) */
192 AST_LIST_ENTRY(dundi_transaction
) parentlist
; /*!< Next with respect to the parent */
193 AST_LIST_ENTRY(dundi_transaction
) all
; /*!< Next with respect to all DUNDi transactions */
196 struct dundi_request
{
197 char dcontext
[AST_MAX_EXTENSION
];
198 char number
[AST_MAX_EXTENSION
];
201 struct dundi_result
*dr
;
202 struct dundi_entity_info
*dei
;
203 struct dundi_hint_metadata
*hmd
;
209 unsigned long crc32
; /*!< CRC-32 of all but root EID's in avoid list */
210 AST_LIST_HEAD_NOLOCK(, dundi_transaction
) trans
; /*!< Transactions */
211 AST_LIST_ENTRY(dundi_request
) list
;
214 struct dundi_mapping
{
215 char dcontext
[AST_MAX_EXTENSION
];
216 char lcontext
[AST_MAX_EXTENSION
];
221 char dest
[AST_MAX_EXTENSION
];
222 AST_LIST_ENTRY(dundi_mapping
) list
;
227 struct sockaddr_in addr
; /*!< Address of DUNDi peer */
228 AST_LIST_HEAD_NOLOCK(permissionlist
, permission
) permit
;
229 struct permissionlist include
;
238 unsigned char txenckey
[256]; /*!< Transmitted encrypted key + sig */
239 unsigned char rxenckey
[256]; /*!< Cache received encrypted key + sig */
240 unsigned long us_keycrc32
; /*!< CRC-32 of our key */
241 aes_encrypt_ctx us_ecx
; /*!< Cached AES 128 Encryption context */
242 aes_decrypt_ctx us_dcx
; /*!< Cached AES 128 Decryption context */
243 unsigned long them_keycrc32
; /*!< CRC-32 of our key */
244 aes_encrypt_ctx them_ecx
; /*!< Cached AES 128 Encryption context */
245 aes_decrypt_ctx them_dcx
; /*!< Cached AES 128 Decryption context */
246 time_t keyexpire
; /*!< When to expire/recreate key */
248 int lookuptimes
[DUNDI_TIMING_HISTORY
];
249 char *lookups
[DUNDI_TIMING_HISTORY
];
251 struct dundi_transaction
*regtrans
; /*!< Registration transaction */
252 struct dundi_transaction
*qualtrans
; /*!< Qualify transaction */
253 int model
; /*!< Pull model */
254 int pcmodel
; /*!< Push/precache model */
255 /*! Dynamic peers register with us */
256 unsigned int dynamic
:1;
257 int lastms
; /*!< Last measured latency */
258 int maxms
; /*!< Max permissible latency */
259 struct timeval qualtx
; /*!< Time of transmit */
260 AST_LIST_ENTRY(dundi_peer
) list
;
263 static AST_LIST_HEAD_STATIC(peers
, dundi_peer
);
264 static AST_LIST_HEAD_STATIC(pcq
, dundi_precache_queue
);
265 static AST_LIST_HEAD_NOLOCK_STATIC(mappings
, dundi_mapping
);
266 static AST_LIST_HEAD_NOLOCK_STATIC(requests
, dundi_request
);
267 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans
, dundi_transaction
);
270 * \brief Wildcard peer
272 * This peer is created if the [*] entry is specified in dundi.conf
274 static struct dundi_peer
*any_peer
;
276 static int dundi_xmit(struct dundi_packet
*pack
);
278 static void dundi_debug_output(const char *data
)
281 ast_verbose("%s", data
);
284 static void dundi_error_output(const char *data
)
286 ast_log(LOG_WARNING
, "%s", data
);
289 static int has_permission(struct permissionlist
*permlist
, char *cont
)
291 struct permission
*perm
;
294 AST_LIST_TRAVERSE(permlist
, perm
, list
) {
295 if (!strcasecmp(perm
->name
, "all") || !strcasecmp(perm
->name
, cont
))
302 static char *tech2str(int tech
)
305 case DUNDI_PROTO_NONE
:
307 case DUNDI_PROTO_IAX
:
309 case DUNDI_PROTO_SIP
:
311 case DUNDI_PROTO_H323
:
318 static int str2tech(char *str
)
320 if (!strcasecmp(str
, "IAX") || !strcasecmp(str
, "IAX2"))
321 return DUNDI_PROTO_IAX
;
322 else if (!strcasecmp(str
, "SIP"))
323 return DUNDI_PROTO_SIP
;
324 else if (!strcasecmp(str
, "H323"))
325 return DUNDI_PROTO_H323
;
330 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
[]);
331 static int dundi_precache_internal(const char *context
, const char *number
, int ttl
, dundi_eid
*avoids
[]);
332 static struct dundi_transaction
*create_transaction(struct dundi_peer
*p
);
333 static struct dundi_transaction
*find_transaction(struct dundi_hdr
*hdr
, struct sockaddr_in
*sin
)
335 struct dundi_transaction
*trans
;
337 /* Look for an exact match first */
338 AST_LIST_TRAVERSE(&alltrans
, trans
, all
) {
339 if (!inaddrcmp(&trans
->addr
, sin
) &&
340 ((trans
->strans
== (ntohs(hdr
->dtrans
) & 32767)) /* Matches our destination */ ||
341 ((trans
->dtrans
== (ntohs(hdr
->strans
) & 32767)) && (!hdr
->dtrans
))) /* We match their destination */) {
343 trans
->dtrans
= ntohs(hdr
->strans
) & 32767;
348 switch(hdr
->cmdresp
& 0x7f) {
349 case DUNDI_COMMAND_DPDISCOVER
:
350 case DUNDI_COMMAND_EIDQUERY
:
351 case DUNDI_COMMAND_PRECACHERQ
:
352 case DUNDI_COMMAND_REGREQ
:
353 case DUNDI_COMMAND_NULL
:
354 case DUNDI_COMMAND_ENCRYPT
:
356 /* Create new transaction */
357 trans
= create_transaction(NULL
);
359 memcpy(&trans
->addr
, sin
, sizeof(trans
->addr
));
360 trans
->dtrans
= ntohs(hdr
->strans
) & 32767;
362 ast_log(LOG_WARNING
, "Out of memory!\n");
372 static int dundi_send(struct dundi_transaction
*trans
, int cmdresp
, int flags
, int final
, struct dundi_ie_data
*ied
);
374 static int dundi_ack(struct dundi_transaction
*trans
, int final
)
376 return dundi_send(trans
, DUNDI_COMMAND_ACK
, 0, final
, NULL
);
378 static void dundi_reject(struct dundi_hdr
*h
, struct sockaddr_in
*sin
)
381 struct dundi_packet pack
;
382 struct dundi_hdr hdr
;
384 struct dundi_transaction trans
;
385 /* Never respond to an INVALID with another INVALID */
386 if (h
->cmdresp
== DUNDI_COMMAND_INVALID
)
388 memset(&tmp
, 0, sizeof(tmp
));
389 memset(&trans
, 0, sizeof(trans
));
390 memcpy(&trans
.addr
, sin
, sizeof(trans
.addr
));
391 tmp
.hdr
.strans
= h
->dtrans
;
392 tmp
.hdr
.dtrans
= h
->strans
;
393 tmp
.hdr
.iseqno
= h
->oseqno
;
394 tmp
.hdr
.oseqno
= h
->iseqno
;
395 tmp
.hdr
.cmdresp
= DUNDI_COMMAND_INVALID
;
396 tmp
.hdr
.cmdflags
= 0;
397 tmp
.pack
.h
= (struct dundi_hdr
*)tmp
.pack
.data
;
398 tmp
.pack
.datalen
= sizeof(struct dundi_hdr
);
399 tmp
.pack
.parent
= &trans
;
400 dundi_xmit(&tmp
.pack
);
403 static void reset_global_eid(void)
405 #if defined(SIOCGIFHWADDR)
410 s
= socket(AF_INET
, SOCK_STREAM
, 0);
414 memset(&ifr
, 0, sizeof(ifr
));
415 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "eth%d", x
);
416 if (!ioctl(s
, SIOCGIFHWADDR
, &ifr
)) {
417 memcpy(&global_eid
, ((unsigned char *)&ifr
.ifr_hwaddr
) + 2, sizeof(global_eid
));
418 ast_log(LOG_DEBUG
, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &global_eid
), ifr
.ifr_name
);
426 #if defined(ifa_broadaddr) && !defined(SOLARIS)
428 struct ifaddrs
*ifap
;
430 if (getifaddrs(&ifap
) == 0) {
432 for (p
= ifap
; p
; p
= p
->ifa_next
) {
433 if ((p
->ifa_addr
->sa_family
== AF_LINK
) && !(p
->ifa_flags
& IFF_LOOPBACK
) && (p
->ifa_flags
& IFF_RUNNING
)) {
434 struct sockaddr_dl
* sdp
= (struct sockaddr_dl
*) p
->ifa_addr
;
435 memcpy(&(global_eid
.eid
), sdp
->sdl_data
+ sdp
->sdl_nlen
, 6);
436 ast_log(LOG_DEBUG
, "Seeding global EID '%s' from '%s' using 'getifaddrs'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &global_eid
), p
->ifa_name
);
445 ast_log(LOG_NOTICE
, "No ethernet interface found for seeding global EID. You will have to set it manually.\n");
448 static int get_trans_id(void)
450 struct dundi_transaction
*t
;
451 int stid
= (ast_random() % 32766) + 1;
455 AST_LIST_TRAVERSE(&alltrans
, t
, all
) {
456 if (t
->strans
== tid
)
461 tid
= (tid
% 32766) + 1;
462 } while (tid
!= stid
);
467 static int reset_transaction(struct dundi_transaction
*trans
)
470 tid
= get_trans_id();
479 ast_clear_flag(trans
, FLAG_FINAL
);
483 static struct dundi_peer
*find_peer(dundi_eid
*eid
)
485 struct dundi_peer
*cur
= NULL
;
490 AST_LIST_TRAVERSE(&peers
, cur
, list
) {
491 if (!dundi_eid_cmp(&cur
->eid
,eid
))
495 if (!cur
&& any_peer
)
501 static void build_iv(unsigned char *iv
)
503 /* XXX Would be nice to be more random XXX */
504 unsigned int *fluffy
;
506 fluffy
= (unsigned int *)(iv
);
508 fluffy
[x
] = ast_random();
511 struct dundi_query_state
{
512 dundi_eid
*eids
[DUNDI_MAX_STACK
+ 1];
513 int directs
[DUNDI_MAX_STACK
+ 1];
515 char called_context
[AST_MAX_EXTENSION
];
516 char called_number
[AST_MAX_EXTENSION
];
517 struct dundi_mapping
*maps
;
520 struct dundi_transaction
*trans
;
527 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
)
529 struct ast_flags flags
= {0};
531 if (!ast_strlen_zero(map
->lcontext
)) {
532 if (ast_exists_extension(NULL
, map
->lcontext
, called_number
, 1, NULL
))
533 ast_set_flag(&flags
, DUNDI_FLAG_EXISTS
);
534 if (ast_canmatch_extension(NULL
, map
->lcontext
, called_number
, 1, NULL
))
535 ast_set_flag(&flags
, DUNDI_FLAG_CANMATCH
);
536 if (ast_matchmore_extension(NULL
, map
->lcontext
, called_number
, 1, NULL
))
537 ast_set_flag(&flags
, DUNDI_FLAG_MATCHMORE
);
538 if (ast_ignore_pattern(map
->lcontext
, called_number
))
539 ast_set_flag(&flags
, DUNDI_FLAG_IGNOREPAT
);
541 /* Clearly we can't say 'don't ask' anymore if we found anything... */
542 if (ast_test_flag(&flags
, AST_FLAGS_ALL
))
543 ast_clear_flag_nonstd(hmd
, DUNDI_HINT_DONT_ASK
);
545 if (map
->options
& DUNDI_FLAG_INTERNAL_NOPARTIAL
) {
546 /* Skip partial answers */
547 ast_clear_flag(&flags
, DUNDI_FLAG_MATCHMORE
|DUNDI_FLAG_CANMATCH
);
549 if (ast_test_flag(&flags
, AST_FLAGS_ALL
)) {
550 struct varshead headp
;
551 struct ast_var_t
*newvariable
;
552 ast_set_flag(&flags
, map
->options
& 0xffff);
553 ast_copy_flags(dr
+ anscnt
, &flags
, AST_FLAGS_ALL
);
554 dr
[anscnt
].techint
= map
->tech
;
555 dr
[anscnt
].weight
= map
->weight
;
556 dr
[anscnt
].expiration
= dundi_cache_time
;
557 ast_copy_string(dr
[anscnt
].tech
, tech2str(map
->tech
), sizeof(dr
[anscnt
].tech
));
558 dr
[anscnt
].eid
= *us_eid
;
559 dundi_eid_to_str(dr
[anscnt
].eid_str
, sizeof(dr
[anscnt
].eid_str
), &dr
[anscnt
].eid
);
560 if (ast_test_flag(&flags
, DUNDI_FLAG_EXISTS
)) {
561 AST_LIST_HEAD_INIT_NOLOCK(&headp
);
562 newvariable
= ast_var_assign("NUMBER", called_number
);
563 AST_LIST_INSERT_HEAD(&headp
, newvariable
, entries
);
564 newvariable
= ast_var_assign("EID", dr
[anscnt
].eid_str
);
565 AST_LIST_INSERT_HEAD(&headp
, newvariable
, entries
);
566 newvariable
= ast_var_assign("SECRET", cursecret
);
567 AST_LIST_INSERT_HEAD(&headp
, newvariable
, entries
);
568 newvariable
= ast_var_assign("IPADDR", ipaddr
);
569 AST_LIST_INSERT_HEAD(&headp
, newvariable
, entries
);
570 pbx_substitute_variables_varshead(&headp
, map
->dest
, dr
[anscnt
].dest
, sizeof(dr
[anscnt
].dest
));
571 while ((newvariable
= AST_LIST_REMOVE_HEAD(&headp
, entries
)))
572 ast_var_delete(newvariable
);
574 dr
[anscnt
].dest
[0] = '\0';
577 /* No answers... Find the fewest number of digits from the
578 number for which we have no answer. */
579 char tmp
[AST_MAX_EXTENSION
+ 1] = "";
580 for (x
= 0; x
< (sizeof(tmp
) - 1); x
++) {
581 tmp
[x
] = called_number
[x
];
584 if (!ast_canmatch_extension(NULL
, map
->lcontext
, tmp
, 1, NULL
)) {
585 /* Oops found something we can't match. If this is longer
586 than the running hint, we have to consider it */
587 if (strlen(tmp
) > strlen(hmd
->exten
)) {
588 ast_copy_string(hmd
->exten
, tmp
, sizeof(hmd
->exten
));
598 static void destroy_trans(struct dundi_transaction
*trans
, int fromtimeout
);
600 static void *dundi_lookup_thread(void *data
)
602 struct dundi_query_state
*st
= data
;
603 struct dundi_result dr
[MAX_RESULTS
];
604 struct dundi_ie_data ied
;
605 struct dundi_hint_metadata hmd
;
610 int expiration
= dundi_cache_time
;
612 ast_log(LOG_DEBUG
, "Whee, looking up '%s@%s' for '%s'\n", st
->called_number
, st
->called_context
,
613 st
->eids
[0] ? dundi_eid_to_str(eid_str
, sizeof(eid_str
), st
->eids
[0]) : "ourselves");
614 memset(&ied
, 0, sizeof(ied
));
615 memset(&dr
, 0, sizeof(dr
));
616 memset(&hmd
, 0, sizeof(hmd
));
617 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
618 hmd
.flags
= DUNDI_HINT_DONT_ASK
| DUNDI_HINT_UNAFFECTED
;
619 for (x
=0;x
<st
->nummaps
;x
++)
620 ouranswers
= dundi_lookup_local(dr
, st
->maps
+ x
, st
->called_number
, &st
->trans
->us_eid
, ouranswers
, &hmd
);
623 for (x
=0;x
<ouranswers
;x
++) {
624 if (dr
[x
].weight
< max
)
629 /* If we do not have a canonical result, keep looking */
630 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
);
632 /* Append answer in result */
635 if ((res
< -1) && (!ouranswers
))
636 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_DUPLICATE
, "Duplicate Request Pending");
639 AST_LIST_LOCK(&peers
);
640 /* Truncate if "don't ask" isn't present */
641 if (!ast_test_flag_nonstd(&hmd
, DUNDI_HINT_DONT_ASK
))
643 if (ast_test_flag(st
->trans
, FLAG_DEAD
)) {
644 ast_log(LOG_DEBUG
, "Our transaction went away!\n");
645 st
->trans
->thread
= 0;
646 destroy_trans(st
->trans
, 0);
648 for (x
=0;x
<ouranswers
;x
++) {
650 if (dr
[x
].expiration
&& (expiration
> dr
[x
].expiration
))
651 expiration
= dr
[x
].expiration
;
652 dundi_ie_append_answer(&ied
, DUNDI_IE_ANSWER
, &dr
[x
].eid
, dr
[x
].techint
, dr
[x
].flags
, dr
[x
].weight
, dr
[x
].dest
);
654 dundi_ie_append_hint(&ied
, DUNDI_IE_HINT
, hmd
.flags
, hmd
.exten
);
655 dundi_ie_append_short(&ied
, DUNDI_IE_EXPIRATION
, expiration
);
656 dundi_send(st
->trans
, DUNDI_COMMAND_DPRESPONSE
, 0, 1, &ied
);
657 st
->trans
->thread
= 0;
659 AST_LIST_UNLOCK(&peers
);
664 static void *dundi_precache_thread(void *data
)
666 struct dundi_query_state
*st
= data
;
667 struct dundi_ie_data ied
;
668 struct dundi_hint_metadata hmd
;
671 ast_log(LOG_DEBUG
, "Whee, precaching '%s@%s' for '%s'\n", st
->called_number
, st
->called_context
,
672 st
->eids
[0] ? dundi_eid_to_str(eid_str
, sizeof(eid_str
), st
->eids
[0]) : "ourselves");
673 memset(&ied
, 0, sizeof(ied
));
675 /* Now produce precache */
676 dundi_precache_internal(st
->called_context
, st
->called_number
, st
->ttl
, st
->eids
);
678 AST_LIST_LOCK(&peers
);
679 /* Truncate if "don't ask" isn't present */
680 if (!ast_test_flag_nonstd(&hmd
, DUNDI_HINT_DONT_ASK
))
682 if (ast_test_flag(st
->trans
, FLAG_DEAD
)) {
683 ast_log(LOG_DEBUG
, "Our transaction went away!\n");
684 st
->trans
->thread
= 0;
685 destroy_trans(st
->trans
, 0);
687 dundi_send(st
->trans
, DUNDI_COMMAND_PRECACHERP
, 0, 1, &ied
);
688 st
->trans
->thread
= 0;
690 AST_LIST_UNLOCK(&peers
);
695 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
[]);
697 static void *dundi_query_thread(void *data
)
699 struct dundi_query_state
*st
= data
;
700 struct dundi_entity_info dei
;
701 struct dundi_ie_data ied
;
702 struct dundi_hint_metadata hmd
;
705 ast_log(LOG_DEBUG
, "Whee, looking up '%s@%s' for '%s'\n", st
->called_number
, st
->called_context
,
706 st
->eids
[0] ? dundi_eid_to_str(eid_str
, sizeof(eid_str
), st
->eids
[0]) : "ourselves");
707 memset(&ied
, 0, sizeof(ied
));
708 memset(&dei
, 0, sizeof(dei
));
709 memset(&hmd
, 0, sizeof(hmd
));
710 if (!dundi_eid_cmp(&st
->trans
->us_eid
, &st
->reqeid
)) {
712 ast_log(LOG_DEBUG
, "Neat, someone look for us!\n");
713 ast_copy_string(dei
.orgunit
, dept
, sizeof(dei
.orgunit
));
714 ast_copy_string(dei
.org
, org
, sizeof(dei
.org
));
715 ast_copy_string(dei
.locality
, locality
, sizeof(dei
.locality
));
716 ast_copy_string(dei
.stateprov
, stateprov
, sizeof(dei
.stateprov
));
717 ast_copy_string(dei
.country
, country
, sizeof(dei
.country
));
718 ast_copy_string(dei
.email
, email
, sizeof(dei
.email
));
719 ast_copy_string(dei
.phone
, phone
, sizeof(dei
.phone
));
722 /* If we do not have a canonical result, keep looking */
723 res
= dundi_query_eid_internal(&dei
, st
->called_context
, &st
->reqeid
, &hmd
, st
->ttl
, 1, st
->eids
);
725 AST_LIST_LOCK(&peers
);
726 if (ast_test_flag(st
->trans
, FLAG_DEAD
)) {
727 ast_log(LOG_DEBUG
, "Our transaction went away!\n");
728 st
->trans
->thread
= 0;
729 destroy_trans(st
->trans
, 0);
732 dundi_ie_append_str(&ied
, DUNDI_IE_DEPARTMENT
, dei
.orgunit
);
733 dundi_ie_append_str(&ied
, DUNDI_IE_ORGANIZATION
, dei
.org
);
734 dundi_ie_append_str(&ied
, DUNDI_IE_LOCALITY
, dei
.locality
);
735 dundi_ie_append_str(&ied
, DUNDI_IE_STATE_PROV
, dei
.stateprov
);
736 dundi_ie_append_str(&ied
, DUNDI_IE_COUNTRY
, dei
.country
);
737 dundi_ie_append_str(&ied
, DUNDI_IE_EMAIL
, dei
.email
);
738 dundi_ie_append_str(&ied
, DUNDI_IE_PHONE
, dei
.phone
);
739 if (!ast_strlen_zero(dei
.ipaddr
))
740 dundi_ie_append_str(&ied
, DUNDI_IE_IPADDR
, dei
.ipaddr
);
742 dundi_ie_append_hint(&ied
, DUNDI_IE_HINT
, hmd
.flags
, hmd
.exten
);
743 dundi_send(st
->trans
, DUNDI_COMMAND_EIDRESPONSE
, 0, 1, &ied
);
744 st
->trans
->thread
= 0;
746 AST_LIST_UNLOCK(&peers
);
751 static int dundi_answer_entity(struct dundi_transaction
*trans
, struct dundi_ies
*ies
, char *ccontext
)
753 struct dundi_query_state
*st
;
757 struct dundi_ie_data ied
;
760 pthread_t lookupthread
;
762 if (ies
->eidcount
> 1) {
763 /* Since it is a requirement that the first EID is the authenticating host
764 and the last EID is the root, it is permissible that the first and last EID
765 could be the same. In that case, we should go ahead copy only the "root" section
766 since we will not need it for authentication. */
767 if (!dundi_eid_cmp(ies
->eids
[0], ies
->eids
[ies
->eidcount
- 1]))
770 totallen
= sizeof(struct dundi_query_state
);
771 totallen
+= (ies
->eidcount
- skipfirst
) * sizeof(dundi_eid
);
772 st
= ast_calloc(1, totallen
);
774 ast_copy_string(st
->called_context
, ies
->called_context
, sizeof(st
->called_context
));
775 memcpy(&st
->reqeid
, ies
->reqeid
, sizeof(st
->reqeid
));
777 st
->ttl
= ies
->ttl
- 1;
781 for (x
=skipfirst
;ies
->eids
[x
];x
++) {
782 st
->eids
[x
-skipfirst
] = (dundi_eid
*)s
;
783 *st
->eids
[x
-skipfirst
] = *ies
->eids
[x
];
784 s
+= sizeof(dundi_eid
);
786 ast_log(LOG_DEBUG
, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), ies
->reqeid
), ies
->called_context
);
787 pthread_attr_init(&attr
);
788 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
790 if (ast_pthread_create(&lookupthread
, &attr
, dundi_query_thread
, st
)) {
792 ast_log(LOG_WARNING
, "Unable to create thread!\n");
794 memset(&ied
, 0, sizeof(ied
));
795 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of threads");
796 dundi_send(trans
, DUNDI_COMMAND_EIDRESPONSE
, 0, 1, &ied
);
797 pthread_attr_destroy(&attr
);
800 pthread_attr_destroy(&attr
);
802 ast_log(LOG_WARNING
, "Out of memory!\n");
803 memset(&ied
, 0, sizeof(ied
));
804 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of memory");
805 dundi_send(trans
, DUNDI_COMMAND_EIDRESPONSE
, 0, 1, &ied
);
811 static int cache_save_hint(dundi_eid
*eidpeer
, struct dundi_request
*req
, struct dundi_hint
*hint
, int expiration
)
816 char eidpeer_str
[20];
817 char eidroot_str
[20];
822 expiration
= dundi_cache_time
;
824 /* Only cache hint if "don't ask" is there... */
825 if (!ast_test_flag_nonstd(hint
, htons(DUNDI_HINT_DONT_ASK
)))
828 unaffected
= ast_test_flag_nonstd(hint
, htons(DUNDI_HINT_UNAFFECTED
));
830 dundi_eid_to_str_short(eidpeer_str
, sizeof(eidpeer_str
), eidpeer
);
831 dundi_eid_to_str_short(eidroot_str
, sizeof(eidroot_str
), &req
->root_eid
);
832 snprintf(key1
, sizeof(key1
), "hint/%s/%s/%s/e%08lx", eidpeer_str
, hint
->data
, req
->dcontext
, unaffected
? 0 : req
->crc32
);
833 snprintf(key2
, sizeof(key2
), "hint/%s/%s/%s/r%s", eidpeer_str
, hint
->data
, req
->dcontext
, eidroot_str
);
836 timeout
+= expiration
;
837 snprintf(data
, sizeof(data
), "%ld|", (long)(timeout
));
839 ast_db_put("dundi/cache", key1
, data
);
840 ast_log(LOG_DEBUG
, "Caching hint at '%s'\n", key1
);
841 ast_db_put("dundi/cache", key2
, data
);
842 ast_log(LOG_DEBUG
, "Caching hint at '%s'\n", key2
);
846 static int cache_save(dundi_eid
*eidpeer
, struct dundi_request
*req
, int start
, int unaffected
, int expiration
, int push
)
852 char eidpeer_str
[20];
853 char eidroot_str
[20];
857 expiration
= dundi_cache_time
;
859 /* Keep pushes a little longer, cut pulls a little short */
866 dundi_eid_to_str_short(eidpeer_str
, sizeof(eidpeer_str
), eidpeer
);
867 dundi_eid_to_str_short(eidroot_str
, sizeof(eidroot_str
), &req
->root_eid
);
868 snprintf(key1
, sizeof(key1
), "%s/%s/%s/e%08lx", eidpeer_str
, req
->number
, req
->dcontext
, unaffected
? 0 : req
->crc32
);
869 snprintf(key2
, sizeof(key2
), "%s/%s/%s/r%s", eidpeer_str
, req
->number
, req
->dcontext
, eidroot_str
);
870 /* Build request string */
872 timeout
+= expiration
;
873 snprintf(data
, sizeof(data
), "%ld|", (long)(timeout
));
874 for (x
=start
;x
<req
->respcount
;x
++) {
875 /* Skip anything with an illegal pipe in it */
876 if (strchr(req
->dr
[x
].dest
, '|'))
878 snprintf(data
+ strlen(data
), sizeof(data
) - strlen(data
), "%d/%d/%d/%s/%s|",
879 req
->dr
[x
].flags
, req
->dr
[x
].weight
, req
->dr
[x
].techint
, req
->dr
[x
].dest
,
880 dundi_eid_to_str_short(eidpeer_str
, sizeof(eidpeer_str
), &req
->dr
[x
].eid
));
882 ast_db_put("dundi/cache", key1
, data
);
883 ast_db_put("dundi/cache", key2
, data
);
887 static int dundi_prop_precache(struct dundi_transaction
*trans
, struct dundi_ies
*ies
, char *ccontext
)
889 struct dundi_query_state
*st
;
892 struct dundi_ie_data ied
;
894 struct dundi_result dr2
[MAX_RESULTS
];
895 struct dundi_request dr
;
896 struct dundi_hint_metadata hmd
;
898 struct dundi_mapping
*cur
;
902 pthread_t lookupthread
;
905 memset(&dr2
, 0, sizeof(dr2
));
906 memset(&dr
, 0, sizeof(dr
));
907 memset(&hmd
, 0, sizeof(hmd
));
909 /* Forge request structure to hold answers for cache */
910 hmd
.flags
= DUNDI_HINT_DONT_ASK
| DUNDI_HINT_UNAFFECTED
;
912 dr
.maxcount
= MAX_RESULTS
;
913 dr
.expiration
= dundi_cache_time
;
915 dr
.pfds
[0] = dr
.pfds
[1] = -1;
917 ast_copy_string(dr
.dcontext
, ies
->called_context
? ies
->called_context
: "e164", sizeof(dr
.dcontext
));
918 ast_copy_string(dr
.number
, ies
->called_number
, sizeof(dr
.number
));
920 for (x
=0;x
<ies
->anscount
;x
++) {
921 if (trans
->parent
->respcount
< trans
->parent
->maxcount
) {
922 /* Make sure it's not already there */
923 for (z
=0;z
<trans
->parent
->respcount
;z
++) {
924 if ((trans
->parent
->dr
[z
].techint
== ies
->answers
[x
]->protocol
) &&
925 !strcmp(trans
->parent
->dr
[z
].dest
, (char *)ies
->answers
[x
]->data
))
928 if (z
== trans
->parent
->respcount
) {
929 /* Copy into parent responses */
930 trans
->parent
->dr
[trans
->parent
->respcount
].flags
= ntohs(ies
->answers
[x
]->flags
);
931 trans
->parent
->dr
[trans
->parent
->respcount
].techint
= ies
->answers
[x
]->protocol
;
932 trans
->parent
->dr
[trans
->parent
->respcount
].weight
= ntohs(ies
->answers
[x
]->weight
);
933 trans
->parent
->dr
[trans
->parent
->respcount
].eid
= ies
->answers
[x
]->eid
;
934 if (ies
->expiration
> 0)
935 trans
->parent
->dr
[trans
->parent
->respcount
].expiration
= ies
->expiration
;
937 trans
->parent
->dr
[trans
->parent
->respcount
].expiration
= dundi_cache_time
;
938 dundi_eid_to_str(trans
->parent
->dr
[trans
->parent
->respcount
].eid_str
,
939 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].eid_str
),
940 &ies
->answers
[x
]->eid
);
941 ast_copy_string(trans
->parent
->dr
[trans
->parent
->respcount
].dest
, (char *)ies
->answers
[x
]->data
,
942 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].dest
));
943 ast_copy_string(trans
->parent
->dr
[trans
->parent
->respcount
].tech
, tech2str(ies
->answers
[x
]->protocol
),
944 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].tech
));
945 trans
->parent
->respcount
++;
946 ast_clear_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_DONT_ASK
);
947 } else if (trans
->parent
->dr
[z
].weight
> ies
->answers
[x
]->weight
) {
948 /* Update weight if appropriate */
949 trans
->parent
->dr
[z
].weight
= ies
->answers
[x
]->weight
;
952 ast_log(LOG_NOTICE
, "Dropping excessive answers in precache for %s@%s\n",
953 trans
->parent
->number
, trans
->parent
->dcontext
);
956 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
957 cache_save(&trans
->them_eid
, trans
->parent
, 0, 0, ies
->expiration
, 1);
959 cache_save_hint(&trans
->them_eid
, trans
->parent
, ies
->hint
, ies
->expiration
);
961 totallen
= sizeof(struct dundi_query_state
);
962 /* Count matching map entries */
964 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
965 if (!strcasecmp(cur
->dcontext
, ccontext
))
969 /* If no maps, return -1 immediately */
973 if (ies
->eidcount
> 1) {
974 /* Since it is a requirement that the first EID is the authenticating host
975 and the last EID is the root, it is permissible that the first and last EID
976 could be the same. In that case, we should go ahead copy only the "root" section
977 since we will not need it for authentication. */
978 if (!dundi_eid_cmp(ies
->eids
[0], ies
->eids
[ies
->eidcount
- 1]))
982 /* Prepare to run a query and then propagate that as necessary */
983 totallen
+= mapcount
* sizeof(struct dundi_mapping
);
984 totallen
+= (ies
->eidcount
- skipfirst
) * sizeof(dundi_eid
);
985 st
= ast_calloc(1, totallen
);
987 ast_copy_string(st
->called_context
, ies
->called_context
, sizeof(st
->called_context
));
988 ast_copy_string(st
->called_number
, ies
->called_number
, sizeof(st
->called_number
));
990 st
->ttl
= ies
->ttl
- 1;
991 st
->nocache
= ies
->cbypass
;
995 for (x
=skipfirst
;ies
->eids
[x
];x
++) {
996 st
->eids
[x
-skipfirst
] = (dundi_eid
*)s
;
997 *st
->eids
[x
-skipfirst
] = *ies
->eids
[x
];
998 st
->directs
[x
-skipfirst
] = ies
->eid_direct
[x
];
999 s
+= sizeof(dundi_eid
);
1001 /* Append mappings */
1003 st
->maps
= (struct dundi_mapping
*)s
;
1004 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
1005 if (!strcasecmp(cur
->dcontext
, ccontext
)) {
1008 st
->maps
[x
].list
.next
= NULL
;
1013 st
->nummaps
= mapcount
;
1014 ast_log(LOG_DEBUG
, "Forwarding precache for '%s@%s'!\n", ies
->called_number
, ies
->called_context
);
1015 pthread_attr_init(&attr
);
1016 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1018 if (ast_pthread_create(&lookupthread
, &attr
, dundi_precache_thread
, st
)) {
1020 ast_log(LOG_WARNING
, "Unable to create thread!\n");
1022 memset(&ied
, 0, sizeof(ied
));
1023 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of threads");
1024 dundi_send(trans
, DUNDI_COMMAND_PRECACHERP
, 0, 1, &ied
);
1025 pthread_attr_destroy(&attr
);
1028 pthread_attr_destroy(&attr
);
1030 ast_log(LOG_WARNING
, "Out of memory!\n");
1031 memset(&ied
, 0, sizeof(ied
));
1032 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of memory");
1033 dundi_send(trans
, DUNDI_COMMAND_PRECACHERP
, 0, 1, &ied
);
1039 static int dundi_answer_query(struct dundi_transaction
*trans
, struct dundi_ies
*ies
, char *ccontext
)
1041 struct dundi_query_state
*st
;
1044 struct dundi_ie_data ied
;
1046 struct dundi_mapping
*cur
;
1050 pthread_t lookupthread
;
1051 pthread_attr_t attr
;
1052 totallen
= sizeof(struct dundi_query_state
);
1053 /* Count matching map entries */
1054 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
1055 if (!strcasecmp(cur
->dcontext
, ccontext
))
1058 /* If no maps, return -1 immediately */
1062 if (ies
->eidcount
> 1) {
1063 /* Since it is a requirement that the first EID is the authenticating host
1064 and the last EID is the root, it is permissible that the first and last EID
1065 could be the same. In that case, we should go ahead copy only the "root" section
1066 since we will not need it for authentication. */
1067 if (!dundi_eid_cmp(ies
->eids
[0], ies
->eids
[ies
->eidcount
- 1]))
1071 totallen
+= mapcount
* sizeof(struct dundi_mapping
);
1072 totallen
+= (ies
->eidcount
- skipfirst
) * sizeof(dundi_eid
);
1073 st
= ast_calloc(1, totallen
);
1075 ast_copy_string(st
->called_context
, ies
->called_context
, sizeof(st
->called_context
));
1076 ast_copy_string(st
->called_number
, ies
->called_number
, sizeof(st
->called_number
));
1078 st
->ttl
= ies
->ttl
- 1;
1079 st
->nocache
= ies
->cbypass
;
1083 for (x
=skipfirst
;ies
->eids
[x
];x
++) {
1084 st
->eids
[x
-skipfirst
] = (dundi_eid
*)s
;
1085 *st
->eids
[x
-skipfirst
] = *ies
->eids
[x
];
1086 st
->directs
[x
-skipfirst
] = ies
->eid_direct
[x
];
1087 s
+= sizeof(dundi_eid
);
1089 /* Append mappings */
1091 st
->maps
= (struct dundi_mapping
*)s
;
1092 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
1093 if (!strcasecmp(cur
->dcontext
, ccontext
)) {
1096 st
->maps
[x
].list
.next
= NULL
;
1101 st
->nummaps
= mapcount
;
1102 ast_log(LOG_DEBUG
, "Answering query for '%s@%s'!\n", ies
->called_number
, ies
->called_context
);
1103 pthread_attr_init(&attr
);
1104 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1106 if (ast_pthread_create(&lookupthread
, &attr
, dundi_lookup_thread
, st
)) {
1108 ast_log(LOG_WARNING
, "Unable to create thread!\n");
1110 memset(&ied
, 0, sizeof(ied
));
1111 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of threads");
1112 dundi_send(trans
, DUNDI_COMMAND_DPRESPONSE
, 0, 1, &ied
);
1113 pthread_attr_destroy(&attr
);
1116 pthread_attr_destroy(&attr
);
1118 ast_log(LOG_WARNING
, "Out of memory!\n");
1119 memset(&ied
, 0, sizeof(ied
));
1120 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of memory");
1121 dundi_send(trans
, DUNDI_COMMAND_DPRESPONSE
, 0, 1, &ied
);
1127 static int cache_lookup_internal(time_t now
, struct dundi_request
*req
, char *key
, char *eid_str_full
, int *lowexpiration
)
1130 char *ptr
, *term
, *src
;
1132 struct ast_flags flags
;
1138 /* Build request string */
1139 if (!ast_db_get("dundi/cache", key
, data
, sizeof(data
))) {
1142 if (!ast_get_time_t(ptr
, &timeout
, 0, &length
)) {
1143 int expiration
= timeout
- now
;
1144 if (expiration
> 0) {
1145 ast_log(LOG_DEBUG
, "Found cache expiring in %d seconds!\n", expiration
);
1147 while((sscanf(ptr
, "%d/%d/%d/%n", &(flags
.flags
), &weight
, &tech
, &length
) == 3)) {
1149 term
= strchr(ptr
, '|');
1152 src
= strrchr(ptr
, '/');
1158 ast_log(LOG_DEBUG
, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1159 tech2str(tech
), ptr
, src
, dundi_flags2str(fs
, sizeof(fs
), flags
.flags
), eid_str_full
);
1160 /* Make sure it's not already there */
1161 for (z
=0;z
<req
->respcount
;z
++) {
1162 if ((req
->dr
[z
].techint
== tech
) &&
1163 !strcmp(req
->dr
[z
].dest
, ptr
))
1166 if (z
== req
->respcount
) {
1167 /* Copy into parent responses */
1168 ast_copy_flags(&(req
->dr
[req
->respcount
]), &flags
, AST_FLAGS_ALL
);
1169 req
->dr
[req
->respcount
].weight
= weight
;
1170 req
->dr
[req
->respcount
].techint
= tech
;
1171 req
->dr
[req
->respcount
].expiration
= expiration
;
1172 dundi_str_short_to_eid(&req
->dr
[req
->respcount
].eid
, src
);
1173 dundi_eid_to_str(req
->dr
[req
->respcount
].eid_str
,
1174 sizeof(req
->dr
[req
->respcount
].eid_str
), &req
->dr
[req
->respcount
].eid
);
1175 ast_copy_string(req
->dr
[req
->respcount
].dest
, ptr
,
1176 sizeof(req
->dr
[req
->respcount
].dest
));
1177 ast_copy_string(req
->dr
[req
->respcount
].tech
, tech2str(tech
),
1178 sizeof(req
->dr
[req
->respcount
].tech
));
1180 ast_clear_flag_nonstd(req
->hmd
, DUNDI_HINT_DONT_ASK
);
1181 } else if (req
->dr
[z
].weight
> weight
)
1182 req
->dr
[z
].weight
= weight
;
1186 /* We found *something* cached */
1187 if (expiration
< *lowexpiration
)
1188 *lowexpiration
= expiration
;
1191 ast_db_del("dundi/cache", key
);
1193 ast_db_del("dundi/cache", key
);
1199 static int cache_lookup(struct dundi_request
*req
, dundi_eid
*peer_eid
, unsigned long crc32
, int *lowexpiration
)
1203 char eidroot_str
[20];
1207 char eid_str_full
[20];
1212 dundi_eid_to_str_short(eid_str
, sizeof(eid_str
), peer_eid
);
1213 dundi_eid_to_str_short(eidroot_str
, sizeof(eidroot_str
), &req
->root_eid
);
1214 dundi_eid_to_str(eid_str_full
, sizeof(eid_str_full
), peer_eid
);
1215 snprintf(key
, sizeof(key
), "%s/%s/%s/e%08lx", eid_str
, req
->number
, req
->dcontext
, crc32
);
1216 res
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1217 snprintf(key
, sizeof(key
), "%s/%s/%s/e%08lx", eid_str
, req
->number
, req
->dcontext
, 0L);
1218 res
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1219 snprintf(key
, sizeof(key
), "%s/%s/%s/r%s", eid_str
, req
->number
, req
->dcontext
, eidroot_str
);
1220 res
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1222 if (!req
->respcount
) {
1224 /* Look and see if we have a hint that would preclude us from looking at this
1225 peer for this number. */
1226 if (!(tmp
[x
] = req
->number
[x
]))
1229 /* Check for hints */
1230 snprintf(key
, sizeof(key
), "hint/%s/%s/%s/e%08lx", eid_str
, tmp
, req
->dcontext
, crc32
);
1231 res2
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1232 snprintf(key
, sizeof(key
), "hint/%s/%s/%s/e%08lx", eid_str
, tmp
, req
->dcontext
, 0L);
1233 res2
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1234 snprintf(key
, sizeof(key
), "hint/%s/%s/%s/r%s", eid_str
, tmp
, req
->dcontext
, eidroot_str
);
1235 res2
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1237 if (strlen(tmp
) > strlen(req
->hmd
->exten
)) {
1238 /* Update meta data if appropriate */
1239 ast_copy_string(req
->hmd
->exten
, tmp
, sizeof(req
->hmd
->exten
));
1249 static void qualify_peer(struct dundi_peer
*peer
, int schedonly
);
1251 static void apply_peer(struct dundi_transaction
*trans
, struct dundi_peer
*p
)
1253 if (!trans
->addr
.sin_addr
.s_addr
)
1254 memcpy(&trans
->addr
, &p
->addr
, sizeof(trans
->addr
));
1255 trans
->us_eid
= p
->us_eid
;
1256 trans
->them_eid
= p
->eid
;
1257 /* Enable encryption if appropriate */
1258 if (!ast_strlen_zero(p
->inkey
))
1259 ast_set_flag(trans
, FLAG_ENCRYPT
);
1261 trans
->autokilltimeout
= p
->maxms
;
1262 trans
->retranstimer
= DUNDI_DEFAULT_RETRANS_TIMER
;
1263 if (p
->lastms
> 1) {
1264 trans
->retranstimer
= p
->lastms
* 2;
1265 /* Keep it from being silly */
1266 if (trans
->retranstimer
< 150)
1267 trans
->retranstimer
= 150;
1269 if (trans
->retranstimer
> DUNDI_DEFAULT_RETRANS_TIMER
)
1270 trans
->retranstimer
= DUNDI_DEFAULT_RETRANS_TIMER
;
1272 trans
->autokilltimeout
= global_autokilltimeout
;
1275 /*! \note Called with the peers list already locked */
1276 static int do_register_expire(const void *data
)
1278 struct dundi_peer
*peer
= (struct dundi_peer
*)data
;
1280 ast_log(LOG_DEBUG
, "Register expired for '%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1281 peer
->registerexpire
= -1;
1283 memset(&peer
->addr
, 0, sizeof(peer
->addr
));
1287 static int update_key(struct dundi_peer
*peer
)
1289 unsigned char key
[16];
1290 struct ast_key
*ekey
, *skey
;
1293 if (!peer
->keyexpire
|| (peer
->keyexpire
< time(NULL
))) {
1295 aes_encrypt_key128(key
, &peer
->us_ecx
);
1296 aes_decrypt_key128(key
, &peer
->us_dcx
);
1297 ekey
= ast_key_get(peer
->inkey
, AST_KEY_PUBLIC
);
1299 ast_log(LOG_NOTICE
, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1300 peer
->inkey
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1303 skey
= ast_key_get(peer
->outkey
, AST_KEY_PRIVATE
);
1305 ast_log(LOG_NOTICE
, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1306 peer
->outkey
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1309 if ((res
= ast_encrypt_bin(peer
->txenckey
, key
, sizeof(key
), ekey
)) != 128) {
1310 ast_log(LOG_NOTICE
, "Whoa, got a weird encrypt size (%d != %d)!\n", res
, 128);
1313 if ((res
= ast_sign_bin(skey
, (char *)peer
->txenckey
, 128, peer
->txenckey
+ 128))) {
1314 ast_log(LOG_NOTICE
, "Failed to sign key (%d)!\n", res
);
1317 peer
->us_keycrc32
= crc32(0L, peer
->txenckey
, 128);
1318 peer
->sentfullkey
= 0;
1320 time(&peer
->keyexpire
);
1321 peer
->keyexpire
+= dundi_key_ttl
;
1326 static int encrypt_memcpy(unsigned char *dst
, unsigned char *src
, int len
, unsigned char *iv
, aes_encrypt_ctx
*ecx
)
1328 unsigned char curblock
[16];
1330 memcpy(curblock
, iv
, sizeof(curblock
));
1333 curblock
[x
] ^= src
[x
];
1334 aes_encrypt(curblock
, dst
, ecx
);
1335 memcpy(curblock
, dst
, sizeof(curblock
));
1342 static int decrypt_memcpy(unsigned char *dst
, unsigned char *src
, int len
, unsigned char *iv
, aes_decrypt_ctx
*dcx
)
1344 unsigned char lastblock
[16];
1346 memcpy(lastblock
, iv
, sizeof(lastblock
));
1348 aes_decrypt(src
, dst
, dcx
);
1350 dst
[x
] ^= lastblock
[x
];
1351 memcpy(lastblock
, src
, sizeof(lastblock
));
1359 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
)
1361 int space
= *dstlen
;
1362 unsigned long bytes
;
1363 struct dundi_hdr
*h
;
1364 unsigned char *decrypt_space
;
1365 decrypt_space
= alloca(srclen
);
1368 decrypt_memcpy(decrypt_space
, src
->encdata
, srclen
, src
->iv
, &trans
->dcx
);
1370 h
= (struct dundi_hdr
*)dst
;
1373 if (uncompress(dst
+ 6, &bytes
, decrypt_space
, srclen
) != Z_OK
) {
1374 ast_log(LOG_DEBUG
, "Ouch, uncompress failed :(\n");
1378 *dstlen
= bytes
+ 6;
1379 /* Return new header */
1383 static int dundi_encrypt(struct dundi_transaction
*trans
, struct dundi_packet
*pack
)
1385 unsigned char *compress_space
;
1388 unsigned long bytes
;
1389 struct dundi_ie_data ied
;
1390 struct dundi_peer
*peer
;
1391 unsigned char iv
[16];
1392 len
= pack
->datalen
+ pack
->datalen
/ 100 + 42;
1393 compress_space
= alloca(len
);
1394 if (compress_space
) {
1395 memset(compress_space
, 0, len
);
1396 /* We care about everthing save the first 6 bytes of header */
1398 res
= compress(compress_space
, &bytes
, pack
->data
+ 6, pack
->datalen
- 6);
1400 ast_log(LOG_DEBUG
, "Ouch, compression failed!\n");
1403 memset(&ied
, 0, sizeof(ied
));
1404 /* Say who we are */
1405 if (!pack
->h
->iseqno
&& !pack
->h
->oseqno
) {
1406 /* Need the key in the first copy */
1407 if (!(peer
= find_peer(&trans
->them_eid
)))
1409 if (update_key(peer
))
1411 if (!peer
->sentfullkey
)
1412 ast_set_flag(trans
, FLAG_SENDFULLKEY
);
1413 /* Append key data */
1414 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->us_eid
);
1415 if (ast_test_flag(trans
, FLAG_SENDFULLKEY
)) {
1416 dundi_ie_append_raw(&ied
, DUNDI_IE_SHAREDKEY
, peer
->txenckey
, 128);
1417 dundi_ie_append_raw(&ied
, DUNDI_IE_SIGNATURE
, peer
->txenckey
+ 128, 128);
1419 dundi_ie_append_int(&ied
, DUNDI_IE_KEYCRC32
, peer
->us_keycrc32
);
1421 /* Setup contexts */
1422 trans
->ecx
= peer
->us_ecx
;
1423 trans
->dcx
= peer
->us_dcx
;
1425 /* We've sent the full key */
1426 peer
->sentfullkey
= 1;
1428 /* Build initialization vector */
1430 /* Add the field, rounded up to 16 bytes */
1431 dundi_ie_append_encdata(&ied
, DUNDI_IE_ENCDATA
, iv
, NULL
, ((bytes
+ 15) / 16) * 16);
1433 if ((ied
.pos
+ bytes
) >= sizeof(ied
.buf
)) {
1434 ast_log(LOG_NOTICE
, "Final packet too large!\n");
1437 encrypt_memcpy(ied
.buf
+ ied
.pos
, compress_space
, bytes
, iv
, &trans
->ecx
);
1438 ied
.pos
+= ((bytes
+ 15) / 16) * 16;
1439 /* Reconstruct header */
1440 pack
->datalen
= sizeof(struct dundi_hdr
);
1441 pack
->h
->cmdresp
= DUNDI_COMMAND_ENCRYPT
;
1442 pack
->h
->cmdflags
= 0;
1443 memcpy(pack
->h
->ies
, ied
.buf
, ied
.pos
);
1444 pack
->datalen
+= ied
.pos
;
1450 static int check_key(struct dundi_peer
*peer
, unsigned char *newkey
, unsigned char *newsig
, unsigned long keycrc32
)
1452 unsigned char dst
[128];
1454 struct ast_key
*key
, *skey
;
1457 ast_log(LOG_DEBUG
, "Expected '%08lx' got '%08lx'\n", peer
->them_keycrc32
, keycrc32
);
1458 if (peer
->them_keycrc32
&& (peer
->them_keycrc32
== keycrc32
)) {
1461 } else if (!newkey
|| !newsig
)
1463 if (!memcmp(peer
->rxenckey
, newkey
, 128) &&
1464 !memcmp(peer
->rxenckey
+ 128, newsig
, 128)) {
1465 /* By definition, a match */
1469 key
= ast_key_get(peer
->outkey
, AST_KEY_PRIVATE
);
1471 ast_log(LOG_NOTICE
, "Unable to find key '%s' to decode shared key from '%s'\n",
1472 peer
->outkey
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1476 skey
= ast_key_get(peer
->inkey
, AST_KEY_PUBLIC
);
1478 ast_log(LOG_NOTICE
, "Unable to find key '%s' to verify shared key from '%s'\n",
1479 peer
->inkey
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1483 /* First check signature */
1484 res
= ast_check_signature_bin(skey
, (char *)newkey
, 128, newsig
);
1488 res
= ast_decrypt_bin(dst
, newkey
, sizeof(dst
), key
);
1491 ast_log(LOG_NOTICE
, "Weird, key decoded to the wrong size (%d)\n", res
);
1494 /* Decrypted, passes signature */
1495 ast_log(LOG_DEBUG
, "Wow, new key combo passed signature and decrypt!\n");
1496 memcpy(peer
->rxenckey
, newkey
, 128);
1497 memcpy(peer
->rxenckey
+ 128, newsig
, 128);
1498 peer
->them_keycrc32
= crc32(0L, peer
->rxenckey
, 128);
1499 aes_decrypt_key128(dst
, &peer
->them_dcx
);
1500 aes_encrypt_key128(dst
, &peer
->them_ecx
);
1504 static void deep_copy_peer(struct dundi_peer
*peer_dst
, const struct dundi_peer
*peer_src
)
1506 struct permission
*cur
, *perm
;
1508 memcpy(peer_dst
, peer_src
, sizeof(*peer_dst
));
1510 memset(&peer_dst
->permit
, 0, sizeof(peer_dst
->permit
));
1511 memset(&peer_dst
->include
, 0, sizeof(peer_dst
->permit
));
1513 AST_LIST_TRAVERSE(&peer_src
->permit
, cur
, list
) {
1514 if (!(perm
= ast_calloc(1, sizeof(*perm
) + strlen(cur
->name
) + 1)))
1517 perm
->allow
= cur
->allow
;
1518 strcpy(perm
->name
, cur
->name
);
1520 AST_LIST_INSERT_HEAD(&peer_dst
->permit
, perm
, list
);
1523 AST_LIST_TRAVERSE(&peer_src
->include
, cur
, list
) {
1524 if (!(perm
= ast_calloc(1, sizeof(*perm
) + strlen(cur
->name
) + 1)))
1527 perm
->allow
= cur
->allow
;
1528 strcpy(perm
->name
, cur
->name
);
1530 AST_LIST_INSERT_HEAD(&peer_dst
->include
, perm
, list
);
1534 static int handle_command_response(struct dundi_transaction
*trans
, struct dundi_hdr
*hdr
, int datalen
, int encrypted
)
1536 /* Handle canonical command / response */
1537 int final
= hdr
->cmdresp
& 0x80;
1538 int cmd
= hdr
->cmdresp
& 0x7f;
1543 unsigned char *bufcpy
;
1544 struct dundi_ie_data ied
;
1545 struct dundi_ies ies
;
1546 struct dundi_peer
*peer
= NULL
;
1549 memset(&ied
, 0, sizeof(ied
));
1550 memset(&ies
, 0, sizeof(ies
));
1552 bufcpy
= alloca(datalen
);
1555 /* Make a copy for parsing */
1556 memcpy(bufcpy
, hdr
->ies
, datalen
);
1557 ast_log(LOG_DEBUG
, "Got canonical message %d (%d), %d bytes data%s\n", cmd
, hdr
->oseqno
, datalen
, final
? " (Final)" : "");
1558 if (dundi_parse_ies(&ies
, bufcpy
, datalen
) < 0) {
1559 ast_log(LOG_WARNING
, "Failed to parse DUNDI information elements!\n");
1564 case DUNDI_COMMAND_DPDISCOVER
:
1565 case DUNDI_COMMAND_EIDQUERY
:
1566 case DUNDI_COMMAND_PRECACHERQ
:
1567 if (cmd
== DUNDI_COMMAND_EIDQUERY
)
1568 resp
= DUNDI_COMMAND_EIDRESPONSE
;
1569 else if (cmd
== DUNDI_COMMAND_PRECACHERQ
)
1570 resp
= DUNDI_COMMAND_PRECACHERP
;
1572 resp
= DUNDI_COMMAND_DPRESPONSE
;
1573 /* A dialplan or entity discover -- qualify by highest level entity */
1574 peer
= find_peer(ies
.eids
[0]);
1576 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, NULL
);
1577 dundi_send(trans
, resp
, 0, 1, &ied
);
1580 trans
->us_eid
= peer
->us_eid
;
1581 if (strlen(peer
->inkey
)) {
1582 hasauth
= encrypted
;
1586 /* Okay we're authentiated and all, now we check if they're authorized */
1587 if (!ies
.called_context
)
1588 ies
.called_context
= "e164";
1589 if (cmd
== DUNDI_COMMAND_EIDQUERY
) {
1590 res
= dundi_answer_entity(trans
, &ies
, ies
.called_context
);
1592 if (ast_strlen_zero(ies
.called_number
)) {
1593 /* They're not permitted to access that context */
1594 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Invalid or missing number/entity");
1595 dundi_send(trans
, resp
, 0, 1, &ied
);
1596 } else if ((cmd
== DUNDI_COMMAND_DPDISCOVER
) &&
1597 (peer
->model
& DUNDI_MODEL_INBOUND
) &&
1598 has_permission(&peer
->permit
, ies
.called_context
)) {
1599 res
= dundi_answer_query(trans
, &ies
, ies
.called_context
);
1601 /* There is no such dundi context */
1602 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Unsupported DUNDI Context");
1603 dundi_send(trans
, resp
, 0, 1, &ied
);
1605 } else if ((cmd
= DUNDI_COMMAND_PRECACHERQ
) &&
1606 (peer
->pcmodel
& DUNDI_MODEL_INBOUND
) &&
1607 has_permission(&peer
->include
, ies
.called_context
)) {
1608 res
= dundi_prop_precache(trans
, &ies
, ies
.called_context
);
1610 /* There is no such dundi context */
1611 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Unsupported DUNDI Context");
1612 dundi_send(trans
, resp
, 0, 1, &ied
);
1615 /* They're not permitted to access that context */
1616 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Permission to context denied");
1617 dundi_send(trans
, resp
, 0, 1, &ied
);
1621 /* They're not permitted to access that context */
1622 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Unencrypted responses not permitted");
1623 dundi_send(trans
, resp
, 0, 1, &ied
);
1627 case DUNDI_COMMAND_REGREQ
:
1628 /* A register request -- should only have one entity */
1629 peer
= find_peer(ies
.eids
[0]);
1631 /* if the peer is not found and we have a valid 'any_peer' setting */
1632 if (any_peer
&& peer
== any_peer
) {
1633 /* copy any_peer into a new peer object */
1634 peer
= ast_calloc(1, sizeof(*peer
));
1636 deep_copy_peer(peer
, any_peer
);
1638 /* set EID to remote EID */
1639 peer
->eid
= *ies
.eids
[0];
1641 AST_LIST_LOCK(&peers
);
1642 AST_LIST_INSERT_HEAD(&peers
, peer
, list
);
1643 AST_LIST_UNLOCK(&peers
);
1647 if (!peer
|| !peer
->dynamic
) {
1648 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, NULL
);
1649 dundi_send(trans
, DUNDI_COMMAND_REGRESPONSE
, 0, 1, &ied
);
1652 trans
->us_eid
= peer
->us_eid
;
1653 if (!ast_strlen_zero(peer
->inkey
)) {
1654 hasauth
= encrypted
;
1658 int expire
= default_expiration
;
1661 AST_SCHED_DEL(sched
, peer
->registerexpire
);
1662 peer
->registerexpire
= ast_sched_add(sched
, (expire
+ 10) * 1000, do_register_expire
, peer
);
1663 snprintf(data
, sizeof(data
), "%s:%d:%d", ast_inet_ntoa(trans
->addr
.sin_addr
),
1664 ntohs(trans
->addr
.sin_port
), expire
);
1665 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str
, sizeof(eid_str
), &peer
->eid
), data
);
1666 if (inaddrcmp(&peer
->addr
, &trans
->addr
)) {
1667 if (option_verbose
> 2) {
1668 ast_verbose(VERBOSE_PREFIX_3
"Registered DUNDi peer '%s' at '%s:%d'\n",
1669 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
),
1670 ast_inet_ntoa(trans
->addr
.sin_addr
), ntohs(trans
->addr
.sin_port
));
1675 memcpy(&peer
->addr
, &trans
->addr
, sizeof(peer
->addr
));
1676 dundi_ie_append_short(&ied
, DUNDI_IE_EXPIRATION
, default_expiration
);
1677 dundi_send(trans
, DUNDI_COMMAND_REGRESPONSE
, 0, 1, &ied
);
1679 qualify_peer(peer
, 1);
1683 case DUNDI_COMMAND_DPRESPONSE
:
1684 /* A dialplan response, lets see what we got... */
1685 if (ies
.cause
< 1) {
1686 /* Success of some sort */
1687 ast_log(LOG_DEBUG
, "Looks like success of some sort (%d), %d answers\n", ies
.cause
, ies
.anscount
);
1688 if (ast_test_flag(trans
, FLAG_ENCRYPT
)) {
1689 authpass
= encrypted
;
1693 /* Pass back up answers */
1694 if (trans
->parent
&& trans
->parent
->dr
) {
1695 y
= trans
->parent
->respcount
;
1696 for (x
=0;x
<ies
.anscount
;x
++) {
1697 if (trans
->parent
->respcount
< trans
->parent
->maxcount
) {
1698 /* Make sure it's not already there */
1699 for (z
=0;z
<trans
->parent
->respcount
;z
++) {
1700 if ((trans
->parent
->dr
[z
].techint
== ies
.answers
[x
]->protocol
) &&
1701 !strcmp(trans
->parent
->dr
[z
].dest
, (char *)ies
.answers
[x
]->data
))
1704 if (z
== trans
->parent
->respcount
) {
1705 /* Copy into parent responses */
1706 trans
->parent
->dr
[trans
->parent
->respcount
].flags
= ntohs(ies
.answers
[x
]->flags
);
1707 trans
->parent
->dr
[trans
->parent
->respcount
].techint
= ies
.answers
[x
]->protocol
;
1708 trans
->parent
->dr
[trans
->parent
->respcount
].weight
= ntohs(ies
.answers
[x
]->weight
);
1709 trans
->parent
->dr
[trans
->parent
->respcount
].eid
= ies
.answers
[x
]->eid
;
1710 if (ies
.expiration
> 0)
1711 trans
->parent
->dr
[trans
->parent
->respcount
].expiration
= ies
.expiration
;
1713 trans
->parent
->dr
[trans
->parent
->respcount
].expiration
= dundi_cache_time
;
1714 dundi_eid_to_str(trans
->parent
->dr
[trans
->parent
->respcount
].eid_str
,
1715 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].eid_str
),
1716 &ies
.answers
[x
]->eid
);
1717 ast_copy_string(trans
->parent
->dr
[trans
->parent
->respcount
].dest
, (char *)ies
.answers
[x
]->data
,
1718 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].dest
));
1719 ast_copy_string(trans
->parent
->dr
[trans
->parent
->respcount
].tech
, tech2str(ies
.answers
[x
]->protocol
),
1720 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].tech
));
1721 trans
->parent
->respcount
++;
1722 ast_clear_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_DONT_ASK
);
1723 } else if (trans
->parent
->dr
[z
].weight
> ies
.answers
[x
]->weight
) {
1724 /* Update weight if appropriate */
1725 trans
->parent
->dr
[z
].weight
= ies
.answers
[x
]->weight
;
1728 ast_log(LOG_NOTICE
, "Dropping excessive answers to request for %s@%s\n",
1729 trans
->parent
->number
, trans
->parent
->dcontext
);
1731 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1732 the cache know if this request was unaffected by our entity list. */
1733 cache_save(&trans
->them_eid
, trans
->parent
, y
,
1734 ies
.hint
? ast_test_flag_nonstd(ies
.hint
, htons(DUNDI_HINT_UNAFFECTED
)) : 0, ies
.expiration
, 0);
1736 cache_save_hint(&trans
->them_eid
, trans
->parent
, ies
.hint
, ies
.expiration
);
1737 if (ast_test_flag_nonstd(ies
.hint
, htons(DUNDI_HINT_TTL_EXPIRED
)))
1738 ast_set_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_TTL_EXPIRED
);
1739 if (ast_test_flag_nonstd(ies
.hint
, htons(DUNDI_HINT_DONT_ASK
))) {
1740 if (strlen((char *)ies
.hint
->data
) > strlen(trans
->parent
->hmd
->exten
)) {
1741 ast_copy_string(trans
->parent
->hmd
->exten
, (char *)ies
.hint
->data
,
1742 sizeof(trans
->parent
->hmd
->exten
));
1745 ast_clear_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_DONT_ASK
);
1748 if (ies
.expiration
> 0) {
1749 if (trans
->parent
->expiration
> ies
.expiration
) {
1750 trans
->parent
->expiration
= ies
.expiration
;
1754 /* Close connection if not final */
1756 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1760 /* Auth failure, check for data */
1762 /* Cancel if they didn't already */
1763 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1767 case DUNDI_COMMAND_EIDRESPONSE
:
1768 /* A dialplan response, lets see what we got... */
1769 if (ies
.cause
< 1) {
1770 /* Success of some sort */
1771 ast_log(LOG_DEBUG
, "Looks like success of some sort (%d)\n", ies
.cause
);
1772 if (ast_test_flag(trans
, FLAG_ENCRYPT
)) {
1773 authpass
= encrypted
;
1777 /* Pass back up answers */
1778 if (trans
->parent
&& trans
->parent
->dei
&& ies
.q_org
) {
1779 if (!trans
->parent
->respcount
) {
1780 trans
->parent
->respcount
++;
1782 ast_copy_string(trans
->parent
->dei
->orgunit
, ies
.q_dept
, sizeof(trans
->parent
->dei
->orgunit
));
1784 ast_copy_string(trans
->parent
->dei
->org
, ies
.q_org
, sizeof(trans
->parent
->dei
->org
));
1786 ast_copy_string(trans
->parent
->dei
->locality
, ies
.q_locality
, sizeof(trans
->parent
->dei
->locality
));
1787 if (ies
.q_stateprov
)
1788 ast_copy_string(trans
->parent
->dei
->stateprov
, ies
.q_stateprov
, sizeof(trans
->parent
->dei
->stateprov
));
1790 ast_copy_string(trans
->parent
->dei
->country
, ies
.q_country
, sizeof(trans
->parent
->dei
->country
));
1792 ast_copy_string(trans
->parent
->dei
->email
, ies
.q_email
, sizeof(trans
->parent
->dei
->email
));
1794 ast_copy_string(trans
->parent
->dei
->phone
, ies
.q_phone
, sizeof(trans
->parent
->dei
->phone
));
1796 ast_copy_string(trans
->parent
->dei
->ipaddr
, ies
.q_ipaddr
, sizeof(trans
->parent
->dei
->ipaddr
));
1797 if (!dundi_eid_cmp(&trans
->them_eid
, &trans
->parent
->query_eid
)) {
1798 /* If it's them, update our address */
1799 ast_copy_string(trans
->parent
->dei
->ipaddr
, ast_inet_ntoa(trans
->addr
.sin_addr
), sizeof(trans
->parent
->dei
->ipaddr
));
1803 if (ast_test_flag_nonstd(ies
.hint
, htons(DUNDI_HINT_TTL_EXPIRED
)))
1804 ast_set_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_TTL_EXPIRED
);
1807 /* Close connection if not final */
1809 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1813 /* Auth failure, check for data */
1815 /* Cancel if they didn't already */
1816 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1820 case DUNDI_COMMAND_REGRESPONSE
:
1821 /* A dialplan response, lets see what we got... */
1822 if (ies
.cause
< 1) {
1824 /* Success of some sort */
1825 if (ast_test_flag(trans
, FLAG_ENCRYPT
)) {
1826 hasauth
= encrypted
;
1831 ast_log(LOG_NOTICE
, "Reponse to register not authorized!\n");
1833 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Improper signature in answer");
1834 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, &ied
);
1837 ast_log(LOG_DEBUG
, "Yay, we've registered as '%s' to '%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &trans
->us_eid
),
1838 dundi_eid_to_str(eid_str2
, sizeof(eid_str2
), &trans
->them_eid
));
1839 /* Close connection if not final */
1841 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1844 /* Auth failure, cancel if they didn't for some reason */
1846 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1850 case DUNDI_COMMAND_INVALID
:
1851 case DUNDI_COMMAND_NULL
:
1852 case DUNDI_COMMAND_PRECACHERP
:
1853 /* Do nothing special */
1855 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1857 case DUNDI_COMMAND_ENCREJ
:
1858 if ((ast_test_flag(trans
, FLAG_SENDFULLKEY
)) || AST_LIST_EMPTY(&trans
->lasttrans
) || !(peer
= find_peer(&trans
->them_eid
))) {
1859 /* No really, it's over at this point */
1861 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1863 /* Send with full key */
1864 ast_set_flag(trans
, FLAG_SENDFULLKEY
);
1866 /* Ooops, we got a final message, start by sending ACK... */
1867 dundi_ack(trans
, hdr
->cmdresp
& 0x80);
1868 trans
->aseqno
= trans
->iseqno
;
1869 /* Now, we gotta create a new transaction */
1870 if (!reset_transaction(trans
)) {
1871 /* Make sure handle_frame doesn't destroy us */
1872 hdr
->cmdresp
&= 0x7f;
1873 /* Parse the message we transmitted */
1874 memset(&ies
, 0, sizeof(ies
));
1875 dundi_parse_ies(&ies
, (AST_LIST_FIRST(&trans
->lasttrans
))->h
->ies
, (AST_LIST_FIRST(&trans
->lasttrans
))->datalen
- sizeof(struct dundi_hdr
));
1876 /* Reconstruct outgoing encrypted packet */
1877 memset(&ied
, 0, sizeof(ied
));
1878 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->us_eid
);
1879 dundi_ie_append_raw(&ied
, DUNDI_IE_SHAREDKEY
, peer
->txenckey
, 128);
1880 dundi_ie_append_raw(&ied
, DUNDI_IE_SIGNATURE
, peer
->txenckey
+ 128, 128);
1882 dundi_ie_append_encdata(&ied
, DUNDI_IE_ENCDATA
, ies
.encblock
->iv
, ies
.encblock
->encdata
, ies
.enclen
);
1883 dundi_send(trans
, DUNDI_COMMAND_ENCRYPT
, 0, (AST_LIST_FIRST(&trans
->lasttrans
))->h
->cmdresp
& 0x80, &ied
);
1884 peer
->sentfullkey
= 1;
1889 case DUNDI_COMMAND_ENCRYPT
:
1891 /* No nested encryption! */
1892 if ((trans
->iseqno
== 1) && !trans
->oseqno
) {
1893 if (!ies
.eids
[0] || !(peer
= find_peer(ies
.eids
[0])) ||
1894 ((!ies
.encsharedkey
|| !ies
.encsig
) && !ies
.keycrc32
) ||
1895 (check_key(peer
, ies
.encsharedkey
, ies
.encsig
, ies
.keycrc32
) < 1)) {
1897 dundi_send(trans
, DUNDI_COMMAND_ENCREJ
, 0, 1, NULL
);
1901 apply_peer(trans
, peer
);
1902 /* Key passed, use new contexts for this session */
1903 trans
->ecx
= peer
->them_ecx
;
1904 trans
->dcx
= peer
->them_dcx
;
1906 if (ast_test_flag(trans
, FLAG_ENCRYPT
) && ies
.encblock
&& ies
.enclen
) {
1907 struct dundi_hdr
*dhdr
;
1908 unsigned char decoded
[MAX_PACKET_SIZE
];
1910 ddatalen
= sizeof(decoded
);
1911 dhdr
= dundi_decrypt(trans
, decoded
, &ddatalen
, hdr
, ies
.encblock
, ies
.enclen
);
1913 /* Handle decrypted response */
1915 dundi_showframe(dhdr
, 3, &trans
->addr
, ddatalen
- sizeof(struct dundi_hdr
));
1916 handle_command_response(trans
, dhdr
, ddatalen
- sizeof(struct dundi_hdr
), 1);
1917 /* Carry back final flag */
1918 hdr
->cmdresp
|= dhdr
->cmdresp
& 0x80;
1921 ast_log(LOG_DEBUG
, "Ouch, decrypt failed :(\n");
1925 /* Turn off encryption */
1926 ast_clear_flag(trans
, FLAG_ENCRYPT
);
1927 dundi_send(trans
, DUNDI_COMMAND_ENCREJ
, 0, 1, NULL
);
1931 /* Send unknown command if we don't know it, with final flag IFF it's the
1932 first command in the dialog and only if we haven't recieved final notification */
1934 dundi_ie_append_byte(&ied
, DUNDI_IE_UNKNOWN
, cmd
);
1935 dundi_send(trans
, DUNDI_COMMAND_UNKNOWN
, 0, !hdr
->oseqno
, &ied
);
1941 static void destroy_packet(struct dundi_packet
*pack
, int needfree
);
1942 static void destroy_packets(struct packetlist
*p
)
1944 struct dundi_packet
*pack
;
1946 while ((pack
= AST_LIST_REMOVE_HEAD(p
, list
))) {
1947 AST_SCHED_DEL(sched
, pack
->retransid
);
1953 static int ack_trans(struct dundi_transaction
*trans
, int iseqno
)
1955 struct dundi_packet
*pack
;
1957 /* Ack transmitted packet corresponding to iseqno */
1958 AST_LIST_TRAVERSE(&trans
->packets
, pack
, list
) {
1959 if ((pack
->h
->oseqno
+ 1) % 255 == iseqno
) {
1960 destroy_packet(pack
, 0);
1961 if (!AST_LIST_EMPTY(&trans
->lasttrans
)) {
1962 ast_log(LOG_WARNING
, "Whoa, there was still a last trans?\n");
1963 destroy_packets(&trans
->lasttrans
);
1965 AST_LIST_INSERT_HEAD(&trans
->lasttrans
, pack
, list
);
1966 AST_SCHED_DEL(sched
, trans
->autokillid
);
1974 static int handle_frame(struct dundi_hdr
*h
, struct sockaddr_in
*sin
, int datalen
)
1976 struct dundi_transaction
*trans
;
1977 trans
= find_transaction(h
, sin
);
1979 dundi_reject(h
, sin
);
1982 /* Got a transaction, see where this header fits in */
1983 if (h
->oseqno
== trans
->iseqno
) {
1984 /* Just what we were looking for... Anything but ack increments iseqno */
1985 if (ack_trans(trans
, h
->iseqno
) && ast_test_flag(trans
, FLAG_FINAL
)) {
1986 /* If final, we're done */
1987 destroy_trans(trans
, 0);
1990 if (h
->cmdresp
!= DUNDI_COMMAND_ACK
) {
1991 trans
->oiseqno
= trans
->iseqno
;
1993 handle_command_response(trans
, h
, datalen
, 0);
1995 if (trans
->aseqno
!= trans
->iseqno
) {
1996 dundi_ack(trans
, h
->cmdresp
& 0x80);
1997 trans
->aseqno
= trans
->iseqno
;
1999 /* Delete any saved last transmissions */
2000 destroy_packets(&trans
->lasttrans
);
2001 if (h
->cmdresp
& 0x80) {
2002 /* Final -- destroy now */
2003 destroy_trans(trans
, 0);
2005 } else if (h
->oseqno
== trans
->oiseqno
) {
2006 /* Last incoming sequence number -- send ACK without processing */
2007 dundi_ack(trans
, 0);
2009 /* Out of window -- simply drop */
2010 ast_log(LOG_DEBUG
, "Dropping packet out of window!\n");
2015 static int socket_read(int *id
, int fd
, short events
, void *cbdata
)
2017 struct sockaddr_in sin
;
2019 struct dundi_hdr
*h
;
2020 char buf
[MAX_PACKET_SIZE
];
2023 res
= recvfrom(netsocket
, buf
, sizeof(buf
) - 1, 0,(struct sockaddr
*) &sin
, &len
);
2025 if (errno
!= ECONNREFUSED
)
2026 ast_log(LOG_WARNING
, "Error: %s\n", strerror(errno
));
2029 if (res
< sizeof(struct dundi_hdr
)) {
2030 ast_log(LOG_WARNING
, "midget packet received (%d of %d min)\n", res
, (int)sizeof(struct dundi_hdr
));
2034 h
= (struct dundi_hdr
*)buf
;
2036 dundi_showframe(h
, 1, &sin
, res
- sizeof(struct dundi_hdr
));
2037 AST_LIST_LOCK(&peers
);
2038 handle_frame(h
, &sin
, res
- sizeof(struct dundi_hdr
));
2039 AST_LIST_UNLOCK(&peers
);
2043 static void build_secret(char *secret
, int seclen
)
2045 unsigned char tmp
[16];
2049 ast_base64encode(secret
, tmp
, sizeof(tmp
), seclen
);
2050 /* Eliminate potential bad characters */
2051 while((s
= strchr(secret
, ';'))) *s
= '+';
2052 while((s
= strchr(secret
, '/'))) *s
= '+';
2053 while((s
= strchr(secret
, ':'))) *s
= '+';
2054 while((s
= strchr(secret
, '@'))) *s
= '+';
2058 static void save_secret(const char *newkey
, const char *oldkey
)
2062 snprintf(tmp
, sizeof(tmp
), "%s;%s", oldkey
, newkey
);
2064 snprintf(tmp
, sizeof(tmp
), "%s", newkey
);
2065 rotatetime
= time(NULL
) + DUNDI_SECRET_TIME
;
2066 ast_db_put(secretpath
, "secret", tmp
);
2067 snprintf(tmp
, sizeof(tmp
), "%d", (int)rotatetime
);
2068 ast_db_put(secretpath
, "secretexpiry", tmp
);
2071 static void load_password(void)
2078 ast_db_get(secretpath
, "secretexpiry", tmp
, sizeof(tmp
));
2079 if (!ast_get_time_t(tmp
, &expired
, 0, NULL
)) {
2080 ast_db_get(secretpath
, "secret", tmp
, sizeof(tmp
));
2081 current
= strchr(tmp
, ';');
2088 if ((time(NULL
) - expired
) < 0) {
2089 if ((expired
- time(NULL
)) > DUNDI_SECRET_TIME
)
2090 expired
= time(NULL
) + DUNDI_SECRET_TIME
;
2091 } else if ((time(NULL
) - (expired
+ DUNDI_SECRET_TIME
)) < 0) {
2100 /* Current key is still valid, just setup rotatation properly */
2101 ast_copy_string(cursecret
, current
, sizeof(cursecret
));
2102 rotatetime
= expired
;
2104 /* Current key is out of date, rotate or eliminate all together */
2105 build_secret(cursecret
, sizeof(cursecret
));
2106 save_secret(cursecret
, last
);
2110 static void check_password(void)
2117 printf("%ld/%ld\n", now
, rotatetime
);
2119 if ((now
- rotatetime
) >= 0) {
2120 /* Time to rotate keys */
2121 ast_copy_string(oldsecret
, cursecret
, sizeof(oldsecret
));
2122 build_secret(cursecret
, sizeof(cursecret
));
2123 save_secret(cursecret
, oldsecret
);
2127 static void *network_thread(void *ignore
)
2129 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2130 from the network, and queue them for delivery to the channels */
2132 /* Establish I/O callback for socket read */
2133 ast_io_add(io
, netsocket
, socket_read
, AST_IO_IN
, NULL
);
2135 while (!dundi_shutdown
) {
2136 res
= ast_sched_wait(sched
);
2137 if ((res
> 1000) || (res
< 0))
2139 res
= ast_io_wait(io
, res
);
2141 AST_LIST_LOCK(&peers
);
2142 ast_sched_runq(sched
);
2143 AST_LIST_UNLOCK(&peers
);
2148 netthreadid
= AST_PTHREADT_NULL
;
2153 static void *process_precache(void *ign
)
2155 struct dundi_precache_queue
*qe
;
2161 while (!dundi_shutdown
) {
2164 AST_LIST_LOCK(&pcq
);
2165 if ((qe
= AST_LIST_FIRST(&pcq
))) {
2166 if (!qe
->expiration
) {
2167 /* Gone... Remove... */
2168 AST_LIST_REMOVE_HEAD(&pcq
, list
);
2170 } else if (qe
->expiration
< now
) {
2171 /* Process this entry */
2173 ast_copy_string(context
, qe
->context
, sizeof(context
));
2174 ast_copy_string(number
, qe
->number
, sizeof(number
));
2178 AST_LIST_UNLOCK(&pcq
);
2180 dundi_precache(context
, number
);
2185 precachethreadid
= AST_PTHREADT_NULL
;
2190 static int start_network_thread(void)
2192 ast_pthread_create_background(&netthreadid
, NULL
, network_thread
, NULL
);
2193 ast_pthread_create_background(&precachethreadid
, NULL
, process_precache
, NULL
);
2197 static int dundi_do_debug(int fd
, int argc
, char *argv
[])
2200 return RESULT_SHOWUSAGE
;
2202 ast_cli(fd
, "DUNDi Debugging Enabled\n");
2203 return RESULT_SUCCESS
;
2206 static int dundi_do_store_history(int fd
, int argc
, char *argv
[])
2209 return RESULT_SHOWUSAGE
;
2210 global_storehistory
= 1;
2211 ast_cli(fd
, "DUNDi History Storage Enabled\n");
2212 return RESULT_SUCCESS
;
2215 static int dundi_flush(int fd
, int argc
, char *argv
[])
2218 if ((argc
< 2) || (argc
> 3))
2219 return RESULT_SHOWUSAGE
;
2221 if (!strcasecmp(argv
[2], "stats"))
2224 return RESULT_SHOWUSAGE
;
2227 /* Flush statistics */
2228 struct dundi_peer
*p
;
2230 AST_LIST_LOCK(&peers
);
2231 AST_LIST_TRAVERSE(&peers
, p
, list
) {
2232 for (x
= 0;x
< DUNDI_TIMING_HISTORY
; x
++) {
2234 free(p
->lookups
[x
]);
2235 p
->lookups
[x
] = NULL
;
2236 p
->lookuptimes
[x
] = 0;
2240 AST_LIST_UNLOCK(&peers
);
2242 ast_db_deltree("dundi/cache", NULL
);
2243 ast_cli(fd
, "DUNDi Cache Flushed\n");
2245 return RESULT_SUCCESS
;
2248 static int dundi_no_debug(int fd
, int argc
, char *argv
[])
2251 return RESULT_SHOWUSAGE
;
2253 ast_cli(fd
, "DUNDi Debugging Disabled\n");
2254 return RESULT_SUCCESS
;
2257 static int dundi_no_store_history(int fd
, int argc
, char *argv
[])
2260 return RESULT_SHOWUSAGE
;
2261 global_storehistory
= 0;
2262 ast_cli(fd
, "DUNDi History Storage Disabled\n");
2263 return RESULT_SUCCESS
;
2266 static char *model2str(int model
)
2269 case DUNDI_MODEL_INBOUND
:
2271 case DUNDI_MODEL_OUTBOUND
:
2273 case DUNDI_MODEL_SYMMETRIC
:
2280 static char *complete_peer_helper(const char *line
, const char *word
, int pos
, int state
, int rpos
)
2284 struct dundi_peer
*p
;
2289 AST_LIST_LOCK(&peers
);
2291 AST_LIST_TRAVERSE(&peers
, p
, list
) {
2292 const char *s
= dundi_eid_to_str(eid_str
, sizeof(eid_str
), &p
->eid
);
2293 if (!strncasecmp(word
, s
, len
) && ++which
> state
) {
2294 ret
= ast_strdup(s
);
2298 AST_LIST_UNLOCK(&peers
);
2302 static char *complete_peer_4(const char *line
, const char *word
, int pos
, int state
)
2304 return complete_peer_helper(line
, word
, pos
, state
, 3);
2307 static int rescomp(const void *a
, const void *b
)
2309 const struct dundi_result
*resa
, *resb
;
2312 if (resa
->weight
< resb
->weight
)
2314 if (resa
->weight
> resb
->weight
)
2319 static void sort_results(struct dundi_result
*results
, int count
)
2321 qsort(results
, count
, sizeof(results
[0]), rescomp
);
2324 static int dundi_do_lookup(int fd
, int argc
, char *argv
[])
2332 struct dundi_result dr
[MAX_RESULTS
];
2333 struct timeval start
;
2334 if ((argc
< 3) || (argc
> 4))
2335 return RESULT_SHOWUSAGE
;
2337 if (!strcasecmp(argv
[3], "bypass"))
2340 return RESULT_SHOWUSAGE
;
2342 ast_copy_string(tmp
, argv
[2], sizeof(tmp
));
2343 context
= strchr(tmp
, '@');
2348 start
= ast_tvnow();
2349 res
= dundi_lookup(dr
, MAX_RESULTS
, NULL
, context
, tmp
, bypass
);
2352 ast_cli(fd
, "DUNDi lookup returned error.\n");
2354 ast_cli(fd
, "DUNDi lookup returned no results.\n");
2356 sort_results(dr
, res
);
2357 for (x
=0;x
<res
;x
++) {
2358 ast_cli(fd
, "%3d. %5d %s/%s (%s)\n", x
+ 1, dr
[x
].weight
, dr
[x
].tech
, dr
[x
].dest
, dundi_flags2str(fs
, sizeof(fs
), dr
[x
].flags
));
2359 ast_cli(fd
, " from %s, expires in %d s\n", dr
[x
].eid_str
, dr
[x
].expiration
);
2361 ast_cli(fd
, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start
));
2362 return RESULT_SUCCESS
;
2365 static int dundi_do_precache(int fd
, int argc
, char *argv
[])
2370 struct timeval start
;
2371 if ((argc
< 3) || (argc
> 3))
2372 return RESULT_SHOWUSAGE
;
2373 ast_copy_string(tmp
, argv
[2], sizeof(tmp
));
2374 context
= strchr(tmp
, '@');
2379 start
= ast_tvnow();
2380 res
= dundi_precache(context
, tmp
);
2383 ast_cli(fd
, "DUNDi precache returned error.\n");
2385 ast_cli(fd
, "DUNDi precache returned no error.\n");
2386 ast_cli(fd
, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start
));
2387 return RESULT_SUCCESS
;
2390 static int dundi_do_query(int fd
, int argc
, char *argv
[])
2396 struct dundi_entity_info dei
;
2397 if ((argc
< 3) || (argc
> 3))
2398 return RESULT_SHOWUSAGE
;
2399 if (dundi_str_to_eid(&eid
, argv
[2])) {
2400 ast_cli(fd
, "'%s' is not a valid EID!\n", argv
[2]);
2401 return RESULT_SHOWUSAGE
;
2403 ast_copy_string(tmp
, argv
[2], sizeof(tmp
));
2404 context
= strchr(tmp
, '@');
2409 res
= dundi_query_eid(&dei
, context
, eid
);
2411 ast_cli(fd
, "DUNDi Query EID returned error.\n");
2413 ast_cli(fd
, "DUNDi Query EID returned no results.\n");
2415 ast_cli(fd
, "DUNDi Query EID succeeded:\n");
2416 ast_cli(fd
, "Department: %s\n", dei
.orgunit
);
2417 ast_cli(fd
, "Organization: %s\n", dei
.org
);
2418 ast_cli(fd
, "City/Locality: %s\n", dei
.locality
);
2419 ast_cli(fd
, "State/Province: %s\n", dei
.stateprov
);
2420 ast_cli(fd
, "Country: %s\n", dei
.country
);
2421 ast_cli(fd
, "E-mail: %s\n", dei
.email
);
2422 ast_cli(fd
, "Phone: %s\n", dei
.phone
);
2423 ast_cli(fd
, "IP Address: %s\n", dei
.ipaddr
);
2425 return RESULT_SUCCESS
;
2428 static int dundi_show_peer(int fd
, int argc
, char *argv
[])
2430 struct dundi_peer
*peer
;
2431 struct permission
*p
;
2437 return RESULT_SHOWUSAGE
;
2438 AST_LIST_LOCK(&peers
);
2439 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
2440 if (!strcasecmp(dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
), argv
[3]))
2444 switch(peer
->order
) {
2449 order
= "Secondary";
2455 order
= "Quartiary";
2460 ast_cli(fd
, "Peer: %s\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
2461 ast_cli(fd
, "Model: %s\n", model2str(peer
->model
));
2462 ast_cli(fd
, "Host: %s\n", peer
->addr
.sin_addr
.s_addr
? ast_inet_ntoa(peer
->addr
.sin_addr
) : "<Unspecified>");
2463 ast_cli(fd
, "Dynamic: %s\n", peer
->dynamic
? "yes" : "no");
2464 ast_cli(fd
, "Reg: %s\n", peer
->registerid
< 0 ? "No" : "Yes");
2465 ast_cli(fd
, "In Key: %s\n", ast_strlen_zero(peer
->inkey
) ? "<None>" : peer
->inkey
);
2466 ast_cli(fd
, "Out Key: %s\n", ast_strlen_zero(peer
->outkey
) ? "<None>" : peer
->outkey
);
2467 if (!AST_LIST_EMPTY(&peer
->include
))
2468 ast_cli(fd
, "Include logic%s:\n", peer
->model
& DUNDI_MODEL_OUTBOUND
? "" : " (IGNORED)");
2469 AST_LIST_TRAVERSE(&peer
->include
, p
, list
)
2470 ast_cli(fd
, "-- %s %s\n", p
->allow
? "include" : "do not include", p
->name
);
2471 if (!AST_LIST_EMPTY(&peer
->permit
))
2472 ast_cli(fd
, "Query logic%s:\n", peer
->model
& DUNDI_MODEL_INBOUND
? "" : " (IGNORED)");
2473 AST_LIST_TRAVERSE(&peer
->permit
, p
, list
)
2474 ast_cli(fd
, "-- %s %s\n", p
->allow
? "permit" : "deny", p
->name
);
2476 for (x
= 0;x
< DUNDI_TIMING_HISTORY
; x
++) {
2477 if (peer
->lookups
[x
]) {
2479 ast_cli(fd
, "Last few query times:\n");
2480 ast_cli(fd
, "-- %d. %s (%d ms)\n", x
+ 1, peer
->lookups
[x
], peer
->lookuptimes
[x
]);
2485 ast_cli(fd
, "Average query time: %d ms\n", peer
->avgms
);
2487 ast_cli(fd
, "No such peer '%s'\n", argv
[3]);
2488 AST_LIST_UNLOCK(&peers
);
2489 return RESULT_SUCCESS
;
2492 static int dundi_show_peers(int fd
, int argc
, char *argv
[])
2494 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2495 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2496 struct dundi_peer
*peer
;
2497 int registeredonly
=0;
2500 int online_peers
= 0;
2501 int offline_peers
= 0;
2502 int unmonitored_peers
= 0;
2503 int total_peers
= 0;
2505 if ((argc
!= 3) && (argc
!= 4) && (argc
!= 5))
2506 return RESULT_SHOWUSAGE
;
2508 if (!strcasecmp(argv
[3], "registered")) {
2511 return RESULT_SHOWUSAGE
;
2513 AST_LIST_LOCK(&peers
);
2514 ast_cli(fd
, FORMAT2
, "EID", "Host", "Model", "AvgTime", "Status");
2515 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
2517 int print_line
= -1;
2520 if (registeredonly
&& !peer
->addr
.sin_addr
.s_addr
)
2523 if (peer
->lastms
< 0) {
2524 strcpy(status
, "UNREACHABLE");
2527 else if (peer
->lastms
> peer
->maxms
) {
2528 snprintf(status
, sizeof(status
), "LAGGED (%d ms)", peer
->lastms
);
2531 else if (peer
->lastms
) {
2532 snprintf(status
, sizeof(status
), "OK (%d ms)", peer
->lastms
);
2536 strcpy(status
, "UNKNOWN");
2540 strcpy(status
, "Unmonitored");
2541 unmonitored_peers
++;
2544 snprintf(avgms
, sizeof(avgms
), "%d ms", peer
->avgms
);
2546 strcpy(avgms
, "Unavail");
2547 snprintf(srch
, sizeof(srch
), FORMAT
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
),
2548 peer
->addr
.sin_addr
.s_addr
? ast_inet_ntoa(peer
->addr
.sin_addr
) : "(Unspecified)",
2549 peer
->dynamic
? "(D)" : "(S)", model2str(peer
->model
), avgms
, status
);
2552 if (!strcasecmp(argv
[3],"include") && strstr(srch
,argv
[4])) {
2554 } else if (!strcasecmp(argv
[3],"exclude") && !strstr(srch
,argv
[4])) {
2556 } else if (!strcasecmp(argv
[3],"begin") && !strncasecmp(srch
,argv
[4],strlen(argv
[4]))) {
2564 ast_cli(fd
, FORMAT
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
),
2565 peer
->addr
.sin_addr
.s_addr
? ast_inet_ntoa(peer
->addr
.sin_addr
) : "(Unspecified)",
2566 peer
->dynamic
? "(D)" : "(S)", model2str(peer
->model
), avgms
, status
);
2569 ast_cli(fd
, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers
, online_peers
, offline_peers
, unmonitored_peers
);
2570 AST_LIST_UNLOCK(&peers
);
2571 return RESULT_SUCCESS
;
2576 static int dundi_show_trans(int fd
, int argc
, char *argv
[])
2578 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2579 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2580 struct dundi_transaction
*trans
;
2582 return RESULT_SHOWUSAGE
;
2583 AST_LIST_LOCK(&peers
);
2584 ast_cli(fd
, FORMAT2
, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2585 AST_LIST_TRAVERSE(&alltrans
, trans
, all
) {
2586 ast_cli(fd
, FORMAT
, ast_inet_ntoa(trans
->addr
.sin_addr
),
2587 ntohs(trans
->addr
.sin_port
), trans
->strans
, trans
->dtrans
, trans
->oseqno
, trans
->iseqno
, trans
->aseqno
);
2589 AST_LIST_UNLOCK(&peers
);
2590 return RESULT_SUCCESS
;
2595 static int dundi_show_entityid(int fd
, int argc
, char *argv
[])
2599 return RESULT_SHOWUSAGE
;
2600 AST_LIST_LOCK(&peers
);
2601 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &global_eid
);
2602 AST_LIST_UNLOCK(&peers
);
2603 ast_cli(fd
, "Global EID for this system is '%s'\n", eid_str
);
2604 return RESULT_SUCCESS
;
2607 static int dundi_show_requests(int fd
, int argc
, char *argv
[])
2609 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2610 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2611 struct dundi_request
*req
;
2614 return RESULT_SHOWUSAGE
;
2615 AST_LIST_LOCK(&peers
);
2616 ast_cli(fd
, FORMAT2
, "Number", "Context", "Root", "Max", "Rsp");
2617 AST_LIST_TRAVERSE(&requests
, req
, list
) {
2618 ast_cli(fd
, FORMAT
, req
->number
, req
->dcontext
,
2619 dundi_eid_zero(&req
->root_eid
) ? "<unspecified>" : dundi_eid_to_str(eidstr
, sizeof(eidstr
), &req
->root_eid
), req
->maxcount
, req
->respcount
);
2621 AST_LIST_UNLOCK(&peers
);
2622 return RESULT_SUCCESS
;
2627 /* Grok-a-dial DUNDi */
2629 static int dundi_show_mappings(int fd
, int argc
, char *argv
[])
2631 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2632 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2633 struct dundi_mapping
*map
;
2636 return RESULT_SHOWUSAGE
;
2637 AST_LIST_LOCK(&peers
);
2638 ast_cli(fd
, FORMAT2
, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2639 AST_LIST_TRAVERSE(&mappings
, map
, list
) {
2640 ast_cli(fd
, FORMAT
, map
->dcontext
, map
->weight
,
2641 ast_strlen_zero(map
->lcontext
) ? "<none>" : map
->lcontext
,
2642 dundi_flags2str(fs
, sizeof(fs
), map
->options
), tech2str(map
->tech
), map
->dest
);
2644 AST_LIST_UNLOCK(&peers
);
2645 return RESULT_SUCCESS
;
2650 static int dundi_show_precache(int fd
, int argc
, char *argv
[])
2652 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2653 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2654 struct dundi_precache_queue
*qe
;
2659 return RESULT_SHOWUSAGE
;
2661 ast_cli(fd
, FORMAT2
, "Number", "Context", "Expiration");
2662 AST_LIST_LOCK(&pcq
);
2663 AST_LIST_TRAVERSE(&pcq
, qe
, list
) {
2664 s
= qe
->expiration
- now
;
2669 ast_cli(fd
, FORMAT
, qe
->number
, qe
->context
, h
,m
,s
);
2671 AST_LIST_UNLOCK(&pcq
);
2673 return RESULT_SUCCESS
;
2678 static char debug_usage
[] =
2679 "Usage: dundi debug\n"
2680 " Enables dumping of DUNDi packets for debugging purposes\n";
2682 static char no_debug_usage
[] =
2683 "Usage: dundi no debug\n"
2684 " Disables dumping of DUNDi packets for debugging purposes\n";
2686 static char store_history_usage
[] =
2687 "Usage: dundi store history\n"
2688 " Enables storing of DUNDi requests and times for debugging\n"
2691 static char no_store_history_usage
[] =
2692 "Usage: dundi no store history\n"
2693 " Disables storing of DUNDi requests and times for debugging\n"
2696 static char show_peers_usage
[] =
2697 "Usage: dundi show peers\n"
2698 " Lists all known DUNDi peers.\n";
2700 static char show_trans_usage
[] =
2701 "Usage: dundi show trans\n"
2702 " Lists all known DUNDi transactions.\n";
2704 static char show_mappings_usage
[] =
2705 "Usage: dundi show mappings\n"
2706 " Lists all known DUNDi mappings.\n";
2708 static char show_precache_usage
[] =
2709 "Usage: dundi show precache\n"
2710 " Lists all known DUNDi scheduled precache updates.\n";
2712 static char show_entityid_usage
[] =
2713 "Usage: dundi show entityid\n"
2714 " Displays the global entityid for this host.\n";
2716 static char show_peer_usage
[] =
2717 "Usage: dundi show peer [peer]\n"
2718 " Provide a detailed description of a specifid DUNDi peer.\n";
2720 static char show_requests_usage
[] =
2721 "Usage: dundi show requests\n"
2722 " Lists all known pending DUNDi requests.\n";
2724 static char lookup_usage
[] =
2725 "Usage: dundi lookup <number>[@context] [bypass]\n"
2726 " Lookup the given number within the given DUNDi context\n"
2727 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2728 "keyword is specified.\n";
2730 static char precache_usage
[] =
2731 "Usage: dundi precache <number>[@context]\n"
2732 " Lookup the given number within the given DUNDi context\n"
2733 "(or e164 if none is specified) and precaches the results to any\n"
2734 "upstream DUNDi push servers.\n";
2736 static char query_usage
[] =
2737 "Usage: dundi query <entity>[@context]\n"
2738 " Attempts to retrieve contact information for a specific\n"
2739 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2740 "e164 if none is specified).\n";
2742 static char flush_usage
[] =
2743 "Usage: dundi flush [stats]\n"
2744 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2745 "'stats' is present, clears timer statistics instead of normal\n"
2748 static struct ast_cli_entry cli_dundi
[] = {
2749 { { "dundi", "debug", NULL
},
2750 dundi_do_debug
, "Enable DUNDi debugging",
2753 { { "dundi", "store", "history", NULL
},
2754 dundi_do_store_history
, "Enable DUNDi historic records",
2755 store_history_usage
},
2757 { { "dundi", "no", "store", "history", NULL
},
2758 dundi_no_store_history
, "Disable DUNDi historic records",
2759 no_store_history_usage
},
2761 { { "dundi", "flush", NULL
},
2762 dundi_flush
, "Flush DUNDi cache",
2765 { { "dundi", "no", "debug", NULL
},
2766 dundi_no_debug
, "Disable DUNDi debugging",
2769 { { "dundi", "show", "peers", NULL
},
2770 dundi_show_peers
, "Show defined DUNDi peers",
2773 { { "dundi", "show", "trans", NULL
},
2774 dundi_show_trans
, "Show active DUNDi transactions",
2777 { { "dundi", "show", "entityid", NULL
},
2778 dundi_show_entityid
, "Display Global Entity ID",
2779 show_entityid_usage
},
2781 { { "dundi", "show", "mappings", NULL
},
2782 dundi_show_mappings
, "Show DUNDi mappings",
2783 show_mappings_usage
},
2785 { { "dundi", "show", "precache", NULL
},
2786 dundi_show_precache
, "Show DUNDi precache",
2787 show_precache_usage
},
2789 { { "dundi", "show", "requests", NULL
},
2790 dundi_show_requests
, "Show DUNDi requests",
2791 show_requests_usage
},
2793 { { "dundi", "show", "peer", NULL
},
2794 dundi_show_peer
, "Show info on a specific DUNDi peer",
2795 show_peer_usage
, complete_peer_4
},
2797 { { "dundi", "lookup", NULL
},
2798 dundi_do_lookup
, "Lookup a number in DUNDi",
2801 { { "dundi", "precache", NULL
},
2802 dundi_do_precache
, "Precache a number in DUNDi",
2805 { { "dundi", "query", NULL
},
2806 dundi_do_query
, "Query a DUNDi EID",
2810 static struct dundi_transaction
*create_transaction(struct dundi_peer
*p
)
2812 struct dundi_transaction
*trans
;
2815 /* Don't allow creation of transactions to non-registered peers */
2816 if (p
&& !p
->addr
.sin_addr
.s_addr
)
2818 tid
= get_trans_id();
2821 trans
= ast_calloc(1, sizeof(*trans
));
2823 if (global_storehistory
) {
2824 trans
->start
= ast_tvnow();
2825 ast_set_flag(trans
, FLAG_STOREHIST
);
2827 trans
->retranstimer
= DUNDI_DEFAULT_RETRANS_TIMER
;
2828 trans
->autokillid
= -1;
2830 apply_peer(trans
, p
);
2831 if (!p
->sentfullkey
)
2832 ast_set_flag(trans
, FLAG_SENDFULLKEY
);
2834 trans
->strans
= tid
;
2835 AST_LIST_INSERT_HEAD(&alltrans
, trans
, all
);
2840 static int dundi_xmit(struct dundi_packet
*pack
)
2844 dundi_showframe(pack
->h
, 0, &pack
->parent
->addr
, pack
->datalen
- sizeof(struct dundi_hdr
));
2845 res
= sendto(netsocket
, pack
->data
, pack
->datalen
, 0, (struct sockaddr
*)&pack
->parent
->addr
, sizeof(pack
->parent
->addr
));
2847 ast_log(LOG_WARNING
, "Failed to transmit to '%s:%d': %s\n",
2848 ast_inet_ntoa(pack
->parent
->addr
.sin_addr
),
2849 ntohs(pack
->parent
->addr
.sin_port
), strerror(errno
));
2856 static void destroy_packet(struct dundi_packet
*pack
, int needfree
)
2859 AST_LIST_REMOVE(&pack
->parent
->packets
, pack
, list
);
2860 AST_SCHED_DEL(sched
, pack
->retransid
);
2865 static void destroy_trans(struct dundi_transaction
*trans
, int fromtimeout
)
2867 struct dundi_peer
*peer
;
2872 if (ast_test_flag(trans
, FLAG_ISREG
| FLAG_ISQUAL
| FLAG_STOREHIST
)) {
2873 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
2874 if (peer
->regtrans
== trans
)
2875 peer
->regtrans
= NULL
;
2876 if (peer
->qualtrans
== trans
) {
2878 if (peer
->lastms
> -1)
2879 ast_log(LOG_NOTICE
, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
2882 ms
= ast_tvdiff_ms(ast_tvnow(), peer
->qualtx
);
2885 if (ms
< peer
->maxms
) {
2886 if ((peer
->lastms
>= peer
->maxms
) || (peer
->lastms
< 0))
2887 ast_log(LOG_NOTICE
, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
2888 } else if (peer
->lastms
< peer
->maxms
) {
2889 ast_log(LOG_NOTICE
, "Peer '%s' has become TOO LAGGED (%d ms)\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
), ms
);
2893 peer
->qualtrans
= NULL
;
2895 if (ast_test_flag(trans
, FLAG_STOREHIST
)) {
2896 if (trans
->parent
&& !ast_strlen_zero(trans
->parent
->number
)) {
2897 if (!dundi_eid_cmp(&trans
->them_eid
, &peer
->eid
)) {
2900 if (peer
->lookups
[DUNDI_TIMING_HISTORY
-1])
2901 free(peer
->lookups
[DUNDI_TIMING_HISTORY
-1]);
2902 for (x
=DUNDI_TIMING_HISTORY
-1;x
>0;x
--) {
2903 peer
->lookuptimes
[x
] = peer
->lookuptimes
[x
-1];
2904 peer
->lookups
[x
] = peer
->lookups
[x
-1];
2905 if (peer
->lookups
[x
]) {
2906 peer
->avgms
+= peer
->lookuptimes
[x
];
2910 peer
->lookuptimes
[0] = ast_tvdiff_ms(ast_tvnow(), trans
->start
);
2911 peer
->lookups
[0] = ast_malloc(strlen(trans
->parent
->number
) + strlen(trans
->parent
->dcontext
) + 2);
2912 if (peer
->lookups
[0]) {
2913 sprintf(peer
->lookups
[0], "%s@%s", trans
->parent
->number
, trans
->parent
->dcontext
);
2914 peer
->avgms
+= peer
->lookuptimes
[0];
2924 if (trans
->parent
) {
2925 /* Unlink from parent if appropriate */
2926 AST_LIST_REMOVE(&trans
->parent
->trans
, trans
, parentlist
);
2927 if (AST_LIST_EMPTY(&trans
->parent
->trans
)) {
2928 /* Wake up sleeper */
2929 if (trans
->parent
->pfds
[1] > -1) {
2930 write(trans
->parent
->pfds
[1], "killa!", 6);
2934 /* Unlink from all trans */
2935 AST_LIST_REMOVE(&alltrans
, trans
, all
);
2936 destroy_packets(&trans
->packets
);
2937 destroy_packets(&trans
->lasttrans
);
2938 AST_SCHED_DEL(sched
, trans
->autokillid
);
2939 if (trans
->thread
) {
2940 /* If used by a thread, mark as dead and be done */
2941 ast_set_flag(trans
, FLAG_DEAD
);
2946 static int dundi_rexmit(const void *data
)
2948 struct dundi_packet
*pack
= (struct dundi_packet
*)data
;
2950 AST_LIST_LOCK(&peers
);
2951 if (pack
->retrans
< 1) {
2952 pack
->retransid
= -1;
2953 if (!ast_test_flag(pack
->parent
, FLAG_ISQUAL
))
2954 ast_log(LOG_NOTICE
, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2955 ast_inet_ntoa(pack
->parent
->addr
.sin_addr
),
2956 ntohs(pack
->parent
->addr
.sin_port
), pack
->h
->oseqno
, ntohs(pack
->h
->strans
));
2957 destroy_trans(pack
->parent
, 1);
2960 /* Decrement retransmission, try again */
2965 AST_LIST_UNLOCK(&peers
);
2969 static int dundi_send(struct dundi_transaction
*trans
, int cmdresp
, int flags
, int final
, struct dundi_ie_data
*ied
)
2971 struct dundi_packet
*pack
;
2975 len
= sizeof(struct dundi_packet
) + sizeof(struct dundi_hdr
) + (ied
? ied
->pos
: 0);
2976 /* Reserve enough space for encryption */
2977 if (ast_test_flag(trans
, FLAG_ENCRYPT
))
2979 pack
= ast_calloc(1, len
);
2981 pack
->h
= (struct dundi_hdr
*)(pack
->data
);
2982 if (cmdresp
!= DUNDI_COMMAND_ACK
) {
2983 pack
->retransid
= ast_sched_add(sched
, trans
->retranstimer
, dundi_rexmit
, pack
);
2984 pack
->retrans
= DUNDI_DEFAULT_RETRANS
- 1;
2985 AST_LIST_INSERT_HEAD(&trans
->packets
, pack
, list
);
2987 pack
->parent
= trans
;
2988 pack
->h
->strans
= htons(trans
->strans
);
2989 pack
->h
->dtrans
= htons(trans
->dtrans
);
2990 pack
->h
->iseqno
= trans
->iseqno
;
2991 pack
->h
->oseqno
= trans
->oseqno
;
2992 pack
->h
->cmdresp
= cmdresp
;
2993 pack
->datalen
= sizeof(struct dundi_hdr
);
2995 memcpy(pack
->h
->ies
, ied
->buf
, ied
->pos
);
2996 pack
->datalen
+= ied
->pos
;
2999 pack
->h
->cmdresp
|= DUNDI_COMMAND_FINAL
;
3000 ast_set_flag(trans
, FLAG_FINAL
);
3002 pack
->h
->cmdflags
= flags
;
3003 if (cmdresp
!= DUNDI_COMMAND_ACK
) {
3005 trans
->oseqno
= trans
->oseqno
% 256;
3007 trans
->aseqno
= trans
->iseqno
;
3008 /* If we have their public key, encrypt */
3009 if (ast_test_flag(trans
, FLAG_ENCRYPT
)) {
3011 case DUNDI_COMMAND_REGREQ
:
3012 case DUNDI_COMMAND_REGRESPONSE
:
3013 case DUNDI_COMMAND_DPDISCOVER
:
3014 case DUNDI_COMMAND_DPRESPONSE
:
3015 case DUNDI_COMMAND_EIDQUERY
:
3016 case DUNDI_COMMAND_EIDRESPONSE
:
3017 case DUNDI_COMMAND_PRECACHERQ
:
3018 case DUNDI_COMMAND_PRECACHERP
:
3020 dundi_showframe(pack
->h
, 2, &trans
->addr
, pack
->datalen
- sizeof(struct dundi_hdr
));
3021 res
= dundi_encrypt(trans
, pack
);
3029 res
= dundi_xmit(pack
);
3031 ast_log(LOG_NOTICE
, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &trans
->them_eid
));
3033 if (cmdresp
== DUNDI_COMMAND_ACK
)
3040 static int do_autokill(const void *data
)
3042 struct dundi_transaction
*trans
= (struct dundi_transaction
*)data
;
3044 ast_log(LOG_NOTICE
, "Transaction to '%s' took too long to ACK, destroying\n",
3045 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &trans
->them_eid
));
3046 trans
->autokillid
= -1;
3047 destroy_trans(trans
, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3051 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data
*ied
, char *context
, dundi_eid
*eid
, dundi_eid
*us
)
3053 struct dundi_peer
*p
;
3054 if (!dundi_eid_cmp(eid
, us
)) {
3055 dundi_ie_append_eid(ied
, DUNDI_IE_EID_DIRECT
, eid
);
3058 AST_LIST_LOCK(&peers
);
3059 AST_LIST_TRAVERSE(&peers
, p
, list
) {
3060 if (!dundi_eid_cmp(&p
->eid
, eid
)) {
3061 if (has_permission(&p
->include
, context
))
3062 dundi_ie_append_eid(ied
, DUNDI_IE_EID_DIRECT
, eid
);
3064 dundi_ie_append_eid(ied
, DUNDI_IE_EID
, eid
);
3069 dundi_ie_append_eid(ied
, DUNDI_IE_EID
, eid
);
3070 AST_LIST_UNLOCK(&peers
);
3073 static int dundi_discover(struct dundi_transaction
*trans
)
3075 struct dundi_ie_data ied
;
3077 if (!trans
->parent
) {
3078 ast_log(LOG_WARNING
, "Tried to discover a transaction with no parent?!?\n");
3081 memset(&ied
, 0, sizeof(ied
));
3082 dundi_ie_append_short(&ied
, DUNDI_IE_VERSION
, DUNDI_DEFAULT_VERSION
);
3083 if (!dundi_eid_zero(&trans
->us_eid
))
3084 dundi_ie_append_eid(&ied
, DUNDI_IE_EID_DIRECT
, &trans
->us_eid
);
3085 for (x
=0;x
<trans
->eidcount
;x
++)
3086 dundi_ie_append_eid_appropriately(&ied
, trans
->parent
->dcontext
, &trans
->eids
[x
], &trans
->us_eid
);
3087 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_NUMBER
, trans
->parent
->number
);
3088 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_CONTEXT
, trans
->parent
->dcontext
);
3089 dundi_ie_append_short(&ied
, DUNDI_IE_TTL
, trans
->ttl
);
3090 if (trans
->parent
->cbypass
)
3091 dundi_ie_append(&ied
, DUNDI_IE_CACHEBYPASS
);
3092 if (trans
->autokilltimeout
)
3093 trans
->autokillid
= ast_sched_add(sched
, trans
->autokilltimeout
, do_autokill
, trans
);
3094 return dundi_send(trans
, DUNDI_COMMAND_DPDISCOVER
, 0, 0, &ied
);
3097 static int precache_trans(struct dundi_transaction
*trans
, struct dundi_mapping
*maps
, int mapcount
, int *minexp
, int *foundanswers
)
3099 struct dundi_ie_data ied
;
3102 int expiration
= dundi_cache_time
;
3104 dundi_eid
*avoid
[1] = { NULL
, };
3105 int direct
[1] = { 0, };
3106 struct dundi_result dr
[MAX_RESULTS
];
3107 struct dundi_hint_metadata hmd
;
3108 if (!trans
->parent
) {
3109 ast_log(LOG_WARNING
, "Tried to discover a transaction with no parent?!?\n");
3112 memset(&hmd
, 0, sizeof(hmd
));
3113 memset(&dr
, 0, sizeof(dr
));
3114 /* Look up the answers we're going to include */
3115 for (x
=0;x
<mapcount
;x
++)
3116 ouranswers
= dundi_lookup_local(dr
, maps
+ x
, trans
->parent
->number
, &trans
->us_eid
, ouranswers
, &hmd
);
3119 for (x
=0;x
<ouranswers
;x
++) {
3120 if (dr
[x
].weight
< max
)
3124 /* If we do not have a canonical result, keep looking */
3125 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
);
3127 /* Append answer in result */
3132 if (ouranswers
> 0) {
3133 *foundanswers
+= ouranswers
;
3134 memset(&ied
, 0, sizeof(ied
));
3135 dundi_ie_append_short(&ied
, DUNDI_IE_VERSION
, DUNDI_DEFAULT_VERSION
);
3136 if (!dundi_eid_zero(&trans
->us_eid
))
3137 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->us_eid
);
3138 for (x
=0;x
<trans
->eidcount
;x
++)
3139 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->eids
[x
]);
3140 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_NUMBER
, trans
->parent
->number
);
3141 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_CONTEXT
, trans
->parent
->dcontext
);
3142 dundi_ie_append_short(&ied
, DUNDI_IE_TTL
, trans
->ttl
);
3143 for (x
=0;x
<ouranswers
;x
++) {
3145 if (dr
[x
].expiration
&& (expiration
> dr
[x
].expiration
))
3146 expiration
= dr
[x
].expiration
;
3147 dundi_ie_append_answer(&ied
, DUNDI_IE_ANSWER
, &dr
[x
].eid
, dr
[x
].techint
, dr
[x
].flags
, dr
[x
].weight
, dr
[x
].dest
);
3149 dundi_ie_append_hint(&ied
, DUNDI_IE_HINT
, hmd
.flags
, hmd
.exten
);
3150 dundi_ie_append_short(&ied
, DUNDI_IE_EXPIRATION
, expiration
);
3151 if (trans
->autokilltimeout
)
3152 trans
->autokillid
= ast_sched_add(sched
, trans
->autokilltimeout
, do_autokill
, trans
);
3153 if (expiration
< *minexp
)
3154 *minexp
= expiration
;
3155 return dundi_send(trans
, DUNDI_COMMAND_PRECACHERQ
, 0, 0, &ied
);
3157 /* Oops, nothing to send... */
3158 destroy_trans(trans
, 0);
3163 static int dundi_query(struct dundi_transaction
*trans
)
3165 struct dundi_ie_data ied
;
3167 if (!trans
->parent
) {
3168 ast_log(LOG_WARNING
, "Tried to query a transaction with no parent?!?\n");
3171 memset(&ied
, 0, sizeof(ied
));
3172 dundi_ie_append_short(&ied
, DUNDI_IE_VERSION
, DUNDI_DEFAULT_VERSION
);
3173 if (!dundi_eid_zero(&trans
->us_eid
))
3174 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->us_eid
);
3175 for (x
=0;x
<trans
->eidcount
;x
++)
3176 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->eids
[x
]);
3177 dundi_ie_append_eid(&ied
, DUNDI_IE_REQEID
, &trans
->parent
->query_eid
);
3178 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_CONTEXT
, trans
->parent
->dcontext
);
3179 dundi_ie_append_short(&ied
, DUNDI_IE_TTL
, trans
->ttl
);
3180 if (trans
->autokilltimeout
)
3181 trans
->autokillid
= ast_sched_add(sched
, trans
->autokilltimeout
, do_autokill
, trans
);
3182 return dundi_send(trans
, DUNDI_COMMAND_EIDQUERY
, 0, 0, &ied
);
3185 static int discover_transactions(struct dundi_request
*dr
)
3187 struct dundi_transaction
*trans
;
3188 AST_LIST_LOCK(&peers
);
3189 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3190 dundi_discover(trans
);
3192 AST_LIST_UNLOCK(&peers
);
3196 static int precache_transactions(struct dundi_request
*dr
, struct dundi_mapping
*maps
, int mapcount
, int *expiration
, int *foundanswers
)
3198 struct dundi_transaction
*trans
;
3200 /* Mark all as "in thread" so they don't disappear */
3201 AST_LIST_LOCK(&peers
);
3202 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3204 ast_log(LOG_WARNING
, "This shouldn't happen, really...\n");
3207 AST_LIST_UNLOCK(&peers
);
3209 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3210 if (!ast_test_flag(trans
, FLAG_DEAD
))
3211 precache_trans(trans
, maps
, mapcount
, expiration
, foundanswers
);
3214 /* Cleanup any that got destroyed in the mean time */
3215 AST_LIST_LOCK(&peers
);
3216 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr
->trans
, trans
, parentlist
) {
3218 if (ast_test_flag(trans
, FLAG_DEAD
)) {
3219 ast_log(LOG_DEBUG
, "Our transaction went away!\n");
3220 /* This is going to remove the transaction from the dundi_request's list, as well
3221 * as the global transactions list */
3222 destroy_trans(trans
, 0);
3225 AST_LIST_TRAVERSE_SAFE_END
3226 AST_LIST_UNLOCK(&peers
);
3231 static int query_transactions(struct dundi_request
*dr
)
3233 struct dundi_transaction
*trans
;
3235 AST_LIST_LOCK(&peers
);
3236 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3239 AST_LIST_UNLOCK(&peers
);
3244 static int optimize_transactions(struct dundi_request
*dr
, int order
)
3246 /* Minimize the message propagation through DUNDi by
3247 alerting the network to hops which should be not be considered */
3248 struct dundi_transaction
*trans
;
3249 struct dundi_peer
*peer
;
3254 AST_LIST_LOCK(&peers
);
3255 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3256 /* Pop off the true root */
3257 if (trans
->eidcount
) {
3258 tmp
= trans
->eids
[--trans
->eidcount
];
3261 tmp
= trans
->us_eid
;
3265 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
3266 if (has_permission(&peer
->include
, dr
->dcontext
) &&
3267 dundi_eid_cmp(&peer
->eid
, &trans
->them_eid
) &&
3268 (peer
->order
<= order
)) {
3269 /* For each other transaction, make sure we don't
3270 ask this EID about the others if they're not
3271 already in the list */
3272 if (!dundi_eid_cmp(&tmp
, &peer
->eid
))
3275 for (x
=0;x
<trans
->eidcount
;x
++) {
3276 if (!dundi_eid_cmp(&trans
->eids
[x
], &peer
->eid
))
3280 if (x
== trans
->eidcount
) {
3281 /* Nope not in the list, if needed, add us at the end since we're the source */
3282 if (trans
->eidcount
< DUNDI_MAX_STACK
- needpush
) {
3283 trans
->eids
[trans
->eidcount
++] = peer
->eid
;
3284 /* Need to insert the real root (or us) at the bottom now as
3285 a requirement now. */
3291 /* If necessary, push the true root back on the end */
3293 trans
->eids
[trans
->eidcount
++] = tmp
;
3295 AST_LIST_UNLOCK(&peers
);
3300 static int append_transaction(struct dundi_request
*dr
, struct dundi_peer
*p
, int ttl
, dundi_eid
*avoid
[])
3302 struct dundi_transaction
*trans
;
3307 /* Ignore if not registered */
3308 if (!p
->addr
.sin_addr
.s_addr
)
3310 if (p
->maxms
&& ((p
->lastms
< 0) || (p
->lastms
>= p
->maxms
)))
3312 if (ast_strlen_zero(dr
->number
))
3313 ast_log(LOG_DEBUG
, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &p
->eid
), dundi_eid_to_str(eid_str2
, sizeof(eid_str2
), &dr
->query_eid
), dr
->dcontext
);
3315 ast_log(LOG_DEBUG
, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &p
->eid
), dr
->number
, dr
->dcontext
);
3316 trans
= create_transaction(p
);
3321 for (x
= 0; avoid
[x
] && (x
< DUNDI_MAX_STACK
); x
++)
3322 trans
->eids
[x
] = *avoid
[x
];
3323 trans
->eidcount
= x
;
3324 AST_LIST_INSERT_HEAD(&dr
->trans
, trans
, parentlist
);
3329 static void cancel_request(struct dundi_request
*dr
)
3331 struct dundi_transaction
*trans
;
3333 AST_LIST_LOCK(&peers
);
3334 while ((trans
= AST_LIST_REMOVE_HEAD(&dr
->trans
, parentlist
))) {
3335 /* Orphan transaction from request */
3336 trans
->parent
= NULL
;
3337 /* Send final cancel */
3338 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
3340 AST_LIST_UNLOCK(&peers
);
3343 static void abort_request(struct dundi_request
*dr
)
3345 struct dundi_transaction
*trans
;
3347 AST_LIST_LOCK(&peers
);
3348 while ((trans
= AST_LIST_FIRST(&dr
->trans
))) {
3349 /* This will remove the transaction from the list */
3350 destroy_trans(trans
, 0);
3352 AST_LIST_UNLOCK(&peers
);
3355 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
[])
3357 struct dundi_peer
*p
;
3363 AST_LIST_LOCK(&peers
);
3364 AST_LIST_TRAVERSE(&peers
, p
, list
) {
3365 if (modeselect
== 1) {
3366 /* Send the precache to push upstreams only! */
3367 pass
= has_permission(&p
->permit
, dr
->dcontext
) && (p
->pcmodel
& DUNDI_MODEL_OUTBOUND
);
3370 /* Normal lookup / EID query */
3371 pass
= has_permission(&p
->include
, dr
->dcontext
);
3372 allowconnect
= p
->model
& DUNDI_MODEL_OUTBOUND
;
3375 if (!dundi_eid_cmp(skip
, &p
->eid
))
3379 if (p
->order
<= order
) {
3380 /* Check order first, then check cache, regardless of
3381 omissions, this gets us more likely to not have an
3383 if((nocache
|| !(res
= cache_lookup(dr
, &p
->eid
, dr
->crc32
, &dr
->expiration
)))) {
3385 /* Make sure we haven't already seen it and that it won't
3386 affect our answer */
3387 for (x
=0;avoid
[x
];x
++) {
3388 if (!dundi_eid_cmp(avoid
[x
], &p
->eid
) || !dundi_eid_cmp(avoid
[x
], &p
->us_eid
)) {
3389 /* If not a direct connection, it affects our answer */
3390 if (directs
&& !directs
[x
])
3391 ast_clear_flag_nonstd(dr
->hmd
, DUNDI_HINT_UNAFFECTED
);
3395 /* Make sure we can ask */
3397 if (!avoid
[x
] && (!blockempty
|| !dundi_eid_zero(&p
->us_eid
))) {
3398 /* Check for a matching or 0 cache entry */
3399 append_transaction(dr
, p
, ttl
, avoid
);
3401 ast_log(LOG_DEBUG
, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), avoid
[x
]));
3405 } else if (!*skipped
|| (p
->order
< *skipped
))
3406 *skipped
= p
->order
;
3409 AST_LIST_UNLOCK(&peers
);
3412 static int register_request(struct dundi_request
*dr
, struct dundi_request
**pending
)
3414 struct dundi_request
*cur
;
3417 AST_LIST_LOCK(&peers
);
3418 AST_LIST_TRAVERSE(&requests
, cur
, list
) {
3420 ast_log(LOG_DEBUG
, "Checking '%s@%s' vs '%s@%s'\n", cur
->dcontext
, cur
->number
,
3421 dr
->dcontext
, dr
->number
);
3422 if (!strcasecmp(cur
->dcontext
, dr
->dcontext
) &&
3423 !strcasecmp(cur
->number
, dr
->number
) &&
3424 (!dundi_eid_cmp(&cur
->root_eid
, &dr
->root_eid
) || (cur
->crc32
== dr
->crc32
))) {
3425 ast_log(LOG_DEBUG
, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n",
3426 cur
->dcontext
, cur
->number
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &cur
->root_eid
), cur
->crc32
);
3433 ast_log(LOG_DEBUG
, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n",
3434 dr
->number
, dr
->dcontext
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &dr
->root_eid
), dr
->crc32
);
3435 /* Go ahead and link us in since nobody else is searching for this */
3436 AST_LIST_INSERT_HEAD(&requests
, dr
, list
);
3439 AST_LIST_UNLOCK(&peers
);
3443 static void unregister_request(struct dundi_request
*dr
)
3445 AST_LIST_LOCK(&peers
);
3446 AST_LIST_REMOVE(&requests
, dr
, list
);
3447 AST_LIST_UNLOCK(&peers
);
3450 static int check_request(struct dundi_request
*dr
)
3452 struct dundi_request
*cur
;
3454 AST_LIST_LOCK(&peers
);
3455 AST_LIST_TRAVERSE(&requests
, cur
, list
) {
3459 AST_LIST_UNLOCK(&peers
);
3464 static unsigned long avoid_crc32(dundi_eid
*avoid
[])
3466 /* Idea is that we're calculating a checksum which is independent of
3467 the order that the EID's are listed in */
3468 unsigned long acrc32
= 0;
3470 for (x
=0;avoid
[x
];x
++) {
3471 /* Order doesn't matter */
3473 acrc32
^= crc32(0L, (unsigned char *)avoid
[x
], sizeof(dundi_eid
));
3479 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
*hmd
, int *expiration
, int cbypass
, int modeselect
, dundi_eid
*skip
, dundi_eid
*avoid
[], int direct
[])
3482 struct dundi_request dr
, *pending
;
3483 dundi_eid
*rooteid
=NULL
;
3491 struct timeval start
;
3493 /* Don't do anthing for a hungup channel */
3494 if (chan
&& chan
->_softhangup
)
3497 ttlms
= DUNDI_FLUFF_TIME
+ ttl
* DUNDI_TTL_TIME
;
3499 for (x
=0;avoid
[x
];x
++)
3501 /* Now perform real check */
3502 memset(&dr
, 0, sizeof(dr
));
3503 if (pipe(dr
.pfds
)) {
3504 ast_log(LOG_WARNING
, "pipe failed: %s\n" , strerror(errno
));
3509 dr
.maxcount
= maxret
;
3510 dr
.expiration
= *expiration
;
3511 dr
.cbypass
= cbypass
;
3512 dr
.crc32
= avoid_crc32(avoid
);
3513 ast_copy_string(dr
.dcontext
, dcontext
? dcontext
: "e164", sizeof(dr
.dcontext
));
3514 ast_copy_string(dr
.number
, number
, sizeof(dr
.number
));
3516 dr
.root_eid
= *rooteid
;
3517 res
= register_request(&dr
, &pending
);
3519 /* Already a request */
3520 if (rooteid
&& !dundi_eid_cmp(&dr
.root_eid
, &pending
->root_eid
)) {
3521 /* This is on behalf of someone else. Go ahead and close this out since
3522 they'll get their answer anyway. */
3523 ast_log(LOG_DEBUG
, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3524 dr
.number
,dr
.dcontext
,dundi_eid_to_str(eid_str
, sizeof(eid_str
), &dr
.root_eid
));
3529 /* Wait for the cache to populate */
3530 ast_log(LOG_DEBUG
, "Waiting for similar request for '%s@%s' for '%s'\n",
3531 dr
.number
,dr
.dcontext
,dundi_eid_to_str(eid_str
, sizeof(eid_str
), &pending
->root_eid
));
3532 start
= ast_tvnow();
3533 while(check_request(pending
) && (ast_tvdiff_ms(ast_tvnow(), start
) < ttlms
) && (!chan
|| !chan
->_softhangup
)) {
3534 /* XXX Would be nice to have a way to poll/select here XXX */
3535 /* XXX this is a busy wait loop!!! */
3538 /* Continue on as normal, our cache should kick in */
3541 /* Create transactions */
3546 build_transactions(&dr
, ttl
, order
, &foundcache
, &skipped
, blockempty
, cbypass
, modeselect
, skip
, avoid
, direct
);
3547 } while (skipped
&& !foundcache
&& AST_LIST_EMPTY(&dr
.trans
));
3548 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3549 do this earlier because we didn't know if we were going to have transactions
3552 ast_set_flag_nonstd(hmd
, DUNDI_HINT_TTL_EXPIRED
);
3554 unregister_request(&dr
);
3560 /* Optimize transactions */
3561 optimize_transactions(&dr
, order
);
3562 /* Actually perform transactions */
3563 discover_transactions(&dr
);
3564 /* Wait for transaction to come back */
3565 start
= ast_tvnow();
3566 while (!AST_LIST_EMPTY(&dr
.trans
) && (ast_tvdiff_ms(ast_tvnow(), start
) < ttlms
) && (!chan
|| !chan
->_softhangup
)) {
3568 ast_waitfor_n_fd(dr
.pfds
, 1, &ms
, NULL
);
3570 if (chan
&& chan
->_softhangup
)
3571 ast_log(LOG_DEBUG
, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan
->name
, dr
.number
, dr
.dcontext
);
3572 cancel_request(&dr
);
3573 unregister_request(&dr
);
3575 *expiration
= dr
.expiration
;
3581 int dundi_lookup(struct dundi_result
*result
, int maxret
, struct ast_channel
*chan
, const char *dcontext
, const char *number
, int cbypass
)
3583 struct dundi_hint_metadata hmd
;
3584 dundi_eid
*avoid
[1] = { NULL
, };
3585 int direct
[1] = { 0, };
3586 int expiration
= dundi_cache_time
;
3587 memset(&hmd
, 0, sizeof(hmd
));
3588 hmd
.flags
= DUNDI_HINT_DONT_ASK
| DUNDI_HINT_UNAFFECTED
;
3589 return dundi_lookup_internal(result
, maxret
, chan
, dcontext
, number
, dundi_ttl
, 0, &hmd
, &expiration
, cbypass
, 0, NULL
, avoid
, direct
);
3592 static void reschedule_precache(const char *number
, const char *context
, int expiration
)
3595 struct dundi_precache_queue
*qe
, *prev
;
3597 AST_LIST_LOCK(&pcq
);
3598 AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq
, qe
, list
) {
3599 if (!strcmp(number
, qe
->number
) && !strcasecmp(context
, qe
->context
)) {
3600 AST_LIST_REMOVE_CURRENT(&pcq
, list
);
3604 AST_LIST_TRAVERSE_SAFE_END
3607 len
+= strlen(number
) + 1;
3608 len
+= strlen(context
) + 1;
3609 if (!(qe
= ast_calloc(1, len
))) {
3610 AST_LIST_UNLOCK(&pcq
);
3613 strcpy(qe
->number
, number
);
3614 qe
->context
= qe
->number
+ strlen(number
) + 1;
3615 strcpy(qe
->context
, context
);
3617 time(&qe
->expiration
);
3618 qe
->expiration
+= expiration
;
3619 if ((prev
= AST_LIST_FIRST(&pcq
))) {
3620 while (AST_LIST_NEXT(prev
, list
) && ((AST_LIST_NEXT(prev
, list
))->expiration
<= qe
->expiration
))
3621 prev
= AST_LIST_NEXT(prev
, list
);
3622 AST_LIST_INSERT_AFTER(&pcq
, prev
, qe
, list
);
3624 AST_LIST_INSERT_HEAD(&pcq
, qe
, list
);
3625 AST_LIST_UNLOCK(&pcq
);
3628 static void dundi_precache_full(void)
3630 struct dundi_mapping
*cur
;
3631 struct ast_context
*con
;
3632 struct ast_exten
*e
;
3634 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
3635 ast_log(LOG_NOTICE
, "Should precache context '%s'\n", cur
->dcontext
);
3636 ast_rdlock_contexts();
3637 con
= ast_walk_contexts(NULL
);
3639 if (!strcasecmp(cur
->lcontext
, ast_get_context_name(con
))) {
3640 /* Found the match, now queue them all up */
3641 ast_lock_context(con
);
3642 e
= ast_walk_context_extensions(con
, NULL
);
3644 reschedule_precache(ast_get_extension_name(e
), cur
->dcontext
, 0);
3645 e
= ast_walk_context_extensions(con
, e
);
3647 ast_unlock_context(con
);
3649 con
= ast_walk_contexts(con
);
3651 ast_unlock_contexts();
3655 static int dundi_precache_internal(const char *context
, const char *number
, int ttl
, dundi_eid
*avoids
[])
3657 struct dundi_request dr
;
3658 struct dundi_hint_metadata hmd
;
3659 struct dundi_result dr2
[MAX_RESULTS
];
3660 struct timeval start
;
3661 struct dundi_mapping
*maps
= NULL
, *cur
;
3664 int foundcache
, skipped
, ttlms
, ms
;
3667 ast_log(LOG_DEBUG
, "Precache internal (%s@%s)!\n", number
, context
);
3669 AST_LIST_LOCK(&peers
);
3670 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
3671 if (!strcasecmp(cur
->dcontext
, context
))
3675 maps
= alloca(nummaps
* sizeof(*maps
));
3678 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
3679 if (!strcasecmp(cur
->dcontext
, context
))
3680 maps
[nummaps
++] = *cur
;
3684 AST_LIST_UNLOCK(&peers
);
3685 if (!nummaps
|| !maps
)
3687 ttlms
= DUNDI_FLUFF_TIME
+ ttl
* DUNDI_TTL_TIME
;
3688 memset(&dr2
, 0, sizeof(dr2
));
3689 memset(&dr
, 0, sizeof(dr
));
3690 memset(&hmd
, 0, sizeof(hmd
));
3692 ast_copy_string(dr
.number
, number
, sizeof(dr
.number
));
3693 ast_copy_string(dr
.dcontext
, context
? context
: "e164", sizeof(dr
.dcontext
));
3694 dr
.maxcount
= MAX_RESULTS
;
3695 dr
.expiration
= dundi_cache_time
;
3697 dr
.pfds
[0] = dr
.pfds
[1] = -1;
3699 build_transactions(&dr
, ttl
, 0, &foundcache
, &skipped
, 0, 1, 1, NULL
, avoids
, NULL
);
3700 optimize_transactions(&dr
, 0);
3702 precache_transactions(&dr
, maps
, nummaps
, &dr
.expiration
, &foundanswers
);
3704 if (dr
.expiration
> 0)
3705 reschedule_precache(dr
.number
, dr
.dcontext
, dr
.expiration
);
3707 ast_log(LOG_NOTICE
, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr
.expiration
, dr
.number
, dr
.dcontext
);
3709 start
= ast_tvnow();
3710 while (!AST_LIST_EMPTY(&dr
.trans
) && (ast_tvdiff_ms(ast_tvnow(), start
) < ttlms
)) {
3711 if (dr
.pfds
[0] > -1) {
3713 ast_waitfor_n_fd(dr
.pfds
, 1, &ms
, NULL
);
3717 cancel_request(&dr
);
3718 if (dr
.pfds
[0] > -1) {
3725 int dundi_precache(const char *context
, const char *number
)
3727 dundi_eid
*avoid
[1] = { NULL
, };
3728 return dundi_precache_internal(context
, number
, dundi_ttl
, avoid
);
3731 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
[])
3734 struct dundi_request dr
;
3735 dundi_eid
*rooteid
=NULL
;
3740 struct timeval start
;
3742 ttlms
= DUNDI_FLUFF_TIME
+ ttl
* DUNDI_TTL_TIME
;
3744 for (x
=0;avoid
[x
];x
++)
3746 /* Now perform real check */
3747 memset(&dr
, 0, sizeof(dr
));
3750 dr
.pfds
[0] = dr
.pfds
[1] = -1;
3751 ast_copy_string(dr
.dcontext
, dcontext
? dcontext
: "e164", sizeof(dr
.dcontext
));
3752 memcpy(&dr
.query_eid
, eid
, sizeof(dr
.query_eid
));
3754 dr
.root_eid
= *rooteid
;
3755 /* Create transactions */
3756 build_transactions(&dr
, ttl
, 9999, &foundcache
, &skipped
, blockempty
, 0, 0, NULL
, avoid
, NULL
);
3758 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3759 do this earlier because we didn't know if we were going to have transactions
3762 ast_set_flag_nonstd(hmd
, DUNDI_HINT_TTL_EXPIRED
);
3766 /* Optimize transactions */
3767 optimize_transactions(&dr
, 9999);
3768 /* Actually perform transactions */
3769 query_transactions(&dr
);
3770 /* Wait for transaction to come back */
3771 start
= ast_tvnow();
3772 while (!AST_LIST_EMPTY(&dr
.trans
) && (ast_tvdiff_ms(ast_tvnow(), start
) < ttlms
))
3778 int dundi_query_eid(struct dundi_entity_info
*dei
, const char *dcontext
, dundi_eid eid
)
3780 dundi_eid
*avoid
[1] = { NULL
, };
3781 struct dundi_hint_metadata hmd
;
3782 memset(&hmd
, 0, sizeof(hmd
));
3783 return dundi_query_eid_internal(dei
, dcontext
, &eid
, &hmd
, dundi_ttl
, 0, avoid
);
3786 static int dundifunc_read(struct ast_channel
*chan
, char *cmd
, char *num
, char *buf
, size_t len
)
3793 struct ast_module_user
*u
;
3794 struct dundi_result dr
[MAX_RESULTS
];
3798 if (ast_strlen_zero(num
)) {
3799 ast_log(LOG_WARNING
, "DUNDILOOKUP requires an argument (number)\n");
3803 u
= ast_module_user_add(chan
);
3805 context
= strchr(num
, '|');
3808 opts
= strchr(context
, '|');
3811 if (strchr(opts
, 'b'))
3816 if (ast_strlen_zero(context
))
3819 results
= dundi_lookup(dr
, MAX_RESULTS
, NULL
, context
, num
, bypass
);
3821 sort_results(dr
, results
);
3822 for (x
= 0; x
< results
; x
++) {
3823 if (ast_test_flag(dr
+ x
, DUNDI_FLAG_EXISTS
)) {
3824 snprintf(buf
, len
, "%s/%s", dr
[x
].tech
, dr
[x
].dest
);
3830 ast_module_user_remove(u
);
3836 * \ingroup functions
3839 static struct ast_custom_function dundi_function
= {
3840 .name
= "DUNDILOOKUP",
3841 .synopsis
= "Do a DUNDi lookup of a phone number.",
3842 .syntax
= "DUNDILOOKUP(number[|context[|options]])",
3843 .desc
= "This will do a DUNDi lookup of the given phone number.\n"
3844 "If no context is given, the default will be e164. The result of\n"
3845 "this function will the Technology/Resource found in the DUNDi\n"
3846 "lookup. If no results were found, the result will be blank.\n"
3847 "If the 'b' option is specified, the internal DUNDi cache will\n"
3849 .read
= dundifunc_read
,
3852 static void mark_peers(void)
3854 struct dundi_peer
*peer
;
3855 AST_LIST_LOCK(&peers
);
3856 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
3859 AST_LIST_UNLOCK(&peers
);
3862 static void mark_mappings(void)
3864 struct dundi_mapping
*map
;
3866 AST_LIST_LOCK(&peers
);
3867 AST_LIST_TRAVERSE(&mappings
, map
, list
) {
3870 AST_LIST_UNLOCK(&peers
);
3873 static void destroy_permissions(struct permissionlist
*permlist
)
3875 struct permission
*perm
;
3877 while ((perm
= AST_LIST_REMOVE_HEAD(permlist
, list
)))
3881 static void destroy_peer(struct dundi_peer
*peer
)
3883 AST_SCHED_DEL(sched
, peer
->registerid
);
3885 destroy_trans(peer
->regtrans
, 0);
3886 AST_SCHED_DEL(sched
, peer
->qualifyid
);
3887 destroy_permissions(&peer
->permit
);
3888 destroy_permissions(&peer
->include
);
3892 static void destroy_map(struct dundi_mapping
*map
)
3897 static void prune_peers(void)
3899 struct dundi_peer
*peer
;
3901 AST_LIST_LOCK(&peers
);
3902 AST_LIST_TRAVERSE_SAFE_BEGIN(&peers
, peer
, list
) {
3904 AST_LIST_REMOVE_CURRENT(&peers
, list
);
3908 AST_LIST_TRAVERSE_SAFE_END
3909 AST_LIST_UNLOCK(&peers
);
3912 static void prune_mappings(void)
3914 struct dundi_mapping
*map
;
3916 AST_LIST_LOCK(&peers
);
3917 AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings
, map
, list
) {
3919 AST_LIST_REMOVE_CURRENT(&mappings
, list
);
3923 AST_LIST_TRAVERSE_SAFE_END
3924 AST_LIST_UNLOCK(&peers
);
3927 static void append_permission(struct permissionlist
*permlist
, char *s
, int allow
)
3929 struct permission
*perm
;
3931 if (!(perm
= ast_calloc(1, sizeof(*perm
) + strlen(s
) + 1)))
3934 strcpy(perm
->name
, s
);
3935 perm
->allow
= allow
;
3937 AST_LIST_INSERT_TAIL(permlist
, perm
, list
);
3940 #define MAX_OPTS 128
3942 static void build_mapping(char *name
, char *value
)
3944 char *t
, *fields
[MAX_OPTS
];
3945 struct dundi_mapping
*map
;
3949 t
= ast_strdupa(value
);
3951 AST_LIST_TRAVERSE(&mappings
, map
, list
) {
3952 /* Find a double match */
3953 if (!strcasecmp(map
->dcontext
, name
) &&
3954 (!strncasecmp(map
->lcontext
, value
, strlen(map
->lcontext
)) &&
3955 (!value
[strlen(map
->lcontext
)] ||
3956 (value
[strlen(map
->lcontext
)] == ','))))
3960 if (!(map
= ast_calloc(1, sizeof(*map
))))
3962 AST_LIST_INSERT_HEAD(&mappings
, map
, list
);
3966 memset(fields
, 0, sizeof(fields
));
3968 while (t
&& x
< MAX_OPTS
) {
3975 } /* Russell was here, arrrr! */
3976 if ((x
== 1) && ast_strlen_zero(fields
[0])) {
3977 /* Placeholder mapping */
3978 ast_copy_string(map
->dcontext
, name
, sizeof(map
->dcontext
));
3980 } else if (x
>= 4) {
3981 ast_copy_string(map
->dcontext
, name
, sizeof(map
->dcontext
));
3982 ast_copy_string(map
->lcontext
, fields
[0], sizeof(map
->lcontext
));
3983 if ((sscanf(fields
[1], "%d", &map
->weight
) == 1) && (map
->weight
>= 0) && (map
->weight
< 60000)) {
3984 ast_copy_string(map
->dest
, fields
[3], sizeof(map
->dest
));
3985 if ((map
->tech
= str2tech(fields
[2]))) {
3989 ast_log(LOG_WARNING
, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields
[1], map
->dcontext
, map
->lcontext
);
3991 for (y
= 4;y
< x
; y
++) {
3992 if (!strcasecmp(fields
[y
], "nounsolicited"))
3993 map
->options
|= DUNDI_FLAG_NOUNSOLICITED
;
3994 else if (!strcasecmp(fields
[y
], "nocomunsolicit"))
3995 map
->options
|= DUNDI_FLAG_NOCOMUNSOLICIT
;
3996 else if (!strcasecmp(fields
[y
], "residential"))
3997 map
->options
|= DUNDI_FLAG_RESIDENTIAL
;
3998 else if (!strcasecmp(fields
[y
], "commercial"))
3999 map
->options
|= DUNDI_FLAG_COMMERCIAL
;
4000 else if (!strcasecmp(fields
[y
], "mobile"))
4001 map
->options
|= DUNDI_FLAG_MOBILE
;
4002 else if (!strcasecmp(fields
[y
], "nopartial"))
4003 map
->options
|= DUNDI_FLAG_INTERNAL_NOPARTIAL
;
4005 ast_log(LOG_WARNING
, "Don't know anything about option '%s'\n", fields
[y
]);
4008 ast_log(LOG_WARNING
, "Expected at least %d arguments in map, but got only %d\n", 4, x
);
4011 /* \note Called with the peers list already locked */
4012 static int do_register(const void *data
)
4014 struct dundi_ie_data ied
;
4015 struct dundi_peer
*peer
= (struct dundi_peer
*)data
;
4018 ast_log(LOG_DEBUG
, "Register us as '%s' to '%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->us_eid
), dundi_eid_to_str(eid_str2
, sizeof(eid_str2
), &peer
->eid
));
4019 peer
->registerid
= ast_sched_add(sched
, default_expiration
* 1000, do_register
, data
);
4020 /* Destroy old transaction if there is one */
4022 destroy_trans(peer
->regtrans
, 0);
4023 peer
->regtrans
= create_transaction(peer
);
4024 if (peer
->regtrans
) {
4025 ast_set_flag(peer
->regtrans
, FLAG_ISREG
);
4026 memset(&ied
, 0, sizeof(ied
));
4027 dundi_ie_append_short(&ied
, DUNDI_IE_VERSION
, DUNDI_DEFAULT_VERSION
);
4028 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &peer
->regtrans
->us_eid
);
4029 dundi_ie_append_short(&ied
, DUNDI_IE_EXPIRATION
, default_expiration
);
4030 dundi_send(peer
->regtrans
, DUNDI_COMMAND_REGREQ
, 0, 0, &ied
);
4033 ast_log(LOG_NOTICE
, "Unable to create new transaction for registering to '%s'!\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4038 static int do_qualify(const void *data
)
4040 struct dundi_peer
*peer
= (struct dundi_peer
*)data
;
4041 peer
->qualifyid
= -1;
4042 qualify_peer(peer
, 0);
4046 static void qualify_peer(struct dundi_peer
*peer
, int schedonly
)
4049 AST_SCHED_DEL(sched
, peer
->qualifyid
);
4050 if (peer
->qualtrans
)
4051 destroy_trans(peer
->qualtrans
, 0);
4052 peer
->qualtrans
= NULL
;
4053 if (peer
->maxms
> 0) {
4055 if (peer
->lastms
< 0)
4059 peer
->qualifyid
= ast_sched_add(sched
, when
, do_qualify
, peer
);
4061 peer
->qualtrans
= create_transaction(peer
);
4062 if (peer
->qualtrans
) {
4063 peer
->qualtx
= ast_tvnow();
4064 ast_set_flag(peer
->qualtrans
, FLAG_ISQUAL
);
4065 dundi_send(peer
->qualtrans
, DUNDI_COMMAND_NULL
, 0, 1, NULL
);
4069 static void populate_addr(struct dundi_peer
*peer
, dundi_eid
*eid
)
4075 dundi_eid_to_str(eid_str
, sizeof(eid_str
), eid
);
4076 if (!ast_db_get("dundi/dpeers", eid_str
, data
, sizeof(data
))) {
4077 c
= strchr(data
, ':');
4081 if (sscanf(c
, "%d:%d", &port
, &expire
) == 2) {
4083 inet_aton(data
, &peer
->addr
.sin_addr
);
4084 peer
->addr
.sin_family
= AF_INET
;
4085 peer
->addr
.sin_port
= htons(port
);
4086 peer
->registerexpire
= ast_sched_add(sched
, (expire
+ 10) * 1000, do_register_expire
, peer
);
4093 static void build_peer(dundi_eid
*eid
, struct ast_variable
*v
, int *globalpcmode
)
4095 struct dundi_peer
*peer
;
4096 struct ast_hostent he
;
4102 AST_LIST_LOCK(&peers
);
4103 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
4104 if (!dundi_eid_cmp(&peer
->eid
, eid
)) {
4109 /* Add us into the list */
4110 if (!(peer
= ast_calloc(1, sizeof(*peer
)))) {
4111 AST_LIST_UNLOCK(&peers
);
4114 peer
->registerid
= -1;
4115 peer
->registerexpire
= -1;
4116 peer
->qualifyid
= -1;
4117 peer
->addr
.sin_family
= AF_INET
;
4118 peer
->addr
.sin_port
= htons(DUNDI_PORT
);
4119 populate_addr(peer
, eid
);
4120 AST_LIST_INSERT_HEAD(&peers
, peer
, list
);
4124 peer
->us_eid
= global_eid
;
4125 destroy_permissions(&peer
->permit
);
4126 destroy_permissions(&peer
->include
);
4127 AST_SCHED_DEL(sched
, peer
->registerid
);
4128 for (; v
; v
= v
->next
) {
4129 if (!strcasecmp(v
->name
, "inkey")) {
4130 ast_copy_string(peer
->inkey
, v
->value
, sizeof(peer
->inkey
));
4131 } else if (!strcasecmp(v
->name
, "outkey")) {
4132 ast_copy_string(peer
->outkey
, v
->value
, sizeof(peer
->outkey
));
4133 } else if (!strcasecmp(v
->name
, "host")) {
4134 if (!strcasecmp(v
->value
, "dynamic")) {
4137 hp
= ast_gethostbyname(v
->value
, &he
);
4139 memcpy(&peer
->addr
.sin_addr
, hp
->h_addr
, sizeof(peer
->addr
.sin_addr
));
4142 ast_log(LOG_WARNING
, "Unable to find host '%s' at line %d\n", v
->value
, v
->lineno
);
4146 } else if (!strcasecmp(v
->name
, "ustothem")) {
4147 if (!dundi_str_to_eid(&testeid
, v
->value
))
4148 peer
->us_eid
= testeid
;
4150 ast_log(LOG_WARNING
, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v
->value
, v
->lineno
);
4151 } else if (!strcasecmp(v
->name
, "include")) {
4152 append_permission(&peer
->include
, v
->value
, 1);
4153 } else if (!strcasecmp(v
->name
, "permit")) {
4154 append_permission(&peer
->permit
, v
->value
, 1);
4155 } else if (!strcasecmp(v
->name
, "noinclude")) {
4156 append_permission(&peer
->include
, v
->value
, 0);
4157 } else if (!strcasecmp(v
->name
, "deny")) {
4158 append_permission(&peer
->permit
, v
->value
, 0);
4159 } else if (!strcasecmp(v
->name
, "register")) {
4160 needregister
= ast_true(v
->value
);
4161 } else if (!strcasecmp(v
->name
, "order")) {
4162 if (!strcasecmp(v
->value
, "primary"))
4164 else if (!strcasecmp(v
->value
, "secondary"))
4166 else if (!strcasecmp(v
->value
, "tertiary"))
4168 else if (!strcasecmp(v
->value
, "quartiary"))
4171 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
);
4173 } else if (!strcasecmp(v
->name
, "qualify")) {
4174 if (!strcasecmp(v
->value
, "no")) {
4176 } else if (!strcasecmp(v
->value
, "yes")) {
4177 peer
->maxms
= DEFAULT_MAXMS
;
4178 } else if (sscanf(v
->value
, "%d", &peer
->maxms
) != 1) {
4179 ast_log(LOG_WARNING
, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4180 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
), v
->lineno
);
4183 } else if (!strcasecmp(v
->name
, "model")) {
4184 if (!strcasecmp(v
->value
, "inbound"))
4185 peer
->model
= DUNDI_MODEL_INBOUND
;
4186 else if (!strcasecmp(v
->value
, "outbound"))
4187 peer
->model
= DUNDI_MODEL_OUTBOUND
;
4188 else if (!strcasecmp(v
->value
, "symmetric"))
4189 peer
->model
= DUNDI_MODEL_SYMMETRIC
;
4190 else if (!strcasecmp(v
->value
, "none"))
4193 ast_log(LOG_WARNING
, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4194 v
->value
, v
->lineno
);
4196 } else if (!strcasecmp(v
->name
, "precache")) {
4197 if (!strcasecmp(v
->value
, "inbound"))
4198 peer
->pcmodel
= DUNDI_MODEL_INBOUND
;
4199 else if (!strcasecmp(v
->value
, "outbound"))
4200 peer
->pcmodel
= DUNDI_MODEL_OUTBOUND
;
4201 else if (!strcasecmp(v
->value
, "symmetric"))
4202 peer
->pcmodel
= DUNDI_MODEL_SYMMETRIC
;
4203 else if (!strcasecmp(v
->value
, "none"))
4206 ast_log(LOG_WARNING
, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4207 v
->value
, v
->lineno
);
4211 (*globalpcmode
) |= peer
->pcmodel
;
4212 if (!peer
->model
&& !peer
->pcmodel
) {
4213 ast_log(LOG_WARNING
, "Peer '%s' lacks a model or pcmodel, discarding!\n",
4214 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4216 } else if ((peer
->model
& DUNDI_MODEL_INBOUND
) && (peer
->pcmodel
& DUNDI_MODEL_OUTBOUND
)) {
4217 ast_log(LOG_WARNING
, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4218 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4220 } else if ((peer
->model
& DUNDI_MODEL_OUTBOUND
) && (peer
->pcmodel
& DUNDI_MODEL_INBOUND
)) {
4221 ast_log(LOG_WARNING
, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4222 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4224 } else if (!AST_LIST_EMPTY(&peer
->include
) && !(peer
->model
& DUNDI_MODEL_OUTBOUND
) && !(peer
->pcmodel
& DUNDI_MODEL_INBOUND
)) {
4225 ast_log(LOG_WARNING
, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4226 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4227 } else if (!AST_LIST_EMPTY(&peer
->permit
) && !(peer
->model
& DUNDI_MODEL_INBOUND
) && !(peer
->pcmodel
& DUNDI_MODEL_OUTBOUND
)) {
4228 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",
4229 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4232 peer
->registerid
= ast_sched_add(sched
, 2000, do_register
, peer
);
4234 qualify_peer(peer
, 1);
4236 AST_LIST_UNLOCK(&peers
);
4239 static int dundi_helper(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *data
, int flag
)
4241 struct dundi_result results
[MAX_RESULTS
];
4245 if (!strncasecmp(context
, "macro-", 6)) {
4247 ast_log(LOG_NOTICE
, "Can't use macro mode without a channel!\n");
4250 /* If done as a macro, use macro extension */
4251 if (!strcasecmp(exten
, "s")) {
4252 exten
= pbx_builtin_getvar_helper(chan
, "ARG1");
4253 if (ast_strlen_zero(exten
))
4254 exten
= chan
->macroexten
;
4255 if (ast_strlen_zero(exten
))
4256 exten
= chan
->exten
;
4257 if (ast_strlen_zero(exten
)) {
4258 ast_log(LOG_WARNING
, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4262 if (ast_strlen_zero(data
))
4265 if (ast_strlen_zero(data
))
4268 res
= dundi_lookup(results
, MAX_RESULTS
, chan
, data
, exten
, 0);
4269 for (x
=0;x
<res
;x
++) {
4270 if (ast_test_flag(results
+ x
, flag
))
4273 if (found
>= priority
)
4278 static int dundi_exists(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *callerid
, const char *data
)
4280 return dundi_helper(chan
, context
, exten
, priority
, data
, DUNDI_FLAG_EXISTS
);
4283 static int dundi_canmatch(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *callerid
, const char *data
)
4285 return dundi_helper(chan
, context
, exten
, priority
, data
, DUNDI_FLAG_CANMATCH
);
4288 static int dundi_exec(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *callerid
, const char *data
)
4290 struct dundi_result results
[MAX_RESULTS
];
4294 struct ast_app
*dial
;
4296 if (!strncasecmp(context
, "macro-", 6)) {
4298 ast_log(LOG_NOTICE
, "Can't use macro mode without a channel!\n");
4301 /* If done as a macro, use macro extension */
4302 if (!strcasecmp(exten
, "s")) {
4303 exten
= pbx_builtin_getvar_helper(chan
, "ARG1");
4304 if (ast_strlen_zero(exten
))
4305 exten
= chan
->macroexten
;
4306 if (ast_strlen_zero(exten
))
4307 exten
= chan
->exten
;
4308 if (ast_strlen_zero(exten
)) {
4309 ast_log(LOG_WARNING
, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4313 if (ast_strlen_zero(data
))
4316 if (ast_strlen_zero(data
))
4319 res
= dundi_lookup(results
, MAX_RESULTS
, chan
, data
, exten
, 0);
4321 sort_results(results
, res
);
4322 for (x
=0;x
<res
;x
++) {
4323 if (ast_test_flag(results
+ x
, DUNDI_FLAG_EXISTS
)) {
4331 snprintf(req
, sizeof(req
), "%s/%s", results
[x
].tech
, results
[x
].dest
);
4332 dial
= pbx_findapp("Dial");
4334 res
= pbx_exec(chan
, dial
, req
);
4340 static int dundi_matchmore(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *callerid
, const char *data
)
4342 return dundi_helper(chan
, context
, exten
, priority
, data
, DUNDI_FLAG_MATCHMORE
);
4345 static struct ast_switch dundi_switch
=
4348 description
: "DUNDi Discovered Dialplan Switch",
4349 exists
: dundi_exists
,
4350 canmatch
: dundi_canmatch
,
4352 matchmore
: dundi_matchmore
,
4355 static int set_config(char *config_file
, struct sockaddr_in
* sin
)
4357 struct ast_config
*cfg
;
4358 struct ast_variable
*v
;
4362 char hn
[MAXHOSTNAMELEN
] = "";
4363 struct ast_hostent he
;
4365 struct sockaddr_in sin2
;
4366 static int last_port
= 0;
4367 int globalpcmodel
= 0;
4370 dundi_ttl
= DUNDI_DEFAULT_TTL
;
4371 dundi_cache_time
= DUNDI_DEFAULT_CACHE_TIME
;
4374 cfg
= ast_config_load(config_file
);
4377 ast_log(LOG_ERROR
, "Unable to load config %s\n", config_file
);
4381 if (!gethostname(hn
, sizeof(hn
)-1)) {
4382 hp
= ast_gethostbyname(hn
, &he
);
4384 memcpy(&sin2
.sin_addr
, hp
->h_addr
, sizeof(sin2
.sin_addr
));
4385 ast_copy_string(ipaddr
, ast_inet_ntoa(sin2
.sin_addr
), sizeof(ipaddr
));
4387 ast_log(LOG_WARNING
, "Unable to look up host '%s'\n", hn
);
4389 ast_log(LOG_WARNING
, "Unable to get host name!\n");
4390 AST_LIST_LOCK(&peers
);
4392 global_storehistory
= 0;
4393 ast_copy_string(secretpath
, "dundi", sizeof(secretpath
));
4394 v
= ast_variable_browse(cfg
, "general");
4396 if (!strcasecmp(v
->name
, "port")){
4397 sin
->sin_port
= ntohs(atoi(v
->value
));
4399 last_port
=sin
->sin_port
;
4400 } else if(sin
->sin_port
!= last_port
)
4401 ast_log(LOG_WARNING
, "change to port ignored until next asterisk re-start\n");
4402 } else if (!strcasecmp(v
->name
, "bindaddr")) {
4404 struct ast_hostent he
;
4405 hp
= ast_gethostbyname(v
->value
, &he
);
4407 memcpy(&sin
->sin_addr
, hp
->h_addr
, sizeof(sin
->sin_addr
));
4409 ast_log(LOG_WARNING
, "Invalid host/IP '%s'\n", v
->value
);
4410 } else if (!strcasecmp(v
->name
, "authdebug")) {
4411 authdebug
= ast_true(v
->value
);
4412 } else if (!strcasecmp(v
->name
, "ttl")) {
4413 if ((sscanf(v
->value
, "%d", &x
) == 1) && (x
> 0) && (x
< DUNDI_DEFAULT_TTL
)) {
4416 ast_log(LOG_WARNING
, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4417 v
->value
, v
->lineno
, DUNDI_DEFAULT_TTL
);
4419 } else if (!strcasecmp(v
->name
, "autokill")) {
4420 if (sscanf(v
->value
, "%d", &x
) == 1) {
4422 global_autokilltimeout
= x
;
4424 ast_log(LOG_NOTICE
, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v
->lineno
);
4425 } else if (ast_true(v
->value
)) {
4426 global_autokilltimeout
= DEFAULT_MAXMS
;
4428 global_autokilltimeout
= 0;
4430 } else if (!strcasecmp(v
->name
, "entityid")) {
4431 if (!dundi_str_to_eid(&testeid
, v
->value
))
4432 global_eid
= testeid
;
4434 ast_log(LOG_WARNING
, "Invalid global endpoint identifier '%s' at line %d\n", v
->value
, v
->lineno
);
4435 } else if (!strcasecmp(v
->name
, "tos")) {
4436 if (sscanf(v
->value
, "%d", &format
) == 1)
4437 tos
= format
& 0xff;
4438 else if (!strcasecmp(v
->value
, "lowdelay"))
4439 tos
= IPTOS_LOWDELAY
;
4440 else if (!strcasecmp(v
->value
, "throughput"))
4441 tos
= IPTOS_THROUGHPUT
;
4442 else if (!strcasecmp(v
->value
, "reliability"))
4443 tos
= IPTOS_RELIABILITY
;
4444 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
4445 else if (!strcasecmp(v
->value
, "mincost"))
4446 tos
= IPTOS_MINCOST
;
4448 else if (!strcasecmp(v
->value
, "none"))
4451 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
4452 ast_log(LOG_WARNING
, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v
->lineno
);
4454 ast_log(LOG_WARNING
, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v
->lineno
);
4456 } else if (!strcasecmp(v
->name
, "department")) {
4457 ast_copy_string(dept
, v
->value
, sizeof(dept
));
4458 } else if (!strcasecmp(v
->name
, "organization")) {
4459 ast_copy_string(org
, v
->value
, sizeof(org
));
4460 } else if (!strcasecmp(v
->name
, "locality")) {
4461 ast_copy_string(locality
, v
->value
, sizeof(locality
));
4462 } else if (!strcasecmp(v
->name
, "stateprov")) {
4463 ast_copy_string(stateprov
, v
->value
, sizeof(stateprov
));
4464 } else if (!strcasecmp(v
->name
, "country")) {
4465 ast_copy_string(country
, v
->value
, sizeof(country
));
4466 } else if (!strcasecmp(v
->name
, "email")) {
4467 ast_copy_string(email
, v
->value
, sizeof(email
));
4468 } else if (!strcasecmp(v
->name
, "phone")) {
4469 ast_copy_string(phone
, v
->value
, sizeof(phone
));
4470 } else if (!strcasecmp(v
->name
, "storehistory")) {
4471 global_storehistory
= ast_true(v
->value
);
4472 } else if (!strcasecmp(v
->name
, "cachetime")) {
4473 if ((sscanf(v
->value
, "%d", &x
) == 1)) {
4474 dundi_cache_time
= x
;
4476 ast_log(LOG_WARNING
, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
4477 v
->value
, v
->lineno
, DUNDI_DEFAULT_CACHE_TIME
);
4482 AST_LIST_UNLOCK(&peers
);
4484 v
= ast_variable_browse(cfg
, "mappings");
4486 build_mapping(v
->name
, v
->value
);
4491 cat
= ast_category_browse(cfg
, NULL
);
4493 if (strcasecmp(cat
, "general") && strcasecmp(cat
, "mappings")) {
4495 if (!dundi_str_to_eid(&testeid
, cat
))
4496 build_peer(&testeid
, ast_variable_browse(cfg
, cat
), &globalpcmodel
);
4497 else if (!strcasecmp(cat
, "*")) {
4498 build_peer(&empty_eid
, ast_variable_browse(cfg
, cat
), &globalpcmodel
);
4499 any_peer
= find_peer(NULL
);
4501 ast_log(LOG_NOTICE
, "Ignoring invalid EID entry '%s'\n", cat
);
4503 cat
= ast_category_browse(cfg
, cat
);
4506 ast_config_destroy(cfg
);
4508 if (globalpcmodel
& DUNDI_MODEL_OUTBOUND
)
4509 dundi_precache_full();
4513 static int unload_module(void)
4515 pthread_t previous_netthreadid
= netthreadid
, previous_precachethreadid
= precachethreadid
;
4516 ast_module_user_hangup_all();
4518 /* Stop all currently running threads */
4520 if (previous_netthreadid
!= AST_PTHREADT_NULL
) {
4521 pthread_kill(previous_netthreadid
, SIGURG
);
4522 pthread_join(previous_netthreadid
, NULL
);
4524 if (previous_precachethreadid
!= AST_PTHREADT_NULL
) {
4525 pthread_kill(previous_precachethreadid
, SIGURG
);
4526 pthread_join(previous_precachethreadid
, NULL
);
4529 ast_cli_unregister_multiple(cli_dundi
, sizeof(cli_dundi
) / sizeof(struct ast_cli_entry
));
4530 ast_unregister_switch(&dundi_switch
);
4531 ast_custom_function_unregister(&dundi_function
);
4533 io_context_destroy(io
);
4534 sched_context_destroy(sched
);
4544 static int reload(void)
4546 struct sockaddr_in sin
;
4547 set_config("dundi.conf",&sin
);
4551 static int load_module(void)
4554 struct sockaddr_in sin
;
4556 dundi_set_output(dundi_debug_output
);
4557 dundi_set_error(dundi_error_output
);
4559 sin
.sin_family
= AF_INET
;
4560 sin
.sin_port
= ntohs(DUNDI_PORT
);
4561 sin
.sin_addr
.s_addr
= INADDR_ANY
;
4563 /* Make a UDP socket */
4564 io
= io_context_create();
4565 sched
= sched_context_create();
4567 if (!io
|| !sched
) {
4568 ast_log(LOG_ERROR
, "Out of memory\n");
4572 if(set_config("dundi.conf",&sin
))
4573 return AST_MODULE_LOAD_DECLINE
;
4575 netsocket
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_IP
);
4577 if (netsocket
< 0) {
4578 ast_log(LOG_ERROR
, "Unable to create network socket: %s\n", strerror(errno
));
4581 if (bind(netsocket
,(struct sockaddr
*)&sin
, sizeof(sin
))) {
4582 ast_log(LOG_ERROR
, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(sin
.sin_addr
), ntohs(sin
.sin_port
), strerror(errno
));
4586 if (option_verbose
> 1)
4587 ast_verbose(VERBOSE_PREFIX_2
"Using TOS bits %d\n", tos
);
4589 if (setsockopt(netsocket
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)))
4590 ast_log(LOG_WARNING
, "Unable to set TOS to %d\n", tos
);
4592 res
= start_network_thread();
4594 ast_log(LOG_ERROR
, "Unable to start network thread\n");
4599 if (option_verbose
> 1)
4600 ast_verbose(VERBOSE_PREFIX_2
"DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin
.sin_addr
), ntohs(sin
.sin_port
));
4602 ast_cli_register_multiple(cli_dundi
, sizeof(cli_dundi
) / sizeof(struct ast_cli_entry
));
4603 if (ast_register_switch(&dundi_switch
))
4604 ast_log(LOG_ERROR
, "Unable to register DUNDi switch\n");
4605 ast_custom_function_register(&dundi_function
);
4610 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "Distributed Universal Number Discovery (DUNDi)",
4611 .load
= load_module
,
4612 .unload
= unload_module
,