2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Distributed Universal Number Discovery (DUNDi)
23 * \todo XXX This module leaks most of the memory it allocates on unload.
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <sys/socket.h>
43 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
44 #include <sys/types.h>
45 #include <netinet/in_systm.h>
47 #include <netinet/ip.h>
48 #include <sys/ioctl.h>
49 #include <netinet/in.h>
51 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
52 #include <net/if_dl.h>
56 #include <sys/signal.h>
59 #include "asterisk/file.h"
60 #include "asterisk/logger.h"
61 #include "asterisk/channel.h"
62 #include "asterisk/config.h"
63 #include "asterisk/options.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/module.h"
66 #include "asterisk/frame.h"
67 #include "asterisk/file.h"
68 #include "asterisk/cli.h"
69 #include "asterisk/lock.h"
70 #include "asterisk/md5.h"
71 #include "asterisk/dundi.h"
72 #include "asterisk/sched.h"
73 #include "asterisk/io.h"
74 #include "asterisk/utils.h"
75 #include "asterisk/netsock.h"
76 #include "asterisk/crypto.h"
77 #include "asterisk/astdb.h"
78 #include "asterisk/acl.h"
79 #include "asterisk/aes.h"
80 #include "asterisk/app.h"
82 #include "dundi-parser.h"
84 #define MAX_RESULTS 64
86 #define MAX_PACKET_SIZE 8192
88 #define MAX_WEIGHT 59999
90 #define DUNDI_MODEL_INBOUND (1 << 0)
91 #define DUNDI_MODEL_OUTBOUND (1 << 1)
92 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
94 /*! Keep times of last 10 lookups */
95 #define DUNDI_TIMING_HISTORY 10
98 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
99 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
100 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
101 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
102 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */
103 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
104 FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
107 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
110 #define DUNDI_SECRET_TIME 15 /* Testing only */
112 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
115 static struct io_context *io;
116 static struct sched_context *sched;
117 static int netsocket = -1;
118 static pthread_t netthreadid = AST_PTHREADT_NULL;
119 static pthread_t precachethreadid = AST_PTHREADT_NULL;
121 static int dundidebug = 0;
122 static int authdebug = 0;
123 static int dundi_ttl = DUNDI_DEFAULT_TTL;
124 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
125 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
126 static int global_autokilltimeout = 0;
127 static dundi_eid global_eid;
128 static int default_expiration = 60;
129 static int global_storehistory = 0;
130 static char dept[80];
132 static char locality[80];
133 static char stateprov[80];
134 static char country[80];
135 static char email[80];
136 static char phone[80];
137 static char secretpath[80];
138 static char cursecret[80];
139 static char ipaddr[80];
140 static time_t rotatetime;
141 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
142 static int dundi_shutdown = 0;
145 AST_LIST_ENTRY(permission) list;
150 struct dundi_packet {
151 AST_LIST_ENTRY(dundi_packet) list;
154 struct dundi_transaction *parent;
157 unsigned char data[0];
160 struct dundi_hint_metadata {
161 unsigned short flags;
162 char exten[AST_MAX_EXTENSION];
165 struct dundi_precache_queue {
166 AST_LIST_ENTRY(dundi_precache_queue) list;
172 struct dundi_request;
174 struct dundi_transaction {
175 struct sockaddr_in addr; /*!< Other end of transaction */
176 struct timeval start; /*!< When this transaction was created */
177 dundi_eid eids[DUNDI_MAX_STACK + 1];
178 int eidcount; /*!< Number of eids in eids */
179 dundi_eid us_eid; /*!< Our EID, to them */
180 dundi_eid them_eid; /*!< Their EID, to us */
181 ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
182 ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
183 unsigned int flags; /*!< Has final packet been sent */
184 int ttl; /*!< Remaining TTL for queries on this one */
185 int thread; /*!< We have a calling thread */
186 int retranstimer; /*!< How long to wait before retransmissions */
187 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
188 int autokilltimeout; /*!< Recommended timeout for autokill */
189 unsigned short strans; /*!< Our transaction identifier */
190 unsigned short dtrans; /*!< Their transaction identifer */
191 unsigned char iseqno; /*!< Next expected received seqno */
192 unsigned char oiseqno; /*!< Last received incoming seqno */
193 unsigned char oseqno; /*!< Next transmitted seqno */
194 unsigned char aseqno; /*!< Last acknowledge seqno */
195 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
196 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
197 struct dundi_request *parent; /*!< Parent request (if there is one) */
198 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
199 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
202 struct dundi_request {
203 char dcontext[AST_MAX_EXTENSION];
204 char number[AST_MAX_EXTENSION];
207 struct dundi_result *dr;
208 struct dundi_entity_info *dei;
209 struct dundi_hint_metadata *hmd;
215 unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */
216 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
217 AST_LIST_ENTRY(dundi_request) list;
220 struct dundi_mapping {
221 char dcontext[AST_MAX_EXTENSION];
222 char lcontext[AST_MAX_EXTENSION];
228 char dest[AST_MAX_EXTENSION];
229 AST_LIST_ENTRY(dundi_mapping) list;
234 struct sockaddr_in addr; /*!< Address of DUNDi peer */
235 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
236 struct permissionlist include;
245 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
246 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
247 unsigned long us_keycrc32; /*!< CRC-32 of our key */
248 ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
249 ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
250 unsigned long them_keycrc32; /*!< CRC-32 of our key */
251 ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
252 ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
253 time_t keyexpire; /*!< When to expire/recreate key */
255 int lookuptimes[DUNDI_TIMING_HISTORY];
256 char *lookups[DUNDI_TIMING_HISTORY];
258 struct dundi_transaction *regtrans; /*!< Registration transaction */
259 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
260 int model; /*!< Pull model */
261 int pcmodel; /*!< Push/precache model */
262 /*! Dynamic peers register with us */
263 unsigned int dynamic:1;
264 int lastms; /*!< Last measured latency */
265 int maxms; /*!< Max permissible latency */
266 struct timeval qualtx; /*!< Time of transmit */
267 AST_LIST_ENTRY(dundi_peer) list;
270 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
271 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
272 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
273 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
274 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
276 static int dundi_xmit(struct dundi_packet *pack);
278 static void dundi_debug_output(const char *data)
281 ast_verbose("%s", data);
284 static void dundi_error_output(const char *data)
286 ast_log(LOG_WARNING, "%s", data);
289 static int has_permission(struct permissionlist *permlist, char *cont)
291 struct permission *perm;
294 AST_LIST_TRAVERSE(permlist, perm, list) {
295 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
302 static char *tech2str(int tech)
305 case DUNDI_PROTO_NONE:
307 case DUNDI_PROTO_IAX:
309 case DUNDI_PROTO_SIP:
311 case DUNDI_PROTO_H323:
318 static int str2tech(char *str)
320 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
321 return DUNDI_PROTO_IAX;
322 else if (!strcasecmp(str, "SIP"))
323 return DUNDI_PROTO_SIP;
324 else if (!strcasecmp(str, "H323"))
325 return DUNDI_PROTO_H323;
330 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[]);
331 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
332 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
333 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
335 struct dundi_transaction *trans;
337 /* Look for an exact match first */
338 AST_LIST_TRAVERSE(&alltrans, trans, all) {
339 if (!inaddrcmp(&trans->addr, sin) &&
340 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
341 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
343 trans->dtrans = ntohs(hdr->strans) & 32767;
348 switch(hdr->cmdresp & 0x7f) {
349 case DUNDI_COMMAND_DPDISCOVER:
350 case DUNDI_COMMAND_EIDQUERY:
351 case DUNDI_COMMAND_PRECACHERQ:
352 case DUNDI_COMMAND_REGREQ:
353 case DUNDI_COMMAND_NULL:
354 case DUNDI_COMMAND_ENCRYPT:
357 /* Create new transaction */
358 if (!(trans = create_transaction(NULL)))
360 memcpy(&trans->addr, sin, sizeof(trans->addr));
361 trans->dtrans = ntohs(hdr->strans) & 32767;
369 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
371 static int dundi_ack(struct dundi_transaction *trans, int final)
373 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
375 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
378 struct dundi_packet pack;
379 struct dundi_hdr hdr;
381 struct dundi_transaction trans;
382 /* Never respond to an INVALID with another INVALID */
383 if (h->cmdresp == DUNDI_COMMAND_INVALID)
385 memset(&tmp, 0, sizeof(tmp));
386 memset(&trans, 0, sizeof(trans));
387 memcpy(&trans.addr, sin, sizeof(trans.addr));
388 tmp.hdr.strans = h->dtrans;
389 tmp.hdr.dtrans = h->strans;
390 tmp.hdr.iseqno = h->oseqno;
391 tmp.hdr.oseqno = h->iseqno;
392 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
393 tmp.hdr.cmdflags = 0;
394 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
395 tmp.pack.datalen = sizeof(struct dundi_hdr);
396 tmp.pack.parent = &trans;
397 dundi_xmit(&tmp.pack);
400 static void reset_global_eid(void)
402 #if defined(SIOCGIFHWADDR)
407 s = socket(AF_INET, SOCK_STREAM, 0);
410 for (x = 0; x < 10; x++) {
411 memset(&ifr, 0, sizeof(ifr));
412 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
413 if (ioctl(s, SIOCGIFHWADDR, &ifr))
415 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
417 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n",
418 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
425 #if defined(ifa_broadaddr) && !defined(SOLARIS)
427 struct ifaddrs *ifap;
429 if (getifaddrs(&ifap) == 0) {
431 for (p = ifap; p; p = p->ifa_next) {
432 if (p->ifa_addr->sa_family == AF_LINK) {
433 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
436 sdp->sdl_data + sdp->sdl_nlen, 6);
438 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);
447 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
450 static int get_trans_id(void)
452 struct dundi_transaction *t;
453 int stid = (ast_random() % 32766) + 1;
457 AST_LIST_TRAVERSE(&alltrans, t, all) {
458 if (t->strans == tid)
463 tid = (tid % 32766) + 1;
464 } while (tid != stid);
469 static int reset_transaction(struct dundi_transaction *trans)
472 tid = get_trans_id();
481 ast_clear_flag(trans, FLAG_FINAL);
485 static struct dundi_peer *find_peer(dundi_eid *eid)
487 struct dundi_peer *cur = NULL;
492 AST_LIST_TRAVERSE(&peers, cur, list) {
493 if (!dundi_eid_cmp(&cur->eid,eid))
500 static void build_iv(unsigned char *iv)
502 /* XXX Would be nice to be more random XXX */
503 unsigned int *fluffy;
505 fluffy = (unsigned int *)(iv);
507 fluffy[x] = ast_random();
510 struct dundi_query_state {
511 dundi_eid *eids[DUNDI_MAX_STACK + 1];
512 int directs[DUNDI_MAX_STACK + 1];
514 char called_context[AST_MAX_EXTENSION];
515 char called_number[AST_MAX_EXTENSION];
516 struct dundi_mapping *maps;
519 struct dundi_transaction *trans;
526 static int get_mapping_weight(struct dundi_mapping *map)
530 if (map->weightstr) {
531 pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
532 if (sscanf(buf, "%d", &map->_weight) != 1)
533 map->_weight = MAX_WEIGHT;
539 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)
541 struct ast_flags flags = {0};
543 if (!ast_strlen_zero(map->lcontext)) {
544 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
545 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
546 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
547 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
548 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
549 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
550 if (ast_ignore_pattern(map->lcontext, called_number))
551 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
553 /* Clearly we can't say 'don't ask' anymore if we found anything... */
554 if (ast_test_flag(&flags, AST_FLAGS_ALL))
555 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
557 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
558 /* Skip partial answers */
559 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
561 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
562 struct varshead headp;
563 struct ast_var_t *newvariable;
564 ast_set_flag(&flags, map->options & 0xffff);
565 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
566 dr[anscnt].techint = map->tech;
567 dr[anscnt].weight = get_mapping_weight(map);
568 dr[anscnt].expiration = dundi_cache_time;
569 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
570 dr[anscnt].eid = *us_eid;
571 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
572 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
573 AST_LIST_HEAD_INIT_NOLOCK(&headp);
574 newvariable = ast_var_assign("NUMBER", called_number);
575 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
576 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
577 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
578 newvariable = ast_var_assign("SECRET", cursecret);
579 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
580 newvariable = ast_var_assign("IPADDR", ipaddr);
581 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
582 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
583 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
584 ast_var_delete(newvariable);
586 dr[anscnt].dest[0] = '\0';
589 /* No answers... Find the fewest number of digits from the
590 number for which we have no answer. */
591 char tmp[AST_MAX_EXTENSION];
592 for (x=0;x<AST_MAX_EXTENSION;x++) {
593 tmp[x] = called_number[x];
596 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
597 /* Oops found something we can't match. If this is longer
598 than the running hint, we have to consider it */
599 if (strlen(tmp) > strlen(hmd->exten)) {
600 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
610 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
612 static void *dundi_lookup_thread(void *data)
614 struct dundi_query_state *st = data;
615 struct dundi_result dr[MAX_RESULTS];
616 struct dundi_ie_data ied;
617 struct dundi_hint_metadata hmd;
622 int expiration = dundi_cache_time;
625 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
626 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
627 memset(&ied, 0, sizeof(ied));
628 memset(&dr, 0, sizeof(dr));
629 memset(&hmd, 0, sizeof(hmd));
630 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
631 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
632 for (x=0;x<st->nummaps;x++)
633 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
636 for (x=0;x<ouranswers;x++) {
637 if (dr[x].weight < max)
642 /* If we do not have a canonical result, keep looking */
643 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);
645 /* Append answer in result */
648 if ((res < -1) && (!ouranswers))
649 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
652 AST_LIST_LOCK(&peers);
653 /* Truncate if "don't ask" isn't present */
654 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
656 if (ast_test_flag(st->trans, FLAG_DEAD)) {
658 ast_log(LOG_DEBUG, "Our transaction went away!\n");
659 st->trans->thread = 0;
660 destroy_trans(st->trans, 0);
662 for (x=0;x<ouranswers;x++) {
664 if (dr[x].expiration && (expiration > dr[x].expiration))
665 expiration = dr[x].expiration;
666 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
668 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
669 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
670 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
671 st->trans->thread = 0;
673 AST_LIST_UNLOCK(&peers);
678 static void *dundi_precache_thread(void *data)
680 struct dundi_query_state *st = data;
681 struct dundi_ie_data ied;
682 struct dundi_hint_metadata hmd;
686 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
687 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
688 memset(&ied, 0, sizeof(ied));
690 /* Now produce precache */
691 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
693 AST_LIST_LOCK(&peers);
694 /* Truncate if "don't ask" isn't present */
695 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
697 if (ast_test_flag(st->trans, FLAG_DEAD)) {
699 ast_log(LOG_DEBUG, "Our transaction went away!\n");
700 st->trans->thread = 0;
701 destroy_trans(st->trans, 0);
703 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
704 st->trans->thread = 0;
706 AST_LIST_UNLOCK(&peers);
711 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[]);
713 static void *dundi_query_thread(void *data)
715 struct dundi_query_state *st = data;
716 struct dundi_entity_info dei;
717 struct dundi_ie_data ied;
718 struct dundi_hint_metadata hmd;
723 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
724 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
725 memset(&ied, 0, sizeof(ied));
726 memset(&dei, 0, sizeof(dei));
727 memset(&hmd, 0, sizeof(hmd));
728 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
731 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
732 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
733 ast_copy_string(dei.org, org, sizeof(dei.org));
734 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
735 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
736 ast_copy_string(dei.country, country, sizeof(dei.country));
737 ast_copy_string(dei.email, email, sizeof(dei.email));
738 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
741 /* If we do not have a canonical result, keep looking */
742 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
744 AST_LIST_LOCK(&peers);
745 if (ast_test_flag(st->trans, FLAG_DEAD)) {
747 ast_log(LOG_DEBUG, "Our transaction went away!\n");
748 st->trans->thread = 0;
749 destroy_trans(st->trans, 0);
752 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
753 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
754 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
755 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
756 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
757 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
758 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
759 if (!ast_strlen_zero(dei.ipaddr))
760 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
762 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
763 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
764 st->trans->thread = 0;
766 AST_LIST_UNLOCK(&peers);
771 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
773 struct dundi_query_state *st;
779 pthread_t lookupthread;
781 if (ies->eidcount > 1) {
782 /* Since it is a requirement that the first EID is the authenticating host
783 and the last EID is the root, it is permissible that the first and last EID
784 could be the same. In that case, we should go ahead copy only the "root" section
785 since we will not need it for authentication. */
786 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
789 totallen = sizeof(struct dundi_query_state);
790 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
791 st = ast_calloc(1, totallen);
793 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
794 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
796 st->ttl = ies->ttl - 1;
800 for (x=skipfirst;ies->eids[x];x++) {
801 st->eids[x-skipfirst] = (dundi_eid *)s;
802 *st->eids[x-skipfirst] = *ies->eids[x];
803 s += sizeof(dundi_eid);
806 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);
809 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
810 struct dundi_ie_data ied = { 0, };
812 ast_log(LOG_WARNING, "Unable to create thread!\n");
814 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
815 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
819 struct dundi_ie_data ied = { 0, };
820 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
821 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
827 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
832 char eidpeer_str[20];
833 char eidroot_str[20];
838 expiration = dundi_cache_time;
840 /* Only cache hint if "don't ask" is there... */
841 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
844 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
846 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
847 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
848 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
849 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
852 timeout += expiration;
853 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
855 ast_db_put("dundi/cache", key1, data);
857 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
858 ast_db_put("dundi/cache", key2, data);
860 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
864 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
870 char eidpeer_str[20];
871 char eidroot_str[20];
875 expiration = dundi_cache_time;
877 /* Keep pushes a little longer, cut pulls a little short */
884 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
885 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
886 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
887 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
888 /* Build request string */
890 timeout += expiration;
891 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
892 for (x=start;x<req->respcount;x++) {
893 /* Skip anything with an illegal pipe in it */
894 if (strchr(req->dr[x].dest, '|'))
896 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
897 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
898 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
900 ast_db_put("dundi/cache", key1, data);
901 ast_db_put("dundi/cache", key2, data);
905 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
907 struct dundi_query_state *st;
910 struct dundi_ie_data ied;
912 struct dundi_result dr2[MAX_RESULTS];
913 struct dundi_request dr;
914 struct dundi_hint_metadata hmd;
916 struct dundi_mapping *cur;
920 pthread_t lookupthread;
922 memset(&dr2, 0, sizeof(dr2));
923 memset(&dr, 0, sizeof(dr));
924 memset(&hmd, 0, sizeof(hmd));
926 /* Forge request structure to hold answers for cache */
927 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
929 dr.maxcount = MAX_RESULTS;
930 dr.expiration = dundi_cache_time;
932 dr.pfds[0] = dr.pfds[1] = -1;
934 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
935 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
937 for (x=0;x<ies->anscount;x++) {
938 if (trans->parent->respcount < trans->parent->maxcount) {
939 /* Make sure it's not already there */
940 for (z=0;z<trans->parent->respcount;z++) {
941 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
942 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
945 if (z == trans->parent->respcount) {
946 /* Copy into parent responses */
947 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
948 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
949 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
950 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
951 if (ies->expiration > 0)
952 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
954 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
955 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
956 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
957 &ies->answers[x]->eid);
958 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
959 sizeof(trans->parent->dr[trans->parent->respcount].dest));
960 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
961 sizeof(trans->parent->dr[trans->parent->respcount].tech));
962 trans->parent->respcount++;
963 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
964 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
965 /* Update weight if appropriate */
966 trans->parent->dr[z].weight = ies->answers[x]->weight;
969 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
970 trans->parent->number, trans->parent->dcontext);
973 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
974 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
976 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
978 totallen = sizeof(struct dundi_query_state);
979 /* Count matching map entries */
981 AST_LIST_TRAVERSE(&mappings, cur, list) {
982 if (!strcasecmp(cur->dcontext, ccontext))
986 /* If no maps, return -1 immediately */
990 if (ies->eidcount > 1) {
991 /* Since it is a requirement that the first EID is the authenticating host
992 and the last EID is the root, it is permissible that the first and last EID
993 could be the same. In that case, we should go ahead copy only the "root" section
994 since we will not need it for authentication. */
995 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
999 /* Prepare to run a query and then propagate that as necessary */
1000 totallen += mapcount * sizeof(struct dundi_mapping);
1001 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1002 st = ast_calloc(1, totallen);
1004 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1005 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1007 st->ttl = ies->ttl - 1;
1008 st->nocache = ies->cbypass;
1012 for (x=skipfirst;ies->eids[x];x++) {
1013 st->eids[x-skipfirst] = (dundi_eid *)s;
1014 *st->eids[x-skipfirst] = *ies->eids[x];
1015 st->directs[x-skipfirst] = ies->eid_direct[x];
1016 s += sizeof(dundi_eid);
1018 /* Append mappings */
1020 st->maps = (struct dundi_mapping *)s;
1021 AST_LIST_TRAVERSE(&mappings, cur, list) {
1022 if (!strcasecmp(cur->dcontext, ccontext)) {
1025 st->maps[x].list.next = NULL;
1030 st->nummaps = mapcount;
1032 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1034 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1036 ast_log(LOG_WARNING, "Unable to create thread!\n");
1038 memset(&ied, 0, sizeof(ied));
1039 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1040 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1044 ast_log(LOG_WARNING, "Out of memory!\n");
1045 memset(&ied, 0, sizeof(ied));
1046 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1047 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1053 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1055 struct dundi_query_state *st;
1058 struct dundi_ie_data ied;
1060 struct dundi_mapping *cur;
1064 pthread_t lookupthread;
1065 totallen = sizeof(struct dundi_query_state);
1066 /* Count matching map entries */
1067 AST_LIST_TRAVERSE(&mappings, cur, list) {
1068 if (!strcasecmp(cur->dcontext, ccontext))
1071 /* If no maps, return -1 immediately */
1075 if (ies->eidcount > 1) {
1076 /* Since it is a requirement that the first EID is the authenticating host
1077 and the last EID is the root, it is permissible that the first and last EID
1078 could be the same. In that case, we should go ahead copy only the "root" section
1079 since we will not need it for authentication. */
1080 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1084 totallen += mapcount * sizeof(struct dundi_mapping);
1085 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1086 st = ast_calloc(1, totallen);
1088 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1089 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1091 st->ttl = ies->ttl - 1;
1092 st->nocache = ies->cbypass;
1096 for (x=skipfirst;ies->eids[x];x++) {
1097 st->eids[x-skipfirst] = (dundi_eid *)s;
1098 *st->eids[x-skipfirst] = *ies->eids[x];
1099 st->directs[x-skipfirst] = ies->eid_direct[x];
1100 s += sizeof(dundi_eid);
1102 /* Append mappings */
1104 st->maps = (struct dundi_mapping *)s;
1105 AST_LIST_TRAVERSE(&mappings, cur, list) {
1106 if (!strcasecmp(cur->dcontext, ccontext)) {
1109 st->maps[x].list.next = NULL;
1114 st->nummaps = mapcount;
1116 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1118 if (ast_pthread_create_detached(&lookupthread, NULL, 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;
1148 /* Build request string */
1149 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1152 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1153 int expiration = timeout - now;
1154 if (expiration > 0) {
1156 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1158 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1160 term = strchr(ptr, '|');
1163 src = strrchr(ptr, '/');
1170 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1171 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1172 /* Make sure it's not already there */
1173 for (z=0;z<req->respcount;z++) {
1174 if ((req->dr[z].techint == tech) &&
1175 !strcmp(req->dr[z].dest, ptr))
1178 if (z == req->respcount) {
1179 /* Copy into parent responses */
1180 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1181 req->dr[req->respcount].weight = weight;
1182 req->dr[req->respcount].techint = tech;
1183 req->dr[req->respcount].expiration = expiration;
1184 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1185 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1186 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1187 ast_copy_string(req->dr[req->respcount].dest, ptr,
1188 sizeof(req->dr[req->respcount].dest));
1189 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1190 sizeof(req->dr[req->respcount].tech));
1192 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1193 } else if (req->dr[z].weight > weight)
1194 req->dr[z].weight = weight;
1198 /* We found *something* cached */
1199 if (expiration < *lowexpiration)
1200 *lowexpiration = expiration;
1203 ast_db_del("dundi/cache", key);
1205 ast_db_del("dundi/cache", key);
1211 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1215 char eidroot_str[20];
1219 char eid_str_full[20];
1224 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1225 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1226 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1227 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1228 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1229 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1230 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1231 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1232 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1234 if (!req->respcount) {
1236 /* Look and see if we have a hint that would preclude us from looking at this
1237 peer for this number. */
1238 if (!(tmp[x] = req->number[x]))
1241 /* Check for hints */
1242 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1243 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1244 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1245 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1246 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1247 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1249 if (strlen(tmp) > strlen(req->hmd->exten)) {
1250 /* Update meta data if appropriate */
1251 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1261 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1263 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1265 if (!trans->addr.sin_addr.s_addr)
1266 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1267 trans->us_eid = p->us_eid;
1268 trans->them_eid = p->eid;
1269 /* Enable encryption if appropriate */
1270 if (!ast_strlen_zero(p->inkey))
1271 ast_set_flag(trans, FLAG_ENCRYPT);
1273 trans->autokilltimeout = p->maxms;
1274 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1275 if (p->lastms > 1) {
1276 trans->retranstimer = p->lastms * 2;
1277 /* Keep it from being silly */
1278 if (trans->retranstimer < 150)
1279 trans->retranstimer = 150;
1281 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1282 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1284 trans->autokilltimeout = global_autokilltimeout;
1287 /*! \note Called with the peers list already locked */
1288 static int do_register_expire(void *data)
1290 struct dundi_peer *peer = data;
1293 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1294 peer->registerexpire = -1;
1296 memset(&peer->addr, 0, sizeof(peer->addr));
1300 static int update_key(struct dundi_peer *peer)
1302 unsigned char key[16];
1303 struct ast_key *ekey, *skey;
1306 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1308 ast_aes_encrypt_key(key, &peer->us_ecx);
1309 ast_aes_decrypt_key(key, &peer->us_dcx);
1310 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1312 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1313 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1316 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1318 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1319 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1322 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1323 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1326 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1327 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1330 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1331 peer->sentfullkey = 0;
1333 time(&peer->keyexpire);
1334 peer->keyexpire += dundi_key_ttl;
1339 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1341 unsigned char curblock[16];
1343 memcpy(curblock, iv, sizeof(curblock));
1346 curblock[x] ^= src[x];
1347 ast_aes_encrypt(curblock, dst, ecx);
1348 memcpy(curblock, dst, sizeof(curblock));
1355 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1357 unsigned char lastblock[16];
1359 memcpy(lastblock, iv, sizeof(lastblock));
1361 ast_aes_decrypt(src, dst, dcx);
1363 dst[x] ^= lastblock[x];
1364 memcpy(lastblock, src, sizeof(lastblock));
1372 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)
1374 int space = *dstlen;
1375 unsigned long bytes;
1376 struct dundi_hdr *h;
1377 unsigned char *decrypt_space;
1378 decrypt_space = alloca(srclen);
1381 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1383 h = (struct dundi_hdr *)dst;
1386 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1388 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1392 *dstlen = bytes + 6;
1393 /* Return new header */
1397 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1399 unsigned char *compress_space;
1402 unsigned long bytes;
1403 struct dundi_ie_data ied;
1404 struct dundi_peer *peer;
1405 unsigned char iv[16];
1406 len = pack->datalen + pack->datalen / 100 + 42;
1407 compress_space = alloca(len);
1408 if (compress_space) {
1409 memset(compress_space, 0, len);
1410 /* We care about everthing save the first 6 bytes of header */
1412 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1415 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1418 memset(&ied, 0, sizeof(ied));
1419 /* Say who we are */
1420 if (!pack->h->iseqno && !pack->h->oseqno) {
1421 /* Need the key in the first copy */
1422 if (!(peer = find_peer(&trans->them_eid)))
1424 if (update_key(peer))
1426 if (!peer->sentfullkey)
1427 ast_set_flag(trans, FLAG_SENDFULLKEY);
1428 /* Append key data */
1429 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1430 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1431 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1432 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1434 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1436 /* Setup contexts */
1437 trans->ecx = peer->us_ecx;
1438 trans->dcx = peer->us_dcx;
1440 /* We've sent the full key */
1441 peer->sentfullkey = 1;
1443 /* Build initialization vector */
1445 /* Add the field, rounded up to 16 bytes */
1446 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1448 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1449 ast_log(LOG_NOTICE, "Final packet too large!\n");
1452 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1453 ied.pos += ((bytes + 15) / 16) * 16;
1454 /* Reconstruct header */
1455 pack->datalen = sizeof(struct dundi_hdr);
1456 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1457 pack->h->cmdflags = 0;
1458 memcpy(pack->h->ies, ied.buf, ied.pos);
1459 pack->datalen += ied.pos;
1465 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1467 unsigned char dst[128];
1469 struct ast_key *key, *skey;
1472 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1473 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1476 } else if (!newkey || !newsig)
1478 if (!memcmp(peer->rxenckey, newkey, 128) &&
1479 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1480 /* By definition, a match */
1484 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1486 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1487 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1491 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1493 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1494 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1498 /* First check signature */
1499 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1503 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1506 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1509 /* Decrypted, passes signature */
1511 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1512 memcpy(peer->rxenckey, newkey, 128);
1513 memcpy(peer->rxenckey + 128, newsig, 128);
1514 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1515 ast_aes_decrypt_key(dst, &peer->them_dcx);
1516 ast_aes_encrypt_key(dst, &peer->them_ecx);
1520 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1522 /* Handle canonical command / response */
1523 int final = hdr->cmdresp & 0x80;
1524 int cmd = hdr->cmdresp & 0x7f;
1529 unsigned char *bufcpy;
1530 struct dundi_ie_data ied;
1531 struct dundi_ies ies;
1532 struct dundi_peer *peer;
1535 memset(&ied, 0, sizeof(ied));
1536 memset(&ies, 0, sizeof(ies));
1538 bufcpy = alloca(datalen);
1541 /* Make a copy for parsing */
1542 memcpy(bufcpy, hdr->ies, datalen);
1544 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1545 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1546 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1551 case DUNDI_COMMAND_DPDISCOVER:
1552 case DUNDI_COMMAND_EIDQUERY:
1553 case DUNDI_COMMAND_PRECACHERQ:
1554 if (cmd == DUNDI_COMMAND_EIDQUERY)
1555 resp = DUNDI_COMMAND_EIDRESPONSE;
1556 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1557 resp = DUNDI_COMMAND_PRECACHERP;
1559 resp = DUNDI_COMMAND_DPRESPONSE;
1560 /* A dialplan or entity discover -- qualify by highest level entity */
1561 peer = find_peer(ies.eids[0]);
1563 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1564 dundi_send(trans, resp, 0, 1, &ied);
1567 trans->us_eid = peer->us_eid;
1568 if (strlen(peer->inkey)) {
1569 hasauth = encrypted;
1573 /* Okay we're authentiated and all, now we check if they're authorized */
1574 if (!ies.called_context)
1575 ies.called_context = "e164";
1576 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1577 res = dundi_answer_entity(trans, &ies, ies.called_context);
1579 if (ast_strlen_zero(ies.called_number)) {
1580 /* They're not permitted to access that context */
1581 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1582 dundi_send(trans, resp, 0, 1, &ied);
1583 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1584 (peer->model & DUNDI_MODEL_INBOUND) &&
1585 has_permission(&peer->permit, ies.called_context)) {
1586 res = dundi_answer_query(trans, &ies, ies.called_context);
1588 /* There is no such dundi context */
1589 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1590 dundi_send(trans, resp, 0, 1, &ied);
1592 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1593 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1594 has_permission(&peer->include, ies.called_context)) {
1595 res = dundi_prop_precache(trans, &ies, ies.called_context);
1597 /* There is no such dundi context */
1598 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1599 dundi_send(trans, resp, 0, 1, &ied);
1602 /* They're not permitted to access that context */
1603 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1604 dundi_send(trans, resp, 0, 1, &ied);
1608 /* They're not permitted to access that context */
1609 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1610 dundi_send(trans, resp, 0, 1, &ied);
1614 case DUNDI_COMMAND_REGREQ:
1615 /* A register request -- should only have one entity */
1616 peer = find_peer(ies.eids[0]);
1617 if (!peer || !peer->dynamic) {
1618 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1619 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1622 trans->us_eid = peer->us_eid;
1623 if (!ast_strlen_zero(peer->inkey)) {
1624 hasauth = encrypted;
1628 int expire = default_expiration;
1631 if (peer->registerexpire > -1)
1632 ast_sched_del(sched, peer->registerexpire);
1633 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1634 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1635 ntohs(trans->addr.sin_port), expire);
1636 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1637 if (inaddrcmp(&peer->addr, &trans->addr)) {
1638 ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
1639 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1640 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1644 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1645 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1646 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1648 qualify_peer(peer, 1);
1652 case DUNDI_COMMAND_DPRESPONSE:
1653 /* A dialplan response, lets see what we got... */
1654 if (ies.cause < 1) {
1655 /* Success of some sort */
1657 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1658 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1659 authpass = encrypted;
1663 /* Pass back up answers */
1664 if (trans->parent && trans->parent->dr) {
1665 y = trans->parent->respcount;
1666 for (x=0;x<ies.anscount;x++) {
1667 if (trans->parent->respcount < trans->parent->maxcount) {
1668 /* Make sure it's not already there */
1669 for (z=0;z<trans->parent->respcount;z++) {
1670 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1671 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1674 if (z == trans->parent->respcount) {
1675 /* Copy into parent responses */
1676 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1677 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1678 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1679 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1680 if (ies.expiration > 0)
1681 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1683 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1684 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1685 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1686 &ies.answers[x]->eid);
1687 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1688 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1689 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1690 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1691 trans->parent->respcount++;
1692 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1693 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1694 /* Update weight if appropriate */
1695 trans->parent->dr[z].weight = ies.answers[x]->weight;
1698 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1699 trans->parent->number, trans->parent->dcontext);
1701 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1702 the cache know if this request was unaffected by our entity list. */
1703 cache_save(&trans->them_eid, trans->parent, y,
1704 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1706 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1707 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1708 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1709 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1710 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1711 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1712 sizeof(trans->parent->hmd->exten));
1715 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1718 if (ies.expiration > 0) {
1719 if (trans->parent->expiration > ies.expiration) {
1720 trans->parent->expiration = ies.expiration;
1724 /* Close connection if not final */
1726 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1730 /* Auth failure, check for data */
1732 /* Cancel if they didn't already */
1733 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1737 case DUNDI_COMMAND_EIDRESPONSE:
1738 /* A dialplan response, lets see what we got... */
1739 if (ies.cause < 1) {
1740 /* Success of some sort */
1742 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1743 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1744 authpass = encrypted;
1748 /* Pass back up answers */
1749 if (trans->parent && trans->parent->dei && ies.q_org) {
1750 if (!trans->parent->respcount) {
1751 trans->parent->respcount++;
1753 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1755 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1757 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1758 if (ies.q_stateprov)
1759 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1761 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1763 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1765 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1767 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1768 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1769 /* If it's them, update our address */
1770 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1774 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1775 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1778 /* Close connection if not final */
1780 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1784 /* Auth failure, check for data */
1786 /* Cancel if they didn't already */
1787 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1791 case DUNDI_COMMAND_REGRESPONSE:
1792 /* A dialplan response, lets see what we got... */
1793 if (ies.cause < 1) {
1795 /* Success of some sort */
1796 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1797 hasauth = encrypted;
1802 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1804 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1805 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1809 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),
1810 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1811 /* Close connection if not final */
1813 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1816 /* Auth failure, cancel if they didn't for some reason */
1818 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1822 case DUNDI_COMMAND_INVALID:
1823 case DUNDI_COMMAND_NULL:
1824 case DUNDI_COMMAND_PRECACHERP:
1825 /* Do nothing special */
1827 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1829 case DUNDI_COMMAND_ENCREJ:
1830 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1831 /* No really, it's over at this point */
1833 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1835 /* Send with full key */
1836 ast_set_flag(trans, FLAG_SENDFULLKEY);
1838 /* Ooops, we got a final message, start by sending ACK... */
1839 dundi_ack(trans, hdr->cmdresp & 0x80);
1840 trans->aseqno = trans->iseqno;
1841 /* Now, we gotta create a new transaction */
1842 if (!reset_transaction(trans)) {
1843 /* Make sure handle_frame doesn't destroy us */
1844 hdr->cmdresp &= 0x7f;
1845 /* Parse the message we transmitted */
1846 memset(&ies, 0, sizeof(ies));
1847 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1848 /* Reconstruct outgoing encrypted packet */
1849 memset(&ied, 0, sizeof(ied));
1850 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1851 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1852 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1854 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1855 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
1856 peer->sentfullkey = 1;
1861 case DUNDI_COMMAND_ENCRYPT:
1863 /* No nested encryption! */
1864 if ((trans->iseqno == 1) && !trans->oseqno) {
1865 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1866 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1867 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1869 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1873 apply_peer(trans, peer);
1874 /* Key passed, use new contexts for this session */
1875 trans->ecx = peer->them_ecx;
1876 trans->dcx = peer->them_dcx;
1878 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1879 struct dundi_hdr *dhdr;
1880 unsigned char decoded[MAX_PACKET_SIZE];
1882 ddatalen = sizeof(decoded);
1883 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1885 /* Handle decrypted response */
1887 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1888 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1889 /* Carry back final flag */
1890 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1894 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1899 /* Turn off encryption */
1900 ast_clear_flag(trans, FLAG_ENCRYPT);
1901 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1905 /* Send unknown command if we don't know it, with final flag IFF it's the
1906 first command in the dialog and only if we haven't recieved final notification */
1908 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1909 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1915 static void destroy_packet(struct dundi_packet *pack, int needfree);
1916 static void destroy_packets(struct packetlist *p)
1918 struct dundi_packet *pack;
1920 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1921 if (pack->retransid > -1)
1922 ast_sched_del(sched, pack->retransid);
1928 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1930 struct dundi_packet *pack;
1932 /* Ack transmitted packet corresponding to iseqno */
1933 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1934 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1935 destroy_packet(pack, 0);
1936 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1937 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1938 destroy_packets(&trans->lasttrans);
1940 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1941 if (trans->autokillid > -1)
1942 ast_sched_del(sched, trans->autokillid);
1943 trans->autokillid = -1;
1951 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1953 struct dundi_transaction *trans;
1954 trans = find_transaction(h, sin);
1956 dundi_reject(h, sin);
1959 /* Got a transaction, see where this header fits in */
1960 if (h->oseqno == trans->iseqno) {
1961 /* Just what we were looking for... Anything but ack increments iseqno */
1962 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1963 /* If final, we're done */
1964 destroy_trans(trans, 0);
1967 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1968 trans->oiseqno = trans->iseqno;
1970 handle_command_response(trans, h, datalen, 0);
1972 if (trans->aseqno != trans->iseqno) {
1973 dundi_ack(trans, h->cmdresp & 0x80);
1974 trans->aseqno = trans->iseqno;
1976 /* Delete any saved last transmissions */
1977 destroy_packets(&trans->lasttrans);
1978 if (h->cmdresp & 0x80) {
1979 /* Final -- destroy now */
1980 destroy_trans(trans, 0);
1982 } else if (h->oseqno == trans->oiseqno) {
1983 /* Last incoming sequence number -- send ACK without processing */
1984 dundi_ack(trans, 0);
1986 /* Out of window -- simply drop */
1988 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1993 static int socket_read(int *id, int fd, short events, void *cbdata)
1995 struct sockaddr_in sin;
1997 struct dundi_hdr *h;
1998 char buf[MAX_PACKET_SIZE];
1999 socklen_t len = sizeof(sin);
2001 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
2003 if (errno != ECONNREFUSED)
2004 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2007 if (res < sizeof(struct dundi_hdr)) {
2008 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2012 h = (struct dundi_hdr *) buf;
2014 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2015 AST_LIST_LOCK(&peers);
2016 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2017 AST_LIST_UNLOCK(&peers);
2021 static void build_secret(char *secret, int seclen)
2023 unsigned char tmp[16];
2027 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2028 /* Eliminate potential bad characters */
2029 while((s = strchr(secret, ';'))) *s = '+';
2030 while((s = strchr(secret, '/'))) *s = '+';
2031 while((s = strchr(secret, ':'))) *s = '+';
2032 while((s = strchr(secret, '@'))) *s = '+';
2036 static void save_secret(const char *newkey, const char *oldkey)
2040 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2042 snprintf(tmp, sizeof(tmp), "%s", newkey);
2043 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2044 ast_db_put(secretpath, "secret", tmp);
2045 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2046 ast_db_put(secretpath, "secretexpiry", tmp);
2049 static void load_password(void)
2056 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2057 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2058 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2059 current = strchr(tmp, ';');
2066 if ((time(NULL) - expired) < 0) {
2067 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2068 expired = time(NULL) + DUNDI_SECRET_TIME;
2069 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2078 /* Current key is still valid, just setup rotatation properly */
2079 ast_copy_string(cursecret, current, sizeof(cursecret));
2080 rotatetime = expired;
2082 /* Current key is out of date, rotate or eliminate all together */
2083 build_secret(cursecret, sizeof(cursecret));
2084 save_secret(cursecret, last);
2088 static void check_password(void)
2095 printf("%ld/%ld\n", now, rotatetime);
2097 if ((now - rotatetime) >= 0) {
2098 /* Time to rotate keys */
2099 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2100 build_secret(cursecret, sizeof(cursecret));
2101 save_secret(cursecret, oldsecret);
2105 static void *network_thread(void *ignore)
2107 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2108 from the network, and queue them for delivery to the channels */
2110 /* Establish I/O callback for socket read */
2111 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2113 while (!dundi_shutdown) {
2114 res = ast_sched_wait(sched);
2115 if ((res > 1000) || (res < 0))
2117 res = ast_io_wait(io, res);
2119 AST_LIST_LOCK(&peers);
2120 ast_sched_runq(sched);
2121 AST_LIST_UNLOCK(&peers);
2126 netthreadid = AST_PTHREADT_NULL;
2131 static void *process_precache(void *ign)
2133 struct dundi_precache_queue *qe;
2139 while (!dundi_shutdown) {
2142 AST_LIST_LOCK(&pcq);
2143 if ((qe = AST_LIST_FIRST(&pcq))) {
2144 if (!qe->expiration) {
2145 /* Gone... Remove... */
2146 AST_LIST_REMOVE_HEAD(&pcq, list);
2148 } else if (qe->expiration < now) {
2149 /* Process this entry */
2151 ast_copy_string(context, qe->context, sizeof(context));
2152 ast_copy_string(number, qe->number, sizeof(number));
2156 AST_LIST_UNLOCK(&pcq);
2158 dundi_precache(context, number);
2163 precachethreadid = AST_PTHREADT_NULL;
2168 static int start_network_thread(void)
2170 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2171 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2175 static int dundi_do_debug(int fd, int argc, char *argv[])
2178 return RESULT_SHOWUSAGE;
2180 ast_cli(fd, "DUNDi Debugging Enabled\n");
2181 return RESULT_SUCCESS;
2184 static int dundi_do_store_history(int fd, int argc, char *argv[])
2187 return RESULT_SHOWUSAGE;
2188 global_storehistory = 1;
2189 ast_cli(fd, "DUNDi History Storage Enabled\n");
2190 return RESULT_SUCCESS;
2193 static int dundi_flush(int fd, int argc, char *argv[])
2196 if ((argc < 2) || (argc > 3))
2197 return RESULT_SHOWUSAGE;
2199 if (!strcasecmp(argv[2], "stats"))
2202 return RESULT_SHOWUSAGE;
2205 /* Flush statistics */
2206 struct dundi_peer *p;
2208 AST_LIST_LOCK(&peers);
2209 AST_LIST_TRAVERSE(&peers, p, list) {
2210 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2212 free(p->lookups[x]);
2213 p->lookups[x] = NULL;
2214 p->lookuptimes[x] = 0;
2218 AST_LIST_UNLOCK(&peers);
2220 ast_db_deltree("dundi/cache", NULL);
2221 ast_cli(fd, "DUNDi Cache Flushed\n");
2223 return RESULT_SUCCESS;
2226 static int dundi_no_debug(int fd, int argc, char *argv[])
2229 return RESULT_SHOWUSAGE;
2231 ast_cli(fd, "DUNDi Debugging Disabled\n");
2232 return RESULT_SUCCESS;
2235 static int dundi_no_store_history(int fd, int argc, char *argv[])
2238 return RESULT_SHOWUSAGE;
2239 global_storehistory = 0;
2240 ast_cli(fd, "DUNDi History Storage Disabled\n");
2241 return RESULT_SUCCESS;
2244 static char *model2str(int model)
2247 case DUNDI_MODEL_INBOUND:
2249 case DUNDI_MODEL_OUTBOUND:
2251 case DUNDI_MODEL_SYMMETRIC:
2258 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2262 struct dundi_peer *p;
2267 AST_LIST_LOCK(&peers);
2269 AST_LIST_TRAVERSE(&peers, p, list) {
2270 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2271 if (!strncasecmp(word, s, len) && ++which > state)
2272 ret = ast_strdup(s);
2274 AST_LIST_UNLOCK(&peers);
2278 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2280 return complete_peer_helper(line, word, pos, state, 3);
2283 static int rescomp(const void *a, const void *b)
2285 const struct dundi_result *resa, *resb;
2288 if (resa->weight < resb->weight)
2290 if (resa->weight > resb->weight)
2295 static void sort_results(struct dundi_result *results, int count)
2297 qsort(results, count, sizeof(results[0]), rescomp);
2300 static int dundi_do_lookup(int fd, int argc, char *argv[])
2308 struct dundi_result dr[MAX_RESULTS];
2309 struct timeval start;
2310 if ((argc < 3) || (argc > 4))
2311 return RESULT_SHOWUSAGE;
2313 if (!strcasecmp(argv[3], "bypass"))
2316 return RESULT_SHOWUSAGE;
2318 ast_copy_string(tmp, argv[2], sizeof(tmp));
2319 context = strchr(tmp, '@');
2324 start = ast_tvnow();
2325 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2328 ast_cli(fd, "DUNDi lookup returned error.\n");
2330 ast_cli(fd, "DUNDi lookup returned no results.\n");
2332 sort_results(dr, res);
2333 for (x=0;x<res;x++) {
2334 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));
2335 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2337 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2338 return RESULT_SUCCESS;
2341 static int dundi_do_precache(int fd, int argc, char *argv[])
2346 struct timeval start;
2347 if ((argc < 3) || (argc > 3))
2348 return RESULT_SHOWUSAGE;
2349 ast_copy_string(tmp, argv[2], sizeof(tmp));
2350 context = strchr(tmp, '@');
2355 start = ast_tvnow();
2356 res = dundi_precache(context, tmp);
2359 ast_cli(fd, "DUNDi precache returned error.\n");
2361 ast_cli(fd, "DUNDi precache returned no error.\n");
2362 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2363 return RESULT_SUCCESS;
2366 static int dundi_do_query(int fd, int argc, char *argv[])
2372 struct dundi_entity_info dei;
2373 if ((argc < 3) || (argc > 3))
2374 return RESULT_SHOWUSAGE;
2375 if (dundi_str_to_eid(&eid, argv[2])) {
2376 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2377 return RESULT_SHOWUSAGE;
2379 ast_copy_string(tmp, argv[2], sizeof(tmp));
2380 context = strchr(tmp, '@');
2385 res = dundi_query_eid(&dei, context, eid);
2387 ast_cli(fd, "DUNDi Query EID returned error.\n");
2389 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2391 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2392 ast_cli(fd, "Department: %s\n", dei.orgunit);
2393 ast_cli(fd, "Organization: %s\n", dei.org);
2394 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2395 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2396 ast_cli(fd, "Country: %s\n", dei.country);
2397 ast_cli(fd, "E-mail: %s\n", dei.email);
2398 ast_cli(fd, "Phone: %s\n", dei.phone);
2399 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2401 return RESULT_SUCCESS;
2404 static int dundi_show_peer(int fd, int argc, char *argv[])
2406 struct dundi_peer *peer;
2407 struct permission *p;
2413 return RESULT_SHOWUSAGE;
2414 AST_LIST_LOCK(&peers);
2415 AST_LIST_TRAVERSE(&peers, peer, list) {
2416 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2420 switch(peer->order) {
2425 order = "Secondary";
2431 order = "Quartiary";
2436 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2437 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2438 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2439 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2440 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2441 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2442 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2443 if (!AST_LIST_EMPTY(&peer->include))
2444 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2445 AST_LIST_TRAVERSE(&peer->include, p, list)
2446 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2447 if (!AST_LIST_EMPTY(&peer->permit))
2448 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2449 AST_LIST_TRAVERSE(&peer->permit, p, list)
2450 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2452 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2453 if (peer->lookups[x]) {
2455 ast_cli(fd, "Last few query times:\n");
2456 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2461 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2463 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2464 AST_LIST_UNLOCK(&peers);
2465 return RESULT_SUCCESS;
2468 static int dundi_show_peers(int fd, int argc, char *argv[])
2470 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2471 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2472 struct dundi_peer *peer;
2473 int registeredonly=0;
2476 int online_peers = 0;
2477 int offline_peers = 0;
2478 int unmonitored_peers = 0;
2479 int total_peers = 0;
2481 if ((argc != 3) && (argc != 4) && (argc != 5))
2482 return RESULT_SHOWUSAGE;
2484 if (!strcasecmp(argv[3], "registered")) {
2487 return RESULT_SHOWUSAGE;
2489 AST_LIST_LOCK(&peers);
2490 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2491 AST_LIST_TRAVERSE(&peers, peer, list) {
2493 int print_line = -1;
2496 if (registeredonly && !peer->addr.sin_addr.s_addr)
2499 if (peer->lastms < 0) {
2500 strcpy(status, "UNREACHABLE");
2503 else if (peer->lastms > peer->maxms) {
2504 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2507 else if (peer->lastms) {
2508 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2512 strcpy(status, "UNKNOWN");
2516 strcpy(status, "Unmonitored");
2517 unmonitored_peers++;
2520 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2522 strcpy(avgms, "Unavail");
2523 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2524 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2525 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2528 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2530 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2532 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2540 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2541 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2542 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2545 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2546 AST_LIST_UNLOCK(&peers);
2547 return RESULT_SUCCESS;
2552 static int dundi_show_trans(int fd, int argc, char *argv[])
2554 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2555 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2556 struct dundi_transaction *trans;
2558 return RESULT_SHOWUSAGE;
2559 AST_LIST_LOCK(&peers);
2560 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2561 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2562 ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2563 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2565 AST_LIST_UNLOCK(&peers);
2566 return RESULT_SUCCESS;
2571 static int dundi_show_entityid(int fd, int argc, char *argv[])
2575 return RESULT_SHOWUSAGE;
2576 AST_LIST_LOCK(&peers);
2577 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2578 AST_LIST_UNLOCK(&peers);
2579 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2580 return RESULT_SUCCESS;
2583 static int dundi_show_requests(int fd, int argc, char *argv[])
2585 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2586 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2587 struct dundi_request *req;
2590 return RESULT_SHOWUSAGE;
2591 AST_LIST_LOCK(&peers);
2592 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2593 AST_LIST_TRAVERSE(&requests, req, list) {
2594 ast_cli(fd, FORMAT, req->number, req->dcontext,
2595 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2597 AST_LIST_UNLOCK(&peers);
2598 return RESULT_SUCCESS;
2603 /* Grok-a-dial DUNDi */
2605 static int dundi_show_mappings(int fd, int argc, char *argv[])
2607 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2608 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2609 struct dundi_mapping *map;
2613 return RESULT_SHOWUSAGE;
2614 AST_LIST_LOCK(&peers);
2615 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2616 AST_LIST_TRAVERSE(&mappings, map, list) {
2617 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map));
2618 ast_cli(fd, FORMAT, map->dcontext, weight,
2619 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2620 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2622 AST_LIST_UNLOCK(&peers);
2623 return RESULT_SUCCESS;
2628 static int dundi_show_precache(int fd, int argc, char *argv[])
2630 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2631 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2632 struct dundi_precache_queue *qe;
2637 return RESULT_SHOWUSAGE;
2639 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2640 AST_LIST_LOCK(&pcq);
2641 AST_LIST_TRAVERSE(&pcq, qe, list) {
2642 s = qe->expiration - now;
2647 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2649 AST_LIST_UNLOCK(&pcq);
2651 return RESULT_SUCCESS;
2656 static const char debug_usage[] =
2657 "Usage: dundi debug\n"
2658 " Enables dumping of DUNDi packets for debugging purposes\n";
2660 static const char no_debug_usage[] =
2661 "Usage: dundi no debug\n"
2662 " Disables dumping of DUNDi packets for debugging purposes\n";
2664 static const char store_history_usage[] =
2665 "Usage: dundi store history\n"
2666 " Enables storing of DUNDi requests and times for debugging\n"
2669 static const 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 const char show_peers_usage[] =
2675 "Usage: dundi show peers\n"
2676 " Lists all known DUNDi peers.\n";
2678 static const char show_trans_usage[] =
2679 "Usage: dundi show trans\n"
2680 " Lists all known DUNDi transactions.\n";
2682 static const char show_mappings_usage[] =
2683 "Usage: dundi show mappings\n"
2684 " Lists all known DUNDi mappings.\n";
2686 static const char show_precache_usage[] =
2687 "Usage: dundi show precache\n"
2688 " Lists all known DUNDi scheduled precache updates.\n";
2690 static const char show_entityid_usage[] =
2691 "Usage: dundi show entityid\n"
2692 " Displays the global entityid for this host.\n";
2694 static const char show_peer_usage[] =
2695 "Usage: dundi show peer [peer]\n"
2696 " Provide a detailed description of a specifid DUNDi peer.\n";
2698 static const char show_requests_usage[] =
2699 "Usage: dundi show requests\n"
2700 " Lists all known pending DUNDi requests.\n";
2702 static const 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 const 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 const 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 const 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_dundi[] = {
2727 { { "dundi", "debug", NULL },
2728 dundi_do_debug, "Enable DUNDi debugging",
2731 { { "dundi", "store", "history", NULL },
2732 dundi_do_store_history, "Enable DUNDi historic records",
2733 store_history_usage },
2735 { { "dundi", "no", "store", "history", NULL },
2736 dundi_no_store_history, "Disable DUNDi historic records",
2737 no_store_history_usage },
2739 { { "dundi", "flush", NULL },
2740 dundi_flush, "Flush DUNDi cache",
2743 { { "dundi", "no", "debug", NULL },
2744 dundi_no_debug, "Disable DUNDi debugging",
2747 { { "dundi", "show", "peers", NULL },
2748 dundi_show_peers, "Show defined DUNDi peers",
2751 { { "dundi", "show", "trans", NULL },
2752 dundi_show_trans, "Show active DUNDi transactions",
2755 { { "dundi", "show", "entityid", NULL },
2756 dundi_show_entityid, "Display Global Entity ID",
2757 show_entityid_usage },
2759 { { "dundi", "show", "mappings", NULL },
2760 dundi_show_mappings, "Show DUNDi mappings",
2761 show_mappings_usage },
2763 { { "dundi", "show", "precache", NULL },
2764 dundi_show_precache, "Show DUNDi precache",
2765 show_precache_usage },
2767 { { "dundi", "show", "requests", NULL },
2768 dundi_show_requests, "Show DUNDi requests",
2769 show_requests_usage },
2771 { { "dundi", "show", "peer", NULL },
2772 dundi_show_peer, "Show info on a specific DUNDi peer",
2773 show_peer_usage, complete_peer_4 },
2775 { { "dundi", "lookup", NULL },
2776 dundi_do_lookup, "Lookup a number in DUNDi",
2779 { { "dundi", "precache", NULL },
2780 dundi_do_precache, "Precache a number in DUNDi",
2783 { { "dundi", "query", NULL },
2784 dundi_do_query, "Query a DUNDi EID",
2788 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2790 struct dundi_transaction *trans;
2793 /* Don't allow creation of transactions to non-registered peers */
2794 if (p && !p->addr.sin_addr.s_addr)
2796 tid = get_trans_id();
2799 if (!(trans = ast_calloc(1, sizeof(*trans))))
2802 if (global_storehistory) {
2803 trans->start = ast_tvnow();
2804 ast_set_flag(trans, FLAG_STOREHIST);
2806 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2807 trans->autokillid = -1;
2809 apply_peer(trans, p);
2810 if (!p->sentfullkey)
2811 ast_set_flag(trans, FLAG_SENDFULLKEY);
2813 trans->strans = tid;
2814 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2819 static int dundi_xmit(struct dundi_packet *pack)
2823 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2824 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2826 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2827 ast_inet_ntoa(pack->parent->addr.sin_addr),
2828 ntohs(pack->parent->addr.sin_port), strerror(errno));
2835 static void destroy_packet(struct dundi_packet *pack, int needfree)
2838 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2839 if (pack->retransid > -1)
2840 ast_sched_del(sched, pack->retransid);
2844 pack->retransid = -1;
2847 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2849 struct dundi_peer *peer;
2854 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2855 AST_LIST_TRAVERSE(&peers, peer, list) {
2856 if (peer->regtrans == trans)
2857 peer->regtrans = NULL;
2858 if (peer->qualtrans == trans) {
2860 if (peer->lastms > -1)
2861 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2864 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2867 if (ms < peer->maxms) {
2868 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2869 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2870 } else if (peer->lastms < peer->maxms) {
2871 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);
2875 peer->qualtrans = NULL;
2877 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2878 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2879 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2882 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2883 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2884 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2885 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2886 peer->lookups[x] = peer->lookups[x-1];
2887 if (peer->lookups[x]) {
2888 peer->avgms += peer->lookuptimes[x];
2892 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2893 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2894 if (peer->lookups[0]) {
2895 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2896 peer->avgms += peer->lookuptimes[0];
2906 if (trans->parent) {
2907 /* Unlink from parent if appropriate */
2908 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
2909 if (AST_LIST_EMPTY(&trans->parent->trans)) {
2910 /* Wake up sleeper */
2911 if (trans->parent->pfds[1] > -1) {
2912 write(trans->parent->pfds[1], "killa!", 6);
2916 /* Unlink from all trans */
2917 AST_LIST_REMOVE(&alltrans, trans, all);
2918 destroy_packets(&trans->packets);
2919 destroy_packets(&trans->lasttrans);
2920 if (trans->autokillid > -1)
2921 ast_sched_del(sched, trans->autokillid);
2922 trans->autokillid = -1;
2923 if (trans->thread) {
2924 /* If used by a thread, mark as dead and be done */
2925 ast_set_flag(trans, FLAG_DEAD);
2930 static int dundi_rexmit(void *data)
2932 struct dundi_packet *pack;
2934 AST_LIST_LOCK(&peers);
2936 if (pack->retrans < 1) {
2937 pack->retransid = -1;
2938 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))