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)
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <sys/socket.h>
33 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
34 #include <sys/types.h>
35 #include <netinet/in_systm.h>
37 #include <netinet/ip.h>
38 #include <sys/ioctl.h>
39 #include <netinet/in.h>
41 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
42 #include <net/if_dl.h>
49 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
51 #include "asterisk/file.h"
52 #include "asterisk/logger.h"
53 #include "asterisk/channel.h"
54 #include "asterisk/config.h"
55 #include "asterisk/options.h"
56 #include "asterisk/pbx.h"
57 #include "asterisk/module.h"
58 #include "asterisk/frame.h"
59 #include "asterisk/file.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/md5.h"
63 #include "asterisk/dundi.h"
64 #include "asterisk/sched.h"
65 #include "asterisk/io.h"
66 #include "asterisk/utils.h"
67 #include "asterisk/crypto.h"
68 #include "asterisk/astdb.h"
69 #include "asterisk/acl.h"
70 #include "asterisk/aes.h"
72 #include "dundi-parser.h"
74 #define MAX_RESULTS 64
76 #define MAX_PACKET_SIZE 8192
78 extern char ast_config_AST_KEY_DIR[];
80 static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
82 #define DUNDI_MODEL_INBOUND (1 << 0)
83 #define DUNDI_MODEL_OUTBOUND (1 << 1)
84 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
86 /* Keep times of last 10 lookups */
87 #define DUNDI_TIMING_HISTORY 10
89 #define FLAG_ISREG (1 << 0) /* Transaction is register request */
90 #define FLAG_DEAD (1 << 1) /* Transaction is dead */
91 #define FLAG_FINAL (1 << 2) /* Transaction has final message sent */
92 #define FLAG_ISQUAL (1 << 3) /* Transaction is a qualification */
93 #define FLAG_ENCRYPT (1 << 4) /* Transaction is encrypted wiht ECX/DCX */
94 #define FLAG_SENDFULLKEY (1 << 5) /* Send full key on transaction */
95 #define FLAG_STOREHIST (1 << 6) /* Record historic performance */
97 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
100 #define DUNDI_SECRET_TIME 15 /* Testing only */
102 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
108 static struct io_context *io;
109 static struct sched_context *sched;
110 static int netsocket = -1;
111 static pthread_t netthreadid = AST_PTHREADT_NULL;
112 static pthread_t precachethreadid = AST_PTHREADT_NULL;
114 static int dundidebug = 0;
115 static int authdebug = 0;
116 static int dundi_ttl = DUNDI_DEFAULT_TTL;
117 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
118 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
119 static int global_autokilltimeout = 0;
120 static dundi_eid global_eid;
121 static int default_expiration = 60;
122 static int global_storehistory = 0;
123 static char dept[80];
125 static char locality[80];
126 static char stateprov[80];
127 static char country[80];
128 static char email[80];
129 static char phone[80];
130 static char secretpath[80];
131 static char cursecret[80];
132 static char ipaddr[80];
133 static time_t rotatetime;
134 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
136 struct permission *next;
141 struct dundi_packet {
143 struct dundi_packet *next;
145 struct dundi_transaction *parent;
148 unsigned char data[0];
151 struct dundi_hint_metadata {
152 unsigned short flags;
153 char exten[AST_MAX_EXTENSION];
156 struct dundi_precache_queue {
157 struct dundi_precache_queue *next;
163 struct dundi_request;
165 struct dundi_transaction {
166 struct sockaddr_in addr; /* Other end of transaction */
167 struct timeval start; /* When this transaction was created */
168 dundi_eid eids[DUNDI_MAX_STACK + 1];
169 int eidcount; /* Number of eids in eids */
170 dundi_eid us_eid; /* Our EID, to them */
171 dundi_eid them_eid; /* Their EID, to us */
172 aes_encrypt_ctx ecx; /* AES 128 Encryption context */
173 aes_decrypt_ctx dcx; /* AES 128 Decryption context */
174 unsigned int flags; /* Has final packet been sent */
175 int ttl; /* Remaining TTL for queries on this one */
176 int thread; /* We have a calling thread */
177 int retranstimer; /* How long to wait before retransmissions */
178 int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
179 int autokilltimeout; /* Recommended timeout for autokill */
180 unsigned short strans; /* Our transaction identifier */
181 unsigned short dtrans; /* Their transaction identifer */
182 unsigned char iseqno; /* Next expected received seqno */
183 unsigned char oiseqno; /* Last received incoming seqno */
184 unsigned char oseqno; /* Next transmitted seqno */
185 unsigned char aseqno; /* Last acknowledge seqno */
186 struct dundi_packet *packets; /* Packets to be retransmitted */
187 struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
188 struct dundi_transaction *next; /* Next with respect to the parent */
189 struct dundi_request *parent; /* Parent request (if there is one) */
190 struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
193 struct dundi_request {
194 char dcontext[AST_MAX_EXTENSION];
195 char number[AST_MAX_EXTENSION];
198 struct dundi_result *dr;
199 struct dundi_entity_info *dei;
200 struct dundi_hint_metadata *hmd;
206 unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
207 struct dundi_transaction *trans; /* Transactions */
208 struct dundi_request *next;
211 static struct dundi_mapping {
212 char dcontext[AST_MAX_EXTENSION];
213 char lcontext[AST_MAX_EXTENSION];
218 char dest[AST_MAX_EXTENSION];
219 struct dundi_mapping *next;
222 static struct dundi_peer {
224 struct sockaddr_in addr; /* Address of DUNDi peer */
225 struct permission *permit;
226 struct permission *include;
227 struct permission *precachesend;
228 struct permission *precachereceive;
237 unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
238 unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
239 unsigned long us_keycrc32; /* CRC-32 of our key */
240 aes_encrypt_ctx us_ecx; /* Cached AES 128 Encryption context */
241 aes_decrypt_ctx us_dcx; /* Cached AES 128 Decryption context */
242 unsigned long them_keycrc32;/* CRC-32 of our key */
243 aes_encrypt_ctx them_ecx; /* Cached AES 128 Encryption context */
244 aes_decrypt_ctx them_dcx; /* Cached AES 128 Decryption context */
245 time_t keyexpire; /* When to expire/recreate key */
247 int lookuptimes[DUNDI_TIMING_HISTORY];
248 char *lookups[DUNDI_TIMING_HISTORY];
250 struct dundi_transaction *regtrans; /* Registration transaction */
251 struct dundi_transaction *qualtrans; /* Qualify transaction */
252 struct dundi_transaction *keypending;
253 int model; /* Pull model */
254 int pcmodel; /* Push/precache model */
255 int dynamic; /* Are we dynamic? */
256 int lastms; /* Last measured latency */
257 int maxms; /* Max permissible latency */
258 struct timeval qualtx; /* Time of transmit */
259 struct dundi_peer *next;
262 static struct dundi_precache_queue *pcq;
264 AST_MUTEX_DEFINE_STATIC(peerlock);
265 AST_MUTEX_DEFINE_STATIC(pclock);
267 static int dundi_xmit(struct dundi_packet *pack);
269 static void dundi_debug_output(const char *data)
272 ast_verbose("%s", data);
275 static void dundi_error_output(const char *data)
277 ast_log(LOG_WARNING, "%s", data);
280 static int has_permission(struct permission *ps, char *cont)
284 if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
291 static char *tech2str(int tech)
294 case DUNDI_PROTO_NONE:
296 case DUNDI_PROTO_IAX:
298 case DUNDI_PROTO_SIP:
300 case DUNDI_PROTO_H323:
307 static int str2tech(char *str)
309 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
310 return DUNDI_PROTO_IAX;
311 else if (!strcasecmp(str, "SIP"))
312 return DUNDI_PROTO_SIP;
313 else if (!strcasecmp(str, "H323"))
314 return DUNDI_PROTO_H323;
319 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[]);
320 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
321 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
322 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
324 /* Look for an exact match first */
325 struct dundi_transaction *trans;
328 if (!inaddrcmp(&trans->addr, sin) &&
329 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
330 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
332 trans->dtrans = ntohs(hdr->strans) & 32767;
335 trans = trans->allnext;
338 switch(hdr->cmdresp & 0x7f) {
339 case DUNDI_COMMAND_DPDISCOVER:
340 case DUNDI_COMMAND_EIDQUERY:
341 case DUNDI_COMMAND_PRECACHERQ:
342 case DUNDI_COMMAND_REGREQ:
343 case DUNDI_COMMAND_NULL:
344 case DUNDI_COMMAND_ENCRYPT:
346 /* Create new transaction */
347 trans = create_transaction(NULL);
349 memcpy(&trans->addr, sin, sizeof(trans->addr));
350 trans->dtrans = ntohs(hdr->strans) & 32767;
352 ast_log(LOG_WARNING, "Out of memory!\n");
362 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
364 static int dundi_ack(struct dundi_transaction *trans, int final)
366 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
368 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
371 struct dundi_packet pack;
372 struct dundi_hdr hdr;
374 struct dundi_transaction trans;
375 /* Never respond to an INVALID with another INVALID */
376 if (h->cmdresp == DUNDI_COMMAND_INVALID)
378 memset(&tmp, 0, sizeof(tmp));
379 memset(&trans, 0, sizeof(trans));
380 memcpy(&trans.addr, sin, sizeof(trans.addr));
381 tmp.hdr.strans = h->dtrans;
382 tmp.hdr.dtrans = h->strans;
383 tmp.hdr.iseqno = h->oseqno;
384 tmp.hdr.oseqno = h->iseqno;
385 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
386 tmp.hdr.cmdflags = 0;
387 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
388 tmp.pack.datalen = sizeof(struct dundi_hdr);
389 tmp.pack.parent = &trans;
390 dundi_xmit(&tmp.pack);
393 static void reset_global_eid(void)
395 #if defined(SIOCGIFHWADDR)
400 s = socket(AF_INET, SOCK_STREAM, 0);
404 memset(&ifr, 0, sizeof(ifr));
405 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
406 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
407 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
408 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
416 #if defined(ifa_broadaddr) && !defined(SOLARIS)
418 struct ifaddrs *ifap;
420 if (getifaddrs(&ifap) == 0) {
422 for (p = ifap; p; p = p->ifa_next) {
423 if (p->ifa_addr->sa_family == AF_LINK) {
424 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
427 sdp->sdl_data + sdp->sdl_nlen, 6);
428 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifap->ifa_name);
437 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
440 static int get_trans_id(void)
442 struct dundi_transaction *t;
443 int stid = (rand() % 32766) + 1;
448 if (t->strans == tid)
454 tid = (tid % 32766) + 1;
455 } while (tid != stid);
459 static int reset_transaction(struct dundi_transaction *trans)
462 tid = get_trans_id();
471 ast_clear_flag(trans, FLAG_FINAL);
475 static struct dundi_peer *find_peer(dundi_eid *eid)
477 struct dundi_peer *cur;
482 if (!dundi_eid_cmp(&cur->eid,eid))
489 static void build_iv(unsigned char *iv)
491 /* XXX Would be nice to be more random XXX */
492 unsigned int *fluffy;
494 fluffy = (unsigned int *)(iv);
499 struct dundi_query_state {
500 dundi_eid *eids[DUNDI_MAX_STACK + 1];
501 int directs[DUNDI_MAX_STACK + 1];
503 char called_context[AST_MAX_EXTENSION];
504 char called_number[AST_MAX_EXTENSION];
505 struct dundi_mapping *maps;
508 struct dundi_transaction *trans;
515 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)
517 struct ast_flags flags = {0};
519 if (!ast_strlen_zero(map->lcontext)) {
520 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
521 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
522 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
523 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
524 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
525 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
526 if (ast_ignore_pattern(map->lcontext, called_number))
527 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
529 /* Clearly we can't say 'don't ask' anymore if we found anything... */
530 if (ast_test_flag(&flags, AST_FLAGS_ALL))
531 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
533 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
534 /* Skip partial answers */
535 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
537 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
538 struct varshead headp;
539 struct ast_var_t *newvariable;
540 ast_set_flag(&flags, map->options & 0xffff);
541 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
542 dr[anscnt].techint = map->tech;
543 dr[anscnt].weight = map->weight;
544 dr[anscnt].expiration = dundi_cache_time;
545 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
546 dr[anscnt].eid = *us_eid;
547 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
548 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
549 AST_LIST_HEAD_INIT_NOLOCK(&headp);
550 newvariable = ast_var_assign("NUMBER", called_number);
551 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
552 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
553 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
554 newvariable = ast_var_assign("SECRET", cursecret);
555 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
556 newvariable = ast_var_assign("IPADDR", ipaddr);
557 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
558 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
559 while (!AST_LIST_EMPTY(&headp)) { /* List Deletion. */
560 newvariable = AST_LIST_REMOVE_HEAD(&headp, entries);
561 ast_var_delete(newvariable);
564 dr[anscnt].dest[0] = '\0';
567 /* No answers... Find the fewest number of digits from the
568 number for which we have no answer. */
569 char tmp[AST_MAX_EXTENSION];
570 for (x=0;x<AST_MAX_EXTENSION;x++) {
571 tmp[x] = called_number[x];
574 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
575 /* Oops found something we can't match. If this is longer
576 than the running hint, we have to consider it */
577 if (strlen(tmp) > strlen(hmd->exten)) {
578 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
588 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
590 static void *dundi_lookup_thread(void *data)
592 struct dundi_query_state *st = data;
593 struct dundi_result dr[MAX_RESULTS];
594 struct dundi_ie_data ied;
595 struct dundi_hint_metadata hmd;
600 int expiration = dundi_cache_time;
602 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
603 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
604 memset(&ied, 0, sizeof(ied));
605 memset(&dr, 0, sizeof(dr));
606 memset(&hmd, 0, sizeof(hmd));
607 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
608 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
609 for (x=0;x<st->nummaps;x++)
610 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
613 for (x=0;x<ouranswers;x++) {
614 if (dr[x].weight < max)
619 /* If we do not have a canonical result, keep looking */
620 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);
622 /* Append answer in result */
625 if ((res < -1) && (!ouranswers))
626 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
629 ast_mutex_lock(&peerlock);
630 /* Truncate if "don't ask" isn't present */
631 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
633 if (ast_test_flag(st->trans, FLAG_DEAD)) {
634 ast_log(LOG_DEBUG, "Our transaction went away!\n");
635 st->trans->thread = 0;
636 destroy_trans(st->trans, 0);
638 for (x=0;x<ouranswers;x++) {
640 if (dr[x].expiration && (expiration > dr[x].expiration))
641 expiration = dr[x].expiration;
642 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
644 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
645 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
646 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
647 st->trans->thread = 0;
649 ast_mutex_unlock(&peerlock);
654 static void *dundi_precache_thread(void *data)
656 struct dundi_query_state *st = data;
657 struct dundi_ie_data ied;
658 struct dundi_hint_metadata hmd;
661 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
662 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
663 memset(&ied, 0, sizeof(ied));
665 /* Now produce precache */
666 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
668 ast_mutex_lock(&peerlock);
669 /* Truncate if "don't ask" isn't present */
670 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
672 if (ast_test_flag(st->trans, FLAG_DEAD)) {
673 ast_log(LOG_DEBUG, "Our transaction went away!\n");
674 st->trans->thread = 0;
675 destroy_trans(st->trans, 0);
677 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
678 st->trans->thread = 0;
680 ast_mutex_unlock(&peerlock);
685 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[]);
687 static void *dundi_query_thread(void *data)
689 struct dundi_query_state *st = data;
690 struct dundi_entity_info dei;
691 struct dundi_ie_data ied;
692 struct dundi_hint_metadata hmd;
695 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
696 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
697 memset(&ied, 0, sizeof(ied));
698 memset(&dei, 0, sizeof(dei));
699 memset(&hmd, 0, sizeof(hmd));
700 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
702 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
703 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
704 ast_copy_string(dei.org, org, sizeof(dei.org));
705 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
706 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
707 ast_copy_string(dei.country, country, sizeof(dei.country));
708 ast_copy_string(dei.email, email, sizeof(dei.email));
709 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
712 /* If we do not have a canonical result, keep looking */
713 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
715 ast_mutex_lock(&peerlock);
716 if (ast_test_flag(st->trans, FLAG_DEAD)) {
717 ast_log(LOG_DEBUG, "Our transaction went away!\n");
718 st->trans->thread = 0;
719 destroy_trans(st->trans, 0);
722 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
723 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
724 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
725 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
726 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
727 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
728 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
729 if (!ast_strlen_zero(dei.ipaddr))
730 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
732 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
733 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
734 st->trans->thread = 0;
736 ast_mutex_unlock(&peerlock);
741 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
743 struct dundi_query_state *st;
747 struct dundi_ie_data ied;
750 pthread_t lookupthread;
752 if (ies->eidcount > 1) {
753 /* Since it is a requirement that the first EID is the authenticating host
754 and the last EID is the root, it is permissible that the first and last EID
755 could be the same. In that case, we should go ahead copy only the "root" section
756 since we will not need it for authentication. */
757 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
760 totallen = sizeof(struct dundi_query_state);
761 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
762 st = malloc(totallen);
764 memset(st, 0, totallen);
765 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
766 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
768 st->ttl = ies->ttl - 1;
772 for (x=skipfirst;ies->eids[x];x++) {
773 st->eids[x-skipfirst] = (dundi_eid *)s;
774 *st->eids[x-skipfirst] = *ies->eids[x];
775 s += sizeof(dundi_eid);
777 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);
778 pthread_attr_init(&attr);
779 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
781 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
783 ast_log(LOG_WARNING, "Unable to create thread!\n");
785 memset(&ied, 0, sizeof(ied));
786 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
787 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
791 ast_log(LOG_WARNING, "Out of memory!\n");
792 memset(&ied, 0, sizeof(ied));
793 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
794 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
800 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
805 char eidpeer_str[20];
806 char eidroot_str[20];
811 expiration = dundi_cache_time;
813 /* Only cache hint if "don't ask" is there... */
814 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
817 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
819 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
820 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
821 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
822 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
825 timeout += expiration;
826 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
828 ast_db_put("dundi/cache", key1, data);
829 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
830 ast_db_put("dundi/cache", key2, data);
831 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
835 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
841 char eidpeer_str[20];
842 char eidroot_str[20];
846 expiration = dundi_cache_time;
848 /* Keep pushes a little longer, cut pulls a little short */
855 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
856 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
857 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
858 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
859 /* Build request string */
861 timeout += expiration;
862 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
863 for (x=start;x<req->respcount;x++) {
864 /* Skip anything with an illegal pipe in it */
865 if (strchr(req->dr[x].dest, '|'))
867 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
868 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
869 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
871 ast_db_put("dundi/cache", key1, data);
872 ast_db_put("dundi/cache", key2, data);
876 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
878 struct dundi_query_state *st;
881 struct dundi_ie_data ied;
883 struct dundi_result dr2[MAX_RESULTS];
884 struct dundi_request dr;
885 struct dundi_hint_metadata hmd;
887 struct dundi_mapping *cur;
891 pthread_t lookupthread;
894 memset(&dr2, 0, sizeof(dr2));
895 memset(&dr, 0, sizeof(dr));
896 memset(&hmd, 0, sizeof(hmd));
898 /* Forge request structure to hold answers for cache */
899 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
901 dr.maxcount = MAX_RESULTS;
902 dr.expiration = dundi_cache_time;
904 dr.pfds[0] = dr.pfds[1] = -1;
906 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
907 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
909 for (x=0;x<ies->anscount;x++) {
910 if (trans->parent->respcount < trans->parent->maxcount) {
911 /* Make sure it's not already there */
912 for (z=0;z<trans->parent->respcount;z++) {
913 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
914 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
917 if (z == trans->parent->respcount) {
918 /* Copy into parent responses */
919 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
920 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
921 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
922 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
923 if (ies->expiration > 0)
924 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
926 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
927 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
928 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
929 &ies->answers[x]->eid);
930 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
931 sizeof(trans->parent->dr[trans->parent->respcount].dest));
932 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
933 sizeof(trans->parent->dr[trans->parent->respcount].tech));
934 trans->parent->respcount++;
935 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
936 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
937 /* Update weight if appropriate */
938 trans->parent->dr[z].weight = ies->answers[x]->weight;
941 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
942 trans->parent->number, trans->parent->dcontext);
945 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
946 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
948 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
950 totallen = sizeof(struct dundi_query_state);
951 /* Count matching map entries */
955 if (!strcasecmp(cur->dcontext, ccontext))
960 /* If no maps, return -1 immediately */
964 if (ies->eidcount > 1) {
965 /* Since it is a requirement that the first EID is the authenticating host
966 and the last EID is the root, it is permissible that the first and last EID
967 could be the same. In that case, we should go ahead copy only the "root" section
968 since we will not need it for authentication. */
969 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
973 /* Prepare to run a query and then propagate that as necessary */
974 totallen += mapcount * sizeof(struct dundi_mapping);
975 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
976 st = malloc(totallen);
978 memset(st, 0, totallen);
979 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
980 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
982 st->ttl = ies->ttl - 1;
983 st->nocache = ies->cbypass;
987 for (x=skipfirst;ies->eids[x];x++) {
988 st->eids[x-skipfirst] = (dundi_eid *)s;
989 *st->eids[x-skipfirst] = *ies->eids[x];
990 st->directs[x-skipfirst] = ies->eid_direct[x];
991 s += sizeof(dundi_eid);
993 /* Append mappings */
995 st->maps = (struct dundi_mapping *)s;
998 if (!strcasecmp(cur->dcontext, ccontext)) {
1001 st->maps[x].next = NULL;
1007 st->nummaps = mapcount;
1008 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1009 pthread_attr_init(&attr);
1010 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1012 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1014 ast_log(LOG_WARNING, "Unable to create thread!\n");
1016 memset(&ied, 0, sizeof(ied));
1017 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1018 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1022 ast_log(LOG_WARNING, "Out of memory!\n");
1023 memset(&ied, 0, sizeof(ied));
1024 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1025 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1031 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1033 struct dundi_query_state *st;
1036 struct dundi_ie_data ied;
1038 struct dundi_mapping *cur;
1042 pthread_t lookupthread;
1043 pthread_attr_t attr;
1044 totallen = sizeof(struct dundi_query_state);
1045 /* Count matching map entries */
1049 if (!strcasecmp(cur->dcontext, ccontext))
1053 /* If no maps, return -1 immediately */
1057 if (ies->eidcount > 1) {
1058 /* Since it is a requirement that the first EID is the authenticating host
1059 and the last EID is the root, it is permissible that the first and last EID
1060 could be the same. In that case, we should go ahead copy only the "root" section
1061 since we will not need it for authentication. */
1062 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1066 totallen += mapcount * sizeof(struct dundi_mapping);
1067 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1068 st = malloc(totallen);
1070 memset(st, 0, totallen);
1071 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1072 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1074 st->ttl = ies->ttl - 1;
1075 st->nocache = ies->cbypass;
1079 for (x=skipfirst;ies->eids[x];x++) {
1080 st->eids[x-skipfirst] = (dundi_eid *)s;
1081 *st->eids[x-skipfirst] = *ies->eids[x];
1082 st->directs[x-skipfirst] = ies->eid_direct[x];
1083 s += sizeof(dundi_eid);
1085 /* Append mappings */
1087 st->maps = (struct dundi_mapping *)s;
1090 if (!strcasecmp(cur->dcontext, ccontext)) {
1093 st->maps[x].next = NULL;
1099 st->nummaps = mapcount;
1100 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1101 pthread_attr_init(&attr);
1102 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1104 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1106 ast_log(LOG_WARNING, "Unable to create thread!\n");
1108 memset(&ied, 0, sizeof(ied));
1109 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1110 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1114 ast_log(LOG_WARNING, "Out of memory!\n");
1115 memset(&ied, 0, sizeof(ied));
1116 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1117 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1123 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1126 char *ptr, *term, *src;
1128 struct ast_flags flags;
1135 /* Build request string */
1136 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1138 if (sscanf(ptr, "%d|%n", (int *)&timeout, &length) == 1) {
1139 expiration = timeout - now;
1140 if (expiration > 0) {
1141 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
1143 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1145 term = strchr(ptr, '|');
1148 src = strrchr(ptr, '/');
1154 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1155 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1156 /* Make sure it's not already there */
1157 for (z=0;z<req->respcount;z++) {
1158 if ((req->dr[z].techint == tech) &&
1159 !strcmp(req->dr[z].dest, ptr))
1162 if (z == req->respcount) {
1163 /* Copy into parent responses */
1164 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1165 req->dr[req->respcount].weight = weight;
1166 req->dr[req->respcount].techint = tech;
1167 req->dr[req->respcount].expiration = expiration;
1168 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1169 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1170 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1171 ast_copy_string(req->dr[req->respcount].dest, ptr,
1172 sizeof(req->dr[req->respcount].dest));
1173 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1174 sizeof(req->dr[req->respcount].tech));
1176 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1177 } else if (req->dr[z].weight > weight)
1178 req->dr[z].weight = weight;
1182 /* We found *something* cached */
1183 if (expiration < *lowexpiration)
1184 *lowexpiration = expiration;
1187 ast_db_del("dundi/cache", key);
1189 ast_db_del("dundi/cache", key);
1195 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1199 char eidroot_str[20];
1203 char eid_str_full[20];
1208 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1209 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1210 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1211 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1212 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1213 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1214 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1215 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1216 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1218 if (!req->respcount) {
1220 /* Look and see if we have a hint that would preclude us from looking at this
1221 peer for this number. */
1222 if (!(tmp[x] = req->number[x]))
1225 /* Check for hints */
1226 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1227 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1228 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1229 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1230 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1231 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1233 if (strlen(tmp) > strlen(req->hmd->exten)) {
1234 /* Update meta data if appropriate */
1235 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1245 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1247 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1249 if (!trans->addr.sin_addr.s_addr)
1250 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1251 trans->us_eid = p->us_eid;
1252 trans->them_eid = p->eid;
1253 /* Enable encryption if appropriate */
1254 if (!ast_strlen_zero(p->inkey))
1255 ast_set_flag(trans, FLAG_ENCRYPT);
1257 trans->autokilltimeout = p->maxms;
1258 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1259 if (p->lastms > 1) {
1260 trans->retranstimer = p->lastms * 2;
1261 /* Keep it from being silly */
1262 if (trans->retranstimer < 150)
1263 trans->retranstimer = 150;
1265 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1266 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1268 trans->autokilltimeout = global_autokilltimeout;
1271 static int do_register_expire(void *data)
1273 struct dundi_peer *peer = data;
1275 /* Called with peerlock already held */
1276 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1277 peer->registerexpire = -1;
1279 memset(&peer->addr, 0, sizeof(peer->addr));
1283 static int update_key(struct dundi_peer *peer)
1285 unsigned char key[16];
1286 struct ast_key *ekey, *skey;
1289 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1291 aes_encrypt_key128(key, &peer->us_ecx);
1292 aes_decrypt_key128(key, &peer->us_dcx);
1293 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1295 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1296 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1299 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1301 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1302 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1305 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1306 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1309 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1310 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1313 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1314 peer->sentfullkey = 0;
1316 time(&peer->keyexpire);
1317 peer->keyexpire += dundi_key_ttl;
1322 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1324 unsigned char curblock[16];
1326 memcpy(curblock, iv, sizeof(curblock));
1329 curblock[x] ^= src[x];
1330 aes_encrypt(curblock, dst, ecx);
1331 memcpy(curblock, dst, sizeof(curblock));
1338 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1340 unsigned char lastblock[16];
1342 memcpy(lastblock, iv, sizeof(lastblock));
1344 aes_decrypt(src, dst, dcx);
1346 dst[x] ^= lastblock[x];
1347 memcpy(lastblock, src, sizeof(lastblock));
1355 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)
1357 int space = *dstlen;
1358 unsigned long bytes;
1359 struct dundi_hdr *h;
1360 unsigned char *decrypt_space;
1361 decrypt_space = alloca(srclen);
1364 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1366 h = (struct dundi_hdr *)dst;
1369 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1370 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1374 *dstlen = bytes + 6;
1375 /* Return new header */
1379 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1381 unsigned char *compress_space;
1384 unsigned long bytes;
1385 struct dundi_ie_data ied;
1386 struct dundi_peer *peer;
1387 unsigned char iv[16];
1388 len = pack->datalen + pack->datalen / 100 + 42;
1389 compress_space = alloca(len);
1390 if (compress_space) {
1391 memset(compress_space, 0, len);
1392 /* We care about everthing save the first 6 bytes of header */
1394 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1396 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1399 memset(&ied, 0, sizeof(ied));
1400 /* Say who we are */
1401 if (!pack->h->iseqno && !pack->h->oseqno) {
1402 /* Need the key in the first copy */
1403 if (!(peer = find_peer(&trans->them_eid)))
1405 if (update_key(peer))
1407 if (!peer->sentfullkey)
1408 ast_set_flag(trans, FLAG_SENDFULLKEY);
1409 /* Append key data */
1410 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1411 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1412 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1413 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1415 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1417 /* Setup contexts */
1418 trans->ecx = peer->us_ecx;
1419 trans->dcx = peer->us_dcx;
1421 /* We've sent the full key */
1422 peer->sentfullkey = 1;
1424 /* Build initialization vector */
1426 /* Add the field, rounded up to 16 bytes */
1427 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1429 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1430 ast_log(LOG_NOTICE, "Final packet too large!\n");
1433 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1434 ied.pos += ((bytes + 15) / 16) * 16;
1435 /* Reconstruct header */
1436 pack->datalen = sizeof(struct dundi_hdr);
1437 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1438 pack->h->cmdflags = 0;
1439 memcpy(pack->h->ies, ied.buf, ied.pos);
1440 pack->datalen += ied.pos;
1446 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1448 unsigned char dst[128];
1450 struct ast_key *key, *skey;
1453 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1454 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1457 } else if (!newkey || !newsig)
1459 if (!memcmp(peer->rxenckey, newkey, 128) &&
1460 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1461 /* By definition, a match */
1465 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1467 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1468 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1472 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1474 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1475 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1479 /* First check signature */
1480 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1484 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1487 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1490 /* Decrypted, passes signature */
1491 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1492 memcpy(peer->rxenckey, newkey, 128);
1493 memcpy(peer->rxenckey + 128, newsig, 128);
1494 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1495 aes_decrypt_key128(dst, &peer->them_dcx);
1496 aes_encrypt_key128(dst, &peer->them_ecx);
1500 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1502 /* Handle canonical command / response */
1503 int final = hdr->cmdresp & 0x80;
1504 int cmd = hdr->cmdresp & 0x7f;
1509 unsigned char *bufcpy;
1510 struct dundi_ie_data ied;
1511 struct dundi_ies ies;
1512 struct dundi_peer *peer;
1515 memset(&ied, 0, sizeof(ied));
1516 memset(&ies, 0, sizeof(ies));
1518 bufcpy = alloca(datalen);
1521 /* Make a copy for parsing */
1522 memcpy(bufcpy, hdr->ies, datalen);
1523 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1524 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1525 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1530 case DUNDI_COMMAND_DPDISCOVER:
1531 case DUNDI_COMMAND_EIDQUERY:
1532 case DUNDI_COMMAND_PRECACHERQ:
1533 if (cmd == DUNDI_COMMAND_EIDQUERY)
1534 resp = DUNDI_COMMAND_EIDRESPONSE;
1535 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1536 resp = DUNDI_COMMAND_PRECACHERP;
1538 resp = DUNDI_COMMAND_DPRESPONSE;
1539 /* A dialplan or entity discover -- qualify by highest level entity */
1540 peer = find_peer(ies.eids[0]);
1542 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1543 dundi_send(trans, resp, 0, 1, &ied);
1546 trans->us_eid = peer->us_eid;
1547 if (strlen(peer->inkey)) {
1548 hasauth = encrypted;
1552 /* Okay we're authentiated and all, now we check if they're authorized */
1553 if (!ies.called_context)
1554 ies.called_context = "e164";
1555 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1556 res = dundi_answer_entity(trans, &ies, ies.called_context);
1558 if (ast_strlen_zero(ies.called_number)) {
1559 /* They're not permitted to access that context */
1560 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1561 dundi_send(trans, resp, 0, 1, &ied);
1562 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1563 (peer->model & DUNDI_MODEL_INBOUND) &&
1564 has_permission(peer->permit, ies.called_context)) {
1565 res = dundi_answer_query(trans, &ies, ies.called_context);
1567 /* There is no such dundi context */
1568 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1569 dundi_send(trans, resp, 0, 1, &ied);
1571 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1572 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1573 has_permission(peer->include, ies.called_context)) {
1574 res = dundi_prop_precache(trans, &ies, ies.called_context);
1576 /* There is no such dundi context */
1577 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1578 dundi_send(trans, resp, 0, 1, &ied);
1581 /* They're not permitted to access that context */
1582 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1583 dundi_send(trans, resp, 0, 1, &ied);
1587 /* They're not permitted to access that context */
1588 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1589 dundi_send(trans, resp, 0, 1, &ied);
1593 case DUNDI_COMMAND_REGREQ:
1594 /* A register request -- should only have one entity */
1595 peer = find_peer(ies.eids[0]);
1596 if (!peer || !peer->dynamic) {
1597 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1598 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1601 trans->us_eid = peer->us_eid;
1602 if (!ast_strlen_zero(peer->inkey)) {
1603 hasauth = encrypted;
1607 int expire = default_expiration;
1608 char iabuf[INET_ADDRSTRLEN];
1611 if (peer->registerexpire > -1)
1612 ast_sched_del(sched, peer->registerexpire);
1613 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1614 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1615 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1616 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1617 if (inaddrcmp(&peer->addr, &trans->addr)) {
1618 if (option_verbose > 2)
1619 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));
1623 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1624 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1625 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1627 qualify_peer(peer, 1);
1631 case DUNDI_COMMAND_DPRESPONSE:
1632 /* A dialplan response, lets see what we got... */
1633 if (ies.cause < 1) {
1634 /* Success of some sort */
1635 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1636 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1637 authpass = encrypted;
1641 /* Pass back up answers */
1642 if (trans->parent && trans->parent->dr) {
1643 y = trans->parent->respcount;
1644 for (x=0;x<ies.anscount;x++) {
1645 if (trans->parent->respcount < trans->parent->maxcount) {
1646 /* Make sure it's not already there */
1647 for (z=0;z<trans->parent->respcount;z++) {
1648 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1649 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1652 if (z == trans->parent->respcount) {
1653 /* Copy into parent responses */
1654 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1655 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1656 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1657 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1658 if (ies.expiration > 0)
1659 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1661 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1662 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1663 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1664 &ies.answers[x]->eid);
1665 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1666 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1667 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1668 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1669 trans->parent->respcount++;
1670 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1671 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1672 /* Update weight if appropriate */
1673 trans->parent->dr[z].weight = ies.answers[x]->weight;
1676 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1677 trans->parent->number, trans->parent->dcontext);
1679 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1680 the cache know if this request was unaffected by our entity list. */
1681 cache_save(&trans->them_eid, trans->parent, y,
1682 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1684 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1685 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1686 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1687 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1688 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1689 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1690 sizeof(trans->parent->hmd->exten));
1693 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1696 if (ies.expiration > 0) {
1697 if (trans->parent->expiration > ies.expiration) {
1698 trans->parent->expiration = ies.expiration;
1702 /* Close connection if not final */
1704 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1708 /* Auth failure, check for data */
1710 /* Cancel if they didn't already */
1711 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1715 case DUNDI_COMMAND_EIDRESPONSE:
1716 /* A dialplan response, lets see what we got... */
1717 if (ies.cause < 1) {
1718 /* Success of some sort */
1719 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1720 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1721 authpass = encrypted;
1725 /* Pass back up answers */
1726 if (trans->parent && trans->parent->dei && ies.q_org) {
1727 if (!trans->parent->respcount) {
1728 trans->parent->respcount++;
1730 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1732 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1734 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1735 if (ies.q_stateprov)
1736 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1738 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1740 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1742 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1744 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1745 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1746 /* If it's them, update our address */
1747 ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1748 trans->addr.sin_addr);
1752 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1753 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1756 /* Close connection if not final */
1758 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1762 /* Auth failure, check for data */
1764 /* Cancel if they didn't already */
1765 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1769 case DUNDI_COMMAND_REGRESPONSE:
1770 /* A dialplan response, lets see what we got... */
1771 if (ies.cause < 1) {
1773 /* Success of some sort */
1774 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1775 hasauth = encrypted;
1780 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1782 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1783 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1786 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),
1787 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1788 /* Close connection if not final */
1790 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1793 /* Auth failure, cancel if they didn't for some reason */
1795 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1799 case DUNDI_COMMAND_INVALID:
1800 case DUNDI_COMMAND_NULL:
1801 case DUNDI_COMMAND_PRECACHERP:
1802 /* Do nothing special */
1804 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1806 case DUNDI_COMMAND_ENCREJ:
1807 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
1808 /* No really, it's over at this point */
1810 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1812 /* Send with full key */
1813 ast_set_flag(trans, FLAG_SENDFULLKEY);
1815 /* Ooops, we got a final message, start by sending ACK... */
1816 dundi_ack(trans, hdr->cmdresp & 0x80);
1817 trans->aseqno = trans->iseqno;
1818 /* Now, we gotta create a new transaction */
1819 if (!reset_transaction(trans)) {
1820 /* Make sure handle_frame doesn't destroy us */
1821 hdr->cmdresp &= 0x7f;
1822 /* Parse the message we transmitted */
1823 memset(&ies, 0, sizeof(ies));
1824 dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
1825 /* Reconstruct outgoing encrypted packet */
1826 memset(&ied, 0, sizeof(ied));
1827 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1828 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1829 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1831 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1832 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
1833 peer->sentfullkey = 1;
1838 case DUNDI_COMMAND_ENCRYPT:
1840 /* No nested encryption! */
1841 if ((trans->iseqno == 1) && !trans->oseqno) {
1842 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1843 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1844 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1846 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1850 apply_peer(trans, peer);
1851 /* Key passed, use new contexts for this session */
1852 trans->ecx = peer->them_ecx;
1853 trans->dcx = peer->them_dcx;
1855 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1856 struct dundi_hdr *dhdr;
1857 unsigned char decoded[MAX_PACKET_SIZE];
1859 ddatalen = sizeof(decoded);
1860 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1862 /* Handle decrypted response */
1864 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1865 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1866 /* Carry back final flag */
1867 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1870 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1874 /* Turn off encryption */
1875 ast_clear_flag(trans, FLAG_ENCRYPT);
1876 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1880 /* Send unknown command if we don't know it, with final flag IFF it's the
1881 first command in the dialog and only if we haven't recieved final notification */
1883 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1884 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1890 static void destroy_packet(struct dundi_packet *pack, int needfree);
1891 static void destroy_packets(struct dundi_packet *p)
1893 struct dundi_packet *prev;
1897 if (prev->retransid > -1)
1898 ast_sched_del(sched, prev->retransid);
1904 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1906 /* Ack transmitted packet corresponding to iseqno */
1907 struct dundi_packet *pack;
1908 pack = trans->packets;
1910 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1911 destroy_packet(pack, 0);
1912 if (trans->lasttrans) {
1913 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1914 destroy_packets(trans->lasttrans);
1916 trans->lasttrans = pack;
1917 if (trans->autokillid > -1)
1918 ast_sched_del(sched, trans->autokillid);
1919 trans->autokillid = -1;
1927 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1929 struct dundi_transaction *trans;
1930 trans = find_transaction(h, sin);
1932 dundi_reject(h, sin);
1935 /* Got a transaction, see where this header fits in */
1936 if (h->oseqno == trans->iseqno) {
1937 /* Just what we were looking for... Anything but ack increments iseqno */
1938 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1939 /* If final, we're done */
1940 destroy_trans(trans, 0);
1943 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1944 trans->oiseqno = trans->iseqno;
1946 handle_command_response(trans, h, datalen, 0);
1948 if (trans->aseqno != trans->iseqno) {
1949 dundi_ack(trans, h->cmdresp & 0x80);
1950 trans->aseqno = trans->iseqno;
1952 /* Delete any saved last transmissions */
1953 destroy_packets(trans->lasttrans);
1954 trans->lasttrans = NULL;
1955 if (h->cmdresp & 0x80) {
1956 /* Final -- destroy now */
1957 destroy_trans(trans, 0);
1959 } else if (h->oseqno == trans->oiseqno) {
1960 /* Last incoming sequence number -- send ACK without processing */
1961 dundi_ack(trans, 0);
1963 /* Out of window -- simply drop */
1964 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1969 static int socket_read(int *id, int fd, short events, void *cbdata)
1971 struct sockaddr_in sin;
1973 struct dundi_hdr *h;
1974 char buf[MAX_PACKET_SIZE];
1977 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1979 if (errno != ECONNREFUSED)
1980 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1983 if (res < sizeof(struct dundi_hdr)) {
1984 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1988 h = (struct dundi_hdr *)buf;
1990 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1991 ast_mutex_lock(&peerlock);
1992 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
1993 ast_mutex_unlock(&peerlock);
1997 static void build_secret(char *secret, int seclen)
1999 unsigned char tmp[16];
2003 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2004 /* Eliminate potential bad characters */
2005 while((s = strchr(secret, ';'))) *s = '+';
2006 while((s = strchr(secret, '/'))) *s = '+';
2007 while((s = strchr(secret, ':'))) *s = '+';
2008 while((s = strchr(secret, '@'))) *s = '+';
2012 static void save_secret(const char *newkey, const char *oldkey)
2016 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2018 snprintf(tmp, sizeof(tmp), "%s", newkey);
2019 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2020 ast_db_put(secretpath, "secret", tmp);
2021 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2022 ast_db_put(secretpath, "secretexpiry", tmp);
2025 static void load_password(void)
2032 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2033 if (sscanf(tmp, "%d", (int *)&expired) == 1) {
2034 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2035 current = strchr(tmp, ';');
2042 if ((time(NULL) - expired) < 0) {
2043 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2044 expired = time(NULL) + DUNDI_SECRET_TIME;
2045 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2054 /* Current key is still valid, just setup rotatation properly */
2055 ast_copy_string(cursecret, current, sizeof(cursecret));
2056 rotatetime = expired;
2058 /* Current key is out of date, rotate or eliminate all together */
2059 build_secret(cursecret, sizeof(cursecret));
2060 save_secret(cursecret, last);
2064 static void check_password(void)
2071 printf("%ld/%ld\n", now, rotatetime);
2073 if ((now - rotatetime) >= 0) {
2074 /* Time to rotate keys */
2075 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2076 build_secret(cursecret, sizeof(cursecret));
2077 save_secret(cursecret, oldsecret);
2081 static void *network_thread(void *ignore)
2083 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2084 from the network, and queue them for delivery to the channels */
2086 /* Establish I/O callback for socket read */
2087 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2089 res = ast_sched_wait(sched);
2090 if ((res > 1000) || (res < 0))
2092 res = ast_io_wait(io, res);
2094 ast_mutex_lock(&peerlock);
2095 ast_sched_runq(sched);
2096 ast_mutex_unlock(&peerlock);
2103 static void *process_precache(void *ign)
2105 struct dundi_precache_queue *qe;
2113 ast_mutex_lock(&pclock);
2115 if (!pcq->expiration) {
2116 /* Gone... Remove... */
2120 } else if (pcq->expiration < now) {
2121 /* Process this entry */
2122 pcq->expiration = 0;
2123 ast_copy_string(context, pcq->context, sizeof(context));
2124 ast_copy_string(number, pcq->number, sizeof(number));
2128 ast_mutex_unlock(&pclock);
2130 dundi_precache(context, number);
2137 static int start_network_thread(void)
2139 ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
2140 ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
2144 static int dundi_do_debug(int fd, int argc, char *argv[])
2147 return RESULT_SHOWUSAGE;
2149 ast_cli(fd, "DUNDi Debugging Enabled\n");
2150 return RESULT_SUCCESS;
2153 static int dundi_do_store_history(int fd, int argc, char *argv[])
2156 return RESULT_SHOWUSAGE;
2157 global_storehistory = 1;
2158 ast_cli(fd, "DUNDi History Storage Enabled\n");
2159 return RESULT_SUCCESS;
2162 static int dundi_flush(int fd, int argc, char *argv[])
2165 if ((argc < 2) || (argc > 3))
2166 return RESULT_SHOWUSAGE;
2168 if (!strcasecmp(argv[2], "stats"))
2171 return RESULT_SHOWUSAGE;
2174 /* Flush statistics */
2175 struct dundi_peer *p;
2177 ast_mutex_lock(&peerlock);
2180 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2182 free(p->lookups[x]);
2183 p->lookups[x] = NULL;
2184 p->lookuptimes[x] = 0;
2189 ast_mutex_unlock(&peerlock);
2191 ast_db_deltree("dundi/cache", NULL);
2192 ast_cli(fd, "DUNDi Cache Flushed\n");
2194 return RESULT_SUCCESS;
2197 static int dundi_no_debug(int fd, int argc, char *argv[])
2200 return RESULT_SHOWUSAGE;
2202 ast_cli(fd, "DUNDi Debugging Disabled\n");
2203 return RESULT_SUCCESS;
2206 static int dundi_no_store_history(int fd, int argc, char *argv[])
2209 return RESULT_SHOWUSAGE;
2210 global_storehistory = 0;
2211 ast_cli(fd, "DUNDi History Storage Disabled\n");
2212 return RESULT_SUCCESS;
2215 static char *model2str(int model)
2218 case DUNDI_MODEL_INBOUND:
2220 case DUNDI_MODEL_OUTBOUND:
2222 case DUNDI_MODEL_SYMMETRIC:
2229 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2233 struct dundi_peer *p;
2237 ast_mutex_lock(&peerlock);
2240 if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
2241 if (++which > state)
2247 ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
2250 ast_mutex_unlock(&peerlock);
2254 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2256 return complete_peer_helper(line, word, pos, state, 3);
2259 static int rescomp(const void *a, const void *b)
2261 const struct dundi_result *resa, *resb;
2264 if (resa->weight < resb->weight)
2266 if (resa->weight > resb->weight)
2271 static void sort_results(struct dundi_result *results, int count)
2273 qsort(results, count, sizeof(results[0]), rescomp);
2276 static int dundi_do_lookup(int fd, int argc, char *argv[])
2284 struct dundi_result dr[MAX_RESULTS];
2285 struct timeval start;
2286 if ((argc < 3) || (argc > 4))
2287 return RESULT_SHOWUSAGE;
2289 if (!strcasecmp(argv[3], "bypass"))
2292 return RESULT_SHOWUSAGE;
2294 ast_copy_string(tmp, argv[2], sizeof(tmp));
2295 context = strchr(tmp, '@');
2300 start = ast_tvnow();
2301 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2304 ast_cli(fd, "DUNDi lookup returned error.\n");
2306 ast_cli(fd, "DUNDi lookup returned no results.\n");
2308 sort_results(dr, res);
2309 for (x=0;x<res;x++) {
2310 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));
2311 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2313 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2314 return RESULT_SUCCESS;
2317 static int dundi_do_precache(int fd, int argc, char *argv[])
2322 struct timeval start;
2323 if ((argc < 3) || (argc > 3))
2324 return RESULT_SHOWUSAGE;
2325 ast_copy_string(tmp, argv[2], sizeof(tmp));
2326 context = strchr(tmp, '@');
2331 start = ast_tvnow();
2332 res = dundi_precache(context, tmp);
2335 ast_cli(fd, "DUNDi precache returned error.\n");
2337 ast_cli(fd, "DUNDi precache returned no error.\n");
2338 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2339 return RESULT_SUCCESS;
2342 static int dundi_do_query(int fd, int argc, char *argv[])
2348 struct dundi_entity_info dei;
2349 if ((argc < 3) || (argc > 3))
2350 return RESULT_SHOWUSAGE;
2351 if (dundi_str_to_eid(&eid, argv[2])) {
2352 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2353 return RESULT_SHOWUSAGE;
2355 ast_copy_string(tmp, argv[2], sizeof(tmp));
2356 context = strchr(tmp, '@');
2361 res = dundi_query_eid(&dei, context, eid);
2363 ast_cli(fd, "DUNDi Query EID returned error.\n");
2365 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2367 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2368 ast_cli(fd, "Department: %s\n", dei.orgunit);
2369 ast_cli(fd, "Organization: %s\n", dei.org);
2370 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2371 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2372 ast_cli(fd, "Country: %s\n", dei.country);
2373 ast_cli(fd, "E-mail: %s\n", dei.email);
2374 ast_cli(fd, "Phone: %s\n", dei.phone);
2375 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2377 return RESULT_SUCCESS;
2380 static int dundi_show_peer(int fd, int argc, char *argv[])
2382 struct dundi_peer *peer;
2383 struct permission *p;
2385 char iabuf[INET_ADDRSTRLEN];
2390 return RESULT_SHOWUSAGE;
2391 ast_mutex_lock(&peerlock);
2394 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2399 switch(peer->order) {
2404 order = "Secondary";
2410 order = "Quartiary";
2415 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2416 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2417 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2418 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2419 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2420 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2421 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2422 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2423 if (peer->include) {
2424 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2428 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2432 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2436 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2440 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2441 if (peer->lookups[x]) {
2443 ast_cli(fd, "Last few query times:\n");
2444 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2449 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2451 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2452 ast_mutex_unlock(&peerlock);
2453 return RESULT_SUCCESS;
2456 static int dundi_show_peers(int fd, int argc, char *argv[])
2458 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2459 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2460 struct dundi_peer *peer;
2461 char iabuf[INET_ADDRSTRLEN];
2462 int registeredonly=0;
2465 int online_peers = 0;
2466 int offline_peers = 0;
2467 int unmonitored_peers = 0;
2468 int total_peers = 0;
2470 if ((argc != 3) && (argc != 4) && (argc != 5))
2471 return RESULT_SHOWUSAGE;
2473 if (!strcasecmp(argv[3], "registered")) {
2476 return RESULT_SHOWUSAGE;
2478 ast_mutex_lock(&peerlock);
2479 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2480 for (peer = peers;peer;peer = peer->next) {
2482 int print_line = -1;
2485 if (registeredonly && !peer->addr.sin_addr.s_addr)
2488 if (peer->lastms < 0) {
2489 strcpy(status, "UNREACHABLE");
2492 else if (peer->lastms > peer->maxms) {
2493 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2496 else if (peer->lastms) {
2497 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2501 strcpy(status, "UNKNOWN");
2505 strcpy(status, "Unmonitored");
2506 unmonitored_peers++;
2509 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2511 strcpy(avgms, "Unavail");
2512 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2513 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2514 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2517 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2519 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2521 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2529 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2530 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2531 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2534 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2535 ast_mutex_unlock(&peerlock);
2536 return RESULT_SUCCESS;
2541 static int dundi_show_trans(int fd, int argc, char *argv[])
2543 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2544 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2545 struct dundi_transaction *trans;
2546 char iabuf[INET_ADDRSTRLEN];
2548 return RESULT_SHOWUSAGE;
2549 ast_mutex_lock(&peerlock);
2550 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2551 for (trans = alltrans;trans;trans = trans->allnext) {
2552 ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
2553 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2555 ast_mutex_unlock(&peerlock);
2556 return RESULT_SUCCESS;
2561 static int dundi_show_entityid(int fd, int argc, char *argv[])
2565 return RESULT_SHOWUSAGE;
2566 ast_mutex_lock(&peerlock);
2567 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2568 ast_mutex_unlock(&peerlock);
2569 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2570 return RESULT_SUCCESS;
2573 static int dundi_show_requests(int fd, int argc, char *argv[])
2575 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2576 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2577 struct dundi_request *req;
2580 return RESULT_SHOWUSAGE;
2581 ast_mutex_lock(&peerlock);
2582 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2583 for (req = requests;req;req = req->next) {
2584 ast_cli(fd, FORMAT, req->number, req->dcontext,
2585 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2587 ast_mutex_unlock(&peerlock);
2588 return RESULT_SUCCESS;
2593 /* Grok-a-dial DUNDi */
2595 static int dundi_show_mappings(int fd, int argc, char *argv[])
2597 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2598 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2599 struct dundi_mapping *map;
2602 return RESULT_SHOWUSAGE;
2603 ast_mutex_lock(&peerlock);
2604 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2605 for (map = mappings;map;map = map->next) {
2606 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2607 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2608 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2610 ast_mutex_unlock(&peerlock);
2611 return RESULT_SUCCESS;
2616 static int dundi_show_precache(int fd, int argc, char *argv[])
2618 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2619 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2620 struct dundi_precache_queue *qe;
2625 return RESULT_SHOWUSAGE;
2627 ast_mutex_lock(&pclock);
2628 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2629 for (qe = pcq;qe;qe = qe->next) {
2630 s = qe->expiration - now;
2635 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2637 ast_mutex_unlock(&pclock);
2638 return RESULT_SUCCESS;
2643 static char debug_usage[] =
2644 "Usage: dundi debug\n"
2645 " Enables dumping of DUNDi packets for debugging purposes\n";
2647 static char no_debug_usage[] =
2648 "Usage: dundi no debug\n"
2649 " Disables dumping of DUNDi packets for debugging purposes\n";
2651 static char store_history_usage[] =
2652 "Usage: dundi store history\n"
2653 " Enables storing of DUNDi requests and times for debugging\n"
2656 static char no_store_history_usage[] =
2657 "Usage: dundi no store history\n"
2658 " Disables storing of DUNDi requests and times for debugging\n"
2661 static char show_peers_usage[] =
2662 "Usage: dundi show peers\n"
2663 " Lists all known DUNDi peers.\n";
2665 static char show_trans_usage[] =
2666 "Usage: dundi show trans\n"
2667 " Lists all known DUNDi transactions.\n";
2669 static char show_mappings_usage[] =
2670 "Usage: dundi show mappings\n"
2671 " Lists all known DUNDi mappings.\n";
2673 static char show_precache_usage[] =
2674 "Usage: dundi show precache\n"
2675 " Lists all known DUNDi scheduled precache updates.\n";
2677 static char show_entityid_usage[] =
2678 "Usage: dundi show entityid\n"
2679 " Displays the global entityid for this host.\n";
2681 static char show_peer_usage[] =
2682 "Usage: dundi show peer [peer]\n"
2683 " Provide a detailed description of a specifid DUNDi peer.\n";
2685 static char show_requests_usage[] =
2686 "Usage: dundi show requests\n"
2687 " Lists all known pending DUNDi requests.\n";
2689 static char lookup_usage[] =
2690 "Usage: dundi lookup <number>[@context] [bypass]\n"
2691 " Lookup the given number within the given DUNDi context\n"
2692 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2693 "keyword is specified.\n";
2695 static char precache_usage[] =
2696 "Usage: dundi precache <number>[@context]\n"
2697 " Lookup the given number within the given DUNDi context\n"
2698 "(or e164 if none is specified) and precaches the results to any\n"
2699 "upstream DUNDi push servers.\n";
2701 static char query_usage[] =
2702 "Usage: dundi query <entity>[@context]\n"
2703 " Attempts to retrieve contact information for a specific\n"
2704 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2705 "e164 if none is specified).\n";
2707 static char flush_usage[] =
2708 "Usage: dundi flush [stats]\n"
2709 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2710 "'stats' is present, clears timer statistics instead of normal\n"
2713 static struct ast_cli_entry cli_debug =
2714 { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2716 static struct ast_cli_entry cli_store_history =
2717 { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2719 static struct ast_cli_entry cli_no_store_history =
2720 { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2722 static struct ast_cli_entry cli_flush =
2723 { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2725 static struct ast_cli_entry cli_no_debug =
2726 { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2728 static struct ast_cli_entry cli_show_peers =
2729 { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2731 static struct ast_cli_entry cli_show_trans =
2732 { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2734 static struct ast_cli_entry cli_show_entityid =
2735 { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2737 static struct ast_cli_entry cli_show_mappings =
2738 { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2740 static struct ast_cli_entry cli_show_precache =
2741 { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2743 static struct ast_cli_entry cli_show_requests =
2744 { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2746 static struct ast_cli_entry cli_show_peer =
2747 { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2749 static struct ast_cli_entry cli_lookup =
2750 { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2752 static struct ast_cli_entry cli_precache =
2753 { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2755 static struct ast_cli_entry cli_queryeid =
2756 { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2760 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2762 struct dundi_transaction *trans;
2765 /* Don't allow creation of transactions to non-registered peers */
2766 if (p && !p->addr.sin_addr.s_addr)
2768 tid = get_trans_id();
2771 trans = malloc(sizeof(struct dundi_transaction));
2773 memset(trans, 0, sizeof(struct dundi_transaction));
2774 if (global_storehistory) {
2775 trans->start = ast_tvnow();
2776 ast_set_flag(trans, FLAG_STOREHIST);
2778 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2779 trans->autokillid = -1;
2781 apply_peer(trans, p);
2782 if (!p->sentfullkey)
2783 ast_set_flag(trans, FLAG_SENDFULLKEY);
2785 trans->strans = tid;
2786 trans->allnext = alltrans;
2792 static int dundi_xmit(struct dundi_packet *pack)
2795 char iabuf[INET_ADDRSTRLEN];
2797 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2798 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2800 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2801 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2802 ntohs(pack->parent->addr.sin_port), strerror(errno));
2809 static void destroy_packet(struct dundi_packet *pack, int needfree)
2811 struct dundi_packet *prev, *cur;
2814 cur = pack->parent->packets;
2818 prev->next = cur->next;
2820 pack->parent->packets = cur->next;
2827 if (pack->retransid > -1)
2828 ast_sched_del(sched, pack->retransid);
2832 pack->retransid = -1;
2837 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2839 struct dundi_transaction *cur, *prev;
2840 struct dundi_peer *peer;
2845 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2848 if (peer->regtrans == trans)
2849 peer->regtrans = NULL;
2850 if (peer->keypending == trans)
2851 peer->keypending = NULL;
2852 if (peer->qualtrans == trans) {
2854 if (peer->lastms > -1)
2855 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2858 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2861 if (ms < peer->maxms) {
2862 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2863 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2864 } else if (peer->lastms < peer->maxms) {
2865 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);
2869 peer->qualtrans = NULL;
2871 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2872 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2873 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2876 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2877 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2878 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2879 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2880 peer->lookups[x] = peer->lookups[x-1];
2881 if (peer->lookups[x]) {
2882 peer->avgms += peer->lookuptimes[x];
2886 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2887 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2888 if (peer->lookups[0]) {
2889 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2890 peer->avgms += peer->lookuptimes[0];
2901 if (trans->parent) {
2902 /* Unlink from parent if appropriate */
2904 cur = trans->parent->trans;
2908 prev->next = trans->next;
2910 trans->parent->trans = trans->next;
2916 if (!trans->parent->trans) {
2917 /* Wake up sleeper */
2918 if (trans->parent->pfds[1] > -1) {
2919 write(trans->parent->pfds[1], "killa!", 6);
2923 /* Unlink from all trans */
2929 prev->allnext = trans->allnext;
2931 alltrans = trans->allnext;
2937 destroy_packets(trans->packets);
2938 destroy_packets(trans->lasttrans);
2939 trans->packets = NULL;
2940 trans->lasttrans = NULL;
2941 if (trans->autokillid > -1)
2942 ast_sched_del(sched, trans->autokillid);
2943 trans->autokillid = -1;
2944 if (trans->thread) {
2945 /* If used by a thread, mark as dead and be done */
2946 ast_set_flag(trans, FLAG_DEAD);
2951 static int dundi_rexmit(void *data)
2953 struct dundi_packet *pack;
2954 char iabuf[INET_ADDRSTRLEN];
2956 ast_mutex_lock(&peerlock);
2958 if (pack->retrans < 1) {
2959 pack->retransid = -1;
2960 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2961 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2962 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2963 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2964 destroy_trans(pack->parent, 1);
2967 /* Decrement retransmission, try again */
2972 ast_mutex_unlock(&peerlock);
2976 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2978 struct dundi_packet *pack;
2982 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2983 /* Reserve enough space for encryption */
2984 if (ast_test_flag(trans, FLAG_ENCRYPT))
2988 memset(pack, 0, len);
2989 pack->h = (struct dundi_hdr *)(pack->data);
2990 if (cmdresp != DUNDI_COMMAND_ACK) {
2991 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2992 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2993 pack->next = trans->packets;
2994 trans->packets = pack;
2996 pack->parent = trans;
2997 pack->h->strans = htons(trans->strans);
2998 pack->h->dtrans = htons(trans->dtrans);
2999 pack->h->iseqno = trans->iseqno;
3000 pack->h->oseqno = trans->oseqno;
3001 pack->h->cmdresp = cmdresp;
3002 pack->datalen = sizeof(struct dundi_hdr);
3004 memcpy(pack->h->ies, ied->buf, ied->pos);
3005 pack->datalen += ied->pos;
3008 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3009 ast_set_flag(trans, FLAG_FINAL);
3011 pack->h->cmdflags = flags;
3012 if (cmdresp != DUNDI_COMMAND_ACK) {
3014 trans->oseqno = trans->oseqno % 256;
3016 trans->aseqno = trans->iseqno;
3017 /* If we have their public key, encrypt */
3018 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3020 case DUNDI_COMMAND_REGREQ:
3021 case DUNDI_COMMAND_REGRESPONSE:
3022 case DUNDI_COMMAND_DPDISCOVER:
3023 case DUNDI_COMMAND_DPRESPONSE:
3024 case DUNDI_COMMAND_EIDQUERY:
3025 case DUNDI_COMMAND_EIDRESPONSE:
3026 case DUNDI_COMMAND_PRECACHERQ:
3027 case DUNDI_COMMAND_PRECACHERP:
3029 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3030 res = dundi_encrypt(trans, pack);
3038 res = dundi_xmit(pack);
3040 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3042 if (cmdresp == DUNDI_COMMAND_ACK)
3049 static int do_autokill(void *data)
3051 struct dundi_transaction *trans = data;
3053 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3054 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3055 trans->autokillid = -1;
3056 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3060 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3062 struct dundi_peer *p;
3063 if (!dundi_eid_cmp(eid, us)) {
3064 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3067 ast_mutex_lock(&peerlock);
3070 if (!dundi_eid_cmp(&p->eid, eid)) {
3071 if (has_permission(p->include, context))
3072 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3074 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3080 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3081 ast_mutex_unlock(&peerlock);
3084 static int dundi_discover(struct dundi_transaction *trans)
3086 struct dundi_ie_data ied;
3088 if (!trans->parent) {
3089 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3092 memset(&ied, 0, sizeof(ied));
3093 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3094 if (!dundi_eid_zero(&trans->us_eid))
3095 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3096 for (x=0;x<trans->eidcount;x++)
3097 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3098 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3099 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3100 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3101 if (trans->parent->cbypass)
3102 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3103 if (trans->autokilltimeout)
3104 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3105 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3108 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3110 struct dundi_ie_data ied;
3113 int expiration = dundi_cache_time;
3115 dundi_eid *avoid[1] = { NULL, };
3116 int direct[1] = { 0, };
3117 struct dundi_result dr[MAX_RESULTS];
3118 struct dundi_hint_metadata hmd;
3119 if (!trans->parent) {
3120 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3123 memset(&hmd, 0, sizeof(hmd));
3124 memset(&dr, 0, sizeof(dr));
3125 /* Look up the answers we're going to include */
3126 for (x=0;x<mapcount;x++)
3127 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3130 for (x=0;x<ouranswers;x++) {
3131 if (dr[x].weight < max)
3135 /* If we do not have a canonical result, keep looking */
3136 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
3138 /* Append answer in result */
3143 if (ouranswers > 0) {
3144 *foundanswers += ouranswers;
3145 memset(&ied, 0, sizeof(ied));
3146 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3147 if (!dundi_eid_zero(&trans->us_eid))
3148 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3149 for (x=0;x<trans->eidcount;x++)
3150 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3151 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3152 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3153 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3154 for (x=0;x<ouranswers;x++) {
3156 if (dr[x].expiration && (expiration > dr[x].expiration))
3157 expiration = dr[x].expiration;
3158 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3160 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3161 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3162 if (trans->autokilltimeout)
3163 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3164 if (expiration < *minexp)
3165 *minexp = expiration;
3166 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3168 /* Oops, nothing to send... */
3169 destroy_trans(trans, 0);
3174 static int dundi_query(struct dundi_transaction *trans)
3176 struct dundi_ie_data ied;
3178 if (!trans->parent) {
3179 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3182 memset(&ied, 0, sizeof(ied));
3183 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3184 if (!dundi_eid_zero(&trans->us_eid))
3185 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3186 for (x=0;x<trans->eidcount;x++)
3187 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3188 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3189 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3190 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3191 if (trans->autokilltimeout)
3192 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3193 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3196 static int discover_transactions(struct dundi_request *dr)
3198 struct dundi_transaction *trans;
3199 ast_mutex_lock(&peerlock);
3202 dundi_discover(trans);
3203 trans = trans->next;
3205 ast_mutex_unlock(&peerlock);
3209 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3211 struct dundi_transaction *trans, *transn;
3212 /* Mark all as "in thread" so they don't disappear */
3213 ast_mutex_lock(&peerlock);
3217 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3219 trans = trans->next;
3221 ast_mutex_unlock(&peerlock);
3225 if (!ast_test_flag(trans, FLAG_DEAD))
3226 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3227 trans = trans->next;
3230 /* Cleanup any that got destroyed in the mean time */
3231 ast_mutex_lock(&peerlock);
3234 transn = trans->next;
3236 if (ast_test_flag(trans, FLAG_DEAD)) {
3237 ast_log(LOG_DEBUG, "Our transaction went away!\n");
3238 destroy_trans(trans, 0);
3242 ast_mutex_unlock(&peerlock);
3246 static int query_transactions(struct dundi_request *dr)
3248 struct dundi_transaction *trans;
3249 ast_mutex_lock(&peerlock);
3253 trans = trans->next;
3255 ast_mutex_unlock(&peerlock);
3259 static int optimize_transactions(struct dundi_request *dr, int order)
3261 /* Minimize the message propagation through DUNDi by
3262 alerting the network to hops which should be not be considered */
3263 struct dundi_transaction *trans;
3264 struct dundi_peer *peer;
3268 ast_mutex_lock(&peerlock);
3271 /* Pop off the true root */
3272 if (trans->eidcount) {
3273 tmp = trans->eids[--trans->eidcount];
3276 tmp = trans->us_eid;
3282 if (has_permission(peer->include, dr->dcontext) &&
3283 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3284 (peer->order <= order)) {
3285 /* For each other transaction, make sure we don't
3286 ask this EID about the others if they're not
3287 already in the list */
3288 if (!dundi_eid_cmp(&tmp, &peer->eid))
3291 for (x=0;x<trans->eidcount;x++) {
3292 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3296 if (x == trans->eidcount) {
3297 /* Nope not in the list, if needed, add us at the end since we're the source */
3298 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3299 trans->eids[trans->eidcount++] = peer->eid;
3300 /* Need to insert the real root (or us) at the bottom now as
3301 a requirement now. */
3308 /* If necessary, push the true root back on the end */
3310 trans->eids[trans->eidcount++] = tmp;
3311 trans = trans->next;
3313 ast_mutex_unlock(&peerlock);
3317 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3319 struct dundi_transaction *trans;
3323 /* Ignore if not registered */
3324 if (!p->addr.sin_addr.s_addr)
3326 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3328 if (ast_strlen_zero(dr->number))
3329 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
3331 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
3332 trans = create_transaction(p);
3335 trans->next = dr->trans;
3338 for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
3339 trans->eids[x] = *avoid[x];
3340 trans->eidcount = x;
3345 static void cancel_request(struct dundi_request *dr)
3347 struct dundi_transaction *trans, *next;
3349 ast_mutex_lock(&peerlock);