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)
31 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <sys/socket.h>
41 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
42 #include <sys/types.h>
43 #include <netinet/in_systm.h>
45 #include <netinet/ip.h>
46 #include <sys/ioctl.h>
47 #include <netinet/in.h>
49 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
50 #include <net/if_dl.h>
55 #include "asterisk/file.h"
56 #include "asterisk/logger.h"
57 #include "asterisk/channel.h"
58 #include "asterisk/config.h"
59 #include "asterisk/options.h"
60 #include "asterisk/pbx.h"
61 #include "asterisk/module.h"
62 #include "asterisk/frame.h"
63 #include "asterisk/file.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/lock.h"
66 #include "asterisk/md5.h"
67 #include "asterisk/dundi.h"
68 #include "asterisk/sched.h"
69 #include "asterisk/io.h"
70 #include "asterisk/utils.h"
71 #include "asterisk/crypto.h"
72 #include "asterisk/astdb.h"
73 #include "asterisk/acl.h"
74 #include "asterisk/aes.h"
76 #include "dundi-parser.h"
78 #define MAX_RESULTS 64
80 #define MAX_PACKET_SIZE 8192
82 #define DUNDI_MODEL_INBOUND (1 << 0)
83 #define DUNDI_MODEL_OUTBOUND (1 << 1)
84 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
86 /*! Keep times of last 10 lookups */
87 #define DUNDI_TIMING_HISTORY 10
89 #define FLAG_ISREG (1 << 0) /*!< Transaction is register request */
90 #define FLAG_DEAD (1 << 1) /*!< Transaction is dead */
91 #define FLAG_FINAL (1 << 2) /*!< Transaction has final message sent */
92 #define FLAG_ISQUAL (1 << 3) /*!< Transaction is a qualification */
93 #define FLAG_ENCRYPT (1 << 4) /*!< Transaction is encrypted wiht ECX/DCX */
94 #define FLAG_SENDFULLKEY (1 << 5) /*!< Send full key on transaction */
95 #define FLAG_STOREHIST (1 << 6) /*!< Record historic performance */
97 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
100 #define DUNDI_SECRET_TIME 15 /* Testing only */
102 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
108 static struct io_context
*io
;
109 static struct sched_context
*sched
;
110 static int netsocket
= -1;
111 static pthread_t netthreadid
= AST_PTHREADT_NULL
;
112 static pthread_t precachethreadid
= AST_PTHREADT_NULL
;
114 static int dundidebug
= 0;
115 static int authdebug
= 0;
116 static int dundi_ttl
= DUNDI_DEFAULT_TTL
;
117 static int dundi_key_ttl
= DUNDI_DEFAULT_KEY_EXPIRE
;
118 static int dundi_cache_time
= DUNDI_DEFAULT_CACHE_TIME
;
119 static int global_autokilltimeout
= 0;
120 static dundi_eid global_eid
;
121 static int default_expiration
= 60;
122 static int global_storehistory
= 0;
123 static char dept
[80];
125 static char locality
[80];
126 static char stateprov
[80];
127 static char country
[80];
128 static char email
[80];
129 static char phone
[80];
130 static char secretpath
[80];
131 static char cursecret
[80];
132 static char ipaddr
[80];
133 static time_t rotatetime
;
134 static dundi_eid empty_eid
= { { 0, 0, 0, 0, 0, 0 } };
137 AST_LIST_ENTRY(permission
) list
;
142 struct dundi_packet
{
143 AST_LIST_ENTRY(dundi_packet
) list
;
146 struct dundi_transaction
*parent
;
149 unsigned char data
[0];
152 struct dundi_hint_metadata
{
153 unsigned short flags
;
154 char exten
[AST_MAX_EXTENSION
];
157 struct dundi_precache_queue
{
158 AST_LIST_ENTRY(dundi_precache_queue
) list
;
164 struct dundi_request
;
166 struct dundi_transaction
{
167 struct sockaddr_in addr
; /*!< Other end of transaction */
168 struct timeval start
; /*!< When this transaction was created */
169 dundi_eid eids
[DUNDI_MAX_STACK
+ 1];
170 int eidcount
; /*!< Number of eids in eids */
171 dundi_eid us_eid
; /*!< Our EID, to them */
172 dundi_eid them_eid
; /*!< Their EID, to us */
173 aes_encrypt_ctx ecx
; /*!< AES 128 Encryption context */
174 aes_decrypt_ctx dcx
; /*!< AES 128 Decryption context */
175 unsigned int flags
; /*!< Has final packet been sent */
176 int ttl
; /*!< Remaining TTL for queries on this one */
177 int thread
; /*!< We have a calling thread */
178 int retranstimer
; /*!< How long to wait before retransmissions */
179 int autokillid
; /*!< ID to kill connection if answer doesn't come back fast enough */
180 int autokilltimeout
; /*!< Recommended timeout for autokill */
181 unsigned short strans
; /*!< Our transaction identifier */
182 unsigned short dtrans
; /*!< Their transaction identifer */
183 unsigned char iseqno
; /*!< Next expected received seqno */
184 unsigned char oiseqno
; /*!< Last received incoming seqno */
185 unsigned char oseqno
; /*!< Next transmitted seqno */
186 unsigned char aseqno
; /*!< Last acknowledge seqno */
187 AST_LIST_HEAD_NOLOCK(packetlist
, dundi_packet
) packets
; /*!< Packets to be retransmitted */
188 struct packetlist lasttrans
; /*!< Last transmitted / ACK'd packet */
189 struct dundi_request
*parent
; /*!< Parent request (if there is one) */
190 AST_LIST_ENTRY(dundi_transaction
) parentlist
; /*!< Next with respect to the parent */
191 AST_LIST_ENTRY(dundi_transaction
) all
; /*!< Next with respect to all DUNDi transactions */
194 struct dundi_request
{
195 char dcontext
[AST_MAX_EXTENSION
];
196 char number
[AST_MAX_EXTENSION
];
199 struct dundi_result
*dr
;
200 struct dundi_entity_info
*dei
;
201 struct dundi_hint_metadata
*hmd
;
207 unsigned long crc32
; /*!< CRC-32 of all but root EID's in avoid list */
208 AST_LIST_HEAD_NOLOCK(, dundi_transaction
) trans
; /*!< Transactions */
209 AST_LIST_ENTRY(dundi_request
) list
;
212 struct dundi_mapping
{
213 char dcontext
[AST_MAX_EXTENSION
];
214 char lcontext
[AST_MAX_EXTENSION
];
219 char dest
[AST_MAX_EXTENSION
];
220 AST_LIST_ENTRY(dundi_mapping
) list
;
225 struct sockaddr_in addr
; /*!< Address of DUNDi peer */
226 AST_LIST_HEAD_NOLOCK(permissionlist
, permission
) permit
;
227 struct permissionlist include
;
236 unsigned char txenckey
[256]; /*!< Transmitted encrypted key + sig */
237 unsigned char rxenckey
[256]; /*!< Cache received encrypted key + sig */
238 unsigned long us_keycrc32
; /*!< CRC-32 of our key */
239 aes_encrypt_ctx us_ecx
; /*!< Cached AES 128 Encryption context */
240 aes_decrypt_ctx us_dcx
; /*!< Cached AES 128 Decryption context */
241 unsigned long them_keycrc32
; /*!< CRC-32 of our key */
242 aes_encrypt_ctx them_ecx
; /*!< Cached AES 128 Encryption context */
243 aes_decrypt_ctx them_dcx
; /*!< Cached AES 128 Decryption context */
244 time_t keyexpire
; /*!< When to expire/recreate key */
246 int lookuptimes
[DUNDI_TIMING_HISTORY
];
247 char *lookups
[DUNDI_TIMING_HISTORY
];
249 struct dundi_transaction
*regtrans
; /*!< Registration transaction */
250 struct dundi_transaction
*qualtrans
; /*!< Qualify transaction */
251 int model
; /*!< Pull model */
252 int pcmodel
; /*!< Push/precache model */
253 int dynamic
; /*!< Are we dynamic? */
254 int lastms
; /*!< Last measured latency */
255 int maxms
; /*!< Max permissible latency */
256 struct timeval qualtx
; /*!< Time of transmit */
257 AST_LIST_ENTRY(dundi_peer
) list
;
260 static AST_LIST_HEAD_STATIC(peers
, dundi_peer
);
261 static AST_LIST_HEAD_STATIC(pcq
, dundi_precache_queue
);
262 static AST_LIST_HEAD_NOLOCK_STATIC(mappings
, dundi_mapping
);
263 static AST_LIST_HEAD_NOLOCK_STATIC(requests
, dundi_request
);
264 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans
, dundi_transaction
);
266 static int dundi_xmit(struct dundi_packet
*pack
);
268 static void dundi_debug_output(const char *data
)
271 ast_verbose("%s", data
);
274 static void dundi_error_output(const char *data
)
276 ast_log(LOG_WARNING
, "%s", data
);
279 static int has_permission(struct permissionlist
*permlist
, char *cont
)
281 struct permission
*perm
;
284 AST_LIST_TRAVERSE(permlist
, perm
, list
) {
285 if (!strcasecmp(perm
->name
, "all") || !strcasecmp(perm
->name
, cont
))
292 static char *tech2str(int tech
)
295 case DUNDI_PROTO_NONE
:
297 case DUNDI_PROTO_IAX
:
299 case DUNDI_PROTO_SIP
:
301 case DUNDI_PROTO_H323
:
308 static int str2tech(char *str
)
310 if (!strcasecmp(str
, "IAX") || !strcasecmp(str
, "IAX2"))
311 return DUNDI_PROTO_IAX
;
312 else if (!strcasecmp(str
, "SIP"))
313 return DUNDI_PROTO_SIP
;
314 else if (!strcasecmp(str
, "H323"))
315 return DUNDI_PROTO_H323
;
320 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
[]);
321 static int dundi_precache_internal(const char *context
, const char *number
, int ttl
, dundi_eid
*avoids
[]);
322 static struct dundi_transaction
*create_transaction(struct dundi_peer
*p
);
323 static struct dundi_transaction
*find_transaction(struct dundi_hdr
*hdr
, struct sockaddr_in
*sin
)
325 struct dundi_transaction
*trans
;
327 /* Look for an exact match first */
328 AST_LIST_TRAVERSE(&alltrans
, trans
, all
) {
329 if (!inaddrcmp(&trans
->addr
, sin
) &&
330 ((trans
->strans
== (ntohs(hdr
->dtrans
) & 32767)) /* Matches our destination */ ||
331 ((trans
->dtrans
== (ntohs(hdr
->strans
) & 32767)) && (!hdr
->dtrans
))) /* We match their destination */) {
333 trans
->dtrans
= ntohs(hdr
->strans
) & 32767;
338 switch(hdr
->cmdresp
& 0x7f) {
339 case DUNDI_COMMAND_DPDISCOVER
:
340 case DUNDI_COMMAND_EIDQUERY
:
341 case DUNDI_COMMAND_PRECACHERQ
:
342 case DUNDI_COMMAND_REGREQ
:
343 case DUNDI_COMMAND_NULL
:
344 case DUNDI_COMMAND_ENCRYPT
:
346 /* Create new transaction */
347 trans
= create_transaction(NULL
);
349 memcpy(&trans
->addr
, sin
, sizeof(trans
->addr
));
350 trans
->dtrans
= ntohs(hdr
->strans
) & 32767;
352 ast_log(LOG_WARNING
, "Out of memory!\n");
362 static int dundi_send(struct dundi_transaction
*trans
, int cmdresp
, int flags
, int final
, struct dundi_ie_data
*ied
);
364 static int dundi_ack(struct dundi_transaction
*trans
, int final
)
366 return dundi_send(trans
, DUNDI_COMMAND_ACK
, 0, final
, NULL
);
368 static void dundi_reject(struct dundi_hdr
*h
, struct sockaddr_in
*sin
)
371 struct dundi_packet pack
;
372 struct dundi_hdr hdr
;
374 struct dundi_transaction trans
;
375 /* Never respond to an INVALID with another INVALID */
376 if (h
->cmdresp
== DUNDI_COMMAND_INVALID
)
378 memset(&tmp
, 0, sizeof(tmp
));
379 memset(&trans
, 0, sizeof(trans
));
380 memcpy(&trans
.addr
, sin
, sizeof(trans
.addr
));
381 tmp
.hdr
.strans
= h
->dtrans
;
382 tmp
.hdr
.dtrans
= h
->strans
;
383 tmp
.hdr
.iseqno
= h
->oseqno
;
384 tmp
.hdr
.oseqno
= h
->iseqno
;
385 tmp
.hdr
.cmdresp
= DUNDI_COMMAND_INVALID
;
386 tmp
.hdr
.cmdflags
= 0;
387 tmp
.pack
.h
= (struct dundi_hdr
*)tmp
.pack
.data
;
388 tmp
.pack
.datalen
= sizeof(struct dundi_hdr
);
389 tmp
.pack
.parent
= &trans
;
390 dundi_xmit(&tmp
.pack
);
393 static void reset_global_eid(void)
395 #if defined(SIOCGIFHWADDR)
400 s
= socket(AF_INET
, SOCK_STREAM
, 0);
404 memset(&ifr
, 0, sizeof(ifr
));
405 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "eth%d", x
);
406 if (!ioctl(s
, SIOCGIFHWADDR
, &ifr
)) {
407 memcpy(&global_eid
, ((unsigned char *)&ifr
.ifr_hwaddr
) + 2, sizeof(global_eid
));
408 ast_log(LOG_DEBUG
, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &global_eid
), ifr
.ifr_name
);
416 #if defined(ifa_broadaddr) && !defined(SOLARIS)
418 struct ifaddrs
*ifap
;
420 if (getifaddrs(&ifap
) == 0) {
422 for (p
= ifap
; p
; p
= p
->ifa_next
) {
423 if (p
->ifa_addr
->sa_family
== AF_LINK
) {
424 struct sockaddr_dl
* sdp
= (struct sockaddr_dl
*) p
->ifa_addr
;
427 sdp
->sdl_data
+ sdp
->sdl_nlen
, 6);
428 ast_log(LOG_DEBUG
, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &global_eid
), ifap
->ifa_name
);
437 ast_log(LOG_NOTICE
, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
440 static int get_trans_id(void)
442 struct dundi_transaction
*t
;
443 int stid
= (ast_random() % 32766) + 1;
447 AST_LIST_TRAVERSE(&alltrans
, t
, all
) {
448 if (t
->strans
== tid
)
453 tid
= (tid
% 32766) + 1;
454 } while (tid
!= stid
);
459 static int reset_transaction(struct dundi_transaction
*trans
)
462 tid
= get_trans_id();
471 ast_clear_flag(trans
, FLAG_FINAL
);
475 static struct dundi_peer
*find_peer(dundi_eid
*eid
)
477 struct dundi_peer
*cur
= NULL
;
482 AST_LIST_TRAVERSE(&peers
, cur
, list
) {
483 if (!dundi_eid_cmp(&cur
->eid
,eid
))
490 static void build_iv(unsigned char *iv
)
492 /* XXX Would be nice to be more random XXX */
493 unsigned int *fluffy
;
495 fluffy
= (unsigned int *)(iv
);
497 fluffy
[x
] = ast_random();
500 struct dundi_query_state
{
501 dundi_eid
*eids
[DUNDI_MAX_STACK
+ 1];
502 int directs
[DUNDI_MAX_STACK
+ 1];
504 char called_context
[AST_MAX_EXTENSION
];
505 char called_number
[AST_MAX_EXTENSION
];
506 struct dundi_mapping
*maps
;
509 struct dundi_transaction
*trans
;
516 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
)
518 struct ast_flags flags
= {0};
520 if (!ast_strlen_zero(map
->lcontext
)) {
521 if (ast_exists_extension(NULL
, map
->lcontext
, called_number
, 1, NULL
))
522 ast_set_flag(&flags
, DUNDI_FLAG_EXISTS
);
523 if (ast_canmatch_extension(NULL
, map
->lcontext
, called_number
, 1, NULL
))
524 ast_set_flag(&flags
, DUNDI_FLAG_CANMATCH
);
525 if (ast_matchmore_extension(NULL
, map
->lcontext
, called_number
, 1, NULL
))
526 ast_set_flag(&flags
, DUNDI_FLAG_MATCHMORE
);
527 if (ast_ignore_pattern(map
->lcontext
, called_number
))
528 ast_set_flag(&flags
, DUNDI_FLAG_IGNOREPAT
);
530 /* Clearly we can't say 'don't ask' anymore if we found anything... */
531 if (ast_test_flag(&flags
, AST_FLAGS_ALL
))
532 ast_clear_flag_nonstd(hmd
, DUNDI_HINT_DONT_ASK
);
534 if (map
->options
& DUNDI_FLAG_INTERNAL_NOPARTIAL
) {
535 /* Skip partial answers */
536 ast_clear_flag(&flags
, DUNDI_FLAG_MATCHMORE
|DUNDI_FLAG_CANMATCH
);
538 if (ast_test_flag(&flags
, AST_FLAGS_ALL
)) {
539 struct varshead headp
;
540 struct ast_var_t
*newvariable
;
541 ast_set_flag(&flags
, map
->options
& 0xffff);
542 ast_copy_flags(dr
+ anscnt
, &flags
, AST_FLAGS_ALL
);
543 dr
[anscnt
].techint
= map
->tech
;
544 dr
[anscnt
].weight
= map
->weight
;
545 dr
[anscnt
].expiration
= dundi_cache_time
;
546 ast_copy_string(dr
[anscnt
].tech
, tech2str(map
->tech
), sizeof(dr
[anscnt
].tech
));
547 dr
[anscnt
].eid
= *us_eid
;
548 dundi_eid_to_str(dr
[anscnt
].eid_str
, sizeof(dr
[anscnt
].eid_str
), &dr
[anscnt
].eid
);
549 if (ast_test_flag(&flags
, DUNDI_FLAG_EXISTS
)) {
550 AST_LIST_HEAD_INIT_NOLOCK(&headp
);
551 newvariable
= ast_var_assign("NUMBER", called_number
);
552 AST_LIST_INSERT_HEAD(&headp
, newvariable
, entries
);
553 newvariable
= ast_var_assign("EID", dr
[anscnt
].eid_str
);
554 AST_LIST_INSERT_HEAD(&headp
, newvariable
, entries
);
555 newvariable
= ast_var_assign("SECRET", cursecret
);
556 AST_LIST_INSERT_HEAD(&headp
, newvariable
, entries
);
557 newvariable
= ast_var_assign("IPADDR", ipaddr
);
558 AST_LIST_INSERT_HEAD(&headp
, newvariable
, entries
);
559 pbx_substitute_variables_varshead(&headp
, map
->dest
, dr
[anscnt
].dest
, sizeof(dr
[anscnt
].dest
));
560 while ((newvariable
= AST_LIST_REMOVE_HEAD(&headp
, entries
)))
561 ast_var_delete(newvariable
);
563 dr
[anscnt
].dest
[0] = '\0';
566 /* No answers... Find the fewest number of digits from the
567 number for which we have no answer. */
568 char tmp
[AST_MAX_EXTENSION
];
569 for (x
=0;x
<AST_MAX_EXTENSION
;x
++) {
570 tmp
[x
] = called_number
[x
];
573 if (!ast_canmatch_extension(NULL
, map
->lcontext
, tmp
, 1, NULL
)) {
574 /* Oops found something we can't match. If this is longer
575 than the running hint, we have to consider it */
576 if (strlen(tmp
) > strlen(hmd
->exten
)) {
577 ast_copy_string(hmd
->exten
, tmp
, sizeof(hmd
->exten
));
587 static void destroy_trans(struct dundi_transaction
*trans
, int fromtimeout
);
589 static void *dundi_lookup_thread(void *data
)
591 struct dundi_query_state
*st
= data
;
592 struct dundi_result dr
[MAX_RESULTS
];
593 struct dundi_ie_data ied
;
594 struct dundi_hint_metadata hmd
;
599 int expiration
= dundi_cache_time
;
601 ast_log(LOG_DEBUG
, "Whee, looking up '%s@%s' for '%s'\n", st
->called_number
, st
->called_context
,
602 st
->eids
[0] ? dundi_eid_to_str(eid_str
, sizeof(eid_str
), st
->eids
[0]) : "ourselves");
603 memset(&ied
, 0, sizeof(ied
));
604 memset(&dr
, 0, sizeof(dr
));
605 memset(&hmd
, 0, sizeof(hmd
));
606 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
607 hmd
.flags
= DUNDI_HINT_DONT_ASK
| DUNDI_HINT_UNAFFECTED
;
608 for (x
=0;x
<st
->nummaps
;x
++)
609 ouranswers
= dundi_lookup_local(dr
, st
->maps
+ x
, st
->called_number
, &st
->trans
->us_eid
, ouranswers
, &hmd
);
612 for (x
=0;x
<ouranswers
;x
++) {
613 if (dr
[x
].weight
< max
)
618 /* If we do not have a canonical result, keep looking */
619 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
);
621 /* Append answer in result */
624 if ((res
< -1) && (!ouranswers
))
625 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_DUPLICATE
, "Duplicate Request Pending");
628 AST_LIST_LOCK(&peers
);
629 /* Truncate if "don't ask" isn't present */
630 if (!ast_test_flag_nonstd(&hmd
, DUNDI_HINT_DONT_ASK
))
632 if (ast_test_flag(st
->trans
, FLAG_DEAD
)) {
633 ast_log(LOG_DEBUG
, "Our transaction went away!\n");
634 st
->trans
->thread
= 0;
635 destroy_trans(st
->trans
, 0);
637 for (x
=0;x
<ouranswers
;x
++) {
639 if (dr
[x
].expiration
&& (expiration
> dr
[x
].expiration
))
640 expiration
= dr
[x
].expiration
;
641 dundi_ie_append_answer(&ied
, DUNDI_IE_ANSWER
, &dr
[x
].eid
, dr
[x
].techint
, dr
[x
].flags
, dr
[x
].weight
, dr
[x
].dest
);
643 dundi_ie_append_hint(&ied
, DUNDI_IE_HINT
, hmd
.flags
, hmd
.exten
);
644 dundi_ie_append_short(&ied
, DUNDI_IE_EXPIRATION
, expiration
);
645 dundi_send(st
->trans
, DUNDI_COMMAND_DPRESPONSE
, 0, 1, &ied
);
646 st
->trans
->thread
= 0;
648 AST_LIST_UNLOCK(&peers
);
653 static void *dundi_precache_thread(void *data
)
655 struct dundi_query_state
*st
= data
;
656 struct dundi_ie_data ied
;
657 struct dundi_hint_metadata hmd
;
660 ast_log(LOG_DEBUG
, "Whee, precaching '%s@%s' for '%s'\n", st
->called_number
, st
->called_context
,
661 st
->eids
[0] ? dundi_eid_to_str(eid_str
, sizeof(eid_str
), st
->eids
[0]) : "ourselves");
662 memset(&ied
, 0, sizeof(ied
));
664 /* Now produce precache */
665 dundi_precache_internal(st
->called_context
, st
->called_number
, st
->ttl
, st
->eids
);
667 AST_LIST_LOCK(&peers
);
668 /* Truncate if "don't ask" isn't present */
669 if (!ast_test_flag_nonstd(&hmd
, DUNDI_HINT_DONT_ASK
))
671 if (ast_test_flag(st
->trans
, FLAG_DEAD
)) {
672 ast_log(LOG_DEBUG
, "Our transaction went away!\n");
673 st
->trans
->thread
= 0;
674 destroy_trans(st
->trans
, 0);
676 dundi_send(st
->trans
, DUNDI_COMMAND_PRECACHERP
, 0, 1, &ied
);
677 st
->trans
->thread
= 0;
679 AST_LIST_UNLOCK(&peers
);
684 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
[]);
686 static void *dundi_query_thread(void *data
)
688 struct dundi_query_state
*st
= data
;
689 struct dundi_entity_info dei
;
690 struct dundi_ie_data ied
;
691 struct dundi_hint_metadata hmd
;
694 ast_log(LOG_DEBUG
, "Whee, looking up '%s@%s' for '%s'\n", st
->called_number
, st
->called_context
,
695 st
->eids
[0] ? dundi_eid_to_str(eid_str
, sizeof(eid_str
), st
->eids
[0]) : "ourselves");
696 memset(&ied
, 0, sizeof(ied
));
697 memset(&dei
, 0, sizeof(dei
));
698 memset(&hmd
, 0, sizeof(hmd
));
699 if (!dundi_eid_cmp(&st
->trans
->us_eid
, &st
->reqeid
)) {
701 ast_log(LOG_DEBUG
, "Neat, someone look for us!\n");
702 ast_copy_string(dei
.orgunit
, dept
, sizeof(dei
.orgunit
));
703 ast_copy_string(dei
.org
, org
, sizeof(dei
.org
));
704 ast_copy_string(dei
.locality
, locality
, sizeof(dei
.locality
));
705 ast_copy_string(dei
.stateprov
, stateprov
, sizeof(dei
.stateprov
));
706 ast_copy_string(dei
.country
, country
, sizeof(dei
.country
));
707 ast_copy_string(dei
.email
, email
, sizeof(dei
.email
));
708 ast_copy_string(dei
.phone
, phone
, sizeof(dei
.phone
));
711 /* If we do not have a canonical result, keep looking */
712 res
= dundi_query_eid_internal(&dei
, st
->called_context
, &st
->reqeid
, &hmd
, st
->ttl
, 1, st
->eids
);
714 AST_LIST_LOCK(&peers
);
715 if (ast_test_flag(st
->trans
, FLAG_DEAD
)) {
716 ast_log(LOG_DEBUG
, "Our transaction went away!\n");
717 st
->trans
->thread
= 0;
718 destroy_trans(st
->trans
, 0);
721 dundi_ie_append_str(&ied
, DUNDI_IE_DEPARTMENT
, dei
.orgunit
);
722 dundi_ie_append_str(&ied
, DUNDI_IE_ORGANIZATION
, dei
.org
);
723 dundi_ie_append_str(&ied
, DUNDI_IE_LOCALITY
, dei
.locality
);
724 dundi_ie_append_str(&ied
, DUNDI_IE_STATE_PROV
, dei
.stateprov
);
725 dundi_ie_append_str(&ied
, DUNDI_IE_COUNTRY
, dei
.country
);
726 dundi_ie_append_str(&ied
, DUNDI_IE_EMAIL
, dei
.email
);
727 dundi_ie_append_str(&ied
, DUNDI_IE_PHONE
, dei
.phone
);
728 if (!ast_strlen_zero(dei
.ipaddr
))
729 dundi_ie_append_str(&ied
, DUNDI_IE_IPADDR
, dei
.ipaddr
);
731 dundi_ie_append_hint(&ied
, DUNDI_IE_HINT
, hmd
.flags
, hmd
.exten
);
732 dundi_send(st
->trans
, DUNDI_COMMAND_EIDRESPONSE
, 0, 1, &ied
);
733 st
->trans
->thread
= 0;
735 AST_LIST_UNLOCK(&peers
);
740 static int dundi_answer_entity(struct dundi_transaction
*trans
, struct dundi_ies
*ies
, char *ccontext
)
742 struct dundi_query_state
*st
;
746 struct dundi_ie_data ied
;
749 pthread_t lookupthread
;
751 if (ies
->eidcount
> 1) {
752 /* Since it is a requirement that the first EID is the authenticating host
753 and the last EID is the root, it is permissible that the first and last EID
754 could be the same. In that case, we should go ahead copy only the "root" section
755 since we will not need it for authentication. */
756 if (!dundi_eid_cmp(ies
->eids
[0], ies
->eids
[ies
->eidcount
- 1]))
759 totallen
= sizeof(struct dundi_query_state
);
760 totallen
+= (ies
->eidcount
- skipfirst
) * sizeof(dundi_eid
);
761 st
= ast_calloc(1, totallen
);
763 ast_copy_string(st
->called_context
, ies
->called_context
, sizeof(st
->called_context
));
764 memcpy(&st
->reqeid
, ies
->reqeid
, sizeof(st
->reqeid
));
766 st
->ttl
= ies
->ttl
- 1;
770 for (x
=skipfirst
;ies
->eids
[x
];x
++) {
771 st
->eids
[x
-skipfirst
] = (dundi_eid
*)s
;
772 *st
->eids
[x
-skipfirst
] = *ies
->eids
[x
];
773 s
+= sizeof(dundi_eid
);
775 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
);
776 pthread_attr_init(&attr
);
777 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
779 if (ast_pthread_create(&lookupthread
, &attr
, dundi_query_thread
, st
)) {
781 ast_log(LOG_WARNING
, "Unable to create thread!\n");
783 memset(&ied
, 0, sizeof(ied
));
784 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of threads");
785 dundi_send(trans
, DUNDI_COMMAND_EIDRESPONSE
, 0, 1, &ied
);
786 pthread_attr_destroy(&attr
);
789 pthread_attr_destroy(&attr
);
791 ast_log(LOG_WARNING
, "Out of memory!\n");
792 memset(&ied
, 0, sizeof(ied
));
793 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of memory");
794 dundi_send(trans
, DUNDI_COMMAND_EIDRESPONSE
, 0, 1, &ied
);
800 static int cache_save_hint(dundi_eid
*eidpeer
, struct dundi_request
*req
, struct dundi_hint
*hint
, int expiration
)
805 char eidpeer_str
[20];
806 char eidroot_str
[20];
811 expiration
= dundi_cache_time
;
813 /* Only cache hint if "don't ask" is there... */
814 if (!ast_test_flag_nonstd(hint
, htons(DUNDI_HINT_DONT_ASK
)))
817 unaffected
= ast_test_flag_nonstd(hint
, htons(DUNDI_HINT_UNAFFECTED
));
819 dundi_eid_to_str_short(eidpeer_str
, sizeof(eidpeer_str
), eidpeer
);
820 dundi_eid_to_str_short(eidroot_str
, sizeof(eidroot_str
), &req
->root_eid
);
821 snprintf(key1
, sizeof(key1
), "hint/%s/%s/%s/e%08lx", eidpeer_str
, hint
->data
, req
->dcontext
, unaffected
? 0 : req
->crc32
);
822 snprintf(key2
, sizeof(key2
), "hint/%s/%s/%s/r%s", eidpeer_str
, hint
->data
, req
->dcontext
, eidroot_str
);
825 timeout
+= expiration
;
826 snprintf(data
, sizeof(data
), "%ld|", (long)(timeout
));
828 ast_db_put("dundi/cache", key1
, data
);
829 ast_log(LOG_DEBUG
, "Caching hint at '%s'\n", key1
);
830 ast_db_put("dundi/cache", key2
, data
);
831 ast_log(LOG_DEBUG
, "Caching hint at '%s'\n", key2
);
835 static int cache_save(dundi_eid
*eidpeer
, struct dundi_request
*req
, int start
, int unaffected
, int expiration
, int push
)
841 char eidpeer_str
[20];
842 char eidroot_str
[20];
846 expiration
= dundi_cache_time
;
848 /* Keep pushes a little longer, cut pulls a little short */
855 dundi_eid_to_str_short(eidpeer_str
, sizeof(eidpeer_str
), eidpeer
);
856 dundi_eid_to_str_short(eidroot_str
, sizeof(eidroot_str
), &req
->root_eid
);
857 snprintf(key1
, sizeof(key1
), "%s/%s/%s/e%08lx", eidpeer_str
, req
->number
, req
->dcontext
, unaffected
? 0 : req
->crc32
);
858 snprintf(key2
, sizeof(key2
), "%s/%s/%s/r%s", eidpeer_str
, req
->number
, req
->dcontext
, eidroot_str
);
859 /* Build request string */
861 timeout
+= expiration
;
862 snprintf(data
, sizeof(data
), "%ld|", (long)(timeout
));
863 for (x
=start
;x
<req
->respcount
;x
++) {
864 /* Skip anything with an illegal pipe in it */
865 if (strchr(req
->dr
[x
].dest
, '|'))
867 snprintf(data
+ strlen(data
), sizeof(data
) - strlen(data
), "%d/%d/%d/%s/%s|",
868 req
->dr
[x
].flags
, req
->dr
[x
].weight
, req
->dr
[x
].techint
, req
->dr
[x
].dest
,
869 dundi_eid_to_str_short(eidpeer_str
, sizeof(eidpeer_str
), &req
->dr
[x
].eid
));
871 ast_db_put("dundi/cache", key1
, data
);
872 ast_db_put("dundi/cache", key2
, data
);
876 static int dundi_prop_precache(struct dundi_transaction
*trans
, struct dundi_ies
*ies
, char *ccontext
)
878 struct dundi_query_state
*st
;
881 struct dundi_ie_data ied
;
883 struct dundi_result dr2
[MAX_RESULTS
];
884 struct dundi_request dr
;
885 struct dundi_hint_metadata hmd
;
887 struct dundi_mapping
*cur
;
891 pthread_t lookupthread
;
894 memset(&dr2
, 0, sizeof(dr2
));
895 memset(&dr
, 0, sizeof(dr
));
896 memset(&hmd
, 0, sizeof(hmd
));
898 /* Forge request structure to hold answers for cache */
899 hmd
.flags
= DUNDI_HINT_DONT_ASK
| DUNDI_HINT_UNAFFECTED
;
901 dr
.maxcount
= MAX_RESULTS
;
902 dr
.expiration
= dundi_cache_time
;
904 dr
.pfds
[0] = dr
.pfds
[1] = -1;
906 ast_copy_string(dr
.dcontext
, ies
->called_context
? ies
->called_context
: "e164", sizeof(dr
.dcontext
));
907 ast_copy_string(dr
.number
, ies
->called_number
, sizeof(dr
.number
));
909 for (x
=0;x
<ies
->anscount
;x
++) {
910 if (trans
->parent
->respcount
< trans
->parent
->maxcount
) {
911 /* Make sure it's not already there */
912 for (z
=0;z
<trans
->parent
->respcount
;z
++) {
913 if ((trans
->parent
->dr
[z
].techint
== ies
->answers
[x
]->protocol
) &&
914 !strcmp(trans
->parent
->dr
[z
].dest
, (char *)ies
->answers
[x
]->data
))
917 if (z
== trans
->parent
->respcount
) {
918 /* Copy into parent responses */
919 trans
->parent
->dr
[trans
->parent
->respcount
].flags
= ntohs(ies
->answers
[x
]->flags
);
920 trans
->parent
->dr
[trans
->parent
->respcount
].techint
= ies
->answers
[x
]->protocol
;
921 trans
->parent
->dr
[trans
->parent
->respcount
].weight
= ntohs(ies
->answers
[x
]->weight
);
922 trans
->parent
->dr
[trans
->parent
->respcount
].eid
= ies
->answers
[x
]->eid
;
923 if (ies
->expiration
> 0)
924 trans
->parent
->dr
[trans
->parent
->respcount
].expiration
= ies
->expiration
;
926 trans
->parent
->dr
[trans
->parent
->respcount
].expiration
= dundi_cache_time
;
927 dundi_eid_to_str(trans
->parent
->dr
[trans
->parent
->respcount
].eid_str
,
928 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].eid_str
),
929 &ies
->answers
[x
]->eid
);
930 ast_copy_string(trans
->parent
->dr
[trans
->parent
->respcount
].dest
, (char *)ies
->answers
[x
]->data
,
931 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].dest
));
932 ast_copy_string(trans
->parent
->dr
[trans
->parent
->respcount
].tech
, tech2str(ies
->answers
[x
]->protocol
),
933 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].tech
));
934 trans
->parent
->respcount
++;
935 ast_clear_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_DONT_ASK
);
936 } else if (trans
->parent
->dr
[z
].weight
> ies
->answers
[x
]->weight
) {
937 /* Update weight if appropriate */
938 trans
->parent
->dr
[z
].weight
= ies
->answers
[x
]->weight
;
941 ast_log(LOG_NOTICE
, "Dropping excessive answers in precache for %s@%s\n",
942 trans
->parent
->number
, trans
->parent
->dcontext
);
945 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
946 cache_save(&trans
->them_eid
, trans
->parent
, 0, 0, ies
->expiration
, 1);
948 cache_save_hint(&trans
->them_eid
, trans
->parent
, ies
->hint
, ies
->expiration
);
950 totallen
= sizeof(struct dundi_query_state
);
951 /* Count matching map entries */
953 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
954 if (!strcasecmp(cur
->dcontext
, ccontext
))
958 /* If no maps, return -1 immediately */
962 if (ies
->eidcount
> 1) {
963 /* Since it is a requirement that the first EID is the authenticating host
964 and the last EID is the root, it is permissible that the first and last EID
965 could be the same. In that case, we should go ahead copy only the "root" section
966 since we will not need it for authentication. */
967 if (!dundi_eid_cmp(ies
->eids
[0], ies
->eids
[ies
->eidcount
- 1]))
971 /* Prepare to run a query and then propagate that as necessary */
972 totallen
+= mapcount
* sizeof(struct dundi_mapping
);
973 totallen
+= (ies
->eidcount
- skipfirst
) * sizeof(dundi_eid
);
974 st
= ast_calloc(1, totallen
);
976 ast_copy_string(st
->called_context
, ies
->called_context
, sizeof(st
->called_context
));
977 ast_copy_string(st
->called_number
, ies
->called_number
, sizeof(st
->called_number
));
979 st
->ttl
= ies
->ttl
- 1;
980 st
->nocache
= ies
->cbypass
;
984 for (x
=skipfirst
;ies
->eids
[x
];x
++) {
985 st
->eids
[x
-skipfirst
] = (dundi_eid
*)s
;
986 *st
->eids
[x
-skipfirst
] = *ies
->eids
[x
];
987 st
->directs
[x
-skipfirst
] = ies
->eid_direct
[x
];
988 s
+= sizeof(dundi_eid
);
990 /* Append mappings */
992 st
->maps
= (struct dundi_mapping
*)s
;
993 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
994 if (!strcasecmp(cur
->dcontext
, ccontext
)) {
997 st
->maps
[x
].list
.next
= NULL
;
1002 st
->nummaps
= mapcount
;
1003 ast_log(LOG_DEBUG
, "Forwarding precache for '%s@%s'!\n", ies
->called_number
, ies
->called_context
);
1004 pthread_attr_init(&attr
);
1005 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1007 if (ast_pthread_create(&lookupthread
, &attr
, dundi_precache_thread
, st
)) {
1009 ast_log(LOG_WARNING
, "Unable to create thread!\n");
1011 memset(&ied
, 0, sizeof(ied
));
1012 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of threads");
1013 dundi_send(trans
, DUNDI_COMMAND_PRECACHERP
, 0, 1, &ied
);
1014 pthread_attr_destroy(&attr
);
1017 pthread_attr_destroy(&attr
);
1019 ast_log(LOG_WARNING
, "Out of memory!\n");
1020 memset(&ied
, 0, sizeof(ied
));
1021 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of memory");
1022 dundi_send(trans
, DUNDI_COMMAND_PRECACHERP
, 0, 1, &ied
);
1028 static int dundi_answer_query(struct dundi_transaction
*trans
, struct dundi_ies
*ies
, char *ccontext
)
1030 struct dundi_query_state
*st
;
1033 struct dundi_ie_data ied
;
1035 struct dundi_mapping
*cur
;
1039 pthread_t lookupthread
;
1040 pthread_attr_t attr
;
1041 totallen
= sizeof(struct dundi_query_state
);
1042 /* Count matching map entries */
1043 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
1044 if (!strcasecmp(cur
->dcontext
, ccontext
))
1047 /* If no maps, return -1 immediately */
1051 if (ies
->eidcount
> 1) {
1052 /* Since it is a requirement that the first EID is the authenticating host
1053 and the last EID is the root, it is permissible that the first and last EID
1054 could be the same. In that case, we should go ahead copy only the "root" section
1055 since we will not need it for authentication. */
1056 if (!dundi_eid_cmp(ies
->eids
[0], ies
->eids
[ies
->eidcount
- 1]))
1060 totallen
+= mapcount
* sizeof(struct dundi_mapping
);
1061 totallen
+= (ies
->eidcount
- skipfirst
) * sizeof(dundi_eid
);
1062 st
= ast_calloc(1, totallen
);
1064 ast_copy_string(st
->called_context
, ies
->called_context
, sizeof(st
->called_context
));
1065 ast_copy_string(st
->called_number
, ies
->called_number
, sizeof(st
->called_number
));
1067 st
->ttl
= ies
->ttl
- 1;
1068 st
->nocache
= ies
->cbypass
;
1072 for (x
=skipfirst
;ies
->eids
[x
];x
++) {
1073 st
->eids
[x
-skipfirst
] = (dundi_eid
*)s
;
1074 *st
->eids
[x
-skipfirst
] = *ies
->eids
[x
];
1075 st
->directs
[x
-skipfirst
] = ies
->eid_direct
[x
];
1076 s
+= sizeof(dundi_eid
);
1078 /* Append mappings */
1080 st
->maps
= (struct dundi_mapping
*)s
;
1081 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
1082 if (!strcasecmp(cur
->dcontext
, ccontext
)) {
1085 st
->maps
[x
].list
.next
= NULL
;
1090 st
->nummaps
= mapcount
;
1091 ast_log(LOG_DEBUG
, "Answering query for '%s@%s'!\n", ies
->called_number
, ies
->called_context
);
1092 pthread_attr_init(&attr
);
1093 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1095 if (ast_pthread_create(&lookupthread
, &attr
, dundi_lookup_thread
, st
)) {
1097 ast_log(LOG_WARNING
, "Unable to create thread!\n");
1099 memset(&ied
, 0, sizeof(ied
));
1100 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of threads");
1101 dundi_send(trans
, DUNDI_COMMAND_DPRESPONSE
, 0, 1, &ied
);
1102 pthread_attr_destroy(&attr
);
1105 pthread_attr_destroy(&attr
);
1107 ast_log(LOG_WARNING
, "Out of memory!\n");
1108 memset(&ied
, 0, sizeof(ied
));
1109 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Out of memory");
1110 dundi_send(trans
, DUNDI_COMMAND_DPRESPONSE
, 0, 1, &ied
);
1116 static int cache_lookup_internal(time_t now
, struct dundi_request
*req
, char *key
, char *eid_str_full
, int *lowexpiration
)
1119 char *ptr
, *term
, *src
;
1121 struct ast_flags flags
;
1127 /* Build request string */
1128 if (!ast_db_get("dundi/cache", key
, data
, sizeof(data
))) {
1131 if (!ast_get_time_t(ptr
, &timeout
, 0, &length
)) {
1132 int expiration
= timeout
- now
;
1133 if (expiration
> 0) {
1134 ast_log(LOG_DEBUG
, "Found cache expiring in %d seconds!\n", expiration
);
1136 while((sscanf(ptr
, "%d/%d/%d/%n", &(flags
.flags
), &weight
, &tech
, &length
) == 3)) {
1138 term
= strchr(ptr
, '|');
1141 src
= strrchr(ptr
, '/');
1147 ast_log(LOG_DEBUG
, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1148 tech2str(tech
), ptr
, src
, dundi_flags2str(fs
, sizeof(fs
), flags
.flags
), eid_str_full
);
1149 /* Make sure it's not already there */
1150 for (z
=0;z
<req
->respcount
;z
++) {
1151 if ((req
->dr
[z
].techint
== tech
) &&
1152 !strcmp(req
->dr
[z
].dest
, ptr
))
1155 if (z
== req
->respcount
) {
1156 /* Copy into parent responses */
1157 ast_copy_flags(&(req
->dr
[req
->respcount
]), &flags
, AST_FLAGS_ALL
);
1158 req
->dr
[req
->respcount
].weight
= weight
;
1159 req
->dr
[req
->respcount
].techint
= tech
;
1160 req
->dr
[req
->respcount
].expiration
= expiration
;
1161 dundi_str_short_to_eid(&req
->dr
[req
->respcount
].eid
, src
);
1162 dundi_eid_to_str(req
->dr
[req
->respcount
].eid_str
,
1163 sizeof(req
->dr
[req
->respcount
].eid_str
), &req
->dr
[req
->respcount
].eid
);
1164 ast_copy_string(req
->dr
[req
->respcount
].dest
, ptr
,
1165 sizeof(req
->dr
[req
->respcount
].dest
));
1166 ast_copy_string(req
->dr
[req
->respcount
].tech
, tech2str(tech
),
1167 sizeof(req
->dr
[req
->respcount
].tech
));
1169 ast_clear_flag_nonstd(req
->hmd
, DUNDI_HINT_DONT_ASK
);
1170 } else if (req
->dr
[z
].weight
> weight
)
1171 req
->dr
[z
].weight
= weight
;
1175 /* We found *something* cached */
1176 if (expiration
< *lowexpiration
)
1177 *lowexpiration
= expiration
;
1180 ast_db_del("dundi/cache", key
);
1182 ast_db_del("dundi/cache", key
);
1188 static int cache_lookup(struct dundi_request
*req
, dundi_eid
*peer_eid
, unsigned long crc32
, int *lowexpiration
)
1192 char eidroot_str
[20];
1196 char eid_str_full
[20];
1201 dundi_eid_to_str_short(eid_str
, sizeof(eid_str
), peer_eid
);
1202 dundi_eid_to_str_short(eidroot_str
, sizeof(eidroot_str
), &req
->root_eid
);
1203 dundi_eid_to_str(eid_str_full
, sizeof(eid_str_full
), peer_eid
);
1204 snprintf(key
, sizeof(key
), "%s/%s/%s/e%08lx", eid_str
, req
->number
, req
->dcontext
, crc32
);
1205 res
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1206 snprintf(key
, sizeof(key
), "%s/%s/%s/e%08lx", eid_str
, req
->number
, req
->dcontext
, 0L);
1207 res
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1208 snprintf(key
, sizeof(key
), "%s/%s/%s/r%s", eid_str
, req
->number
, req
->dcontext
, eidroot_str
);
1209 res
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1211 if (!req
->respcount
) {
1213 /* Look and see if we have a hint that would preclude us from looking at this
1214 peer for this number. */
1215 if (!(tmp
[x
] = req
->number
[x
]))
1218 /* Check for hints */
1219 snprintf(key
, sizeof(key
), "hint/%s/%s/%s/e%08lx", eid_str
, tmp
, req
->dcontext
, crc32
);
1220 res2
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1221 snprintf(key
, sizeof(key
), "hint/%s/%s/%s/e%08lx", eid_str
, tmp
, req
->dcontext
, 0L);
1222 res2
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1223 snprintf(key
, sizeof(key
), "hint/%s/%s/%s/r%s", eid_str
, tmp
, req
->dcontext
, eidroot_str
);
1224 res2
|= cache_lookup_internal(now
, req
, key
, eid_str_full
, lowexpiration
);
1226 if (strlen(tmp
) > strlen(req
->hmd
->exten
)) {
1227 /* Update meta data if appropriate */
1228 ast_copy_string(req
->hmd
->exten
, tmp
, sizeof(req
->hmd
->exten
));
1238 static void qualify_peer(struct dundi_peer
*peer
, int schedonly
);
1240 static void apply_peer(struct dundi_transaction
*trans
, struct dundi_peer
*p
)
1242 if (!trans
->addr
.sin_addr
.s_addr
)
1243 memcpy(&trans
->addr
, &p
->addr
, sizeof(trans
->addr
));
1244 trans
->us_eid
= p
->us_eid
;
1245 trans
->them_eid
= p
->eid
;
1246 /* Enable encryption if appropriate */
1247 if (!ast_strlen_zero(p
->inkey
))
1248 ast_set_flag(trans
, FLAG_ENCRYPT
);
1250 trans
->autokilltimeout
= p
->maxms
;
1251 trans
->retranstimer
= DUNDI_DEFAULT_RETRANS_TIMER
;
1252 if (p
->lastms
> 1) {
1253 trans
->retranstimer
= p
->lastms
* 2;
1254 /* Keep it from being silly */
1255 if (trans
->retranstimer
< 150)
1256 trans
->retranstimer
= 150;
1258 if (trans
->retranstimer
> DUNDI_DEFAULT_RETRANS_TIMER
)
1259 trans
->retranstimer
= DUNDI_DEFAULT_RETRANS_TIMER
;
1261 trans
->autokilltimeout
= global_autokilltimeout
;
1264 /*! \note Called with the peers list already locked */
1265 static int do_register_expire(void *data
)
1267 struct dundi_peer
*peer
= data
;
1269 ast_log(LOG_DEBUG
, "Register expired for '%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1270 peer
->registerexpire
= -1;
1272 memset(&peer
->addr
, 0, sizeof(peer
->addr
));
1276 static int update_key(struct dundi_peer
*peer
)
1278 unsigned char key
[16];
1279 struct ast_key
*ekey
, *skey
;
1282 if (!peer
->keyexpire
|| (peer
->keyexpire
< time(NULL
))) {
1284 aes_encrypt_key128(key
, &peer
->us_ecx
);
1285 aes_decrypt_key128(key
, &peer
->us_dcx
);
1286 ekey
= ast_key_get(peer
->inkey
, AST_KEY_PUBLIC
);
1288 ast_log(LOG_NOTICE
, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1289 peer
->inkey
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1292 skey
= ast_key_get(peer
->outkey
, AST_KEY_PRIVATE
);
1294 ast_log(LOG_NOTICE
, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1295 peer
->outkey
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1298 if ((res
= ast_encrypt_bin(peer
->txenckey
, key
, sizeof(key
), ekey
)) != 128) {
1299 ast_log(LOG_NOTICE
, "Whoa, got a weird encrypt size (%d != %d)!\n", res
, 128);
1302 if ((res
= ast_sign_bin(skey
, (char *)peer
->txenckey
, 128, peer
->txenckey
+ 128))) {
1303 ast_log(LOG_NOTICE
, "Failed to sign key (%d)!\n", res
);
1306 peer
->us_keycrc32
= crc32(0L, peer
->txenckey
, 128);
1307 peer
->sentfullkey
= 0;
1309 time(&peer
->keyexpire
);
1310 peer
->keyexpire
+= dundi_key_ttl
;
1315 static int encrypt_memcpy(unsigned char *dst
, unsigned char *src
, int len
, unsigned char *iv
, aes_encrypt_ctx
*ecx
)
1317 unsigned char curblock
[16];
1319 memcpy(curblock
, iv
, sizeof(curblock
));
1322 curblock
[x
] ^= src
[x
];
1323 aes_encrypt(curblock
, dst
, ecx
);
1324 memcpy(curblock
, dst
, sizeof(curblock
));
1331 static int decrypt_memcpy(unsigned char *dst
, unsigned char *src
, int len
, unsigned char *iv
, aes_decrypt_ctx
*dcx
)
1333 unsigned char lastblock
[16];
1335 memcpy(lastblock
, iv
, sizeof(lastblock
));
1337 aes_decrypt(src
, dst
, dcx
);
1339 dst
[x
] ^= lastblock
[x
];
1340 memcpy(lastblock
, src
, sizeof(lastblock
));
1348 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
)
1350 int space
= *dstlen
;
1351 unsigned long bytes
;
1352 struct dundi_hdr
*h
;
1353 unsigned char *decrypt_space
;
1354 decrypt_space
= alloca(srclen
);
1357 decrypt_memcpy(decrypt_space
, src
->encdata
, srclen
, src
->iv
, &trans
->dcx
);
1359 h
= (struct dundi_hdr
*)dst
;
1362 if (uncompress(dst
+ 6, &bytes
, decrypt_space
, srclen
) != Z_OK
) {
1363 ast_log(LOG_DEBUG
, "Ouch, uncompress failed :(\n");
1367 *dstlen
= bytes
+ 6;
1368 /* Return new header */
1372 static int dundi_encrypt(struct dundi_transaction
*trans
, struct dundi_packet
*pack
)
1374 unsigned char *compress_space
;
1377 unsigned long bytes
;
1378 struct dundi_ie_data ied
;
1379 struct dundi_peer
*peer
;
1380 unsigned char iv
[16];
1381 len
= pack
->datalen
+ pack
->datalen
/ 100 + 42;
1382 compress_space
= alloca(len
);
1383 if (compress_space
) {
1384 memset(compress_space
, 0, len
);
1385 /* We care about everthing save the first 6 bytes of header */
1387 res
= compress(compress_space
, &bytes
, pack
->data
+ 6, pack
->datalen
- 6);
1389 ast_log(LOG_DEBUG
, "Ouch, compression failed!\n");
1392 memset(&ied
, 0, sizeof(ied
));
1393 /* Say who we are */
1394 if (!pack
->h
->iseqno
&& !pack
->h
->oseqno
) {
1395 /* Need the key in the first copy */
1396 if (!(peer
= find_peer(&trans
->them_eid
)))
1398 if (update_key(peer
))
1400 if (!peer
->sentfullkey
)
1401 ast_set_flag(trans
, FLAG_SENDFULLKEY
);
1402 /* Append key data */
1403 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->us_eid
);
1404 if (ast_test_flag(trans
, FLAG_SENDFULLKEY
)) {
1405 dundi_ie_append_raw(&ied
, DUNDI_IE_SHAREDKEY
, peer
->txenckey
, 128);
1406 dundi_ie_append_raw(&ied
, DUNDI_IE_SIGNATURE
, peer
->txenckey
+ 128, 128);
1408 dundi_ie_append_int(&ied
, DUNDI_IE_KEYCRC32
, peer
->us_keycrc32
);
1410 /* Setup contexts */
1411 trans
->ecx
= peer
->us_ecx
;
1412 trans
->dcx
= peer
->us_dcx
;
1414 /* We've sent the full key */
1415 peer
->sentfullkey
= 1;
1417 /* Build initialization vector */
1419 /* Add the field, rounded up to 16 bytes */
1420 dundi_ie_append_encdata(&ied
, DUNDI_IE_ENCDATA
, iv
, NULL
, ((bytes
+ 15) / 16) * 16);
1422 if ((ied
.pos
+ bytes
) >= sizeof(ied
.buf
)) {
1423 ast_log(LOG_NOTICE
, "Final packet too large!\n");
1426 encrypt_memcpy(ied
.buf
+ ied
.pos
, compress_space
, bytes
, iv
, &trans
->ecx
);
1427 ied
.pos
+= ((bytes
+ 15) / 16) * 16;
1428 /* Reconstruct header */
1429 pack
->datalen
= sizeof(struct dundi_hdr
);
1430 pack
->h
->cmdresp
= DUNDI_COMMAND_ENCRYPT
;
1431 pack
->h
->cmdflags
= 0;
1432 memcpy(pack
->h
->ies
, ied
.buf
, ied
.pos
);
1433 pack
->datalen
+= ied
.pos
;
1439 static int check_key(struct dundi_peer
*peer
, unsigned char *newkey
, unsigned char *newsig
, unsigned long keycrc32
)
1441 unsigned char dst
[128];
1443 struct ast_key
*key
, *skey
;
1446 ast_log(LOG_DEBUG
, "Expected '%08lx' got '%08lx'\n", peer
->them_keycrc32
, keycrc32
);
1447 if (peer
->them_keycrc32
&& (peer
->them_keycrc32
== keycrc32
)) {
1450 } else if (!newkey
|| !newsig
)
1452 if (!memcmp(peer
->rxenckey
, newkey
, 128) &&
1453 !memcmp(peer
->rxenckey
+ 128, newsig
, 128)) {
1454 /* By definition, a match */
1458 key
= ast_key_get(peer
->outkey
, AST_KEY_PRIVATE
);
1460 ast_log(LOG_NOTICE
, "Unable to find key '%s' to decode shared key from '%s'\n",
1461 peer
->outkey
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1465 skey
= ast_key_get(peer
->inkey
, AST_KEY_PUBLIC
);
1467 ast_log(LOG_NOTICE
, "Unable to find key '%s' to verify shared key from '%s'\n",
1468 peer
->inkey
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
1472 /* First check signature */
1473 res
= ast_check_signature_bin(skey
, (char *)newkey
, 128, newsig
);
1477 res
= ast_decrypt_bin(dst
, newkey
, sizeof(dst
), key
);
1480 ast_log(LOG_NOTICE
, "Weird, key decoded to the wrong size (%d)\n", res
);
1483 /* Decrypted, passes signature */
1484 ast_log(LOG_DEBUG
, "Wow, new key combo passed signature and decrypt!\n");
1485 memcpy(peer
->rxenckey
, newkey
, 128);
1486 memcpy(peer
->rxenckey
+ 128, newsig
, 128);
1487 peer
->them_keycrc32
= crc32(0L, peer
->rxenckey
, 128);
1488 aes_decrypt_key128(dst
, &peer
->them_dcx
);
1489 aes_encrypt_key128(dst
, &peer
->them_ecx
);
1493 static int handle_command_response(struct dundi_transaction
*trans
, struct dundi_hdr
*hdr
, int datalen
, int encrypted
)
1495 /* Handle canonical command / response */
1496 int final
= hdr
->cmdresp
& 0x80;
1497 int cmd
= hdr
->cmdresp
& 0x7f;
1502 unsigned char *bufcpy
;
1503 struct dundi_ie_data ied
;
1504 struct dundi_ies ies
;
1505 struct dundi_peer
*peer
;
1508 memset(&ied
, 0, sizeof(ied
));
1509 memset(&ies
, 0, sizeof(ies
));
1511 bufcpy
= alloca(datalen
);
1514 /* Make a copy for parsing */
1515 memcpy(bufcpy
, hdr
->ies
, datalen
);
1516 ast_log(LOG_DEBUG
, "Got canonical message %d (%d), %d bytes data%s\n", cmd
, hdr
->oseqno
, datalen
, final
? " (Final)" : "");
1517 if (dundi_parse_ies(&ies
, bufcpy
, datalen
) < 0) {
1518 ast_log(LOG_WARNING
, "Failed to parse DUNDI information elements!\n");
1523 case DUNDI_COMMAND_DPDISCOVER
:
1524 case DUNDI_COMMAND_EIDQUERY
:
1525 case DUNDI_COMMAND_PRECACHERQ
:
1526 if (cmd
== DUNDI_COMMAND_EIDQUERY
)
1527 resp
= DUNDI_COMMAND_EIDRESPONSE
;
1528 else if (cmd
== DUNDI_COMMAND_PRECACHERQ
)
1529 resp
= DUNDI_COMMAND_PRECACHERP
;
1531 resp
= DUNDI_COMMAND_DPRESPONSE
;
1532 /* A dialplan or entity discover -- qualify by highest level entity */
1533 peer
= find_peer(ies
.eids
[0]);
1535 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, NULL
);
1536 dundi_send(trans
, resp
, 0, 1, &ied
);
1539 trans
->us_eid
= peer
->us_eid
;
1540 if (strlen(peer
->inkey
)) {
1541 hasauth
= encrypted
;
1545 /* Okay we're authentiated and all, now we check if they're authorized */
1546 if (!ies
.called_context
)
1547 ies
.called_context
= "e164";
1548 if (cmd
== DUNDI_COMMAND_EIDQUERY
) {
1549 res
= dundi_answer_entity(trans
, &ies
, ies
.called_context
);
1551 if (ast_strlen_zero(ies
.called_number
)) {
1552 /* They're not permitted to access that context */
1553 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_GENERAL
, "Invalid or missing number/entity");
1554 dundi_send(trans
, resp
, 0, 1, &ied
);
1555 } else if ((cmd
== DUNDI_COMMAND_DPDISCOVER
) &&
1556 (peer
->model
& DUNDI_MODEL_INBOUND
) &&
1557 has_permission(&peer
->permit
, ies
.called_context
)) {
1558 res
= dundi_answer_query(trans
, &ies
, ies
.called_context
);
1560 /* There is no such dundi context */
1561 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Unsupported DUNDI Context");
1562 dundi_send(trans
, resp
, 0, 1, &ied
);
1564 } else if ((cmd
= DUNDI_COMMAND_PRECACHERQ
) &&
1565 (peer
->pcmodel
& DUNDI_MODEL_INBOUND
) &&
1566 has_permission(&peer
->include
, ies
.called_context
)) {
1567 res
= dundi_prop_precache(trans
, &ies
, ies
.called_context
);
1569 /* There is no such dundi context */
1570 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Unsupported DUNDI Context");
1571 dundi_send(trans
, resp
, 0, 1, &ied
);
1574 /* They're not permitted to access that context */
1575 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Permission to context denied");
1576 dundi_send(trans
, resp
, 0, 1, &ied
);
1580 /* They're not permitted to access that context */
1581 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Unencrypted responses not permitted");
1582 dundi_send(trans
, resp
, 0, 1, &ied
);
1586 case DUNDI_COMMAND_REGREQ
:
1587 /* A register request -- should only have one entity */
1588 peer
= find_peer(ies
.eids
[0]);
1589 if (!peer
|| !peer
->dynamic
) {
1590 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, NULL
);
1591 dundi_send(trans
, DUNDI_COMMAND_REGRESPONSE
, 0, 1, &ied
);
1594 trans
->us_eid
= peer
->us_eid
;
1595 if (!ast_strlen_zero(peer
->inkey
)) {
1596 hasauth
= encrypted
;
1600 int expire
= default_expiration
;
1603 if (peer
->registerexpire
> -1)
1604 ast_sched_del(sched
, peer
->registerexpire
);
1605 peer
->registerexpire
= ast_sched_add(sched
, (expire
+ 10) * 1000, do_register_expire
, peer
);
1606 snprintf(data
, sizeof(data
), "%s:%d:%d", ast_inet_ntoa(trans
->addr
.sin_addr
),
1607 ntohs(trans
->addr
.sin_port
), expire
);
1608 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str
, sizeof(eid_str
), &peer
->eid
), data
);
1609 if (inaddrcmp(&peer
->addr
, &trans
->addr
)) {
1610 if (option_verbose
> 2) {
1611 ast_verbose(VERBOSE_PREFIX_3
"Registered DUNDi peer '%s' at '%s:%d'\n",
1612 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
),
1613 ast_inet_ntoa(trans
->addr
.sin_addr
), ntohs(trans
->addr
.sin_port
));
1618 memcpy(&peer
->addr
, &trans
->addr
, sizeof(peer
->addr
));
1619 dundi_ie_append_short(&ied
, DUNDI_IE_EXPIRATION
, default_expiration
);
1620 dundi_send(trans
, DUNDI_COMMAND_REGRESPONSE
, 0, 1, &ied
);
1622 qualify_peer(peer
, 1);
1626 case DUNDI_COMMAND_DPRESPONSE
:
1627 /* A dialplan response, lets see what we got... */
1628 if (ies
.cause
< 1) {
1629 /* Success of some sort */
1630 ast_log(LOG_DEBUG
, "Looks like success of some sort (%d), %d answers\n", ies
.cause
, ies
.anscount
);
1631 if (ast_test_flag(trans
, FLAG_ENCRYPT
)) {
1632 authpass
= encrypted
;
1636 /* Pass back up answers */
1637 if (trans
->parent
&& trans
->parent
->dr
) {
1638 y
= trans
->parent
->respcount
;
1639 for (x
=0;x
<ies
.anscount
;x
++) {
1640 if (trans
->parent
->respcount
< trans
->parent
->maxcount
) {
1641 /* Make sure it's not already there */
1642 for (z
=0;z
<trans
->parent
->respcount
;z
++) {
1643 if ((trans
->parent
->dr
[z
].techint
== ies
.answers
[x
]->protocol
) &&
1644 !strcmp(trans
->parent
->dr
[z
].dest
, (char *)ies
.answers
[x
]->data
))
1647 if (z
== trans
->parent
->respcount
) {
1648 /* Copy into parent responses */
1649 trans
->parent
->dr
[trans
->parent
->respcount
].flags
= ntohs(ies
.answers
[x
]->flags
);
1650 trans
->parent
->dr
[trans
->parent
->respcount
].techint
= ies
.answers
[x
]->protocol
;
1651 trans
->parent
->dr
[trans
->parent
->respcount
].weight
= ntohs(ies
.answers
[x
]->weight
);
1652 trans
->parent
->dr
[trans
->parent
->respcount
].eid
= ies
.answers
[x
]->eid
;
1653 if (ies
.expiration
> 0)
1654 trans
->parent
->dr
[trans
->parent
->respcount
].expiration
= ies
.expiration
;
1656 trans
->parent
->dr
[trans
->parent
->respcount
].expiration
= dundi_cache_time
;
1657 dundi_eid_to_str(trans
->parent
->dr
[trans
->parent
->respcount
].eid_str
,
1658 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].eid_str
),
1659 &ies
.answers
[x
]->eid
);
1660 ast_copy_string(trans
->parent
->dr
[trans
->parent
->respcount
].dest
, (char *)ies
.answers
[x
]->data
,
1661 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].dest
));
1662 ast_copy_string(trans
->parent
->dr
[trans
->parent
->respcount
].tech
, tech2str(ies
.answers
[x
]->protocol
),
1663 sizeof(trans
->parent
->dr
[trans
->parent
->respcount
].tech
));
1664 trans
->parent
->respcount
++;
1665 ast_clear_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_DONT_ASK
);
1666 } else if (trans
->parent
->dr
[z
].weight
> ies
.answers
[x
]->weight
) {
1667 /* Update weight if appropriate */
1668 trans
->parent
->dr
[z
].weight
= ies
.answers
[x
]->weight
;
1671 ast_log(LOG_NOTICE
, "Dropping excessive answers to request for %s@%s\n",
1672 trans
->parent
->number
, trans
->parent
->dcontext
);
1674 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1675 the cache know if this request was unaffected by our entity list. */
1676 cache_save(&trans
->them_eid
, trans
->parent
, y
,
1677 ies
.hint
? ast_test_flag_nonstd(ies
.hint
, htons(DUNDI_HINT_UNAFFECTED
)) : 0, ies
.expiration
, 0);
1679 cache_save_hint(&trans
->them_eid
, trans
->parent
, ies
.hint
, ies
.expiration
);
1680 if (ast_test_flag_nonstd(ies
.hint
, htons(DUNDI_HINT_TTL_EXPIRED
)))
1681 ast_set_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_TTL_EXPIRED
);
1682 if (ast_test_flag_nonstd(ies
.hint
, htons(DUNDI_HINT_DONT_ASK
))) {
1683 if (strlen((char *)ies
.hint
->data
) > strlen(trans
->parent
->hmd
->exten
)) {
1684 ast_copy_string(trans
->parent
->hmd
->exten
, (char *)ies
.hint
->data
,
1685 sizeof(trans
->parent
->hmd
->exten
));
1688 ast_clear_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_DONT_ASK
);
1691 if (ies
.expiration
> 0) {
1692 if (trans
->parent
->expiration
> ies
.expiration
) {
1693 trans
->parent
->expiration
= ies
.expiration
;
1697 /* Close connection if not final */
1699 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1703 /* Auth failure, check for data */
1705 /* Cancel if they didn't already */
1706 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1710 case DUNDI_COMMAND_EIDRESPONSE
:
1711 /* A dialplan response, lets see what we got... */
1712 if (ies
.cause
< 1) {
1713 /* Success of some sort */
1714 ast_log(LOG_DEBUG
, "Looks like success of some sort (%d)\n", ies
.cause
);
1715 if (ast_test_flag(trans
, FLAG_ENCRYPT
)) {
1716 authpass
= encrypted
;
1720 /* Pass back up answers */
1721 if (trans
->parent
&& trans
->parent
->dei
&& ies
.q_org
) {
1722 if (!trans
->parent
->respcount
) {
1723 trans
->parent
->respcount
++;
1725 ast_copy_string(trans
->parent
->dei
->orgunit
, ies
.q_dept
, sizeof(trans
->parent
->dei
->orgunit
));
1727 ast_copy_string(trans
->parent
->dei
->org
, ies
.q_org
, sizeof(trans
->parent
->dei
->org
));
1729 ast_copy_string(trans
->parent
->dei
->locality
, ies
.q_locality
, sizeof(trans
->parent
->dei
->locality
));
1730 if (ies
.q_stateprov
)
1731 ast_copy_string(trans
->parent
->dei
->stateprov
, ies
.q_stateprov
, sizeof(trans
->parent
->dei
->stateprov
));
1733 ast_copy_string(trans
->parent
->dei
->country
, ies
.q_country
, sizeof(trans
->parent
->dei
->country
));
1735 ast_copy_string(trans
->parent
->dei
->email
, ies
.q_email
, sizeof(trans
->parent
->dei
->email
));
1737 ast_copy_string(trans
->parent
->dei
->phone
, ies
.q_phone
, sizeof(trans
->parent
->dei
->phone
));
1739 ast_copy_string(trans
->parent
->dei
->ipaddr
, ies
.q_ipaddr
, sizeof(trans
->parent
->dei
->ipaddr
));
1740 if (!dundi_eid_cmp(&trans
->them_eid
, &trans
->parent
->query_eid
)) {
1741 /* If it's them, update our address */
1742 ast_copy_string(trans
->parent
->dei
->ipaddr
, ast_inet_ntoa(trans
->addr
.sin_addr
), sizeof(trans
->parent
->dei
->ipaddr
));
1746 if (ast_test_flag_nonstd(ies
.hint
, htons(DUNDI_HINT_TTL_EXPIRED
)))
1747 ast_set_flag_nonstd(trans
->parent
->hmd
, DUNDI_HINT_TTL_EXPIRED
);
1750 /* Close connection if not final */
1752 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1756 /* Auth failure, check for data */
1758 /* Cancel if they didn't already */
1759 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1763 case DUNDI_COMMAND_REGRESPONSE
:
1764 /* A dialplan response, lets see what we got... */
1765 if (ies
.cause
< 1) {
1767 /* Success of some sort */
1768 if (ast_test_flag(trans
, FLAG_ENCRYPT
)) {
1769 hasauth
= encrypted
;
1774 ast_log(LOG_NOTICE
, "Reponse to register not authorized!\n");
1776 dundi_ie_append_cause(&ied
, DUNDI_IE_CAUSE
, DUNDI_CAUSE_NOAUTH
, "Improper signature in answer");
1777 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, &ied
);
1780 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
),
1781 dundi_eid_to_str(eid_str2
, sizeof(eid_str2
), &trans
->them_eid
));
1782 /* Close connection if not final */
1784 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1787 /* Auth failure, cancel if they didn't for some reason */
1789 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1793 case DUNDI_COMMAND_INVALID
:
1794 case DUNDI_COMMAND_NULL
:
1795 case DUNDI_COMMAND_PRECACHERP
:
1796 /* Do nothing special */
1798 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1800 case DUNDI_COMMAND_ENCREJ
:
1801 if ((ast_test_flag(trans
, FLAG_SENDFULLKEY
)) || AST_LIST_EMPTY(&trans
->lasttrans
) || !(peer
= find_peer(&trans
->them_eid
))) {
1802 /* No really, it's over at this point */
1804 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
1806 /* Send with full key */
1807 ast_set_flag(trans
, FLAG_SENDFULLKEY
);
1809 /* Ooops, we got a final message, start by sending ACK... */
1810 dundi_ack(trans
, hdr
->cmdresp
& 0x80);
1811 trans
->aseqno
= trans
->iseqno
;
1812 /* Now, we gotta create a new transaction */
1813 if (!reset_transaction(trans
)) {
1814 /* Make sure handle_frame doesn't destroy us */
1815 hdr
->cmdresp
&= 0x7f;
1816 /* Parse the message we transmitted */
1817 memset(&ies
, 0, sizeof(ies
));
1818 dundi_parse_ies(&ies
, (AST_LIST_FIRST(&trans
->lasttrans
))->h
->ies
, (AST_LIST_FIRST(&trans
->lasttrans
))->datalen
- sizeof(struct dundi_hdr
));
1819 /* Reconstruct outgoing encrypted packet */
1820 memset(&ied
, 0, sizeof(ied
));
1821 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->us_eid
);
1822 dundi_ie_append_raw(&ied
, DUNDI_IE_SHAREDKEY
, peer
->txenckey
, 128);
1823 dundi_ie_append_raw(&ied
, DUNDI_IE_SIGNATURE
, peer
->txenckey
+ 128, 128);
1825 dundi_ie_append_encdata(&ied
, DUNDI_IE_ENCDATA
, ies
.encblock
->iv
, ies
.encblock
->encdata
, ies
.enclen
);
1826 dundi_send(trans
, DUNDI_COMMAND_ENCRYPT
, 0, (AST_LIST_FIRST(&trans
->lasttrans
))->h
->cmdresp
& 0x80, &ied
);
1827 peer
->sentfullkey
= 1;
1832 case DUNDI_COMMAND_ENCRYPT
:
1834 /* No nested encryption! */
1835 if ((trans
->iseqno
== 1) && !trans
->oseqno
) {
1836 if (!ies
.eids
[0] || !(peer
= find_peer(ies
.eids
[0])) ||
1837 ((!ies
.encsharedkey
|| !ies
.encsig
) && !ies
.keycrc32
) ||
1838 (check_key(peer
, ies
.encsharedkey
, ies
.encsig
, ies
.keycrc32
) < 1)) {
1840 dundi_send(trans
, DUNDI_COMMAND_ENCREJ
, 0, 1, NULL
);
1844 apply_peer(trans
, peer
);
1845 /* Key passed, use new contexts for this session */
1846 trans
->ecx
= peer
->them_ecx
;
1847 trans
->dcx
= peer
->them_dcx
;
1849 if (ast_test_flag(trans
, FLAG_ENCRYPT
) && ies
.encblock
&& ies
.enclen
) {
1850 struct dundi_hdr
*dhdr
;
1851 unsigned char decoded
[MAX_PACKET_SIZE
];
1853 ddatalen
= sizeof(decoded
);
1854 dhdr
= dundi_decrypt(trans
, decoded
, &ddatalen
, hdr
, ies
.encblock
, ies
.enclen
);
1856 /* Handle decrypted response */
1858 dundi_showframe(dhdr
, 3, &trans
->addr
, ddatalen
- sizeof(struct dundi_hdr
));
1859 handle_command_response(trans
, dhdr
, ddatalen
- sizeof(struct dundi_hdr
), 1);
1860 /* Carry back final flag */
1861 hdr
->cmdresp
|= dhdr
->cmdresp
& 0x80;
1864 ast_log(LOG_DEBUG
, "Ouch, decrypt failed :(\n");
1868 /* Turn off encryption */
1869 ast_clear_flag(trans
, FLAG_ENCRYPT
);
1870 dundi_send(trans
, DUNDI_COMMAND_ENCREJ
, 0, 1, NULL
);
1874 /* Send unknown command if we don't know it, with final flag IFF it's the
1875 first command in the dialog and only if we haven't recieved final notification */
1877 dundi_ie_append_byte(&ied
, DUNDI_IE_UNKNOWN
, cmd
);
1878 dundi_send(trans
, DUNDI_COMMAND_UNKNOWN
, 0, !hdr
->oseqno
, &ied
);
1884 static void destroy_packet(struct dundi_packet
*pack
, int needfree
);
1885 static void destroy_packets(struct packetlist
*p
)
1887 struct dundi_packet
*pack
;
1889 while ((pack
= AST_LIST_REMOVE_HEAD(p
, list
))) {
1890 if (pack
->retransid
> -1)
1891 ast_sched_del(sched
, pack
->retransid
);
1897 static int ack_trans(struct dundi_transaction
*trans
, int iseqno
)
1899 struct dundi_packet
*pack
;
1901 /* Ack transmitted packet corresponding to iseqno */
1902 AST_LIST_TRAVERSE(&trans
->packets
, pack
, list
) {
1903 if ((pack
->h
->oseqno
+ 1) % 255 == iseqno
) {
1904 destroy_packet(pack
, 0);
1905 if (!AST_LIST_EMPTY(&trans
->lasttrans
)) {
1906 ast_log(LOG_WARNING
, "Whoa, there was still a last trans?\n");
1907 destroy_packets(&trans
->lasttrans
);
1909 AST_LIST_INSERT_HEAD(&trans
->lasttrans
, pack
, list
);
1910 if (trans
->autokillid
> -1)
1911 ast_sched_del(sched
, trans
->autokillid
);
1912 trans
->autokillid
= -1;
1920 static int handle_frame(struct dundi_hdr
*h
, struct sockaddr_in
*sin
, int datalen
)
1922 struct dundi_transaction
*trans
;
1923 trans
= find_transaction(h
, sin
);
1925 dundi_reject(h
, sin
);
1928 /* Got a transaction, see where this header fits in */
1929 if (h
->oseqno
== trans
->iseqno
) {
1930 /* Just what we were looking for... Anything but ack increments iseqno */
1931 if (ack_trans(trans
, h
->iseqno
) && ast_test_flag(trans
, FLAG_FINAL
)) {
1932 /* If final, we're done */
1933 destroy_trans(trans
, 0);
1936 if (h
->cmdresp
!= DUNDI_COMMAND_ACK
) {
1937 trans
->oiseqno
= trans
->iseqno
;
1939 handle_command_response(trans
, h
, datalen
, 0);
1941 if (trans
->aseqno
!= trans
->iseqno
) {
1942 dundi_ack(trans
, h
->cmdresp
& 0x80);
1943 trans
->aseqno
= trans
->iseqno
;
1945 /* Delete any saved last transmissions */
1946 destroy_packets(&trans
->lasttrans
);
1947 if (h
->cmdresp
& 0x80) {
1948 /* Final -- destroy now */
1949 destroy_trans(trans
, 0);
1951 } else if (h
->oseqno
== trans
->oiseqno
) {
1952 /* Last incoming sequence number -- send ACK without processing */
1953 dundi_ack(trans
, 0);
1955 /* Out of window -- simply drop */
1956 ast_log(LOG_DEBUG
, "Dropping packet out of window!\n");
1961 static int socket_read(int *id
, int fd
, short events
, void *cbdata
)
1963 struct sockaddr_in sin
;
1965 struct dundi_hdr
*h
;
1966 char buf
[MAX_PACKET_SIZE
];
1969 res
= recvfrom(netsocket
, buf
, sizeof(buf
) - 1, 0,(struct sockaddr
*) &sin
, &len
);
1971 if (errno
!= ECONNREFUSED
)
1972 ast_log(LOG_WARNING
, "Error: %s\n", strerror(errno
));
1975 if (res
< sizeof(struct dundi_hdr
)) {
1976 ast_log(LOG_WARNING
, "midget packet received (%d of %d min)\n", res
, (int)sizeof(struct dundi_hdr
));
1980 h
= (struct dundi_hdr
*)buf
;
1982 dundi_showframe(h
, 1, &sin
, res
- sizeof(struct dundi_hdr
));
1983 AST_LIST_LOCK(&peers
);
1984 handle_frame(h
, &sin
, res
- sizeof(struct dundi_hdr
));
1985 AST_LIST_UNLOCK(&peers
);
1989 static void build_secret(char *secret
, int seclen
)
1991 unsigned char tmp
[16];
1995 ast_base64encode(secret
, tmp
, sizeof(tmp
), seclen
);
1996 /* Eliminate potential bad characters */
1997 while((s
= strchr(secret
, ';'))) *s
= '+';
1998 while((s
= strchr(secret
, '/'))) *s
= '+';
1999 while((s
= strchr(secret
, ':'))) *s
= '+';
2000 while((s
= strchr(secret
, '@'))) *s
= '+';
2004 static void save_secret(const char *newkey
, const char *oldkey
)
2008 snprintf(tmp
, sizeof(tmp
), "%s;%s", oldkey
, newkey
);
2010 snprintf(tmp
, sizeof(tmp
), "%s", newkey
);
2011 rotatetime
= time(NULL
) + DUNDI_SECRET_TIME
;
2012 ast_db_put(secretpath
, "secret", tmp
);
2013 snprintf(tmp
, sizeof(tmp
), "%d", (int)rotatetime
);
2014 ast_db_put(secretpath
, "secretexpiry", tmp
);
2017 static void load_password(void)
2024 ast_db_get(secretpath
, "secretexpiry", tmp
, sizeof(tmp
));
2025 if (!ast_get_time_t(tmp
, &expired
, 0, NULL
)) {
2026 ast_db_get(secretpath
, "secret", tmp
, sizeof(tmp
));
2027 current
= strchr(tmp
, ';');
2034 if ((time(NULL
) - expired
) < 0) {
2035 if ((expired
- time(NULL
)) > DUNDI_SECRET_TIME
)
2036 expired
= time(NULL
) + DUNDI_SECRET_TIME
;
2037 } else if ((time(NULL
) - (expired
+ DUNDI_SECRET_TIME
)) < 0) {
2046 /* Current key is still valid, just setup rotatation properly */
2047 ast_copy_string(cursecret
, current
, sizeof(cursecret
));
2048 rotatetime
= expired
;
2050 /* Current key is out of date, rotate or eliminate all together */
2051 build_secret(cursecret
, sizeof(cursecret
));
2052 save_secret(cursecret
, last
);
2056 static void check_password(void)
2063 printf("%ld/%ld\n", now
, rotatetime
);
2065 if ((now
- rotatetime
) >= 0) {
2066 /* Time to rotate keys */
2067 ast_copy_string(oldsecret
, cursecret
, sizeof(oldsecret
));
2068 build_secret(cursecret
, sizeof(cursecret
));
2069 save_secret(cursecret
, oldsecret
);
2073 static void *network_thread(void *ignore
)
2075 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2076 from the network, and queue them for delivery to the channels */
2078 /* Establish I/O callback for socket read */
2079 ast_io_add(io
, netsocket
, socket_read
, AST_IO_IN
, NULL
);
2081 res
= ast_sched_wait(sched
);
2082 if ((res
> 1000) || (res
< 0))
2084 res
= ast_io_wait(io
, res
);
2086 AST_LIST_LOCK(&peers
);
2087 ast_sched_runq(sched
);
2088 AST_LIST_UNLOCK(&peers
);
2095 static void *process_precache(void *ign
)
2097 struct dundi_precache_queue
*qe
;
2106 AST_LIST_LOCK(&pcq
);
2107 if ((qe
= AST_LIST_FIRST(&pcq
))) {
2108 if (!qe
->expiration
) {
2109 /* Gone... Remove... */
2110 AST_LIST_REMOVE_HEAD(&pcq
, list
);
2112 } else if (qe
->expiration
< now
) {
2113 /* Process this entry */
2115 ast_copy_string(context
, qe
->context
, sizeof(context
));
2116 ast_copy_string(number
, qe
->number
, sizeof(number
));
2120 AST_LIST_UNLOCK(&pcq
);
2122 dundi_precache(context
, number
);
2130 static int start_network_thread(void)
2132 ast_pthread_create_background(&netthreadid
, NULL
, network_thread
, NULL
);
2133 ast_pthread_create_background(&precachethreadid
, NULL
, process_precache
, NULL
);
2137 static int dundi_do_debug(int fd
, int argc
, char *argv
[])
2140 return RESULT_SHOWUSAGE
;
2142 ast_cli(fd
, "DUNDi Debugging Enabled\n");
2143 return RESULT_SUCCESS
;
2146 static int dundi_do_store_history(int fd
, int argc
, char *argv
[])
2149 return RESULT_SHOWUSAGE
;
2150 global_storehistory
= 1;
2151 ast_cli(fd
, "DUNDi History Storage Enabled\n");
2152 return RESULT_SUCCESS
;
2155 static int dundi_flush(int fd
, int argc
, char *argv
[])
2158 if ((argc
< 2) || (argc
> 3))
2159 return RESULT_SHOWUSAGE
;
2161 if (!strcasecmp(argv
[2], "stats"))
2164 return RESULT_SHOWUSAGE
;
2167 /* Flush statistics */
2168 struct dundi_peer
*p
;
2170 AST_LIST_LOCK(&peers
);
2171 AST_LIST_TRAVERSE(&peers
, p
, list
) {
2172 for (x
= 0;x
< DUNDI_TIMING_HISTORY
; x
++) {
2174 free(p
->lookups
[x
]);
2175 p
->lookups
[x
] = NULL
;
2176 p
->lookuptimes
[x
] = 0;
2180 AST_LIST_UNLOCK(&peers
);
2182 ast_db_deltree("dundi/cache", NULL
);
2183 ast_cli(fd
, "DUNDi Cache Flushed\n");
2185 return RESULT_SUCCESS
;
2188 static int dundi_no_debug(int fd
, int argc
, char *argv
[])
2191 return RESULT_SHOWUSAGE
;
2193 ast_cli(fd
, "DUNDi Debugging Disabled\n");
2194 return RESULT_SUCCESS
;
2197 static int dundi_no_store_history(int fd
, int argc
, char *argv
[])
2200 return RESULT_SHOWUSAGE
;
2201 global_storehistory
= 0;
2202 ast_cli(fd
, "DUNDi History Storage Disabled\n");
2203 return RESULT_SUCCESS
;
2206 static char *model2str(int model
)
2209 case DUNDI_MODEL_INBOUND
:
2211 case DUNDI_MODEL_OUTBOUND
:
2213 case DUNDI_MODEL_SYMMETRIC
:
2220 static char *complete_peer_helper(const char *line
, const char *word
, int pos
, int state
, int rpos
)
2224 struct dundi_peer
*p
;
2229 AST_LIST_LOCK(&peers
);
2231 AST_LIST_TRAVERSE(&peers
, p
, list
) {
2232 const char *s
= dundi_eid_to_str(eid_str
, sizeof(eid_str
), &p
->eid
);
2233 if (!strncasecmp(word
, s
, len
) && ++which
> state
)
2234 ret
= ast_strdup(s
);
2236 AST_LIST_UNLOCK(&peers
);
2240 static char *complete_peer_4(const char *line
, const char *word
, int pos
, int state
)
2242 return complete_peer_helper(line
, word
, pos
, state
, 3);
2245 static int rescomp(const void *a
, const void *b
)
2247 const struct dundi_result
*resa
, *resb
;
2250 if (resa
->weight
< resb
->weight
)
2252 if (resa
->weight
> resb
->weight
)
2257 static void sort_results(struct dundi_result
*results
, int count
)
2259 qsort(results
, count
, sizeof(results
[0]), rescomp
);
2262 static int dundi_do_lookup(int fd
, int argc
, char *argv
[])
2270 struct dundi_result dr
[MAX_RESULTS
];
2271 struct timeval start
;
2272 if ((argc
< 3) || (argc
> 4))
2273 return RESULT_SHOWUSAGE
;
2275 if (!strcasecmp(argv
[3], "bypass"))
2278 return RESULT_SHOWUSAGE
;
2280 ast_copy_string(tmp
, argv
[2], sizeof(tmp
));
2281 context
= strchr(tmp
, '@');
2286 start
= ast_tvnow();
2287 res
= dundi_lookup(dr
, MAX_RESULTS
, NULL
, context
, tmp
, bypass
);
2290 ast_cli(fd
, "DUNDi lookup returned error.\n");
2292 ast_cli(fd
, "DUNDi lookup returned no results.\n");
2294 sort_results(dr
, res
);
2295 for (x
=0;x
<res
;x
++) {
2296 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
));
2297 ast_cli(fd
, " from %s, expires in %d s\n", dr
[x
].eid_str
, dr
[x
].expiration
);
2299 ast_cli(fd
, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start
));
2300 return RESULT_SUCCESS
;
2303 static int dundi_do_precache(int fd
, int argc
, char *argv
[])
2308 struct timeval start
;
2309 if ((argc
< 3) || (argc
> 3))
2310 return RESULT_SHOWUSAGE
;
2311 ast_copy_string(tmp
, argv
[2], sizeof(tmp
));
2312 context
= strchr(tmp
, '@');
2317 start
= ast_tvnow();
2318 res
= dundi_precache(context
, tmp
);
2321 ast_cli(fd
, "DUNDi precache returned error.\n");
2323 ast_cli(fd
, "DUNDi precache returned no error.\n");
2324 ast_cli(fd
, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start
));
2325 return RESULT_SUCCESS
;
2328 static int dundi_do_query(int fd
, int argc
, char *argv
[])
2334 struct dundi_entity_info dei
;
2335 if ((argc
< 3) || (argc
> 3))
2336 return RESULT_SHOWUSAGE
;
2337 if (dundi_str_to_eid(&eid
, argv
[2])) {
2338 ast_cli(fd
, "'%s' is not a valid EID!\n", argv
[2]);
2339 return RESULT_SHOWUSAGE
;
2341 ast_copy_string(tmp
, argv
[2], sizeof(tmp
));
2342 context
= strchr(tmp
, '@');
2347 res
= dundi_query_eid(&dei
, context
, eid
);
2349 ast_cli(fd
, "DUNDi Query EID returned error.\n");
2351 ast_cli(fd
, "DUNDi Query EID returned no results.\n");
2353 ast_cli(fd
, "DUNDi Query EID succeeded:\n");
2354 ast_cli(fd
, "Department: %s\n", dei
.orgunit
);
2355 ast_cli(fd
, "Organization: %s\n", dei
.org
);
2356 ast_cli(fd
, "City/Locality: %s\n", dei
.locality
);
2357 ast_cli(fd
, "State/Province: %s\n", dei
.stateprov
);
2358 ast_cli(fd
, "Country: %s\n", dei
.country
);
2359 ast_cli(fd
, "E-mail: %s\n", dei
.email
);
2360 ast_cli(fd
, "Phone: %s\n", dei
.phone
);
2361 ast_cli(fd
, "IP Address: %s\n", dei
.ipaddr
);
2363 return RESULT_SUCCESS
;
2366 static int dundi_show_peer(int fd
, int argc
, char *argv
[])
2368 struct dundi_peer
*peer
;
2369 struct permission
*p
;
2375 return RESULT_SHOWUSAGE
;
2376 AST_LIST_LOCK(&peers
);
2377 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
2378 if (!strcasecmp(dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
), argv
[3]))
2382 switch(peer
->order
) {
2387 order
= "Secondary";
2393 order
= "Quartiary";
2398 ast_cli(fd
, "Peer: %s\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
2399 ast_cli(fd
, "Model: %s\n", model2str(peer
->model
));
2400 ast_cli(fd
, "Host: %s\n", peer
->addr
.sin_addr
.s_addr
? ast_inet_ntoa(peer
->addr
.sin_addr
) : "<Unspecified>");
2401 ast_cli(fd
, "Dynamic: %s\n", peer
->dynamic
? "yes" : "no");
2402 ast_cli(fd
, "Reg: %s\n", peer
->registerid
< 0 ? "No" : "Yes");
2403 ast_cli(fd
, "In Key: %s\n", ast_strlen_zero(peer
->inkey
) ? "<None>" : peer
->inkey
);
2404 ast_cli(fd
, "Out Key: %s\n", ast_strlen_zero(peer
->outkey
) ? "<None>" : peer
->outkey
);
2405 if (!AST_LIST_EMPTY(&peer
->include
))
2406 ast_cli(fd
, "Include logic%s:\n", peer
->model
& DUNDI_MODEL_OUTBOUND
? "" : " (IGNORED)");
2407 AST_LIST_TRAVERSE(&peer
->include
, p
, list
)
2408 ast_cli(fd
, "-- %s %s\n", p
->allow
? "include" : "do not include", p
->name
);
2409 if (!AST_LIST_EMPTY(&peer
->permit
))
2410 ast_cli(fd
, "Query logic%s:\n", peer
->model
& DUNDI_MODEL_INBOUND
? "" : " (IGNORED)");
2411 AST_LIST_TRAVERSE(&peer
->permit
, p
, list
)
2412 ast_cli(fd
, "-- %s %s\n", p
->allow
? "permit" : "deny", p
->name
);
2414 for (x
= 0;x
< DUNDI_TIMING_HISTORY
; x
++) {
2415 if (peer
->lookups
[x
]) {
2417 ast_cli(fd
, "Last few query times:\n");
2418 ast_cli(fd
, "-- %d. %s (%d ms)\n", x
+ 1, peer
->lookups
[x
], peer
->lookuptimes
[x
]);
2423 ast_cli(fd
, "Average query time: %d ms\n", peer
->avgms
);
2425 ast_cli(fd
, "No such peer '%s'\n", argv
[3]);
2426 AST_LIST_UNLOCK(&peers
);
2427 return RESULT_SUCCESS
;
2430 static int dundi_show_peers(int fd
, int argc
, char *argv
[])
2432 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2433 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2434 struct dundi_peer
*peer
;
2435 int registeredonly
=0;
2438 int online_peers
= 0;
2439 int offline_peers
= 0;
2440 int unmonitored_peers
= 0;
2441 int total_peers
= 0;
2443 if ((argc
!= 3) && (argc
!= 4) && (argc
!= 5))
2444 return RESULT_SHOWUSAGE
;
2446 if (!strcasecmp(argv
[3], "registered")) {
2449 return RESULT_SHOWUSAGE
;
2451 AST_LIST_LOCK(&peers
);
2452 ast_cli(fd
, FORMAT2
, "EID", "Host", "Model", "AvgTime", "Status");
2453 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
2455 int print_line
= -1;
2458 if (registeredonly
&& !peer
->addr
.sin_addr
.s_addr
)
2461 if (peer
->lastms
< 0) {
2462 strcpy(status
, "UNREACHABLE");
2465 else if (peer
->lastms
> peer
->maxms
) {
2466 snprintf(status
, sizeof(status
), "LAGGED (%d ms)", peer
->lastms
);
2469 else if (peer
->lastms
) {
2470 snprintf(status
, sizeof(status
), "OK (%d ms)", peer
->lastms
);
2474 strcpy(status
, "UNKNOWN");
2478 strcpy(status
, "Unmonitored");
2479 unmonitored_peers
++;
2482 snprintf(avgms
, sizeof(avgms
), "%d ms", peer
->avgms
);
2484 strcpy(avgms
, "Unavail");
2485 snprintf(srch
, sizeof(srch
), FORMAT
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
),
2486 peer
->addr
.sin_addr
.s_addr
? ast_inet_ntoa(peer
->addr
.sin_addr
) : "(Unspecified)",
2487 peer
->dynamic
? "(D)" : "(S)", model2str(peer
->model
), avgms
, status
);
2490 if (!strcasecmp(argv
[3],"include") && strstr(srch
,argv
[4])) {
2492 } else if (!strcasecmp(argv
[3],"exclude") && !strstr(srch
,argv
[4])) {
2494 } else if (!strcasecmp(argv
[3],"begin") && !strncasecmp(srch
,argv
[4],strlen(argv
[4]))) {
2502 ast_cli(fd
, FORMAT
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
),
2503 peer
->addr
.sin_addr
.s_addr
? ast_inet_ntoa(peer
->addr
.sin_addr
) : "(Unspecified)",
2504 peer
->dynamic
? "(D)" : "(S)", model2str(peer
->model
), avgms
, status
);
2507 ast_cli(fd
, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers
, online_peers
, offline_peers
, unmonitored_peers
);
2508 AST_LIST_UNLOCK(&peers
);
2509 return RESULT_SUCCESS
;
2514 static int dundi_show_trans(int fd
, int argc
, char *argv
[])
2516 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2517 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2518 struct dundi_transaction
*trans
;
2520 return RESULT_SHOWUSAGE
;
2521 AST_LIST_LOCK(&peers
);
2522 ast_cli(fd
, FORMAT2
, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2523 AST_LIST_TRAVERSE(&alltrans
, trans
, all
) {
2524 ast_cli(fd
, FORMAT
, ast_inet_ntoa(trans
->addr
.sin_addr
),
2525 ntohs(trans
->addr
.sin_port
), trans
->strans
, trans
->dtrans
, trans
->oseqno
, trans
->iseqno
, trans
->aseqno
);
2527 AST_LIST_UNLOCK(&peers
);
2528 return RESULT_SUCCESS
;
2533 static int dundi_show_entityid(int fd
, int argc
, char *argv
[])
2537 return RESULT_SHOWUSAGE
;
2538 AST_LIST_LOCK(&peers
);
2539 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &global_eid
);
2540 AST_LIST_UNLOCK(&peers
);
2541 ast_cli(fd
, "Global EID for this system is '%s'\n", eid_str
);
2542 return RESULT_SUCCESS
;
2545 static int dundi_show_requests(int fd
, int argc
, char *argv
[])
2547 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2548 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2549 struct dundi_request
*req
;
2552 return RESULT_SHOWUSAGE
;
2553 AST_LIST_LOCK(&peers
);
2554 ast_cli(fd
, FORMAT2
, "Number", "Context", "Root", "Max", "Rsp");
2555 AST_LIST_TRAVERSE(&requests
, req
, list
) {
2556 ast_cli(fd
, FORMAT
, req
->number
, req
->dcontext
,
2557 dundi_eid_zero(&req
->root_eid
) ? "<unspecified>" : dundi_eid_to_str(eidstr
, sizeof(eidstr
), &req
->root_eid
), req
->maxcount
, req
->respcount
);
2559 AST_LIST_UNLOCK(&peers
);
2560 return RESULT_SUCCESS
;
2565 /* Grok-a-dial DUNDi */
2567 static int dundi_show_mappings(int fd
, int argc
, char *argv
[])
2569 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2570 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2571 struct dundi_mapping
*map
;
2574 return RESULT_SHOWUSAGE
;
2575 AST_LIST_LOCK(&peers
);
2576 ast_cli(fd
, FORMAT2
, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2577 AST_LIST_TRAVERSE(&mappings
, map
, list
) {
2578 ast_cli(fd
, FORMAT
, map
->dcontext
, map
->weight
,
2579 ast_strlen_zero(map
->lcontext
) ? "<none>" : map
->lcontext
,
2580 dundi_flags2str(fs
, sizeof(fs
), map
->options
), tech2str(map
->tech
), map
->dest
);
2582 AST_LIST_UNLOCK(&peers
);
2583 return RESULT_SUCCESS
;
2588 static int dundi_show_precache(int fd
, int argc
, char *argv
[])
2590 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2591 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2592 struct dundi_precache_queue
*qe
;
2597 return RESULT_SHOWUSAGE
;
2599 ast_cli(fd
, FORMAT2
, "Number", "Context", "Expiration");
2600 AST_LIST_LOCK(&pcq
);
2601 AST_LIST_TRAVERSE(&pcq
, qe
, list
) {
2602 s
= qe
->expiration
- now
;
2607 ast_cli(fd
, FORMAT
, qe
->number
, qe
->context
, h
,m
,s
);
2609 AST_LIST_UNLOCK(&pcq
);
2611 return RESULT_SUCCESS
;
2616 static char debug_usage
[] =
2617 "Usage: dundi debug\n"
2618 " Enables dumping of DUNDi packets for debugging purposes\n";
2620 static char no_debug_usage
[] =
2621 "Usage: dundi no debug\n"
2622 " Disables dumping of DUNDi packets for debugging purposes\n";
2624 static char store_history_usage
[] =
2625 "Usage: dundi store history\n"
2626 " Enables storing of DUNDi requests and times for debugging\n"
2629 static char no_store_history_usage
[] =
2630 "Usage: dundi no store history\n"
2631 " Disables storing of DUNDi requests and times for debugging\n"
2634 static char show_peers_usage
[] =
2635 "Usage: dundi show peers\n"
2636 " Lists all known DUNDi peers.\n";
2638 static char show_trans_usage
[] =
2639 "Usage: dundi show trans\n"
2640 " Lists all known DUNDi transactions.\n";
2642 static char show_mappings_usage
[] =
2643 "Usage: dundi show mappings\n"
2644 " Lists all known DUNDi mappings.\n";
2646 static char show_precache_usage
[] =
2647 "Usage: dundi show precache\n"
2648 " Lists all known DUNDi scheduled precache updates.\n";
2650 static char show_entityid_usage
[] =
2651 "Usage: dundi show entityid\n"
2652 " Displays the global entityid for this host.\n";
2654 static char show_peer_usage
[] =
2655 "Usage: dundi show peer [peer]\n"
2656 " Provide a detailed description of a specifid DUNDi peer.\n";
2658 static char show_requests_usage
[] =
2659 "Usage: dundi show requests\n"
2660 " Lists all known pending DUNDi requests.\n";
2662 static char lookup_usage
[] =
2663 "Usage: dundi lookup <number>[@context] [bypass]\n"
2664 " Lookup the given number within the given DUNDi context\n"
2665 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2666 "keyword is specified.\n";
2668 static char precache_usage
[] =
2669 "Usage: dundi precache <number>[@context]\n"
2670 " Lookup the given number within the given DUNDi context\n"
2671 "(or e164 if none is specified) and precaches the results to any\n"
2672 "upstream DUNDi push servers.\n";
2674 static char query_usage
[] =
2675 "Usage: dundi query <entity>[@context]\n"
2676 " Attempts to retrieve contact information for a specific\n"
2677 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2678 "e164 if none is specified).\n";
2680 static char flush_usage
[] =
2681 "Usage: dundi flush [stats]\n"
2682 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2683 "'stats' is present, clears timer statistics instead of normal\n"
2686 static struct ast_cli_entry cli_dundi
[] = {
2687 { { "dundi", "debug", NULL
},
2688 dundi_do_debug
, "Enable DUNDi debugging",
2691 { { "dundi", "store", "history", NULL
},
2692 dundi_do_store_history
, "Enable DUNDi historic records",
2693 store_history_usage
},
2695 { { "dundi", "no", "store", "history", NULL
},
2696 dundi_no_store_history
, "Disable DUNDi historic records",
2697 no_store_history_usage
},
2699 { { "dundi", "flush", NULL
},
2700 dundi_flush
, "Flush DUNDi cache",
2703 { { "dundi", "no", "debug", NULL
},
2704 dundi_no_debug
, "Disable DUNDi debugging",
2707 { { "dundi", "show", "peers", NULL
},
2708 dundi_show_peers
, "Show defined DUNDi peers",
2711 { { "dundi", "show", "trans", NULL
},
2712 dundi_show_trans
, "Show active DUNDi transactions",
2715 { { "dundi", "show", "entityid", NULL
},
2716 dundi_show_entityid
, "Display Global Entity ID",
2717 show_entityid_usage
},
2719 { { "dundi", "show", "mappings", NULL
},
2720 dundi_show_mappings
, "Show DUNDi mappings",
2721 show_mappings_usage
},
2723 { { "dundi", "show", "precache", NULL
},
2724 dundi_show_precache
, "Show DUNDi precache",
2725 show_precache_usage
},
2727 { { "dundi", "show", "requests", NULL
},
2728 dundi_show_requests
, "Show DUNDi requests",
2729 show_requests_usage
},
2731 { { "dundi", "show", "peer", NULL
},
2732 dundi_show_peer
, "Show info on a specific DUNDi peer",
2733 show_peer_usage
, complete_peer_4
},
2735 { { "dundi", "lookup", NULL
},
2736 dundi_do_lookup
, "Lookup a number in DUNDi",
2739 { { "dundi", "precache", NULL
},
2740 dundi_do_precache
, "Precache a number in DUNDi",
2743 { { "dundi", "query", NULL
},
2744 dundi_do_query
, "Query a DUNDi EID",
2748 static struct dundi_transaction
*create_transaction(struct dundi_peer
*p
)
2750 struct dundi_transaction
*trans
;
2753 /* Don't allow creation of transactions to non-registered peers */
2754 if (p
&& !p
->addr
.sin_addr
.s_addr
)
2756 tid
= get_trans_id();
2759 trans
= ast_calloc(1, sizeof(*trans
));
2761 if (global_storehistory
) {
2762 trans
->start
= ast_tvnow();
2763 ast_set_flag(trans
, FLAG_STOREHIST
);
2765 trans
->retranstimer
= DUNDI_DEFAULT_RETRANS_TIMER
;
2766 trans
->autokillid
= -1;
2768 apply_peer(trans
, p
);
2769 if (!p
->sentfullkey
)
2770 ast_set_flag(trans
, FLAG_SENDFULLKEY
);
2772 trans
->strans
= tid
;
2773 AST_LIST_INSERT_HEAD(&alltrans
, trans
, all
);
2778 static int dundi_xmit(struct dundi_packet
*pack
)
2782 dundi_showframe(pack
->h
, 0, &pack
->parent
->addr
, pack
->datalen
- sizeof(struct dundi_hdr
));
2783 res
= sendto(netsocket
, pack
->data
, pack
->datalen
, 0, (struct sockaddr
*)&pack
->parent
->addr
, sizeof(pack
->parent
->addr
));
2785 ast_log(LOG_WARNING
, "Failed to transmit to '%s:%d': %s\n",
2786 ast_inet_ntoa(pack
->parent
->addr
.sin_addr
),
2787 ntohs(pack
->parent
->addr
.sin_port
), strerror(errno
));
2794 static void destroy_packet(struct dundi_packet
*pack
, int needfree
)
2797 AST_LIST_REMOVE(&pack
->parent
->packets
, pack
, list
);
2798 if (pack
->retransid
> -1)
2799 ast_sched_del(sched
, pack
->retransid
);
2803 pack
->retransid
= -1;
2806 static void destroy_trans(struct dundi_transaction
*trans
, int fromtimeout
)
2808 struct dundi_peer
*peer
;
2813 if (ast_test_flag(trans
, FLAG_ISREG
| FLAG_ISQUAL
| FLAG_STOREHIST
)) {
2814 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
2815 if (peer
->regtrans
== trans
)
2816 peer
->regtrans
= NULL
;
2817 if (peer
->qualtrans
== trans
) {
2819 if (peer
->lastms
> -1)
2820 ast_log(LOG_NOTICE
, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
2823 ms
= ast_tvdiff_ms(ast_tvnow(), peer
->qualtx
);
2826 if (ms
< peer
->maxms
) {
2827 if ((peer
->lastms
>= peer
->maxms
) || (peer
->lastms
< 0))
2828 ast_log(LOG_NOTICE
, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
2829 } else if (peer
->lastms
< peer
->maxms
) {
2830 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
);
2834 peer
->qualtrans
= NULL
;
2836 if (ast_test_flag(trans
, FLAG_STOREHIST
)) {
2837 if (trans
->parent
&& !ast_strlen_zero(trans
->parent
->number
)) {
2838 if (!dundi_eid_cmp(&trans
->them_eid
, &peer
->eid
)) {
2841 if (peer
->lookups
[DUNDI_TIMING_HISTORY
-1])
2842 free(peer
->lookups
[DUNDI_TIMING_HISTORY
-1]);
2843 for (x
=DUNDI_TIMING_HISTORY
-1;x
>0;x
--) {
2844 peer
->lookuptimes
[x
] = peer
->lookuptimes
[x
-1];
2845 peer
->lookups
[x
] = peer
->lookups
[x
-1];
2846 if (peer
->lookups
[x
]) {
2847 peer
->avgms
+= peer
->lookuptimes
[x
];
2851 peer
->lookuptimes
[0] = ast_tvdiff_ms(ast_tvnow(), trans
->start
);
2852 peer
->lookups
[0] = ast_malloc(strlen(trans
->parent
->number
) + strlen(trans
->parent
->dcontext
) + 2);
2853 if (peer
->lookups
[0]) {
2854 sprintf(peer
->lookups
[0], "%s@%s", trans
->parent
->number
, trans
->parent
->dcontext
);
2855 peer
->avgms
+= peer
->lookuptimes
[0];
2865 if (trans
->parent
) {
2866 /* Unlink from parent if appropriate */
2867 AST_LIST_REMOVE(&trans
->parent
->trans
, trans
, parentlist
);
2868 if (AST_LIST_EMPTY(&trans
->parent
->trans
)) {
2869 /* Wake up sleeper */
2870 if (trans
->parent
->pfds
[1] > -1) {
2871 write(trans
->parent
->pfds
[1], "killa!", 6);
2875 /* Unlink from all trans */
2876 AST_LIST_REMOVE(&alltrans
, trans
, all
);
2877 destroy_packets(&trans
->packets
);
2878 destroy_packets(&trans
->lasttrans
);
2879 if (trans
->autokillid
> -1)
2880 ast_sched_del(sched
, trans
->autokillid
);
2881 trans
->autokillid
= -1;
2882 if (trans
->thread
) {
2883 /* If used by a thread, mark as dead and be done */
2884 ast_set_flag(trans
, FLAG_DEAD
);
2889 static int dundi_rexmit(void *data
)
2891 struct dundi_packet
*pack
;
2893 AST_LIST_LOCK(&peers
);
2895 if (pack
->retrans
< 1) {
2896 pack
->retransid
= -1;
2897 if (!ast_test_flag(pack
->parent
, FLAG_ISQUAL
))
2898 ast_log(LOG_NOTICE
, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2899 ast_inet_ntoa(pack
->parent
->addr
.sin_addr
),
2900 ntohs(pack
->parent
->addr
.sin_port
), pack
->h
->oseqno
, ntohs(pack
->h
->strans
));
2901 destroy_trans(pack
->parent
, 1);
2904 /* Decrement retransmission, try again */
2909 AST_LIST_UNLOCK(&peers
);
2913 static int dundi_send(struct dundi_transaction
*trans
, int cmdresp
, int flags
, int final
, struct dundi_ie_data
*ied
)
2915 struct dundi_packet
*pack
;
2919 len
= sizeof(struct dundi_packet
) + sizeof(struct dundi_hdr
) + (ied
? ied
->pos
: 0);
2920 /* Reserve enough space for encryption */
2921 if (ast_test_flag(trans
, FLAG_ENCRYPT
))
2923 pack
= ast_calloc(1, len
);
2925 pack
->h
= (struct dundi_hdr
*)(pack
->data
);
2926 if (cmdresp
!= DUNDI_COMMAND_ACK
) {
2927 pack
->retransid
= ast_sched_add(sched
, trans
->retranstimer
, dundi_rexmit
, pack
);
2928 pack
->retrans
= DUNDI_DEFAULT_RETRANS
- 1;
2929 AST_LIST_INSERT_HEAD(&trans
->packets
, pack
, list
);
2931 pack
->parent
= trans
;
2932 pack
->h
->strans
= htons(trans
->strans
);
2933 pack
->h
->dtrans
= htons(trans
->dtrans
);
2934 pack
->h
->iseqno
= trans
->iseqno
;
2935 pack
->h
->oseqno
= trans
->oseqno
;
2936 pack
->h
->cmdresp
= cmdresp
;
2937 pack
->datalen
= sizeof(struct dundi_hdr
);
2939 memcpy(pack
->h
->ies
, ied
->buf
, ied
->pos
);
2940 pack
->datalen
+= ied
->pos
;
2943 pack
->h
->cmdresp
|= DUNDI_COMMAND_FINAL
;
2944 ast_set_flag(trans
, FLAG_FINAL
);
2946 pack
->h
->cmdflags
= flags
;
2947 if (cmdresp
!= DUNDI_COMMAND_ACK
) {
2949 trans
->oseqno
= trans
->oseqno
% 256;
2951 trans
->aseqno
= trans
->iseqno
;
2952 /* If we have their public key, encrypt */
2953 if (ast_test_flag(trans
, FLAG_ENCRYPT
)) {
2955 case DUNDI_COMMAND_REGREQ
:
2956 case DUNDI_COMMAND_REGRESPONSE
:
2957 case DUNDI_COMMAND_DPDISCOVER
:
2958 case DUNDI_COMMAND_DPRESPONSE
:
2959 case DUNDI_COMMAND_EIDQUERY
:
2960 case DUNDI_COMMAND_EIDRESPONSE
:
2961 case DUNDI_COMMAND_PRECACHERQ
:
2962 case DUNDI_COMMAND_PRECACHERP
:
2964 dundi_showframe(pack
->h
, 2, &trans
->addr
, pack
->datalen
- sizeof(struct dundi_hdr
));
2965 res
= dundi_encrypt(trans
, pack
);
2973 res
= dundi_xmit(pack
);
2975 ast_log(LOG_NOTICE
, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), &trans
->them_eid
));
2977 if (cmdresp
== DUNDI_COMMAND_ACK
)
2984 static int do_autokill(void *data
)
2986 struct dundi_transaction
*trans
= data
;
2988 ast_log(LOG_NOTICE
, "Transaction to '%s' took too long to ACK, destroying\n",
2989 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &trans
->them_eid
));
2990 trans
->autokillid
= -1;
2991 destroy_trans(trans
, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
2995 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data
*ied
, char *context
, dundi_eid
*eid
, dundi_eid
*us
)
2997 struct dundi_peer
*p
;
2998 if (!dundi_eid_cmp(eid
, us
)) {
2999 dundi_ie_append_eid(ied
, DUNDI_IE_EID_DIRECT
, eid
);
3002 AST_LIST_LOCK(&peers
);
3003 AST_LIST_TRAVERSE(&peers
, p
, list
) {
3004 if (!dundi_eid_cmp(&p
->eid
, eid
)) {
3005 if (has_permission(&p
->include
, context
))
3006 dundi_ie_append_eid(ied
, DUNDI_IE_EID_DIRECT
, eid
);
3008 dundi_ie_append_eid(ied
, DUNDI_IE_EID
, eid
);
3013 dundi_ie_append_eid(ied
, DUNDI_IE_EID
, eid
);
3014 AST_LIST_UNLOCK(&peers
);
3017 static int dundi_discover(struct dundi_transaction
*trans
)
3019 struct dundi_ie_data ied
;
3021 if (!trans
->parent
) {
3022 ast_log(LOG_WARNING
, "Tried to discover a transaction with no parent?!?\n");
3025 memset(&ied
, 0, sizeof(ied
));
3026 dundi_ie_append_short(&ied
, DUNDI_IE_VERSION
, DUNDI_DEFAULT_VERSION
);
3027 if (!dundi_eid_zero(&trans
->us_eid
))
3028 dundi_ie_append_eid(&ied
, DUNDI_IE_EID_DIRECT
, &trans
->us_eid
);
3029 for (x
=0;x
<trans
->eidcount
;x
++)
3030 dundi_ie_append_eid_appropriately(&ied
, trans
->parent
->dcontext
, &trans
->eids
[x
], &trans
->us_eid
);
3031 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_NUMBER
, trans
->parent
->number
);
3032 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_CONTEXT
, trans
->parent
->dcontext
);
3033 dundi_ie_append_short(&ied
, DUNDI_IE_TTL
, trans
->ttl
);
3034 if (trans
->parent
->cbypass
)
3035 dundi_ie_append(&ied
, DUNDI_IE_CACHEBYPASS
);
3036 if (trans
->autokilltimeout
)
3037 trans
->autokillid
= ast_sched_add(sched
, trans
->autokilltimeout
, do_autokill
, trans
);
3038 return dundi_send(trans
, DUNDI_COMMAND_DPDISCOVER
, 0, 0, &ied
);
3041 static int precache_trans(struct dundi_transaction
*trans
, struct dundi_mapping
*maps
, int mapcount
, int *minexp
, int *foundanswers
)
3043 struct dundi_ie_data ied
;
3046 int expiration
= dundi_cache_time
;
3048 dundi_eid
*avoid
[1] = { NULL
, };
3049 int direct
[1] = { 0, };
3050 struct dundi_result dr
[MAX_RESULTS
];
3051 struct dundi_hint_metadata hmd
;
3052 if (!trans
->parent
) {
3053 ast_log(LOG_WARNING
, "Tried to discover a transaction with no parent?!?\n");
3056 memset(&hmd
, 0, sizeof(hmd
));
3057 memset(&dr
, 0, sizeof(dr
));
3058 /* Look up the answers we're going to include */
3059 for (x
=0;x
<mapcount
;x
++)
3060 ouranswers
= dundi_lookup_local(dr
, maps
+ x
, trans
->parent
->number
, &trans
->us_eid
, ouranswers
, &hmd
);
3063 for (x
=0;x
<ouranswers
;x
++) {
3064 if (dr
[x
].weight
< max
)
3068 /* If we do not have a canonical result, keep looking */
3069 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
);
3071 /* Append answer in result */
3076 if (ouranswers
> 0) {
3077 *foundanswers
+= ouranswers
;
3078 memset(&ied
, 0, sizeof(ied
));
3079 dundi_ie_append_short(&ied
, DUNDI_IE_VERSION
, DUNDI_DEFAULT_VERSION
);
3080 if (!dundi_eid_zero(&trans
->us_eid
))
3081 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->us_eid
);
3082 for (x
=0;x
<trans
->eidcount
;x
++)
3083 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->eids
[x
]);
3084 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_NUMBER
, trans
->parent
->number
);
3085 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_CONTEXT
, trans
->parent
->dcontext
);
3086 dundi_ie_append_short(&ied
, DUNDI_IE_TTL
, trans
->ttl
);
3087 for (x
=0;x
<ouranswers
;x
++) {
3089 if (dr
[x
].expiration
&& (expiration
> dr
[x
].expiration
))
3090 expiration
= dr
[x
].expiration
;
3091 dundi_ie_append_answer(&ied
, DUNDI_IE_ANSWER
, &dr
[x
].eid
, dr
[x
].techint
, dr
[x
].flags
, dr
[x
].weight
, dr
[x
].dest
);
3093 dundi_ie_append_hint(&ied
, DUNDI_IE_HINT
, hmd
.flags
, hmd
.exten
);
3094 dundi_ie_append_short(&ied
, DUNDI_IE_EXPIRATION
, expiration
);
3095 if (trans
->autokilltimeout
)
3096 trans
->autokillid
= ast_sched_add(sched
, trans
->autokilltimeout
, do_autokill
, trans
);
3097 if (expiration
< *minexp
)
3098 *minexp
= expiration
;
3099 return dundi_send(trans
, DUNDI_COMMAND_PRECACHERQ
, 0, 0, &ied
);
3101 /* Oops, nothing to send... */
3102 destroy_trans(trans
, 0);
3107 static int dundi_query(struct dundi_transaction
*trans
)
3109 struct dundi_ie_data ied
;
3111 if (!trans
->parent
) {
3112 ast_log(LOG_WARNING
, "Tried to query a transaction with no parent?!?\n");
3115 memset(&ied
, 0, sizeof(ied
));
3116 dundi_ie_append_short(&ied
, DUNDI_IE_VERSION
, DUNDI_DEFAULT_VERSION
);
3117 if (!dundi_eid_zero(&trans
->us_eid
))
3118 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->us_eid
);
3119 for (x
=0;x
<trans
->eidcount
;x
++)
3120 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &trans
->eids
[x
]);
3121 dundi_ie_append_eid(&ied
, DUNDI_IE_REQEID
, &trans
->parent
->query_eid
);
3122 dundi_ie_append_str(&ied
, DUNDI_IE_CALLED_CONTEXT
, trans
->parent
->dcontext
);
3123 dundi_ie_append_short(&ied
, DUNDI_IE_TTL
, trans
->ttl
);
3124 if (trans
->autokilltimeout
)
3125 trans
->autokillid
= ast_sched_add(sched
, trans
->autokilltimeout
, do_autokill
, trans
);
3126 return dundi_send(trans
, DUNDI_COMMAND_EIDQUERY
, 0, 0, &ied
);
3129 static int discover_transactions(struct dundi_request
*dr
)
3131 struct dundi_transaction
*trans
;
3132 AST_LIST_LOCK(&peers
);
3133 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3134 dundi_discover(trans
);
3136 AST_LIST_UNLOCK(&peers
);
3140 static int precache_transactions(struct dundi_request
*dr
, struct dundi_mapping
*maps
, int mapcount
, int *expiration
, int *foundanswers
)
3142 struct dundi_transaction
*trans
;
3144 /* Mark all as "in thread" so they don't disappear */
3145 AST_LIST_LOCK(&peers
);
3146 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3148 ast_log(LOG_WARNING
, "This shouldn't happen, really...\n");
3151 AST_LIST_UNLOCK(&peers
);
3153 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3154 if (!ast_test_flag(trans
, FLAG_DEAD
))
3155 precache_trans(trans
, maps
, mapcount
, expiration
, foundanswers
);
3158 /* Cleanup any that got destroyed in the mean time */
3159 AST_LIST_LOCK(&peers
);
3160 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr
->trans
, trans
, parentlist
) {
3162 if (ast_test_flag(trans
, FLAG_DEAD
)) {
3163 ast_log(LOG_DEBUG
, "Our transaction went away!\n");
3164 /* This is going to remove the transaction from the dundi_request's list, as well
3165 * as the global transactions list */
3166 destroy_trans(trans
, 0);
3169 AST_LIST_TRAVERSE_SAFE_END
3170 AST_LIST_UNLOCK(&peers
);
3175 static int query_transactions(struct dundi_request
*dr
)
3177 struct dundi_transaction
*trans
;
3179 AST_LIST_LOCK(&peers
);
3180 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3183 AST_LIST_UNLOCK(&peers
);
3188 static int optimize_transactions(struct dundi_request
*dr
, int order
)
3190 /* Minimize the message propagation through DUNDi by
3191 alerting the network to hops which should be not be considered */
3192 struct dundi_transaction
*trans
;
3193 struct dundi_peer
*peer
;
3198 AST_LIST_LOCK(&peers
);
3199 AST_LIST_TRAVERSE(&dr
->trans
, trans
, parentlist
) {
3200 /* Pop off the true root */
3201 if (trans
->eidcount
) {
3202 tmp
= trans
->eids
[--trans
->eidcount
];
3205 tmp
= trans
->us_eid
;
3209 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
3210 if (has_permission(&peer
->include
, dr
->dcontext
) &&
3211 dundi_eid_cmp(&peer
->eid
, &trans
->them_eid
) &&
3212 (peer
->order
<= order
)) {
3213 /* For each other transaction, make sure we don't
3214 ask this EID about the others if they're not
3215 already in the list */
3216 if (!dundi_eid_cmp(&tmp
, &peer
->eid
))
3219 for (x
=0;x
<trans
->eidcount
;x
++) {
3220 if (!dundi_eid_cmp(&trans
->eids
[x
], &peer
->eid
))
3224 if (x
== trans
->eidcount
) {
3225 /* Nope not in the list, if needed, add us at the end since we're the source */
3226 if (trans
->eidcount
< DUNDI_MAX_STACK
- needpush
) {
3227 trans
->eids
[trans
->eidcount
++] = peer
->eid
;
3228 /* Need to insert the real root (or us) at the bottom now as
3229 a requirement now. */
3235 /* If necessary, push the true root back on the end */
3237 trans
->eids
[trans
->eidcount
++] = tmp
;
3239 AST_LIST_UNLOCK(&peers
);
3244 static int append_transaction(struct dundi_request
*dr
, struct dundi_peer
*p
, int ttl
, dundi_eid
*avoid
[])
3246 struct dundi_transaction
*trans
;
3251 /* Ignore if not registered */
3252 if (!p
->addr
.sin_addr
.s_addr
)
3254 if (p
->maxms
&& ((p
->lastms
< 0) || (p
->lastms
>= p
->maxms
)))
3256 if (ast_strlen_zero(dr
->number
))
3257 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
);
3259 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
);
3260 trans
= create_transaction(p
);
3265 for (x
= 0; avoid
[x
] && (x
< DUNDI_MAX_STACK
); x
++)
3266 trans
->eids
[x
] = *avoid
[x
];
3267 trans
->eidcount
= x
;
3268 AST_LIST_INSERT_HEAD(&dr
->trans
, trans
, parentlist
);
3273 static void cancel_request(struct dundi_request
*dr
)
3275 struct dundi_transaction
*trans
;
3277 AST_LIST_LOCK(&peers
);
3278 while ((trans
= AST_LIST_REMOVE_HEAD(&dr
->trans
, parentlist
))) {
3279 /* Orphan transaction from request */
3280 trans
->parent
= NULL
;
3281 /* Send final cancel */
3282 dundi_send(trans
, DUNDI_COMMAND_CANCEL
, 0, 1, NULL
);
3284 AST_LIST_UNLOCK(&peers
);
3287 static void abort_request(struct dundi_request
*dr
)
3289 struct dundi_transaction
*trans
;
3291 AST_LIST_LOCK(&peers
);
3292 while ((trans
= AST_LIST_FIRST(&dr
->trans
))) {
3293 /* This will remove the transaction from the list */
3294 destroy_trans(trans
, 0);
3296 AST_LIST_UNLOCK(&peers
);
3299 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
[])
3301 struct dundi_peer
*p
;
3307 AST_LIST_LOCK(&peers
);
3308 AST_LIST_TRAVERSE(&peers
, p
, list
) {
3309 if (modeselect
== 1) {
3310 /* Send the precache to push upstreams only! */
3311 pass
= has_permission(&p
->permit
, dr
->dcontext
) && (p
->pcmodel
& DUNDI_MODEL_OUTBOUND
);
3314 /* Normal lookup / EID query */
3315 pass
= has_permission(&p
->include
, dr
->dcontext
);
3316 allowconnect
= p
->model
& DUNDI_MODEL_OUTBOUND
;
3319 if (!dundi_eid_cmp(skip
, &p
->eid
))
3323 if (p
->order
<= order
) {
3324 /* Check order first, then check cache, regardless of
3325 omissions, this gets us more likely to not have an
3327 if((nocache
|| !(res
= cache_lookup(dr
, &p
->eid
, dr
->crc32
, &dr
->expiration
)))) {
3329 /* Make sure we haven't already seen it and that it won't
3330 affect our answer */
3331 for (x
=0;avoid
[x
];x
++) {
3332 if (!dundi_eid_cmp(avoid
[x
], &p
->eid
) || !dundi_eid_cmp(avoid
[x
], &p
->us_eid
)) {
3333 /* If not a direct connection, it affects our answer */
3334 if (directs
&& !directs
[x
])
3335 ast_clear_flag_nonstd(dr
->hmd
, DUNDI_HINT_UNAFFECTED
);
3339 /* Make sure we can ask */
3341 if (!avoid
[x
] && (!blockempty
|| !dundi_eid_zero(&p
->us_eid
))) {
3342 /* Check for a matching or 0 cache entry */
3343 append_transaction(dr
, p
, ttl
, avoid
);
3345 ast_log(LOG_DEBUG
, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str
, sizeof(eid_str
), avoid
[x
]));
3349 } else if (!*skipped
|| (p
->order
< *skipped
))
3350 *skipped
= p
->order
;
3353 AST_LIST_UNLOCK(&peers
);
3356 static int register_request(struct dundi_request
*dr
, struct dundi_request
**pending
)
3358 struct dundi_request
*cur
;
3361 AST_LIST_LOCK(&peers
);
3362 AST_LIST_TRAVERSE(&requests
, cur
, list
) {
3364 ast_log(LOG_DEBUG
, "Checking '%s@%s' vs '%s@%s'\n", cur
->dcontext
, cur
->number
,
3365 dr
->dcontext
, dr
->number
);
3366 if (!strcasecmp(cur
->dcontext
, dr
->dcontext
) &&
3367 !strcasecmp(cur
->number
, dr
->number
) &&
3368 (!dundi_eid_cmp(&cur
->root_eid
, &dr
->root_eid
) || (cur
->crc32
== dr
->crc32
))) {
3369 ast_log(LOG_DEBUG
, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n",
3370 cur
->dcontext
, cur
->number
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &cur
->root_eid
), cur
->crc32
);
3377 ast_log(LOG_DEBUG
, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n",
3378 dr
->number
, dr
->dcontext
, dundi_eid_to_str(eid_str
, sizeof(eid_str
), &dr
->root_eid
), dr
->crc32
);
3379 /* Go ahead and link us in since nobody else is searching for this */
3380 AST_LIST_INSERT_HEAD(&requests
, dr
, list
);
3383 AST_LIST_UNLOCK(&peers
);
3387 static void unregister_request(struct dundi_request
*dr
)
3389 AST_LIST_LOCK(&peers
);
3390 AST_LIST_REMOVE(&requests
, dr
, list
);
3391 AST_LIST_UNLOCK(&peers
);
3394 static int check_request(struct dundi_request
*dr
)
3396 struct dundi_request
*cur
;
3398 AST_LIST_LOCK(&peers
);
3399 AST_LIST_TRAVERSE(&requests
, cur
, list
) {
3403 AST_LIST_UNLOCK(&peers
);
3408 static unsigned long avoid_crc32(dundi_eid
*avoid
[])
3410 /* Idea is that we're calculating a checksum which is independent of
3411 the order that the EID's are listed in */
3412 unsigned long acrc32
= 0;
3414 for (x
=0;avoid
[x
];x
++) {
3415 /* Order doesn't matter */
3417 acrc32
^= crc32(0L, (unsigned char *)avoid
[x
], sizeof(dundi_eid
));
3423 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
[])
3426 struct dundi_request dr
, *pending
;
3427 dundi_eid
*rooteid
=NULL
;
3435 struct timeval start
;
3437 /* Don't do anthing for a hungup channel */
3438 if (chan
&& chan
->_softhangup
)
3441 ttlms
= DUNDI_FLUFF_TIME
+ ttl
* DUNDI_TTL_TIME
;
3443 for (x
=0;avoid
[x
];x
++)
3445 /* Now perform real check */
3446 memset(&dr
, 0, sizeof(dr
));
3447 if (pipe(dr
.pfds
)) {
3448 ast_log(LOG_WARNING
, "pipe failed: %s\n" , strerror(errno
));
3453 dr
.maxcount
= maxret
;
3454 dr
.expiration
= *expiration
;
3455 dr
.cbypass
= cbypass
;
3456 dr
.crc32
= avoid_crc32(avoid
);
3457 ast_copy_string(dr
.dcontext
, dcontext
? dcontext
: "e164", sizeof(dr
.dcontext
));
3458 ast_copy_string(dr
.number
, number
, sizeof(dr
.number
));
3460 dr
.root_eid
= *rooteid
;
3461 res
= register_request(&dr
, &pending
);
3463 /* Already a request */
3464 if (rooteid
&& !dundi_eid_cmp(&dr
.root_eid
, &pending
->root_eid
)) {
3465 /* This is on behalf of someone else. Go ahead and close this out since
3466 they'll get their answer anyway. */
3467 ast_log(LOG_DEBUG
, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3468 dr
.number
,dr
.dcontext
,dundi_eid_to_str(eid_str
, sizeof(eid_str
), &dr
.root_eid
));
3473 /* Wait for the cache to populate */
3474 ast_log(LOG_DEBUG
, "Waiting for similar request for '%s@%s' for '%s'\n",
3475 dr
.number
,dr
.dcontext
,dundi_eid_to_str(eid_str
, sizeof(eid_str
), &pending
->root_eid
));
3476 start
= ast_tvnow();
3477 while(check_request(pending
) && (ast_tvdiff_ms(ast_tvnow(), start
) < ttlms
) && (!chan
|| !chan
->_softhangup
)) {
3478 /* XXX Would be nice to have a way to poll/select here XXX */
3479 /* XXX this is a busy wait loop!!! */
3482 /* Continue on as normal, our cache should kick in */
3485 /* Create transactions */
3490 build_transactions(&dr
, ttl
, order
, &foundcache
, &skipped
, blockempty
, cbypass
, modeselect
, skip
, avoid
, direct
);
3491 } while (skipped
&& !foundcache
&& AST_LIST_EMPTY(&dr
.trans
));
3492 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3493 do this earlier because we didn't know if we were going to have transactions
3496 ast_set_flag_nonstd(hmd
, DUNDI_HINT_TTL_EXPIRED
);
3498 unregister_request(&dr
);
3504 /* Optimize transactions */
3505 optimize_transactions(&dr
, order
);
3506 /* Actually perform transactions */
3507 discover_transactions(&dr
);
3508 /* Wait for transaction to come back */
3509 start
= ast_tvnow();
3510 while (!AST_LIST_EMPTY(&dr
.trans
) && (ast_tvdiff_ms(ast_tvnow(), start
) < ttlms
) && (!chan
|| !chan
->_softhangup
)) {
3512 ast_waitfor_n_fd(dr
.pfds
, 1, &ms
, NULL
);
3514 if (chan
&& chan
->_softhangup
)
3515 ast_log(LOG_DEBUG
, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan
->name
, dr
.number
, dr
.dcontext
);
3516 cancel_request(&dr
);
3517 unregister_request(&dr
);
3519 *expiration
= dr
.expiration
;
3525 int dundi_lookup(struct dundi_result
*result
, int maxret
, struct ast_channel
*chan
, const char *dcontext
, const char *number
, int cbypass
)
3527 struct dundi_hint_metadata hmd
;
3528 dundi_eid
*avoid
[1] = { NULL
, };
3529 int direct
[1] = { 0, };
3530 int expiration
= dundi_cache_time
;
3531 memset(&hmd
, 0, sizeof(hmd
));
3532 hmd
.flags
= DUNDI_HINT_DONT_ASK
| DUNDI_HINT_UNAFFECTED
;
3533 return dundi_lookup_internal(result
, maxret
, chan
, dcontext
, number
, dundi_ttl
, 0, &hmd
, &expiration
, cbypass
, 0, NULL
, avoid
, direct
);
3536 static void reschedule_precache(const char *number
, const char *context
, int expiration
)
3539 struct dundi_precache_queue
*qe
, *prev
;
3541 AST_LIST_LOCK(&pcq
);
3542 AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq
, qe
, list
) {
3543 if (!strcmp(number
, qe
->number
) && !strcasecmp(context
, qe
->context
)) {
3544 AST_LIST_REMOVE_CURRENT(&pcq
, list
);
3548 AST_LIST_TRAVERSE_SAFE_END
3551 len
+= strlen(number
) + 1;
3552 len
+= strlen(context
) + 1;
3553 if (!(qe
= ast_calloc(1, len
))) {
3554 AST_LIST_UNLOCK(&pcq
);
3557 strcpy(qe
->number
, number
);
3558 qe
->context
= qe
->number
+ strlen(number
) + 1;
3559 strcpy(qe
->context
, context
);
3561 time(&qe
->expiration
);
3562 qe
->expiration
+= expiration
;
3563 if ((prev
= AST_LIST_FIRST(&pcq
))) {
3564 while (AST_LIST_NEXT(prev
, list
) && ((AST_LIST_NEXT(prev
, list
))->expiration
<= qe
->expiration
))
3565 prev
= AST_LIST_NEXT(prev
, list
);
3566 AST_LIST_INSERT_AFTER(&pcq
, prev
, qe
, list
);
3568 AST_LIST_INSERT_HEAD(&pcq
, qe
, list
);
3569 AST_LIST_UNLOCK(&pcq
);
3572 static void dundi_precache_full(void)
3574 struct dundi_mapping
*cur
;
3575 struct ast_context
*con
;
3576 struct ast_exten
*e
;
3578 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
3579 ast_log(LOG_NOTICE
, "Should precache context '%s'\n", cur
->dcontext
);
3580 ast_lock_contexts();
3581 con
= ast_walk_contexts(NULL
);
3583 if (!strcasecmp(cur
->lcontext
, ast_get_context_name(con
))) {
3584 /* Found the match, now queue them all up */
3585 ast_lock_context(con
);
3586 e
= ast_walk_context_extensions(con
, NULL
);
3588 reschedule_precache(ast_get_extension_name(e
), cur
->dcontext
, 0);
3589 e
= ast_walk_context_extensions(con
, e
);
3591 ast_unlock_context(con
);
3593 con
= ast_walk_contexts(con
);
3595 ast_unlock_contexts();
3599 static int dundi_precache_internal(const char *context
, const char *number
, int ttl
, dundi_eid
*avoids
[])
3601 struct dundi_request dr
;
3602 struct dundi_hint_metadata hmd
;
3603 struct dundi_result dr2
[MAX_RESULTS
];
3604 struct timeval start
;
3605 struct dundi_mapping
*maps
= NULL
, *cur
;
3608 int foundcache
, skipped
, ttlms
, ms
;
3611 ast_log(LOG_DEBUG
, "Precache internal (%s@%s)!\n", number
, context
);
3613 AST_LIST_LOCK(&peers
);
3614 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
3615 if (!strcasecmp(cur
->dcontext
, context
))
3619 maps
= alloca(nummaps
* sizeof(*maps
));
3622 AST_LIST_TRAVERSE(&mappings
, cur
, list
) {
3623 if (!strcasecmp(cur
->dcontext
, context
))
3624 maps
[nummaps
++] = *cur
;
3628 AST_LIST_UNLOCK(&peers
);
3629 if (!nummaps
|| !maps
)
3631 ttlms
= DUNDI_FLUFF_TIME
+ ttl
* DUNDI_TTL_TIME
;
3632 memset(&dr2
, 0, sizeof(dr2
));
3633 memset(&dr
, 0, sizeof(dr
));
3634 memset(&hmd
, 0, sizeof(hmd
));
3636 ast_copy_string(dr
.number
, number
, sizeof(dr
.number
));
3637 ast_copy_string(dr
.dcontext
, context
? context
: "e164", sizeof(dr
.dcontext
));
3638 dr
.maxcount
= MAX_RESULTS
;
3639 dr
.expiration
= dundi_cache_time
;
3641 dr
.pfds
[0] = dr
.pfds
[1] = -1;
3643 build_transactions(&dr
, ttl
, 0, &foundcache
, &skipped
, 0, 1, 1, NULL
, avoids
, NULL
);
3644 optimize_transactions(&dr
, 0);
3646 precache_transactions(&dr
, maps
, nummaps
, &dr
.expiration
, &foundanswers
);
3648 if (dr
.expiration
> 0)
3649 reschedule_precache(dr
.number
, dr
.dcontext
, dr
.expiration
);
3651 ast_log(LOG_NOTICE
, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr
.expiration
, dr
.number
, dr
.dcontext
);
3653 start
= ast_tvnow();
3654 while (!AST_LIST_EMPTY(&dr
.trans
) && (ast_tvdiff_ms(ast_tvnow(), start
) < ttlms
)) {
3655 if (dr
.pfds
[0] > -1) {
3657 ast_waitfor_n_fd(dr
.pfds
, 1, &ms
, NULL
);
3661 cancel_request(&dr
);
3662 if (dr
.pfds
[0] > -1) {
3669 int dundi_precache(const char *context
, const char *number
)
3671 dundi_eid
*avoid
[1] = { NULL
, };
3672 return dundi_precache_internal(context
, number
, dundi_ttl
, avoid
);
3675 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
[])
3678 struct dundi_request dr
;
3679 dundi_eid
*rooteid
=NULL
;
3684 struct timeval start
;
3686 ttlms
= DUNDI_FLUFF_TIME
+ ttl
* DUNDI_TTL_TIME
;
3688 for (x
=0;avoid
[x
];x
++)
3690 /* Now perform real check */
3691 memset(&dr
, 0, sizeof(dr
));
3694 dr
.pfds
[0] = dr
.pfds
[1] = -1;
3695 ast_copy_string(dr
.dcontext
, dcontext
? dcontext
: "e164", sizeof(dr
.dcontext
));
3696 memcpy(&dr
.query_eid
, eid
, sizeof(dr
.query_eid
));
3698 dr
.root_eid
= *rooteid
;
3699 /* Create transactions */
3700 build_transactions(&dr
, ttl
, 9999, &foundcache
, &skipped
, blockempty
, 0, 0, NULL
, avoid
, NULL
);
3702 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3703 do this earlier because we didn't know if we were going to have transactions
3706 ast_set_flag_nonstd(hmd
, DUNDI_HINT_TTL_EXPIRED
);
3710 /* Optimize transactions */
3711 optimize_transactions(&dr
, 9999);
3712 /* Actually perform transactions */
3713 query_transactions(&dr
);
3714 /* Wait for transaction to come back */
3715 start
= ast_tvnow();
3716 while (!AST_LIST_EMPTY(&dr
.trans
) && (ast_tvdiff_ms(ast_tvnow(), start
) < ttlms
))
3722 int dundi_query_eid(struct dundi_entity_info
*dei
, const char *dcontext
, dundi_eid eid
)
3724 dundi_eid
*avoid
[1] = { NULL
, };
3725 struct dundi_hint_metadata hmd
;
3726 memset(&hmd
, 0, sizeof(hmd
));
3727 return dundi_query_eid_internal(dei
, dcontext
, &eid
, &hmd
, dundi_ttl
, 0, avoid
);
3730 static int dundifunc_read(struct ast_channel
*chan
, char *cmd
, char *num
, char *buf
, size_t len
)
3737 struct ast_module_user
*u
;
3738 struct dundi_result dr
[MAX_RESULTS
];
3742 if (ast_strlen_zero(num
)) {
3743 ast_log(LOG_WARNING
, "DUNDILOOKUP requires an argument (number)\n");
3747 u
= ast_module_user_add(chan
);
3749 context
= strchr(num
, '|');
3752 opts
= strchr(context
, '|');
3755 if (strchr(opts
, 'b'))
3760 if (ast_strlen_zero(context
))
3763 results
= dundi_lookup(dr
, MAX_RESULTS
, NULL
, context
, num
, bypass
);
3765 sort_results(dr
, results
);
3766 for (x
= 0; x
< results
; x
++) {
3767 if (ast_test_flag(dr
+ x
, DUNDI_FLAG_EXISTS
)) {
3768 snprintf(buf
, len
, "%s/%s", dr
[x
].tech
, dr
[x
].dest
);
3774 ast_module_user_remove(u
);
3780 * \ingroup functions
3783 static struct ast_custom_function dundi_function
= {
3784 .name
= "DUNDILOOKUP",
3785 .synopsis
= "Do a DUNDi lookup of a phone number.",
3786 .syntax
= "DUNDILOOKUP(number[|context[|options]])",
3787 .desc
= "This will do a DUNDi lookup of the given phone number.\n"
3788 "If no context is given, the default will be e164. The result of\n"
3789 "this function will the Technology/Resource found in the DUNDi\n"
3790 "lookup. If no results were found, the result will be blank.\n"
3791 "If the 'b' option is specified, the internal DUNDi cache will\n"
3793 .read
= dundifunc_read
,
3796 static void mark_peers(void)
3798 struct dundi_peer
*peer
;
3799 AST_LIST_LOCK(&peers
);
3800 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
3803 AST_LIST_UNLOCK(&peers
);
3806 static void mark_mappings(void)
3808 struct dundi_mapping
*map
;
3810 AST_LIST_LOCK(&peers
);
3811 AST_LIST_TRAVERSE(&mappings
, map
, list
) {
3814 AST_LIST_UNLOCK(&peers
);
3817 static void destroy_permissions(struct permissionlist
*permlist
)
3819 struct permission
*perm
;
3821 while ((perm
= AST_LIST_REMOVE_HEAD(permlist
, list
)))
3825 static void destroy_peer(struct dundi_peer
*peer
)
3827 if (peer
->registerid
> -1)
3828 ast_sched_del(sched
, peer
->registerid
);
3830 destroy_trans(peer
->regtrans
, 0);
3831 if (peer
->qualifyid
> -1)
3832 ast_sched_del(sched
, peer
->qualifyid
);
3833 destroy_permissions(&peer
->permit
);
3834 destroy_permissions(&peer
->include
);
3838 static void destroy_map(struct dundi_mapping
*map
)
3843 static void prune_peers(void)
3845 struct dundi_peer
*peer
;
3847 AST_LIST_LOCK(&peers
);
3848 AST_LIST_TRAVERSE_SAFE_BEGIN(&peers
, peer
, list
) {
3850 AST_LIST_REMOVE_CURRENT(&peers
, list
);
3854 AST_LIST_TRAVERSE_SAFE_END
3855 AST_LIST_UNLOCK(&peers
);
3858 static void prune_mappings(void)
3860 struct dundi_mapping
*map
;
3862 AST_LIST_LOCK(&peers
);
3863 AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings
, map
, list
) {
3865 AST_LIST_REMOVE_CURRENT(&mappings
, list
);
3869 AST_LIST_TRAVERSE_SAFE_END
3870 AST_LIST_UNLOCK(&peers
);
3873 static void append_permission(struct permissionlist
*permlist
, char *s
, int allow
)
3875 struct permission
*perm
;
3877 if (!(perm
= ast_calloc(1, sizeof(*perm
) + strlen(s
) + 1)))
3880 strcpy(perm
->name
, s
);
3881 perm
->allow
= allow
;
3883 AST_LIST_INSERT_TAIL(permlist
, perm
, list
);
3886 #define MAX_OPTS 128
3888 static void build_mapping(char *name
, char *value
)
3890 char *t
, *fields
[MAX_OPTS
];
3891 struct dundi_mapping
*map
;
3895 t
= ast_strdupa(value
);
3897 AST_LIST_TRAVERSE(&mappings
, map
, list
) {
3898 /* Find a double match */
3899 if (!strcasecmp(map
->dcontext
, name
) &&
3900 (!strncasecmp(map
->lcontext
, value
, strlen(map
->lcontext
)) &&
3901 (!value
[strlen(map
->lcontext
)] ||
3902 (value
[strlen(map
->lcontext
)] == ','))))
3906 if (!(map
= ast_calloc(1, sizeof(*map
))))
3908 AST_LIST_INSERT_HEAD(&mappings
, map
, list
);
3912 memset(fields
, 0, sizeof(fields
));
3914 while (t
&& x
< MAX_OPTS
) {
3921 } /* Russell was here, arrrr! */
3922 if ((x
== 1) && ast_strlen_zero(fields
[0])) {
3923 /* Placeholder mapping */
3924 ast_copy_string(map
->dcontext
, name
, sizeof(map
->dcontext
));
3926 } else if (x
>= 4) {
3927 ast_copy_string(map
->dcontext
, name
, sizeof(map
->dcontext
));
3928 ast_copy_string(map
->lcontext
, fields
[0], sizeof(map
->lcontext
));
3929 if ((sscanf(fields
[1], "%d", &map
->weight
) == 1) && (map
->weight
>= 0) && (map
->weight
< 60000)) {
3930 ast_copy_string(map
->dest
, fields
[3], sizeof(map
->dest
));
3931 if ((map
->tech
= str2tech(fields
[2]))) {
3935 ast_log(LOG_WARNING
, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields
[1], map
->dcontext
, map
->lcontext
);
3937 for (y
= 4;y
< x
; y
++) {
3938 if (!strcasecmp(fields
[y
], "nounsolicited"))
3939 map
->options
|= DUNDI_FLAG_NOUNSOLICITED
;
3940 else if (!strcasecmp(fields
[y
], "nocomunsolicit"))
3941 map
->options
|= DUNDI_FLAG_NOCOMUNSOLICIT
;
3942 else if (!strcasecmp(fields
[y
], "residential"))
3943 map
->options
|= DUNDI_FLAG_RESIDENTIAL
;
3944 else if (!strcasecmp(fields
[y
], "commercial"))
3945 map
->options
|= DUNDI_FLAG_COMMERCIAL
;
3946 else if (!strcasecmp(fields
[y
], "mobile"))
3947 map
->options
|= DUNDI_FLAG_MOBILE
;
3948 else if (!strcasecmp(fields
[y
], "nopartial"))
3949 map
->options
|= DUNDI_FLAG_INTERNAL_NOPARTIAL
;
3951 ast_log(LOG_WARNING
, "Don't know anything about option '%s'\n", fields
[y
]);
3954 ast_log(LOG_WARNING
, "Expected at least %d arguments in map, but got only %d\n", 4, x
);
3957 /* \note Called with the peers list already locked */
3958 static int do_register(void *data
)
3960 struct dundi_ie_data ied
;
3961 struct dundi_peer
*peer
= data
;
3964 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
));
3965 peer
->registerid
= ast_sched_add(sched
, default_expiration
* 1000, do_register
, data
);
3966 /* Destroy old transaction if there is one */
3968 destroy_trans(peer
->regtrans
, 0);
3969 peer
->regtrans
= create_transaction(peer
);
3970 if (peer
->regtrans
) {
3971 ast_set_flag(peer
->regtrans
, FLAG_ISREG
);
3972 memset(&ied
, 0, sizeof(ied
));
3973 dundi_ie_append_short(&ied
, DUNDI_IE_VERSION
, DUNDI_DEFAULT_VERSION
);
3974 dundi_ie_append_eid(&ied
, DUNDI_IE_EID
, &peer
->regtrans
->us_eid
);
3975 dundi_ie_append_short(&ied
, DUNDI_IE_EXPIRATION
, default_expiration
);
3976 dundi_send(peer
->regtrans
, DUNDI_COMMAND_REGREQ
, 0, 0, &ied
);
3979 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
));
3984 static int do_qualify(void *data
)
3986 struct dundi_peer
*peer
;
3988 peer
->qualifyid
= -1;
3989 qualify_peer(peer
, 0);
3993 static void qualify_peer(struct dundi_peer
*peer
, int schedonly
)
3996 if (peer
->qualifyid
> -1)
3997 ast_sched_del(sched
, peer
->qualifyid
);
3998 peer
->qualifyid
= -1;
3999 if (peer
->qualtrans
)
4000 destroy_trans(peer
->qualtrans
, 0);
4001 peer
->qualtrans
= NULL
;
4002 if (peer
->maxms
> 0) {
4004 if (peer
->lastms
< 0)
4008 peer
->qualifyid
= ast_sched_add(sched
, when
, do_qualify
, peer
);
4010 peer
->qualtrans
= create_transaction(peer
);
4011 if (peer
->qualtrans
) {
4012 peer
->qualtx
= ast_tvnow();
4013 ast_set_flag(peer
->qualtrans
, FLAG_ISQUAL
);
4014 dundi_send(peer
->qualtrans
, DUNDI_COMMAND_NULL
, 0, 1, NULL
);
4018 static void populate_addr(struct dundi_peer
*peer
, dundi_eid
*eid
)
4024 dundi_eid_to_str(eid_str
, sizeof(eid_str
), eid
);
4025 if (!ast_db_get("dundi/dpeers", eid_str
, data
, sizeof(data
))) {
4026 c
= strchr(data
, ':');
4030 if (sscanf(c
, "%d:%d", &port
, &expire
) == 2) {
4032 inet_aton(data
, &peer
->addr
.sin_addr
);
4033 peer
->addr
.sin_family
= AF_INET
;
4034 peer
->addr
.sin_port
= htons(port
);
4035 peer
->registerexpire
= ast_sched_add(sched
, (expire
+ 10) * 1000, do_register_expire
, peer
);
4042 static void build_peer(dundi_eid
*eid
, struct ast_variable
*v
, int *globalpcmode
)
4044 struct dundi_peer
*peer
;
4045 struct ast_hostent he
;
4051 AST_LIST_LOCK(&peers
);
4052 AST_LIST_TRAVERSE(&peers
, peer
, list
) {
4053 if (!dundi_eid_cmp(&peer
->eid
, eid
)) {
4058 /* Add us into the list */
4059 if (!(peer
= ast_calloc(1, sizeof(*peer
)))) {
4060 AST_LIST_UNLOCK(&peers
);
4063 peer
->registerid
= -1;
4064 peer
->registerexpire
= -1;
4065 peer
->qualifyid
= -1;
4066 peer
->addr
.sin_family
= AF_INET
;
4067 peer
->addr
.sin_port
= htons(DUNDI_PORT
);
4068 populate_addr(peer
, eid
);
4069 AST_LIST_INSERT_HEAD(&peers
, peer
, list
);
4073 peer
->us_eid
= global_eid
;
4074 destroy_permissions(&peer
->permit
);
4075 destroy_permissions(&peer
->include
);
4076 if (peer
->registerid
> -1)
4077 ast_sched_del(sched
, peer
->registerid
);
4078 peer
->registerid
= -1;
4079 for (; v
; v
= v
->next
) {
4080 if (!strcasecmp(v
->name
, "inkey")) {
4081 ast_copy_string(peer
->inkey
, v
->value
, sizeof(peer
->inkey
));
4082 } else if (!strcasecmp(v
->name
, "outkey")) {
4083 ast_copy_string(peer
->outkey
, v
->value
, sizeof(peer
->outkey
));
4084 } else if (!strcasecmp(v
->name
, "host")) {
4085 if (!strcasecmp(v
->value
, "dynamic")) {
4088 hp
= ast_gethostbyname(v
->value
, &he
);
4090 memcpy(&peer
->addr
.sin_addr
, hp
->h_addr
, sizeof(peer
->addr
.sin_addr
));
4093 ast_log(LOG_WARNING
, "Unable to find host '%s' at line %d\n", v
->value
, v
->lineno
);
4097 } else if (!strcasecmp(v
->name
, "ustothem")) {
4098 if (!dundi_str_to_eid(&testeid
, v
->value
))
4099 peer
->us_eid
= testeid
;
4101 ast_log(LOG_WARNING
, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v
->value
, v
->lineno
);
4102 } else if (!strcasecmp(v
->name
, "include")) {
4103 append_permission(&peer
->include
, v
->value
, 1);
4104 } else if (!strcasecmp(v
->name
, "permit")) {
4105 append_permission(&peer
->permit
, v
->value
, 1);
4106 } else if (!strcasecmp(v
->name
, "noinclude")) {
4107 append_permission(&peer
->include
, v
->value
, 0);
4108 } else if (!strcasecmp(v
->name
, "deny")) {
4109 append_permission(&peer
->permit
, v
->value
, 0);
4110 } else if (!strcasecmp(v
->name
, "register")) {
4111 needregister
= ast_true(v
->value
);
4112 } else if (!strcasecmp(v
->name
, "order")) {
4113 if (!strcasecmp(v
->value
, "primary"))
4115 else if (!strcasecmp(v
->value
, "secondary"))
4117 else if (!strcasecmp(v
->value
, "tertiary"))
4119 else if (!strcasecmp(v
->value
, "quartiary"))
4122 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
);
4124 } else if (!strcasecmp(v
->name
, "qualify")) {
4125 if (!strcasecmp(v
->value
, "no")) {
4127 } else if (!strcasecmp(v
->value
, "yes")) {
4128 peer
->maxms
= DEFAULT_MAXMS
;
4129 } else if (sscanf(v
->value
, "%d", &peer
->maxms
) != 1) {
4130 ast_log(LOG_WARNING
, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4131 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
), v
->lineno
);
4134 } else if (!strcasecmp(v
->name
, "model")) {
4135 if (!strcasecmp(v
->value
, "inbound"))
4136 peer
->model
= DUNDI_MODEL_INBOUND
;
4137 else if (!strcasecmp(v
->value
, "outbound"))
4138 peer
->model
= DUNDI_MODEL_OUTBOUND
;
4139 else if (!strcasecmp(v
->value
, "symmetric"))
4140 peer
->model
= DUNDI_MODEL_SYMMETRIC
;
4141 else if (!strcasecmp(v
->value
, "none"))
4144 ast_log(LOG_WARNING
, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4145 v
->value
, v
->lineno
);
4147 } else if (!strcasecmp(v
->name
, "precache")) {
4148 if (!strcasecmp(v
->value
, "inbound"))
4149 peer
->pcmodel
= DUNDI_MODEL_INBOUND
;
4150 else if (!strcasecmp(v
->value
, "outbound"))
4151 peer
->pcmodel
= DUNDI_MODEL_OUTBOUND
;
4152 else if (!strcasecmp(v
->value
, "symmetric"))
4153 peer
->pcmodel
= DUNDI_MODEL_SYMMETRIC
;
4154 else if (!strcasecmp(v
->value
, "none"))
4157 ast_log(LOG_WARNING
, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4158 v
->value
, v
->lineno
);
4162 (*globalpcmode
) |= peer
->pcmodel
;
4163 if (!peer
->model
&& !peer
->pcmodel
) {
4164 ast_log(LOG_WARNING
, "Peer '%s' lacks a model or pcmodel, discarding!\n",
4165 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4167 } else if ((peer
->model
& DUNDI_MODEL_INBOUND
) && (peer
->pcmodel
& DUNDI_MODEL_OUTBOUND
)) {
4168 ast_log(LOG_WARNING
, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4169 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4171 } else if ((peer
->model
& DUNDI_MODEL_OUTBOUND
) && (peer
->pcmodel
& DUNDI_MODEL_INBOUND
)) {
4172 ast_log(LOG_WARNING
, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4173 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4175 } else if (!AST_LIST_EMPTY(&peer
->include
) && !(peer
->model
& DUNDI_MODEL_OUTBOUND
) && !(peer
->pcmodel
& DUNDI_MODEL_INBOUND
)) {
4176 ast_log(LOG_WARNING
, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4177 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4178 } else if (!AST_LIST_EMPTY(&peer
->permit
) && !(peer
->model
& DUNDI_MODEL_INBOUND
) && !(peer
->pcmodel
& DUNDI_MODEL_OUTBOUND
)) {
4179 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",
4180 dundi_eid_to_str(eid_str
, sizeof(eid_str
), &peer
->eid
));
4183 peer
->registerid
= ast_sched_add(sched
, 2000, do_register
, peer
);
4185 qualify_peer(peer
, 1);
4187 AST_LIST_UNLOCK(&peers
);
4190 static int dundi_helper(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *data
, int flag
)
4192 struct dundi_result results
[MAX_RESULTS
];
4196 if (!strncasecmp(context
, "macro-", 6)) {
4198 ast_log(LOG_NOTICE
, "Can't use macro mode without a channel!\n");
4201 /* If done as a macro, use macro extension */
4202 if (!strcasecmp(exten
, "s")) {
4203 exten
= pbx_builtin_getvar_helper(chan
, "ARG1");
4204 if (ast_strlen_zero(exten
))
4205 exten
= chan
->macroexten
;
4206 if (ast_strlen_zero(exten
))
4207 exten
= chan
->exten
;
4208 if (ast_strlen_zero(exten
)) {
4209 ast_log(LOG_WARNING
, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4213 if (ast_strlen_zero(data
))
4216 if (ast_strlen_zero(data
))
4219 res
= dundi_lookup(results
, MAX_RESULTS
, chan
, data
, exten
, 0);
4220 for (x
=0;x
<res
;x
++) {
4221 if (ast_test_flag(results
+ x
, flag
))
4224 if (found
>= priority
)
4229 static int dundi_exists(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *callerid
, const char *data
)
4231 return dundi_helper(chan
, context
, exten
, priority
, data
, DUNDI_FLAG_EXISTS
);
4234 static int dundi_canmatch(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *callerid
, const char *data
)
4236 return dundi_helper(chan
, context
, exten
, priority
, data
, DUNDI_FLAG_CANMATCH
);
4239 static int dundi_exec(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *callerid
, const char *data
)
4241 struct dundi_result results
[MAX_RESULTS
];
4245 struct ast_app
*dial
;
4247 if (!strncasecmp(context
, "macro-", 6)) {
4249 ast_log(LOG_NOTICE
, "Can't use macro mode without a channel!\n");
4252 /* If done as a macro, use macro extension */
4253 if (!strcasecmp(exten
, "s")) {
4254 exten
= pbx_builtin_getvar_helper(chan
, "ARG1");
4255 if (ast_strlen_zero(exten
))
4256 exten
= chan
->macroexten
;
4257 if (ast_strlen_zero(exten
))
4258 exten
= chan
->exten
;
4259 if (ast_strlen_zero(exten
)) {
4260 ast_log(LOG_WARNING
, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4264 if (ast_strlen_zero(data
))
4267 if (ast_strlen_zero(data
))
4270 res
= dundi_lookup(results
, MAX_RESULTS
, chan
, data
, exten
, 0);
4272 sort_results(results
, res
);
4273 for (x
=0;x
<res
;x
++) {
4274 if (ast_test_flag(results
+ x
, DUNDI_FLAG_EXISTS
)) {
4282 snprintf(req
, sizeof(req
), "%s/%s", results
[x
].tech
, results
[x
].dest
);
4283 dial
= pbx_findapp("Dial");
4285 res
= pbx_exec(chan
, dial
, req
);
4291 static int dundi_matchmore(struct ast_channel
*chan
, const char *context
, const char *exten
, int priority
, const char *callerid
, const char *data
)
4293 return dundi_helper(chan
, context
, exten
, priority
, data
, DUNDI_FLAG_MATCHMORE
);
4296 static struct ast_switch dundi_switch
=
4299 description
: "DUNDi Discovered Dialplan Switch",
4300 exists
: dundi_exists
,
4301 canmatch
: dundi_canmatch
,
4303 matchmore
: dundi_matchmore
,
4306 static int set_config(char *config_file
, struct sockaddr_in
* sin
)
4308 struct ast_config
*cfg
;
4309 struct ast_variable
*v
;
4313 char hn
[MAXHOSTNAMELEN
] = "";
4314 struct ast_hostent he
;
4316 struct sockaddr_in sin2
;
4317 static int last_port
= 0;
4318 int globalpcmodel
= 0;
4321 dundi_ttl
= DUNDI_DEFAULT_TTL
;
4322 dundi_cache_time
= DUNDI_DEFAULT_CACHE_TIME
;
4323 cfg
= ast_config_load(config_file
);
4327 ast_log(LOG_ERROR
, "Unable to load config %s\n", config_file
);
4331 if (!gethostname(hn
, sizeof(hn
)-1)) {
4332 hp
= ast_gethostbyname(hn
, &he
);
4334 memcpy(&sin2
.sin_addr
, hp
->h_addr
, sizeof(sin2
.sin_addr
));
4335 ast_copy_string(ipaddr
, ast_inet_ntoa(sin2
.sin_addr
), sizeof(ipaddr
));
4337 ast_log(LOG_WARNING
, "Unable to look up host '%s'\n", hn
);
4339 ast_log(LOG_WARNING
, "Unable to get host name!\n");
4340 AST_LIST_LOCK(&peers
);
4342 global_storehistory
= 0;
4343 ast_copy_string(secretpath
, "dundi", sizeof(secretpath
));
4344 v
= ast_variable_browse(cfg
, "general");
4346 if (!strcasecmp(v
->name
, "port")){
4347 sin
->sin_port
= ntohs(atoi(v
->value
));
4349 last_port
=sin
->sin_port
;
4350 } else if(sin
->sin_port
!= last_port
)
4351 ast_log(LOG_WARNING
, "change to port ignored until next asterisk re-start\n");
4352 } else if (!strcasecmp(v
->name
, "bindaddr")) {
4354 struct ast_hostent he
;
4355 hp
= ast_gethostbyname(v
->value
, &he
);
4357 memcpy(&sin
->sin_addr
, hp
->h_addr
, sizeof(sin
->sin_addr
));
4359 ast_log(LOG_WARNING
, "Invalid host/IP '%s'\n", v
->value
);
4360 } else if (!strcasecmp(v
->name
, "authdebug")) {
4361 authdebug
= ast_true(v
->value
);
4362 } else if (!strcasecmp(v
->name
, "ttl")) {
4363 if ((sscanf(v
->value
, "%d", &x
) == 1) && (x
> 0) && (x
< DUNDI_DEFAULT_TTL
)) {
4366 ast_log(LOG_WARNING
, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4367 v
->value
, v
->lineno
, DUNDI_DEFAULT_TTL
);
4369 } else if (!strcasecmp(v
->name
, "autokill")) {
4370 if (sscanf(v
->value
, "%d", &x
) == 1) {
4372 global_autokilltimeout
= x
;
4374 ast_log(LOG_NOTICE
, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v
->lineno
);
4375 } else if (ast_true(v
->value
)) {
4376 global_autokilltimeout
= DEFAULT_MAXMS
;
4378 global_autokilltimeout
= 0;
4380 } else if (!strcasecmp(v
->name
, "entityid")) {
4381 if (!dundi_str_to_eid(&testeid
, v
->value
))
4382 global_eid
= testeid
;
4384 ast_log(LOG_WARNING
, "Invalid global endpoint identifier '%s' at line %d\n", v
->value
, v
->lineno
);
4385 } else if (!strcasecmp(v
->name
, "tos")) {
4386 if (sscanf(v
->value
, "%d", &format
) == 1)
4387 tos
= format
& 0xff;
4388 else if (!strcasecmp(v
->value
, "lowdelay"))
4389 tos
= IPTOS_LOWDELAY
;
4390 else if (!strcasecmp(v
->value
, "throughput"))
4391 tos
= IPTOS_THROUGHPUT
;
4392 else if (!strcasecmp(v
->value
, "reliability"))
4393 tos
= IPTOS_RELIABILITY
;
4394 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
4395 else if (!strcasecmp(v
->value
, "mincost"))
4396 tos
= IPTOS_MINCOST
;
4398 else if (!strcasecmp(v
->value
, "none"))
4401 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
4402 ast_log(LOG_WARNING
, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v
->lineno
);
4404 ast_log(LOG_WARNING
, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v
->lineno
);
4406 } else if (!strcasecmp(v
->name
, "department")) {
4407 ast_copy_string(dept
, v
->value
, sizeof(dept
));
4408 } else if (!strcasecmp(v
->name
, "organization")) {
4409 ast_copy_string(org
, v
->value
, sizeof(org
));
4410 } else if (!strcasecmp(v
->name
, "locality")) {
4411 ast_copy_string(locality
, v
->value
, sizeof(locality
));
4412 } else if (!strcasecmp(v
->name
, "stateprov")) {
4413 ast_copy_string(stateprov
, v
->value
, sizeof(stateprov
));
4414 } else if (!strcasecmp(v
->name
, "country")) {
4415 ast_copy_string(country
, v
->value
, sizeof(country
));
4416 } else if (!strcasecmp(v
->name
, "email")) {
4417 ast_copy_string(email
, v
->value
, sizeof(email
));
4418 } else if (!strcasecmp(v
->name
, "phone")) {
4419 ast_copy_string(phone
, v
->value
, sizeof(phone
));
4420 } else if (!strcasecmp(v
->name
, "storehistory")) {
4421 global_storehistory
= ast_true(v
->value
);
4422 } else if (!strcasecmp(v
->name
, "cachetime")) {
4423 if ((sscanf(v
->value
, "%d", &x
) == 1)) {
4424 dundi_cache_time
= x
;
4426 ast_log(LOG_WARNING
, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
4427 v
->value
, v
->lineno
, DUNDI_DEFAULT_CACHE_TIME
);
4432 AST_LIST_UNLOCK(&peers
);
4434 v
= ast_variable_browse(cfg
, "mappings");
4436 build_mapping(v
->name
, v
->value
);
4441 cat
= ast_category_browse(cfg
, NULL
);
4443 if (strcasecmp(cat
, "general") && strcasecmp(cat
, "mappings")) {
4445 if (!dundi_str_to_eid(&testeid
, cat
))
4446 build_peer(&testeid
, ast_variable_browse(cfg
, cat
), &globalpcmodel
);
4448 ast_log(LOG_NOTICE
, "Ignoring invalid EID entry '%s'\n", cat
);
4450 cat
= ast_category_browse(cfg
, cat
);
4453 ast_config_destroy(cfg
);
4455 if (globalpcmodel
& DUNDI_MODEL_OUTBOUND
)
4456 dundi_precache_full();
4460 static int unload_module(void)
4462 ast_module_user_hangup_all();
4464 ast_cli_unregister_multiple(cli_dundi
, sizeof(cli_dundi
) / sizeof(struct ast_cli_entry
));
4465 ast_unregister_switch(&dundi_switch
);
4466 ast_custom_function_unregister(&dundi_function
);
4467 sched_context_destroy(sched
);
4472 static int reload(void)
4474 struct sockaddr_in sin
;
4475 set_config("dundi.conf",&sin
);
4479 static int load_module(void)
4482 struct sockaddr_in sin
;
4484 dundi_set_output(dundi_debug_output
);
4485 dundi_set_error(dundi_error_output
);
4487 sin
.sin_family
= AF_INET
;
4488 sin
.sin_port
= ntohs(DUNDI_PORT
);
4489 sin
.sin_addr
.s_addr
= INADDR_ANY
;
4491 /* Make a UDP socket */
4492 io
= io_context_create();
4493 sched
= sched_context_create();
4495 if (!io
|| !sched
) {
4496 ast_log(LOG_ERROR
, "Out of memory\n");
4500 if(set_config("dundi.conf",&sin
))
4501 return AST_MODULE_LOAD_DECLINE
;
4503 netsocket
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_IP
);
4505 if (netsocket
< 0) {
4506 ast_log(LOG_ERROR
, "Unable to create network socket: %s\n", strerror(errno
));
4509 if (bind(netsocket
,(struct sockaddr
*)&sin
, sizeof(sin
))) {
4510 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
));
4514 if (option_verbose
> 1)
4515 ast_verbose(VERBOSE_PREFIX_2
"Using TOS bits %d\n", tos
);
4517 if (setsockopt(netsocket
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
)))
4518 ast_log(LOG_WARNING
, "Unable to set TOS to %d\n", tos
);
4520 res
= start_network_thread();
4522 ast_log(LOG_ERROR
, "Unable to start network thread\n");
4527 if (option_verbose
> 1)
4528 ast_verbose(VERBOSE_PREFIX_2
"DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin
.sin_addr
), ntohs(sin
.sin_port
));
4530 ast_cli_register_multiple(cli_dundi
, sizeof(cli_dundi
) / sizeof(struct ast_cli_entry
));
4531 if (ast_register_switch(&dundi_switch
))
4532 ast_log(LOG_ERROR
, "Unable to register DUNDi switch\n");
4533 ast_custom_function_register(&dundi_function
);
4538 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "Distributed Universal Number Discovery (DUNDi)",
4539 .load
= load_module
,
4540 .unload
= unload_module
,