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)
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <sys/socket.h>
32 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
33 #include <sys/types.h>
34 #include <netinet/in_systm.h>
36 #include <netinet/ip.h>
37 #include <sys/ioctl.h>
38 #include <netinet/in.h>
40 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
41 #include <net/if_dl.h>
48 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
50 #include "asterisk/file.h"
51 #include "asterisk/logger.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/config.h"
54 #include "asterisk/options.h"
55 #include "asterisk/pbx.h"
56 #include "asterisk/module.h"
57 #include "asterisk/frame.h"
58 #include "asterisk/file.h"
59 #include "asterisk/cli.h"
60 #include "asterisk/lock.h"
61 #include "asterisk/md5.h"
62 #include "asterisk/dundi.h"
63 #include "asterisk/sched.h"
64 #include "asterisk/io.h"
65 #include "asterisk/utils.h"
66 #include "asterisk/crypto.h"
67 #include "asterisk/astdb.h"
68 #include "asterisk/acl.h"
69 #include "asterisk/aes.h"
71 #include "dundi-parser.h"
73 #define MAX_RESULTS 64
75 #define MAX_PACKET_SIZE 8192
77 extern char ast_config_AST_KEY_DIR[];
79 static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
81 static char *app = "DUNDiLookup";
82 static char *synopsis = "Look up a number with DUNDi";
83 static char *descrip =
84 "DUNDiLookup(number[|context[|options]])\n"
85 " Looks up a given number in the global context specified or in\n"
86 "the reserved 'e164' context if not specified. Returns -1 if the channel\n"
87 "is hungup during the lookup or 0 otherwise. On completion, the variable\n"
88 "${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
89 "of the appropriate technology and destination to access the number. If no\n"
90 "answer was found, and the priority n + 101 exists, execution will continue\n"
91 "at that location. Note that this will only occur if the global priority\n"
92 "jumping option is enabled in extensions.conf. If the 'b' option is specified,\n"
93 "the internal DUNDi cache will by bypassed.\n";
95 #define DUNDI_MODEL_INBOUND (1 << 0)
96 #define DUNDI_MODEL_OUTBOUND (1 << 1)
97 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
99 /* Keep times of last 10 lookups */
100 #define DUNDI_TIMING_HISTORY 10
102 #define FLAG_ISREG (1 << 0) /* Transaction is register request */
103 #define FLAG_DEAD (1 << 1) /* Transaction is dead */
104 #define FLAG_FINAL (1 << 2) /* Transaction has final message sent */
105 #define FLAG_ISQUAL (1 << 3) /* Transaction is a qualification */
106 #define FLAG_ENCRYPT (1 << 4) /* Transaction is encrypted wiht ECX/DCX */
107 #define FLAG_SENDFULLKEY (1 << 5) /* Send full key on transaction */
108 #define FLAG_STOREHIST (1 << 6) /* Record historic performance */
110 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
113 #define DUNDI_SECRET_TIME 15 /* Testing only */
115 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
121 static struct io_context *io;
122 static struct sched_context *sched;
123 static int netsocket = -1;
124 static pthread_t netthreadid = AST_PTHREADT_NULL;
125 static pthread_t precachethreadid = AST_PTHREADT_NULL;
127 static int dundidebug = 0;
128 static int authdebug = 0;
129 static int dundi_ttl = DUNDI_DEFAULT_TTL;
130 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
131 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
132 static int global_autokilltimeout = 0;
133 static dundi_eid global_eid;
134 static int default_expiration = 60;
135 static int global_storehistory = 0;
136 static char dept[80];
138 static char locality[80];
139 static char stateprov[80];
140 static char country[80];
141 static char email[80];
142 static char phone[80];
143 static char secretpath[80];
144 static char cursecret[80];
145 static char ipaddr[80];
146 static time_t rotatetime;
147 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
149 struct permission *next;
154 struct dundi_packet {
156 struct dundi_packet *next;
158 struct dundi_transaction *parent;
161 unsigned char data[0];
164 struct dundi_hint_metadata {
165 unsigned short flags;
166 char exten[AST_MAX_EXTENSION];
169 struct dundi_precache_queue {
170 struct dundi_precache_queue *next;
176 struct dundi_request;
178 struct dundi_transaction {
179 struct sockaddr_in addr; /* Other end of transaction */
180 struct timeval start; /* When this transaction was created */
181 dundi_eid eids[DUNDI_MAX_STACK + 1];
182 int eidcount; /* Number of eids in eids */
183 dundi_eid us_eid; /* Our EID, to them */
184 dundi_eid them_eid; /* Their EID, to us */
185 aes_encrypt_ctx ecx; /* AES 128 Encryption context */
186 aes_decrypt_ctx dcx; /* AES 128 Decryption context */
187 unsigned int flags; /* Has final packet been sent */
188 int ttl; /* Remaining TTL for queries on this one */
189 int thread; /* We have a calling thread */
190 int retranstimer; /* How long to wait before retransmissions */
191 int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
192 int autokilltimeout; /* Recommended timeout for autokill */
193 unsigned short strans; /* Our transaction identifier */
194 unsigned short dtrans; /* Their transaction identifer */
195 unsigned char iseqno; /* Next expected received seqno */
196 unsigned char oiseqno; /* Last received incoming seqno */
197 unsigned char oseqno; /* Next transmitted seqno */
198 unsigned char aseqno; /* Last acknowledge seqno */
199 struct dundi_packet *packets; /* Packets to be retransmitted */
200 struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
201 struct dundi_transaction *next; /* Next with respect to the parent */
202 struct dundi_request *parent; /* Parent request (if there is one) */
203 struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
206 struct dundi_request {
207 char dcontext[AST_MAX_EXTENSION];
208 char number[AST_MAX_EXTENSION];
211 struct dundi_result *dr;
212 struct dundi_entity_info *dei;
213 struct dundi_hint_metadata *hmd;
219 unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
220 struct dundi_transaction *trans; /* Transactions */
221 struct dundi_request *next;
224 static struct dundi_mapping {
225 char dcontext[AST_MAX_EXTENSION];
226 char lcontext[AST_MAX_EXTENSION];
231 char dest[AST_MAX_EXTENSION];
232 struct dundi_mapping *next;
235 static struct dundi_peer {
237 struct sockaddr_in addr; /* Address of DUNDi peer */
238 struct permission *permit;
239 struct permission *include;
240 struct permission *precachesend;
241 struct permission *precachereceive;
250 unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
251 unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
252 unsigned long us_keycrc32; /* CRC-32 of our key */
253 aes_encrypt_ctx us_ecx; /* Cached AES 128 Encryption context */
254 aes_decrypt_ctx us_dcx; /* Cached AES 128 Decryption context */
255 unsigned long them_keycrc32;/* CRC-32 of our key */
256 aes_encrypt_ctx them_ecx; /* Cached AES 128 Encryption context */
257 aes_decrypt_ctx them_dcx; /* Cached AES 128 Decryption context */
258 time_t keyexpire; /* When to expire/recreate key */
260 int lookuptimes[DUNDI_TIMING_HISTORY];
261 char *lookups[DUNDI_TIMING_HISTORY];
263 struct dundi_transaction *regtrans; /* Registration transaction */
264 struct dundi_transaction *qualtrans; /* Qualify transaction */
265 struct dundi_transaction *keypending;
266 int model; /* Pull model */
267 int pcmodel; /* Push/precache model */
268 int dynamic; /* Are we dynamic? */
269 int lastms; /* Last measured latency */
270 int maxms; /* Max permissible latency */
271 struct timeval qualtx; /* Time of transmit */
272 struct dundi_peer *next;
275 static struct dundi_precache_queue *pcq;
277 AST_MUTEX_DEFINE_STATIC(peerlock);
278 AST_MUTEX_DEFINE_STATIC(pclock);
280 static int dundi_xmit(struct dundi_packet *pack);
282 static void dundi_debug_output(const char *data)
285 ast_verbose("%s", data);
288 static void dundi_error_output(const char *data)
290 ast_log(LOG_WARNING, "%s", data);
293 static int has_permission(struct permission *ps, char *cont)
297 if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
304 static char *tech2str(int tech)
307 case DUNDI_PROTO_NONE:
309 case DUNDI_PROTO_IAX:
311 case DUNDI_PROTO_SIP:
313 case DUNDI_PROTO_H323:
320 static int str2tech(char *str)
322 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
323 return DUNDI_PROTO_IAX;
324 else if (!strcasecmp(str, "SIP"))
325 return DUNDI_PROTO_SIP;
326 else if (!strcasecmp(str, "H323"))
327 return DUNDI_PROTO_H323;
332 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[]);
333 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
334 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
335 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
337 /* Look for an exact match first */
338 struct dundi_transaction *trans;
341 if (!inaddrcmp(&trans->addr, sin) &&
342 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
343 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
345 trans->dtrans = ntohs(hdr->strans) & 32767;
348 trans = trans->allnext;
351 switch(hdr->cmdresp & 0x7f) {
352 case DUNDI_COMMAND_DPDISCOVER:
353 case DUNDI_COMMAND_EIDQUERY:
354 case DUNDI_COMMAND_PRECACHERQ:
355 case DUNDI_COMMAND_REGREQ:
356 case DUNDI_COMMAND_NULL:
357 case DUNDI_COMMAND_ENCRYPT:
359 /* Create new transaction */
360 trans = create_transaction(NULL);
362 memcpy(&trans->addr, sin, sizeof(trans->addr));
363 trans->dtrans = ntohs(hdr->strans) & 32767;
365 ast_log(LOG_WARNING, "Out of memory!\n");
375 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
377 static int dundi_ack(struct dundi_transaction *trans, int final)
379 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
381 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
384 struct dundi_packet pack;
385 struct dundi_hdr hdr;
387 struct dundi_transaction trans;
388 /* Never respond to an INVALID with another INVALID */
389 if (h->cmdresp == DUNDI_COMMAND_INVALID)
391 memset(&tmp, 0, sizeof(tmp));
392 memset(&trans, 0, sizeof(trans));
393 memcpy(&trans.addr, sin, sizeof(trans.addr));
394 tmp.hdr.strans = h->dtrans;
395 tmp.hdr.dtrans = h->strans;
396 tmp.hdr.iseqno = h->oseqno;
397 tmp.hdr.oseqno = h->iseqno;
398 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
399 tmp.hdr.cmdflags = 0;
400 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
401 tmp.pack.datalen = sizeof(struct dundi_hdr);
402 tmp.pack.parent = &trans;
403 dundi_xmit(&tmp.pack);
406 static void reset_global_eid(void)
408 #if defined(SIOCGIFHWADDR)
413 s = socket(AF_INET, SOCK_STREAM, 0);
417 memset(&ifr, 0, sizeof(ifr));
418 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
419 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
420 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
421 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);
429 #if defined(ifa_broadaddr) && !defined(SOLARIS)
431 struct ifaddrs *ifap;
433 if (getifaddrs(&ifap) == 0) {
435 for (p = ifap; p; p = p->ifa_next) {
436 if (p->ifa_addr->sa_family == AF_LINK) {
437 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
440 sdp->sdl_data + sdp->sdl_nlen, 6);
441 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);
450 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
453 static int get_trans_id(void)
455 struct dundi_transaction *t;
456 int stid = (rand() % 32766) + 1;
461 if (t->strans == tid)
467 tid = (tid % 32766) + 1;
468 } while (tid != stid);
472 static int reset_transaction(struct dundi_transaction *trans)
475 tid = get_trans_id();
484 ast_clear_flag(trans, FLAG_FINAL);
488 static struct dundi_peer *find_peer(dundi_eid *eid)
490 struct dundi_peer *cur;
495 if (!dundi_eid_cmp(&cur->eid,eid))
502 static void build_iv(unsigned char *iv)
504 /* XXX Would be nice to be more random XXX */
505 unsigned int *fluffy;
507 fluffy = (unsigned int *)(iv);
512 struct dundi_query_state {
513 dundi_eid *eids[DUNDI_MAX_STACK + 1];
514 int directs[DUNDI_MAX_STACK + 1];
516 char called_context[AST_MAX_EXTENSION];
517 char called_number[AST_MAX_EXTENSION];
518 struct dundi_mapping *maps;
521 struct dundi_transaction *trans;
528 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)
530 struct ast_flags flags = {0};
532 if (!ast_strlen_zero(map->lcontext)) {
533 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
534 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
535 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
536 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
537 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
538 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
539 if (ast_ignore_pattern(map->lcontext, called_number))
540 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
542 /* Clearly we can't say 'don't ask' anymore if we found anything... */
543 if (ast_test_flag(&flags, AST_FLAGS_ALL))
544 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
546 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
547 /* Skip partial answers */
548 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
550 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
551 struct varshead headp;
552 struct ast_var_t *newvariable;
553 ast_set_flag(&flags, map->options & 0xffff);
554 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
555 dr[anscnt].techint = map->tech;
556 dr[anscnt].weight = map->weight;
557 dr[anscnt].expiration = dundi_cache_time;
558 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
559 dr[anscnt].eid = *us_eid;
560 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
561 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
562 AST_LIST_HEAD_INIT_NOLOCK(&headp);
563 newvariable = ast_var_assign("NUMBER", called_number);
564 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
565 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
566 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
567 newvariable = ast_var_assign("SECRET", cursecret);
568 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
569 newvariable = ast_var_assign("IPADDR", ipaddr);
570 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
571 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
572 while (!AST_LIST_EMPTY(&headp)) { /* List Deletion. */
573 newvariable = AST_LIST_REMOVE_HEAD(&headp, entries);
574 ast_var_delete(newvariable);
577 dr[anscnt].dest[0] = '\0';
580 /* No answers... Find the fewest number of digits from the
581 number for which we have no answer. */
582 char tmp[AST_MAX_EXTENSION];
583 for (x=0;x<AST_MAX_EXTENSION;x++) {
584 tmp[x] = called_number[x];
587 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
588 /* Oops found something we can't match. If this is longer
589 than the running hint, we have to consider it */
590 if (strlen(tmp) > strlen(hmd->exten)) {
591 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
601 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
603 static void *dundi_lookup_thread(void *data)
605 struct dundi_query_state *st = data;
606 struct dundi_result dr[MAX_RESULTS];
607 struct dundi_ie_data ied;
608 struct dundi_hint_metadata hmd;
613 int expiration = dundi_cache_time;
615 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
616 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
617 memset(&ied, 0, sizeof(ied));
618 memset(&dr, 0, sizeof(dr));
619 memset(&hmd, 0, sizeof(hmd));
620 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
621 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
622 for (x=0;x<st->nummaps;x++)
623 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
626 for (x=0;x<ouranswers;x++) {
627 if (dr[x].weight < max)
632 /* If we do not have a canonical result, keep looking */
633 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);
635 /* Append answer in result */
638 if ((res < -1) && (!ouranswers))
639 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
642 ast_mutex_lock(&peerlock);
643 /* Truncate if "don't ask" isn't present */
644 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
646 if (ast_test_flag(st->trans, FLAG_DEAD)) {
647 ast_log(LOG_DEBUG, "Our transaction went away!\n");
648 st->trans->thread = 0;
649 destroy_trans(st->trans, 0);
651 for (x=0;x<ouranswers;x++) {
653 if (dr[x].expiration && (expiration > dr[x].expiration))
654 expiration = dr[x].expiration;
655 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
657 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
658 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
659 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
660 st->trans->thread = 0;
662 ast_mutex_unlock(&peerlock);
667 static void *dundi_precache_thread(void *data)
669 struct dundi_query_state *st = data;
670 struct dundi_ie_data ied;
671 struct dundi_hint_metadata hmd;
674 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
675 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
676 memset(&ied, 0, sizeof(ied));
678 /* Now produce precache */
679 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
681 ast_mutex_lock(&peerlock);
682 /* Truncate if "don't ask" isn't present */
683 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
685 if (ast_test_flag(st->trans, FLAG_DEAD)) {
686 ast_log(LOG_DEBUG, "Our transaction went away!\n");
687 st->trans->thread = 0;
688 destroy_trans(st->trans, 0);
690 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
691 st->trans->thread = 0;
693 ast_mutex_unlock(&peerlock);
698 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[]);
700 static void *dundi_query_thread(void *data)
702 struct dundi_query_state *st = data;
703 struct dundi_entity_info dei;
704 struct dundi_ie_data ied;
705 struct dundi_hint_metadata hmd;
708 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
709 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
710 memset(&ied, 0, sizeof(ied));
711 memset(&dei, 0, sizeof(dei));
712 memset(&hmd, 0, sizeof(hmd));
713 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
715 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
716 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
717 ast_copy_string(dei.org, org, sizeof(dei.org));
718 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
719 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
720 ast_copy_string(dei.country, country, sizeof(dei.country));
721 ast_copy_string(dei.email, email, sizeof(dei.email));
722 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
725 /* If we do not have a canonical result, keep looking */
726 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
728 ast_mutex_lock(&peerlock);
729 if (ast_test_flag(st->trans, FLAG_DEAD)) {
730 ast_log(LOG_DEBUG, "Our transaction went away!\n");
731 st->trans->thread = 0;
732 destroy_trans(st->trans, 0);
735 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
736 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
737 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
738 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
739 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
740 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
741 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
742 if (!ast_strlen_zero(dei.ipaddr))
743 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
745 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
746 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
747 st->trans->thread = 0;
749 ast_mutex_unlock(&peerlock);
754 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
756 struct dundi_query_state *st;
760 struct dundi_ie_data ied;
763 pthread_t lookupthread;
765 if (ies->eidcount > 1) {
766 /* Since it is a requirement that the first EID is the authenticating host
767 and the last EID is the root, it is permissible that the first and last EID
768 could be the same. In that case, we should go ahead copy only the "root" section
769 since we will not need it for authentication. */
770 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
773 totallen = sizeof(struct dundi_query_state);
774 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
775 st = malloc(totallen);
777 memset(st, 0, totallen);
778 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
779 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
781 st->ttl = ies->ttl - 1;
785 for (x=skipfirst;ies->eids[x];x++) {
786 st->eids[x-skipfirst] = (dundi_eid *)s;
787 *st->eids[x-skipfirst] = *ies->eids[x];
788 s += sizeof(dundi_eid);
790 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);
791 pthread_attr_init(&attr);
792 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
794 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
796 ast_log(LOG_WARNING, "Unable to create thread!\n");
798 memset(&ied, 0, sizeof(ied));
799 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
800 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
804 ast_log(LOG_WARNING, "Out of memory!\n");
805 memset(&ied, 0, sizeof(ied));
806 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
807 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
813 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
818 char eidpeer_str[20];
819 char eidroot_str[20];
824 expiration = dundi_cache_time;
826 /* Only cache hint if "don't ask" is there... */
827 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
830 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
832 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
833 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
834 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
835 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
838 timeout += expiration;
839 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
841 ast_db_put("dundi/cache", key1, data);
842 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
843 ast_db_put("dundi/cache", key2, data);
844 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
848 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
854 char eidpeer_str[20];
855 char eidroot_str[20];
859 expiration = dundi_cache_time;
861 /* Keep pushes a little longer, cut pulls a little short */
868 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
869 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
870 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
871 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
872 /* Build request string */
874 timeout += expiration;
875 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
876 for (x=start;x<req->respcount;x++) {
877 /* Skip anything with an illegal pipe in it */
878 if (strchr(req->dr[x].dest, '|'))
880 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
881 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
882 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
884 ast_db_put("dundi/cache", key1, data);
885 ast_db_put("dundi/cache", key2, data);
889 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
891 struct dundi_query_state *st;
894 struct dundi_ie_data ied;
896 struct dundi_result dr2[MAX_RESULTS];
897 struct dundi_request dr;
898 struct dundi_hint_metadata hmd;
900 struct dundi_mapping *cur;
904 pthread_t lookupthread;
907 memset(&dr2, 0, sizeof(dr2));
908 memset(&dr, 0, sizeof(dr));
909 memset(&hmd, 0, sizeof(hmd));
911 /* Forge request structure to hold answers for cache */
912 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
914 dr.maxcount = MAX_RESULTS;
915 dr.expiration = dundi_cache_time;
917 dr.pfds[0] = dr.pfds[1] = -1;
919 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
920 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
922 for (x=0;x<ies->anscount;x++) {
923 if (trans->parent->respcount < trans->parent->maxcount) {
924 /* Make sure it's not already there */
925 for (z=0;z<trans->parent->respcount;z++) {
926 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
927 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
930 if (z == trans->parent->respcount) {
931 /* Copy into parent responses */
932 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
933 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
934 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
935 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
936 if (ies->expiration > 0)
937 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
939 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
940 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
941 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
942 &ies->answers[x]->eid);
943 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
944 sizeof(trans->parent->dr[trans->parent->respcount].dest));
945 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
946 sizeof(trans->parent->dr[trans->parent->respcount].tech));
947 trans->parent->respcount++;
948 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
949 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
950 /* Update weight if appropriate */
951 trans->parent->dr[z].weight = ies->answers[x]->weight;
954 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
955 trans->parent->number, trans->parent->dcontext);
958 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
959 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
961 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
963 totallen = sizeof(struct dundi_query_state);
964 /* Count matching map entries */
968 if (!strcasecmp(cur->dcontext, ccontext))
973 /* If no maps, return -1 immediately */
977 if (ies->eidcount > 1) {
978 /* Since it is a requirement that the first EID is the authenticating host
979 and the last EID is the root, it is permissible that the first and last EID
980 could be the same. In that case, we should go ahead copy only the "root" section
981 since we will not need it for authentication. */
982 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
986 /* Prepare to run a query and then propagate that as necessary */
987 totallen += mapcount * sizeof(struct dundi_mapping);
988 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
989 st = malloc(totallen);
991 memset(st, 0, totallen);
992 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
993 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
995 st->ttl = ies->ttl - 1;
996 st->nocache = ies->cbypass;
1000 for (x=skipfirst;ies->eids[x];x++) {
1001 st->eids[x-skipfirst] = (dundi_eid *)s;
1002 *st->eids[x-skipfirst] = *ies->eids[x];
1003 st->directs[x-skipfirst] = ies->eid_direct[x];
1004 s += sizeof(dundi_eid);
1006 /* Append mappings */
1008 st->maps = (struct dundi_mapping *)s;
1011 if (!strcasecmp(cur->dcontext, ccontext)) {
1014 st->maps[x].next = NULL;
1020 st->nummaps = mapcount;
1021 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1022 pthread_attr_init(&attr);
1023 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1025 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1027 ast_log(LOG_WARNING, "Unable to create thread!\n");
1029 memset(&ied, 0, sizeof(ied));
1030 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1031 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1035 ast_log(LOG_WARNING, "Out of memory!\n");
1036 memset(&ied, 0, sizeof(ied));
1037 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1038 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1044 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1046 struct dundi_query_state *st;
1049 struct dundi_ie_data ied;
1051 struct dundi_mapping *cur;
1055 pthread_t lookupthread;
1056 pthread_attr_t attr;
1057 totallen = sizeof(struct dundi_query_state);
1058 /* Count matching map entries */
1062 if (!strcasecmp(cur->dcontext, ccontext))
1066 /* If no maps, return -1 immediately */
1070 if (ies->eidcount > 1) {
1071 /* Since it is a requirement that the first EID is the authenticating host
1072 and the last EID is the root, it is permissible that the first and last EID
1073 could be the same. In that case, we should go ahead copy only the "root" section
1074 since we will not need it for authentication. */
1075 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1079 totallen += mapcount * sizeof(struct dundi_mapping);
1080 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1081 st = malloc(totallen);
1083 memset(st, 0, totallen);
1084 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1085 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1087 st->ttl = ies->ttl - 1;
1088 st->nocache = ies->cbypass;
1092 for (x=skipfirst;ies->eids[x];x++) {
1093 st->eids[x-skipfirst] = (dundi_eid *)s;
1094 *st->eids[x-skipfirst] = *ies->eids[x];
1095 st->directs[x-skipfirst] = ies->eid_direct[x];
1096 s += sizeof(dundi_eid);
1098 /* Append mappings */
1100 st->maps = (struct dundi_mapping *)s;
1103 if (!strcasecmp(cur->dcontext, ccontext)) {
1106 st->maps[x].next = NULL;
1112 st->nummaps = mapcount;
1113 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1114 pthread_attr_init(&attr);
1115 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1117 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1119 ast_log(LOG_WARNING, "Unable to create thread!\n");
1121 memset(&ied, 0, sizeof(ied));
1122 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1123 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1127 ast_log(LOG_WARNING, "Out of memory!\n");
1128 memset(&ied, 0, sizeof(ied));
1129 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1130 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1136 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1139 char *ptr, *term, *src;
1141 struct ast_flags flags;
1148 /* Build request string */
1149 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1151 if (sscanf(ptr, "%ld|%n", &timeout, &length) == 1) {
1152 expiration = timeout - now;
1153 if (expiration > 0) {
1154 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
1156 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1158 term = strchr(ptr, '|');
1161 src = strrchr(ptr, '/');
1167 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1168 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1169 /* Make sure it's not already there */
1170 for (z=0;z<req->respcount;z++) {
1171 if ((req->dr[z].techint == tech) &&
1172 !strcmp(req->dr[z].dest, ptr))
1175 if (z == req->respcount) {
1176 /* Copy into parent responses */
1177 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1178 req->dr[req->respcount].weight = weight;
1179 req->dr[req->respcount].techint = tech;
1180 req->dr[req->respcount].expiration = expiration;
1181 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1182 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1183 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1184 ast_copy_string(req->dr[req->respcount].dest, ptr,
1185 sizeof(req->dr[req->respcount].dest));
1186 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1187 sizeof(req->dr[req->respcount].tech));
1189 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1190 } else if (req->dr[z].weight > weight)
1191 req->dr[z].weight = weight;
1195 /* We found *something* cached */
1196 if (expiration < *lowexpiration)
1197 *lowexpiration = expiration;
1200 ast_db_del("dundi/cache", key);
1202 ast_db_del("dundi/cache", key);
1208 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1212 char eidroot_str[20];
1216 char eid_str_full[20];
1221 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1222 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1223 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1224 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1225 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1226 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1227 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1228 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1229 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1231 if (!req->respcount) {
1233 /* Look and see if we have a hint that would preclude us from looking at this
1234 peer for this number. */
1235 if (!(tmp[x] = req->number[x]))
1238 /* Check for hints */
1239 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1240 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1241 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1242 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1243 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1244 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1246 if (strlen(tmp) > strlen(req->hmd->exten)) {
1247 /* Update meta data if appropriate */
1248 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1258 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1260 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1262 if (!trans->addr.sin_addr.s_addr)
1263 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1264 trans->us_eid = p->us_eid;
1265 trans->them_eid = p->eid;
1266 /* Enable encryption if appropriate */
1267 if (!ast_strlen_zero(p->inkey))
1268 ast_set_flag(trans, FLAG_ENCRYPT);
1270 trans->autokilltimeout = p->maxms;
1271 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1272 if (p->lastms > 1) {
1273 trans->retranstimer = p->lastms * 2;
1274 /* Keep it from being silly */
1275 if (trans->retranstimer < 150)
1276 trans->retranstimer = 150;
1278 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1279 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1281 trans->autokilltimeout = global_autokilltimeout;
1284 static int do_register_expire(void *data)
1286 struct dundi_peer *peer = data;
1288 /* Called with peerlock already held */
1289 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1290 peer->registerexpire = -1;
1292 memset(&peer->addr, 0, sizeof(peer->addr));
1296 static int update_key(struct dundi_peer *peer)
1298 unsigned char key[16];
1299 struct ast_key *ekey, *skey;
1302 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1304 aes_encrypt_key128(key, &peer->us_ecx);
1305 aes_decrypt_key128(key, &peer->us_dcx);
1306 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1308 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1309 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1312 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1314 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1315 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1318 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1319 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1322 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1323 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1326 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1327 peer->sentfullkey = 0;
1329 time(&peer->keyexpire);
1330 peer->keyexpire += dundi_key_ttl;
1335 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1337 unsigned char curblock[16];
1339 memcpy(curblock, iv, sizeof(curblock));
1342 curblock[x] ^= src[x];
1343 aes_encrypt(curblock, dst, ecx);
1344 memcpy(curblock, dst, sizeof(curblock));
1351 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1353 unsigned char lastblock[16];
1355 memcpy(lastblock, iv, sizeof(lastblock));
1357 aes_decrypt(src, dst, dcx);
1359 dst[x] ^= lastblock[x];
1360 memcpy(lastblock, src, sizeof(lastblock));
1368 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)
1370 int space = *dstlen;
1371 unsigned long bytes;
1372 struct dundi_hdr *h;
1373 unsigned char *decrypt_space;
1374 decrypt_space = alloca(srclen);
1377 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1379 h = (struct dundi_hdr *)dst;
1382 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1383 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1387 *dstlen = bytes + 6;
1388 /* Return new header */
1392 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1394 unsigned char *compress_space;
1397 unsigned long bytes;
1398 struct dundi_ie_data ied;
1399 struct dundi_peer *peer;
1400 unsigned char iv[16];
1401 len = pack->datalen + pack->datalen / 100 + 42;
1402 compress_space = alloca(len);
1403 if (compress_space) {
1404 memset(compress_space, 0, len);
1405 /* We care about everthing save the first 6 bytes of header */
1407 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1409 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1412 memset(&ied, 0, sizeof(ied));
1413 /* Say who we are */
1414 if (!pack->h->iseqno && !pack->h->oseqno) {
1415 /* Need the key in the first copy */
1416 if (!(peer = find_peer(&trans->them_eid)))
1418 if (update_key(peer))
1420 if (!peer->sentfullkey)
1421 ast_set_flag(trans, FLAG_SENDFULLKEY);
1422 /* Append key data */
1423 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1424 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1425 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1426 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1428 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1430 /* Setup contexts */
1431 trans->ecx = peer->us_ecx;
1432 trans->dcx = peer->us_dcx;
1434 /* We've sent the full key */
1435 peer->sentfullkey = 1;
1437 /* Build initialization vector */
1439 /* Add the field, rounded up to 16 bytes */
1440 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1442 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1443 ast_log(LOG_NOTICE, "Final packet too large!\n");
1446 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1447 ied.pos += ((bytes + 15) / 16) * 16;
1448 /* Reconstruct header */
1449 pack->datalen = sizeof(struct dundi_hdr);
1450 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1451 pack->h->cmdflags = 0;
1452 memcpy(pack->h->ies, ied.buf, ied.pos);
1453 pack->datalen += ied.pos;
1459 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1461 unsigned char dst[128];
1463 struct ast_key *key, *skey;
1466 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1467 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1470 } else if (!newkey || !newsig)
1472 if (!memcmp(peer->rxenckey, newkey, 128) &&
1473 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1474 /* By definition, a match */
1478 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1480 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1481 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1485 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1487 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1488 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1492 /* First check signature */
1493 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1497 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1500 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1503 /* Decrypted, passes signature */
1504 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1505 memcpy(peer->rxenckey, newkey, 128);
1506 memcpy(peer->rxenckey + 128, newsig, 128);
1507 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1508 aes_decrypt_key128(dst, &peer->them_dcx);
1509 aes_encrypt_key128(dst, &peer->them_ecx);
1513 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1515 /* Handle canonical command / response */
1516 int final = hdr->cmdresp & 0x80;
1517 int cmd = hdr->cmdresp & 0x7f;
1522 unsigned char *bufcpy;
1523 struct dundi_ie_data ied;
1524 struct dundi_ies ies;
1525 struct dundi_peer *peer;
1528 memset(&ied, 0, sizeof(ied));
1529 memset(&ies, 0, sizeof(ies));
1531 bufcpy = alloca(datalen);
1534 /* Make a copy for parsing */
1535 memcpy(bufcpy, hdr->ies, datalen);
1536 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1537 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1538 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1543 case DUNDI_COMMAND_DPDISCOVER:
1544 case DUNDI_COMMAND_EIDQUERY:
1545 case DUNDI_COMMAND_PRECACHERQ:
1546 if (cmd == DUNDI_COMMAND_EIDQUERY)
1547 resp = DUNDI_COMMAND_EIDRESPONSE;
1548 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1549 resp = DUNDI_COMMAND_PRECACHERP;
1551 resp = DUNDI_COMMAND_DPRESPONSE;
1552 /* A dialplan or entity discover -- qualify by highest level entity */
1553 peer = find_peer(ies.eids[0]);
1555 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1556 dundi_send(trans, resp, 0, 1, &ied);
1559 trans->us_eid = peer->us_eid;
1560 if (strlen(peer->inkey)) {
1561 hasauth = encrypted;
1565 /* Okay we're authentiated and all, now we check if they're authorized */
1566 if (!ies.called_context)
1567 ies.called_context = "e164";
1568 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1569 res = dundi_answer_entity(trans, &ies, ies.called_context);
1571 if (!ies.called_number || ast_strlen_zero(ies.called_number)) {
1572 /* They're not permitted to access that context */
1573 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1574 dundi_send(trans, resp, 0, 1, &ied);
1575 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1576 (peer->model & DUNDI_MODEL_INBOUND) &&
1577 has_permission(peer->permit, ies.called_context)) {
1578 res = dundi_answer_query(trans, &ies, ies.called_context);
1580 /* There is no such dundi context */
1581 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1582 dundi_send(trans, resp, 0, 1, &ied);
1584 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1585 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1586 has_permission(peer->include, ies.called_context)) {
1587 res = dundi_prop_precache(trans, &ies, ies.called_context);
1589 /* There is no such dundi context */
1590 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1591 dundi_send(trans, resp, 0, 1, &ied);
1594 /* They're not permitted to access that context */
1595 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1596 dundi_send(trans, resp, 0, 1, &ied);
1600 /* They're not permitted to access that context */
1601 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1602 dundi_send(trans, resp, 0, 1, &ied);
1606 case DUNDI_COMMAND_REGREQ:
1607 /* A register request -- should only have one entity */
1608 peer = find_peer(ies.eids[0]);
1609 if (!peer || !peer->dynamic) {
1610 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1611 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1614 trans->us_eid = peer->us_eid;
1615 if (!ast_strlen_zero(peer->inkey)) {
1616 hasauth = encrypted;
1620 int expire = default_expiration;
1621 char iabuf[INET_ADDRSTRLEN];
1624 if (peer->registerexpire > -1)
1625 ast_sched_del(sched, peer->registerexpire);
1626 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1627 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1628 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1629 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1630 if (inaddrcmp(&peer->addr, &trans->addr)) {
1631 if (option_verbose > 2)
1632 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));
1636 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1637 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1638 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1640 qualify_peer(peer, 1);
1644 case DUNDI_COMMAND_DPRESPONSE:
1645 /* A dialplan response, lets see what we got... */
1646 if (ies.cause < 1) {
1647 /* Success of some sort */
1648 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1649 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1650 authpass = encrypted;
1654 /* Pass back up answers */
1655 if (trans->parent && trans->parent->dr) {
1656 y = trans->parent->respcount;
1657 for (x=0;x<ies.anscount;x++) {
1658 if (trans->parent->respcount < trans->parent->maxcount) {
1659 /* Make sure it's not already there */
1660 for (z=0;z<trans->parent->respcount;z++) {
1661 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1662 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1665 if (z == trans->parent->respcount) {
1666 /* Copy into parent responses */
1667 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1668 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1669 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1670 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1671 if (ies.expiration > 0)
1672 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1674 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1675 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1676 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1677 &ies.answers[x]->eid);
1678 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1679 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1680 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1681 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1682 trans->parent->respcount++;
1683 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1684 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1685 /* Update weight if appropriate */
1686 trans->parent->dr[z].weight = ies.answers[x]->weight;
1689 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1690 trans->parent->number, trans->parent->dcontext);
1692 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1693 the cache know if this request was unaffected by our entity list. */
1694 cache_save(&trans->them_eid, trans->parent, y,
1695 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1697 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1698 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1699 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1700 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1701 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1702 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1703 sizeof(trans->parent->hmd->exten));
1706 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1709 if (ies.expiration > 0) {
1710 if (trans->parent->expiration > ies.expiration) {
1711 trans->parent->expiration = ies.expiration;
1715 /* Close connection if not final */
1717 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1721 /* Auth failure, check for data */
1723 /* Cancel if they didn't already */
1724 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1728 case DUNDI_COMMAND_EIDRESPONSE:
1729 /* A dialplan response, lets see what we got... */
1730 if (ies.cause < 1) {
1731 /* Success of some sort */
1732 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1733 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1734 authpass = encrypted;
1738 /* Pass back up answers */
1739 if (trans->parent && trans->parent->dei && ies.q_org) {
1740 if (!trans->parent->respcount) {
1741 trans->parent->respcount++;
1743 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1745 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1747 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1748 if (ies.q_stateprov)
1749 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1751 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1753 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1755 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1757 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1758 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1759 /* If it's them, update our address */
1760 ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1761 trans->addr.sin_addr);
1765 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1766 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1769 /* Close connection if not final */
1771 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1775 /* Auth failure, check for data */
1777 /* Cancel if they didn't already */
1778 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1782 case DUNDI_COMMAND_REGRESPONSE:
1783 /* A dialplan response, lets see what we got... */
1784 if (ies.cause < 1) {
1786 /* Success of some sort */
1787 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1788 hasauth = encrypted;
1793 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1795 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1796 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1799 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),
1800 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1801 /* Close connection if not final */
1803 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1806 /* Auth failure, cancel if they didn't for some reason */
1808 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1812 case DUNDI_COMMAND_INVALID:
1813 case DUNDI_COMMAND_NULL:
1814 case DUNDI_COMMAND_PRECACHERP:
1815 /* Do nothing special */
1817 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1819 case DUNDI_COMMAND_ENCREJ:
1820 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
1821 /* No really, it's over at this point */
1823 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1825 /* Send with full key */
1826 ast_set_flag(trans, FLAG_SENDFULLKEY);
1828 /* Ooops, we got a final message, start by sending ACK... */
1829 dundi_ack(trans, hdr->cmdresp & 0x80);
1830 trans->aseqno = trans->iseqno;
1831 /* Now, we gotta create a new transaction */
1832 if (!reset_transaction(trans)) {
1833 /* Make sure handle_frame doesn't destroy us */
1834 hdr->cmdresp &= 0x7f;
1835 /* Parse the message we transmitted */
1836 memset(&ies, 0, sizeof(ies));
1837 dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
1838 /* Reconstruct outgoing encrypted packet */
1839 memset(&ied, 0, sizeof(ied));
1840 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1841 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1842 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1844 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1845 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
1846 peer->sentfullkey = 1;
1851 case DUNDI_COMMAND_ENCRYPT:
1853 /* No nested encryption! */
1854 if ((trans->iseqno == 1) && !trans->oseqno) {
1855 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1856 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1857 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1859 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1863 apply_peer(trans, peer);
1864 /* Key passed, use new contexts for this session */
1865 trans->ecx = peer->them_ecx;
1866 trans->dcx = peer->them_dcx;
1868 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1869 struct dundi_hdr *dhdr;
1870 unsigned char decoded[MAX_PACKET_SIZE];
1872 ddatalen = sizeof(decoded);
1873 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1875 /* Handle decrypted response */
1877 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1878 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1879 /* Carry back final flag */
1880 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1883 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1887 /* Turn off encryption */
1888 ast_clear_flag(trans, FLAG_ENCRYPT);
1889 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1893 /* Send unknown command if we don't know it, with final flag IFF it's the
1894 first command in the dialog and only if we haven't recieved final notification */
1896 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1897 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1903 static void destroy_packet(struct dundi_packet *pack, int needfree);
1904 static void destroy_packets(struct dundi_packet *p)
1906 struct dundi_packet *prev;
1910 if (prev->retransid > -1)
1911 ast_sched_del(sched, prev->retransid);
1917 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1919 /* Ack transmitted packet corresponding to iseqno */
1920 struct dundi_packet *pack;
1921 pack = trans->packets;
1923 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1924 destroy_packet(pack, 0);
1925 if (trans->lasttrans) {
1926 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1927 destroy_packets(trans->lasttrans);
1929 trans->lasttrans = pack;
1930 if (trans->autokillid > -1)
1931 ast_sched_del(sched, trans->autokillid);
1932 trans->autokillid = -1;
1940 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1942 struct dundi_transaction *trans;
1943 trans = find_transaction(h, sin);
1945 dundi_reject(h, sin);
1948 /* Got a transaction, see where this header fits in */
1949 if (h->oseqno == trans->iseqno) {
1950 /* Just what we were looking for... Anything but ack increments iseqno */
1951 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1952 /* If final, we're done */
1953 destroy_trans(trans, 0);
1956 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1957 trans->oiseqno = trans->iseqno;
1959 handle_command_response(trans, h, datalen, 0);
1961 if (trans->aseqno != trans->iseqno) {
1962 dundi_ack(trans, h->cmdresp & 0x80);
1963 trans->aseqno = trans->iseqno;
1965 /* Delete any saved last transmissions */
1966 destroy_packets(trans->lasttrans);
1967 trans->lasttrans = NULL;
1968 if (h->cmdresp & 0x80) {
1969 /* Final -- destroy now */
1970 destroy_trans(trans, 0);
1972 } else if (h->oseqno == trans->oiseqno) {
1973 /* Last incoming sequence number -- send ACK without processing */
1974 dundi_ack(trans, 0);
1976 /* Out of window -- simply drop */
1977 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1982 static int socket_read(int *id, int fd, short events, void *cbdata)
1984 struct sockaddr_in sin;
1986 struct dundi_hdr *h;
1987 char buf[MAX_PACKET_SIZE];
1990 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1992 if (errno != ECONNREFUSED)
1993 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1996 if (res < sizeof(struct dundi_hdr)) {
1997 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2001 h = (struct dundi_hdr *)buf;
2003 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2004 ast_mutex_lock(&peerlock);
2005 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2006 ast_mutex_unlock(&peerlock);
2010 static void build_secret(char *secret, int seclen)
2012 unsigned char tmp[16];
2016 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2017 /* Eliminate potential bad characters */
2018 while((s = strchr(secret, ';'))) *s = '+';
2019 while((s = strchr(secret, '/'))) *s = '+';
2020 while((s = strchr(secret, ':'))) *s = '+';
2021 while((s = strchr(secret, '@'))) *s = '+';
2025 static void save_secret(const char *newkey, const char *oldkey)
2029 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2031 snprintf(tmp, sizeof(tmp), "%s", newkey);
2032 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2033 ast_db_put(secretpath, "secret", tmp);
2034 snprintf(tmp, sizeof(tmp), "%ld", rotatetime);
2035 ast_db_put(secretpath, "secretexpiry", tmp);
2038 static void load_password(void)
2045 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2046 if (sscanf(tmp, "%ld", &expired) == 1) {
2047 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2048 current = strchr(tmp, ';');
2055 if ((time(NULL) - expired) < 0) {
2056 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2057 expired = time(NULL) + DUNDI_SECRET_TIME;
2058 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2067 /* Current key is still valid, just setup rotatation properly */
2068 ast_copy_string(cursecret, current, sizeof(cursecret));
2069 rotatetime = expired;
2071 /* Current key is out of date, rotate or eliminate all together */
2072 build_secret(cursecret, sizeof(cursecret));
2073 save_secret(cursecret, last);
2077 static void check_password(void)
2084 printf("%ld/%ld\n", now, rotatetime);
2086 if ((now - rotatetime) >= 0) {
2087 /* Time to rotate keys */
2088 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2089 build_secret(cursecret, sizeof(cursecret));
2090 save_secret(cursecret, oldsecret);
2094 static void *network_thread(void *ignore)
2096 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2097 from the network, and queue them for delivery to the channels */
2099 /* Establish I/O callback for socket read */
2100 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2102 res = ast_sched_wait(sched);
2103 if ((res > 1000) || (res < 0))
2105 res = ast_io_wait(io, res);
2107 ast_mutex_lock(&peerlock);
2108 ast_sched_runq(sched);
2109 ast_mutex_unlock(&peerlock);
2116 static void *process_precache(void *ign)
2118 struct dundi_precache_queue *qe;
2126 ast_mutex_lock(&pclock);
2128 if (!pcq->expiration) {
2129 /* Gone... Remove... */
2133 } else if (pcq->expiration < now) {
2134 /* Process this entry */
2135 pcq->expiration = 0;
2136 ast_copy_string(context, pcq->context, sizeof(context));
2137 ast_copy_string(number, pcq->number, sizeof(number));
2141 ast_mutex_unlock(&pclock);
2143 dundi_precache(context, number);
2150 static int start_network_thread(void)
2152 ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
2153 ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
2157 static int dundi_do_debug(int fd, int argc, char *argv[])
2160 return RESULT_SHOWUSAGE;
2162 ast_cli(fd, "DUNDi Debugging Enabled\n");
2163 return RESULT_SUCCESS;
2166 static int dundi_do_store_history(int fd, int argc, char *argv[])
2169 return RESULT_SHOWUSAGE;
2170 global_storehistory = 1;
2171 ast_cli(fd, "DUNDi History Storage Enabled\n");
2172 return RESULT_SUCCESS;
2175 static int dundi_flush(int fd, int argc, char *argv[])
2178 if ((argc < 2) || (argc > 3))
2179 return RESULT_SHOWUSAGE;
2181 if (!strcasecmp(argv[2], "stats"))
2184 return RESULT_SHOWUSAGE;
2187 /* Flush statistics */
2188 struct dundi_peer *p;
2190 ast_mutex_lock(&peerlock);
2193 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2195 free(p->lookups[x]);
2196 p->lookups[x] = NULL;
2197 p->lookuptimes[x] = 0;
2202 ast_mutex_unlock(&peerlock);
2204 ast_db_deltree("dundi/cache", NULL);
2205 ast_cli(fd, "DUNDi Cache Flushed\n");
2207 return RESULT_SUCCESS;
2210 static int dundi_no_debug(int fd, int argc, char *argv[])
2213 return RESULT_SHOWUSAGE;
2215 ast_cli(fd, "DUNDi Debugging Disabled\n");
2216 return RESULT_SUCCESS;
2219 static int dundi_no_store_history(int fd, int argc, char *argv[])
2222 return RESULT_SHOWUSAGE;
2223 global_storehistory = 0;
2224 ast_cli(fd, "DUNDi History Storage Disabled\n");
2225 return RESULT_SUCCESS;
2228 static char *model2str(int model)
2231 case DUNDI_MODEL_INBOUND:
2233 case DUNDI_MODEL_OUTBOUND:
2235 case DUNDI_MODEL_SYMMETRIC:
2242 static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
2246 struct dundi_peer *p;
2250 ast_mutex_lock(&peerlock);
2253 if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
2254 if (++which > state)
2260 ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
2263 ast_mutex_unlock(&peerlock);
2267 static char *complete_peer_4(char *line, char *word, int pos, int state)
2269 return complete_peer_helper(line, word, pos, state, 3);
2272 static int rescomp(const void *a, const void *b)
2274 const struct dundi_result *resa, *resb;
2277 if (resa->weight < resb->weight)
2279 if (resa->weight > resb->weight)
2284 static void sort_results(struct dundi_result *results, int count)
2286 qsort(results, count, sizeof(results[0]), rescomp);
2289 static int dundi_do_lookup(int fd, int argc, char *argv[])
2297 struct dundi_result dr[MAX_RESULTS];
2298 struct timeval start;
2299 if ((argc < 3) || (argc > 4))
2300 return RESULT_SHOWUSAGE;
2302 if (!strcasecmp(argv[3], "bypass"))
2305 return RESULT_SHOWUSAGE;
2307 ast_copy_string(tmp, argv[2], sizeof(tmp));
2308 context = strchr(tmp, '@');
2313 start = ast_tvnow();
2314 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2317 ast_cli(fd, "DUNDi lookup returned error.\n");
2319 ast_cli(fd, "DUNDi lookup returned no results.\n");
2321 sort_results(dr, res);
2322 for (x=0;x<res;x++) {
2323 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));
2324 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2326 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2327 return RESULT_SUCCESS;
2330 static int dundi_do_precache(int fd, int argc, char *argv[])
2335 struct timeval start;
2336 if ((argc < 3) || (argc > 3))
2337 return RESULT_SHOWUSAGE;
2338 ast_copy_string(tmp, argv[2], sizeof(tmp));
2339 context = strchr(tmp, '@');
2344 start = ast_tvnow();
2345 res = dundi_precache(context, tmp);
2348 ast_cli(fd, "DUNDi precache returned error.\n");
2350 ast_cli(fd, "DUNDi precache returned no error.\n");
2351 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2352 return RESULT_SUCCESS;
2355 static int dundi_do_query(int fd, int argc, char *argv[])
2361 struct dundi_entity_info dei;
2362 if ((argc < 3) || (argc > 3))
2363 return RESULT_SHOWUSAGE;
2364 if (dundi_str_to_eid(&eid, argv[2])) {
2365 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2366 return RESULT_SHOWUSAGE;
2368 ast_copy_string(tmp, argv[2], sizeof(tmp));
2369 context = strchr(tmp, '@');
2374 res = dundi_query_eid(&dei, context, eid);
2376 ast_cli(fd, "DUNDi Query EID returned error.\n");
2378 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2380 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2381 ast_cli(fd, "Department: %s\n", dei.orgunit);
2382 ast_cli(fd, "Organization: %s\n", dei.org);
2383 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2384 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2385 ast_cli(fd, "Country: %s\n", dei.country);
2386 ast_cli(fd, "E-mail: %s\n", dei.email);
2387 ast_cli(fd, "Phone: %s\n", dei.phone);
2388 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2390 return RESULT_SUCCESS;
2393 static int dundi_show_peer(int fd, int argc, char *argv[])
2395 struct dundi_peer *peer;
2396 struct permission *p;
2398 char iabuf[INET_ADDRSTRLEN];
2403 return RESULT_SHOWUSAGE;
2404 ast_mutex_lock(&peerlock);
2407 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2412 switch(peer->order) {
2417 order = "Secondary";
2423 order = "Quartiary";
2428 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2429 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2430 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2431 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2432 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2433 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2434 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2435 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2436 if (peer->include) {
2437 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2441 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2445 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2449 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2453 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2454 if (peer->lookups[x]) {
2456 ast_cli(fd, "Last few query times:\n");
2457 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2462 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2464 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2465 ast_mutex_unlock(&peerlock);
2466 return RESULT_SUCCESS;
2469 static int dundi_show_peers(int fd, int argc, char *argv[])
2471 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2472 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2473 struct dundi_peer *peer;
2474 char iabuf[INET_ADDRSTRLEN];
2475 int registeredonly=0;
2478 int online_peers = 0;
2479 int offline_peers = 0;
2480 int unmonitored_peers = 0;
2481 int total_peers = 0;
2483 if ((argc != 3) && (argc != 4) && (argc != 5))
2484 return RESULT_SHOWUSAGE;
2486 if (!strcasecmp(argv[3], "registered")) {
2489 return RESULT_SHOWUSAGE;
2491 ast_mutex_lock(&peerlock);
2492 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2493 for (peer = peers;peer;peer = peer->next) {
2495 int print_line = -1;
2498 if (registeredonly && !peer->addr.sin_addr.s_addr)
2501 if (peer->lastms < 0) {
2502 strcpy(status, "UNREACHABLE");
2505 else if (peer->lastms > peer->maxms) {
2506 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2509 else if (peer->lastms) {
2510 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2514 strcpy(status, "UNKNOWN");
2518 strcpy(status, "Unmonitored");
2519 unmonitored_peers++;
2522 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2524 strcpy(avgms, "Unavail");
2525 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2526 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2527 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2530 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2532 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2534 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2542 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2543 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2544 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2547 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2548 ast_mutex_unlock(&peerlock);
2549 return RESULT_SUCCESS;
2554 static int dundi_show_trans(int fd, int argc, char *argv[])
2556 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2557 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2558 struct dundi_transaction *trans;
2559 char iabuf[INET_ADDRSTRLEN];
2561 return RESULT_SHOWUSAGE;
2562 ast_mutex_lock(&peerlock);
2563 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2564 for (trans = alltrans;trans;trans = trans->allnext) {
2565 ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
2566 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2568 ast_mutex_unlock(&peerlock);
2569 return RESULT_SUCCESS;
2574 static int dundi_show_entityid(int fd, int argc, char *argv[])
2578 return RESULT_SHOWUSAGE;
2579 ast_mutex_lock(&peerlock);
2580 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2581 ast_mutex_unlock(&peerlock);
2582 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2583 return RESULT_SUCCESS;
2586 static int dundi_show_requests(int fd, int argc, char *argv[])
2588 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2589 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2590 struct dundi_request *req;
2593 return RESULT_SHOWUSAGE;
2594 ast_mutex_lock(&peerlock);
2595 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2596 for (req = requests;req;req = req->next) {
2597 ast_cli(fd, FORMAT, req->number, req->dcontext,
2598 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2600 ast_mutex_unlock(&peerlock);
2601 return RESULT_SUCCESS;
2606 /* Grok-a-dial DUNDi */
2608 static int dundi_show_mappings(int fd, int argc, char *argv[])
2610 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2611 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2612 struct dundi_mapping *map;
2615 return RESULT_SHOWUSAGE;
2616 ast_mutex_lock(&peerlock);
2617 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2618 for (map = mappings;map;map = map->next) {
2619 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2620 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2621 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2623 ast_mutex_unlock(&peerlock);
2624 return RESULT_SUCCESS;
2629 static int dundi_show_precache(int fd, int argc, char *argv[])
2631 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2632 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2633 struct dundi_precache_queue *qe;
2638 return RESULT_SHOWUSAGE;
2640 ast_mutex_lock(&pclock);
2641 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2642 for (qe = pcq;qe;qe = qe->next) {
2643 s = qe->expiration - now;
2648 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2650 ast_mutex_unlock(&pclock);
2651 return RESULT_SUCCESS;
2656 static char debug_usage[] =
2657 "Usage: dundi debug\n"
2658 " Enables dumping of DUNDi packets for debugging purposes\n";
2660 static char no_debug_usage[] =
2661 "Usage: dundi no debug\n"
2662 " Disables dumping of DUNDi packets for debugging purposes\n";
2664 static char store_history_usage[] =
2665 "Usage: dundi store history\n"
2666 " Enables storing of DUNDi requests and times for debugging\n"
2669 static char no_store_history_usage[] =
2670 "Usage: dundi no store history\n"
2671 " Disables storing of DUNDi requests and times for debugging\n"
2674 static char show_peers_usage[] =
2675 "Usage: dundi show peers\n"
2676 " Lists all known DUNDi peers.\n";
2678 static char show_trans_usage[] =
2679 "Usage: dundi show trans\n"
2680 " Lists all known DUNDi transactions.\n";
2682 static char show_mappings_usage[] =
2683 "Usage: dundi show mappings\n"
2684 " Lists all known DUNDi mappings.\n";
2686 static char show_precache_usage[] =
2687 "Usage: dundi show precache\n"
2688 " Lists all known DUNDi scheduled precache updates.\n";
2690 static char show_entityid_usage[] =
2691 "Usage: dundi show entityid\n"
2692 " Displays the global entityid for this host.\n";
2694 static char show_peer_usage[] =
2695 "Usage: dundi show peer [peer]\n"
2696 " Provide a detailed description of a specifid DUNDi peer.\n";
2698 static char show_requests_usage[] =
2699 "Usage: dundi show requests\n"
2700 " Lists all known pending DUNDi requests.\n";
2702 static char lookup_usage[] =
2703 "Usage: dundi lookup <number>[@context] [bypass]\n"
2704 " Lookup the given number within the given DUNDi context\n"
2705 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2706 "keyword is specified.\n";
2708 static char precache_usage[] =
2709 "Usage: dundi precache <number>[@context]\n"
2710 " Lookup the given number within the given DUNDi context\n"
2711 "(or e164 if none is specified) and precaches the results to any\n"
2712 "upstream DUNDi push servers.\n";
2714 static char query_usage[] =
2715 "Usage: dundi query <entity>[@context]\n"
2716 " Attempts to retrieve contact information for a specific\n"
2717 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2718 "e164 if none is specified).\n";
2720 static char flush_usage[] =
2721 "Usage: dundi flush [stats]\n"
2722 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2723 "'stats' is present, clears timer statistics instead of normal\n"
2726 static struct ast_cli_entry cli_debug =
2727 { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2729 static struct ast_cli_entry cli_store_history =
2730 { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2732 static struct ast_cli_entry cli_no_store_history =
2733 { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2735 static struct ast_cli_entry cli_flush =
2736 { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2738 static struct ast_cli_entry cli_no_debug =
2739 { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2741 static struct ast_cli_entry cli_show_peers =
2742 { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2744 static struct ast_cli_entry cli_show_trans =
2745 { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2747 static struct ast_cli_entry cli_show_entityid =
2748 { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2750 static struct ast_cli_entry cli_show_mappings =
2751 { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2753 static struct ast_cli_entry cli_show_precache =
2754 { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2756 static struct ast_cli_entry cli_show_requests =
2757 { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2759 static struct ast_cli_entry cli_show_peer =
2760 { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2762 static struct ast_cli_entry cli_lookup =
2763 { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2765 static struct ast_cli_entry cli_precache =
2766 { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2768 static struct ast_cli_entry cli_queryeid =
2769 { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2771 STANDARD_LOCAL_USER;
2775 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2777 struct dundi_transaction *trans;
2780 /* Don't allow creation of transactions to non-registered peers */
2781 if (p && !p->addr.sin_addr.s_addr)
2783 tid = get_trans_id();
2786 trans = malloc(sizeof(struct dundi_transaction));
2788 memset(trans, 0, sizeof(struct dundi_transaction));
2789 if (global_storehistory) {
2790 trans->start = ast_tvnow();
2791 ast_set_flag(trans, FLAG_STOREHIST);
2793 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2794 trans->autokillid = -1;
2796 apply_peer(trans, p);
2797 if (!p->sentfullkey)
2798 ast_set_flag(trans, FLAG_SENDFULLKEY);
2800 trans->strans = tid;
2801 trans->allnext = alltrans;
2807 static int dundi_xmit(struct dundi_packet *pack)
2810 char iabuf[INET_ADDRSTRLEN];
2812 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2813 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2815 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2816 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2817 ntohs(pack->parent->addr.sin_port), strerror(errno));
2824 static void destroy_packet(struct dundi_packet *pack, int needfree)
2826 struct dundi_packet *prev, *cur;
2829 cur = pack->parent->packets;
2833 prev->next = cur->next;
2835 pack->parent->packets = cur->next;
2842 if (pack->retransid > -1)
2843 ast_sched_del(sched, pack->retransid);
2847 pack->retransid = -1;
2852 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2854 struct dundi_transaction *cur, *prev;
2855 struct dundi_peer *peer;
2860 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2863 if (peer->regtrans == trans)
2864 peer->regtrans = NULL;
2865 if (peer->keypending == trans)
2866 peer->keypending = NULL;
2867 if (peer->qualtrans == trans) {
2869 if (peer->lastms > -1)
2870 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2873 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2876 if (ms < peer->maxms) {
2877 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2878 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2879 } else if (peer->lastms < peer->maxms) {
2880 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);
2884 peer->qualtrans = NULL;
2886 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2887 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2888 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2891 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2892 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2893 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2894 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2895 peer->lookups[x] = peer->lookups[x-1];
2896 if (peer->lookups[x]) {
2897 peer->avgms += peer->lookuptimes[x];
2901 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2902 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2903 if (peer->lookups[0]) {
2904 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2905 peer->avgms += peer->lookuptimes[0];
2916 if (trans->parent) {
2917 /* Unlink from parent if appropriate */
2919 cur = trans->parent->trans;