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)
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <sys/socket.h>
37 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
38 #include <sys/types.h>
39 #include <netinet/in_systm.h>
41 #include <netinet/ip.h>
42 #include <sys/ioctl.h>
43 #include <netinet/in.h>
45 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
46 #include <net/if_dl.h>
53 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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 extern char ast_config_AST_KEY_DIR[];
85 #define DUNDI_MODEL_INBOUND (1 << 0)
86 #define DUNDI_MODEL_OUTBOUND (1 << 1)
87 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
89 /* Keep times of last 10 lookups */
90 #define DUNDI_TIMING_HISTORY 10
92 #define FLAG_ISREG (1 << 0) /* Transaction is register request */
93 #define FLAG_DEAD (1 << 1) /* Transaction is dead */
94 #define FLAG_FINAL (1 << 2) /* Transaction has final message sent */
95 #define FLAG_ISQUAL (1 << 3) /* Transaction is a qualification */
96 #define FLAG_ENCRYPT (1 << 4) /* Transaction is encrypted wiht ECX/DCX */
97 #define FLAG_SENDFULLKEY (1 << 5) /* Send full key on transaction */
98 #define FLAG_STOREHIST (1 << 6) /* Record historic performance */
100 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
103 #define DUNDI_SECRET_TIME 15 /* Testing only */
105 #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 } };
139 struct permission *next;
144 struct dundi_packet {
146 struct dundi_packet *next;
148 struct dundi_transaction *parent;
151 unsigned char data[0];
154 struct dundi_hint_metadata {
155 unsigned short flags;
156 char exten[AST_MAX_EXTENSION];
159 struct dundi_precache_queue {
160 struct dundi_precache_queue *next;
166 struct dundi_request;
168 struct dundi_transaction {
169 struct sockaddr_in addr; /* Other end of transaction */
170 struct timeval start; /* When this transaction was created */
171 dundi_eid eids[DUNDI_MAX_STACK + 1];
172 int eidcount; /* Number of eids in eids */
173 dundi_eid us_eid; /* Our EID, to them */
174 dundi_eid them_eid; /* Their EID, to us */
175 aes_encrypt_ctx ecx; /* AES 128 Encryption context */
176 aes_decrypt_ctx dcx; /* AES 128 Decryption context */
177 unsigned int flags; /* Has final packet been sent */
178 int ttl; /* Remaining TTL for queries on this one */
179 int thread; /* We have a calling thread */
180 int retranstimer; /* How long to wait before retransmissions */
181 int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
182 int autokilltimeout; /* Recommended timeout for autokill */
183 unsigned short strans; /* Our transaction identifier */
184 unsigned short dtrans; /* Their transaction identifer */
185 unsigned char iseqno; /* Next expected received seqno */
186 unsigned char oiseqno; /* Last received incoming seqno */
187 unsigned char oseqno; /* Next transmitted seqno */
188 unsigned char aseqno; /* Last acknowledge seqno */
189 struct dundi_packet *packets; /* Packets to be retransmitted */
190 struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
191 struct dundi_transaction *next; /* Next with respect to the parent */
192 struct dundi_request *parent; /* Parent request (if there is one) */
193 struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
196 struct dundi_request {
197 char dcontext[AST_MAX_EXTENSION];
198 char number[AST_MAX_EXTENSION];
201 struct dundi_result *dr;
202 struct dundi_entity_info *dei;
203 struct dundi_hint_metadata *hmd;
209 unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
210 struct dundi_transaction *trans; /* Transactions */
211 struct dundi_request *next;
214 static struct dundi_mapping {
215 char dcontext[AST_MAX_EXTENSION];
216 char lcontext[AST_MAX_EXTENSION];
221 char dest[AST_MAX_EXTENSION];
222 struct dundi_mapping *next;
225 static struct dundi_peer {
227 struct sockaddr_in addr; /* Address of DUNDi peer */
228 struct permission *permit;
229 struct permission *include;
230 struct permission *precachesend;
231 struct permission *precachereceive;
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 struct dundi_transaction *keypending;
256 int model; /* Pull model */
257 int pcmodel; /* Push/precache model */
258 int dynamic; /* Are we dynamic? */
259 int lastms; /* Last measured latency */
260 int maxms; /* Max permissible latency */
261 struct timeval qualtx; /* Time of transmit */
262 struct dundi_peer *next;
265 static struct dundi_precache_queue *pcq;
267 AST_MUTEX_DEFINE_STATIC(peerlock);
268 AST_MUTEX_DEFINE_STATIC(pclock);
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 permission *ps, char *cont)
287 if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
294 static char *tech2str(int tech)
297 case DUNDI_PROTO_NONE:
299 case DUNDI_PROTO_IAX:
301 case DUNDI_PROTO_SIP:
303 case DUNDI_PROTO_H323:
310 static int str2tech(char *str)
312 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
313 return DUNDI_PROTO_IAX;
314 else if (!strcasecmp(str, "SIP"))
315 return DUNDI_PROTO_SIP;
316 else if (!strcasecmp(str, "H323"))
317 return DUNDI_PROTO_H323;
322 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[]);
323 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
324 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
325 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
327 /* Look for an exact match first */
328 struct dundi_transaction *trans;
331 if (!inaddrcmp(&trans->addr, sin) &&
332 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
333 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
335 trans->dtrans = ntohs(hdr->strans) & 32767;
338 trans = trans->allnext;
341 switch(hdr->cmdresp & 0x7f) {
342 case DUNDI_COMMAND_DPDISCOVER:
343 case DUNDI_COMMAND_EIDQUERY:
344 case DUNDI_COMMAND_PRECACHERQ:
345 case DUNDI_COMMAND_REGREQ:
346 case DUNDI_COMMAND_NULL:
347 case DUNDI_COMMAND_ENCRYPT:
349 /* Create new transaction */
350 trans = create_transaction(NULL);
352 memcpy(&trans->addr, sin, sizeof(trans->addr));
353 trans->dtrans = ntohs(hdr->strans) & 32767;
355 ast_log(LOG_WARNING, "Out of memory!\n");
365 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
367 static int dundi_ack(struct dundi_transaction *trans, int final)
369 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
371 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
374 struct dundi_packet pack;
375 struct dundi_hdr hdr;
377 struct dundi_transaction trans;
378 /* Never respond to an INVALID with another INVALID */
379 if (h->cmdresp == DUNDI_COMMAND_INVALID)
381 memset(&tmp, 0, sizeof(tmp));
382 memset(&trans, 0, sizeof(trans));
383 memcpy(&trans.addr, sin, sizeof(trans.addr));
384 tmp.hdr.strans = h->dtrans;
385 tmp.hdr.dtrans = h->strans;
386 tmp.hdr.iseqno = h->oseqno;
387 tmp.hdr.oseqno = h->iseqno;
388 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
389 tmp.hdr.cmdflags = 0;
390 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
391 tmp.pack.datalen = sizeof(struct dundi_hdr);
392 tmp.pack.parent = &trans;
393 dundi_xmit(&tmp.pack);
396 static void reset_global_eid(void)
398 #if defined(SIOCGIFHWADDR)
403 s = socket(AF_INET, SOCK_STREAM, 0);
407 memset(&ifr, 0, sizeof(ifr));
408 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
409 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
410 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
411 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);
419 #if defined(ifa_broadaddr) && !defined(SOLARIS)
421 struct ifaddrs *ifap;
423 if (getifaddrs(&ifap) == 0) {
425 for (p = ifap; p; p = p->ifa_next) {
426 if (p->ifa_addr->sa_family == AF_LINK) {
427 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
430 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;
451 if (t->strans == tid)
457 tid = (tid % 32766) + 1;
458 } 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;
485 if (!dundi_eid_cmp(&cur->eid,eid))
492 static void build_iv(unsigned char *iv)
494 /* XXX Would be nice to be more random XXX */
495 unsigned int *fluffy;
497 fluffy = (unsigned int *)(iv);
499 fluffy[x] = ast_random();
502 struct dundi_query_state {
503 dundi_eid *eids[DUNDI_MAX_STACK + 1];
504 int directs[DUNDI_MAX_STACK + 1];
506 char called_context[AST_MAX_EXTENSION];
507 char called_number[AST_MAX_EXTENSION];
508 struct dundi_mapping *maps;
511 struct dundi_transaction *trans;
518 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)
520 struct ast_flags flags = {0};
522 if (!ast_strlen_zero(map->lcontext)) {
523 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
524 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
525 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
526 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
527 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
528 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
529 if (ast_ignore_pattern(map->lcontext, called_number))
530 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
532 /* Clearly we can't say 'don't ask' anymore if we found anything... */
533 if (ast_test_flag(&flags, AST_FLAGS_ALL))
534 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
536 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
537 /* Skip partial answers */
538 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
540 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
541 struct varshead headp;
542 struct ast_var_t *newvariable;
543 ast_set_flag(&flags, map->options & 0xffff);
544 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
545 dr[anscnt].techint = map->tech;
546 dr[anscnt].weight = map->weight;
547 dr[anscnt].expiration = dundi_cache_time;
548 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
549 dr[anscnt].eid = *us_eid;
550 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
551 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
552 AST_LIST_HEAD_INIT_NOLOCK(&headp);
553 newvariable = ast_var_assign("NUMBER", called_number);
554 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
555 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
556 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
557 newvariable = ast_var_assign("SECRET", cursecret);
558 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
559 newvariable = ast_var_assign("IPADDR", ipaddr);
560 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
561 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
562 while (!AST_LIST_EMPTY(&headp)) { /* List Deletion. */
563 newvariable = AST_LIST_REMOVE_HEAD(&headp, entries);
564 ast_var_delete(newvariable);
567 dr[anscnt].dest[0] = '\0';
570 /* No answers... Find the fewest number of digits from the
571 number for which we have no answer. */
572 char tmp[AST_MAX_EXTENSION];
573 for (x=0;x<AST_MAX_EXTENSION;x++) {
574 tmp[x] = called_number[x];
577 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
578 /* Oops found something we can't match. If this is longer
579 than the running hint, we have to consider it */
580 if (strlen(tmp) > strlen(hmd->exten)) {
581 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
591 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
593 static void *dundi_lookup_thread(void *data)
595 struct dundi_query_state *st = data;
596 struct dundi_result dr[MAX_RESULTS];
597 struct dundi_ie_data ied;
598 struct dundi_hint_metadata hmd;
603 int expiration = dundi_cache_time;
605 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
606 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
607 memset(&ied, 0, sizeof(ied));
608 memset(&dr, 0, sizeof(dr));
609 memset(&hmd, 0, sizeof(hmd));
610 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
611 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
612 for (x=0;x<st->nummaps;x++)
613 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
616 for (x=0;x<ouranswers;x++) {
617 if (dr[x].weight < max)
622 /* If we do not have a canonical result, keep looking */
623 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);
625 /* Append answer in result */
628 if ((res < -1) && (!ouranswers))
629 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
632 ast_mutex_lock(&peerlock);
633 /* Truncate if "don't ask" isn't present */
634 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
636 if (ast_test_flag(st->trans, FLAG_DEAD)) {
637 ast_log(LOG_DEBUG, "Our transaction went away!\n");
638 st->trans->thread = 0;
639 destroy_trans(st->trans, 0);
641 for (x=0;x<ouranswers;x++) {
643 if (dr[x].expiration && (expiration > dr[x].expiration))
644 expiration = dr[x].expiration;
645 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
647 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
648 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
649 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
650 st->trans->thread = 0;
652 ast_mutex_unlock(&peerlock);
657 static void *dundi_precache_thread(void *data)
659 struct dundi_query_state *st = data;
660 struct dundi_ie_data ied;
661 struct dundi_hint_metadata hmd;
664 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
665 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
666 memset(&ied, 0, sizeof(ied));
668 /* Now produce precache */
669 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
671 ast_mutex_lock(&peerlock);
672 /* Truncate if "don't ask" isn't present */
673 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
675 if (ast_test_flag(st->trans, FLAG_DEAD)) {
676 ast_log(LOG_DEBUG, "Our transaction went away!\n");
677 st->trans->thread = 0;
678 destroy_trans(st->trans, 0);
680 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
681 st->trans->thread = 0;
683 ast_mutex_unlock(&peerlock);
688 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[]);
690 static void *dundi_query_thread(void *data)
692 struct dundi_query_state *st = data;
693 struct dundi_entity_info dei;
694 struct dundi_ie_data ied;
695 struct dundi_hint_metadata hmd;
698 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
699 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
700 memset(&ied, 0, sizeof(ied));
701 memset(&dei, 0, sizeof(dei));
702 memset(&hmd, 0, sizeof(hmd));
703 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
705 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
706 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
707 ast_copy_string(dei.org, org, sizeof(dei.org));
708 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
709 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
710 ast_copy_string(dei.country, country, sizeof(dei.country));
711 ast_copy_string(dei.email, email, sizeof(dei.email));
712 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
715 /* If we do not have a canonical result, keep looking */
716 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
718 ast_mutex_lock(&peerlock);
719 if (ast_test_flag(st->trans, FLAG_DEAD)) {
720 ast_log(LOG_DEBUG, "Our transaction went away!\n");
721 st->trans->thread = 0;
722 destroy_trans(st->trans, 0);
725 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
726 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
727 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
728 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
729 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
730 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
731 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
732 if (!ast_strlen_zero(dei.ipaddr))
733 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
735 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
736 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
737 st->trans->thread = 0;
739 ast_mutex_unlock(&peerlock);
744 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
746 struct dundi_query_state *st;
750 struct dundi_ie_data ied;
753 pthread_t lookupthread;
755 if (ies->eidcount > 1) {
756 /* Since it is a requirement that the first EID is the authenticating host
757 and the last EID is the root, it is permissible that the first and last EID
758 could be the same. In that case, we should go ahead copy only the "root" section
759 since we will not need it for authentication. */
760 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
763 totallen = sizeof(struct dundi_query_state);
764 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
765 st = malloc(totallen);
767 memset(st, 0, totallen);
768 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
769 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
771 st->ttl = ies->ttl - 1;
775 for (x=skipfirst;ies->eids[x];x++) {
776 st->eids[x-skipfirst] = (dundi_eid *)s;
777 *st->eids[x-skipfirst] = *ies->eids[x];
778 s += sizeof(dundi_eid);
780 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);
781 pthread_attr_init(&attr);
782 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
784 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
786 ast_log(LOG_WARNING, "Unable to create thread!\n");
788 memset(&ied, 0, sizeof(ied));
789 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
790 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
794 ast_log(LOG_WARNING, "Out of memory!\n");
795 memset(&ied, 0, sizeof(ied));
796 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
797 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
803 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
808 char eidpeer_str[20];
809 char eidroot_str[20];
814 expiration = dundi_cache_time;
816 /* Only cache hint if "don't ask" is there... */
817 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
820 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
822 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
823 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
824 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
825 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
828 timeout += expiration;
829 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
831 ast_db_put("dundi/cache", key1, data);
832 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
833 ast_db_put("dundi/cache", key2, data);
834 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
838 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
844 char eidpeer_str[20];
845 char eidroot_str[20];
849 expiration = dundi_cache_time;
851 /* Keep pushes a little longer, cut pulls a little short */
858 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
859 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
860 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
861 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
862 /* Build request string */
864 timeout += expiration;
865 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
866 for (x=start;x<req->respcount;x++) {
867 /* Skip anything with an illegal pipe in it */
868 if (strchr(req->dr[x].dest, '|'))
870 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
871 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
872 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
874 ast_db_put("dundi/cache", key1, data);
875 ast_db_put("dundi/cache", key2, data);
879 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
881 struct dundi_query_state *st;
884 struct dundi_ie_data ied;
886 struct dundi_result dr2[MAX_RESULTS];
887 struct dundi_request dr;
888 struct dundi_hint_metadata hmd;
890 struct dundi_mapping *cur;
894 pthread_t lookupthread;
897 memset(&dr2, 0, sizeof(dr2));
898 memset(&dr, 0, sizeof(dr));
899 memset(&hmd, 0, sizeof(hmd));
901 /* Forge request structure to hold answers for cache */
902 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
904 dr.maxcount = MAX_RESULTS;
905 dr.expiration = dundi_cache_time;
907 dr.pfds[0] = dr.pfds[1] = -1;
909 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
910 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
912 for (x=0;x<ies->anscount;x++) {
913 if (trans->parent->respcount < trans->parent->maxcount) {
914 /* Make sure it's not already there */
915 for (z=0;z<trans->parent->respcount;z++) {
916 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
917 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
920 if (z == trans->parent->respcount) {
921 /* Copy into parent responses */
922 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
923 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
924 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
925 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
926 if (ies->expiration > 0)
927 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
929 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
930 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
931 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
932 &ies->answers[x]->eid);
933 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
934 sizeof(trans->parent->dr[trans->parent->respcount].dest));
935 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
936 sizeof(trans->parent->dr[trans->parent->respcount].tech));
937 trans->parent->respcount++;
938 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
939 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
940 /* Update weight if appropriate */
941 trans->parent->dr[z].weight = ies->answers[x]->weight;
944 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
945 trans->parent->number, trans->parent->dcontext);
948 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
949 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
951 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
953 totallen = sizeof(struct dundi_query_state);
954 /* Count matching map entries */
958 if (!strcasecmp(cur->dcontext, ccontext))
963 /* If no maps, return -1 immediately */
967 if (ies->eidcount > 1) {
968 /* Since it is a requirement that the first EID is the authenticating host
969 and the last EID is the root, it is permissible that the first and last EID
970 could be the same. In that case, we should go ahead copy only the "root" section
971 since we will not need it for authentication. */
972 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
976 /* Prepare to run a query and then propagate that as necessary */
977 totallen += mapcount * sizeof(struct dundi_mapping);
978 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
979 st = malloc(totallen);
981 memset(st, 0, totallen);
982 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
983 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
985 st->ttl = ies->ttl - 1;
986 st->nocache = ies->cbypass;
990 for (x=skipfirst;ies->eids[x];x++) {
991 st->eids[x-skipfirst] = (dundi_eid *)s;
992 *st->eids[x-skipfirst] = *ies->eids[x];
993 st->directs[x-skipfirst] = ies->eid_direct[x];
994 s += sizeof(dundi_eid);
996 /* Append mappings */
998 st->maps = (struct dundi_mapping *)s;
1001 if (!strcasecmp(cur->dcontext, ccontext)) {
1004 st->maps[x].next = NULL;
1010 st->nummaps = mapcount;
1011 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1012 pthread_attr_init(&attr);
1013 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1015 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1017 ast_log(LOG_WARNING, "Unable to create thread!\n");
1019 memset(&ied, 0, sizeof(ied));
1020 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1021 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1025 ast_log(LOG_WARNING, "Out of memory!\n");
1026 memset(&ied, 0, sizeof(ied));
1027 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1028 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1034 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1036 struct dundi_query_state *st;
1039 struct dundi_ie_data ied;
1041 struct dundi_mapping *cur;
1045 pthread_t lookupthread;
1046 pthread_attr_t attr;
1047 totallen = sizeof(struct dundi_query_state);
1048 /* Count matching map entries */
1052 if (!strcasecmp(cur->dcontext, ccontext))
1056 /* If no maps, return -1 immediately */
1060 if (ies->eidcount > 1) {
1061 /* Since it is a requirement that the first EID is the authenticating host
1062 and the last EID is the root, it is permissible that the first and last EID
1063 could be the same. In that case, we should go ahead copy only the "root" section
1064 since we will not need it for authentication. */
1065 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1069 totallen += mapcount * sizeof(struct dundi_mapping);
1070 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1071 st = malloc(totallen);
1073 memset(st, 0, totallen);
1074 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1075 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1077 st->ttl = ies->ttl - 1;
1078 st->nocache = ies->cbypass;
1082 for (x=skipfirst;ies->eids[x];x++) {
1083 st->eids[x-skipfirst] = (dundi_eid *)s;
1084 *st->eids[x-skipfirst] = *ies->eids[x];
1085 st->directs[x-skipfirst] = ies->eid_direct[x];
1086 s += sizeof(dundi_eid);
1088 /* Append mappings */
1090 st->maps = (struct dundi_mapping *)s;
1093 if (!strcasecmp(cur->dcontext, ccontext)) {
1096 st->maps[x].next = NULL;
1102 st->nummaps = mapcount;
1103 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1104 pthread_attr_init(&attr);
1105 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1107 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1109 ast_log(LOG_WARNING, "Unable to create thread!\n");
1111 memset(&ied, 0, sizeof(ied));
1112 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1113 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1117 ast_log(LOG_WARNING, "Out of memory!\n");
1118 memset(&ied, 0, sizeof(ied));
1119 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1120 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1126 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1129 char *ptr, *term, *src;
1131 struct ast_flags flags;
1137 /* Build request string */
1138 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1141 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1142 int expiration = timeout - now;
1143 if (expiration > 0) {
1144 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1146 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1148 term = strchr(ptr, '|');
1151 src = strrchr(ptr, '/');
1157 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1158 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1159 /* Make sure it's not already there */
1160 for (z=0;z<req->respcount;z++) {
1161 if ((req->dr[z].techint == tech) &&
1162 !strcmp(req->dr[z].dest, ptr))
1165 if (z == req->respcount) {
1166 /* Copy into parent responses */
1167 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1168 req->dr[req->respcount].weight = weight;
1169 req->dr[req->respcount].techint = tech;
1170 req->dr[req->respcount].expiration = expiration;
1171 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1172 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1173 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1174 ast_copy_string(req->dr[req->respcount].dest, ptr,
1175 sizeof(req->dr[req->respcount].dest));
1176 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1177 sizeof(req->dr[req->respcount].tech));
1179 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1180 } else if (req->dr[z].weight > weight)
1181 req->dr[z].weight = weight;
1185 /* We found *something* cached */
1186 if (expiration < *lowexpiration)
1187 *lowexpiration = expiration;
1190 ast_db_del("dundi/cache", key);
1192 ast_db_del("dundi/cache", key);
1198 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1202 char eidroot_str[20];
1206 char eid_str_full[20];
1211 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1212 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1213 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1214 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1215 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1216 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1217 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1218 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1219 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1221 if (!req->respcount) {
1223 /* Look and see if we have a hint that would preclude us from looking at this
1224 peer for this number. */
1225 if (!(tmp[x] = req->number[x]))
1228 /* Check for hints */
1229 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1230 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1231 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1232 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1233 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1234 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1236 if (strlen(tmp) > strlen(req->hmd->exten)) {
1237 /* Update meta data if appropriate */
1238 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1248 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1250 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1252 if (!trans->addr.sin_addr.s_addr)
1253 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1254 trans->us_eid = p->us_eid;
1255 trans->them_eid = p->eid;
1256 /* Enable encryption if appropriate */
1257 if (!ast_strlen_zero(p->inkey))
1258 ast_set_flag(trans, FLAG_ENCRYPT);
1260 trans->autokilltimeout = p->maxms;
1261 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1262 if (p->lastms > 1) {
1263 trans->retranstimer = p->lastms * 2;
1264 /* Keep it from being silly */
1265 if (trans->retranstimer < 150)
1266 trans->retranstimer = 150;
1268 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1269 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1271 trans->autokilltimeout = global_autokilltimeout;
1274 static int do_register_expire(void *data)
1276 struct dundi_peer *peer = data;
1278 /* Called with peerlock already held */
1279 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1280 peer->registerexpire = -1;
1282 memset(&peer->addr, 0, sizeof(peer->addr));
1286 static int update_key(struct dundi_peer *peer)
1288 unsigned char key[16];
1289 struct ast_key *ekey, *skey;
1292 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1294 aes_encrypt_key128(key, &peer->us_ecx);
1295 aes_decrypt_key128(key, &peer->us_dcx);
1296 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1298 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1299 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1302 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1304 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1305 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1308 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1309 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1312 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1313 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1316 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1317 peer->sentfullkey = 0;
1319 time(&peer->keyexpire);
1320 peer->keyexpire += dundi_key_ttl;
1325 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1327 unsigned char curblock[16];
1329 memcpy(curblock, iv, sizeof(curblock));
1332 curblock[x] ^= src[x];
1333 aes_encrypt(curblock, dst, ecx);
1334 memcpy(curblock, dst, sizeof(curblock));
1341 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1343 unsigned char lastblock[16];
1345 memcpy(lastblock, iv, sizeof(lastblock));
1347 aes_decrypt(src, dst, dcx);
1349 dst[x] ^= lastblock[x];
1350 memcpy(lastblock, src, sizeof(lastblock));
1358 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)
1360 int space = *dstlen;
1361 unsigned long bytes;
1362 struct dundi_hdr *h;
1363 unsigned char *decrypt_space;
1364 decrypt_space = alloca(srclen);
1367 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1369 h = (struct dundi_hdr *)dst;
1372 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1373 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1377 *dstlen = bytes + 6;
1378 /* Return new header */
1382 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1384 unsigned char *compress_space;
1387 unsigned long bytes;
1388 struct dundi_ie_data ied;
1389 struct dundi_peer *peer;
1390 unsigned char iv[16];
1391 len = pack->datalen + pack->datalen / 100 + 42;
1392 compress_space = alloca(len);
1393 if (compress_space) {
1394 memset(compress_space, 0, len);
1395 /* We care about everthing save the first 6 bytes of header */
1397 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1399 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1402 memset(&ied, 0, sizeof(ied));
1403 /* Say who we are */
1404 if (!pack->h->iseqno && !pack->h->oseqno) {
1405 /* Need the key in the first copy */
1406 if (!(peer = find_peer(&trans->them_eid)))
1408 if (update_key(peer))
1410 if (!peer->sentfullkey)
1411 ast_set_flag(trans, FLAG_SENDFULLKEY);
1412 /* Append key data */
1413 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1414 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1415 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1416 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1418 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1420 /* Setup contexts */
1421 trans->ecx = peer->us_ecx;
1422 trans->dcx = peer->us_dcx;
1424 /* We've sent the full key */
1425 peer->sentfullkey = 1;
1427 /* Build initialization vector */
1429 /* Add the field, rounded up to 16 bytes */
1430 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1432 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1433 ast_log(LOG_NOTICE, "Final packet too large!\n");
1436 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1437 ied.pos += ((bytes + 15) / 16) * 16;
1438 /* Reconstruct header */
1439 pack->datalen = sizeof(struct dundi_hdr);
1440 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1441 pack->h->cmdflags = 0;
1442 memcpy(pack->h->ies, ied.buf, ied.pos);
1443 pack->datalen += ied.pos;
1449 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1451 unsigned char dst[128];
1453 struct ast_key *key, *skey;
1456 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1457 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1460 } else if (!newkey || !newsig)
1462 if (!memcmp(peer->rxenckey, newkey, 128) &&
1463 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1464 /* By definition, a match */
1468 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1470 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1471 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1475 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1477 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1478 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1482 /* First check signature */
1483 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1487 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1490 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1493 /* Decrypted, passes signature */
1494 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1495 memcpy(peer->rxenckey, newkey, 128);
1496 memcpy(peer->rxenckey + 128, newsig, 128);
1497 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1498 aes_decrypt_key128(dst, &peer->them_dcx);
1499 aes_encrypt_key128(dst, &peer->them_ecx);
1503 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1505 /* Handle canonical command / response */
1506 int final = hdr->cmdresp & 0x80;
1507 int cmd = hdr->cmdresp & 0x7f;
1512 unsigned char *bufcpy;
1513 struct dundi_ie_data ied;
1514 struct dundi_ies ies;
1515 struct dundi_peer *peer;
1518 memset(&ied, 0, sizeof(ied));
1519 memset(&ies, 0, sizeof(ies));
1521 bufcpy = alloca(datalen);
1524 /* Make a copy for parsing */
1525 memcpy(bufcpy, hdr->ies, datalen);
1526 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1527 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1528 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1533 case DUNDI_COMMAND_DPDISCOVER:
1534 case DUNDI_COMMAND_EIDQUERY:
1535 case DUNDI_COMMAND_PRECACHERQ:
1536 if (cmd == DUNDI_COMMAND_EIDQUERY)
1537 resp = DUNDI_COMMAND_EIDRESPONSE;
1538 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1539 resp = DUNDI_COMMAND_PRECACHERP;
1541 resp = DUNDI_COMMAND_DPRESPONSE;
1542 /* A dialplan or entity discover -- qualify by highest level entity */
1543 peer = find_peer(ies.eids[0]);
1545 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1546 dundi_send(trans, resp, 0, 1, &ied);
1549 trans->us_eid = peer->us_eid;
1550 if (strlen(peer->inkey)) {
1551 hasauth = encrypted;
1555 /* Okay we're authentiated and all, now we check if they're authorized */
1556 if (!ies.called_context)
1557 ies.called_context = "e164";
1558 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1559 res = dundi_answer_entity(trans, &ies, ies.called_context);
1561 if (ast_strlen_zero(ies.called_number)) {
1562 /* They're not permitted to access that context */
1563 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1564 dundi_send(trans, resp, 0, 1, &ied);
1565 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1566 (peer->model & DUNDI_MODEL_INBOUND) &&
1567 has_permission(peer->permit, ies.called_context)) {
1568 res = dundi_answer_query(trans, &ies, ies.called_context);
1570 /* There is no such dundi context */
1571 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1572 dundi_send(trans, resp, 0, 1, &ied);
1574 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1575 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1576 has_permission(peer->include, ies.called_context)) {
1577 res = dundi_prop_precache(trans, &ies, ies.called_context);
1579 /* There is no such dundi context */
1580 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1581 dundi_send(trans, resp, 0, 1, &ied);
1584 /* They're not permitted to access that context */
1585 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1586 dundi_send(trans, resp, 0, 1, &ied);
1590 /* They're not permitted to access that context */
1591 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1592 dundi_send(trans, resp, 0, 1, &ied);
1596 case DUNDI_COMMAND_REGREQ:
1597 /* A register request -- should only have one entity */
1598 peer = find_peer(ies.eids[0]);
1599 if (!peer || !peer->dynamic) {
1600 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1601 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1604 trans->us_eid = peer->us_eid;
1605 if (!ast_strlen_zero(peer->inkey)) {
1606 hasauth = encrypted;
1610 int expire = default_expiration;
1611 char iabuf[INET_ADDRSTRLEN];
1614 if (peer->registerexpire > -1)
1615 ast_sched_del(sched, peer->registerexpire);
1616 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1617 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1618 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1619 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1620 if (inaddrcmp(&peer->addr, &trans->addr)) {
1621 if (option_verbose > 2)
1622 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), iabuf, ntohs(trans->addr.sin_port));
1626 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1627 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1628 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1630 qualify_peer(peer, 1);
1634 case DUNDI_COMMAND_DPRESPONSE:
1635 /* A dialplan response, lets see what we got... */
1636 if (ies.cause < 1) {
1637 /* Success of some sort */
1638 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1639 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1640 authpass = encrypted;
1644 /* Pass back up answers */
1645 if (trans->parent && trans->parent->dr) {
1646 y = trans->parent->respcount;
1647 for (x=0;x<ies.anscount;x++) {
1648 if (trans->parent->respcount < trans->parent->maxcount) {
1649 /* Make sure it's not already there */
1650 for (z=0;z<trans->parent->respcount;z++) {
1651 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1652 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1655 if (z == trans->parent->respcount) {
1656 /* Copy into parent responses */
1657 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1658 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1659 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1660 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1661 if (ies.expiration > 0)
1662 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1664 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1665 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1666 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1667 &ies.answers[x]->eid);
1668 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1669 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1670 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1671 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1672 trans->parent->respcount++;
1673 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1674 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1675 /* Update weight if appropriate */
1676 trans->parent->dr[z].weight = ies.answers[x]->weight;
1679 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1680 trans->parent->number, trans->parent->dcontext);
1682 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1683 the cache know if this request was unaffected by our entity list. */
1684 cache_save(&trans->them_eid, trans->parent, y,
1685 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1687 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1688 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1689 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1690 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1691 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1692 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1693 sizeof(trans->parent->hmd->exten));
1696 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1699 if (ies.expiration > 0) {
1700 if (trans->parent->expiration > ies.expiration) {
1701 trans->parent->expiration = ies.expiration;
1705 /* Close connection if not final */
1707 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1711 /* Auth failure, check for data */
1713 /* Cancel if they didn't already */
1714 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1718 case DUNDI_COMMAND_EIDRESPONSE:
1719 /* A dialplan response, lets see what we got... */
1720 if (ies.cause < 1) {
1721 /* Success of some sort */
1722 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1723 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1724 authpass = encrypted;
1728 /* Pass back up answers */
1729 if (trans->parent && trans->parent->dei && ies.q_org) {
1730 if (!trans->parent->respcount) {
1731 trans->parent->respcount++;
1733 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1735 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1737 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1738 if (ies.q_stateprov)
1739 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1741 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1743 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1745 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1747 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1748 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1749 /* If it's them, update our address */
1750 ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1751 trans->addr.sin_addr);
1755 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1756 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1759 /* Close connection if not final */
1761 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1765 /* Auth failure, check for data */
1767 /* Cancel if they didn't already */
1768 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1772 case DUNDI_COMMAND_REGRESPONSE:
1773 /* A dialplan response, lets see what we got... */
1774 if (ies.cause < 1) {
1776 /* Success of some sort */
1777 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1778 hasauth = encrypted;
1783 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1785 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1786 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1789 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),
1790 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1791 /* Close connection if not final */
1793 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1796 /* Auth failure, cancel if they didn't for some reason */
1798 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1802 case DUNDI_COMMAND_INVALID:
1803 case DUNDI_COMMAND_NULL:
1804 case DUNDI_COMMAND_PRECACHERP:
1805 /* Do nothing special */
1807 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1809 case DUNDI_COMMAND_ENCREJ:
1810 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
1811 /* No really, it's over at this point */
1813 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1815 /* Send with full key */
1816 ast_set_flag(trans, FLAG_SENDFULLKEY);
1818 /* Ooops, we got a final message, start by sending ACK... */
1819 dundi_ack(trans, hdr->cmdresp & 0x80);
1820 trans->aseqno = trans->iseqno;
1821 /* Now, we gotta create a new transaction */
1822 if (!reset_transaction(trans)) {
1823 /* Make sure handle_frame doesn't destroy us */
1824 hdr->cmdresp &= 0x7f;
1825 /* Parse the message we transmitted */
1826 memset(&ies, 0, sizeof(ies));
1827 dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
1828 /* Reconstruct outgoing encrypted packet */
1829 memset(&ied, 0, sizeof(ied));
1830 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1831 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1832 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1834 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1835 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
1836 peer->sentfullkey = 1;
1841 case DUNDI_COMMAND_ENCRYPT:
1843 /* No nested encryption! */
1844 if ((trans->iseqno == 1) && !trans->oseqno) {
1845 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1846 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1847 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1849 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1853 apply_peer(trans, peer);
1854 /* Key passed, use new contexts for this session */
1855 trans->ecx = peer->them_ecx;
1856 trans->dcx = peer->them_dcx;
1858 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1859 struct dundi_hdr *dhdr;
1860 unsigned char decoded[MAX_PACKET_SIZE];
1862 ddatalen = sizeof(decoded);
1863 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1865 /* Handle decrypted response */
1867 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1868 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1869 /* Carry back final flag */
1870 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1873 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1877 /* Turn off encryption */
1878 ast_clear_flag(trans, FLAG_ENCRYPT);
1879 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1883 /* Send unknown command if we don't know it, with final flag IFF it's the
1884 first command in the dialog and only if we haven't recieved final notification */
1886 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1887 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1893 static void destroy_packet(struct dundi_packet *pack, int needfree);
1894 static void destroy_packets(struct dundi_packet *p)
1896 struct dundi_packet *prev;
1900 if (prev->retransid > -1)
1901 ast_sched_del(sched, prev->retransid);
1907 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1909 /* Ack transmitted packet corresponding to iseqno */
1910 struct dundi_packet *pack;
1911 pack = trans->packets;
1913 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1914 destroy_packet(pack, 0);
1915 if (trans->lasttrans) {
1916 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1917 destroy_packets(trans->lasttrans);
1919 trans->lasttrans = pack;
1920 if (trans->autokillid > -1)
1921 ast_sched_del(sched, trans->autokillid);
1922 trans->autokillid = -1;
1930 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1932 struct dundi_transaction *trans;
1933 trans = find_transaction(h, sin);
1935 dundi_reject(h, sin);
1938 /* Got a transaction, see where this header fits in */
1939 if (h->oseqno == trans->iseqno) {
1940 /* Just what we were looking for... Anything but ack increments iseqno */
1941 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1942 /* If final, we're done */
1943 destroy_trans(trans, 0);
1946 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1947 trans->oiseqno = trans->iseqno;
1949 handle_command_response(trans, h, datalen, 0);
1951 if (trans->aseqno != trans->iseqno) {
1952 dundi_ack(trans, h->cmdresp & 0x80);
1953 trans->aseqno = trans->iseqno;
1955 /* Delete any saved last transmissions */
1956 destroy_packets(trans->lasttrans);
1957 trans->lasttrans = NULL;
1958 if (h->cmdresp & 0x80) {
1959 /* Final -- destroy now */
1960 destroy_trans(trans, 0);
1962 } else if (h->oseqno == trans->oiseqno) {
1963 /* Last incoming sequence number -- send ACK without processing */
1964 dundi_ack(trans, 0);
1966 /* Out of window -- simply drop */
1967 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1972 static int socket_read(int *id, int fd, short events, void *cbdata)
1974 struct sockaddr_in sin;
1976 struct dundi_hdr *h;
1977 char buf[MAX_PACKET_SIZE];
1980 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1982 if (errno != ECONNREFUSED)
1983 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1986 if (res < sizeof(struct dundi_hdr)) {
1987 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1991 h = (struct dundi_hdr *)buf;
1993 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1994 ast_mutex_lock(&peerlock);
1995 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
1996 ast_mutex_unlock(&peerlock);
2000 static void build_secret(char *secret, int seclen)
2002 unsigned char tmp[16];
2006 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2007 /* Eliminate potential bad characters */
2008 while((s = strchr(secret, ';'))) *s = '+';
2009 while((s = strchr(secret, '/'))) *s = '+';
2010 while((s = strchr(secret, ':'))) *s = '+';
2011 while((s = strchr(secret, '@'))) *s = '+';
2015 static void save_secret(const char *newkey, const char *oldkey)
2019 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2021 snprintf(tmp, sizeof(tmp), "%s", newkey);
2022 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2023 ast_db_put(secretpath, "secret", tmp);
2024 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2025 ast_db_put(secretpath, "secretexpiry", tmp);
2028 static void load_password(void)
2035 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2036 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2037 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2038 current = strchr(tmp, ';');
2045 if ((time(NULL) - expired) < 0) {
2046 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2047 expired = time(NULL) + DUNDI_SECRET_TIME;
2048 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2057 /* Current key is still valid, just setup rotatation properly */
2058 ast_copy_string(cursecret, current, sizeof(cursecret));
2059 rotatetime = expired;
2061 /* Current key is out of date, rotate or eliminate all together */
2062 build_secret(cursecret, sizeof(cursecret));
2063 save_secret(cursecret, last);
2067 static void check_password(void)
2074 printf("%ld/%ld\n", now, rotatetime);
2076 if ((now - rotatetime) >= 0) {
2077 /* Time to rotate keys */
2078 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2079 build_secret(cursecret, sizeof(cursecret));
2080 save_secret(cursecret, oldsecret);
2084 static void *network_thread(void *ignore)
2086 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2087 from the network, and queue them for delivery to the channels */
2089 /* Establish I/O callback for socket read */
2090 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2092 res = ast_sched_wait(sched);
2093 if ((res > 1000) || (res < 0))
2095 res = ast_io_wait(io, res);
2097 ast_mutex_lock(&peerlock);
2098 ast_sched_runq(sched);
2099 ast_mutex_unlock(&peerlock);
2106 static void *process_precache(void *ign)
2108 struct dundi_precache_queue *qe;
2116 ast_mutex_lock(&pclock);
2118 if (!pcq->expiration) {
2119 /* Gone... Remove... */
2123 } else if (pcq->expiration < now) {
2124 /* Process this entry */
2125 pcq->expiration = 0;
2126 ast_copy_string(context, pcq->context, sizeof(context));
2127 ast_copy_string(number, pcq->number, sizeof(number));
2131 ast_mutex_unlock(&pclock);
2133 dundi_precache(context, number);
2140 static int start_network_thread(void)
2142 ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
2143 ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
2147 static int dundi_do_debug(int fd, int argc, char *argv[])
2150 return RESULT_SHOWUSAGE;
2152 ast_cli(fd, "DUNDi Debugging Enabled\n");
2153 return RESULT_SUCCESS;
2156 static int dundi_do_store_history(int fd, int argc, char *argv[])
2159 return RESULT_SHOWUSAGE;
2160 global_storehistory = 1;
2161 ast_cli(fd, "DUNDi History Storage Enabled\n");
2162 return RESULT_SUCCESS;
2165 static int dundi_flush(int fd, int argc, char *argv[])
2168 if ((argc < 2) || (argc > 3))
2169 return RESULT_SHOWUSAGE;
2171 if (!strcasecmp(argv[2], "stats"))
2174 return RESULT_SHOWUSAGE;
2177 /* Flush statistics */
2178 struct dundi_peer *p;
2180 ast_mutex_lock(&peerlock);
2183 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2185 free(p->lookups[x]);
2186 p->lookups[x] = NULL;
2187 p->lookuptimes[x] = 0;
2192 ast_mutex_unlock(&peerlock);
2194 ast_db_deltree("dundi/cache", NULL);
2195 ast_cli(fd, "DUNDi Cache Flushed\n");
2197 return RESULT_SUCCESS;
2200 static int dundi_no_debug(int fd, int argc, char *argv[])
2203 return RESULT_SHOWUSAGE;
2205 ast_cli(fd, "DUNDi Debugging Disabled\n");
2206 return RESULT_SUCCESS;
2209 static int dundi_no_store_history(int fd, int argc, char *argv[])
2212 return RESULT_SHOWUSAGE;
2213 global_storehistory = 0;
2214 ast_cli(fd, "DUNDi History Storage Disabled\n");
2215 return RESULT_SUCCESS;
2218 static char *model2str(int model)
2221 case DUNDI_MODEL_INBOUND:
2223 case DUNDI_MODEL_OUTBOUND:
2225 case DUNDI_MODEL_SYMMETRIC:
2232 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2236 struct dundi_peer *p;
2241 ast_mutex_lock(&peerlock);
2243 for (p = peers; !ret && p; p = p->next) {
2244 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2245 if (!strncasecmp(word, s, len) && ++which > state)
2246 ret = ast_strdup(s);
2248 ast_mutex_unlock(&peerlock);
2252 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2254 return complete_peer_helper(line, word, pos, state, 3);
2257 static int rescomp(const void *a, const void *b)
2259 const struct dundi_result *resa, *resb;
2262 if (resa->weight < resb->weight)
2264 if (resa->weight > resb->weight)
2269 static void sort_results(struct dundi_result *results, int count)
2271 qsort(results, count, sizeof(results[0]), rescomp);
2274 static int dundi_do_lookup(int fd, int argc, char *argv[])
2282 struct dundi_result dr[MAX_RESULTS];
2283 struct timeval start;
2284 if ((argc < 3) || (argc > 4))
2285 return RESULT_SHOWUSAGE;
2287 if (!strcasecmp(argv[3], "bypass"))
2290 return RESULT_SHOWUSAGE;
2292 ast_copy_string(tmp, argv[2], sizeof(tmp));
2293 context = strchr(tmp, '@');
2298 start = ast_tvnow();
2299 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2302 ast_cli(fd, "DUNDi lookup returned error.\n");
2304 ast_cli(fd, "DUNDi lookup returned no results.\n");
2306 sort_results(dr, res);
2307 for (x=0;x<res;x++) {
2308 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));
2309 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2311 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2312 return RESULT_SUCCESS;
2315 static int dundi_do_precache(int fd, int argc, char *argv[])
2320 struct timeval start;
2321 if ((argc < 3) || (argc > 3))
2322 return RESULT_SHOWUSAGE;
2323 ast_copy_string(tmp, argv[2], sizeof(tmp));
2324 context = strchr(tmp, '@');
2329 start = ast_tvnow();
2330 res = dundi_precache(context, tmp);
2333 ast_cli(fd, "DUNDi precache returned error.\n");
2335 ast_cli(fd, "DUNDi precache returned no error.\n");
2336 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2337 return RESULT_SUCCESS;
2340 static int dundi_do_query(int fd, int argc, char *argv[])
2346 struct dundi_entity_info dei;
2347 if ((argc < 3) || (argc > 3))
2348 return RESULT_SHOWUSAGE;
2349 if (dundi_str_to_eid(&eid, argv[2])) {
2350 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2351 return RESULT_SHOWUSAGE;
2353 ast_copy_string(tmp, argv[2], sizeof(tmp));
2354 context = strchr(tmp, '@');
2359 res = dundi_query_eid(&dei, context, eid);
2361 ast_cli(fd, "DUNDi Query EID returned error.\n");
2363 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2365 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2366 ast_cli(fd, "Department: %s\n", dei.orgunit);
2367 ast_cli(fd, "Organization: %s\n", dei.org);
2368 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2369 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2370 ast_cli(fd, "Country: %s\n", dei.country);
2371 ast_cli(fd, "E-mail: %s\n", dei.email);
2372 ast_cli(fd, "Phone: %s\n", dei.phone);
2373 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2375 return RESULT_SUCCESS;
2378 static int dundi_show_peer(int fd, int argc, char *argv[])
2380 struct dundi_peer *peer;
2381 struct permission *p;
2383 char iabuf[INET_ADDRSTRLEN];
2388 return RESULT_SHOWUSAGE;
2389 ast_mutex_lock(&peerlock);
2392 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2397 switch(peer->order) {
2402 order = "Secondary";
2408 order = "Quartiary";
2413 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2414 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2415 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2416 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2417 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2418 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2419 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2420 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2421 if (peer->include) {
2422 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2426 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2430 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2434 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2438 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2439 if (peer->lookups[x]) {
2441 ast_cli(fd, "Last few query times:\n");
2442 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2447 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2449 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2450 ast_mutex_unlock(&peerlock);
2451 return RESULT_SUCCESS;
2454 static int dundi_show_peers(int fd, int argc, char *argv[])
2456 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2457 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2458 struct dundi_peer *peer;
2459 char iabuf[INET_ADDRSTRLEN];
2460 int registeredonly=0;
2463 int online_peers = 0;
2464 int offline_peers = 0;
2465 int unmonitored_peers = 0;
2466 int total_peers = 0;
2468 if ((argc != 3) && (argc != 4) && (argc != 5))
2469 return RESULT_SHOWUSAGE;
2471 if (!strcasecmp(argv[3], "registered")) {
2474 return RESULT_SHOWUSAGE;
2476 ast_mutex_lock(&peerlock);
2477 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2478 for (peer = peers;peer;peer = peer->next) {
2480 int print_line = -1;
2483 if (registeredonly && !peer->addr.sin_addr.s_addr)
2486 if (peer->lastms < 0) {
2487 strcpy(status, "UNREACHABLE");
2490 else if (peer->lastms > peer->maxms) {
2491 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2494 else if (peer->lastms) {
2495 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2499 strcpy(status, "UNKNOWN");
2503 strcpy(status, "Unmonitored");
2504 unmonitored_peers++;
2507 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2509 strcpy(avgms, "Unavail");
2510 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2511 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2512 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2515 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2517 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2519 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2527 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2528 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2529 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2532 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2533 ast_mutex_unlock(&peerlock);
2534 return RESULT_SUCCESS;
2539 static int dundi_show_trans(int fd, int argc, char *argv[])
2541 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2542 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2543 struct dundi_transaction *trans;
2544 char iabuf[INET_ADDRSTRLEN];
2546 return RESULT_SHOWUSAGE;
2547 ast_mutex_lock(&peerlock);
2548 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2549 for (trans = alltrans;trans;trans = trans->allnext) {
2550 ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
2551 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2553 ast_mutex_unlock(&peerlock);
2554 return RESULT_SUCCESS;
2559 static int dundi_show_entityid(int fd, int argc, char *argv[])
2563 return RESULT_SHOWUSAGE;
2564 ast_mutex_lock(&peerlock);
2565 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2566 ast_mutex_unlock(&peerlock);
2567 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2568 return RESULT_SUCCESS;
2571 static int dundi_show_requests(int fd, int argc, char *argv[])
2573 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2574 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2575 struct dundi_request *req;
2578 return RESULT_SHOWUSAGE;
2579 ast_mutex_lock(&peerlock);
2580 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2581 for (req = requests;req;req = req->next) {
2582 ast_cli(fd, FORMAT, req->number, req->dcontext,
2583 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2585 ast_mutex_unlock(&peerlock);
2586 return RESULT_SUCCESS;
2591 /* Grok-a-dial DUNDi */
2593 static int dundi_show_mappings(int fd, int argc, char *argv[])
2595 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2596 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2597 struct dundi_mapping *map;
2600 return RESULT_SHOWUSAGE;
2601 ast_mutex_lock(&peerlock);
2602 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2603 for (map = mappings;map;map = map->next) {
2604 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2605 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2606 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2608 ast_mutex_unlock(&peerlock);
2609 return RESULT_SUCCESS;
2614 static int dundi_show_precache(int fd, int argc, char *argv[])
2616 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2617 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2618 struct dundi_precache_queue *qe;
2623 return RESULT_SHOWUSAGE;
2625 ast_mutex_lock(&pclock);
2626 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2627 for (qe = pcq;qe;qe = qe->next) {
2628 s = qe->expiration - now;
2633 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2635 ast_mutex_unlock(&pclock);
2636 return RESULT_SUCCESS;
2641 static char debug_usage[] =
2642 "Usage: dundi debug\n"
2643 " Enables dumping of DUNDi packets for debugging purposes\n";
2645 static char no_debug_usage[] =
2646 "Usage: dundi no debug\n"
2647 " Disables dumping of DUNDi packets for debugging purposes\n";
2649 static char store_history_usage[] =
2650 "Usage: dundi store history\n"
2651 " Enables storing of DUNDi requests and times for debugging\n"
2654 static char no_store_history_usage[] =
2655 "Usage: dundi no store history\n"
2656 " Disables storing of DUNDi requests and times for debugging\n"
2659 static char show_peers_usage[] =
2660 "Usage: dundi show peers\n"
2661 " Lists all known DUNDi peers.\n";
2663 static char show_trans_usage[] =
2664 "Usage: dundi show trans\n"
2665 " Lists all known DUNDi transactions.\n";
2667 static char show_mappings_usage[] =
2668 "Usage: dundi show mappings\n"
2669 " Lists all known DUNDi mappings.\n";
2671 static char show_precache_usage[] =
2672 "Usage: dundi show precache\n"
2673 " Lists all known DUNDi scheduled precache updates.\n";
2675 static char show_entityid_usage[] =
2676 "Usage: dundi show entityid\n"
2677 " Displays the global entityid for this host.\n";
2679 static char show_peer_usage[] =
2680 "Usage: dundi show peer [peer]\n"
2681 " Provide a detailed description of a specifid DUNDi peer.\n";
2683 static char show_requests_usage[] =
2684 "Usage: dundi show requests\n"
2685 " Lists all known pending DUNDi requests.\n";
2687 static char lookup_usage[] =
2688 "Usage: dundi lookup <number>[@context] [bypass]\n"
2689 " Lookup the given number within the given DUNDi context\n"
2690 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2691 "keyword is specified.\n";
2693 static char precache_usage[] =
2694 "Usage: dundi precache <number>[@context]\n"
2695 " Lookup the given number within the given DUNDi context\n"
2696 "(or e164 if none is specified) and precaches the results to any\n"
2697 "upstream DUNDi push servers.\n";
2699 static char query_usage[] =
2700 "Usage: dundi query <entity>[@context]\n"
2701 " Attempts to retrieve contact information for a specific\n"
2702 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2703 "e164 if none is specified).\n";
2705 static char flush_usage[] =
2706 "Usage: dundi flush [stats]\n"
2707 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2708 "'stats' is present, clears timer statistics instead of normal\n"
2711 static struct ast_cli_entry cli_debug =
2712 { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2714 static struct ast_cli_entry cli_store_history =
2715 { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2717 static struct ast_cli_entry cli_no_store_history =
2718 { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2720 static struct ast_cli_entry cli_flush =
2721 { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2723 static struct ast_cli_entry cli_no_debug =
2724 { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2726 static struct ast_cli_entry cli_show_peers =
2727 { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2729 static struct ast_cli_entry cli_show_trans =
2730 { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2732 static struct ast_cli_entry cli_show_entityid =
2733 { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2735 static struct ast_cli_entry cli_show_mappings =
2736 { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2738 static struct ast_cli_entry cli_show_precache =
2739 { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2741 static struct ast_cli_entry cli_show_requests =
2742 { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2744 static struct ast_cli_entry cli_show_peer =
2745 { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2747 static struct ast_cli_entry cli_lookup =
2748 { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2750 static struct ast_cli_entry cli_precache =
2751 { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2753 static struct ast_cli_entry cli_queryeid =
2754 { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2758 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2760 struct dundi_transaction *trans;
2763 /* Don't allow creation of transactions to non-registered peers */
2764 if (p && !p->addr.sin_addr.s_addr)
2766 tid = get_trans_id();
2769 trans = malloc(sizeof(struct dundi_transaction));
2771 memset(trans, 0, sizeof(struct dundi_transaction));
2772 if (global_storehistory) {
2773 trans->start = ast_tvnow();
2774 ast_set_flag(trans, FLAG_STOREHIST);
2776 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2777 trans->autokillid = -1;
2779 apply_peer(trans, p);
2780 if (!p->sentfullkey)
2781 ast_set_flag(trans, FLAG_SENDFULLKEY);
2783 trans->strans = tid;
2784 trans->allnext = alltrans;
2790 static int dundi_xmit(struct dundi_packet *pack)
2793 char iabuf[INET_ADDRSTRLEN];
2795 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2796 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2798 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2799 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2800 ntohs(pack->parent->addr.sin_port), strerror(errno));
2807 static void destroy_packet(struct dundi_packet *pack, int needfree)
2809 struct dundi_packet *prev, *cur;
2812 cur = pack->parent->packets;
2816 prev->next = cur->next;
2818 pack->parent->packets = cur->next;
2825 if (pack->retransid > -1)
2826 ast_sched_del(sched, pack->retransid);
2830 pack->retransid = -1;
2835 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2837 struct dundi_transaction *cur, *prev;
2838 struct dundi_peer *peer;
2843 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2846 if (peer->regtrans == trans)
2847 peer->regtrans = NULL;
2848 if (peer->keypending == trans)
2849 peer->keypending = NULL;
2850 if (peer->qualtrans == trans) {
2852 if (peer->lastms > -1)
2853 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2856 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2859 if (ms < peer->maxms) {
2860 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2861 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2862 } else if (peer->lastms < peer->maxms) {
2863 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);
2867 peer->qualtrans = NULL;
2869 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2870 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2871 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2874 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2875 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2876 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2877 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2878 peer->lookups[x] = peer->lookups[x-1];
2879 if (peer->lookups[x]) {
2880 peer->avgms += peer->lookuptimes[x];
2884 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2885 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2886 if (peer->lookups[0]) {
2887 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2888 peer->avgms += peer->lookuptimes[0];
2899 if (trans->parent) {
2900 /* Unlink from parent if appropriate */
2902 cur = trans->parent->trans;
2906 prev->next = trans->next;
2908 trans->parent->trans = trans->next;
2914 if (!trans->parent->trans) {
2915 /* Wake up sleeper */
2916 if (trans->parent->pfds[1] > -1) {
2917 write(trans->parent->pfds[1], "killa!", 6);