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/netsock.h"
72 #include "asterisk/crypto.h"
73 #include "asterisk/astdb.h"
74 #include "asterisk/acl.h"
75 #include "asterisk/aes.h"
76 #include "asterisk/app.h"
78 #include "dundi-parser.h"
80 #define MAX_RESULTS 64
82 #define MAX_PACKET_SIZE 8192
84 #define MAX_WEIGHT 59999
86 #define DUNDI_MODEL_INBOUND (1 << 0)
87 #define DUNDI_MODEL_OUTBOUND (1 << 1)
88 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
90 /*! Keep times of last 10 lookups */
91 #define DUNDI_TIMING_HISTORY 10
94 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
95 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
96 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
97 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
98 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */
99 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
100 FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
103 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
106 #define DUNDI_SECRET_TIME 15 /* Testing only */
108 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
111 static struct io_context *io;
112 static struct sched_context *sched;
113 static int netsocket = -1;
114 static pthread_t netthreadid = AST_PTHREADT_NULL;
115 static pthread_t precachethreadid = AST_PTHREADT_NULL;
117 static int dundidebug = 0;
118 static int authdebug = 0;
119 static int dundi_ttl = DUNDI_DEFAULT_TTL;
120 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
121 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
122 static int global_autokilltimeout = 0;
123 static dundi_eid global_eid;
124 static int default_expiration = 60;
125 static int global_storehistory = 0;
126 static char dept[80];
128 static char locality[80];
129 static char stateprov[80];
130 static char country[80];
131 static char email[80];
132 static char phone[80];
133 static char secretpath[80];
134 static char cursecret[80];
135 static char ipaddr[80];
136 static time_t rotatetime;
137 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
140 AST_LIST_ENTRY(permission) list;
145 struct dundi_packet {
146 AST_LIST_ENTRY(dundi_packet) list;
149 struct dundi_transaction *parent;
152 unsigned char data[0];
155 struct dundi_hint_metadata {
156 unsigned short flags;
157 char exten[AST_MAX_EXTENSION];
160 struct dundi_precache_queue {
161 AST_LIST_ENTRY(dundi_precache_queue) list;
167 struct dundi_request;
169 struct dundi_transaction {
170 struct sockaddr_in addr; /*!< Other end of transaction */
171 struct timeval start; /*!< When this transaction was created */
172 dundi_eid eids[DUNDI_MAX_STACK + 1];
173 int eidcount; /*!< Number of eids in eids */
174 dundi_eid us_eid; /*!< Our EID, to them */
175 dundi_eid them_eid; /*!< Their EID, to us */
176 aes_encrypt_ctx ecx; /*!< AES 128 Encryption context */
177 aes_decrypt_ctx dcx; /*!< AES 128 Decryption context */
178 unsigned int flags; /*!< Has final packet been sent */
179 int ttl; /*!< Remaining TTL for queries on this one */
180 int thread; /*!< We have a calling thread */
181 int retranstimer; /*!< How long to wait before retransmissions */
182 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
183 int autokilltimeout; /*!< Recommended timeout for autokill */
184 unsigned short strans; /*!< Our transaction identifier */
185 unsigned short dtrans; /*!< Their transaction identifer */
186 unsigned char iseqno; /*!< Next expected received seqno */
187 unsigned char oiseqno; /*!< Last received incoming seqno */
188 unsigned char oseqno; /*!< Next transmitted seqno */
189 unsigned char aseqno; /*!< Last acknowledge seqno */
190 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
191 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
192 struct dundi_request *parent; /*!< Parent request (if there is one) */
193 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
194 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
197 struct dundi_request {
198 char dcontext[AST_MAX_EXTENSION];
199 char number[AST_MAX_EXTENSION];
202 struct dundi_result *dr;
203 struct dundi_entity_info *dei;
204 struct dundi_hint_metadata *hmd;
210 unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */
211 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
212 AST_LIST_ENTRY(dundi_request) list;
215 struct dundi_mapping {
216 char dcontext[AST_MAX_EXTENSION];
217 char lcontext[AST_MAX_EXTENSION];
223 char dest[AST_MAX_EXTENSION];
224 AST_LIST_ENTRY(dundi_mapping) list;
229 struct sockaddr_in addr; /*!< Address of DUNDi peer */
230 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
231 struct permissionlist include;
240 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
241 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
242 unsigned long us_keycrc32; /*!< CRC-32 of our key */
243 aes_encrypt_ctx us_ecx; /*!< Cached AES 128 Encryption context */
244 aes_decrypt_ctx us_dcx; /*!< Cached AES 128 Decryption context */
245 unsigned long them_keycrc32; /*!< CRC-32 of our key */
246 aes_encrypt_ctx them_ecx; /*!< Cached AES 128 Encryption context */
247 aes_decrypt_ctx them_dcx; /*!< Cached AES 128 Decryption context */
248 time_t keyexpire; /*!< When to expire/recreate key */
250 int lookuptimes[DUNDI_TIMING_HISTORY];
251 char *lookups[DUNDI_TIMING_HISTORY];
253 struct dundi_transaction *regtrans; /*!< Registration transaction */
254 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
255 int model; /*!< Pull model */
256 int pcmodel; /*!< Push/precache model */
257 int dynamic; /*!< Are we dynamic? */
258 int lastms; /*!< Last measured latency */
259 int maxms; /*!< Max permissible latency */
260 struct timeval qualtx; /*!< Time of transmit */
261 AST_LIST_ENTRY(dundi_peer) list;
264 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
265 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
266 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
267 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
268 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
270 static int dundi_xmit(struct dundi_packet *pack);
272 static void dundi_debug_output(const char *data)
275 ast_verbose("%s", data);
278 static void dundi_error_output(const char *data)
280 ast_log(LOG_WARNING, "%s", data);
283 static int has_permission(struct permissionlist *permlist, char *cont)
285 struct permission *perm;
288 AST_LIST_TRAVERSE(permlist, perm, list) {
289 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
296 static char *tech2str(int tech)
299 case DUNDI_PROTO_NONE:
301 case DUNDI_PROTO_IAX:
303 case DUNDI_PROTO_SIP:
305 case DUNDI_PROTO_H323:
312 static int str2tech(char *str)
314 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
315 return DUNDI_PROTO_IAX;
316 else if (!strcasecmp(str, "SIP"))
317 return DUNDI_PROTO_SIP;
318 else if (!strcasecmp(str, "H323"))
319 return DUNDI_PROTO_H323;
324 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[]);
325 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
326 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
327 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
329 struct dundi_transaction *trans;
331 /* Look for an exact match first */
332 AST_LIST_TRAVERSE(&alltrans, trans, all) {
333 if (!inaddrcmp(&trans->addr, sin) &&
334 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
335 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
337 trans->dtrans = ntohs(hdr->strans) & 32767;
342 switch(hdr->cmdresp & 0x7f) {
343 case DUNDI_COMMAND_DPDISCOVER:
344 case DUNDI_COMMAND_EIDQUERY:
345 case DUNDI_COMMAND_PRECACHERQ:
346 case DUNDI_COMMAND_REGREQ:
347 case DUNDI_COMMAND_NULL:
348 case DUNDI_COMMAND_ENCRYPT:
351 /* Create new transaction */
352 if (!(trans = create_transaction(NULL)))
354 memcpy(&trans->addr, sin, sizeof(trans->addr));
355 trans->dtrans = ntohs(hdr->strans) & 32767;
363 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
365 static int dundi_ack(struct dundi_transaction *trans, int final)
367 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
369 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
372 struct dundi_packet pack;
373 struct dundi_hdr hdr;
375 struct dundi_transaction trans;
376 /* Never respond to an INVALID with another INVALID */
377 if (h->cmdresp == DUNDI_COMMAND_INVALID)
379 memset(&tmp, 0, sizeof(tmp));
380 memset(&trans, 0, sizeof(trans));
381 memcpy(&trans.addr, sin, sizeof(trans.addr));
382 tmp.hdr.strans = h->dtrans;
383 tmp.hdr.dtrans = h->strans;
384 tmp.hdr.iseqno = h->oseqno;
385 tmp.hdr.oseqno = h->iseqno;
386 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
387 tmp.hdr.cmdflags = 0;
388 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
389 tmp.pack.datalen = sizeof(struct dundi_hdr);
390 tmp.pack.parent = &trans;
391 dundi_xmit(&tmp.pack);
394 static void reset_global_eid(void)
396 #if defined(SIOCGIFHWADDR)
401 s = socket(AF_INET, SOCK_STREAM, 0);
404 for (x = 0; x < 10; x++) {
405 memset(&ifr, 0, sizeof(ifr));
406 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
407 if (ioctl(s, SIOCGIFHWADDR, &ifr))
409 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
411 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n",
412 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
418 #if defined(ifa_broadaddr) && !defined(SOLARIS)
420 struct ifaddrs *ifap;
422 if (getifaddrs(&ifap) == 0) {
424 for (p = ifap; p; p = p->ifa_next) {
425 if (p->ifa_addr->sa_family == AF_LINK) {
426 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
429 sdp->sdl_data + sdp->sdl_nlen, 6);
431 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);
440 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
443 static int get_trans_id(void)
445 struct dundi_transaction *t;
446 int stid = (ast_random() % 32766) + 1;
450 AST_LIST_TRAVERSE(&alltrans, t, all) {
451 if (t->strans == tid)
456 tid = (tid % 32766) + 1;
457 } while (tid != stid);
462 static int reset_transaction(struct dundi_transaction *trans)
465 tid = get_trans_id();
474 ast_clear_flag(trans, FLAG_FINAL);
478 static struct dundi_peer *find_peer(dundi_eid *eid)
480 struct dundi_peer *cur = NULL;
485 AST_LIST_TRAVERSE(&peers, cur, list) {
486 if (!dundi_eid_cmp(&cur->eid,eid))
493 static void build_iv(unsigned char *iv)
495 /* XXX Would be nice to be more random XXX */
496 unsigned int *fluffy;
498 fluffy = (unsigned int *)(iv);
500 fluffy[x] = ast_random();
503 struct dundi_query_state {
504 dundi_eid *eids[DUNDI_MAX_STACK + 1];
505 int directs[DUNDI_MAX_STACK + 1];
507 char called_context[AST_MAX_EXTENSION];
508 char called_number[AST_MAX_EXTENSION];
509 struct dundi_mapping *maps;
512 struct dundi_transaction *trans;
519 static int get_mapping_weight(struct dundi_mapping *map)
523 if (map->weightstr) {
524 pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
525 if (sscanf(buf, "%d", &map->_weight) != 1)
526 map->_weight = MAX_WEIGHT;
532 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)
534 struct ast_flags flags = {0};
536 if (!ast_strlen_zero(map->lcontext)) {
537 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
538 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
539 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
540 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
541 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
542 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
543 if (ast_ignore_pattern(map->lcontext, called_number))
544 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
546 /* Clearly we can't say 'don't ask' anymore if we found anything... */
547 if (ast_test_flag(&flags, AST_FLAGS_ALL))
548 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
550 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
551 /* Skip partial answers */
552 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
554 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
555 struct varshead headp;
556 struct ast_var_t *newvariable;
557 ast_set_flag(&flags, map->options & 0xffff);
558 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
559 dr[anscnt].techint = map->tech;
560 dr[anscnt].weight = get_mapping_weight(map);
561 dr[anscnt].expiration = dundi_cache_time;
562 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
563 dr[anscnt].eid = *us_eid;
564 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
565 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
566 AST_LIST_HEAD_INIT_NOLOCK(&headp);
567 newvariable = ast_var_assign("NUMBER", called_number);
568 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
569 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
570 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
571 newvariable = ast_var_assign("SECRET", cursecret);
572 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
573 newvariable = ast_var_assign("IPADDR", ipaddr);
574 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
575 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
576 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
577 ast_var_delete(newvariable);
579 dr[anscnt].dest[0] = '\0';
582 /* No answers... Find the fewest number of digits from the
583 number for which we have no answer. */
584 char tmp[AST_MAX_EXTENSION];
585 for (x=0;x<AST_MAX_EXTENSION;x++) {
586 tmp[x] = called_number[x];
589 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
590 /* Oops found something we can't match. If this is longer
591 than the running hint, we have to consider it */
592 if (strlen(tmp) > strlen(hmd->exten)) {
593 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
603 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
605 static void *dundi_lookup_thread(void *data)
607 struct dundi_query_state *st = data;
608 struct dundi_result dr[MAX_RESULTS];
609 struct dundi_ie_data ied;
610 struct dundi_hint_metadata hmd;
615 int expiration = dundi_cache_time;
618 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
619 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
620 memset(&ied, 0, sizeof(ied));
621 memset(&dr, 0, sizeof(dr));
622 memset(&hmd, 0, sizeof(hmd));
623 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
624 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
625 for (x=0;x<st->nummaps;x++)
626 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
629 for (x=0;x<ouranswers;x++) {
630 if (dr[x].weight < max)
635 /* If we do not have a canonical result, keep looking */
636 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);
638 /* Append answer in result */
641 if ((res < -1) && (!ouranswers))
642 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
645 AST_LIST_LOCK(&peers);
646 /* Truncate if "don't ask" isn't present */
647 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
649 if (ast_test_flag(st->trans, FLAG_DEAD)) {
651 ast_log(LOG_DEBUG, "Our transaction went away!\n");
652 st->trans->thread = 0;
653 destroy_trans(st->trans, 0);
655 for (x=0;x<ouranswers;x++) {
657 if (dr[x].expiration && (expiration > dr[x].expiration))
658 expiration = dr[x].expiration;
659 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
661 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
662 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
663 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
664 st->trans->thread = 0;
666 AST_LIST_UNLOCK(&peers);
671 static void *dundi_precache_thread(void *data)
673 struct dundi_query_state *st = data;
674 struct dundi_ie_data ied;
675 struct dundi_hint_metadata hmd;
679 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
680 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
681 memset(&ied, 0, sizeof(ied));
683 /* Now produce precache */
684 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
686 AST_LIST_LOCK(&peers);
687 /* Truncate if "don't ask" isn't present */
688 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
690 if (ast_test_flag(st->trans, FLAG_DEAD)) {
692 ast_log(LOG_DEBUG, "Our transaction went away!\n");
693 st->trans->thread = 0;
694 destroy_trans(st->trans, 0);
696 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
697 st->trans->thread = 0;
699 AST_LIST_UNLOCK(&peers);
704 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[]);
706 static void *dundi_query_thread(void *data)
708 struct dundi_query_state *st = data;
709 struct dundi_entity_info dei;
710 struct dundi_ie_data ied;
711 struct dundi_hint_metadata hmd;
716 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
717 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
718 memset(&ied, 0, sizeof(ied));
719 memset(&dei, 0, sizeof(dei));
720 memset(&hmd, 0, sizeof(hmd));
721 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
724 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
725 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
726 ast_copy_string(dei.org, org, sizeof(dei.org));
727 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
728 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
729 ast_copy_string(dei.country, country, sizeof(dei.country));
730 ast_copy_string(dei.email, email, sizeof(dei.email));
731 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
734 /* If we do not have a canonical result, keep looking */
735 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
737 AST_LIST_LOCK(&peers);
738 if (ast_test_flag(st->trans, FLAG_DEAD)) {
740 ast_log(LOG_DEBUG, "Our transaction went away!\n");
741 st->trans->thread = 0;
742 destroy_trans(st->trans, 0);
745 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
746 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
747 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
748 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
749 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
750 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
751 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
752 if (!ast_strlen_zero(dei.ipaddr))
753 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
755 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
756 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
757 st->trans->thread = 0;
759 AST_LIST_UNLOCK(&peers);
764 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
766 struct dundi_query_state *st;
770 struct dundi_ie_data ied;
773 pthread_t lookupthread;
775 if (ies->eidcount > 1) {
776 /* Since it is a requirement that the first EID is the authenticating host
777 and the last EID is the root, it is permissible that the first and last EID
778 could be the same. In that case, we should go ahead copy only the "root" section
779 since we will not need it for authentication. */
780 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
783 totallen = sizeof(struct dundi_query_state);
784 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
785 st = ast_calloc(1, totallen);
787 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
788 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
790 st->ttl = ies->ttl - 1;
794 for (x=skipfirst;ies->eids[x];x++) {
795 st->eids[x-skipfirst] = (dundi_eid *)s;
796 *st->eids[x-skipfirst] = *ies->eids[x];
797 s += sizeof(dundi_eid);
800 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);
801 pthread_attr_init(&attr);
802 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
804 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
806 ast_log(LOG_WARNING, "Unable to create thread!\n");
808 memset(&ied, 0, sizeof(ied));
809 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
810 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
811 pthread_attr_destroy(&attr);
814 pthread_attr_destroy(&attr);
816 ast_log(LOG_WARNING, "Out of memory!\n");
817 memset(&ied, 0, sizeof(ied));
818 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
819 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
825 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
830 char eidpeer_str[20];
831 char eidroot_str[20];
836 expiration = dundi_cache_time;
838 /* Only cache hint if "don't ask" is there... */
839 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
842 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
844 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
845 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
846 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
847 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
850 timeout += expiration;
851 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
853 ast_db_put("dundi/cache", key1, data);
855 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
856 ast_db_put("dundi/cache", key2, data);
858 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
862 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
868 char eidpeer_str[20];
869 char eidroot_str[20];
873 expiration = dundi_cache_time;
875 /* Keep pushes a little longer, cut pulls a little short */
882 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
883 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
884 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
885 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
886 /* Build request string */
888 timeout += expiration;
889 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
890 for (x=start;x<req->respcount;x++) {
891 /* Skip anything with an illegal pipe in it */
892 if (strchr(req->dr[x].dest, '|'))
894 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
895 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
896 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
898 ast_db_put("dundi/cache", key1, data);
899 ast_db_put("dundi/cache", key2, data);
903 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
905 struct dundi_query_state *st;
908 struct dundi_ie_data ied;
910 struct dundi_result dr2[MAX_RESULTS];
911 struct dundi_request dr;
912 struct dundi_hint_metadata hmd;
914 struct dundi_mapping *cur;
918 pthread_t lookupthread;
921 memset(&dr2, 0, sizeof(dr2));
922 memset(&dr, 0, sizeof(dr));
923 memset(&hmd, 0, sizeof(hmd));
925 /* Forge request structure to hold answers for cache */
926 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
928 dr.maxcount = MAX_RESULTS;
929 dr.expiration = dundi_cache_time;
931 dr.pfds[0] = dr.pfds[1] = -1;
933 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
934 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
936 for (x=0;x<ies->anscount;x++) {
937 if (trans->parent->respcount < trans->parent->maxcount) {
938 /* Make sure it's not already there */
939 for (z=0;z<trans->parent->respcount;z++) {
940 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
941 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
944 if (z == trans->parent->respcount) {
945 /* Copy into parent responses */
946 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
947 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
948 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
949 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
950 if (ies->expiration > 0)
951 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
953 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
954 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
955 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
956 &ies->answers[x]->eid);
957 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
958 sizeof(trans->parent->dr[trans->parent->respcount].dest));
959 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
960 sizeof(trans->parent->dr[trans->parent->respcount].tech));
961 trans->parent->respcount++;
962 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
963 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
964 /* Update weight if appropriate */
965 trans->parent->dr[z].weight = ies->answers[x]->weight;
968 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
969 trans->parent->number, trans->parent->dcontext);
972 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
973 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
975 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
977 totallen = sizeof(struct dundi_query_state);
978 /* Count matching map entries */
980 AST_LIST_TRAVERSE(&mappings, cur, list) {
981 if (!strcasecmp(cur->dcontext, ccontext))
985 /* If no maps, return -1 immediately */
989 if (ies->eidcount > 1) {
990 /* Since it is a requirement that the first EID is the authenticating host
991 and the last EID is the root, it is permissible that the first and last EID
992 could be the same. In that case, we should go ahead copy only the "root" section
993 since we will not need it for authentication. */
994 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
998 /* Prepare to run a query and then propagate that as necessary */
999 totallen += mapcount * sizeof(struct dundi_mapping);
1000 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1001 st = ast_calloc(1, totallen);
1003 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1004 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1006 st->ttl = ies->ttl - 1;
1007 st->nocache = ies->cbypass;
1011 for (x=skipfirst;ies->eids[x];x++) {
1012 st->eids[x-skipfirst] = (dundi_eid *)s;
1013 *st->eids[x-skipfirst] = *ies->eids[x];
1014 st->directs[x-skipfirst] = ies->eid_direct[x];
1015 s += sizeof(dundi_eid);
1017 /* Append mappings */
1019 st->maps = (struct dundi_mapping *)s;
1020 AST_LIST_TRAVERSE(&mappings, cur, list) {
1021 if (!strcasecmp(cur->dcontext, ccontext)) {
1024 st->maps[x].list.next = NULL;
1029 st->nummaps = mapcount;
1031 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1032 pthread_attr_init(&attr);
1033 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1035 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1037 ast_log(LOG_WARNING, "Unable to create thread!\n");
1039 memset(&ied, 0, sizeof(ied));
1040 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1041 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1042 pthread_attr_destroy(&attr);
1045 pthread_attr_destroy(&attr);
1047 ast_log(LOG_WARNING, "Out of memory!\n");
1048 memset(&ied, 0, sizeof(ied));
1049 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1050 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1056 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1058 struct dundi_query_state *st;
1061 struct dundi_ie_data ied;
1063 struct dundi_mapping *cur;
1067 pthread_t lookupthread;
1068 pthread_attr_t attr;
1069 totallen = sizeof(struct dundi_query_state);
1070 /* Count matching map entries */
1071 AST_LIST_TRAVERSE(&mappings, cur, list) {
1072 if (!strcasecmp(cur->dcontext, ccontext))
1075 /* If no maps, return -1 immediately */
1079 if (ies->eidcount > 1) {
1080 /* Since it is a requirement that the first EID is the authenticating host
1081 and the last EID is the root, it is permissible that the first and last EID
1082 could be the same. In that case, we should go ahead copy only the "root" section
1083 since we will not need it for authentication. */
1084 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1088 totallen += mapcount * sizeof(struct dundi_mapping);
1089 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1090 st = ast_calloc(1, totallen);
1092 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1093 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1095 st->ttl = ies->ttl - 1;
1096 st->nocache = ies->cbypass;
1100 for (x=skipfirst;ies->eids[x];x++) {
1101 st->eids[x-skipfirst] = (dundi_eid *)s;
1102 *st->eids[x-skipfirst] = *ies->eids[x];
1103 st->directs[x-skipfirst] = ies->eid_direct[x];
1104 s += sizeof(dundi_eid);
1106 /* Append mappings */
1108 st->maps = (struct dundi_mapping *)s;
1109 AST_LIST_TRAVERSE(&mappings, cur, list) {
1110 if (!strcasecmp(cur->dcontext, ccontext)) {
1113 st->maps[x].list.next = NULL;
1118 st->nummaps = mapcount;
1120 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1121 pthread_attr_init(&attr);
1122 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1124 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1126 ast_log(LOG_WARNING, "Unable to create thread!\n");
1128 memset(&ied, 0, sizeof(ied));
1129 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1130 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1131 pthread_attr_destroy(&attr);
1134 pthread_attr_destroy(&attr);
1136 ast_log(LOG_WARNING, "Out of memory!\n");
1137 memset(&ied, 0, sizeof(ied));
1138 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1139 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1145 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1148 char *ptr, *term, *src;
1150 struct ast_flags flags;
1156 /* Build request string */
1157 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1160 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1161 int expiration = timeout - now;
1162 if (expiration > 0) {
1164 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1166 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1168 term = strchr(ptr, '|');
1171 src = strrchr(ptr, '/');
1178 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1179 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1180 /* Make sure it's not already there */
1181 for (z=0;z<req->respcount;z++) {
1182 if ((req->dr[z].techint == tech) &&
1183 !strcmp(req->dr[z].dest, ptr))
1186 if (z == req->respcount) {
1187 /* Copy into parent responses */
1188 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1189 req->dr[req->respcount].weight = weight;
1190 req->dr[req->respcount].techint = tech;
1191 req->dr[req->respcount].expiration = expiration;
1192 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1193 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1194 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1195 ast_copy_string(req->dr[req->respcount].dest, ptr,
1196 sizeof(req->dr[req->respcount].dest));
1197 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1198 sizeof(req->dr[req->respcount].tech));
1200 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1201 } else if (req->dr[z].weight > weight)
1202 req->dr[z].weight = weight;
1206 /* We found *something* cached */
1207 if (expiration < *lowexpiration)
1208 *lowexpiration = expiration;
1211 ast_db_del("dundi/cache", key);
1213 ast_db_del("dundi/cache", key);
1219 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1223 char eidroot_str[20];
1227 char eid_str_full[20];
1232 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1233 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1234 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1235 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1236 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1237 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1238 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1239 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1240 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1242 if (!req->respcount) {
1244 /* Look and see if we have a hint that would preclude us from looking at this
1245 peer for this number. */
1246 if (!(tmp[x] = req->number[x]))
1249 /* Check for hints */
1250 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1251 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1252 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1253 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1254 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1255 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1257 if (strlen(tmp) > strlen(req->hmd->exten)) {
1258 /* Update meta data if appropriate */
1259 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1269 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1271 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1273 if (!trans->addr.sin_addr.s_addr)
1274 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1275 trans->us_eid = p->us_eid;
1276 trans->them_eid = p->eid;
1277 /* Enable encryption if appropriate */
1278 if (!ast_strlen_zero(p->inkey))
1279 ast_set_flag(trans, FLAG_ENCRYPT);
1281 trans->autokilltimeout = p->maxms;
1282 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1283 if (p->lastms > 1) {
1284 trans->retranstimer = p->lastms * 2;
1285 /* Keep it from being silly */
1286 if (trans->retranstimer < 150)
1287 trans->retranstimer = 150;
1289 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1290 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1292 trans->autokilltimeout = global_autokilltimeout;
1295 /*! \note Called with the peers list already locked */
1296 static int do_register_expire(void *data)
1298 struct dundi_peer *peer = data;
1301 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1302 peer->registerexpire = -1;
1304 memset(&peer->addr, 0, sizeof(peer->addr));
1308 static int update_key(struct dundi_peer *peer)
1310 unsigned char key[16];
1311 struct ast_key *ekey, *skey;
1314 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1316 aes_encrypt_key128(key, &peer->us_ecx);
1317 aes_decrypt_key128(key, &peer->us_dcx);
1318 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1320 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1321 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1324 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1326 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1327 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1330 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1331 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1334 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1335 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1338 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1339 peer->sentfullkey = 0;
1341 time(&peer->keyexpire);
1342 peer->keyexpire += dundi_key_ttl;
1347 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1349 unsigned char curblock[16];
1351 memcpy(curblock, iv, sizeof(curblock));
1354 curblock[x] ^= src[x];
1355 aes_encrypt(curblock, dst, ecx);
1356 memcpy(curblock, dst, sizeof(curblock));
1363 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1365 unsigned char lastblock[16];
1367 memcpy(lastblock, iv, sizeof(lastblock));
1369 aes_decrypt(src, dst, dcx);
1371 dst[x] ^= lastblock[x];
1372 memcpy(lastblock, src, sizeof(lastblock));
1380 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)
1382 int space = *dstlen;
1383 unsigned long bytes;
1384 struct dundi_hdr *h;
1385 unsigned char *decrypt_space;
1386 decrypt_space = alloca(srclen);
1389 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1391 h = (struct dundi_hdr *)dst;
1394 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1396 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1400 *dstlen = bytes + 6;
1401 /* Return new header */
1405 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1407 unsigned char *compress_space;
1410 unsigned long bytes;
1411 struct dundi_ie_data ied;
1412 struct dundi_peer *peer;
1413 unsigned char iv[16];
1414 len = pack->datalen + pack->datalen / 100 + 42;
1415 compress_space = alloca(len);
1416 if (compress_space) {
1417 memset(compress_space, 0, len);
1418 /* We care about everthing save the first 6 bytes of header */
1420 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1423 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1426 memset(&ied, 0, sizeof(ied));
1427 /* Say who we are */
1428 if (!pack->h->iseqno && !pack->h->oseqno) {
1429 /* Need the key in the first copy */
1430 if (!(peer = find_peer(&trans->them_eid)))
1432 if (update_key(peer))
1434 if (!peer->sentfullkey)
1435 ast_set_flag(trans, FLAG_SENDFULLKEY);
1436 /* Append key data */
1437 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1438 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1439 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1440 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1442 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1444 /* Setup contexts */
1445 trans->ecx = peer->us_ecx;
1446 trans->dcx = peer->us_dcx;
1448 /* We've sent the full key */
1449 peer->sentfullkey = 1;
1451 /* Build initialization vector */
1453 /* Add the field, rounded up to 16 bytes */
1454 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1456 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1457 ast_log(LOG_NOTICE, "Final packet too large!\n");
1460 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1461 ied.pos += ((bytes + 15) / 16) * 16;
1462 /* Reconstruct header */
1463 pack->datalen = sizeof(struct dundi_hdr);
1464 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1465 pack->h->cmdflags = 0;
1466 memcpy(pack->h->ies, ied.buf, ied.pos);
1467 pack->datalen += ied.pos;
1473 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1475 unsigned char dst[128];
1477 struct ast_key *key, *skey;
1480 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1481 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1484 } else if (!newkey || !newsig)
1486 if (!memcmp(peer->rxenckey, newkey, 128) &&
1487 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1488 /* By definition, a match */
1492 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1494 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1495 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1499 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1501 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1502 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1506 /* First check signature */
1507 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1511 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1514 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1517 /* Decrypted, passes signature */
1519 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1520 memcpy(peer->rxenckey, newkey, 128);
1521 memcpy(peer->rxenckey + 128, newsig, 128);
1522 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1523 aes_decrypt_key128(dst, &peer->them_dcx);
1524 aes_encrypt_key128(dst, &peer->them_ecx);
1528 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1530 /* Handle canonical command / response */
1531 int final = hdr->cmdresp & 0x80;
1532 int cmd = hdr->cmdresp & 0x7f;
1537 unsigned char *bufcpy;
1538 struct dundi_ie_data ied;
1539 struct dundi_ies ies;
1540 struct dundi_peer *peer;
1543 memset(&ied, 0, sizeof(ied));
1544 memset(&ies, 0, sizeof(ies));
1546 bufcpy = alloca(datalen);
1549 /* Make a copy for parsing */
1550 memcpy(bufcpy, hdr->ies, datalen);
1552 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1553 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1554 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1559 case DUNDI_COMMAND_DPDISCOVER:
1560 case DUNDI_COMMAND_EIDQUERY:
1561 case DUNDI_COMMAND_PRECACHERQ:
1562 if (cmd == DUNDI_COMMAND_EIDQUERY)
1563 resp = DUNDI_COMMAND_EIDRESPONSE;
1564 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1565 resp = DUNDI_COMMAND_PRECACHERP;
1567 resp = DUNDI_COMMAND_DPRESPONSE;
1568 /* A dialplan or entity discover -- qualify by highest level entity */
1569 peer = find_peer(ies.eids[0]);
1571 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1572 dundi_send(trans, resp, 0, 1, &ied);
1575 trans->us_eid = peer->us_eid;
1576 if (strlen(peer->inkey)) {
1577 hasauth = encrypted;
1581 /* Okay we're authentiated and all, now we check if they're authorized */
1582 if (!ies.called_context)
1583 ies.called_context = "e164";
1584 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1585 res = dundi_answer_entity(trans, &ies, ies.called_context);
1587 if (ast_strlen_zero(ies.called_number)) {
1588 /* They're not permitted to access that context */
1589 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1590 dundi_send(trans, resp, 0, 1, &ied);
1591 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1592 (peer->model & DUNDI_MODEL_INBOUND) &&
1593 has_permission(&peer->permit, ies.called_context)) {
1594 res = dundi_answer_query(trans, &ies, ies.called_context);
1596 /* There is no such dundi context */
1597 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1598 dundi_send(trans, resp, 0, 1, &ied);
1600 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1601 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1602 has_permission(&peer->include, ies.called_context)) {
1603 res = dundi_prop_precache(trans, &ies, ies.called_context);
1605 /* There is no such dundi context */
1606 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1607 dundi_send(trans, resp, 0, 1, &ied);
1610 /* They're not permitted to access that context */
1611 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1612 dundi_send(trans, resp, 0, 1, &ied);
1616 /* They're not permitted to access that context */
1617 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1618 dundi_send(trans, resp, 0, 1, &ied);
1622 case DUNDI_COMMAND_REGREQ:
1623 /* A register request -- should only have one entity */
1624 peer = find_peer(ies.eids[0]);
1625 if (!peer || !peer->dynamic) {
1626 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1627 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1630 trans->us_eid = peer->us_eid;
1631 if (!ast_strlen_zero(peer->inkey)) {
1632 hasauth = encrypted;
1636 int expire = default_expiration;
1639 if (peer->registerexpire > -1)
1640 ast_sched_del(sched, peer->registerexpire);
1641 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1642 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1643 ntohs(trans->addr.sin_port), expire);
1644 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1645 if (inaddrcmp(&peer->addr, &trans->addr)) {
1646 if (option_verbose > 2) {
1647 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n",
1648 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1649 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1654 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1655 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1656 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1658 qualify_peer(peer, 1);
1662 case DUNDI_COMMAND_DPRESPONSE:
1663 /* A dialplan response, lets see what we got... */
1664 if (ies.cause < 1) {
1665 /* Success of some sort */
1667 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1668 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1669 authpass = encrypted;
1673 /* Pass back up answers */
1674 if (trans->parent && trans->parent->dr) {
1675 y = trans->parent->respcount;
1676 for (x=0;x<ies.anscount;x++) {
1677 if (trans->parent->respcount < trans->parent->maxcount) {
1678 /* Make sure it's not already there */
1679 for (z=0;z<trans->parent->respcount;z++) {
1680 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1681 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1684 if (z == trans->parent->respcount) {
1685 /* Copy into parent responses */
1686 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1687 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1688 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1689 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1690 if (ies.expiration > 0)
1691 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1693 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1694 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1695 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1696 &ies.answers[x]->eid);
1697 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1698 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1699 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1700 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1701 trans->parent->respcount++;
1702 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1703 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1704 /* Update weight if appropriate */
1705 trans->parent->dr[z].weight = ies.answers[x]->weight;
1708 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1709 trans->parent->number, trans->parent->dcontext);
1711 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1712 the cache know if this request was unaffected by our entity list. */
1713 cache_save(&trans->them_eid, trans->parent, y,
1714 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1716 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1717 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1718 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1719 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1720 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1721 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1722 sizeof(trans->parent->hmd->exten));
1725 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1728 if (ies.expiration > 0) {
1729 if (trans->parent->expiration > ies.expiration) {
1730 trans->parent->expiration = ies.expiration;
1734 /* Close connection if not final */
1736 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1740 /* Auth failure, check for data */
1742 /* Cancel if they didn't already */
1743 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1747 case DUNDI_COMMAND_EIDRESPONSE:
1748 /* A dialplan response, lets see what we got... */
1749 if (ies.cause < 1) {
1750 /* Success of some sort */
1752 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1753 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1754 authpass = encrypted;
1758 /* Pass back up answers */
1759 if (trans->parent && trans->parent->dei && ies.q_org) {
1760 if (!trans->parent->respcount) {
1761 trans->parent->respcount++;
1763 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1765 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1767 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1768 if (ies.q_stateprov)
1769 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1771 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1773 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1775 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1777 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1778 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1779 /* If it's them, update our address */
1780 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1784 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1785 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1788 /* Close connection if not final */
1790 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1794 /* Auth failure, check for data */
1796 /* Cancel if they didn't already */
1797 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1801 case DUNDI_COMMAND_REGRESPONSE:
1802 /* A dialplan response, lets see what we got... */
1803 if (ies.cause < 1) {
1805 /* Success of some sort */
1806 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1807 hasauth = encrypted;
1812 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1814 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1815 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1819 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),
1820 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1821 /* Close connection if not final */
1823 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1826 /* Auth failure, cancel if they didn't for some reason */
1828 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1832 case DUNDI_COMMAND_INVALID:
1833 case DUNDI_COMMAND_NULL:
1834 case DUNDI_COMMAND_PRECACHERP:
1835 /* Do nothing special */
1837 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1839 case DUNDI_COMMAND_ENCREJ:
1840 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1841 /* No really, it's over at this point */
1843 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1845 /* Send with full key */
1846 ast_set_flag(trans, FLAG_SENDFULLKEY);
1848 /* Ooops, we got a final message, start by sending ACK... */
1849 dundi_ack(trans, hdr->cmdresp & 0x80);
1850 trans->aseqno = trans->iseqno;
1851 /* Now, we gotta create a new transaction */
1852 if (!reset_transaction(trans)) {
1853 /* Make sure handle_frame doesn't destroy us */
1854 hdr->cmdresp &= 0x7f;
1855 /* Parse the message we transmitted */
1856 memset(&ies, 0, sizeof(ies));
1857 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1858 /* Reconstruct outgoing encrypted packet */
1859 memset(&ied, 0, sizeof(ied));
1860 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1861 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1862 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1864 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1865 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
1866 peer->sentfullkey = 1;
1871 case DUNDI_COMMAND_ENCRYPT:
1873 /* No nested encryption! */
1874 if ((trans->iseqno == 1) && !trans->oseqno) {
1875 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1876 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1877 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1879 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1883 apply_peer(trans, peer);
1884 /* Key passed, use new contexts for this session */
1885 trans->ecx = peer->them_ecx;
1886 trans->dcx = peer->them_dcx;
1888 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1889 struct dundi_hdr *dhdr;
1890 unsigned char decoded[MAX_PACKET_SIZE];
1892 ddatalen = sizeof(decoded);
1893 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1895 /* Handle decrypted response */
1897 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1898 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1899 /* Carry back final flag */
1900 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1904 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1909 /* Turn off encryption */
1910 ast_clear_flag(trans, FLAG_ENCRYPT);
1911 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1915 /* Send unknown command if we don't know it, with final flag IFF it's the
1916 first command in the dialog and only if we haven't recieved final notification */
1918 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1919 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1925 static void destroy_packet(struct dundi_packet *pack, int needfree);
1926 static void destroy_packets(struct packetlist *p)
1928 struct dundi_packet *pack;
1930 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1931 if (pack->retransid > -1)
1932 ast_sched_del(sched, pack->retransid);
1938 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1940 struct dundi_packet *pack;
1942 /* Ack transmitted packet corresponding to iseqno */
1943 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1944 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1945 destroy_packet(pack, 0);
1946 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1947 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1948 destroy_packets(&trans->lasttrans);
1950 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1951 if (trans->autokillid > -1)
1952 ast_sched_del(sched, trans->autokillid);
1953 trans->autokillid = -1;
1961 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1963 struct dundi_transaction *trans;
1964 trans = find_transaction(h, sin);
1966 dundi_reject(h, sin);
1969 /* Got a transaction, see where this header fits in */
1970 if (h->oseqno == trans->iseqno) {
1971 /* Just what we were looking for... Anything but ack increments iseqno */
1972 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1973 /* If final, we're done */
1974 destroy_trans(trans, 0);
1977 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1978 trans->oiseqno = trans->iseqno;
1980 handle_command_response(trans, h, datalen, 0);
1982 if (trans->aseqno != trans->iseqno) {
1983 dundi_ack(trans, h->cmdresp & 0x80);
1984 trans->aseqno = trans->iseqno;
1986 /* Delete any saved last transmissions */
1987 destroy_packets(&trans->lasttrans);
1988 if (h->cmdresp & 0x80) {
1989 /* Final -- destroy now */
1990 destroy_trans(trans, 0);
1992 } else if (h->oseqno == trans->oiseqno) {
1993 /* Last incoming sequence number -- send ACK without processing */
1994 dundi_ack(trans, 0);
1996 /* Out of window -- simply drop */
1998 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
2003 static int socket_read(int *id, int fd, short events, void *cbdata)
2005 struct sockaddr_in sin;
2007 struct dundi_hdr *h;
2008 char buf[MAX_PACKET_SIZE];
2009 socklen_t len = sizeof(sin);
2011 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
2013 if (errno != ECONNREFUSED)
2014 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2017 if (res < sizeof(struct dundi_hdr)) {
2018 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2022 h = (struct dundi_hdr *) buf;
2024 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2025 AST_LIST_LOCK(&peers);
2026 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2027 AST_LIST_UNLOCK(&peers);
2031 static void build_secret(char *secret, int seclen)
2033 unsigned char tmp[16];
2037 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2038 /* Eliminate potential bad characters */
2039 while((s = strchr(secret, ';'))) *s = '+';
2040 while((s = strchr(secret, '/'))) *s = '+';
2041 while((s = strchr(secret, ':'))) *s = '+';
2042 while((s = strchr(secret, '@'))) *s = '+';
2046 static void save_secret(const char *newkey, const char *oldkey)
2050 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2052 snprintf(tmp, sizeof(tmp), "%s", newkey);
2053 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2054 ast_db_put(secretpath, "secret", tmp);
2055 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2056 ast_db_put(secretpath, "secretexpiry", tmp);
2059 static void load_password(void)
2066 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2067 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2068 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2069 current = strchr(tmp, ';');
2076 if ((time(NULL) - expired) < 0) {
2077 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2078 expired = time(NULL) + DUNDI_SECRET_TIME;
2079 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2088 /* Current key is still valid, just setup rotatation properly */
2089 ast_copy_string(cursecret, current, sizeof(cursecret));
2090 rotatetime = expired;
2092 /* Current key is out of date, rotate or eliminate all together */
2093 build_secret(cursecret, sizeof(cursecret));
2094 save_secret(cursecret, last);
2098 static void check_password(void)
2105 printf("%ld/%ld\n", now, rotatetime);
2107 if ((now - rotatetime) >= 0) {
2108 /* Time to rotate keys */
2109 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2110 build_secret(cursecret, sizeof(cursecret));
2111 save_secret(cursecret, oldsecret);
2115 static void *network_thread(void *ignore)
2117 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2118 from the network, and queue them for delivery to the channels */
2120 /* Establish I/O callback for socket read */
2121 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2123 res = ast_sched_wait(sched);
2124 if ((res > 1000) || (res < 0))
2126 res = ast_io_wait(io, res);
2128 AST_LIST_LOCK(&peers);
2129 ast_sched_runq(sched);
2130 AST_LIST_UNLOCK(&peers);
2137 static void *process_precache(void *ign)
2139 struct dundi_precache_queue *qe;
2148 AST_LIST_LOCK(&pcq);
2149 if ((qe = AST_LIST_FIRST(&pcq))) {
2150 if (!qe->expiration) {
2151 /* Gone... Remove... */
2152 AST_LIST_REMOVE_HEAD(&pcq, list);
2154 } else if (qe->expiration < now) {
2155 /* Process this entry */
2157 ast_copy_string(context, qe->context, sizeof(context));
2158 ast_copy_string(number, qe->number, sizeof(number));
2162 AST_LIST_UNLOCK(&pcq);
2164 dundi_precache(context, number);
2172 static int start_network_thread(void)
2174 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2175 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2179 static int dundi_do_debug(int fd, int argc, char *argv[])
2182 return RESULT_SHOWUSAGE;
2184 ast_cli(fd, "DUNDi Debugging Enabled\n");
2185 return RESULT_SUCCESS;
2188 static int dundi_do_store_history(int fd, int argc, char *argv[])
2191 return RESULT_SHOWUSAGE;
2192 global_storehistory = 1;
2193 ast_cli(fd, "DUNDi History Storage Enabled\n");
2194 return RESULT_SUCCESS;
2197 static int dundi_flush(int fd, int argc, char *argv[])
2200 if ((argc < 2) || (argc > 3))
2201 return RESULT_SHOWUSAGE;
2203 if (!strcasecmp(argv[2], "stats"))
2206 return RESULT_SHOWUSAGE;
2209 /* Flush statistics */
2210 struct dundi_peer *p;
2212 AST_LIST_LOCK(&peers);
2213 AST_LIST_TRAVERSE(&peers, p, list) {
2214 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2216 free(p->lookups[x]);
2217 p->lookups[x] = NULL;
2218 p->lookuptimes[x] = 0;
2222 AST_LIST_UNLOCK(&peers);
2224 ast_db_deltree("dundi/cache", NULL);
2225 ast_cli(fd, "DUNDi Cache Flushed\n");
2227 return RESULT_SUCCESS;
2230 static int dundi_no_debug(int fd, int argc, char *argv[])
2233 return RESULT_SHOWUSAGE;
2235 ast_cli(fd, "DUNDi Debugging Disabled\n");
2236 return RESULT_SUCCESS;
2239 static int dundi_no_store_history(int fd, int argc, char *argv[])
2242 return RESULT_SHOWUSAGE;
2243 global_storehistory = 0;
2244 ast_cli(fd, "DUNDi History Storage Disabled\n");
2245 return RESULT_SUCCESS;
2248 static char *model2str(int model)
2251 case DUNDI_MODEL_INBOUND:
2253 case DUNDI_MODEL_OUTBOUND:
2255 case DUNDI_MODEL_SYMMETRIC:
2262 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2266 struct dundi_peer *p;
2271 AST_LIST_LOCK(&peers);
2273 AST_LIST_TRAVERSE(&peers, p, list) {
2274 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2275 if (!strncasecmp(word, s, len) && ++which > state)
2276 ret = ast_strdup(s);
2278 AST_LIST_UNLOCK(&peers);
2282 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2284 return complete_peer_helper(line, word, pos, state, 3);
2287 static int rescomp(const void *a, const void *b)
2289 const struct dundi_result *resa, *resb;
2292 if (resa->weight < resb->weight)
2294 if (resa->weight > resb->weight)
2299 static void sort_results(struct dundi_result *results, int count)
2301 qsort(results, count, sizeof(results[0]), rescomp);
2304 static int dundi_do_lookup(int fd, int argc, char *argv[])
2312 struct dundi_result dr[MAX_RESULTS];
2313 struct timeval start;
2314 if ((argc < 3) || (argc > 4))
2315 return RESULT_SHOWUSAGE;
2317 if (!strcasecmp(argv[3], "bypass"))
2320 return RESULT_SHOWUSAGE;
2322 ast_copy_string(tmp, argv[2], sizeof(tmp));
2323 context = strchr(tmp, '@');
2328 start = ast_tvnow();
2329 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2332 ast_cli(fd, "DUNDi lookup returned error.\n");
2334 ast_cli(fd, "DUNDi lookup returned no results.\n");
2336 sort_results(dr, res);
2337 for (x=0;x<res;x++) {
2338 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));
2339 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2341 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2342 return RESULT_SUCCESS;
2345 static int dundi_do_precache(int fd, int argc, char *argv[])
2350 struct timeval start;
2351 if ((argc < 3) || (argc > 3))
2352 return RESULT_SHOWUSAGE;
2353 ast_copy_string(tmp, argv[2], sizeof(tmp));
2354 context = strchr(tmp, '@');
2359 start = ast_tvnow();
2360 res = dundi_precache(context, tmp);
2363 ast_cli(fd, "DUNDi precache returned error.\n");
2365 ast_cli(fd, "DUNDi precache returned no error.\n");
2366 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2367 return RESULT_SUCCESS;
2370 static int dundi_do_query(int fd, int argc, char *argv[])
2376 struct dundi_entity_info dei;
2377 if ((argc < 3) || (argc > 3))
2378 return RESULT_SHOWUSAGE;
2379 if (dundi_str_to_eid(&eid, argv[2])) {
2380 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2381 return RESULT_SHOWUSAGE;
2383 ast_copy_string(tmp, argv[2], sizeof(tmp));
2384 context = strchr(tmp, '@');
2389 res = dundi_query_eid(&dei, context, eid);
2391 ast_cli(fd, "DUNDi Query EID returned error.\n");
2393 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2395 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2396 ast_cli(fd, "Department: %s\n", dei.orgunit);
2397 ast_cli(fd, "Organization: %s\n", dei.org);
2398 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2399 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2400 ast_cli(fd, "Country: %s\n", dei.country);
2401 ast_cli(fd, "E-mail: %s\n", dei.email);
2402 ast_cli(fd, "Phone: %s\n", dei.phone);
2403 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2405 return RESULT_SUCCESS;
2408 static int dundi_show_peer(int fd, int argc, char *argv[])
2410 struct dundi_peer *peer;
2411 struct permission *p;
2417 return RESULT_SHOWUSAGE;
2418 AST_LIST_LOCK(&peers);
2419 AST_LIST_TRAVERSE(&peers, peer, list) {
2420 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2424 switch(peer->order) {
2429 order = "Secondary";
2435 order = "Quartiary";
2440 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2441 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2442 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2443 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2444 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2445 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2446 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2447 if (!AST_LIST_EMPTY(&peer->include))
2448 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2449 AST_LIST_TRAVERSE(&peer->include, p, list)
2450 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2451 if (!AST_LIST_EMPTY(&peer->permit))
2452 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2453 AST_LIST_TRAVERSE(&peer->permit, p, list)
2454 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2456 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2457 if (peer->lookups[x]) {
2459 ast_cli(fd, "Last few query times:\n");
2460 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2465 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2467 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2468 AST_LIST_UNLOCK(&peers);
2469 return RESULT_SUCCESS;
2472 static int dundi_show_peers(int fd, int argc, char *argv[])
2474 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2475 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2476 struct dundi_peer *peer;
2477 int registeredonly=0;
2480 int online_peers = 0;
2481 int offline_peers = 0;
2482 int unmonitored_peers = 0;
2483 int total_peers = 0;
2485 if ((argc != 3) && (argc != 4) && (argc != 5))
2486 return RESULT_SHOWUSAGE;
2488 if (!strcasecmp(argv[3], "registered")) {
2491 return RESULT_SHOWUSAGE;
2493 AST_LIST_LOCK(&peers);
2494 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2495 AST_LIST_TRAVERSE(&peers, peer, list) {
2497 int print_line = -1;
2500 if (registeredonly && !peer->addr.sin_addr.s_addr)
2503 if (peer->lastms < 0) {
2504 strcpy(status, "UNREACHABLE");
2507 else if (peer->lastms > peer->maxms) {
2508 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2511 else if (peer->lastms) {
2512 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2516 strcpy(status, "UNKNOWN");
2520 strcpy(status, "Unmonitored");
2521 unmonitored_peers++;
2524 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2526 strcpy(avgms, "Unavail");
2527 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2528 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2529 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2532 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2534 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2536 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2544 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2545 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2546 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2549 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2550 AST_LIST_UNLOCK(&peers);
2551 return RESULT_SUCCESS;
2556 static int dundi_show_trans(int fd, int argc, char *argv[])
2558 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2559 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2560 struct dundi_transaction *trans;
2562 return RESULT_SHOWUSAGE;
2563 AST_LIST_LOCK(&peers);
2564 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2565 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2566 ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2567 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2569 AST_LIST_UNLOCK(&peers);
2570 return RESULT_SUCCESS;
2575 static int dundi_show_entityid(int fd, int argc, char *argv[])
2579 return RESULT_SHOWUSAGE;
2580 AST_LIST_LOCK(&peers);
2581 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2582 AST_LIST_UNLOCK(&peers);
2583 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2584 return RESULT_SUCCESS;
2587 static int dundi_show_requests(int fd, int argc, char *argv[])
2589 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2590 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2591 struct dundi_request *req;
2594 return RESULT_SHOWUSAGE;
2595 AST_LIST_LOCK(&peers);
2596 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2597 AST_LIST_TRAVERSE(&requests, req, list) {
2598 ast_cli(fd, FORMAT, req->number, req->dcontext,
2599 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2601 AST_LIST_UNLOCK(&peers);
2602 return RESULT_SUCCESS;
2607 /* Grok-a-dial DUNDi */
2609 static int dundi_show_mappings(int fd, int argc, char *argv[])
2611 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2612 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2613 struct dundi_mapping *map;
2617 return RESULT_SHOWUSAGE;
2618 AST_LIST_LOCK(&peers);
2619 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2620 AST_LIST_TRAVERSE(&mappings, map, list) {
2621 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map));
2622 ast_cli(fd, FORMAT, map->dcontext, weight,
2623 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2624 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2626 AST_LIST_UNLOCK(&peers);
2627 return RESULT_SUCCESS;
2632 static int dundi_show_precache(int fd, int argc, char *argv[])
2634 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2635 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2636 struct dundi_precache_queue *qe;
2641 return RESULT_SHOWUSAGE;
2643 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2644 AST_LIST_LOCK(&pcq);
2645 AST_LIST_TRAVERSE(&pcq, qe, list) {
2646 s = qe->expiration - now;
2651 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2653 AST_LIST_UNLOCK(&pcq);
2655 return RESULT_SUCCESS;
2660 static const char debug_usage[] =
2661 "Usage: dundi debug\n"
2662 " Enables dumping of DUNDi packets for debugging purposes\n";
2664 static const char no_debug_usage[] =
2665 "Usage: dundi no debug\n"
2666 " Disables dumping of DUNDi packets for debugging purposes\n";
2668 static const char store_history_usage[] =
2669 "Usage: dundi store history\n"
2670 " Enables storing of DUNDi requests and times for debugging\n"
2673 static const char no_store_history_usage[] =
2674 "Usage: dundi no store history\n"
2675 " Disables storing of DUNDi requests and times for debugging\n"
2678 static const char show_peers_usage[] =
2679 "Usage: dundi show peers\n"
2680 " Lists all known DUNDi peers.\n";
2682 static const char show_trans_usage[] =
2683 "Usage: dundi show trans\n"
2684 " Lists all known DUNDi transactions.\n";
2686 static const char show_mappings_usage[] =
2687 "Usage: dundi show mappings\n"
2688 " Lists all known DUNDi mappings.\n";
2690 static const char show_precache_usage[] =
2691 "Usage: dundi show precache\n"
2692 " Lists all known DUNDi scheduled precache updates.\n";
2694 static const char show_entityid_usage[] =
2695 "Usage: dundi show entityid\n"
2696 " Displays the global entityid for this host.\n";
2698 static const char show_peer_usage[] =
2699 "Usage: dundi show peer [peer]\n"
2700 " Provide a detailed description of a specifid DUNDi peer.\n";
2702 static const char show_requests_usage[] =
2703 "Usage: dundi show requests\n"
2704 " Lists all known pending DUNDi requests.\n";
2706 static const char lookup_usage[] =
2707 "Usage: dundi lookup <number>[@context] [bypass]\n"
2708 " Lookup the given number within the given DUNDi context\n"
2709 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2710 "keyword is specified.\n";
2712 static const char precache_usage[] =
2713 "Usage: dundi precache <number>[@context]\n"
2714 " Lookup the given number within the given DUNDi context\n"
2715 "(or e164 if none is specified) and precaches the results to any\n"
2716 "upstream DUNDi push servers.\n";
2718 static const char query_usage[] =
2719 "Usage: dundi query <entity>[@context]\n"
2720 " Attempts to retrieve contact information for a specific\n"
2721 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2722 "e164 if none is specified).\n";
2724 static const char flush_usage[] =
2725 "Usage: dundi flush [stats]\n"
2726 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2727 "'stats' is present, clears timer statistics instead of normal\n"
2730 static struct ast_cli_entry cli_dundi[] = {
2731 { { "dundi", "debug", NULL },
2732 dundi_do_debug, "Enable DUNDi debugging",
2735 { { "dundi", "store", "history", NULL },
2736 dundi_do_store_history, "Enable DUNDi historic records",
2737 store_history_usage },
2739 { { "dundi", "no", "store", "history", NULL },
2740 dundi_no_store_history, "Disable DUNDi historic records",
2741 no_store_history_usage },
2743 { { "dundi", "flush", NULL },
2744 dundi_flush, "Flush DUNDi cache",
2747 { { "dundi", "no", "debug", NULL },
2748 dundi_no_debug, "Disable DUNDi debugging",
2751 { { "dundi", "show", "peers", NULL },
2752 dundi_show_peers, "Show defined DUNDi peers",
2755 { { "dundi", "show", "trans", NULL },
2756 dundi_show_trans, "Show active DUNDi transactions",
2759 { { "dundi", "show", "entityid", NULL },
2760 dundi_show_entityid, "Display Global Entity ID",
2761 show_entityid_usage },
2763 { { "dundi", "show", "mappings", NULL },
2764 dundi_show_mappings, "Show DUNDi mappings",
2765 show_mappings_usage },
2767 { { "dundi", "show", "precache", NULL },
2768 dundi_show_precache, "Show DUNDi precache",
2769 show_precache_usage },
2771 { { "dundi", "show", "requests", NULL },
2772 dundi_show_requests, "Show DUNDi requests",
2773 show_requests_usage },
2775 { { "dundi", "show", "peer", NULL },
2776 dundi_show_peer, "Show info on a specific DUNDi peer",
2777 show_peer_usage, complete_peer_4 },
2779 { { "dundi", "lookup", NULL },
2780 dundi_do_lookup, "Lookup a number in DUNDi",
2783 { { "dundi", "precache", NULL },
2784 dundi_do_precache, "Precache a number in DUNDi",
2787 { { "dundi", "query", NULL },
2788 dundi_do_query, "Query a DUNDi EID",
2792 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2794 struct dundi_transaction *trans;
2797 /* Don't allow creation of transactions to non-registered peers */
2798 if (p && !p->addr.sin_addr.s_addr)
2800 tid = get_trans_id();
2803 if (!(trans = ast_calloc(1, sizeof(*trans))))
2806 if (global_storehistory) {
2807 trans->start = ast_tvnow();
2808 ast_set_flag(trans, FLAG_STOREHIST);
2810 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2811 trans->autokillid = -1;
2813 apply_peer(trans, p);
2814 if (!p->sentfullkey)
2815 ast_set_flag(trans, FLAG_SENDFULLKEY);
2817 trans->strans = tid;
2818 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2823 static int dundi_xmit(struct dundi_packet *pack)
2827 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2828 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2830 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2831 ast_inet_ntoa(pack->parent->addr.sin_addr),
2832 ntohs(pack->parent->addr.sin_port), strerror(errno));
2839 static void destroy_packet(struct dundi_packet *pack, int needfree)
2842 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2843 if (pack->retransid > -1)
2844 ast_sched_del(sched, pack->retransid);
2848 pack->retransid = -1;
2851 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2853 struct dundi_peer *peer;
2858 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2859 AST_LIST_TRAVERSE(&peers, peer, list) {
2860 if (peer->regtrans == trans)
2861 peer->regtrans = NULL;
2862 if (peer->qualtrans == trans) {
2864 if (peer->lastms > -1)
2865 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2868 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2871 if (ms < peer->maxms) {
2872 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2873 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2874 } else if (peer->lastms < peer->maxms) {
2875 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);
2879 peer->qualtrans = NULL;
2881 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2882 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2883 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2886 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2887 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2888 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2889 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2890 peer->lookups[x] = peer->lookups[x-1];
2891 if (peer->lookups[x]) {
2892 peer->avgms += peer->lookuptimes[x];
2896 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2897 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2898 if (peer->lookups[0]) {
2899 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2900 peer->avgms += peer->lookuptimes[0];
2910 if (trans->parent) {
2911 /* Unlink from parent if appropriate */
2912 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
2913 if (AST_LIST_EMPTY(&trans->parent->trans)) {
2914 /* Wake up sleeper */
2915 if (trans->parent->pfds[1] > -1) {
2916 write(trans->parent->pfds[1], "killa!", 6);
2920 /* Unlink from all trans */
2921 AST_LIST_REMOVE(&alltrans, trans, all);
2922 destroy_packets(&trans->packets);
2923 destroy_packets(&trans->lasttrans);
2924 if (trans->autokillid > -1)
2925 ast_sched_del(sched, trans->autokillid);
2926 trans->autokillid = -1;
2927 if (trans->thread) {
2928 /* If used by a thread, mark as dead and be done */
2929 ast_set_flag(trans, FLAG_DEAD);