2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, 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 static char *app = "DUNDiLookup";
83 static char *synopsis = "Look up a number with DUNDi";
84 static char *descrip =
85 "DUNDiLookup(number[|context[|options]])\n"
86 " Looks up a given number in the global context specified or in\n"
87 "the reserved 'e164' context if not specified. Returns -1 if the channel\n"
88 "is hungup during the lookup or 0 otherwise. On completion, the variable\n"
89 "${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
90 "of the appropriate technology and destination to access the number. If no\n"
91 "answer was found, and the priority n + 101 exists, execution will continue\n"
92 "at that location. Note that this will only occur if the global priority\n"
93 "jumping option is enabled in extensions.conf. If the 'b' option is specified,\n"
94 "the internal DUNDi cache will by bypassed.\n";
96 #define DUNDI_MODEL_INBOUND (1 << 0)
97 #define DUNDI_MODEL_OUTBOUND (1 << 1)
98 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
100 /* Keep times of last 10 lookups */
101 #define DUNDI_TIMING_HISTORY 10
103 #define FLAG_ISREG (1 << 0) /* Transaction is register request */
104 #define FLAG_DEAD (1 << 1) /* Transaction is dead */
105 #define FLAG_FINAL (1 << 2) /* Transaction has final message sent */
106 #define FLAG_ISQUAL (1 << 3) /* Transaction is a qualification */
107 #define FLAG_ENCRYPT (1 << 4) /* Transaction is encrypted wiht ECX/DCX */
108 #define FLAG_SENDFULLKEY (1 << 5) /* Send full key on transaction */
109 #define FLAG_STOREHIST (1 << 6) /* Record historic performance */
111 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
114 #define DUNDI_SECRET_TIME 15 /* Testing only */
116 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
122 static struct io_context *io;
123 static struct sched_context *sched;
124 static int netsocket = -1;
125 static pthread_t netthreadid = AST_PTHREADT_NULL;
126 static pthread_t precachethreadid = AST_PTHREADT_NULL;
128 static int dundidebug = 0;
129 static int authdebug = 0;
130 static int dundi_ttl = DUNDI_DEFAULT_TTL;
131 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
132 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
133 static int global_autokilltimeout = 0;
134 static dundi_eid global_eid;
135 static int default_expiration = 60;
136 static int global_storehistory = 0;
137 static char dept[80];
139 static char locality[80];
140 static char stateprov[80];
141 static char country[80];
142 static char email[80];
143 static char phone[80];
144 static char secretpath[80];
145 static char cursecret[80];
146 static char ipaddr[80];
147 static time_t rotatetime;
148 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
150 struct permission *next;
155 struct dundi_packet {
157 struct dundi_packet *next;
159 struct dundi_transaction *parent;
162 unsigned char data[0];
165 struct dundi_hint_metadata {
166 unsigned short flags;
167 char exten[AST_MAX_EXTENSION];
170 struct dundi_precache_queue {
171 struct dundi_precache_queue *next;
177 struct dundi_request;
179 struct dundi_transaction {
180 struct sockaddr_in addr; /* Other end of transaction */
181 struct timeval start; /* When this transaction was created */
182 dundi_eid eids[DUNDI_MAX_STACK + 1];
183 int eidcount; /* Number of eids in eids */
184 dundi_eid us_eid; /* Our EID, to them */
185 dundi_eid them_eid; /* Their EID, to us */
186 aes_encrypt_ctx ecx; /* AES 128 Encryption context */
187 aes_decrypt_ctx dcx; /* AES 128 Decryption context */
188 unsigned int flags; /* Has final packet been sent */
189 int ttl; /* Remaining TTL for queries on this one */
190 int thread; /* We have a calling thread */
191 int retranstimer; /* How long to wait before retransmissions */
192 int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
193 int autokilltimeout; /* Recommended timeout for autokill */
194 unsigned short strans; /* Our transaction identifier */
195 unsigned short dtrans; /* Their transaction identifer */
196 unsigned char iseqno; /* Next expected received seqno */
197 unsigned char oiseqno; /* Last received incoming seqno */
198 unsigned char oseqno; /* Next transmitted seqno */
199 unsigned char aseqno; /* Last acknowledge seqno */
200 struct dundi_packet *packets; /* Packets to be retransmitted */
201 struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
202 struct dundi_transaction *next; /* Next with respect to the parent */
203 struct dundi_request *parent; /* Parent request (if there is one) */
204 struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
207 struct dundi_request {
208 char dcontext[AST_MAX_EXTENSION];
209 char number[AST_MAX_EXTENSION];
212 struct dundi_result *dr;
213 struct dundi_entity_info *dei;
214 struct dundi_hint_metadata *hmd;
220 unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
221 struct dundi_transaction *trans; /* Transactions */
222 struct dundi_request *next;
225 static struct dundi_mapping {
226 char dcontext[AST_MAX_EXTENSION];
227 char lcontext[AST_MAX_EXTENSION];
232 char dest[AST_MAX_EXTENSION];
233 struct dundi_mapping *next;
236 static struct dundi_peer {
238 struct sockaddr_in addr; /* Address of DUNDi peer */
239 struct permission *permit;
240 struct permission *include;
241 struct permission *precachesend;
242 struct permission *precachereceive;
251 unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
252 unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
253 unsigned long us_keycrc32; /* CRC-32 of our key */
254 aes_encrypt_ctx us_ecx; /* Cached AES 128 Encryption context */
255 aes_decrypt_ctx us_dcx; /* Cached AES 128 Decryption context */
256 unsigned long them_keycrc32;/* CRC-32 of our key */
257 aes_encrypt_ctx them_ecx; /* Cached AES 128 Encryption context */
258 aes_decrypt_ctx them_dcx; /* Cached AES 128 Decryption context */
259 time_t keyexpire; /* When to expire/recreate key */
261 int lookuptimes[DUNDI_TIMING_HISTORY];
262 char *lookups[DUNDI_TIMING_HISTORY];
264 struct dundi_transaction *regtrans; /* Registration transaction */
265 struct dundi_transaction *qualtrans; /* Qualify transaction */
266 struct dundi_transaction *keypending;
267 int model; /* Pull model */
268 int pcmodel; /* Push/precache model */
269 int dynamic; /* Are we dynamic? */
270 int lastms; /* Last measured latency */
271 int maxms; /* Max permissible latency */
272 struct timeval qualtx; /* Time of transmit */
273 struct dundi_peer *next;
276 static struct dundi_precache_queue *pcq;
278 AST_MUTEX_DEFINE_STATIC(peerlock);
279 AST_MUTEX_DEFINE_STATIC(pclock);
281 static int dundi_xmit(struct dundi_packet *pack);
283 static void dundi_debug_output(const char *data)
286 ast_verbose("%s", data);
289 static void dundi_error_output(const char *data)
291 ast_log(LOG_WARNING, "%s", data);
294 static int has_permission(struct permission *ps, char *cont)
298 if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
305 static char *tech2str(int tech)
308 case DUNDI_PROTO_NONE:
310 case DUNDI_PROTO_IAX:
312 case DUNDI_PROTO_SIP:
314 case DUNDI_PROTO_H323:
321 static int str2tech(char *str)
323 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
324 return DUNDI_PROTO_IAX;
325 else if (!strcasecmp(str, "SIP"))
326 return DUNDI_PROTO_SIP;
327 else if (!strcasecmp(str, "H323"))
328 return DUNDI_PROTO_H323;
333 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[]);
334 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
335 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
336 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
338 /* Look for an exact match first */
339 struct dundi_transaction *trans;
342 if (!inaddrcmp(&trans->addr, sin) &&
343 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
344 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
346 trans->dtrans = ntohs(hdr->strans) & 32767;
349 trans = trans->allnext;
352 switch(hdr->cmdresp & 0x7f) {
353 case DUNDI_COMMAND_DPDISCOVER:
354 case DUNDI_COMMAND_EIDQUERY:
355 case DUNDI_COMMAND_PRECACHERQ:
356 case DUNDI_COMMAND_REGREQ:
357 case DUNDI_COMMAND_NULL:
358 case DUNDI_COMMAND_ENCRYPT:
360 /* Create new transaction */
361 trans = create_transaction(NULL);
363 memcpy(&trans->addr, sin, sizeof(trans->addr));
364 trans->dtrans = ntohs(hdr->strans) & 32767;
366 ast_log(LOG_WARNING, "Out of memory!\n");
376 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
378 static int dundi_ack(struct dundi_transaction *trans, int final)
380 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
382 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
385 struct dundi_packet pack;
386 struct dundi_hdr hdr;
388 struct dundi_transaction trans;
389 /* Never respond to an INVALID with another INVALID */
390 if (h->cmdresp == DUNDI_COMMAND_INVALID)
392 memset(&tmp, 0, sizeof(tmp));
393 memset(&trans, 0, sizeof(trans));
394 memcpy(&trans.addr, sin, sizeof(trans.addr));
395 tmp.hdr.strans = h->dtrans;
396 tmp.hdr.dtrans = h->strans;
397 tmp.hdr.iseqno = h->oseqno;
398 tmp.hdr.oseqno = h->iseqno;
399 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
400 tmp.hdr.cmdflags = 0;
401 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
402 tmp.pack.datalen = sizeof(struct dundi_hdr);
403 tmp.pack.parent = &trans;
404 dundi_xmit(&tmp.pack);
407 static void reset_global_eid(void)
409 #if defined(SIOCGIFHWADDR)
414 s = socket(AF_INET, SOCK_STREAM, 0);
418 memset(&ifr, 0, sizeof(ifr));
419 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
420 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
421 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
422 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);
430 #if defined(ifa_broadaddr) && !defined(SOLARIS)
432 struct ifaddrs *ifap;
434 if (getifaddrs(&ifap) == 0) {
436 for (p = ifap; p; p = p->ifa_next) {
437 if (p->ifa_addr->sa_family == AF_LINK) {
438 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
441 sdp->sdl_data + sdp->sdl_nlen, 6);
442 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);
451 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
454 static int get_trans_id(void)
456 struct dundi_transaction *t;
457 int stid = (rand() % 32766) + 1;
462 if (t->strans == tid)
468 tid = (tid % 32766) + 1;
469 } while (tid != stid);
473 static int reset_transaction(struct dundi_transaction *trans)
476 tid = get_trans_id();
485 ast_clear_flag(trans, FLAG_FINAL);
489 static struct dundi_peer *find_peer(dundi_eid *eid)
491 struct dundi_peer *cur;
496 if (!dundi_eid_cmp(&cur->eid,eid))
503 static void build_iv(unsigned char *iv)
505 /* XXX Would be nice to be more random XXX */
506 unsigned int *fluffy;
508 fluffy = (unsigned int *)(iv);
513 struct dundi_query_state {
514 dundi_eid *eids[DUNDI_MAX_STACK + 1];
515 int directs[DUNDI_MAX_STACK + 1];
517 char called_context[AST_MAX_EXTENSION];
518 char called_number[AST_MAX_EXTENSION];
519 struct dundi_mapping *maps;
522 struct dundi_transaction *trans;
529 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)
531 struct ast_flags flags = {0};
533 if (!ast_strlen_zero(map->lcontext)) {
534 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
535 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
536 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
537 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
538 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
539 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
540 if (ast_ignore_pattern(map->lcontext, called_number))
541 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
543 /* Clearly we can't say 'don't ask' anymore if we found anything... */
544 if (ast_test_flag(&flags, AST_FLAGS_ALL))
545 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
547 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
548 /* Skip partial answers */
549 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
551 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
552 struct varshead headp;
553 struct ast_var_t *newvariable;
554 ast_set_flag(&flags, map->options & 0xffff);
555 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
556 dr[anscnt].techint = map->tech;
557 dr[anscnt].weight = map->weight;
558 dr[anscnt].expiration = dundi_cache_time;
559 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
560 dr[anscnt].eid = *us_eid;
561 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
562 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
563 AST_LIST_HEAD_INIT_NOLOCK(&headp);
564 newvariable = ast_var_assign("NUMBER", called_number);
565 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
566 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
567 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
568 newvariable = ast_var_assign("SECRET", cursecret);
569 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
570 newvariable = ast_var_assign("IPADDR", ipaddr);
571 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
572 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
573 while (!AST_LIST_EMPTY(&headp)) { /* List Deletion. */
574 newvariable = AST_LIST_REMOVE_HEAD(&headp, entries);
575 ast_var_delete(newvariable);
578 dr[anscnt].dest[0] = '\0';
581 /* No answers... Find the fewest number of digits from the
582 number for which we have no answer. */
583 char tmp[AST_MAX_EXTENSION];
584 for (x=0;x<AST_MAX_EXTENSION;x++) {
585 tmp[x] = called_number[x];
588 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
589 /* Oops found something we can't match. If this is longer
590 than the running hint, we have to consider it */
591 if (strlen(tmp) > strlen(hmd->exten)) {
592 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
602 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
604 static void *dundi_lookup_thread(void *data)
606 struct dundi_query_state *st = data;
607 struct dundi_result dr[MAX_RESULTS];
608 struct dundi_ie_data ied;
609 struct dundi_hint_metadata hmd;
614 int expiration = dundi_cache_time;
616 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
617 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
618 memset(&ied, 0, sizeof(ied));
619 memset(&dr, 0, sizeof(dr));
620 memset(&hmd, 0, sizeof(hmd));
621 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
622 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
623 for (x=0;x<st->nummaps;x++)
624 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
627 for (x=0;x<ouranswers;x++) {
628 if (dr[x].weight < max)
633 /* If we do not have a canonical result, keep looking */
634 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);
636 /* Append answer in result */
639 if ((res < -1) && (!ouranswers))
640 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
643 ast_mutex_lock(&peerlock);
644 /* Truncate if "don't ask" isn't present */
645 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
647 if (ast_test_flag(st->trans, FLAG_DEAD)) {
648 ast_log(LOG_DEBUG, "Our transaction went away!\n");
649 st->trans->thread = 0;
650 destroy_trans(st->trans, 0);
652 for (x=0;x<ouranswers;x++) {
654 if (dr[x].expiration && (expiration > dr[x].expiration))
655 expiration = dr[x].expiration;
656 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
658 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
659 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
660 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
661 st->trans->thread = 0;
663 ast_mutex_unlock(&peerlock);
668 static void *dundi_precache_thread(void *data)
670 struct dundi_query_state *st = data;
671 struct dundi_ie_data ied;
672 struct dundi_hint_metadata hmd;
675 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
676 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
677 memset(&ied, 0, sizeof(ied));
679 /* Now produce precache */
680 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
682 ast_mutex_lock(&peerlock);
683 /* Truncate if "don't ask" isn't present */
684 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
686 if (ast_test_flag(st->trans, FLAG_DEAD)) {
687 ast_log(LOG_DEBUG, "Our transaction went away!\n");
688 st->trans->thread = 0;
689 destroy_trans(st->trans, 0);
691 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
692 st->trans->thread = 0;
694 ast_mutex_unlock(&peerlock);
699 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[]);
701 static void *dundi_query_thread(void *data)
703 struct dundi_query_state *st = data;
704 struct dundi_entity_info dei;
705 struct dundi_ie_data ied;
706 struct dundi_hint_metadata hmd;
709 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
710 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
711 memset(&ied, 0, sizeof(ied));
712 memset(&dei, 0, sizeof(dei));
713 memset(&hmd, 0, sizeof(hmd));
714 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
716 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
717 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
718 ast_copy_string(dei.org, org, sizeof(dei.org));
719 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
720 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
721 ast_copy_string(dei.country, country, sizeof(dei.country));
722 ast_copy_string(dei.email, email, sizeof(dei.email));
723 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
726 /* If we do not have a canonical result, keep looking */
727 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
729 ast_mutex_lock(&peerlock);
730 if (ast_test_flag(st->trans, FLAG_DEAD)) {
731 ast_log(LOG_DEBUG, "Our transaction went away!\n");
732 st->trans->thread = 0;
733 destroy_trans(st->trans, 0);
736 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
737 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
738 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
739 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
740 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
741 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
742 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
743 if (!ast_strlen_zero(dei.ipaddr))
744 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
746 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
747 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
748 st->trans->thread = 0;
750 ast_mutex_unlock(&peerlock);
755 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
757 struct dundi_query_state *st;
761 struct dundi_ie_data ied;
764 pthread_t lookupthread;
766 if (ies->eidcount > 1) {
767 /* Since it is a requirement that the first EID is the authenticating host
768 and the last EID is the root, it is permissible that the first and last EID
769 could be the same. In that case, we should go ahead copy only the "root" section
770 since we will not need it for authentication. */
771 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
774 totallen = sizeof(struct dundi_query_state);
775 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
776 st = malloc(totallen);
778 memset(st, 0, totallen);
779 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
780 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
782 st->ttl = ies->ttl - 1;
786 for (x=skipfirst;ies->eids[x];x++) {
787 st->eids[x-skipfirst] = (dundi_eid *)s;
788 *st->eids[x-skipfirst] = *ies->eids[x];
789 s += sizeof(dundi_eid);
791 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);
792 pthread_attr_init(&attr);
793 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
795 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
797 ast_log(LOG_WARNING, "Unable to create thread!\n");
799 memset(&ied, 0, sizeof(ied));
800 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
801 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
805 ast_log(LOG_WARNING, "Out of memory!\n");
806 memset(&ied, 0, sizeof(ied));
807 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
808 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
814 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
819 char eidpeer_str[20];
820 char eidroot_str[20];
825 expiration = dundi_cache_time;
827 /* Only cache hint if "don't ask" is there... */
828 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
831 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
833 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
834 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
835 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
836 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
839 timeout += expiration;
840 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
842 ast_db_put("dundi/cache", key1, data);
843 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
844 ast_db_put("dundi/cache", key2, data);
845 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
849 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
855 char eidpeer_str[20];
856 char eidroot_str[20];
860 expiration = dundi_cache_time;
862 /* Keep pushes a little longer, cut pulls a little short */
869 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
870 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
871 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
872 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
873 /* Build request string */
875 timeout += expiration;
876 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
877 for (x=start;x<req->respcount;x++) {
878 /* Skip anything with an illegal pipe in it */
879 if (strchr(req->dr[x].dest, '|'))
881 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
882 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
883 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
885 ast_db_put("dundi/cache", key1, data);
886 ast_db_put("dundi/cache", key2, data);
890 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
892 struct dundi_query_state *st;
895 struct dundi_ie_data ied;
897 struct dundi_result dr2[MAX_RESULTS];
898 struct dundi_request dr;
899 struct dundi_hint_metadata hmd;
901 struct dundi_mapping *cur;
905 pthread_t lookupthread;
908 memset(&dr2, 0, sizeof(dr2));
909 memset(&dr, 0, sizeof(dr));
910 memset(&hmd, 0, sizeof(hmd));
912 /* Forge request structure to hold answers for cache */
913 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
915 dr.maxcount = MAX_RESULTS;
916 dr.expiration = dundi_cache_time;
918 dr.pfds[0] = dr.pfds[1] = -1;
920 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
921 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
923 for (x=0;x<ies->anscount;x++) {
924 if (trans->parent->respcount < trans->parent->maxcount) {
925 /* Make sure it's not already there */
926 for (z=0;z<trans->parent->respcount;z++) {
927 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
928 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
931 if (z == trans->parent->respcount) {
932 /* Copy into parent responses */
933 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
934 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
935 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
936 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
937 if (ies->expiration > 0)
938 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
940 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
941 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
942 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
943 &ies->answers[x]->eid);
944 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
945 sizeof(trans->parent->dr[trans->parent->respcount].dest));
946 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
947 sizeof(trans->parent->dr[trans->parent->respcount].tech));
948 trans->parent->respcount++;
949 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
950 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
951 /* Update weight if appropriate */
952 trans->parent->dr[z].weight = ies->answers[x]->weight;
955 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
956 trans->parent->number, trans->parent->dcontext);
959 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
960 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
962 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
964 totallen = sizeof(struct dundi_query_state);
965 /* Count matching map entries */
969 if (!strcasecmp(cur->dcontext, ccontext))
974 /* If no maps, return -1 immediately */
978 if (ies->eidcount > 1) {
979 /* Since it is a requirement that the first EID is the authenticating host
980 and the last EID is the root, it is permissible that the first and last EID
981 could be the same. In that case, we should go ahead copy only the "root" section
982 since we will not need it for authentication. */
983 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
987 /* Prepare to run a query and then propagate that as necessary */
988 totallen += mapcount * sizeof(struct dundi_mapping);
989 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
990 st = malloc(totallen);
992 memset(st, 0, totallen);
993 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
994 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
996 st->ttl = ies->ttl - 1;
997 st->nocache = ies->cbypass;
1001 for (x=skipfirst;ies->eids[x];x++) {
1002 st->eids[x-skipfirst] = (dundi_eid *)s;
1003 *st->eids[x-skipfirst] = *ies->eids[x];
1004 st->directs[x-skipfirst] = ies->eid_direct[x];
1005 s += sizeof(dundi_eid);
1007 /* Append mappings */
1009 st->maps = (struct dundi_mapping *)s;
1012 if (!strcasecmp(cur->dcontext, ccontext)) {
1015 st->maps[x].next = NULL;
1021 st->nummaps = mapcount;
1022 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1023 pthread_attr_init(&attr);
1024 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1026 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1028 ast_log(LOG_WARNING, "Unable to create thread!\n");
1030 memset(&ied, 0, sizeof(ied));
1031 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1032 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1036 ast_log(LOG_WARNING, "Out of memory!\n");
1037 memset(&ied, 0, sizeof(ied));
1038 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1039 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1045 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1047 struct dundi_query_state *st;
1050 struct dundi_ie_data ied;
1052 struct dundi_mapping *cur;
1056 pthread_t lookupthread;
1057 pthread_attr_t attr;
1058 totallen = sizeof(struct dundi_query_state);
1059 /* Count matching map entries */
1063 if (!strcasecmp(cur->dcontext, ccontext))
1067 /* If no maps, return -1 immediately */
1071 if (ies->eidcount > 1) {
1072 /* Since it is a requirement that the first EID is the authenticating host
1073 and the last EID is the root, it is permissible that the first and last EID
1074 could be the same. In that case, we should go ahead copy only the "root" section
1075 since we will not need it for authentication. */
1076 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1080 totallen += mapcount * sizeof(struct dundi_mapping);
1081 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1082 st = malloc(totallen);
1084 memset(st, 0, totallen);
1085 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1086 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1088 st->ttl = ies->ttl - 1;
1089 st->nocache = ies->cbypass;
1093 for (x=skipfirst;ies->eids[x];x++) {
1094 st->eids[x-skipfirst] = (dundi_eid *)s;
1095 *st->eids[x-skipfirst] = *ies->eids[x];
1096 st->directs[x-skipfirst] = ies->eid_direct[x];
1097 s += sizeof(dundi_eid);
1099 /* Append mappings */
1101 st->maps = (struct dundi_mapping *)s;
1104 if (!strcasecmp(cur->dcontext, ccontext)) {
1107 st->maps[x].next = NULL;
1113 st->nummaps = mapcount;
1114 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1115 pthread_attr_init(&attr);
1116 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1118 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1120 ast_log(LOG_WARNING, "Unable to create thread!\n");
1122 memset(&ied, 0, sizeof(ied));
1123 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1124 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1128 ast_log(LOG_WARNING, "Out of memory!\n");
1129 memset(&ied, 0, sizeof(ied));
1130 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1131 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1137 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1140 char *ptr, *term, *src;
1142 struct ast_flags flags;
1149 /* Build request string */
1150 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1152 if (sscanf(ptr, "%d|%n", (int *)&timeout, &length) == 1) {
1153 expiration = timeout - now;
1154 if (expiration > 0) {
1155 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
1157 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1159 term = strchr(ptr, '|');
1162 src = strrchr(ptr, '/');
1168 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1169 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1170 /* Make sure it's not already there */
1171 for (z=0;z<req->respcount;z++) {
1172 if ((req->dr[z].techint == tech) &&
1173 !strcmp(req->dr[z].dest, ptr))
1176 if (z == req->respcount) {
1177 /* Copy into parent responses */
1178 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1179 req->dr[req->respcount].weight = weight;
1180 req->dr[req->respcount].techint = tech;
1181 req->dr[req->respcount].expiration = expiration;
1182 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1183 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1184 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1185 ast_copy_string(req->dr[req->respcount].dest, ptr,
1186 sizeof(req->dr[req->respcount].dest));
1187 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1188 sizeof(req->dr[req->respcount].tech));
1190 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1191 } else if (req->dr[z].weight > weight)
1192 req->dr[z].weight = weight;
1196 /* We found *something* cached */
1197 if (expiration < *lowexpiration)
1198 *lowexpiration = expiration;
1201 ast_db_del("dundi/cache", key);
1203 ast_db_del("dundi/cache", key);
1209 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1213 char eidroot_str[20];
1217 char eid_str_full[20];
1222 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1223 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1224 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1225 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1226 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1227 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1228 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1229 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1230 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1232 if (!req->respcount) {
1234 /* Look and see if we have a hint that would preclude us from looking at this
1235 peer for this number. */
1236 if (!(tmp[x] = req->number[x]))
1239 /* Check for hints */
1240 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1241 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1242 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1243 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1244 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1245 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1247 if (strlen(tmp) > strlen(req->hmd->exten)) {
1248 /* Update meta data if appropriate */
1249 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1259 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1261 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1263 if (!trans->addr.sin_addr.s_addr)
1264 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1265 trans->us_eid = p->us_eid;
1266 trans->them_eid = p->eid;
1267 /* Enable encryption if appropriate */
1268 if (!ast_strlen_zero(p->inkey))
1269 ast_set_flag(trans, FLAG_ENCRYPT);
1271 trans->autokilltimeout = p->maxms;
1272 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1273 if (p->lastms > 1) {
1274 trans->retranstimer = p->lastms * 2;
1275 /* Keep it from being silly */
1276 if (trans->retranstimer < 150)
1277 trans->retranstimer = 150;
1279 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1280 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1282 trans->autokilltimeout = global_autokilltimeout;
1285 static int do_register_expire(void *data)
1287 struct dundi_peer *peer = data;
1289 /* Called with peerlock already held */
1290 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1291 peer->registerexpire = -1;
1293 memset(&peer->addr, 0, sizeof(peer->addr));
1297 static int update_key(struct dundi_peer *peer)
1299 unsigned char key[16];
1300 struct ast_key *ekey, *skey;
1303 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1305 aes_encrypt_key128(key, &peer->us_ecx);
1306 aes_decrypt_key128(key, &peer->us_dcx);
1307 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1309 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1310 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1313 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1315 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1316 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1319 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1320 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1323 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1324 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1327 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1328 peer->sentfullkey = 0;
1330 time(&peer->keyexpire);
1331 peer->keyexpire += dundi_key_ttl;
1336 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1338 unsigned char curblock[16];
1340 memcpy(curblock, iv, sizeof(curblock));
1343 curblock[x] ^= src[x];
1344 aes_encrypt(curblock, dst, ecx);
1345 memcpy(curblock, dst, sizeof(curblock));
1352 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1354 unsigned char lastblock[16];
1356 memcpy(lastblock, iv, sizeof(lastblock));
1358 aes_decrypt(src, dst, dcx);
1360 dst[x] ^= lastblock[x];
1361 memcpy(lastblock, src, sizeof(lastblock));
1369 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)
1371 int space = *dstlen;
1372 unsigned long bytes;
1373 struct dundi_hdr *h;
1374 unsigned char *decrypt_space;
1375 decrypt_space = alloca(srclen);
1378 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1380 h = (struct dundi_hdr *)dst;
1383 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1384 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1388 *dstlen = bytes + 6;
1389 /* Return new header */
1393 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1395 unsigned char *compress_space;
1398 unsigned long bytes;
1399 struct dundi_ie_data ied;
1400 struct dundi_peer *peer;
1401 unsigned char iv[16];
1402 len = pack->datalen + pack->datalen / 100 + 42;
1403 compress_space = alloca(len);
1404 if (compress_space) {
1405 memset(compress_space, 0, len);
1406 /* We care about everthing save the first 6 bytes of header */
1408 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1410 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1413 memset(&ied, 0, sizeof(ied));
1414 /* Say who we are */
1415 if (!pack->h->iseqno && !pack->h->oseqno) {
1416 /* Need the key in the first copy */
1417 if (!(peer = find_peer(&trans->them_eid)))
1419 if (update_key(peer))
1421 if (!peer->sentfullkey)
1422 ast_set_flag(trans, FLAG_SENDFULLKEY);
1423 /* Append key data */
1424 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1425 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1426 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1427 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1429 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1431 /* Setup contexts */
1432 trans->ecx = peer->us_ecx;
1433 trans->dcx = peer->us_dcx;
1435 /* We've sent the full key */
1436 peer->sentfullkey = 1;
1438 /* Build initialization vector */
1440 /* Add the field, rounded up to 16 bytes */
1441 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1443 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1444 ast_log(LOG_NOTICE, "Final packet too large!\n");
1447 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1448 ied.pos += ((bytes + 15) / 16) * 16;
1449 /* Reconstruct header */
1450 pack->datalen = sizeof(struct dundi_hdr);
1451 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1452 pack->h->cmdflags = 0;
1453 memcpy(pack->h->ies, ied.buf, ied.pos);
1454 pack->datalen += ied.pos;
1460 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1462 unsigned char dst[128];
1464 struct ast_key *key, *skey;
1467 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1468 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1471 } else if (!newkey || !newsig)
1473 if (!memcmp(peer->rxenckey, newkey, 128) &&
1474 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1475 /* By definition, a match */
1479 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1481 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1482 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1486 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1488 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1489 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1493 /* First check signature */
1494 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1498 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1501 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1504 /* Decrypted, passes signature */
1505 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1506 memcpy(peer->rxenckey, newkey, 128);
1507 memcpy(peer->rxenckey + 128, newsig, 128);
1508 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1509 aes_decrypt_key128(dst, &peer->them_dcx);
1510 aes_encrypt_key128(dst, &peer->them_ecx);
1514 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1516 /* Handle canonical command / response */
1517 int final = hdr->cmdresp & 0x80;
1518 int cmd = hdr->cmdresp & 0x7f;
1523 unsigned char *bufcpy;
1524 struct dundi_ie_data ied;
1525 struct dundi_ies ies;
1526 struct dundi_peer *peer;
1529 memset(&ied, 0, sizeof(ied));
1530 memset(&ies, 0, sizeof(ies));
1532 bufcpy = alloca(datalen);
1535 /* Make a copy for parsing */
1536 memcpy(bufcpy, hdr->ies, datalen);
1537 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1538 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1539 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1544 case DUNDI_COMMAND_DPDISCOVER:
1545 case DUNDI_COMMAND_EIDQUERY:
1546 case DUNDI_COMMAND_PRECACHERQ:
1547 if (cmd == DUNDI_COMMAND_EIDQUERY)
1548 resp = DUNDI_COMMAND_EIDRESPONSE;
1549 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1550 resp = DUNDI_COMMAND_PRECACHERP;
1552 resp = DUNDI_COMMAND_DPRESPONSE;
1553 /* A dialplan or entity discover -- qualify by highest level entity */
1554 peer = find_peer(ies.eids[0]);
1556 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1557 dundi_send(trans, resp, 0, 1, &ied);
1560 trans->us_eid = peer->us_eid;
1561 if (strlen(peer->inkey)) {
1562 hasauth = encrypted;
1566 /* Okay we're authentiated and all, now we check if they're authorized */
1567 if (!ies.called_context)
1568 ies.called_context = "e164";
1569 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1570 res = dundi_answer_entity(trans, &ies, ies.called_context);
1572 if (ast_strlen_zero(ies.called_number)) {
1573 /* They're not permitted to access that context */
1574 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1575 dundi_send(trans, resp, 0, 1, &ied);
1576 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1577 (peer->model & DUNDI_MODEL_INBOUND) &&
1578 has_permission(peer->permit, ies.called_context)) {
1579 res = dundi_answer_query(trans, &ies, ies.called_context);
1581 /* There is no such dundi context */
1582 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1583 dundi_send(trans, resp, 0, 1, &ied);
1585 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1586 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1587 has_permission(peer->include, ies.called_context)) {
1588 res = dundi_prop_precache(trans, &ies, ies.called_context);
1590 /* There is no such dundi context */
1591 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1592 dundi_send(trans, resp, 0, 1, &ied);
1595 /* They're not permitted to access that context */
1596 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1597 dundi_send(trans, resp, 0, 1, &ied);
1601 /* They're not permitted to access that context */
1602 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1603 dundi_send(trans, resp, 0, 1, &ied);
1607 case DUNDI_COMMAND_REGREQ:
1608 /* A register request -- should only have one entity */
1609 peer = find_peer(ies.eids[0]);
1610 if (!peer || !peer->dynamic) {
1611 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1612 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1615 trans->us_eid = peer->us_eid;
1616 if (!ast_strlen_zero(peer->inkey)) {
1617 hasauth = encrypted;
1621 int expire = default_expiration;
1622 char iabuf[INET_ADDRSTRLEN];
1625 if (peer->registerexpire > -1)
1626 ast_sched_del(sched, peer->registerexpire);
1627 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1628 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1629 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1630 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1631 if (inaddrcmp(&peer->addr, &trans->addr)) {
1632 if (option_verbose > 2)
1633 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));
1637 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1638 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1639 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1641 qualify_peer(peer, 1);
1645 case DUNDI_COMMAND_DPRESPONSE:
1646 /* A dialplan response, lets see what we got... */
1647 if (ies.cause < 1) {
1648 /* Success of some sort */
1649 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1650 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1651 authpass = encrypted;
1655 /* Pass back up answers */
1656 if (trans->parent && trans->parent->dr) {
1657 y = trans->parent->respcount;
1658 for (x=0;x<ies.anscount;x++) {
1659 if (trans->parent->respcount < trans->parent->maxcount) {
1660 /* Make sure it's not already there */
1661 for (z=0;z<trans->parent->respcount;z++) {
1662 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1663 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1666 if (z == trans->parent->respcount) {
1667 /* Copy into parent responses */
1668 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1669 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1670 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1671 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1672 if (ies.expiration > 0)
1673 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1675 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1676 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1677 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1678 &ies.answers[x]->eid);
1679 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1680 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1681 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1682 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1683 trans->parent->respcount++;
1684 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1685 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1686 /* Update weight if appropriate */
1687 trans->parent->dr[z].weight = ies.answers[x]->weight;
1690 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1691 trans->parent->number, trans->parent->dcontext);
1693 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1694 the cache know if this request was unaffected by our entity list. */
1695 cache_save(&trans->them_eid, trans->parent, y,
1696 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1698 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1699 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1700 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1701 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1702 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1703 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1704 sizeof(trans->parent->hmd->exten));
1707 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1710 if (ies.expiration > 0) {
1711 if (trans->parent->expiration > ies.expiration) {
1712 trans->parent->expiration = ies.expiration;
1716 /* Close connection if not final */
1718 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1722 /* Auth failure, check for data */
1724 /* Cancel if they didn't already */
1725 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1729 case DUNDI_COMMAND_EIDRESPONSE:
1730 /* A dialplan response, lets see what we got... */
1731 if (ies.cause < 1) {
1732 /* Success of some sort */
1733 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1734 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1735 authpass = encrypted;
1739 /* Pass back up answers */
1740 if (trans->parent && trans->parent->dei && ies.q_org) {
1741 if (!trans->parent->respcount) {
1742 trans->parent->respcount++;
1744 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1746 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1748 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1749 if (ies.q_stateprov)
1750 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1752 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1754 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1756 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1758 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1759 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1760 /* If it's them, update our address */
1761 ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1762 trans->addr.sin_addr);
1766 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1767 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1770 /* Close connection if not final */
1772 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1776 /* Auth failure, check for data */
1778 /* Cancel if they didn't already */
1779 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1783 case DUNDI_COMMAND_REGRESPONSE:
1784 /* A dialplan response, lets see what we got... */
1785 if (ies.cause < 1) {
1787 /* Success of some sort */
1788 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1789 hasauth = encrypted;
1794 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1796 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1797 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1800 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),
1801 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1802 /* Close connection if not final */
1804 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1807 /* Auth failure, cancel if they didn't for some reason */
1809 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1813 case DUNDI_COMMAND_INVALID:
1814 case DUNDI_COMMAND_NULL:
1815 case DUNDI_COMMAND_PRECACHERP:
1816 /* Do nothing special */
1818 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1820 case DUNDI_COMMAND_ENCREJ:
1821 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
1822 /* No really, it's over at this point */
1824 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1826 /* Send with full key */
1827 ast_set_flag(trans, FLAG_SENDFULLKEY);
1829 /* Ooops, we got a final message, start by sending ACK... */
1830 dundi_ack(trans, hdr->cmdresp & 0x80);
1831 trans->aseqno = trans->iseqno;
1832 /* Now, we gotta create a new transaction */
1833 if (!reset_transaction(trans)) {
1834 /* Make sure handle_frame doesn't destroy us */
1835 hdr->cmdresp &= 0x7f;
1836 /* Parse the message we transmitted */
1837 memset(&ies, 0, sizeof(ies));
1838 dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
1839 /* Reconstruct outgoing encrypted packet */
1840 memset(&ied, 0, sizeof(ied));
1841 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1842 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1843 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1845 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1846 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
1847 peer->sentfullkey = 1;
1852 case DUNDI_COMMAND_ENCRYPT:
1854 /* No nested encryption! */
1855 if ((trans->iseqno == 1) && !trans->oseqno) {
1856 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1857 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1858 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1860 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1864 apply_peer(trans, peer);
1865 /* Key passed, use new contexts for this session */
1866 trans->ecx = peer->them_ecx;
1867 trans->dcx = peer->them_dcx;
1869 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1870 struct dundi_hdr *dhdr;
1871 unsigned char decoded[MAX_PACKET_SIZE];
1873 ddatalen = sizeof(decoded);
1874 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1876 /* Handle decrypted response */
1878 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1879 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1880 /* Carry back final flag */
1881 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1884 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1888 /* Turn off encryption */
1889 ast_clear_flag(trans, FLAG_ENCRYPT);
1890 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1894 /* Send unknown command if we don't know it, with final flag IFF it's the
1895 first command in the dialog and only if we haven't recieved final notification */
1897 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1898 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1904 static void destroy_packet(struct dundi_packet *pack, int needfree);
1905 static void destroy_packets(struct dundi_packet *p)
1907 struct dundi_packet *prev;
1911 if (prev->retransid > -1)
1912 ast_sched_del(sched, prev->retransid);
1918 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1920 /* Ack transmitted packet corresponding to iseqno */
1921 struct dundi_packet *pack;
1922 pack = trans->packets;
1924 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1925 destroy_packet(pack, 0);
1926 if (trans->lasttrans) {
1927 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1928 destroy_packets(trans->lasttrans);
1930 trans->lasttrans = pack;
1931 if (trans->autokillid > -1)
1932 ast_sched_del(sched, trans->autokillid);
1933 trans->autokillid = -1;
1941 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1943 struct dundi_transaction *trans;
1944 trans = find_transaction(h, sin);
1946 dundi_reject(h, sin);
1949 /* Got a transaction, see where this header fits in */
1950 if (h->oseqno == trans->iseqno) {
1951 /* Just what we were looking for... Anything but ack increments iseqno */
1952 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1953 /* If final, we're done */
1954 destroy_trans(trans, 0);
1957 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1958 trans->oiseqno = trans->iseqno;
1960 handle_command_response(trans, h, datalen, 0);
1962 if (trans->aseqno != trans->iseqno) {
1963 dundi_ack(trans, h->cmdresp & 0x80);
1964 trans->aseqno = trans->iseqno;
1966 /* Delete any saved last transmissions */
1967 destroy_packets(trans->lasttrans);
1968 trans->lasttrans = NULL;
1969 if (h->cmdresp & 0x80) {
1970 /* Final -- destroy now */
1971 destroy_trans(trans, 0);
1973 } else if (h->oseqno == trans->oiseqno) {
1974 /* Last incoming sequence number -- send ACK without processing */
1975 dundi_ack(trans, 0);
1977 /* Out of window -- simply drop */
1978 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1983 static int socket_read(int *id, int fd, short events, void *cbdata)
1985 struct sockaddr_in sin;
1987 struct dundi_hdr *h;
1988 char buf[MAX_PACKET_SIZE];
1991 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1993 if (errno != ECONNREFUSED)
1994 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1997 if (res < sizeof(struct dundi_hdr)) {
1998 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2002 h = (struct dundi_hdr *)buf;
2004 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2005 ast_mutex_lock(&peerlock);
2006 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2007 ast_mutex_unlock(&peerlock);
2011 static void build_secret(char *secret, int seclen)
2013 unsigned char tmp[16];
2017 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2018 /* Eliminate potential bad characters */
2019 while((s = strchr(secret, ';'))) *s = '+';
2020 while((s = strchr(secret, '/'))) *s = '+';
2021 while((s = strchr(secret, ':'))) *s = '+';
2022 while((s = strchr(secret, '@'))) *s = '+';
2026 static void save_secret(const char *newkey, const char *oldkey)
2030 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2032 snprintf(tmp, sizeof(tmp), "%s", newkey);
2033 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2034 ast_db_put(secretpath, "secret", tmp);
2035 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2036 ast_db_put(secretpath, "secretexpiry", tmp);
2039 static void load_password(void)
2046 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2047 if (sscanf(tmp, "%d", (int *)&expired) == 1) {
2048 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2049 current = strchr(tmp, ';');
2056 if ((time(NULL) - expired) < 0) {
2057 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2058 expired = time(NULL) + DUNDI_SECRET_TIME;
2059 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2068 /* Current key is still valid, just setup rotatation properly */
2069 ast_copy_string(cursecret, current, sizeof(cursecret));
2070 rotatetime = expired;
2072 /* Current key is out of date, rotate or eliminate all together */
2073 build_secret(cursecret, sizeof(cursecret));
2074 save_secret(cursecret, last);
2078 static void check_password(void)
2085 printf("%ld/%ld\n", now, rotatetime);
2087 if ((now - rotatetime) >= 0) {
2088 /* Time to rotate keys */
2089 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2090 build_secret(cursecret, sizeof(cursecret));
2091 save_secret(cursecret, oldsecret);
2095 static void *network_thread(void *ignore)
2097 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2098 from the network, and queue them for delivery to the channels */
2100 /* Establish I/O callback for socket read */
2101 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2103 res = ast_sched_wait(sched);
2104 if ((res > 1000) || (res < 0))
2106 res = ast_io_wait(io, res);
2108 ast_mutex_lock(&peerlock);
2109 ast_sched_runq(sched);
2110 ast_mutex_unlock(&peerlock);
2117 static void *process_precache(void *ign)
2119 struct dundi_precache_queue *qe;
2127 ast_mutex_lock(&pclock);
2129 if (!pcq->expiration) {
2130 /* Gone... Remove... */
2134 } else if (pcq->expiration < now) {
2135 /* Process this entry */
2136 pcq->expiration = 0;
2137 ast_copy_string(context, pcq->context, sizeof(context));
2138 ast_copy_string(number, pcq->number, sizeof(number));
2142 ast_mutex_unlock(&pclock);
2144 dundi_precache(context, number);
2151 static int start_network_thread(void)
2153 ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
2154 ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
2158 static int dundi_do_debug(int fd, int argc, char *argv[])
2161 return RESULT_SHOWUSAGE;
2163 ast_cli(fd, "DUNDi Debugging Enabled\n");
2164 return RESULT_SUCCESS;
2167 static int dundi_do_store_history(int fd, int argc, char *argv[])
2170 return RESULT_SHOWUSAGE;
2171 global_storehistory = 1;
2172 ast_cli(fd, "DUNDi History Storage Enabled\n");
2173 return RESULT_SUCCESS;
2176 static int dundi_flush(int fd, int argc, char *argv[])
2179 if ((argc < 2) || (argc > 3))
2180 return RESULT_SHOWUSAGE;
2182 if (!strcasecmp(argv[2], "stats"))
2185 return RESULT_SHOWUSAGE;
2188 /* Flush statistics */
2189 struct dundi_peer *p;
2191 ast_mutex_lock(&peerlock);
2194 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2196 free(p->lookups[x]);
2197 p->lookups[x] = NULL;
2198 p->lookuptimes[x] = 0;
2203 ast_mutex_unlock(&peerlock);
2205 ast_db_deltree("dundi/cache", NULL);
2206 ast_cli(fd, "DUNDi Cache Flushed\n");
2208 return RESULT_SUCCESS;
2211 static int dundi_no_debug(int fd, int argc, char *argv[])
2214 return RESULT_SHOWUSAGE;
2216 ast_cli(fd, "DUNDi Debugging Disabled\n");
2217 return RESULT_SUCCESS;
2220 static int dundi_no_store_history(int fd, int argc, char *argv[])
2223 return RESULT_SHOWUSAGE;
2224 global_storehistory = 0;
2225 ast_cli(fd, "DUNDi History Storage Disabled\n");
2226 return RESULT_SUCCESS;
2229 static char *model2str(int model)
2232 case DUNDI_MODEL_INBOUND:
2234 case DUNDI_MODEL_OUTBOUND:
2236 case DUNDI_MODEL_SYMMETRIC:
2243 static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
2247 struct dundi_peer *p;
2251 ast_mutex_lock(&peerlock);
2254 if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
2255 if (++which > state)
2261 ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
2264 ast_mutex_unlock(&peerlock);
2268 static char *complete_peer_4(char *line, char *word, int pos, int state)
2270 return complete_peer_helper(line, word, pos, state, 3);
2273 static int rescomp(const void *a, const void *b)
2275 const struct dundi_result *resa, *resb;
2278 if (resa->weight < resb->weight)
2280 if (resa->weight > resb->weight)
2285 static void sort_results(struct dundi_result *results, int count)
2287 qsort(results, count, sizeof(results[0]), rescomp);
2290 static int dundi_do_lookup(int fd, int argc, char *argv[])
2298 struct dundi_result dr[MAX_RESULTS];
2299 struct timeval start;
2300 if ((argc < 3) || (argc > 4))
2301 return RESULT_SHOWUSAGE;
2303 if (!strcasecmp(argv[3], "bypass"))
2306 return RESULT_SHOWUSAGE;
2308 ast_copy_string(tmp, argv[2], sizeof(tmp));
2309 context = strchr(tmp, '@');
2314 start = ast_tvnow();
2315 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2318 ast_cli(fd, "DUNDi lookup returned error.\n");
2320 ast_cli(fd, "DUNDi lookup returned no results.\n");
2322 sort_results(dr, res);
2323 for (x=0;x<res;x++) {
2324 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));
2325 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2327 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2328 return RESULT_SUCCESS;
2331 static int dundi_do_precache(int fd, int argc, char *argv[])
2336 struct timeval start;
2337 if ((argc < 3) || (argc > 3))
2338 return RESULT_SHOWUSAGE;
2339 ast_copy_string(tmp, argv[2], sizeof(tmp));
2340 context = strchr(tmp, '@');
2345 start = ast_tvnow();
2346 res = dundi_precache(context, tmp);
2349 ast_cli(fd, "DUNDi precache returned error.\n");
2351 ast_cli(fd, "DUNDi precache returned no error.\n");
2352 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2353 return RESULT_SUCCESS;
2356 static int dundi_do_query(int fd, int argc, char *argv[])
2362 struct dundi_entity_info dei;
2363 if ((argc < 3) || (argc > 3))
2364 return RESULT_SHOWUSAGE;
2365 if (dundi_str_to_eid(&eid, argv[2])) {
2366 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2367 return RESULT_SHOWUSAGE;
2369 ast_copy_string(tmp, argv[2], sizeof(tmp));
2370 context = strchr(tmp, '@');
2375 res = dundi_query_eid(&dei, context, eid);
2377 ast_cli(fd, "DUNDi Query EID returned error.\n");
2379 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2381 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2382 ast_cli(fd, "Department: %s\n", dei.orgunit);
2383 ast_cli(fd, "Organization: %s\n", dei.org);
2384 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2385 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2386 ast_cli(fd, "Country: %s\n", dei.country);
2387 ast_cli(fd, "E-mail: %s\n", dei.email);
2388 ast_cli(fd, "Phone: %s\n", dei.phone);
2389 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2391 return RESULT_SUCCESS;
2394 static int dundi_show_peer(int fd, int argc, char *argv[])
2396 struct dundi_peer *peer;
2397 struct permission *p;
2399 char iabuf[INET_ADDRSTRLEN];
2404 return RESULT_SHOWUSAGE;
2405 ast_mutex_lock(&peerlock);
2408 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2413 switch(peer->order) {
2418 order = "Secondary";
2424 order = "Quartiary";
2429 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2430 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2431 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2432 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2433 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2434 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2435 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2436 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2437 if (peer->include) {
2438 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2442 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2446 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2450 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2454 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2455 if (peer->lookups[x]) {
2457 ast_cli(fd, "Last few query times:\n");
2458 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2463 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2465 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2466 ast_mutex_unlock(&peerlock);
2467 return RESULT_SUCCESS;
2470 static int dundi_show_peers(int fd, int argc, char *argv[])
2472 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2473 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2474 struct dundi_peer *peer;
2475 char iabuf[INET_ADDRSTRLEN];
2476 int registeredonly=0;
2479 int online_peers = 0;
2480 int offline_peers = 0;
2481 int unmonitored_peers = 0;
2482 int total_peers = 0;
2484 if ((argc != 3) && (argc != 4) && (argc != 5))
2485 return RESULT_SHOWUSAGE;
2487 if (!strcasecmp(argv[3], "registered")) {
2490 return RESULT_SHOWUSAGE;
2492 ast_mutex_lock(&peerlock);
2493 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2494 for (peer = peers;peer;peer = peer->next) {
2496 int print_line = -1;
2499 if (registeredonly && !peer->addr.sin_addr.s_addr)
2502 if (peer->lastms < 0) {
2503 strcpy(status, "UNREACHABLE");
2506 else if (peer->lastms > peer->maxms) {
2507 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2510 else if (peer->lastms) {
2511 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2515 strcpy(status, "UNKNOWN");
2519 strcpy(status, "Unmonitored");
2520 unmonitored_peers++;
2523 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2525 strcpy(avgms, "Unavail");
2526 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2527 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2528 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2531 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2533 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2535 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2543 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2544 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2545 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2548 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2549 ast_mutex_unlock(&peerlock);
2550 return RESULT_SUCCESS;
2555 static int dundi_show_trans(int fd, int argc, char *argv[])
2557 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2558 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2559 struct dundi_transaction *trans;
2560 char iabuf[INET_ADDRSTRLEN];
2562 return RESULT_SHOWUSAGE;
2563 ast_mutex_lock(&peerlock);
2564 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2565 for (trans = alltrans;trans;trans = trans->allnext) {
2566 ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
2567 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2569 ast_mutex_unlock(&peerlock);
2570 return RESULT_SUCCESS;
2575 static int dundi_show_entityid(int fd, int argc, char *argv[])
2579 return RESULT_SHOWUSAGE;
2580 ast_mutex_lock(&peerlock);
2581 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2582 ast_mutex_unlock(&peerlock);
2583 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2584 return RESULT_SUCCESS;
2587 static int dundi_show_requests(int fd, int argc, char *argv[])
2589 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2590 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2591 struct dundi_request *req;
2594 return RESULT_SHOWUSAGE;
2595 ast_mutex_lock(&peerlock);
2596 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2597 for (req = requests;req;req = req->next) {
2598 ast_cli(fd, FORMAT, req->number, req->dcontext,
2599 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2601 ast_mutex_unlock(&peerlock);
2602 return RESULT_SUCCESS;
2607 /* Grok-a-dial DUNDi */
2609 static int dundi_show_mappings(int fd, int argc, char *argv[])
2611 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2612 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2613 struct dundi_mapping *map;
2616 return RESULT_SHOWUSAGE;
2617 ast_mutex_lock(&peerlock);
2618 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2619 for (map = mappings;map;map = map->next) {
2620 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2621 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2622 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2624 ast_mutex_unlock(&peerlock);
2625 return RESULT_SUCCESS;
2630 static int dundi_show_precache(int fd, int argc, char *argv[])
2632 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2633 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2634 struct dundi_precache_queue *qe;
2639 return RESULT_SHOWUSAGE;
2641 ast_mutex_lock(&pclock);
2642 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2643 for (qe = pcq;qe;qe = qe->next) {
2644 s = qe->expiration - now;
2649 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2651 ast_mutex_unlock(&pclock);
2652 return RESULT_SUCCESS;
2657 static char debug_usage[] =
2658 "Usage: dundi debug\n"
2659 " Enables dumping of DUNDi packets for debugging purposes\n";
2661 static char no_debug_usage[] =
2662 "Usage: dundi no debug\n"
2663 " Disables dumping of DUNDi packets for debugging purposes\n";
2665 static char store_history_usage[] =
2666 "Usage: dundi store history\n"
2667 " Enables storing of DUNDi requests and times for debugging\n"
2670 static char no_store_history_usage[] =
2671 "Usage: dundi no store history\n"
2672 " Disables storing of DUNDi requests and times for debugging\n"
2675 static char show_peers_usage[] =
2676 "Usage: dundi show peers\n"
2677 " Lists all known DUNDi peers.\n";
2679 static char show_trans_usage[] =
2680 "Usage: dundi show trans\n"
2681 " Lists all known DUNDi transactions.\n";
2683 static char show_mappings_usage[] =
2684 "Usage: dundi show mappings\n"
2685 " Lists all known DUNDi mappings.\n";
2687 static char show_precache_usage[] =
2688 "Usage: dundi show precache\n"
2689 " Lists all known DUNDi scheduled precache updates.\n";
2691 static char show_entityid_usage[] =
2692 "Usage: dundi show entityid\n"
2693 " Displays the global entityid for this host.\n";
2695 static char show_peer_usage[] =
2696 "Usage: dundi show peer [peer]\n"
2697 " Provide a detailed description of a specifid DUNDi peer.\n";
2699 static char show_requests_usage[] =
2700 "Usage: dundi show requests\n"
2701 " Lists all known pending DUNDi requests.\n";
2703 static char lookup_usage[] =
2704 "Usage: dundi lookup <number>[@context] [bypass]\n"
2705 " Lookup the given number within the given DUNDi context\n"
2706 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2707 "keyword is specified.\n";
2709 static char precache_usage[] =
2710 "Usage: dundi precache <number>[@context]\n"
2711 " Lookup the given number within the given DUNDi context\n"
2712 "(or e164 if none is specified) and precaches the results to any\n"
2713 "upstream DUNDi push servers.\n";
2715 static char query_usage[] =
2716 "Usage: dundi query <entity>[@context]\n"
2717 " Attempts to retrieve contact information for a specific\n"
2718 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2719 "e164 if none is specified).\n";
2721 static char flush_usage[] =
2722 "Usage: dundi flush [stats]\n"
2723 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2724 "'stats' is present, clears timer statistics instead of normal\n"
2727 static struct ast_cli_entry cli_debug =
2728 { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2730 static struct ast_cli_entry cli_store_history =
2731 { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2733 static struct ast_cli_entry cli_no_store_history =
2734 { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2736 static struct ast_cli_entry cli_flush =
2737 { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2739 static struct ast_cli_entry cli_no_debug =
2740 { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2742 static struct ast_cli_entry cli_show_peers =
2743 { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2745 static struct ast_cli_entry cli_show_trans =
2746 { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2748 static struct ast_cli_entry cli_show_entityid =
2749 { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2751 static struct ast_cli_entry cli_show_mappings =
2752 { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2754 static struct ast_cli_entry cli_show_precache =
2755 { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2757 static struct ast_cli_entry cli_show_requests =
2758 { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2760 static struct ast_cli_entry cli_show_peer =
2761 { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2763 static struct ast_cli_entry cli_lookup =
2764 { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2766 static struct ast_cli_entry cli_precache =
2767 { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2769 static struct ast_cli_entry cli_queryeid =
2770 { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2772 STANDARD_LOCAL_USER;
2776 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2778 struct dundi_transaction *trans;
2781 /* Don't allow creation of transactions to non-registered peers */
2782 if (p && !p->addr.sin_addr.s_addr)
2784 tid = get_trans_id();
2787 trans = malloc(sizeof(struct dundi_transaction));
2789 memset(trans, 0, sizeof(struct dundi_transaction));
2790 if (global_storehistory) {
2791 trans->start = ast_tvnow();
2792 ast_set_flag(trans, FLAG_STOREHIST);
2794 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2795 trans->autokillid = -1;
2797 apply_peer(trans, p);
2798 if (!p->sentfullkey)
2799 ast_set_flag(trans, FLAG_SENDFULLKEY);
2801 trans->strans = tid;
2802 trans->allnext = alltrans;
2808 static int dundi_xmit(struct dundi_packet *pack)
2811 char iabuf[INET_ADDRSTRLEN];
2813 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2814 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2816 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2817 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2818 ntohs(pack->parent->addr.sin_port), strerror(errno));
2825 static void destroy_packet(struct dundi_packet *pack, int needfree)
2827 struct dundi_packet *prev, *cur;
2830 cur = pack->parent->packets;
2834 prev->next = cur->next;
2836 pack->parent->packets = cur->next;
2843 if (pack->retransid > -1)
2844 ast_sched_del(sched, pack->retransid);
2848 pack->retransid = -1;
2853 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2855 struct dundi_transaction *cur, *prev;
2856 struct dundi_peer *peer;
2861 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2864 if (peer->regtrans == trans)
2865 peer->regtrans = NULL;
2866 if (peer->keypending == trans)
2867 peer->keypending = NULL;
2868 if (peer->qualtrans == trans) {
2870 if (peer->lastms > -1)
2871 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2874 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2877 if (ms < peer->maxms) {
2878 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2879 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2880 } else if (peer->lastms < peer->maxms) {
2881 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);
2885 peer->qualtrans = NULL;
2887 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2888 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2889 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2892 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2893 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2894 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2895 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2896 peer->lookups[x] = peer->lookups[x-1];
2897 if (peer->lookups[x]) {
2898 peer->avgms += peer->lookuptimes[x];
2902 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2903 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2904 if (peer->lookups[0]) {
2905 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2906 peer->avgms += peer->lookuptimes[0];
2917 if (trans->parent) {
2918 /* Unlink from parent if appropriate */
2920 cur = trans->parent->trans;
2924 prev->next = trans->next;
2926 trans->parent->trans = trans->next;
2932 if (!trans->parent->trans) {
2933 /* Wake up sleeper */
2934 if (trans->parent->pfds[1] > -1) {
2935 write(trans->parent->pfds[1], "killa!", 6);
2939 /* Unlink from all trans */
2945 prev->allnext = trans->allnext;
2947 alltrans = trans->allnext;
2953 destroy_packets(trans->packets);
2954 destroy_packets(trans->lasttrans);
2955 trans->packets = NULL;
2956 trans->lasttrans = NULL;
2957 if (trans->autokillid > -1)
2958 ast_sched_del(sched, trans->autokillid);
2959 trans->autokillid = -1;
2960 if (trans->thread) {
2961 /* If used by a thread, mark as dead and be done */
2962 ast_set_flag(trans, FLAG_DEAD);
2967 static int dundi_rexmit(void *data)
2969 struct dundi_packet *pack;
2970 char iabuf[INET_ADDRSTRLEN];
2972 ast_mutex_lock(&peerlock);
2974 if (pack->retrans < 1) {
2975 pack->retransid = -1;
2976 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2977 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2978 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2979 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2980 destroy_trans(pack->parent, 1);
2983 /* Decrement retransmission, try again */
2988 ast_mutex_unlock(&peerlock);
2992 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2994 struct dundi_packet *pack;
2998 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2999 /* Reserve enough space for encryption */
3000 if (ast_test_flag(trans, FLAG_ENCRYPT))
3004 memset(pack, 0, len);
3005 pack->h = (struct dundi_hdr *)(pack->data);
3006 if (cmdresp != DUNDI_COMMAND_ACK) {
3007 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
3008 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3009 pack->next = trans->packets;
3010 trans->packets = pack;
3012 pack->parent = trans;
3013 pack->h->strans = htons(trans->strans);
3014 pack->h->dtrans = htons(trans->dtrans);
3015 pack->h->iseqno = trans->iseqno;
3016 pack->h->oseqno = trans->oseqno;
3017 pack->h->cmdresp = cmdresp;
3018 pack->datalen = sizeof(struct dundi_hdr);
3020 memcpy(pack->h->ies, ied->buf, ied->pos);
3021 pack->datalen += ied->pos;
3024 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3025 ast_set_flag(trans, FLAG_FINAL);
3027 pack->h->cmdflags = flags;
3028 if (cmdresp != DUNDI_COMMAND_ACK) {
3030 trans->oseqno = trans->oseqno % 256;
3032 trans->aseqno = trans->iseqno;
3033 /* If we have their public key, encrypt */
3034 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3036 case DUNDI_COMMAND_REGREQ:
3037 case DUNDI_COMMAND_REGRESPONSE:
3038 case DUNDI_COMMAND_DPDISCOVER:
3039 case DUNDI_COMMAND_DPRESPONSE:
3040 case DUNDI_COMMAND_EIDQUERY:
3041 case DUNDI_COMMAND_EIDRESPONSE:
3042 case DUNDI_COMMAND_PRECACHERQ:
3043 case DUNDI_COMMAND_PRECACHERP:
3045 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3046 res = dundi_encrypt(trans, pack);
3054 res = dundi_xmit(pack);
3056 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3058 if (cmdresp == DUNDI_COMMAND_ACK)
3065 static int do_autokill(void *data)
3067 struct dundi_transaction *trans = data;
3069 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3070 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3071 trans->autokillid = -1;
3072 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3076 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3078 struct dundi_peer *p;
3079 if (!dundi_eid_cmp(eid, us)) {
3080 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3083 ast_mutex_lock(&peerlock);
3086 if (!dundi_eid_cmp(&p->eid, eid)) {
3087 if (has_permission(p->include, context))
3088 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3090 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3096 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3097 ast_mutex_unlock(&peerlock);
3100 static int dundi_discover(struct dundi_transaction *trans)
3102 struct dundi_ie_data ied;
3104 if (!trans->parent) {
3105 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3108 memset(&ied, 0, sizeof(ied));
3109 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3110 if (!dundi_eid_zero(&trans->us_eid))
3111 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3112 for (x=0;x<trans->eidcount;x++)
3113 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3114 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3115 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3116 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3117 if (trans->parent->cbypass)
3118 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3119 if (trans->autokilltimeout)
3120 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3121 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3124 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3126 struct dundi_ie_data ied;
3129 int expiration = dundi_cache_time;
3131 dundi_eid *avoid[1] = { NULL, };
3132 int direct[1] = { 0, };
3133 struct dundi_result dr[MAX_RESULTS];
3134 struct dundi_hint_metadata hmd;
3135 if (!trans->parent) {
3136 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3139 memset(&hmd, 0, sizeof(hmd));
3140 memset(&dr, 0, sizeof(dr));
3141 /* Look up the answers we're going to include */
3142 for (x=0;x<mapcount;x++)
3143 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3146 for (x=0;x<ouranswers;x++) {
3147 if (dr[x].weight < max)
3151 /* If we do not have a canonical result, keep looking */
3152 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);
3154 /* Append answer in result */
3159 if (ouranswers > 0) {
3160 *foundanswers += ouranswers;
3161 memset(&ied, 0, sizeof(ied));
3162 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3163 if (!dundi_eid_zero(&trans->us_eid))
3164 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3165 for (x=0;x<trans->eidcount;x++)
3166 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3167 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3168 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3169 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3170 for (x=0;x<ouranswers;x++) {
3172 if (dr[x].expiration && (expiration > dr[x].expiration))
3173 expiration = dr[x].expiration;
3174 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3176 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3177 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3178 if (trans->autokilltimeout)
3179 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3180 if (expiration < *minexp)
3181 *minexp = expiration;
3182 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3184 /* Oops, nothing to send... */
3185 destroy_trans(trans, 0);
3190 static int dundi_query(struct dundi_transaction *trans)
3192 struct dundi_ie_data ied;
3194 if (!trans->parent) {
3195 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3198 memset(&ied, 0, sizeof(ied));
3199 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3200 if (!dundi_eid_zero(&trans->us_eid))
3201 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3202 for (x=0;x<trans->eidcount;x++)
3203 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3204 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3205 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3206 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3207 if (trans->autokilltimeout)
3208 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3209 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3212 static int discover_transactions(struct dundi_request *dr)
3214 struct dundi_transaction *trans;
3215 ast_mutex_lock(&peerlock);
3218 dundi_discover(trans);
3219 trans = trans->next;
3221 ast_mutex_unlock(&peerlock);
3225 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3227 struct dundi_transaction *trans, *transn;
3228 /* Mark all as "in thread" so they don't disappear */
3229 ast_mutex_lock(&peerlock);
3233 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3235 trans = trans->next;
3237 ast_mutex_unlock(&peerlock);
3241 if (!ast_test_flag(trans, FLAG_DEAD))
3242 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3243 trans = trans->next;
3246 /* Cleanup any that got destroyed in the mean time */
3247 ast_mutex_lock(&peerlock);
3250 transn = trans->next;
3252 if (ast_test_flag(trans, FLAG_DEAD)) {
3253 ast_log(LOG_DEBUG, "Our transaction went away!\n");
3254 destroy_trans(trans, 0);
3258 ast_mutex_unlock(&peerlock);
3262 static int query_transactions(struct dundi_request *dr)
3264 struct dundi_transaction *trans;
3265 ast_mutex_lock(&peerlock);
3269 trans = trans->next;
3271 ast_mutex_unlock(&peerlock);
3275 static int optimize_transactions(struct dundi_request *dr, int order)
3277 /* Minimize the message propagation through DUNDi by
3278 alerting the network to hops which should be not be considered */
3279 struct dundi_transaction *trans;
3280 struct dundi_peer *peer;
3284 ast_mutex_lock(&peerlock);
3287 /* Pop off the true root */
3288 if (trans->eidcount) {
3289 tmp = trans->eids[--trans->eidcount];
3292 tmp = trans->us_eid;
3298 if (has_permission(peer->include, dr->dcontext) &&
3299 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3300 (peer->order <= order)) {
3301 /* For each other transaction, make sure we don't
3302 ask this EID about the others if they're not
3303 already in the list */
3304 if (!dundi_eid_cmp(&tmp, &peer->eid))
3307 for (x=0;x<trans->eidcount;x++) {
3308 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3312 if (x == trans->eidcount) {
3313 /* Nope not in the list, if needed, add us at the end since we're the source */
3314 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3315 trans->eids[trans->eidcount++] = peer->eid;
3316 /* Need to insert the real root (or us) at the bottom now as
3317 a requirement now. */
3324 /* If necessary, push the true root back on the end */
3326 trans->eids[trans->eidcount++] = tmp;
3327 trans = trans->next;
3329 ast_mutex_unlock(&peerlock);
3333 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])