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)
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <sys/socket.h>
41 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
42 #include <sys/types.h>
43 #include <netinet/in_systm.h>
45 #include <netinet/ip.h>
46 #include <sys/ioctl.h>
47 #include <netinet/in.h>
49 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
50 #include <net/if_dl.h>
55 #include "asterisk/file.h"
56 #include "asterisk/logger.h"
57 #include "asterisk/channel.h"
58 #include "asterisk/config.h"
59 #include "asterisk/options.h"
60 #include "asterisk/pbx.h"
61 #include "asterisk/module.h"
62 #include "asterisk/frame.h"
63 #include "asterisk/file.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/lock.h"
66 #include "asterisk/md5.h"
67 #include "asterisk/dundi.h"
68 #include "asterisk/sched.h"
69 #include "asterisk/io.h"
70 #include "asterisk/utils.h"
71 #include "asterisk/crypto.h"
72 #include "asterisk/astdb.h"
73 #include "asterisk/acl.h"
74 #include "asterisk/aes.h"
76 #include "dundi-parser.h"
78 #define MAX_RESULTS 64
80 #define MAX_PACKET_SIZE 8192
82 #define DUNDI_MODEL_INBOUND (1 << 0)
83 #define DUNDI_MODEL_OUTBOUND (1 << 1)
84 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
86 /*! Keep times of last 10 lookups */
87 #define DUNDI_TIMING_HISTORY 10
90 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
91 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
92 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
93 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
94 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */
95 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
96 FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
99 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
102 #define DUNDI_SECRET_TIME 15 /* Testing only */
104 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
107 static struct io_context *io;
108 static struct sched_context *sched;
109 static int netsocket = -1;
110 static pthread_t netthreadid = AST_PTHREADT_NULL;
111 static pthread_t precachethreadid = AST_PTHREADT_NULL;
113 static int dundidebug = 0;
114 static int authdebug = 0;
115 static int dundi_ttl = DUNDI_DEFAULT_TTL;
116 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
117 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
118 static int global_autokilltimeout = 0;
119 static dundi_eid global_eid;
120 static int default_expiration = 60;
121 static int global_storehistory = 0;
122 static char dept[80];
124 static char locality[80];
125 static char stateprov[80];
126 static char country[80];
127 static char email[80];
128 static char phone[80];
129 static char secretpath[80];
130 static char cursecret[80];
131 static char ipaddr[80];
132 static time_t rotatetime;
133 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
136 AST_LIST_ENTRY(permission) list;
141 struct dundi_packet {
142 AST_LIST_ENTRY(dundi_packet) list;
145 struct dundi_transaction *parent;
148 unsigned char data[0];
151 struct dundi_hint_metadata {
152 unsigned short flags;
153 char exten[AST_MAX_EXTENSION];
156 struct dundi_precache_queue {
157 AST_LIST_ENTRY(dundi_precache_queue) list;
163 struct dundi_request;
165 struct dundi_transaction {
166 struct sockaddr_in addr; /*!< Other end of transaction */
167 struct timeval start; /*!< When this transaction was created */
168 dundi_eid eids[DUNDI_MAX_STACK + 1];
169 int eidcount; /*!< Number of eids in eids */
170 dundi_eid us_eid; /*!< Our EID, to them */
171 dundi_eid them_eid; /*!< Their EID, to us */
172 aes_encrypt_ctx ecx; /*!< AES 128 Encryption context */
173 aes_decrypt_ctx dcx; /*!< AES 128 Decryption context */
174 unsigned int flags; /*!< Has final packet been sent */
175 int ttl; /*!< Remaining TTL for queries on this one */
176 int thread; /*!< We have a calling thread */
177 int retranstimer; /*!< How long to wait before retransmissions */
178 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
179 int autokilltimeout; /*!< Recommended timeout for autokill */
180 unsigned short strans; /*!< Our transaction identifier */
181 unsigned short dtrans; /*!< Their transaction identifer */
182 unsigned char iseqno; /*!< Next expected received seqno */
183 unsigned char oiseqno; /*!< Last received incoming seqno */
184 unsigned char oseqno; /*!< Next transmitted seqno */
185 unsigned char aseqno; /*!< Last acknowledge seqno */
186 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
187 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
188 struct dundi_request *parent; /*!< Parent request (if there is one) */
189 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
190 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
193 struct dundi_request {
194 char dcontext[AST_MAX_EXTENSION];
195 char number[AST_MAX_EXTENSION];
198 struct dundi_result *dr;
199 struct dundi_entity_info *dei;
200 struct dundi_hint_metadata *hmd;
206 unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */
207 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
208 AST_LIST_ENTRY(dundi_request) list;
211 struct dundi_mapping {
212 char dcontext[AST_MAX_EXTENSION];
213 char lcontext[AST_MAX_EXTENSION];
218 char dest[AST_MAX_EXTENSION];
219 AST_LIST_ENTRY(dundi_mapping) list;
224 struct sockaddr_in addr; /*!< Address of DUNDi peer */
225 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
226 struct permissionlist include;
235 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
236 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
237 unsigned long us_keycrc32; /*!< CRC-32 of our key */
238 aes_encrypt_ctx us_ecx; /*!< Cached AES 128 Encryption context */
239 aes_decrypt_ctx us_dcx; /*!< Cached AES 128 Decryption context */
240 unsigned long them_keycrc32; /*!< CRC-32 of our key */
241 aes_encrypt_ctx them_ecx; /*!< Cached AES 128 Encryption context */
242 aes_decrypt_ctx them_dcx; /*!< Cached AES 128 Decryption context */
243 time_t keyexpire; /*!< When to expire/recreate key */
245 int lookuptimes[DUNDI_TIMING_HISTORY];
246 char *lookups[DUNDI_TIMING_HISTORY];
248 struct dundi_transaction *regtrans; /*!< Registration transaction */
249 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
250 int model; /*!< Pull model */
251 int pcmodel; /*!< Push/precache model */
252 int dynamic; /*!< Are we dynamic? */
253 int lastms; /*!< Last measured latency */
254 int maxms; /*!< Max permissible latency */
255 struct timeval qualtx; /*!< Time of transmit */
256 AST_LIST_ENTRY(dundi_peer) list;
259 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
260 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
261 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
262 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
263 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
265 static int dundi_xmit(struct dundi_packet *pack);
267 static void dundi_debug_output(const char *data)
270 ast_verbose("%s", data);
273 static void dundi_error_output(const char *data)
275 ast_log(LOG_WARNING, "%s", data);
278 static int has_permission(struct permissionlist *permlist, char *cont)
280 struct permission *perm;
283 AST_LIST_TRAVERSE(permlist, perm, list) {
284 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
291 static char *tech2str(int tech)
294 case DUNDI_PROTO_NONE:
296 case DUNDI_PROTO_IAX:
298 case DUNDI_PROTO_SIP:
300 case DUNDI_PROTO_H323:
307 static int str2tech(char *str)
309 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
310 return DUNDI_PROTO_IAX;
311 else if (!strcasecmp(str, "SIP"))
312 return DUNDI_PROTO_SIP;
313 else if (!strcasecmp(str, "H323"))
314 return DUNDI_PROTO_H323;
319 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
320 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
321 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
322 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
324 struct dundi_transaction *trans;
326 /* Look for an exact match first */
327 AST_LIST_TRAVERSE(&alltrans, trans, all) {
328 if (!inaddrcmp(&trans->addr, sin) &&
329 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
330 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
332 trans->dtrans = ntohs(hdr->strans) & 32767;
337 switch(hdr->cmdresp & 0x7f) {
338 case DUNDI_COMMAND_DPDISCOVER:
339 case DUNDI_COMMAND_EIDQUERY:
340 case DUNDI_COMMAND_PRECACHERQ:
341 case DUNDI_COMMAND_REGREQ:
342 case DUNDI_COMMAND_NULL:
343 case DUNDI_COMMAND_ENCRYPT:
345 /* Create new transaction */
346 trans = create_transaction(NULL);
348 memcpy(&trans->addr, sin, sizeof(trans->addr));
349 trans->dtrans = ntohs(hdr->strans) & 32767;
351 ast_log(LOG_WARNING, "Out of memory!\n");
361 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
363 static int dundi_ack(struct dundi_transaction *trans, int final)
365 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
367 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
370 struct dundi_packet pack;
371 struct dundi_hdr hdr;
373 struct dundi_transaction trans;
374 /* Never respond to an INVALID with another INVALID */
375 if (h->cmdresp == DUNDI_COMMAND_INVALID)
377 memset(&tmp, 0, sizeof(tmp));
378 memset(&trans, 0, sizeof(trans));
379 memcpy(&trans.addr, sin, sizeof(trans.addr));
380 tmp.hdr.strans = h->dtrans;
381 tmp.hdr.dtrans = h->strans;
382 tmp.hdr.iseqno = h->oseqno;
383 tmp.hdr.oseqno = h->iseqno;
384 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
385 tmp.hdr.cmdflags = 0;
386 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
387 tmp.pack.datalen = sizeof(struct dundi_hdr);
388 tmp.pack.parent = &trans;
389 dundi_xmit(&tmp.pack);
392 static void reset_global_eid(void)
394 #if defined(SIOCGIFHWADDR)
399 s = socket(AF_INET, SOCK_STREAM, 0);
402 for (x = 0; x < 10; x++) {
403 memset(&ifr, 0, sizeof(ifr));
404 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
405 if (ioctl(s, SIOCGIFHWADDR, &ifr))
407 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
409 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n",
410 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
416 #if defined(ifa_broadaddr) && !defined(SOLARIS)
418 struct ifaddrs *ifap;
420 if (getifaddrs(&ifap) == 0) {
422 for (p = ifap; p; p = p->ifa_next) {
423 if (p->ifa_addr->sa_family == AF_LINK) {
424 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
427 sdp->sdl_data + sdp->sdl_nlen, 6);
429 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);
438 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
441 static int get_trans_id(void)
443 struct dundi_transaction *t;
444 int stid = (ast_random() % 32766) + 1;
448 AST_LIST_TRAVERSE(&alltrans, t, all) {
449 if (t->strans == tid)
454 tid = (tid % 32766) + 1;
455 } while (tid != stid);
460 static int reset_transaction(struct dundi_transaction *trans)
463 tid = get_trans_id();
472 ast_clear_flag(trans, FLAG_FINAL);
476 static struct dundi_peer *find_peer(dundi_eid *eid)
478 struct dundi_peer *cur = NULL;
483 AST_LIST_TRAVERSE(&peers, cur, list) {
484 if (!dundi_eid_cmp(&cur->eid,eid))
491 static void build_iv(unsigned char *iv)
493 /* XXX Would be nice to be more random XXX */
494 unsigned int *fluffy;
496 fluffy = (unsigned int *)(iv);
498 fluffy[x] = ast_random();
501 struct dundi_query_state {
502 dundi_eid *eids[DUNDI_MAX_STACK + 1];
503 int directs[DUNDI_MAX_STACK + 1];
505 char called_context[AST_MAX_EXTENSION];
506 char called_number[AST_MAX_EXTENSION];
507 struct dundi_mapping *maps;
510 struct dundi_transaction *trans;
517 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)
519 struct ast_flags flags = {0};
521 if (!ast_strlen_zero(map->lcontext)) {
522 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
523 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
524 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
525 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
526 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
527 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
528 if (ast_ignore_pattern(map->lcontext, called_number))
529 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
531 /* Clearly we can't say 'don't ask' anymore if we found anything... */
532 if (ast_test_flag(&flags, AST_FLAGS_ALL))
533 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
535 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
536 /* Skip partial answers */
537 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
539 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
540 struct varshead headp;
541 struct ast_var_t *newvariable;
542 ast_set_flag(&flags, map->options & 0xffff);
543 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
544 dr[anscnt].techint = map->tech;
545 dr[anscnt].weight = map->weight;
546 dr[anscnt].expiration = dundi_cache_time;
547 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
548 dr[anscnt].eid = *us_eid;
549 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
550 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
551 AST_LIST_HEAD_INIT_NOLOCK(&headp);
552 newvariable = ast_var_assign("NUMBER", called_number);
553 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
554 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
555 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
556 newvariable = ast_var_assign("SECRET", cursecret);
557 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
558 newvariable = ast_var_assign("IPADDR", ipaddr);
559 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
560 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
561 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
562 ast_var_delete(newvariable);
564 dr[anscnt].dest[0] = '\0';
567 /* No answers... Find the fewest number of digits from the
568 number for which we have no answer. */
569 char tmp[AST_MAX_EXTENSION];
570 for (x=0;x<AST_MAX_EXTENSION;x++) {
571 tmp[x] = called_number[x];
574 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
575 /* Oops found something we can't match. If this is longer
576 than the running hint, we have to consider it */
577 if (strlen(tmp) > strlen(hmd->exten)) {
578 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
588 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
590 static void *dundi_lookup_thread(void *data)
592 struct dundi_query_state *st = data;
593 struct dundi_result dr[MAX_RESULTS];
594 struct dundi_ie_data ied;
595 struct dundi_hint_metadata hmd;
600 int expiration = dundi_cache_time;
603 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
604 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
605 memset(&ied, 0, sizeof(ied));
606 memset(&dr, 0, sizeof(dr));
607 memset(&hmd, 0, sizeof(hmd));
608 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
609 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
610 for (x=0;x<st->nummaps;x++)
611 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
614 for (x=0;x<ouranswers;x++) {
615 if (dr[x].weight < max)
620 /* If we do not have a canonical result, keep looking */
621 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);
623 /* Append answer in result */
626 if ((res < -1) && (!ouranswers))
627 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
630 AST_LIST_LOCK(&peers);
631 /* Truncate if "don't ask" isn't present */
632 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
634 if (ast_test_flag(st->trans, FLAG_DEAD)) {
636 ast_log(LOG_DEBUG, "Our transaction went away!\n");
637 st->trans->thread = 0;
638 destroy_trans(st->trans, 0);
640 for (x=0;x<ouranswers;x++) {
642 if (dr[x].expiration && (expiration > dr[x].expiration))
643 expiration = dr[x].expiration;
644 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
646 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
647 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
648 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
649 st->trans->thread = 0;
651 AST_LIST_UNLOCK(&peers);
656 static void *dundi_precache_thread(void *data)
658 struct dundi_query_state *st = data;
659 struct dundi_ie_data ied;
660 struct dundi_hint_metadata hmd;
664 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
665 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
666 memset(&ied, 0, sizeof(ied));
668 /* Now produce precache */
669 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
671 AST_LIST_LOCK(&peers);
672 /* Truncate if "don't ask" isn't present */
673 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
675 if (ast_test_flag(st->trans, FLAG_DEAD)) {
677 ast_log(LOG_DEBUG, "Our transaction went away!\n");
678 st->trans->thread = 0;
679 destroy_trans(st->trans, 0);
681 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
682 st->trans->thread = 0;
684 AST_LIST_UNLOCK(&peers);
689 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[]);
691 static void *dundi_query_thread(void *data)
693 struct dundi_query_state *st = data;
694 struct dundi_entity_info dei;
695 struct dundi_ie_data ied;
696 struct dundi_hint_metadata hmd;
701 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
702 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
703 memset(&ied, 0, sizeof(ied));
704 memset(&dei, 0, sizeof(dei));
705 memset(&hmd, 0, sizeof(hmd));
706 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
709 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
710 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
711 ast_copy_string(dei.org, org, sizeof(dei.org));
712 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
713 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
714 ast_copy_string(dei.country, country, sizeof(dei.country));
715 ast_copy_string(dei.email, email, sizeof(dei.email));
716 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
719 /* If we do not have a canonical result, keep looking */
720 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
722 AST_LIST_LOCK(&peers);
723 if (ast_test_flag(st->trans, FLAG_DEAD)) {
725 ast_log(LOG_DEBUG, "Our transaction went away!\n");
726 st->trans->thread = 0;
727 destroy_trans(st->trans, 0);
730 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
731 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
732 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
733 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
734 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
735 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
736 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
737 if (!ast_strlen_zero(dei.ipaddr))
738 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
740 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
741 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
742 st->trans->thread = 0;
744 AST_LIST_UNLOCK(&peers);
749 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
751 struct dundi_query_state *st;
755 struct dundi_ie_data ied;
758 pthread_t lookupthread;
760 if (ies->eidcount > 1) {
761 /* Since it is a requirement that the first EID is the authenticating host
762 and the last EID is the root, it is permissible that the first and last EID
763 could be the same. In that case, we should go ahead copy only the "root" section
764 since we will not need it for authentication. */
765 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
768 totallen = sizeof(struct dundi_query_state);
769 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
770 st = ast_calloc(1, totallen);
772 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
773 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
775 st->ttl = ies->ttl - 1;
779 for (x=skipfirst;ies->eids[x];x++) {
780 st->eids[x-skipfirst] = (dundi_eid *)s;
781 *st->eids[x-skipfirst] = *ies->eids[x];
782 s += sizeof(dundi_eid);
785 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);
786 pthread_attr_init(&attr);
787 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
789 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
791 ast_log(LOG_WARNING, "Unable to create thread!\n");
793 memset(&ied, 0, sizeof(ied));
794 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
795 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
799 ast_log(LOG_WARNING, "Out of memory!\n");
800 memset(&ied, 0, sizeof(ied));
801 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
802 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
808 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
813 char eidpeer_str[20];
814 char eidroot_str[20];
819 expiration = dundi_cache_time;
821 /* Only cache hint if "don't ask" is there... */
822 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
825 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
827 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
828 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
829 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
830 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
833 timeout += expiration;
834 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
836 ast_db_put("dundi/cache", key1, data);
838 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
839 ast_db_put("dundi/cache", key2, data);
841 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
845 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
851 char eidpeer_str[20];
852 char eidroot_str[20];
856 expiration = dundi_cache_time;
858 /* Keep pushes a little longer, cut pulls a little short */
865 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
866 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
867 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
868 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
869 /* Build request string */
871 timeout += expiration;
872 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
873 for (x=start;x<req->respcount;x++) {
874 /* Skip anything with an illegal pipe in it */
875 if (strchr(req->dr[x].dest, '|'))
877 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
878 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
879 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
881 ast_db_put("dundi/cache", key1, data);
882 ast_db_put("dundi/cache", key2, data);
886 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
888 struct dundi_query_state *st;
891 struct dundi_ie_data ied;
893 struct dundi_result dr2[MAX_RESULTS];
894 struct dundi_request dr;
895 struct dundi_hint_metadata hmd;
897 struct dundi_mapping *cur;
901 pthread_t lookupthread;
904 memset(&dr2, 0, sizeof(dr2));
905 memset(&dr, 0, sizeof(dr));
906 memset(&hmd, 0, sizeof(hmd));
908 /* Forge request structure to hold answers for cache */
909 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
911 dr.maxcount = MAX_RESULTS;
912 dr.expiration = dundi_cache_time;
914 dr.pfds[0] = dr.pfds[1] = -1;
916 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
917 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
919 for (x=0;x<ies->anscount;x++) {
920 if (trans->parent->respcount < trans->parent->maxcount) {
921 /* Make sure it's not already there */
922 for (z=0;z<trans->parent->respcount;z++) {
923 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
924 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
927 if (z == trans->parent->respcount) {
928 /* Copy into parent responses */
929 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
930 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
931 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
932 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
933 if (ies->expiration > 0)
934 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
936 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
937 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
938 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
939 &ies->answers[x]->eid);
940 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
941 sizeof(trans->parent->dr[trans->parent->respcount].dest));
942 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
943 sizeof(trans->parent->dr[trans->parent->respcount].tech));
944 trans->parent->respcount++;
945 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
946 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
947 /* Update weight if appropriate */
948 trans->parent->dr[z].weight = ies->answers[x]->weight;
951 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
952 trans->parent->number, trans->parent->dcontext);
955 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
956 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
958 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
960 totallen = sizeof(struct dundi_query_state);
961 /* Count matching map entries */
963 AST_LIST_TRAVERSE(&mappings, cur, list) {
964 if (!strcasecmp(cur->dcontext, ccontext))
968 /* If no maps, return -1 immediately */
972 if (ies->eidcount > 1) {
973 /* Since it is a requirement that the first EID is the authenticating host
974 and the last EID is the root, it is permissible that the first and last EID
975 could be the same. In that case, we should go ahead copy only the "root" section
976 since we will not need it for authentication. */
977 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
981 /* Prepare to run a query and then propagate that as necessary */
982 totallen += mapcount * sizeof(struct dundi_mapping);
983 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
984 st = ast_calloc(1, totallen);
986 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
987 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
989 st->ttl = ies->ttl - 1;
990 st->nocache = ies->cbypass;
994 for (x=skipfirst;ies->eids[x];x++) {
995 st->eids[x-skipfirst] = (dundi_eid *)s;
996 *st->eids[x-skipfirst] = *ies->eids[x];
997 st->directs[x-skipfirst] = ies->eid_direct[x];
998 s += sizeof(dundi_eid);
1000 /* Append mappings */
1002 st->maps = (struct dundi_mapping *)s;
1003 AST_LIST_TRAVERSE(&mappings, cur, list) {
1004 if (!strcasecmp(cur->dcontext, ccontext)) {
1007 st->maps[x].list.next = NULL;
1012 st->nummaps = mapcount;
1014 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1015 pthread_attr_init(&attr);
1016 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1018 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1020 ast_log(LOG_WARNING, "Unable to create thread!\n");
1022 memset(&ied, 0, sizeof(ied));
1023 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1024 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1028 ast_log(LOG_WARNING, "Out of memory!\n");
1029 memset(&ied, 0, sizeof(ied));
1030 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1031 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1037 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1039 struct dundi_query_state *st;
1042 struct dundi_ie_data ied;
1044 struct dundi_mapping *cur;
1048 pthread_t lookupthread;
1049 pthread_attr_t attr;
1050 totallen = sizeof(struct dundi_query_state);
1051 /* Count matching map entries */
1052 AST_LIST_TRAVERSE(&mappings, cur, list) {
1053 if (!strcasecmp(cur->dcontext, ccontext))
1056 /* If no maps, return -1 immediately */
1060 if (ies->eidcount > 1) {
1061 /* Since it is a requirement that the first EID is the authenticating host
1062 and the last EID is the root, it is permissible that the first and last EID
1063 could be the same. In that case, we should go ahead copy only the "root" section
1064 since we will not need it for authentication. */
1065 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1069 totallen += mapcount * sizeof(struct dundi_mapping);
1070 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1071 st = ast_calloc(1, totallen);
1073 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1074 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1076 st->ttl = ies->ttl - 1;
1077 st->nocache = ies->cbypass;
1081 for (x=skipfirst;ies->eids[x];x++) {
1082 st->eids[x-skipfirst] = (dundi_eid *)s;
1083 *st->eids[x-skipfirst] = *ies->eids[x];
1084 st->directs[x-skipfirst] = ies->eid_direct[x];
1085 s += sizeof(dundi_eid);
1087 /* Append mappings */
1089 st->maps = (struct dundi_mapping *)s;
1090 AST_LIST_TRAVERSE(&mappings, cur, list) {
1091 if (!strcasecmp(cur->dcontext, ccontext)) {
1094 st->maps[x].list.next = NULL;
1099 st->nummaps = mapcount;
1101 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1102 pthread_attr_init(&attr);
1103 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1105 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1107 ast_log(LOG_WARNING, "Unable to create thread!\n");
1109 memset(&ied, 0, sizeof(ied));
1110 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1111 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1115 ast_log(LOG_WARNING, "Out of memory!\n");
1116 memset(&ied, 0, sizeof(ied));
1117 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1118 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1124 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1127 char *ptr, *term, *src;
1129 struct ast_flags flags;
1135 /* Build request string */
1136 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1139 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1140 int expiration = timeout - now;
1141 if (expiration > 0) {
1143 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1145 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1147 term = strchr(ptr, '|');
1150 src = strrchr(ptr, '/');
1157 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1158 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1159 /* Make sure it's not already there */
1160 for (z=0;z<req->respcount;z++) {
1161 if ((req->dr[z].techint == tech) &&
1162 !strcmp(req->dr[z].dest, ptr))
1165 if (z == req->respcount) {
1166 /* Copy into parent responses */
1167 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1168 req->dr[req->respcount].weight = weight;
1169 req->dr[req->respcount].techint = tech;
1170 req->dr[req->respcount].expiration = expiration;
1171 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1172 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1173 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1174 ast_copy_string(req->dr[req->respcount].dest, ptr,
1175 sizeof(req->dr[req->respcount].dest));
1176 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1177 sizeof(req->dr[req->respcount].tech));
1179 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1180 } else if (req->dr[z].weight > weight)
1181 req->dr[z].weight = weight;
1185 /* We found *something* cached */
1186 if (expiration < *lowexpiration)
1187 *lowexpiration = expiration;
1190 ast_db_del("dundi/cache", key);
1192 ast_db_del("dundi/cache", key);
1198 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1202 char eidroot_str[20];
1206 char eid_str_full[20];
1211 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1212 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1213 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1214 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1215 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1216 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1217 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1218 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1219 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1221 if (!req->respcount) {
1223 /* Look and see if we have a hint that would preclude us from looking at this
1224 peer for this number. */
1225 if (!(tmp[x] = req->number[x]))
1228 /* Check for hints */
1229 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1230 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1231 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1232 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1233 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1234 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1236 if (strlen(tmp) > strlen(req->hmd->exten)) {
1237 /* Update meta data if appropriate */
1238 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1248 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1250 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1252 if (!trans->addr.sin_addr.s_addr)
1253 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1254 trans->us_eid = p->us_eid;
1255 trans->them_eid = p->eid;
1256 /* Enable encryption if appropriate */
1257 if (!ast_strlen_zero(p->inkey))
1258 ast_set_flag(trans, FLAG_ENCRYPT);
1260 trans->autokilltimeout = p->maxms;
1261 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1262 if (p->lastms > 1) {
1263 trans->retranstimer = p->lastms * 2;
1264 /* Keep it from being silly */
1265 if (trans->retranstimer < 150)
1266 trans->retranstimer = 150;
1268 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1269 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1271 trans->autokilltimeout = global_autokilltimeout;
1274 /*! \note Called with the peers list already locked */
1275 static int do_register_expire(void *data)
1277 struct dundi_peer *peer = data;
1280 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1281 peer->registerexpire = -1;
1283 memset(&peer->addr, 0, sizeof(peer->addr));
1287 static int update_key(struct dundi_peer *peer)
1289 unsigned char key[16];
1290 struct ast_key *ekey, *skey;
1293 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1295 aes_encrypt_key128(key, &peer->us_ecx);
1296 aes_decrypt_key128(key, &peer->us_dcx);
1297 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1299 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1300 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1303 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1305 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1306 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1309 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1310 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1313 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1314 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1317 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1318 peer->sentfullkey = 0;
1320 time(&peer->keyexpire);
1321 peer->keyexpire += dundi_key_ttl;
1326 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1328 unsigned char curblock[16];
1330 memcpy(curblock, iv, sizeof(curblock));
1333 curblock[x] ^= src[x];
1334 aes_encrypt(curblock, dst, ecx);
1335 memcpy(curblock, dst, sizeof(curblock));
1342 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1344 unsigned char lastblock[16];
1346 memcpy(lastblock, iv, sizeof(lastblock));
1348 aes_decrypt(src, dst, dcx);
1350 dst[x] ^= lastblock[x];
1351 memcpy(lastblock, src, sizeof(lastblock));
1359 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)
1361 int space = *dstlen;
1362 unsigned long bytes;
1363 struct dundi_hdr *h;
1364 unsigned char *decrypt_space;
1365 decrypt_space = alloca(srclen);
1368 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1370 h = (struct dundi_hdr *)dst;
1373 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1375 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1379 *dstlen = bytes + 6;
1380 /* Return new header */
1384 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1386 unsigned char *compress_space;
1389 unsigned long bytes;
1390 struct dundi_ie_data ied;
1391 struct dundi_peer *peer;
1392 unsigned char iv[16];
1393 len = pack->datalen + pack->datalen / 100 + 42;
1394 compress_space = alloca(len);
1395 if (compress_space) {
1396 memset(compress_space, 0, len);
1397 /* We care about everthing save the first 6 bytes of header */
1399 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1402 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1405 memset(&ied, 0, sizeof(ied));
1406 /* Say who we are */
1407 if (!pack->h->iseqno && !pack->h->oseqno) {
1408 /* Need the key in the first copy */
1409 if (!(peer = find_peer(&trans->them_eid)))
1411 if (update_key(peer))
1413 if (!peer->sentfullkey)
1414 ast_set_flag(trans, FLAG_SENDFULLKEY);
1415 /* Append key data */
1416 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1417 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1418 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1419 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1421 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1423 /* Setup contexts */
1424 trans->ecx = peer->us_ecx;
1425 trans->dcx = peer->us_dcx;
1427 /* We've sent the full key */
1428 peer->sentfullkey = 1;
1430 /* Build initialization vector */
1432 /* Add the field, rounded up to 16 bytes */
1433 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1435 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1436 ast_log(LOG_NOTICE, "Final packet too large!\n");
1439 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1440 ied.pos += ((bytes + 15) / 16) * 16;
1441 /* Reconstruct header */
1442 pack->datalen = sizeof(struct dundi_hdr);
1443 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1444 pack->h->cmdflags = 0;
1445 memcpy(pack->h->ies, ied.buf, ied.pos);
1446 pack->datalen += ied.pos;
1452 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1454 unsigned char dst[128];
1456 struct ast_key *key, *skey;
1459 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1460 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1463 } else if (!newkey || !newsig)
1465 if (!memcmp(peer->rxenckey, newkey, 128) &&
1466 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1467 /* By definition, a match */
1471 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1473 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1474 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1478 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1480 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1481 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1485 /* First check signature */
1486 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1490 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1493 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1496 /* Decrypted, passes signature */
1498 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1499 memcpy(peer->rxenckey, newkey, 128);
1500 memcpy(peer->rxenckey + 128, newsig, 128);
1501 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1502 aes_decrypt_key128(dst, &peer->them_dcx);
1503 aes_encrypt_key128(dst, &peer->them_ecx);
1507 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1509 /* Handle canonical command / response */
1510 int final = hdr->cmdresp & 0x80;
1511 int cmd = hdr->cmdresp & 0x7f;
1516 unsigned char *bufcpy;
1517 struct dundi_ie_data ied;
1518 struct dundi_ies ies;
1519 struct dundi_peer *peer;
1522 memset(&ied, 0, sizeof(ied));
1523 memset(&ies, 0, sizeof(ies));
1525 bufcpy = alloca(datalen);
1528 /* Make a copy for parsing */
1529 memcpy(bufcpy, hdr->ies, datalen);
1531 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1532 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1533 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1538 case DUNDI_COMMAND_DPDISCOVER:
1539 case DUNDI_COMMAND_EIDQUERY:
1540 case DUNDI_COMMAND_PRECACHERQ:
1541 if (cmd == DUNDI_COMMAND_EIDQUERY)
1542 resp = DUNDI_COMMAND_EIDRESPONSE;
1543 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1544 resp = DUNDI_COMMAND_PRECACHERP;
1546 resp = DUNDI_COMMAND_DPRESPONSE;
1547 /* A dialplan or entity discover -- qualify by highest level entity */
1548 peer = find_peer(ies.eids[0]);
1550 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1551 dundi_send(trans, resp, 0, 1, &ied);
1554 trans->us_eid = peer->us_eid;
1555 if (strlen(peer->inkey)) {
1556 hasauth = encrypted;
1560 /* Okay we're authentiated and all, now we check if they're authorized */
1561 if (!ies.called_context)
1562 ies.called_context = "e164";
1563 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1564 res = dundi_answer_entity(trans, &ies, ies.called_context);
1566 if (ast_strlen_zero(ies.called_number)) {
1567 /* They're not permitted to access that context */
1568 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1569 dundi_send(trans, resp, 0, 1, &ied);
1570 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1571 (peer->model & DUNDI_MODEL_INBOUND) &&
1572 has_permission(&peer->permit, ies.called_context)) {
1573 res = dundi_answer_query(trans, &ies, ies.called_context);
1575 /* There is no such dundi context */
1576 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1577 dundi_send(trans, resp, 0, 1, &ied);
1579 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1580 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1581 has_permission(&peer->include, ies.called_context)) {
1582 res = dundi_prop_precache(trans, &ies, ies.called_context);
1584 /* There is no such dundi context */
1585 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1586 dundi_send(trans, resp, 0, 1, &ied);
1589 /* They're not permitted to access that context */
1590 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1591 dundi_send(trans, resp, 0, 1, &ied);
1595 /* They're not permitted to access that context */
1596 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1597 dundi_send(trans, resp, 0, 1, &ied);
1601 case DUNDI_COMMAND_REGREQ:
1602 /* A register request -- should only have one entity */
1603 peer = find_peer(ies.eids[0]);
1604 if (!peer || !peer->dynamic) {
1605 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1606 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1609 trans->us_eid = peer->us_eid;
1610 if (!ast_strlen_zero(peer->inkey)) {
1611 hasauth = encrypted;
1615 int expire = default_expiration;
1618 if (peer->registerexpire > -1)
1619 ast_sched_del(sched, peer->registerexpire);
1620 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1621 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1622 ntohs(trans->addr.sin_port), expire);
1623 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1624 if (inaddrcmp(&peer->addr, &trans->addr)) {
1625 if (option_verbose > 2) {
1626 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n",
1627 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1628 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1633 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1634 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1635 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1637 qualify_peer(peer, 1);
1641 case DUNDI_COMMAND_DPRESPONSE:
1642 /* A dialplan response, lets see what we got... */
1643 if (ies.cause < 1) {
1644 /* Success of some sort */
1646 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1647 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1648 authpass = encrypted;
1652 /* Pass back up answers */
1653 if (trans->parent && trans->parent->dr) {
1654 y = trans->parent->respcount;
1655 for (x=0;x<ies.anscount;x++) {
1656 if (trans->parent->respcount < trans->parent->maxcount) {
1657 /* Make sure it's not already there */
1658 for (z=0;z<trans->parent->respcount;z++) {
1659 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1660 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1663 if (z == trans->parent->respcount) {
1664 /* Copy into parent responses */
1665 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1666 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1667 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1668 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1669 if (ies.expiration > 0)
1670 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1672 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1673 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1674 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1675 &ies.answers[x]->eid);
1676 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1677 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1678 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1679 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1680 trans->parent->respcount++;
1681 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1682 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1683 /* Update weight if appropriate */
1684 trans->parent->dr[z].weight = ies.answers[x]->weight;
1687 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1688 trans->parent->number, trans->parent->dcontext);
1690 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1691 the cache know if this request was unaffected by our entity list. */
1692 cache_save(&trans->them_eid, trans->parent, y,
1693 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1695 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1696 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1697 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1698 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1699 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1700 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1701 sizeof(trans->parent->hmd->exten));
1704 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1707 if (ies.expiration > 0) {
1708 if (trans->parent->expiration > ies.expiration) {
1709 trans->parent->expiration = ies.expiration;
1713 /* Close connection if not final */
1715 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1719 /* Auth failure, check for data */
1721 /* Cancel if they didn't already */
1722 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1726 case DUNDI_COMMAND_EIDRESPONSE:
1727 /* A dialplan response, lets see what we got... */
1728 if (ies.cause < 1) {
1729 /* Success of some sort */
1731 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1732 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1733 authpass = encrypted;
1737 /* Pass back up answers */
1738 if (trans->parent && trans->parent->dei && ies.q_org) {
1739 if (!trans->parent->respcount) {
1740 trans->parent->respcount++;
1742 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1744 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1746 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1747 if (ies.q_stateprov)
1748 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1750 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1752 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1754 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1756 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1757 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1758 /* If it's them, update our address */
1759 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1763 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1764 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1767 /* Close connection if not final */
1769 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1773 /* Auth failure, check for data */
1775 /* Cancel if they didn't already */
1776 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1780 case DUNDI_COMMAND_REGRESPONSE:
1781 /* A dialplan response, lets see what we got... */
1782 if (ies.cause < 1) {
1784 /* Success of some sort */
1785 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1786 hasauth = encrypted;
1791 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1793 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1794 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1798 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),
1799 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1800 /* Close connection if not final */
1802 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1805 /* Auth failure, cancel if they didn't for some reason */
1807 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1811 case DUNDI_COMMAND_INVALID:
1812 case DUNDI_COMMAND_NULL:
1813 case DUNDI_COMMAND_PRECACHERP:
1814 /* Do nothing special */
1816 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1818 case DUNDI_COMMAND_ENCREJ:
1819 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1820 /* No really, it's over at this point */
1822 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1824 /* Send with full key */
1825 ast_set_flag(trans, FLAG_SENDFULLKEY);
1827 /* Ooops, we got a final message, start by sending ACK... */
1828 dundi_ack(trans, hdr->cmdresp & 0x80);
1829 trans->aseqno = trans->iseqno;
1830 /* Now, we gotta create a new transaction */
1831 if (!reset_transaction(trans)) {
1832 /* Make sure handle_frame doesn't destroy us */
1833 hdr->cmdresp &= 0x7f;
1834 /* Parse the message we transmitted */
1835 memset(&ies, 0, sizeof(ies));
1836 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1837 /* Reconstruct outgoing encrypted packet */
1838 memset(&ied, 0, sizeof(ied));
1839 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1840 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1841 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1843 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1844 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
1845 peer->sentfullkey = 1;
1850 case DUNDI_COMMAND_ENCRYPT:
1852 /* No nested encryption! */
1853 if ((trans->iseqno == 1) && !trans->oseqno) {
1854 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1855 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1856 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1858 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1862 apply_peer(trans, peer);
1863 /* Key passed, use new contexts for this session */
1864 trans->ecx = peer->them_ecx;
1865 trans->dcx = peer->them_dcx;
1867 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1868 struct dundi_hdr *dhdr;
1869 unsigned char decoded[MAX_PACKET_SIZE];
1871 ddatalen = sizeof(decoded);
1872 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1874 /* Handle decrypted response */
1876 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1877 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1878 /* Carry back final flag */
1879 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1883 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1888 /* Turn off encryption */
1889 ast_clear_flag(trans, FLAG_ENCRYPT);
1890 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1894 /* Send unknown command if we don't know it, with final flag IFF it's the
1895 first command in the dialog and only if we haven't recieved final notification */
1897 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1898 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1904 static void destroy_packet(struct dundi_packet *pack, int needfree);
1905 static void destroy_packets(struct packetlist *p)
1907 struct dundi_packet *pack;
1909 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1910 if (pack->retransid > -1)
1911 ast_sched_del(sched, pack->retransid);
1917 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1919 struct dundi_packet *pack;
1921 /* Ack transmitted packet corresponding to iseqno */
1922 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1923 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1924 destroy_packet(pack, 0);
1925 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1926 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1927 destroy_packets(&trans->lasttrans);
1929 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
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 if (h->cmdresp & 0x80) {
1968 /* Final -- destroy now */
1969 destroy_trans(trans, 0);
1971 } else if (h->oseqno == trans->oiseqno) {
1972 /* Last incoming sequence number -- send ACK without processing */
1973 dundi_ack(trans, 0);
1975 /* 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_LIST_LOCK(&peers);
2005 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2006 AST_LIST_UNLOCK(&peers);
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), "%d", (int)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 (!ast_get_time_t(tmp, &expired, 0, NULL)) {
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_LIST_LOCK(&peers);
2108 ast_sched_runq(sched);
2109 AST_LIST_UNLOCK(&peers);
2116 static void *process_precache(void *ign)
2118 struct dundi_precache_queue *qe;
2127 AST_LIST_LOCK(&pcq);
2128 if ((qe = AST_LIST_FIRST(&pcq))) {
2129 if (!qe->expiration) {
2130 /* Gone... Remove... */
2131 AST_LIST_REMOVE_HEAD(&pcq, list);
2133 } else if (qe->expiration < now) {
2134 /* Process this entry */
2136 ast_copy_string(context, qe->context, sizeof(context));
2137 ast_copy_string(number, qe->number, sizeof(number));
2141 AST_LIST_UNLOCK(&pcq);
2143 dundi_precache(context, number);
2151 static int start_network_thread(void)
2153 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2154 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2158 static int dundi_do_debug(int fd, int argc, char *argv[])
2161 return RESULT_SHOWUSAGE;
2163 ast_cli(fd, "DUNDi Debugging Enabled\n");
2164 return RESULT_SUCCESS;
2167 static int dundi_do_store_history(int fd, int argc, char *argv[])
2170 return RESULT_SHOWUSAGE;
2171 global_storehistory = 1;
2172 ast_cli(fd, "DUNDi History Storage Enabled\n");
2173 return RESULT_SUCCESS;
2176 static int dundi_flush(int fd, int argc, char *argv[])
2179 if ((argc < 2) || (argc > 3))
2180 return RESULT_SHOWUSAGE;
2182 if (!strcasecmp(argv[2], "stats"))
2185 return RESULT_SHOWUSAGE;
2188 /* Flush statistics */
2189 struct dundi_peer *p;
2191 AST_LIST_LOCK(&peers);
2192 AST_LIST_TRAVERSE(&peers, p, list) {
2193 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2195 free(p->lookups[x]);
2196 p->lookups[x] = NULL;
2197 p->lookuptimes[x] = 0;
2201 AST_LIST_UNLOCK(&peers);
2203 ast_db_deltree("dundi/cache", NULL);
2204 ast_cli(fd, "DUNDi Cache Flushed\n");
2206 return RESULT_SUCCESS;
2209 static int dundi_no_debug(int fd, int argc, char *argv[])
2212 return RESULT_SHOWUSAGE;
2214 ast_cli(fd, "DUNDi Debugging Disabled\n");
2215 return RESULT_SUCCESS;
2218 static int dundi_no_store_history(int fd, int argc, char *argv[])
2221 return RESULT_SHOWUSAGE;
2222 global_storehistory = 0;
2223 ast_cli(fd, "DUNDi History Storage Disabled\n");
2224 return RESULT_SUCCESS;
2227 static char *model2str(int model)
2230 case DUNDI_MODEL_INBOUND:
2232 case DUNDI_MODEL_OUTBOUND:
2234 case DUNDI_MODEL_SYMMETRIC:
2241 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2245 struct dundi_peer *p;
2250 AST_LIST_LOCK(&peers);
2252 AST_LIST_TRAVERSE(&peers, p, list) {
2253 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2254 if (!strncasecmp(word, s, len) && ++which > state)
2255 ret = ast_strdup(s);
2257 AST_LIST_UNLOCK(&peers);
2261 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2263 return complete_peer_helper(line, word, pos, state, 3);
2266 static int rescomp(const void *a, const void *b)
2268 const struct dundi_result *resa, *resb;
2271 if (resa->weight < resb->weight)
2273 if (resa->weight > resb->weight)
2278 static void sort_results(struct dundi_result *results, int count)
2280 qsort(results, count, sizeof(results[0]), rescomp);
2283 static int dundi_do_lookup(int fd, int argc, char *argv[])
2291 struct dundi_result dr[MAX_RESULTS];
2292 struct timeval start;
2293 if ((argc < 3) || (argc > 4))
2294 return RESULT_SHOWUSAGE;
2296 if (!strcasecmp(argv[3], "bypass"))
2299 return RESULT_SHOWUSAGE;
2301 ast_copy_string(tmp, argv[2], sizeof(tmp));
2302 context = strchr(tmp, '@');
2307 start = ast_tvnow();
2308 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2311 ast_cli(fd, "DUNDi lookup returned error.\n");
2313 ast_cli(fd, "DUNDi lookup returned no results.\n");
2315 sort_results(dr, res);
2316 for (x=0;x<res;x++) {
2317 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));
2318 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2320 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2321 return RESULT_SUCCESS;
2324 static int dundi_do_precache(int fd, int argc, char *argv[])
2329 struct timeval start;
2330 if ((argc < 3) || (argc > 3))
2331 return RESULT_SHOWUSAGE;
2332 ast_copy_string(tmp, argv[2], sizeof(tmp));
2333 context = strchr(tmp, '@');
2338 start = ast_tvnow();
2339 res = dundi_precache(context, tmp);
2342 ast_cli(fd, "DUNDi precache returned error.\n");
2344 ast_cli(fd, "DUNDi precache returned no error.\n");
2345 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2346 return RESULT_SUCCESS;
2349 static int dundi_do_query(int fd, int argc, char *argv[])
2355 struct dundi_entity_info dei;
2356 if ((argc < 3) || (argc > 3))
2357 return RESULT_SHOWUSAGE;
2358 if (dundi_str_to_eid(&eid, argv[2])) {
2359 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2360 return RESULT_SHOWUSAGE;
2362 ast_copy_string(tmp, argv[2], sizeof(tmp));
2363 context = strchr(tmp, '@');
2368 res = dundi_query_eid(&dei, context, eid);
2370 ast_cli(fd, "DUNDi Query EID returned error.\n");
2372 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2374 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2375 ast_cli(fd, "Department: %s\n", dei.orgunit);
2376 ast_cli(fd, "Organization: %s\n", dei.org);
2377 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2378 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2379 ast_cli(fd, "Country: %s\n", dei.country);
2380 ast_cli(fd, "E-mail: %s\n", dei.email);
2381 ast_cli(fd, "Phone: %s\n", dei.phone);
2382 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2384 return RESULT_SUCCESS;
2387 static int dundi_show_peer(int fd, int argc, char *argv[])
2389 struct dundi_peer *peer;
2390 struct permission *p;
2396 return RESULT_SHOWUSAGE;
2397 AST_LIST_LOCK(&peers);
2398 AST_LIST_TRAVERSE(&peers, peer, list) {
2399 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2403 switch(peer->order) {
2408 order = "Secondary";
2414 order = "Quartiary";
2419 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2420 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2421 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2422 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2423 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2424 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2425 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2426 if (!AST_LIST_EMPTY(&peer->include))
2427 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2428 AST_LIST_TRAVERSE(&peer->include, p, list)
2429 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2430 if (!AST_LIST_EMPTY(&peer->permit))
2431 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2432 AST_LIST_TRAVERSE(&peer->permit, p, list)
2433 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2435 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2436 if (peer->lookups[x]) {
2438 ast_cli(fd, "Last few query times:\n");
2439 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2444 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2446 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2447 AST_LIST_UNLOCK(&peers);
2448 return RESULT_SUCCESS;
2451 static int dundi_show_peers(int fd, int argc, char *argv[])
2453 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2454 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2455 struct dundi_peer *peer;
2456 int registeredonly=0;
2459 int online_peers = 0;
2460 int offline_peers = 0;
2461 int unmonitored_peers = 0;
2462 int total_peers = 0;
2464 if ((argc != 3) && (argc != 4) && (argc != 5))
2465 return RESULT_SHOWUSAGE;
2467 if (!strcasecmp(argv[3], "registered")) {
2470 return RESULT_SHOWUSAGE;
2472 AST_LIST_LOCK(&peers);
2473 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2474 AST_LIST_TRAVERSE(&peers, peer, list) {
2476 int print_line = -1;
2479 if (registeredonly && !peer->addr.sin_addr.s_addr)
2482 if (peer->lastms < 0) {
2483 strcpy(status, "UNREACHABLE");
2486 else if (peer->lastms > peer->maxms) {
2487 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2490 else if (peer->lastms) {
2491 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2495 strcpy(status, "UNKNOWN");
2499 strcpy(status, "Unmonitored");
2500 unmonitored_peers++;
2503 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2505 strcpy(avgms, "Unavail");
2506 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2507 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2508 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2511 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2513 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2515 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2523 ast_cli(fd, 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 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2529 AST_LIST_UNLOCK(&peers);
2530 return RESULT_SUCCESS;
2535 static int dundi_show_trans(int fd, int argc, char *argv[])
2537 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2538 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2539 struct dundi_transaction *trans;
2541 return RESULT_SHOWUSAGE;
2542 AST_LIST_LOCK(&peers);
2543 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2544 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2545 ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2546 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2548 AST_LIST_UNLOCK(&peers);
2549 return RESULT_SUCCESS;
2554 static int dundi_show_entityid(int fd, int argc, char *argv[])
2558 return RESULT_SHOWUSAGE;
2559 AST_LIST_LOCK(&peers);
2560 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2561 AST_LIST_UNLOCK(&peers);
2562 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2563 return RESULT_SUCCESS;
2566 static int dundi_show_requests(int fd, int argc, char *argv[])
2568 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2569 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2570 struct dundi_request *req;
2573 return RESULT_SHOWUSAGE;
2574 AST_LIST_LOCK(&peers);
2575 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2576 AST_LIST_TRAVERSE(&requests, req, list) {
2577 ast_cli(fd, FORMAT, req->number, req->dcontext,
2578 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2580 AST_LIST_UNLOCK(&peers);
2581 return RESULT_SUCCESS;
2586 /* Grok-a-dial DUNDi */
2588 static int dundi_show_mappings(int fd, int argc, char *argv[])
2590 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2591 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2592 struct dundi_mapping *map;
2595 return RESULT_SHOWUSAGE;
2596 AST_LIST_LOCK(&peers);
2597 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2598 AST_LIST_TRAVERSE(&mappings, map, list) {
2599 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2600 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2601 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2603 AST_LIST_UNLOCK(&peers);
2604 return RESULT_SUCCESS;
2609 static int dundi_show_precache(int fd, int argc, char *argv[])
2611 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2612 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2613 struct dundi_precache_queue *qe;
2618 return RESULT_SHOWUSAGE;
2620 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2621 AST_LIST_LOCK(&pcq);
2622 AST_LIST_TRAVERSE(&pcq, qe, list) {
2623 s = qe->expiration - now;
2628 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2630 AST_LIST_UNLOCK(&pcq);
2632 return RESULT_SUCCESS;
2637 static char debug_usage[] =
2638 "Usage: dundi debug\n"
2639 " Enables dumping of DUNDi packets for debugging purposes\n";
2641 static char no_debug_usage[] =
2642 "Usage: dundi no debug\n"
2643 " Disables dumping of DUNDi packets for debugging purposes\n";
2645 static char store_history_usage[] =
2646 "Usage: dundi store history\n"
2647 " Enables storing of DUNDi requests and times for debugging\n"
2650 static char no_store_history_usage[] =
2651 "Usage: dundi no store history\n"
2652 " Disables storing of DUNDi requests and times for debugging\n"
2655 static char show_peers_usage[] =
2656 "Usage: dundi show peers\n"
2657 " Lists all known DUNDi peers.\n";
2659 static char show_trans_usage[] =
2660 "Usage: dundi show trans\n"
2661 " Lists all known DUNDi transactions.\n";
2663 static char show_mappings_usage[] =
2664 "Usage: dundi show mappings\n"
2665 " Lists all known DUNDi mappings.\n";
2667 static char show_precache_usage[] =
2668 "Usage: dundi show precache\n"
2669 " Lists all known DUNDi scheduled precache updates.\n";
2671 static char show_entityid_usage[] =
2672 "Usage: dundi show entityid\n"
2673 " Displays the global entityid for this host.\n";
2675 static char show_peer_usage[] =
2676 "Usage: dundi show peer [peer]\n"
2677 " Provide a detailed description of a specifid DUNDi peer.\n";
2679 static char show_requests_usage[] =
2680 "Usage: dundi show requests\n"
2681 " Lists all known pending DUNDi requests.\n";
2683 static char lookup_usage[] =
2684 "Usage: dundi lookup <number>[@context] [bypass]\n"
2685 " Lookup the given number within the given DUNDi context\n"
2686 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2687 "keyword is specified.\n";
2689 static char precache_usage[] =
2690 "Usage: dundi precache <number>[@context]\n"
2691 " Lookup the given number within the given DUNDi context\n"
2692 "(or e164 if none is specified) and precaches the results to any\n"
2693 "upstream DUNDi push servers.\n";
2695 static char query_usage[] =
2696 "Usage: dundi query <entity>[@context]\n"
2697 " Attempts to retrieve contact information for a specific\n"
2698 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2699 "e164 if none is specified).\n";
2701 static char flush_usage[] =
2702 "Usage: dundi flush [stats]\n"
2703 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2704 "'stats' is present, clears timer statistics instead of normal\n"
2707 static struct ast_cli_entry cli_dundi[] = {
2708 { { "dundi", "debug", NULL },
2709 dundi_do_debug, "Enable DUNDi debugging",
2712 { { "dundi", "store", "history", NULL },
2713 dundi_do_store_history, "Enable DUNDi historic records",
2714 store_history_usage },
2716 { { "dundi", "no", "store", "history", NULL },
2717 dundi_no_store_history, "Disable DUNDi historic records",
2718 no_store_history_usage },
2720 { { "dundi", "flush", NULL },
2721 dundi_flush, "Flush DUNDi cache",
2724 { { "dundi", "no", "debug", NULL },
2725 dundi_no_debug, "Disable DUNDi debugging",
2728 { { "dundi", "show", "peers", NULL },
2729 dundi_show_peers, "Show defined DUNDi peers",
2732 { { "dundi", "show", "trans", NULL },
2733 dundi_show_trans, "Show active DUNDi transactions",
2736 { { "dundi", "show", "entityid", NULL },
2737 dundi_show_entityid, "Display Global Entity ID",
2738 show_entityid_usage },
2740 { { "dundi", "show", "mappings", NULL },
2741 dundi_show_mappings, "Show DUNDi mappings",
2742 show_mappings_usage },
2744 { { "dundi", "show", "precache", NULL },
2745 dundi_show_precache, "Show DUNDi precache",
2746 show_precache_usage },
2748 { { "dundi", "show", "requests", NULL },
2749 dundi_show_requests, "Show DUNDi requests",
2750 show_requests_usage },
2752 { { "dundi", "show", "peer", NULL },
2753 dundi_show_peer, "Show info on a specific DUNDi peer",
2754 show_peer_usage, complete_peer_4 },
2756 { { "dundi", "lookup", NULL },
2757 dundi_do_lookup, "Lookup a number in DUNDi",
2760 { { "dundi", "precache", NULL },
2761 dundi_do_precache, "Precache a number in DUNDi",
2764 { { "dundi", "query", NULL },
2765 dundi_do_query, "Query a DUNDi EID",
2769 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2771 struct dundi_transaction *trans;
2774 /* Don't allow creation of transactions to non-registered peers */
2775 if (p && !p->addr.sin_addr.s_addr)
2777 tid = get_trans_id();
2780 trans = ast_calloc(1, sizeof(*trans));
2782 if (global_storehistory) {
2783 trans->start = ast_tvnow();
2784 ast_set_flag(trans, FLAG_STOREHIST);
2786 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2787 trans->autokillid = -1;
2789 apply_peer(trans, p);
2790 if (!p->sentfullkey)
2791 ast_set_flag(trans, FLAG_SENDFULLKEY);
2793 trans->strans = tid;
2794 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2799 static int dundi_xmit(struct dundi_packet *pack)
2803 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2804 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2806 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2807 ast_inet_ntoa(pack->parent->addr.sin_addr),
2808 ntohs(pack->parent->addr.sin_port), strerror(errno));
2815 static void destroy_packet(struct dundi_packet *pack, int needfree)
2818 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2819 if (pack->retransid > -1)
2820 ast_sched_del(sched, pack->retransid);
2824 pack->retransid = -1;
2827 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2829 struct dundi_peer *peer;
2834 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2835 AST_LIST_TRAVERSE(&peers, peer, list) {
2836 if (peer->regtrans == trans)
2837 peer->regtrans = NULL;
2838 if (peer->qualtrans == trans) {
2840 if (peer->lastms > -1)
2841 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2844 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2847 if (ms < peer->maxms) {
2848 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2849 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2850 } else if (peer->lastms < peer->maxms) {
2851 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);
2855 peer->qualtrans = NULL;
2857 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2858 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2859 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2862 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2863 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2864 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2865 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2866 peer->lookups[x] = peer->lookups[x-1];
2867 if (peer->lookups[x]) {
2868 peer->avgms += peer->lookuptimes[x];
2872 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2873 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2874 if (peer->lookups[0]) {
2875 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2876 peer->avgms += peer->lookuptimes[0];
2886 if (trans->parent) {
2887 /* Unlink from parent if appropriate */
2888 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
2889 if (AST_LIST_EMPTY(&trans->parent->trans)) {
2890 /* Wake up sleeper */
2891 if (trans->parent->pfds[1] > -1) {
2892 write(trans->parent->pfds[1], "killa!", 6);
2896 /* Unlink from all trans */
2897 AST_LIST_REMOVE(&alltrans, trans, all);
2898 destroy_packets(&trans->packets);
2899 destroy_packets(&trans->lasttrans);
2900 if (trans->autokillid > -1)
2901 ast_sched_del(sched, trans->autokillid);
2902 trans->autokillid = -1;
2903 if (trans->thread) {
2904 /* If used by a thread, mark as dead and be done */
2905 ast_set_flag(trans, FLAG_DEAD);
2910 static int dundi_rexmit(void *data)
2912 struct dundi_packet *pack;
2914 AST_LIST_LOCK(&peers);
2916 if (pack->retrans < 1) {
2917 pack->retransid = -1;
2918 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2919 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2920 ast_inet_ntoa(pack->parent->addr.sin_addr),
2921 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2922 destroy_trans(pack->parent, 1);
2925 /* Decrement retransmission, try again */
2930 AST_LIST_UNLOCK(&peers);
2934 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2936 struct dundi_packet *pack;
2940 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2941 /* Reserve enough space for encryption */
2942 if (ast_test_flag(trans, FLAG_ENCRYPT))
2944 pack = ast_calloc(1, len);
2946 pack->h = (struct dundi_hdr *)(pack->data);
2947 if (cmdresp != DUNDI_COMMAND_ACK) {
2948 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2949 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2950 AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
2952 pack->parent = trans;
2953 pack->h->strans = htons(trans->strans);
2954 pack->h->dtrans = htons(trans->dtrans);
2955 pack->h->iseqno = trans->iseqno;
2956 pack->h->oseqno = trans->oseqno;
2957 pack->h->cmdresp = cmdresp;
2958 pack->datalen = sizeof(struct dundi_hdr);
2960 memcpy(pack->h->ies, ied->buf, ied->pos);
2961 pack->datalen += ied->pos;
2964 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
2965 ast_set_flag(trans, FLAG_FINAL);
2967 pack->h->cmdflags = flags;
2968 if (cmdresp != DUNDI_COMMAND_ACK) {
2970 trans->oseqno = trans->oseqno % 256;
2972 trans->aseqno = trans->iseqno;
2973 /* If we have their public key, encrypt */
2974 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
2976 case DUNDI_COMMAND_REGREQ:
2977 case DUNDI_COMMAND_REGRESPONSE:
2978 case DUNDI_COMMAND_DPDISCOVER:
2979 case DUNDI_COMMAND_DPRESPONSE:
2980 case DUNDI_COMMAND_EIDQUERY:
2981 case DUNDI_COMMAND_EIDRESPONSE:
2982 case DUNDI_COMMAND_PRECACHERQ:
2983 case DUNDI_COMMAND_PRECACHERP:
2985 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
2986 res = dundi_encrypt(trans, pack);
2994 res = dundi_xmit(pack);
2996 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
2998 if (cmdresp == DUNDI_COMMAND_ACK)
3005 static int do_autokill(void *data)
3007 struct dundi_transaction *trans = data;
3009 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3010 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3011 trans->autokillid = -1;
3012 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3016 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3018 struct dundi_peer *p;
3019 if (!dundi_eid_cmp(eid, us)) {
3020 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3023 AST_LIST_LOCK(&peers);
3024 AST_LIST_TRAVERSE(&peers, p, list) {
3025 if (!dundi_eid_cmp(&p->eid, eid)) {
3026 if (has_permission(&p->include, context))
3027 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3029 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3034 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3035 AST_LIST_UNLOCK(&peers);
3038 static int dundi_discover(struct dundi_transaction *trans)
3040 struct dundi_ie_data ied;
3042 if (!trans->parent) {
3043 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3046 memset(&ied, 0, sizeof(ied));
3047 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3048 if (!dundi_eid_zero(&trans->us_eid))
3049 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3050 for (x=0;x<trans->eidcount;x++)
3051 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3052 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3053 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3054 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3055 if (trans->parent->cbypass)
3056 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3057 if (trans->autokilltimeout)
3058 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3059 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3062 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3064 struct dundi_ie_data ied;
3067 int expiration = dundi_cache_time;
3069 dundi_eid *avoid[1] = { NULL, };
3070 int direct[1] = { 0, };
3071 struct dundi_result dr[MAX_RESULTS];
3072 struct dundi_hint_metadata hmd;
3073 if (!trans->parent) {
3074 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3077 memset(&hmd, 0, sizeof(hmd));
3078 memset(&dr, 0, sizeof(dr));
3079 /* Look up the answers we're going to include */
3080 for (x=0;x<mapcount;x++)
3081 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3084 for (x=0;x<ouranswers;x++) {
3085 if (dr[x].weight < max)
3089 /* If we do not have a canonical result, keep looking */
3090 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
3092 /* Append answer in result */
3097 if (ouranswers > 0) {
3098 *foundanswers += ouranswers;
3099 memset(&ied, 0, sizeof(ied));
3100 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3101 if (!dundi_eid_zero(&trans->us_eid))
3102 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3103 for (x=0;x<trans->eidcount;x++)
3104 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3105 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3106 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3107 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3108 for (x=0;x<ouranswers;x++) {
3110 if (dr[x].expiration && (expiration > dr[x].expiration))
3111 expiration = dr[x].expiration;
3112 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3114 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3115 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3116 if (trans->autokilltimeout)
3117 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3118 if (expiration < *minexp)
3119 *minexp = expiration;
3120 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3122 /* Oops, nothing to send... */
3123 destroy_trans(trans, 0);
3128 static int dundi_query(struct dundi_transaction *trans)
3130 struct dundi_ie_data ied;
3132 if (!trans->parent) {
3133 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3136 memset(&ied, 0, sizeof(ied));
3137 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3138 if (!dundi_eid_zero(&trans->us_eid))
3139 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3140 for (x=0;x<trans->eidcount;x++)
3141 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3142 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3143 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3144 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3145 if (trans->autokilltimeout)
3146 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3147 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3150 static int discover_transactions(struct dundi_request *dr)
3152 struct dundi_transaction *trans;
3153 AST_LIST_LOCK(&peers);
3154 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3155 dundi_discover(trans);
3157 AST_LIST_UNLOCK(&peers);
3161 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3163 struct dundi_transaction *trans;
3165 /* Mark all as "in thread" so they don't disappear */
3166 AST_LIST_LOCK(&peers);
3167 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3169 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3172 AST_LIST_UNLOCK(&peers);
3174 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3175 if (!ast_test_flag(trans, FLAG_DEAD))
3176 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3179 /* Cleanup any that got destroyed in the mean time */
3180 AST_LIST_LOCK(&peers);
3181 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
3183 if (ast_test_flag(trans, FLAG_DEAD)) {
3185 ast_log(LOG_DEBUG, "Our transaction went away!\n");
3186 /* This is going to remove the transaction from the dundi_request's list, as well
3187 * as the global transactions list */
3188 destroy_trans(trans, 0);
3191 AST_LIST_TRAVERSE_SAFE_END
3192 AST_LIST_UNLOCK(&peers);
3197 static int query_transactions(struct dundi_request *dr)
3199 struct dundi_transaction *trans;
3201 AST_LIST_LOCK(&peers);
3202 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3205 AST_LIST_UNLOCK(&peers);
3210 static int optimize_transactions(struct dundi_request *dr, int order)
3212 /* Minimize the message propagation through DUNDi by
3213 alerting the network to hops which should be not be considered */
3214 struct dundi_transaction *trans;
3215 struct dundi_peer *peer;
3220 AST_LIST_LOCK(&peers);
3221 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3222 /* Pop off the true root */
3223 if (trans->eidcount) {
3224 tmp = trans->eids[--trans->eidcount];
3227 tmp = trans->us_eid;
3231 AST_LIST_TRAVERSE(&peers, peer, list) {
3232 if (has_permission(&peer->include, dr->dcontext) &&
3233 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3234 (peer->order <= order)) {
3235 /* For each other transaction, make sure we don't
3236 ask this EID about the others if they're not
3237 already in the list */
3238 if (!dundi_eid_cmp(&tmp, &peer->eid))
3241 for (x=0;x<trans->eidcount;x++) {
3242 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3246 if (x == trans->eidcount) {
3247 /* Nope not in the list, if needed, add us at the end since we're the source */
3248 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3249 trans->eids[trans->eidcount++] = peer->eid;
3250 /* Need to insert the real root (or us) at the bottom now as
3251 a requirement now. */
3257 /* If necessary, push the true root back on the end */
3259 trans->eids[trans->eidcount++] = tmp;
3261 AST_LIST_UNLOCK(&peers);
3266 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3268 struct dundi_transaction *trans;
3273 /* Ignore if not registered */
3274 if (!p->addr.sin_addr.s_addr)
3276 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3280 if (ast_strlen_zero(dr->number))
3281 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
3283 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
3286 trans = create_transaction(p);
3291 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3292 trans->eids[x] = *avoid[x];
3293 trans->eidcount = x;
3294 AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
3299 static void cancel_request(struct dundi_request *dr)
3301 struct dundi_transaction *trans;
3303 AST_LIST_LOCK(&peers);
3304 while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
3305 /* Orphan transaction from request */
3306 trans->parent = NULL;
3307 /* Send final cancel */
3308 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3310 AST_LIST_UNLOCK(&peers);
3313 static void abort_request(struct dundi_request *dr)
3315 struct dundi_transaction *trans;
3317 AST_LIST_LOCK(&peers);
3318 while ((trans = AST_LIST_FIRST(&dr->trans))) {
3319 /* This will remove the transaction from the list */
3320 destroy_trans(trans, 0);
3322 AST_LIST_UNLOCK(&peers);
3325 static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
3327 struct dundi_peer *p;
3333 AST_LIST_LOCK(&peers);
3334 AST_LIST_TRAVERSE(&peers, p, list) {
3335 if (modeselect == 1) {
3336 /* Send the precache to push upstreams only! */
3337 pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
3340 /* Normal lookup / EID query */
3341 pass = has_permission(&p->include, dr->dcontext);
3342 allowconnect = p->model & DUNDI_MODEL_OUTBOUND;