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)
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <sys/socket.h>
37 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
38 #include <sys/types.h>
39 #include <netinet/in_systm.h>
41 #include <netinet/ip.h>
42 #include <sys/ioctl.h>
43 #include <netinet/in.h>
45 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
46 #include <net/if_dl.h>
53 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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 extern char ast_config_AST_KEY_DIR[];
85 #define DUNDI_MODEL_INBOUND (1 << 0)
86 #define DUNDI_MODEL_OUTBOUND (1 << 1)
87 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
89 /* Keep times of last 10 lookups */
90 #define DUNDI_TIMING_HISTORY 10
92 #define FLAG_ISREG (1 << 0) /* Transaction is register request */
93 #define FLAG_DEAD (1 << 1) /* Transaction is dead */
94 #define FLAG_FINAL (1 << 2) /* Transaction has final message sent */
95 #define FLAG_ISQUAL (1 << 3) /* Transaction is a qualification */
96 #define FLAG_ENCRYPT (1 << 4) /* Transaction is encrypted wiht ECX/DCX */
97 #define FLAG_SENDFULLKEY (1 << 5) /* Send full key on transaction */
98 #define FLAG_STOREHIST (1 << 6) /* Record historic performance */
100 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
103 #define DUNDI_SECRET_TIME 15 /* Testing only */
105 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
111 static struct io_context *io;
112 static struct sched_context *sched;
113 static int netsocket = -1;
114 static pthread_t netthreadid = AST_PTHREADT_NULL;
115 static pthread_t precachethreadid = AST_PTHREADT_NULL;
117 static int dundidebug = 0;
118 static int authdebug = 0;
119 static int dundi_ttl = DUNDI_DEFAULT_TTL;
120 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
121 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
122 static int global_autokilltimeout = 0;
123 static dundi_eid global_eid;
124 static int default_expiration = 60;
125 static int global_storehistory = 0;
126 static char dept[80];
128 static char locality[80];
129 static char stateprov[80];
130 static char country[80];
131 static char email[80];
132 static char phone[80];
133 static char secretpath[80];
134 static char cursecret[80];
135 static char ipaddr[80];
136 static time_t rotatetime;
137 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
139 struct permission *next;
144 struct dundi_packet {
146 struct dundi_packet *next;
148 struct dundi_transaction *parent;
151 unsigned char data[0];
154 struct dundi_hint_metadata {
155 unsigned short flags;
156 char exten[AST_MAX_EXTENSION];
159 struct dundi_precache_queue {
160 struct dundi_precache_queue *next;
166 struct dundi_request;
168 struct dundi_transaction {
169 struct sockaddr_in addr; /* Other end of transaction */
170 struct timeval start; /* When this transaction was created */
171 dundi_eid eids[DUNDI_MAX_STACK + 1];
172 int eidcount; /* Number of eids in eids */
173 dundi_eid us_eid; /* Our EID, to them */
174 dundi_eid them_eid; /* Their EID, to us */
175 aes_encrypt_ctx ecx; /* AES 128 Encryption context */
176 aes_decrypt_ctx dcx; /* AES 128 Decryption context */
177 unsigned int flags; /* Has final packet been sent */
178 int ttl; /* Remaining TTL for queries on this one */
179 int thread; /* We have a calling thread */
180 int retranstimer; /* How long to wait before retransmissions */
181 int autokillid; /* ID to kill connection if answer doesn't come back fast enough */
182 int autokilltimeout; /* Recommended timeout for autokill */
183 unsigned short strans; /* Our transaction identifier */
184 unsigned short dtrans; /* Their transaction identifer */
185 unsigned char iseqno; /* Next expected received seqno */
186 unsigned char oiseqno; /* Last received incoming seqno */
187 unsigned char oseqno; /* Next transmitted seqno */
188 unsigned char aseqno; /* Last acknowledge seqno */
189 struct dundi_packet *packets; /* Packets to be retransmitted */
190 struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
191 struct dundi_transaction *next; /* Next with respect to the parent */
192 struct dundi_request *parent; /* Parent request (if there is one) */
193 struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
196 struct dundi_request {
197 char dcontext[AST_MAX_EXTENSION];
198 char number[AST_MAX_EXTENSION];
201 struct dundi_result *dr;
202 struct dundi_entity_info *dei;
203 struct dundi_hint_metadata *hmd;
209 unsigned long crc32; /* CRC-32 of all but root EID's in avoid list */
210 struct dundi_transaction *trans; /* Transactions */
211 struct dundi_request *next;
214 struct dundi_mapping {
215 char dcontext[AST_MAX_EXTENSION];
216 char lcontext[AST_MAX_EXTENSION];
221 char dest[AST_MAX_EXTENSION];
222 AST_LIST_ENTRY(dundi_mapping) list;
227 struct sockaddr_in addr; /*!< Address of DUNDi peer */
228 struct permission *permit;
229 struct permission *include;
230 struct permission *precachesend;
231 struct permission *precachereceive;
240 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
241 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
242 unsigned long us_keycrc32; /*!< CRC-32 of our key */
243 aes_encrypt_ctx us_ecx; /*!< Cached AES 128 Encryption context */
244 aes_decrypt_ctx us_dcx; /*!< Cached AES 128 Decryption context */
245 unsigned long them_keycrc32; /*!< CRC-32 of our key */
246 aes_encrypt_ctx them_ecx; /*!< Cached AES 128 Encryption context */
247 aes_decrypt_ctx them_dcx; /*!< Cached AES 128 Decryption context */
248 time_t keyexpire; /*!< When to expire/recreate key */
250 int lookuptimes[DUNDI_TIMING_HISTORY];
251 char *lookups[DUNDI_TIMING_HISTORY];
253 struct dundi_transaction *regtrans; /*!< Registration transaction */
254 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
255 struct dundi_transaction *keypending;
256 int model; /*!< Pull model */
257 int pcmodel; /*!< Push/precache model */
258 int dynamic; /*!< Are we dynamic? */
259 int lastms; /*!< Last measured latency */
260 int maxms; /*!< Max permissible latency */
261 struct timeval qualtx; /*!< Time of transmit */
262 AST_LIST_ENTRY(dundi_peer) list;
265 AST_LIST_HEAD_STATIC(peers, dundi_peer);
266 AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
268 static struct dundi_precache_queue *pcq;
270 AST_MUTEX_DEFINE_STATIC(pclock);
272 static int dundi_xmit(struct dundi_packet *pack);
274 static void dundi_debug_output(const char *data)
277 ast_verbose("%s", data);
280 static void dundi_error_output(const char *data)
282 ast_log(LOG_WARNING, "%s", data);
285 static int has_permission(struct permission *ps, char *cont)
289 if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
296 static char *tech2str(int tech)
299 case DUNDI_PROTO_NONE:
301 case DUNDI_PROTO_IAX:
303 case DUNDI_PROTO_SIP:
305 case DUNDI_PROTO_H323:
312 static int str2tech(char *str)
314 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
315 return DUNDI_PROTO_IAX;
316 else if (!strcasecmp(str, "SIP"))
317 return DUNDI_PROTO_SIP;
318 else if (!strcasecmp(str, "H323"))
319 return DUNDI_PROTO_H323;
324 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[]);
325 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
326 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
327 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
329 /* Look for an exact match first */
330 struct dundi_transaction *trans;
333 if (!inaddrcmp(&trans->addr, sin) &&
334 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
335 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
337 trans->dtrans = ntohs(hdr->strans) & 32767;
340 trans = trans->allnext;
343 switch(hdr->cmdresp & 0x7f) {
344 case DUNDI_COMMAND_DPDISCOVER:
345 case DUNDI_COMMAND_EIDQUERY:
346 case DUNDI_COMMAND_PRECACHERQ:
347 case DUNDI_COMMAND_REGREQ:
348 case DUNDI_COMMAND_NULL:
349 case DUNDI_COMMAND_ENCRYPT:
351 /* Create new transaction */
352 trans = create_transaction(NULL);
354 memcpy(&trans->addr, sin, sizeof(trans->addr));
355 trans->dtrans = ntohs(hdr->strans) & 32767;
357 ast_log(LOG_WARNING, "Out of memory!\n");
367 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
369 static int dundi_ack(struct dundi_transaction *trans, int final)
371 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
373 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
376 struct dundi_packet pack;
377 struct dundi_hdr hdr;
379 struct dundi_transaction trans;
380 /* Never respond to an INVALID with another INVALID */
381 if (h->cmdresp == DUNDI_COMMAND_INVALID)
383 memset(&tmp, 0, sizeof(tmp));
384 memset(&trans, 0, sizeof(trans));
385 memcpy(&trans.addr, sin, sizeof(trans.addr));
386 tmp.hdr.strans = h->dtrans;
387 tmp.hdr.dtrans = h->strans;
388 tmp.hdr.iseqno = h->oseqno;
389 tmp.hdr.oseqno = h->iseqno;
390 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
391 tmp.hdr.cmdflags = 0;
392 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
393 tmp.pack.datalen = sizeof(struct dundi_hdr);
394 tmp.pack.parent = &trans;
395 dundi_xmit(&tmp.pack);
398 static void reset_global_eid(void)
400 #if defined(SIOCGIFHWADDR)
405 s = socket(AF_INET, SOCK_STREAM, 0);
409 memset(&ifr, 0, sizeof(ifr));
410 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
411 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
412 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
413 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
421 #if defined(ifa_broadaddr) && !defined(SOLARIS)
423 struct ifaddrs *ifap;
425 if (getifaddrs(&ifap) == 0) {
427 for (p = ifap; p; p = p->ifa_next) {
428 if (p->ifa_addr->sa_family == AF_LINK) {
429 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
432 sdp->sdl_data + sdp->sdl_nlen, 6);
433 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);
442 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
445 static int get_trans_id(void)
447 struct dundi_transaction *t;
448 int stid = (ast_random() % 32766) + 1;
453 if (t->strans == tid)
459 tid = (tid % 32766) + 1;
460 } while (tid != stid);
464 static int reset_transaction(struct dundi_transaction *trans)
467 tid = get_trans_id();
476 ast_clear_flag(trans, FLAG_FINAL);
480 static struct dundi_peer *find_peer(dundi_eid *eid)
482 struct dundi_peer *cur = NULL;
487 AST_LIST_TRAVERSE(&peers, cur, list) {
488 if (!dundi_eid_cmp(&cur->eid,eid))
495 static void build_iv(unsigned char *iv)
497 /* XXX Would be nice to be more random XXX */
498 unsigned int *fluffy;
500 fluffy = (unsigned int *)(iv);
502 fluffy[x] = ast_random();
505 struct dundi_query_state {
506 dundi_eid *eids[DUNDI_MAX_STACK + 1];
507 int directs[DUNDI_MAX_STACK + 1];
509 char called_context[AST_MAX_EXTENSION];
510 char called_number[AST_MAX_EXTENSION];
511 struct dundi_mapping *maps;
514 struct dundi_transaction *trans;
521 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)
523 struct ast_flags flags = {0};
525 if (!ast_strlen_zero(map->lcontext)) {
526 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
527 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
528 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
529 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
530 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
531 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
532 if (ast_ignore_pattern(map->lcontext, called_number))
533 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
535 /* Clearly we can't say 'don't ask' anymore if we found anything... */
536 if (ast_test_flag(&flags, AST_FLAGS_ALL))
537 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
539 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
540 /* Skip partial answers */
541 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
543 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
544 struct varshead headp;
545 struct ast_var_t *newvariable;
546 ast_set_flag(&flags, map->options & 0xffff);
547 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
548 dr[anscnt].techint = map->tech;
549 dr[anscnt].weight = map->weight;
550 dr[anscnt].expiration = dundi_cache_time;
551 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
552 dr[anscnt].eid = *us_eid;
553 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
554 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
555 AST_LIST_HEAD_INIT_NOLOCK(&headp);
556 newvariable = ast_var_assign("NUMBER", called_number);
557 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
558 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
559 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
560 newvariable = ast_var_assign("SECRET", cursecret);
561 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
562 newvariable = ast_var_assign("IPADDR", ipaddr);
563 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
564 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
565 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
566 ast_var_delete(newvariable);
568 dr[anscnt].dest[0] = '\0';
571 /* No answers... Find the fewest number of digits from the
572 number for which we have no answer. */
573 char tmp[AST_MAX_EXTENSION];
574 for (x=0;x<AST_MAX_EXTENSION;x++) {
575 tmp[x] = called_number[x];
578 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
579 /* Oops found something we can't match. If this is longer
580 than the running hint, we have to consider it */
581 if (strlen(tmp) > strlen(hmd->exten)) {
582 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
592 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
594 static void *dundi_lookup_thread(void *data)
596 struct dundi_query_state *st = data;
597 struct dundi_result dr[MAX_RESULTS];
598 struct dundi_ie_data ied;
599 struct dundi_hint_metadata hmd;
604 int expiration = dundi_cache_time;
606 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
607 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
608 memset(&ied, 0, sizeof(ied));
609 memset(&dr, 0, sizeof(dr));
610 memset(&hmd, 0, sizeof(hmd));
611 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
612 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
613 for (x=0;x<st->nummaps;x++)
614 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
617 for (x=0;x<ouranswers;x++) {
618 if (dr[x].weight < max)
623 /* If we do not have a canonical result, keep looking */
624 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);
626 /* Append answer in result */
629 if ((res < -1) && (!ouranswers))
630 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
633 AST_LIST_LOCK(&peers);
634 /* Truncate if "don't ask" isn't present */
635 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
637 if (ast_test_flag(st->trans, FLAG_DEAD)) {
638 ast_log(LOG_DEBUG, "Our transaction went away!\n");
639 st->trans->thread = 0;
640 destroy_trans(st->trans, 0);
642 for (x=0;x<ouranswers;x++) {
644 if (dr[x].expiration && (expiration > dr[x].expiration))
645 expiration = dr[x].expiration;
646 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
648 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
649 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
650 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
651 st->trans->thread = 0;
653 AST_LIST_UNLOCK(&peers);
658 static void *dundi_precache_thread(void *data)
660 struct dundi_query_state *st = data;
661 struct dundi_ie_data ied;
662 struct dundi_hint_metadata hmd;
665 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
666 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
667 memset(&ied, 0, sizeof(ied));
669 /* Now produce precache */
670 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
672 AST_LIST_LOCK(&peers);
673 /* Truncate if "don't ask" isn't present */
674 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
676 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;
699 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
700 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
701 memset(&ied, 0, sizeof(ied));
702 memset(&dei, 0, sizeof(dei));
703 memset(&hmd, 0, sizeof(hmd));
704 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
706 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
707 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
708 ast_copy_string(dei.org, org, sizeof(dei.org));
709 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
710 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
711 ast_copy_string(dei.country, country, sizeof(dei.country));
712 ast_copy_string(dei.email, email, sizeof(dei.email));
713 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
716 /* If we do not have a canonical result, keep looking */
717 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
719 AST_LIST_LOCK(&peers);
720 if (ast_test_flag(st->trans, FLAG_DEAD)) {
721 ast_log(LOG_DEBUG, "Our transaction went away!\n");
722 st->trans->thread = 0;
723 destroy_trans(st->trans, 0);
726 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
727 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
728 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
729 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
730 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
731 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
732 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
733 if (!ast_strlen_zero(dei.ipaddr))
734 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
736 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
737 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
738 st->trans->thread = 0;
740 AST_LIST_UNLOCK(&peers);
745 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
747 struct dundi_query_state *st;
751 struct dundi_ie_data ied;
754 pthread_t lookupthread;
756 if (ies->eidcount > 1) {
757 /* Since it is a requirement that the first EID is the authenticating host
758 and the last EID is the root, it is permissible that the first and last EID
759 could be the same. In that case, we should go ahead copy only the "root" section
760 since we will not need it for authentication. */
761 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
764 totallen = sizeof(struct dundi_query_state);
765 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
766 st = malloc(totallen);
768 memset(st, 0, totallen);
769 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
770 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
772 st->ttl = ies->ttl - 1;
776 for (x=skipfirst;ies->eids[x];x++) {
777 st->eids[x-skipfirst] = (dundi_eid *)s;
778 *st->eids[x-skipfirst] = *ies->eids[x];
779 s += sizeof(dundi_eid);
781 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);
782 pthread_attr_init(&attr);
783 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
785 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
787 ast_log(LOG_WARNING, "Unable to create thread!\n");
789 memset(&ied, 0, sizeof(ied));
790 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
791 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
795 ast_log(LOG_WARNING, "Out of memory!\n");
796 memset(&ied, 0, sizeof(ied));
797 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
798 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
804 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
809 char eidpeer_str[20];
810 char eidroot_str[20];
815 expiration = dundi_cache_time;
817 /* Only cache hint if "don't ask" is there... */
818 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
821 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
823 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
824 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
825 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
826 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
829 timeout += expiration;
830 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
832 ast_db_put("dundi/cache", key1, data);
833 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
834 ast_db_put("dundi/cache", key2, data);
835 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
839 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
845 char eidpeer_str[20];
846 char eidroot_str[20];
850 expiration = dundi_cache_time;
852 /* Keep pushes a little longer, cut pulls a little short */
859 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
860 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
861 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
862 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
863 /* Build request string */
865 timeout += expiration;
866 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
867 for (x=start;x<req->respcount;x++) {
868 /* Skip anything with an illegal pipe in it */
869 if (strchr(req->dr[x].dest, '|'))
871 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
872 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
873 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
875 ast_db_put("dundi/cache", key1, data);
876 ast_db_put("dundi/cache", key2, data);
880 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
882 struct dundi_query_state *st;
885 struct dundi_ie_data ied;
887 struct dundi_result dr2[MAX_RESULTS];
888 struct dundi_request dr;
889 struct dundi_hint_metadata hmd;
891 struct dundi_mapping *cur;
895 pthread_t lookupthread;
898 memset(&dr2, 0, sizeof(dr2));
899 memset(&dr, 0, sizeof(dr));
900 memset(&hmd, 0, sizeof(hmd));
902 /* Forge request structure to hold answers for cache */
903 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
905 dr.maxcount = MAX_RESULTS;
906 dr.expiration = dundi_cache_time;
908 dr.pfds[0] = dr.pfds[1] = -1;
910 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
911 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
913 for (x=0;x<ies->anscount;x++) {
914 if (trans->parent->respcount < trans->parent->maxcount) {
915 /* Make sure it's not already there */
916 for (z=0;z<trans->parent->respcount;z++) {
917 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
918 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
921 if (z == trans->parent->respcount) {
922 /* Copy into parent responses */
923 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
924 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
925 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
926 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
927 if (ies->expiration > 0)
928 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
930 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
931 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
932 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
933 &ies->answers[x]->eid);
934 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
935 sizeof(trans->parent->dr[trans->parent->respcount].dest));
936 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
937 sizeof(trans->parent->dr[trans->parent->respcount].tech));
938 trans->parent->respcount++;
939 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
940 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
941 /* Update weight if appropriate */
942 trans->parent->dr[z].weight = ies->answers[x]->weight;
945 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
946 trans->parent->number, trans->parent->dcontext);
949 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
950 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
952 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
954 totallen = sizeof(struct dundi_query_state);
955 /* Count matching map entries */
957 AST_LIST_TRAVERSE(&mappings, cur, list) {
958 if (!strcasecmp(cur->dcontext, ccontext))
962 /* If no maps, return -1 immediately */
966 if (ies->eidcount > 1) {
967 /* Since it is a requirement that the first EID is the authenticating host
968 and the last EID is the root, it is permissible that the first and last EID
969 could be the same. In that case, we should go ahead copy only the "root" section
970 since we will not need it for authentication. */
971 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
975 /* Prepare to run a query and then propagate that as necessary */
976 totallen += mapcount * sizeof(struct dundi_mapping);
977 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
978 st = malloc(totallen);
980 memset(st, 0, totallen);
981 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
982 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
984 st->ttl = ies->ttl - 1;
985 st->nocache = ies->cbypass;
989 for (x=skipfirst;ies->eids[x];x++) {
990 st->eids[x-skipfirst] = (dundi_eid *)s;
991 *st->eids[x-skipfirst] = *ies->eids[x];
992 st->directs[x-skipfirst] = ies->eid_direct[x];
993 s += sizeof(dundi_eid);
995 /* Append mappings */
997 st->maps = (struct dundi_mapping *)s;
998 AST_LIST_TRAVERSE(&mappings, cur, list) {
999 if (!strcasecmp(cur->dcontext, ccontext)) {
1002 st->maps[x].list.next = NULL;
1007 st->nummaps = mapcount;
1008 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1009 pthread_attr_init(&attr);
1010 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1012 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
1014 ast_log(LOG_WARNING, "Unable to create thread!\n");
1016 memset(&ied, 0, sizeof(ied));
1017 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1018 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1022 ast_log(LOG_WARNING, "Out of memory!\n");
1023 memset(&ied, 0, sizeof(ied));
1024 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1025 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1031 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1033 struct dundi_query_state *st;
1036 struct dundi_ie_data ied;
1038 struct dundi_mapping *cur;
1042 pthread_t lookupthread;
1043 pthread_attr_t attr;
1044 totallen = sizeof(struct dundi_query_state);
1045 /* Count matching map entries */
1046 AST_LIST_TRAVERSE(&mappings, cur, list) {
1047 if (!strcasecmp(cur->dcontext, ccontext))
1050 /* If no maps, return -1 immediately */
1054 if (ies->eidcount > 1) {
1055 /* Since it is a requirement that the first EID is the authenticating host
1056 and the last EID is the root, it is permissible that the first and last EID
1057 could be the same. In that case, we should go ahead copy only the "root" section
1058 since we will not need it for authentication. */
1059 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1063 totallen += mapcount * sizeof(struct dundi_mapping);
1064 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1065 st = malloc(totallen);
1067 memset(st, 0, totallen);
1068 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1069 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1071 st->ttl = ies->ttl - 1;
1072 st->nocache = ies->cbypass;
1076 for (x=skipfirst;ies->eids[x];x++) {
1077 st->eids[x-skipfirst] = (dundi_eid *)s;
1078 *st->eids[x-skipfirst] = *ies->eids[x];
1079 st->directs[x-skipfirst] = ies->eid_direct[x];
1080 s += sizeof(dundi_eid);
1082 /* Append mappings */
1084 st->maps = (struct dundi_mapping *)s;
1085 AST_LIST_TRAVERSE(&mappings, cur, list) {
1086 if (!strcasecmp(cur->dcontext, ccontext)) {
1089 st->maps[x].list.next = NULL;
1094 st->nummaps = mapcount;
1095 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1096 pthread_attr_init(&attr);
1097 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1099 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
1101 ast_log(LOG_WARNING, "Unable to create thread!\n");
1103 memset(&ied, 0, sizeof(ied));
1104 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1105 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1109 ast_log(LOG_WARNING, "Out of memory!\n");
1110 memset(&ied, 0, sizeof(ied));
1111 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1112 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1118 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1121 char *ptr, *term, *src;
1123 struct ast_flags flags;
1129 /* Build request string */
1130 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1133 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1134 int expiration = timeout - now;
1135 if (expiration > 0) {
1136 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1138 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1140 term = strchr(ptr, '|');
1143 src = strrchr(ptr, '/');
1149 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1150 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1151 /* Make sure it's not already there */
1152 for (z=0;z<req->respcount;z++) {
1153 if ((req->dr[z].techint == tech) &&
1154 !strcmp(req->dr[z].dest, ptr))
1157 if (z == req->respcount) {
1158 /* Copy into parent responses */
1159 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1160 req->dr[req->respcount].weight = weight;
1161 req->dr[req->respcount].techint = tech;
1162 req->dr[req->respcount].expiration = expiration;
1163 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1164 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1165 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1166 ast_copy_string(req->dr[req->respcount].dest, ptr,
1167 sizeof(req->dr[req->respcount].dest));
1168 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1169 sizeof(req->dr[req->respcount].tech));
1171 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1172 } else if (req->dr[z].weight > weight)
1173 req->dr[z].weight = weight;
1177 /* We found *something* cached */
1178 if (expiration < *lowexpiration)
1179 *lowexpiration = expiration;
1182 ast_db_del("dundi/cache", key);
1184 ast_db_del("dundi/cache", key);
1190 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1194 char eidroot_str[20];
1198 char eid_str_full[20];
1203 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1204 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1205 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1206 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1207 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1208 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1209 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1210 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1211 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1213 if (!req->respcount) {
1215 /* Look and see if we have a hint that would preclude us from looking at this
1216 peer for this number. */
1217 if (!(tmp[x] = req->number[x]))
1220 /* Check for hints */
1221 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1222 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1223 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1224 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1225 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1226 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1228 if (strlen(tmp) > strlen(req->hmd->exten)) {
1229 /* Update meta data if appropriate */
1230 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1240 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1242 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1244 if (!trans->addr.sin_addr.s_addr)
1245 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1246 trans->us_eid = p->us_eid;
1247 trans->them_eid = p->eid;
1248 /* Enable encryption if appropriate */
1249 if (!ast_strlen_zero(p->inkey))
1250 ast_set_flag(trans, FLAG_ENCRYPT);
1252 trans->autokilltimeout = p->maxms;
1253 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1254 if (p->lastms > 1) {
1255 trans->retranstimer = p->lastms * 2;
1256 /* Keep it from being silly */
1257 if (trans->retranstimer < 150)
1258 trans->retranstimer = 150;
1260 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1261 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1263 trans->autokilltimeout = global_autokilltimeout;
1266 /*! \note Called with the peers list already locked */
1267 static int do_register_expire(void *data)
1269 struct dundi_peer *peer = data;
1271 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1272 peer->registerexpire = -1;
1274 memset(&peer->addr, 0, sizeof(peer->addr));
1278 static int update_key(struct dundi_peer *peer)
1280 unsigned char key[16];
1281 struct ast_key *ekey, *skey;
1284 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1286 aes_encrypt_key128(key, &peer->us_ecx);
1287 aes_decrypt_key128(key, &peer->us_dcx);
1288 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1290 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1291 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1294 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1296 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1297 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1300 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1301 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1304 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1305 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1308 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1309 peer->sentfullkey = 0;
1311 time(&peer->keyexpire);
1312 peer->keyexpire += dundi_key_ttl;
1317 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
1319 unsigned char curblock[16];
1321 memcpy(curblock, iv, sizeof(curblock));
1324 curblock[x] ^= src[x];
1325 aes_encrypt(curblock, dst, ecx);
1326 memcpy(curblock, dst, sizeof(curblock));
1333 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
1335 unsigned char lastblock[16];
1337 memcpy(lastblock, iv, sizeof(lastblock));
1339 aes_decrypt(src, dst, dcx);
1341 dst[x] ^= lastblock[x];
1342 memcpy(lastblock, src, sizeof(lastblock));
1350 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)
1352 int space = *dstlen;
1353 unsigned long bytes;
1354 struct dundi_hdr *h;
1355 unsigned char *decrypt_space;
1356 decrypt_space = alloca(srclen);
1359 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1361 h = (struct dundi_hdr *)dst;
1364 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1365 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1369 *dstlen = bytes + 6;
1370 /* Return new header */
1374 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1376 unsigned char *compress_space;
1379 unsigned long bytes;
1380 struct dundi_ie_data ied;
1381 struct dundi_peer *peer;
1382 unsigned char iv[16];
1383 len = pack->datalen + pack->datalen / 100 + 42;
1384 compress_space = alloca(len);
1385 if (compress_space) {
1386 memset(compress_space, 0, len);
1387 /* We care about everthing save the first 6 bytes of header */
1389 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1391 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1394 memset(&ied, 0, sizeof(ied));
1395 /* Say who we are */
1396 if (!pack->h->iseqno && !pack->h->oseqno) {
1397 /* Need the key in the first copy */
1398 if (!(peer = find_peer(&trans->them_eid)))
1400 if (update_key(peer))
1402 if (!peer->sentfullkey)
1403 ast_set_flag(trans, FLAG_SENDFULLKEY);
1404 /* Append key data */
1405 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1406 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1407 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1408 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1410 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1412 /* Setup contexts */
1413 trans->ecx = peer->us_ecx;
1414 trans->dcx = peer->us_dcx;
1416 /* We've sent the full key */
1417 peer->sentfullkey = 1;
1419 /* Build initialization vector */
1421 /* Add the field, rounded up to 16 bytes */
1422 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1424 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1425 ast_log(LOG_NOTICE, "Final packet too large!\n");
1428 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1429 ied.pos += ((bytes + 15) / 16) * 16;
1430 /* Reconstruct header */
1431 pack->datalen = sizeof(struct dundi_hdr);
1432 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1433 pack->h->cmdflags = 0;
1434 memcpy(pack->h->ies, ied.buf, ied.pos);
1435 pack->datalen += ied.pos;
1441 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1443 unsigned char dst[128];
1445 struct ast_key *key, *skey;
1448 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1449 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1452 } else if (!newkey || !newsig)
1454 if (!memcmp(peer->rxenckey, newkey, 128) &&
1455 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1456 /* By definition, a match */
1460 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1462 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1463 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1467 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1469 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1470 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1474 /* First check signature */
1475 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1479 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1482 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1485 /* Decrypted, passes signature */
1486 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1487 memcpy(peer->rxenckey, newkey, 128);
1488 memcpy(peer->rxenckey + 128, newsig, 128);
1489 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1490 aes_decrypt_key128(dst, &peer->them_dcx);
1491 aes_encrypt_key128(dst, &peer->them_ecx);
1495 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1497 /* Handle canonical command / response */
1498 int final = hdr->cmdresp & 0x80;
1499 int cmd = hdr->cmdresp & 0x7f;
1504 unsigned char *bufcpy;
1505 struct dundi_ie_data ied;
1506 struct dundi_ies ies;
1507 struct dundi_peer *peer;
1510 memset(&ied, 0, sizeof(ied));
1511 memset(&ies, 0, sizeof(ies));
1513 bufcpy = alloca(datalen);
1516 /* Make a copy for parsing */
1517 memcpy(bufcpy, hdr->ies, datalen);
1518 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1519 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1520 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1525 case DUNDI_COMMAND_DPDISCOVER:
1526 case DUNDI_COMMAND_EIDQUERY:
1527 case DUNDI_COMMAND_PRECACHERQ:
1528 if (cmd == DUNDI_COMMAND_EIDQUERY)
1529 resp = DUNDI_COMMAND_EIDRESPONSE;
1530 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1531 resp = DUNDI_COMMAND_PRECACHERP;
1533 resp = DUNDI_COMMAND_DPRESPONSE;
1534 /* A dialplan or entity discover -- qualify by highest level entity */
1535 peer = find_peer(ies.eids[0]);
1537 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1538 dundi_send(trans, resp, 0, 1, &ied);
1541 trans->us_eid = peer->us_eid;
1542 if (strlen(peer->inkey)) {
1543 hasauth = encrypted;
1547 /* Okay we're authentiated and all, now we check if they're authorized */
1548 if (!ies.called_context)
1549 ies.called_context = "e164";
1550 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1551 res = dundi_answer_entity(trans, &ies, ies.called_context);
1553 if (ast_strlen_zero(ies.called_number)) {
1554 /* They're not permitted to access that context */
1555 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1556 dundi_send(trans, resp, 0, 1, &ied);
1557 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1558 (peer->model & DUNDI_MODEL_INBOUND) &&
1559 has_permission(peer->permit, ies.called_context)) {
1560 res = dundi_answer_query(trans, &ies, ies.called_context);
1562 /* There is no such dundi context */
1563 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1564 dundi_send(trans, resp, 0, 1, &ied);
1566 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1567 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1568 has_permission(peer->include, ies.called_context)) {
1569 res = dundi_prop_precache(trans, &ies, ies.called_context);
1571 /* There is no such dundi context */
1572 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1573 dundi_send(trans, resp, 0, 1, &ied);
1576 /* They're not permitted to access that context */
1577 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1578 dundi_send(trans, resp, 0, 1, &ied);
1582 /* They're not permitted to access that context */
1583 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1584 dundi_send(trans, resp, 0, 1, &ied);
1588 case DUNDI_COMMAND_REGREQ:
1589 /* A register request -- should only have one entity */
1590 peer = find_peer(ies.eids[0]);
1591 if (!peer || !peer->dynamic) {
1592 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1593 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1596 trans->us_eid = peer->us_eid;
1597 if (!ast_strlen_zero(peer->inkey)) {
1598 hasauth = encrypted;
1602 int expire = default_expiration;
1603 char iabuf[INET_ADDRSTRLEN];
1606 if (peer->registerexpire > -1)
1607 ast_sched_del(sched, peer->registerexpire);
1608 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1609 ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
1610 snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
1611 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1612 if (inaddrcmp(&peer->addr, &trans->addr)) {
1613 if (option_verbose > 2)
1614 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), iabuf, ntohs(trans->addr.sin_port));
1618 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1619 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1620 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1622 qualify_peer(peer, 1);
1626 case DUNDI_COMMAND_DPRESPONSE:
1627 /* A dialplan response, lets see what we got... */
1628 if (ies.cause < 1) {
1629 /* Success of some sort */
1630 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1631 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1632 authpass = encrypted;
1636 /* Pass back up answers */
1637 if (trans->parent && trans->parent->dr) {
1638 y = trans->parent->respcount;
1639 for (x=0;x<ies.anscount;x++) {
1640 if (trans->parent->respcount < trans->parent->maxcount) {
1641 /* Make sure it's not already there */
1642 for (z=0;z<trans->parent->respcount;z++) {
1643 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1644 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1647 if (z == trans->parent->respcount) {
1648 /* Copy into parent responses */
1649 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1650 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1651 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1652 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1653 if (ies.expiration > 0)
1654 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1656 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1657 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1658 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1659 &ies.answers[x]->eid);
1660 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1661 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1662 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1663 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1664 trans->parent->respcount++;
1665 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1666 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1667 /* Update weight if appropriate */
1668 trans->parent->dr[z].weight = ies.answers[x]->weight;
1671 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1672 trans->parent->number, trans->parent->dcontext);
1674 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1675 the cache know if this request was unaffected by our entity list. */
1676 cache_save(&trans->them_eid, trans->parent, y,
1677 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1679 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1680 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1681 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1682 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1683 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1684 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1685 sizeof(trans->parent->hmd->exten));
1688 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1691 if (ies.expiration > 0) {
1692 if (trans->parent->expiration > ies.expiration) {
1693 trans->parent->expiration = ies.expiration;
1697 /* Close connection if not final */
1699 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1703 /* Auth failure, check for data */
1705 /* Cancel if they didn't already */
1706 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1710 case DUNDI_COMMAND_EIDRESPONSE:
1711 /* A dialplan response, lets see what we got... */
1712 if (ies.cause < 1) {
1713 /* Success of some sort */
1714 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1715 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1716 authpass = encrypted;
1720 /* Pass back up answers */
1721 if (trans->parent && trans->parent->dei && ies.q_org) {
1722 if (!trans->parent->respcount) {
1723 trans->parent->respcount++;
1725 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1727 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1729 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1730 if (ies.q_stateprov)
1731 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1733 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1735 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1737 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1739 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1740 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1741 /* If it's them, update our address */
1742 ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
1743 trans->addr.sin_addr);
1747 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1748 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1751 /* Close connection if not final */
1753 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1757 /* Auth failure, check for data */
1759 /* Cancel if they didn't already */
1760 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1764 case DUNDI_COMMAND_REGRESPONSE:
1765 /* A dialplan response, lets see what we got... */
1766 if (ies.cause < 1) {
1768 /* Success of some sort */
1769 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1770 hasauth = encrypted;
1775 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1777 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1778 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1781 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),
1782 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1783 /* Close connection if not final */
1785 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1788 /* Auth failure, cancel if they didn't for some reason */
1790 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1794 case DUNDI_COMMAND_INVALID:
1795 case DUNDI_COMMAND_NULL:
1796 case DUNDI_COMMAND_PRECACHERP:
1797 /* Do nothing special */
1799 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1801 case DUNDI_COMMAND_ENCREJ:
1802 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
1803 /* No really, it's over at this point */
1805 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1807 /* Send with full key */
1808 ast_set_flag(trans, FLAG_SENDFULLKEY);
1810 /* Ooops, we got a final message, start by sending ACK... */
1811 dundi_ack(trans, hdr->cmdresp & 0x80);
1812 trans->aseqno = trans->iseqno;
1813 /* Now, we gotta create a new transaction */
1814 if (!reset_transaction(trans)) {
1815 /* Make sure handle_frame doesn't destroy us */
1816 hdr->cmdresp &= 0x7f;
1817 /* Parse the message we transmitted */
1818 memset(&ies, 0, sizeof(ies));
1819 dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
1820 /* Reconstruct outgoing encrypted packet */
1821 memset(&ied, 0, sizeof(ied));
1822 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1823 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1824 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1826 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1827 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
1828 peer->sentfullkey = 1;
1833 case DUNDI_COMMAND_ENCRYPT:
1835 /* No nested encryption! */
1836 if ((trans->iseqno == 1) && !trans->oseqno) {
1837 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1838 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1839 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1841 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1845 apply_peer(trans, peer);
1846 /* Key passed, use new contexts for this session */
1847 trans->ecx = peer->them_ecx;
1848 trans->dcx = peer->them_dcx;
1850 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1851 struct dundi_hdr *dhdr;
1852 unsigned char decoded[MAX_PACKET_SIZE];
1854 ddatalen = sizeof(decoded);
1855 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1857 /* Handle decrypted response */
1859 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1860 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1861 /* Carry back final flag */
1862 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1865 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1869 /* Turn off encryption */
1870 ast_clear_flag(trans, FLAG_ENCRYPT);
1871 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1875 /* Send unknown command if we don't know it, with final flag IFF it's the
1876 first command in the dialog and only if we haven't recieved final notification */
1878 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1879 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1885 static void destroy_packet(struct dundi_packet *pack, int needfree);
1886 static void destroy_packets(struct dundi_packet *p)
1888 struct dundi_packet *prev;
1892 if (prev->retransid > -1)
1893 ast_sched_del(sched, prev->retransid);
1899 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1901 /* Ack transmitted packet corresponding to iseqno */
1902 struct dundi_packet *pack;
1903 pack = trans->packets;
1905 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1906 destroy_packet(pack, 0);
1907 if (trans->lasttrans) {
1908 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1909 destroy_packets(trans->lasttrans);
1911 trans->lasttrans = pack;
1912 if (trans->autokillid > -1)
1913 ast_sched_del(sched, trans->autokillid);
1914 trans->autokillid = -1;
1922 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1924 struct dundi_transaction *trans;
1925 trans = find_transaction(h, sin);
1927 dundi_reject(h, sin);
1930 /* Got a transaction, see where this header fits in */
1931 if (h->oseqno == trans->iseqno) {
1932 /* Just what we were looking for... Anything but ack increments iseqno */
1933 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
1934 /* If final, we're done */
1935 destroy_trans(trans, 0);
1938 if (h->cmdresp != DUNDI_COMMAND_ACK) {
1939 trans->oiseqno = trans->iseqno;
1941 handle_command_response(trans, h, datalen, 0);
1943 if (trans->aseqno != trans->iseqno) {
1944 dundi_ack(trans, h->cmdresp & 0x80);
1945 trans->aseqno = trans->iseqno;
1947 /* Delete any saved last transmissions */
1948 destroy_packets(trans->lasttrans);
1949 trans->lasttrans = NULL;
1950 if (h->cmdresp & 0x80) {
1951 /* Final -- destroy now */
1952 destroy_trans(trans, 0);
1954 } else if (h->oseqno == trans->oiseqno) {
1955 /* Last incoming sequence number -- send ACK without processing */
1956 dundi_ack(trans, 0);
1958 /* Out of window -- simply drop */
1959 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
1964 static int socket_read(int *id, int fd, short events, void *cbdata)
1966 struct sockaddr_in sin;
1968 struct dundi_hdr *h;
1969 char buf[MAX_PACKET_SIZE];
1972 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
1974 if (errno != ECONNREFUSED)
1975 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
1978 if (res < sizeof(struct dundi_hdr)) {
1979 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
1983 h = (struct dundi_hdr *)buf;
1985 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
1986 AST_LIST_LOCK(&peers);
1987 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
1988 AST_LIST_UNLOCK(&peers);
1992 static void build_secret(char *secret, int seclen)
1994 unsigned char tmp[16];
1998 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
1999 /* Eliminate potential bad characters */
2000 while((s = strchr(secret, ';'))) *s = '+';
2001 while((s = strchr(secret, '/'))) *s = '+';
2002 while((s = strchr(secret, ':'))) *s = '+';
2003 while((s = strchr(secret, '@'))) *s = '+';
2007 static void save_secret(const char *newkey, const char *oldkey)
2011 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2013 snprintf(tmp, sizeof(tmp), "%s", newkey);
2014 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2015 ast_db_put(secretpath, "secret", tmp);
2016 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2017 ast_db_put(secretpath, "secretexpiry", tmp);
2020 static void load_password(void)
2027 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2028 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2029 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2030 current = strchr(tmp, ';');
2037 if ((time(NULL) - expired) < 0) {
2038 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2039 expired = time(NULL) + DUNDI_SECRET_TIME;
2040 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2049 /* Current key is still valid, just setup rotatation properly */
2050 ast_copy_string(cursecret, current, sizeof(cursecret));
2051 rotatetime = expired;
2053 /* Current key is out of date, rotate or eliminate all together */
2054 build_secret(cursecret, sizeof(cursecret));
2055 save_secret(cursecret, last);
2059 static void check_password(void)
2066 printf("%ld/%ld\n", now, rotatetime);
2068 if ((now - rotatetime) >= 0) {
2069 /* Time to rotate keys */
2070 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2071 build_secret(cursecret, sizeof(cursecret));
2072 save_secret(cursecret, oldsecret);
2076 static void *network_thread(void *ignore)
2078 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2079 from the network, and queue them for delivery to the channels */
2081 /* Establish I/O callback for socket read */
2082 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2084 res = ast_sched_wait(sched);
2085 if ((res > 1000) || (res < 0))
2087 res = ast_io_wait(io, res);
2089 AST_LIST_LOCK(&peers);
2090 ast_sched_runq(sched);
2091 AST_LIST_UNLOCK(&peers);
2098 static void *process_precache(void *ign)
2100 struct dundi_precache_queue *qe;
2108 ast_mutex_lock(&pclock);
2110 if (!pcq->expiration) {
2111 /* Gone... Remove... */
2115 } else if (pcq->expiration < now) {
2116 /* Process this entry */
2117 pcq->expiration = 0;
2118 ast_copy_string(context, pcq->context, sizeof(context));
2119 ast_copy_string(number, pcq->number, sizeof(number));
2123 ast_mutex_unlock(&pclock);
2125 dundi_precache(context, number);
2132 static int start_network_thread(void)
2134 ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
2135 ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
2139 static int dundi_do_debug(int fd, int argc, char *argv[])
2142 return RESULT_SHOWUSAGE;
2144 ast_cli(fd, "DUNDi Debugging Enabled\n");
2145 return RESULT_SUCCESS;
2148 static int dundi_do_store_history(int fd, int argc, char *argv[])
2151 return RESULT_SHOWUSAGE;
2152 global_storehistory = 1;
2153 ast_cli(fd, "DUNDi History Storage Enabled\n");
2154 return RESULT_SUCCESS;
2157 static int dundi_flush(int fd, int argc, char *argv[])
2160 if ((argc < 2) || (argc > 3))
2161 return RESULT_SHOWUSAGE;
2163 if (!strcasecmp(argv[2], "stats"))
2166 return RESULT_SHOWUSAGE;
2169 /* Flush statistics */
2170 struct dundi_peer *p;
2172 AST_LIST_LOCK(&peers);
2173 AST_LIST_TRAVERSE(&peers, p, list) {
2174 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2176 free(p->lookups[x]);
2177 p->lookups[x] = NULL;
2178 p->lookuptimes[x] = 0;
2182 AST_LIST_UNLOCK(&peers);
2184 ast_db_deltree("dundi/cache", NULL);
2185 ast_cli(fd, "DUNDi Cache Flushed\n");
2187 return RESULT_SUCCESS;
2190 static int dundi_no_debug(int fd, int argc, char *argv[])
2193 return RESULT_SHOWUSAGE;
2195 ast_cli(fd, "DUNDi Debugging Disabled\n");
2196 return RESULT_SUCCESS;
2199 static int dundi_no_store_history(int fd, int argc, char *argv[])
2202 return RESULT_SHOWUSAGE;
2203 global_storehistory = 0;
2204 ast_cli(fd, "DUNDi History Storage Disabled\n");
2205 return RESULT_SUCCESS;
2208 static char *model2str(int model)
2211 case DUNDI_MODEL_INBOUND:
2213 case DUNDI_MODEL_OUTBOUND:
2215 case DUNDI_MODEL_SYMMETRIC:
2222 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2226 struct dundi_peer *p;
2231 AST_LIST_LOCK(&peers);
2233 AST_LIST_TRAVERSE(&peers, p, list) {
2234 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2235 if (!strncasecmp(word, s, len) && ++which > state)
2236 ret = ast_strdup(s);
2238 AST_LIST_UNLOCK(&peers);
2242 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
2244 return complete_peer_helper(line, word, pos, state, 3);
2247 static int rescomp(const void *a, const void *b)
2249 const struct dundi_result *resa, *resb;
2252 if (resa->weight < resb->weight)
2254 if (resa->weight > resb->weight)
2259 static void sort_results(struct dundi_result *results, int count)
2261 qsort(results, count, sizeof(results[0]), rescomp);
2264 static int dundi_do_lookup(int fd, int argc, char *argv[])
2272 struct dundi_result dr[MAX_RESULTS];
2273 struct timeval start;
2274 if ((argc < 3) || (argc > 4))
2275 return RESULT_SHOWUSAGE;
2277 if (!strcasecmp(argv[3], "bypass"))
2280 return RESULT_SHOWUSAGE;
2282 ast_copy_string(tmp, argv[2], sizeof(tmp));
2283 context = strchr(tmp, '@');
2288 start = ast_tvnow();
2289 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2292 ast_cli(fd, "DUNDi lookup returned error.\n");
2294 ast_cli(fd, "DUNDi lookup returned no results.\n");
2296 sort_results(dr, res);
2297 for (x=0;x<res;x++) {
2298 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));
2299 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2301 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2302 return RESULT_SUCCESS;
2305 static int dundi_do_precache(int fd, int argc, char *argv[])
2310 struct timeval start;
2311 if ((argc < 3) || (argc > 3))
2312 return RESULT_SHOWUSAGE;
2313 ast_copy_string(tmp, argv[2], sizeof(tmp));
2314 context = strchr(tmp, '@');
2319 start = ast_tvnow();
2320 res = dundi_precache(context, tmp);
2323 ast_cli(fd, "DUNDi precache returned error.\n");
2325 ast_cli(fd, "DUNDi precache returned no error.\n");
2326 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2327 return RESULT_SUCCESS;
2330 static int dundi_do_query(int fd, int argc, char *argv[])
2336 struct dundi_entity_info dei;
2337 if ((argc < 3) || (argc > 3))
2338 return RESULT_SHOWUSAGE;
2339 if (dundi_str_to_eid(&eid, argv[2])) {
2340 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
2341 return RESULT_SHOWUSAGE;
2343 ast_copy_string(tmp, argv[2], sizeof(tmp));
2344 context = strchr(tmp, '@');
2349 res = dundi_query_eid(&dei, context, eid);
2351 ast_cli(fd, "DUNDi Query EID returned error.\n");
2353 ast_cli(fd, "DUNDi Query EID returned no results.\n");
2355 ast_cli(fd, "DUNDi Query EID succeeded:\n");
2356 ast_cli(fd, "Department: %s\n", dei.orgunit);
2357 ast_cli(fd, "Organization: %s\n", dei.org);
2358 ast_cli(fd, "City/Locality: %s\n", dei.locality);
2359 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
2360 ast_cli(fd, "Country: %s\n", dei.country);
2361 ast_cli(fd, "E-mail: %s\n", dei.email);
2362 ast_cli(fd, "Phone: %s\n", dei.phone);
2363 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
2365 return RESULT_SUCCESS;
2368 static int dundi_show_peer(int fd, int argc, char *argv[])
2370 struct dundi_peer *peer;
2371 struct permission *p;
2373 char iabuf[INET_ADDRSTRLEN];
2378 return RESULT_SHOWUSAGE;
2379 AST_LIST_LOCK(&peers);
2380 AST_LIST_TRAVERSE(&peers, peer, list) {
2381 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
2385 switch(peer->order) {
2390 order = "Secondary";
2396 order = "Quartiary";
2401 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2402 ast_cli(fd, "Model: %s\n", model2str(peer->model));
2403 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
2404 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2405 ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
2406 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2407 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2408 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2409 if (peer->include) {
2410 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2414 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2418 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2422 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2426 for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
2427 if (peer->lookups[x]) {
2429 ast_cli(fd, "Last few query times:\n");
2430 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2435 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
2437 ast_cli(fd, "No such peer '%s'\n", argv[3]);
2438 AST_LIST_UNLOCK(&peers);
2439 return RESULT_SUCCESS;
2442 static int dundi_show_peers(int fd, int argc, char *argv[])
2444 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2445 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2446 struct dundi_peer *peer;
2447 char iabuf[INET_ADDRSTRLEN];
2448 int registeredonly=0;
2451 int online_peers = 0;
2452 int offline_peers = 0;
2453 int unmonitored_peers = 0;
2454 int total_peers = 0;
2456 if ((argc != 3) && (argc != 4) && (argc != 5))
2457 return RESULT_SHOWUSAGE;
2459 if (!strcasecmp(argv[3], "registered")) {
2462 return RESULT_SHOWUSAGE;
2464 AST_LIST_LOCK(&peers);
2465 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2466 AST_LIST_TRAVERSE(&peers, peer, list) {
2468 int print_line = -1;
2471 if (registeredonly && !peer->addr.sin_addr.s_addr)
2474 if (peer->lastms < 0) {
2475 strcpy(status, "UNREACHABLE");
2478 else if (peer->lastms > peer->maxms) {
2479 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2482 else if (peer->lastms) {
2483 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2487 strcpy(status, "UNKNOWN");
2491 strcpy(status, "Unmonitored");
2492 unmonitored_peers++;
2495 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2497 strcpy(avgms, "Unavail");
2498 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2499 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2500 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2503 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
2505 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
2507 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
2515 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2516 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
2517 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2520 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2521 AST_LIST_UNLOCK(&peers);
2522 return RESULT_SUCCESS;
2527 static int dundi_show_trans(int fd, int argc, char *argv[])
2529 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2530 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2531 struct dundi_transaction *trans;
2532 char iabuf[INET_ADDRSTRLEN];
2534 return RESULT_SHOWUSAGE;
2535 AST_LIST_LOCK(&peers);
2536 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2537 for (trans = alltrans;trans;trans = trans->allnext) {
2538 ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr),
2539 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2541 AST_LIST_UNLOCK(&peers);
2542 return RESULT_SUCCESS;
2547 static int dundi_show_entityid(int fd, int argc, char *argv[])
2551 return RESULT_SHOWUSAGE;
2552 AST_LIST_LOCK(&peers);
2553 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2554 AST_LIST_UNLOCK(&peers);
2555 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
2556 return RESULT_SUCCESS;
2559 static int dundi_show_requests(int fd, int argc, char *argv[])
2561 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2562 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2563 struct dundi_request *req;
2566 return RESULT_SHOWUSAGE;
2567 AST_LIST_LOCK(&peers);
2568 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2569 for (req = requests;req;req = req->next) {
2570 ast_cli(fd, FORMAT, req->number, req->dcontext,
2571 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2573 AST_LIST_UNLOCK(&peers);
2574 return RESULT_SUCCESS;
2579 /* Grok-a-dial DUNDi */
2581 static int dundi_show_mappings(int fd, int argc, char *argv[])
2583 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2584 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
2585 struct dundi_mapping *map;
2588 return RESULT_SHOWUSAGE;
2589 AST_LIST_LOCK(&peers);
2590 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2591 AST_LIST_TRAVERSE(&mappings, map, list) {
2592 ast_cli(fd, FORMAT, map->dcontext, map->weight,
2593 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2594 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2596 AST_LIST_UNLOCK(&peers);
2597 return RESULT_SUCCESS;
2602 static int dundi_show_precache(int fd, int argc, char *argv[])
2604 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2605 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2606 struct dundi_precache_queue *qe;
2611 return RESULT_SHOWUSAGE;
2613 ast_mutex_lock(&pclock);
2614 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
2615 for (qe = pcq;qe;qe = qe->next) {
2616 s = qe->expiration - now;
2621 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
2623 ast_mutex_unlock(&pclock);
2624 return RESULT_SUCCESS;
2629 static char debug_usage[] =
2630 "Usage: dundi debug\n"
2631 " Enables dumping of DUNDi packets for debugging purposes\n";
2633 static char no_debug_usage[] =
2634 "Usage: dundi no debug\n"
2635 " Disables dumping of DUNDi packets for debugging purposes\n";
2637 static char store_history_usage[] =
2638 "Usage: dundi store history\n"
2639 " Enables storing of DUNDi requests and times for debugging\n"
2642 static char no_store_history_usage[] =
2643 "Usage: dundi no store history\n"
2644 " Disables storing of DUNDi requests and times for debugging\n"
2647 static char show_peers_usage[] =
2648 "Usage: dundi show peers\n"
2649 " Lists all known DUNDi peers.\n";
2651 static char show_trans_usage[] =
2652 "Usage: dundi show trans\n"
2653 " Lists all known DUNDi transactions.\n";
2655 static char show_mappings_usage[] =
2656 "Usage: dundi show mappings\n"
2657 " Lists all known DUNDi mappings.\n";
2659 static char show_precache_usage[] =
2660 "Usage: dundi show precache\n"
2661 " Lists all known DUNDi scheduled precache updates.\n";
2663 static char show_entityid_usage[] =
2664 "Usage: dundi show entityid\n"
2665 " Displays the global entityid for this host.\n";
2667 static char show_peer_usage[] =
2668 "Usage: dundi show peer [peer]\n"
2669 " Provide a detailed description of a specifid DUNDi peer.\n";
2671 static char show_requests_usage[] =
2672 "Usage: dundi show requests\n"
2673 " Lists all known pending DUNDi requests.\n";
2675 static char lookup_usage[] =
2676 "Usage: dundi lookup <number>[@context] [bypass]\n"
2677 " Lookup the given number within the given DUNDi context\n"
2678 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2679 "keyword is specified.\n";
2681 static char precache_usage[] =
2682 "Usage: dundi precache <number>[@context]\n"
2683 " Lookup the given number within the given DUNDi context\n"
2684 "(or e164 if none is specified) and precaches the results to any\n"
2685 "upstream DUNDi push servers.\n";
2687 static char query_usage[] =
2688 "Usage: dundi query <entity>[@context]\n"
2689 " Attempts to retrieve contact information for a specific\n"
2690 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2691 "e164 if none is specified).\n";
2693 static char flush_usage[] =
2694 "Usage: dundi flush [stats]\n"
2695 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2696 "'stats' is present, clears timer statistics instead of normal\n"
2699 static struct ast_cli_entry cli_debug =
2700 { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
2702 static struct ast_cli_entry cli_store_history =
2703 { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
2705 static struct ast_cli_entry cli_no_store_history =
2706 { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
2708 static struct ast_cli_entry cli_flush =
2709 { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
2711 static struct ast_cli_entry cli_no_debug =
2712 { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
2714 static struct ast_cli_entry cli_show_peers =
2715 { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
2717 static struct ast_cli_entry cli_show_trans =
2718 { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
2720 static struct ast_cli_entry cli_show_entityid =
2721 { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
2723 static struct ast_cli_entry cli_show_mappings =
2724 { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
2726 static struct ast_cli_entry cli_show_precache =
2727 { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
2729 static struct ast_cli_entry cli_show_requests =
2730 { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
2732 static struct ast_cli_entry cli_show_peer =
2733 { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
2735 static struct ast_cli_entry cli_lookup =
2736 { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
2738 static struct ast_cli_entry cli_precache =
2739 { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
2741 static struct ast_cli_entry cli_queryeid =
2742 { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
2746 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2748 struct dundi_transaction *trans;
2751 /* Don't allow creation of transactions to non-registered peers */
2752 if (p && !p->addr.sin_addr.s_addr)
2754 tid = get_trans_id();
2757 trans = malloc(sizeof(struct dundi_transaction));
2759 memset(trans, 0, sizeof(struct dundi_transaction));
2760 if (global_storehistory) {
2761 trans->start = ast_tvnow();
2762 ast_set_flag(trans, FLAG_STOREHIST);
2764 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2765 trans->autokillid = -1;
2767 apply_peer(trans, p);
2768 if (!p->sentfullkey)
2769 ast_set_flag(trans, FLAG_SENDFULLKEY);
2771 trans->strans = tid;
2772 trans->allnext = alltrans;
2778 static int dundi_xmit(struct dundi_packet *pack)
2781 char iabuf[INET_ADDRSTRLEN];
2783 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2784 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2786 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2787 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2788 ntohs(pack->parent->addr.sin_port), strerror(errno));
2795 static void destroy_packet(struct dundi_packet *pack, int needfree)
2797 struct dundi_packet *prev, *cur;
2800 cur = pack->parent->packets;
2804 prev->next = cur->next;
2806 pack->parent->packets = cur->next;
2813 if (pack->retransid > -1)
2814 ast_sched_del(sched, pack->retransid);
2818 pack->retransid = -1;
2823 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2825 struct dundi_transaction *cur, *prev;
2826 struct dundi_peer *peer;
2831 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2832 AST_LIST_TRAVERSE(&peers, peer, list) {
2833 if (peer->regtrans == trans)
2834 peer->regtrans = NULL;
2835 if (peer->keypending == trans)
2836 peer->keypending = NULL;
2837 if (peer->qualtrans == trans) {
2839 if (peer->lastms > -1)
2840 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2843 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2846 if (ms < peer->maxms) {
2847 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2848 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2849 } else if (peer->lastms < peer->maxms) {
2850 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);
2854 peer->qualtrans = NULL;
2856 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2857 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2858 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2861 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2862 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2863 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2864 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2865 peer->lookups[x] = peer->lookups[x-1];
2866 if (peer->lookups[x]) {
2867 peer->avgms += peer->lookuptimes[x];
2871 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2872 peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2873 if (peer->lookups[0]) {
2874 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2875 peer->avgms += peer->lookuptimes[0];
2885 if (trans->parent) {
2886 /* Unlink from parent if appropriate */
2888 cur = trans->parent->trans;
2892 prev->next = trans->next;
2894 trans->parent->trans = trans->next;
2900 if (!trans->parent->trans) {
2901 /* Wake up sleeper */
2902 if (trans->parent->pfds[1] > -1) {
2903 write(trans->parent->pfds[1], "killa!", 6);
2907 /* Unlink from all trans */
2913 prev->allnext = trans->allnext;
2915 alltrans = trans->allnext;
2921 destroy_packets(trans->packets);
2922 destroy_packets(trans->lasttrans);
2923 trans->packets = NULL;
2924 trans->lasttrans = NULL;
2925 if (trans->autokillid > -1)
2926 ast_sched_del(sched, trans->autokillid);
2927 trans->autokillid = -1;
2928 if (trans->thread) {
2929 /* If used by a thread, mark as dead and be done */
2930 ast_set_flag(trans, FLAG_DEAD);
2935 static int dundi_rexmit(void *data)
2937 struct dundi_packet *pack;
2938 char iabuf[INET_ADDRSTRLEN];
2940 AST_LIST_LOCK(&peers);
2942 if (pack->retrans < 1) {
2943 pack->retransid = -1;
2944 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
2945 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
2946 ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
2947 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
2948 destroy_trans(pack->parent, 1);
2951 /* Decrement retransmission, try again */
2956 AST_LIST_UNLOCK(&peers);
2960 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
2962 struct dundi_packet *pack;
2966 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
2967 /* Reserve enough space for encryption */
2968 if (ast_test_flag(trans, FLAG_ENCRYPT))
2972 memset(pack, 0, len);
2973 pack->h = (struct dundi_hdr *)(pack->data);
2974 if (cmdresp != DUNDI_COMMAND_ACK) {
2975 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
2976 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
2977 pack->next = trans->packets;
2978 trans->packets = pack;
2980 pack->parent = trans;
2981 pack->h->strans = htons(trans->strans);
2982 pack->h->dtrans = htons(trans->dtrans);
2983 pack->h->iseqno = trans->iseqno;
2984 pack->h->oseqno = trans->oseqno;
2985 pack->h->cmdresp = cmdresp;
2986 pack->datalen = sizeof(struct dundi_hdr);
2988 memcpy(pack->h->ies, ied->buf, ied->pos);
2989 pack->datalen += ied->pos;
2992 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
2993 ast_set_flag(trans, FLAG_FINAL);
2995 pack->h->cmdflags = flags;
2996 if (cmdresp != DUNDI_COMMAND_ACK) {
2998 trans->oseqno = trans->oseqno % 256;
3000 trans->aseqno = trans->iseqno;
3001 /* If we have their public key, encrypt */
3002 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3004 case DUNDI_COMMAND_REGREQ:
3005 case DUNDI_COMMAND_REGRESPONSE:
3006 case DUNDI_COMMAND_DPDISCOVER:
3007 case DUNDI_COMMAND_DPRESPONSE:
3008 case DUNDI_COMMAND_EIDQUERY:
3009 case DUNDI_COMMAND_EIDRESPONSE:
3010 case DUNDI_COMMAND_PRECACHERQ:
3011 case DUNDI_COMMAND_PRECACHERP:
3013 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3014 res = dundi_encrypt(trans, pack);
3022 res = dundi_xmit(pack);
3024 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3026 if (cmdresp == DUNDI_COMMAND_ACK)
3033 static int do_autokill(void *data)
3035 struct dundi_transaction *trans = data;
3037 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3038 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3039 trans->autokillid = -1;
3040 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3044 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3046 struct dundi_peer *p;
3047 if (!dundi_eid_cmp(eid, us)) {
3048 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3051 AST_LIST_LOCK(&peers);
3052 AST_LIST_TRAVERSE(&peers, p, list) {
3053 if (!dundi_eid_cmp(&p->eid, eid)) {
3054 if (has_permission(p->include, context))
3055 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3057 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3062 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3063 AST_LIST_UNLOCK(&peers);
3066 static int dundi_discover(struct dundi_transaction *trans)
3068 struct dundi_ie_data ied;
3070 if (!trans->parent) {
3071 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3074 memset(&ied, 0, sizeof(ied));
3075 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3076 if (!dundi_eid_zero(&trans->us_eid))
3077 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3078 for (x=0;x<trans->eidcount;x++)
3079 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3080 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3081 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3082 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3083 if (trans->parent->cbypass)
3084 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3085 if (trans->autokilltimeout)
3086 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3087 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3090 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3092 struct dundi_ie_data ied;
3095 int expiration = dundi_cache_time;
3097 dundi_eid *avoid[1] = { NULL, };
3098 int direct[1] = { 0, };
3099 struct dundi_result dr[MAX_RESULTS];
3100 struct dundi_hint_metadata hmd;
3101 if (!trans->parent) {
3102 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3105 memset(&hmd, 0, sizeof(hmd));
3106 memset(&dr, 0, sizeof(dr));
3107 /* Look up the answers we're going to include */
3108 for (x=0;x<mapcount;x++)
3109 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3112 for (x=0;x<ouranswers;x++) {
3113 if (dr[x].weight < max)
3117 /* If we do not have a canonical result, keep looking */
3118 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);
3120 /* Append answer in result */
3125 if (ouranswers > 0) {
3126 *foundanswers += ouranswers;
3127 memset(&ied, 0, sizeof(ied));
3128 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3129 if (!dundi_eid_zero(&trans->us_eid))
3130 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3131 for (x=0;x<trans->eidcount;x++)
3132 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3133 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3134 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3135 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3136 for (x=0;x<ouranswers;x++) {
3138 if (dr[x].expiration && (expiration > dr[x].expiration))
3139 expiration = dr[x].expiration;
3140 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3142 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3143 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3144 if (trans->autokilltimeout)
3145 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3146 if (expiration < *minexp)
3147 *minexp = expiration;
3148 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3150 /* Oops, nothing to send... */
3151 destroy_trans(trans, 0);
3156 static int dundi_query(struct dundi_transaction *trans)
3158 struct dundi_ie_data ied;
3160 if (!trans->parent) {
3161 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3164 memset(&ied, 0, sizeof(ied));
3165 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3166 if (!dundi_eid_zero(&trans->us_eid))
3167 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3168 for (x=0;x<trans->eidcount;x++)
3169 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3170 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3171 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3172 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3173 if (trans->autokilltimeout)
3174 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3175 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3178 static int discover_transactions(struct dundi_request *dr)
3180 struct dundi_transaction *trans;
3181 AST_LIST_LOCK(&peers);
3184 dundi_discover(trans);
3185 trans = trans->next;
3187 AST_LIST_UNLOCK(&peers);
3191 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3193 struct dundi_transaction *trans, *transn;
3194 /* Mark all as "in thread" so they don't disappear */
3195 AST_LIST_LOCK(&peers);
3199 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3201 trans = trans->next;
3203 AST_LIST_UNLOCK(&peers);
3207 if (!ast_test_flag(trans, FLAG_DEAD))
3208 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3209 trans = trans->next;
3212 /* Cleanup any that got destroyed in the mean time */
3213 AST_LIST_LOCK(&peers);
3216 transn = trans->next;
3218 if (ast_test_flag(trans, FLAG_DEAD)) {
3219 ast_log(LOG_DEBUG, "Our transaction went away!\n");
3220 destroy_trans(trans, 0);
3224 AST_LIST_UNLOCK(&peers);
3228 static int query_transactions(struct dundi_request *dr)
3230 struct dundi_transaction *trans;
3231 AST_LIST_LOCK(&peers);
3235 trans = trans->next;
3237 AST_LIST_UNLOCK(&peers);
3241 static int optimize_transactions(struct dundi_request *dr, int order)
3243 /* Minimize the message propagation through DUNDi by
3244 alerting the network to hops which should be not be considered */
3245 struct dundi_transaction *trans;
3246 struct dundi_peer *peer;
3250 AST_LIST_LOCK(&peers);
3253 /* Pop off the true root */
3254 if (trans->eidcount) {
3255 tmp = trans->eids[--trans->eidcount];
3258 tmp = trans->us_eid;
3262 AST_LIST_TRAVERSE(&peers, peer, list) {
3263 if (has_permission(peer->include, dr->dcontext) &&
3264 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3265 (peer->order <= order)) {
3266 /* For each other transaction, make sure we don't
3267 ask this EID about the others if they're not
3268 already in the list */
3269 if (!dundi_eid_cmp(&tmp, &peer->eid))
3272 for (x=0;x<trans->eidcount;x++) {
3273 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
3277 if (x == trans->eidcount) {
3278 /* Nope not in the list, if needed, add us at the end since we're the source */
3279 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3280 trans->eids[trans->eidcount++] = peer->eid;
3281 /* Need to insert the real root (or us) at the bottom now as
3282 a requirement now. */
3288 /* If necessary, push the true root back on the end */
3290 trans->eids[trans->eidcount++] = tmp;
3291 trans = trans->next;
3293 AST_LIST_UNLOCK(&peers);
3297 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3299 struct dundi_transaction *trans;
3303 /* Ignore if not registered */
3304 if (!p->addr.sin_addr.s_addr)
3306 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3308 if (ast_strlen_zero(dr->number))
3309 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);
3311 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);
3312 trans = create_transaction(p);
3315 trans->next = dr->trans;
3318 for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
3319 trans->eids[x] = *avoid[x];
3320 trans->eidcount = x;
3325 static void cancel_request(struct dundi_request *dr)
3327 struct dundi_transaction *trans, *next;
3329 AST_LIST_LOCK(&peers);
3334 /* Orphan transaction from request */
3335 trans->parent = NULL;
3337 /* Send final cancel */
3338 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);