2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Distributed Universal Number Discovery (DUNDi)
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33 #include "asterisk/network.h"
35 #include <sys/ioctl.h>
37 #include <sys/signal.h>
40 #include "asterisk/file.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/config.h"
44 #include "asterisk/options.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/frame.h"
48 #include "asterisk/cli.h"
49 #include "asterisk/lock.h"
50 #include "asterisk/md5.h"
51 #include "asterisk/dundi.h"
52 #include "asterisk/sched.h"
53 #include "asterisk/io.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/netsock.h"
56 #include "asterisk/crypto.h"
57 #include "asterisk/astdb.h"
58 #include "asterisk/acl.h"
59 #include "asterisk/aes.h"
60 #include "asterisk/app.h"
62 #include "dundi-parser.h"
64 #define MAX_RESULTS 64
66 #define MAX_PACKET_SIZE 8192
68 #define MAX_WEIGHT 59999
70 #define DUNDI_MODEL_INBOUND (1 << 0)
71 #define DUNDI_MODEL_OUTBOUND (1 << 1)
72 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
74 /*! Keep times of last 10 lookups */
75 #define DUNDI_TIMING_HISTORY 10
78 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
79 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
80 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
81 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
82 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */
83 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
84 FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
87 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
90 #define DUNDI_SECRET_TIME 15 /* Testing only */
92 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
95 static struct io_context *io;
96 static struct sched_context *sched;
97 static int netsocket = -1;
98 static pthread_t netthreadid = AST_PTHREADT_NULL;
99 static pthread_t precachethreadid = AST_PTHREADT_NULL;
100 static unsigned int tos = 0;
101 static int dundidebug = 0;
102 static int authdebug = 0;
103 static int dundi_ttl = DUNDI_DEFAULT_TTL;
104 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
105 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
106 static int global_autokilltimeout = 0;
107 static dundi_eid global_eid;
108 static int default_expiration = 60;
109 static int global_storehistory = 0;
110 static char dept[80];
112 static char locality[80];
113 static char stateprov[80];
114 static char country[80];
115 static char email[80];
116 static char phone[80];
117 static char secretpath[80];
118 static char cursecret[80];
119 static char ipaddr[80];
120 static time_t rotatetime;
121 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
122 static int dundi_shutdown = 0;
125 AST_LIST_ENTRY(permission) list;
130 struct dundi_packet {
131 AST_LIST_ENTRY(dundi_packet) list;
134 struct dundi_transaction *parent;
137 unsigned char data[0];
140 struct dundi_hint_metadata {
141 unsigned short flags;
142 char exten[AST_MAX_EXTENSION];
145 struct dundi_precache_queue {
146 AST_LIST_ENTRY(dundi_precache_queue) list;
152 struct dundi_request;
154 struct dundi_transaction {
155 struct sockaddr_in addr; /*!< Other end of transaction */
156 struct timeval start; /*!< When this transaction was created */
157 dundi_eid eids[DUNDI_MAX_STACK + 1];
158 int eidcount; /*!< Number of eids in eids */
159 dundi_eid us_eid; /*!< Our EID, to them */
160 dundi_eid them_eid; /*!< Their EID, to us */
161 ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
162 ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
163 unsigned int flags; /*!< Has final packet been sent */
164 int ttl; /*!< Remaining TTL for queries on this one */
165 int thread; /*!< We have a calling thread */
166 int retranstimer; /*!< How long to wait before retransmissions */
167 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
168 int autokilltimeout; /*!< Recommended timeout for autokill */
169 unsigned short strans; /*!< Our transaction identifier */
170 unsigned short dtrans; /*!< Their transaction identifer */
171 unsigned char iseqno; /*!< Next expected received seqno */
172 unsigned char oiseqno; /*!< Last received incoming seqno */
173 unsigned char oseqno; /*!< Next transmitted seqno */
174 unsigned char aseqno; /*!< Last acknowledge seqno */
175 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
176 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
177 struct dundi_request *parent; /*!< Parent request (if there is one) */
178 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
179 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
182 struct dundi_request {
183 char dcontext[AST_MAX_EXTENSION];
184 char number[AST_MAX_EXTENSION];
187 struct dundi_result *dr;
188 struct dundi_entity_info *dei;
189 struct dundi_hint_metadata *hmd;
195 unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */
196 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
197 AST_LIST_ENTRY(dundi_request) list;
200 struct dundi_mapping {
201 char dcontext[AST_MAX_EXTENSION];
202 char lcontext[AST_MAX_EXTENSION];
208 char dest[AST_MAX_EXTENSION];
209 AST_LIST_ENTRY(dundi_mapping) list;
214 struct sockaddr_in addr; /*!< Address of DUNDi peer */
215 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
216 struct permissionlist include;
225 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
226 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
227 unsigned long us_keycrc32; /*!< CRC-32 of our key */
228 ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
229 ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
230 unsigned long them_keycrc32; /*!< CRC-32 of our key */
231 ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
232 ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
233 time_t keyexpire; /*!< When to expire/recreate key */
235 int lookuptimes[DUNDI_TIMING_HISTORY];
236 char *lookups[DUNDI_TIMING_HISTORY];
238 struct dundi_transaction *regtrans; /*!< Registration transaction */
239 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
240 int model; /*!< Pull model */
241 int pcmodel; /*!< Push/precache model */
242 /*! Dynamic peers register with us */
243 unsigned int dynamic:1;
244 int lastms; /*!< Last measured latency */
245 int maxms; /*!< Max permissible latency */
246 struct timeval qualtx; /*!< Time of transmit */
247 AST_LIST_ENTRY(dundi_peer) list;
250 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
251 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
252 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
253 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
254 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
257 * \brief Wildcard peer
259 * This peer is created if the [*] entry is specified in dundi.conf
261 static struct dundi_peer *any_peer;
263 static int dundi_xmit(struct dundi_packet *pack);
265 static void dundi_debug_output(const char *data)
268 ast_verbose("%s", data);
271 static void dundi_error_output(const char *data)
273 ast_log(LOG_WARNING, "%s", data);
276 static int has_permission(struct permissionlist *permlist, char *cont)
278 struct permission *perm;
281 AST_LIST_TRAVERSE(permlist, perm, list) {
282 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
289 static char *tech2str(int tech)
292 case DUNDI_PROTO_NONE:
294 case DUNDI_PROTO_IAX:
296 case DUNDI_PROTO_SIP:
298 case DUNDI_PROTO_H323:
305 static int str2tech(char *str)
307 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
308 return DUNDI_PROTO_IAX;
309 else if (!strcasecmp(str, "SIP"))
310 return DUNDI_PROTO_SIP;
311 else if (!strcasecmp(str, "H323"))
312 return DUNDI_PROTO_H323;
317 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[]);
318 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
319 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
320 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
322 struct dundi_transaction *trans;
324 /* Look for an exact match first */
325 AST_LIST_TRAVERSE(&alltrans, trans, all) {
326 if (!inaddrcmp(&trans->addr, sin) &&
327 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
328 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
330 trans->dtrans = ntohs(hdr->strans) & 32767;
335 switch(hdr->cmdresp & 0x7f) {
336 case DUNDI_COMMAND_DPDISCOVER:
337 case DUNDI_COMMAND_EIDQUERY:
338 case DUNDI_COMMAND_PRECACHERQ:
339 case DUNDI_COMMAND_REGREQ:
340 case DUNDI_COMMAND_NULL:
341 case DUNDI_COMMAND_ENCRYPT:
344 /* Create new transaction */
345 if (!(trans = create_transaction(NULL)))
347 memcpy(&trans->addr, sin, sizeof(trans->addr));
348 trans->dtrans = ntohs(hdr->strans) & 32767;
356 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
358 static int dundi_ack(struct dundi_transaction *trans, int final)
360 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
362 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
365 struct dundi_packet pack;
366 struct dundi_hdr hdr;
368 struct dundi_transaction trans;
369 /* Never respond to an INVALID with another INVALID */
370 if (h->cmdresp == DUNDI_COMMAND_INVALID)
372 memset(&tmp, 0, sizeof(tmp));
373 memset(&trans, 0, sizeof(trans));
374 memcpy(&trans.addr, sin, sizeof(trans.addr));
375 tmp.hdr.strans = h->dtrans;
376 tmp.hdr.dtrans = h->strans;
377 tmp.hdr.iseqno = h->oseqno;
378 tmp.hdr.oseqno = h->iseqno;
379 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
380 tmp.hdr.cmdflags = 0;
381 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
382 tmp.pack.datalen = sizeof(struct dundi_hdr);
383 tmp.pack.parent = &trans;
384 dundi_xmit(&tmp.pack);
387 static void reset_global_eid(void)
389 #if defined(SIOCGIFHWADDR)
394 s = socket(AF_INET, SOCK_STREAM, 0);
397 for (x = 0; x < 10; x++) {
398 memset(&ifr, 0, sizeof(ifr));
399 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
400 if (ioctl(s, SIOCGIFHWADDR, &ifr))
402 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
404 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n",
405 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
412 #if defined(ifa_broadaddr) && !defined(SOLARIS)
414 struct ifaddrs *ifap;
416 if (getifaddrs(&ifap) == 0) {
418 for (p = ifap; p; p = p->ifa_next) {
419 if (p->ifa_addr->sa_family == AF_LINK) {
420 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
423 sdp->sdl_data + sdp->sdl_nlen, 6);
425 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);
434 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
437 static int get_trans_id(void)
439 struct dundi_transaction *t;
440 int stid = (ast_random() % 32766) + 1;
444 AST_LIST_TRAVERSE(&alltrans, t, all) {
445 if (t->strans == tid)
450 tid = (tid % 32766) + 1;
451 } while (tid != stid);
456 static int reset_transaction(struct dundi_transaction *trans)
459 tid = get_trans_id();
468 ast_clear_flag(trans, FLAG_FINAL);
472 static struct dundi_peer *find_peer(dundi_eid *eid)
474 struct dundi_peer *cur = NULL;
479 AST_LIST_TRAVERSE(&peers, cur, list) {
480 if (!dundi_eid_cmp(&cur->eid,eid))
484 if (!cur && any_peer)
490 static void build_iv(unsigned char *iv)
492 /* XXX Would be nice to be more random XXX */
493 unsigned int *fluffy;
495 fluffy = (unsigned int *)(iv);
497 fluffy[x] = ast_random();
500 struct dundi_query_state {
501 dundi_eid *eids[DUNDI_MAX_STACK + 1];
502 int directs[DUNDI_MAX_STACK + 1];
504 char called_context[AST_MAX_EXTENSION];
505 char called_number[AST_MAX_EXTENSION];
506 struct dundi_mapping *maps;
509 struct dundi_transaction *trans;
516 static int get_mapping_weight(struct dundi_mapping *map)
521 if (map->weightstr) {
522 pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
523 if (sscanf(buf, "%d", &map->_weight) != 1)
524 map->_weight = MAX_WEIGHT;
530 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)
532 struct ast_flags flags = {0};
534 if (!ast_strlen_zero(map->lcontext)) {
535 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
536 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
537 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
538 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
539 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
540 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
541 if (ast_ignore_pattern(map->lcontext, called_number))
542 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
544 /* Clearly we can't say 'don't ask' anymore if we found anything... */
545 if (ast_test_flag(&flags, AST_FLAGS_ALL))
546 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
548 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
549 /* Skip partial answers */
550 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
552 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
553 struct varshead headp;
554 struct ast_var_t *newvariable;
555 ast_set_flag(&flags, map->options & 0xffff);
556 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
557 dr[anscnt].techint = map->tech;
558 dr[anscnt].weight = get_mapping_weight(map);
559 dr[anscnt].expiration = dundi_cache_time;
560 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
561 dr[anscnt].eid = *us_eid;
562 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
563 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
564 AST_LIST_HEAD_INIT_NOLOCK(&headp);
565 newvariable = ast_var_assign("NUMBER", called_number);
566 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
567 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
568 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
569 newvariable = ast_var_assign("SECRET", cursecret);
570 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
571 newvariable = ast_var_assign("IPADDR", ipaddr);
572 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
573 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
574 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
575 ast_var_delete(newvariable);
577 dr[anscnt].dest[0] = '\0';
580 /* No answers... Find the fewest number of digits from the
581 number for which we have no answer. */
582 char tmp[AST_MAX_EXTENSION + 1] = "";
583 for (x = 0; x < (sizeof(tmp) - 1); x++) {
584 tmp[x] = called_number[x];
587 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
588 /* Oops found something we can't match. If this is longer
589 than the running hint, we have to consider it */
590 if (strlen(tmp) > strlen(hmd->exten)) {
591 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
601 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
603 static void *dundi_lookup_thread(void *data)
605 struct dundi_query_state *st = data;
606 struct dundi_result dr[MAX_RESULTS];
607 struct dundi_ie_data ied;
608 struct dundi_hint_metadata hmd;
613 int expiration = dundi_cache_time;
616 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
617 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
618 memset(&ied, 0, sizeof(ied));
619 memset(&dr, 0, sizeof(dr));
620 memset(&hmd, 0, sizeof(hmd));
621 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
622 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
623 for (x=0;x<st->nummaps;x++)
624 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
627 for (x=0;x<ouranswers;x++) {
628 if (dr[x].weight < max)
633 /* If we do not have a canonical result, keep looking */
634 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs);
636 /* Append answer in result */
639 if ((res < -1) && (!ouranswers))
640 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
643 AST_LIST_LOCK(&peers);
644 /* Truncate if "don't ask" isn't present */
645 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
647 if (ast_test_flag(st->trans, FLAG_DEAD)) {
649 ast_log(LOG_DEBUG, "Our transaction went away!\n");
650 st->trans->thread = 0;
651 destroy_trans(st->trans, 0);
653 for (x=0;x<ouranswers;x++) {
655 if (dr[x].expiration && (expiration > dr[x].expiration))
656 expiration = dr[x].expiration;
657 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
659 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
660 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
661 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
662 st->trans->thread = 0;
664 AST_LIST_UNLOCK(&peers);
669 static void *dundi_precache_thread(void *data)
671 struct dundi_query_state *st = data;
672 struct dundi_ie_data ied;
673 struct dundi_hint_metadata hmd;
677 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
678 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
679 memset(&ied, 0, sizeof(ied));
681 /* Now produce precache */
682 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
684 AST_LIST_LOCK(&peers);
685 /* Truncate if "don't ask" isn't present */
686 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
688 if (ast_test_flag(st->trans, FLAG_DEAD)) {
690 ast_log(LOG_DEBUG, "Our transaction went away!\n");
691 st->trans->thread = 0;
692 destroy_trans(st->trans, 0);
694 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
695 st->trans->thread = 0;
697 AST_LIST_UNLOCK(&peers);
702 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[]);
704 static void *dundi_query_thread(void *data)
706 struct dundi_query_state *st = data;
707 struct dundi_entity_info dei;
708 struct dundi_ie_data ied;
709 struct dundi_hint_metadata hmd;
714 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
715 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
716 memset(&ied, 0, sizeof(ied));
717 memset(&dei, 0, sizeof(dei));
718 memset(&hmd, 0, sizeof(hmd));
719 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
722 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
723 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
724 ast_copy_string(dei.org, org, sizeof(dei.org));
725 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
726 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
727 ast_copy_string(dei.country, country, sizeof(dei.country));
728 ast_copy_string(dei.email, email, sizeof(dei.email));
729 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
732 /* If we do not have a canonical result, keep looking */
733 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
735 AST_LIST_LOCK(&peers);
736 if (ast_test_flag(st->trans, FLAG_DEAD)) {
738 ast_log(LOG_DEBUG, "Our transaction went away!\n");
739 st->trans->thread = 0;
740 destroy_trans(st->trans, 0);
743 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
744 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
745 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
746 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
747 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
748 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
749 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
750 if (!ast_strlen_zero(dei.ipaddr))
751 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
753 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
754 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
755 st->trans->thread = 0;
757 AST_LIST_UNLOCK(&peers);
762 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
764 struct dundi_query_state *st;
770 pthread_t lookupthread;
772 if (ies->eidcount > 1) {
773 /* Since it is a requirement that the first EID is the authenticating host
774 and the last EID is the root, it is permissible that the first and last EID
775 could be the same. In that case, we should go ahead copy only the "root" section
776 since we will not need it for authentication. */
777 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
780 totallen = sizeof(struct dundi_query_state);
781 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
782 st = ast_calloc(1, totallen);
784 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
785 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
787 st->ttl = ies->ttl - 1;
791 for (x=skipfirst;ies->eids[x];x++) {
792 st->eids[x-skipfirst] = (dundi_eid *)s;
793 *st->eids[x-skipfirst] = *ies->eids[x];
794 s += sizeof(dundi_eid);
797 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);
800 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
801 struct dundi_ie_data ied = { 0, };
803 ast_log(LOG_WARNING, "Unable to create thread!\n");
805 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
806 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
810 struct dundi_ie_data ied = { 0, };
811 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
812 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
818 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
823 char eidpeer_str[20];
824 char eidroot_str[20];
829 expiration = dundi_cache_time;
831 /* Only cache hint if "don't ask" is there... */
832 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
835 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
837 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
838 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
839 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
840 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
843 timeout += expiration;
844 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
846 ast_db_put("dundi/cache", key1, data);
848 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
849 ast_db_put("dundi/cache", key2, data);
851 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
855 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
861 char eidpeer_str[20];
862 char eidroot_str[20];
866 expiration = dundi_cache_time;
868 /* Keep pushes a little longer, cut pulls a little short */
875 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
876 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
877 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
878 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
879 /* Build request string */
881 timeout += expiration;
882 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
883 for (x=start;x<req->respcount;x++) {
884 /* Skip anything with an illegal pipe in it */
885 if (strchr(req->dr[x].dest, '|'))
887 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
888 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
889 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
891 ast_db_put("dundi/cache", key1, data);
892 ast_db_put("dundi/cache", key2, data);
896 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
898 struct dundi_query_state *st;
901 struct dundi_ie_data ied;
903 struct dundi_result dr2[MAX_RESULTS];
904 struct dundi_request dr;
905 struct dundi_hint_metadata hmd;
907 struct dundi_mapping *cur;
911 pthread_t lookupthread;
913 memset(&dr2, 0, sizeof(dr2));
914 memset(&dr, 0, sizeof(dr));
915 memset(&hmd, 0, sizeof(hmd));
917 /* Forge request structure to hold answers for cache */
918 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
920 dr.maxcount = MAX_RESULTS;
921 dr.expiration = dundi_cache_time;
923 dr.pfds[0] = dr.pfds[1] = -1;
925 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
926 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
928 for (x=0;x<ies->anscount;x++) {
929 if (trans->parent->respcount < trans->parent->maxcount) {
930 /* Make sure it's not already there */
931 for (z=0;z<trans->parent->respcount;z++) {
932 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
933 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
936 if (z == trans->parent->respcount) {
937 /* Copy into parent responses */
938 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
939 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
940 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
941 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
942 if (ies->expiration > 0)
943 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
945 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
946 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
947 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
948 &ies->answers[x]->eid);
949 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
950 sizeof(trans->parent->dr[trans->parent->respcount].dest));
951 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
952 sizeof(trans->parent->dr[trans->parent->respcount].tech));
953 trans->parent->respcount++;
954 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
955 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
956 /* Update weight if appropriate */
957 trans->parent->dr[z].weight = ies->answers[x]->weight;
960 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
961 trans->parent->number, trans->parent->dcontext);
964 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
965 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
967 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
969 totallen = sizeof(struct dundi_query_state);
970 /* Count matching map entries */
972 AST_LIST_TRAVERSE(&mappings, cur, list) {
973 if (!strcasecmp(cur->dcontext, ccontext))
977 /* If no maps, return -1 immediately */
981 if (ies->eidcount > 1) {
982 /* Since it is a requirement that the first EID is the authenticating host
983 and the last EID is the root, it is permissible that the first and last EID
984 could be the same. In that case, we should go ahead copy only the "root" section
985 since we will not need it for authentication. */
986 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
990 /* Prepare to run a query and then propagate that as necessary */
991 totallen += mapcount * sizeof(struct dundi_mapping);
992 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
993 st = ast_calloc(1, totallen);
995 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
996 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
998 st->ttl = ies->ttl - 1;
999 st->nocache = ies->cbypass;
1003 for (x=skipfirst;ies->eids[x];x++) {
1004 st->eids[x-skipfirst] = (dundi_eid *)s;
1005 *st->eids[x-skipfirst] = *ies->eids[x];
1006 st->directs[x-skipfirst] = ies->eid_direct[x];
1007 s += sizeof(dundi_eid);
1009 /* Append mappings */
1011 st->maps = (struct dundi_mapping *)s;
1012 AST_LIST_TRAVERSE(&mappings, cur, list) {
1013 if (!strcasecmp(cur->dcontext, ccontext)) {
1016 st->maps[x].list.next = NULL;
1021 st->nummaps = mapcount;
1023 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1025 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1027 ast_log(LOG_WARNING, "Unable to create thread!\n");
1029 memset(&ied, 0, sizeof(ied));
1030 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1031 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1035 ast_log(LOG_WARNING, "Out of memory!\n");
1036 memset(&ied, 0, sizeof(ied));
1037 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1038 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1044 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1046 struct dundi_query_state *st;
1049 struct dundi_ie_data ied;
1051 struct dundi_mapping *cur;
1055 pthread_t lookupthread;
1056 totallen = sizeof(struct dundi_query_state);
1057 /* Count matching map entries */
1058 AST_LIST_TRAVERSE(&mappings, cur, list) {
1059 if (!strcasecmp(cur->dcontext, ccontext))
1062 /* If no maps, return -1 immediately */
1066 if (ies->eidcount > 1) {
1067 /* Since it is a requirement that the first EID is the authenticating host
1068 and the last EID is the root, it is permissible that the first and last EID
1069 could be the same. In that case, we should go ahead copy only the "root" section
1070 since we will not need it for authentication. */
1071 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1075 totallen += mapcount * sizeof(struct dundi_mapping);
1076 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1077 st = ast_calloc(1, totallen);
1079 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1080 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1082 st->ttl = ies->ttl - 1;
1083 st->nocache = ies->cbypass;
1087 for (x=skipfirst;ies->eids[x];x++) {
1088 st->eids[x-skipfirst] = (dundi_eid *)s;
1089 *st->eids[x-skipfirst] = *ies->eids[x];
1090 st->directs[x-skipfirst] = ies->eid_direct[x];
1091 s += sizeof(dundi_eid);
1093 /* Append mappings */
1095 st->maps = (struct dundi_mapping *)s;
1096 AST_LIST_TRAVERSE(&mappings, cur, list) {
1097 if (!strcasecmp(cur->dcontext, ccontext)) {
1100 st->maps[x].list.next = NULL;
1105 st->nummaps = mapcount;
1107 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1109 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1111 ast_log(LOG_WARNING, "Unable to create thread!\n");
1113 memset(&ied, 0, sizeof(ied));
1114 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1115 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1119 ast_log(LOG_WARNING, "Out of memory!\n");
1120 memset(&ied, 0, sizeof(ied));
1121 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1122 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1128 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1131 char *ptr, *term, *src;
1133 struct ast_flags flags;
1139 /* Build request string */
1140 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1143 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1144 int expiration = timeout - now;
1145 if (expiration > 0) {
1147 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1149 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1151 term = strchr(ptr, '|');
1154 src = strrchr(ptr, '/');
1161 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1162 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1163 /* Make sure it's not already there */
1164 for (z=0;z<req->respcount;z++) {
1165 if ((req->dr[z].techint == tech) &&
1166 !strcmp(req->dr[z].dest, ptr))
1169 if (z == req->respcount) {
1170 /* Copy into parent responses */
1171 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1172 req->dr[req->respcount].weight = weight;
1173 req->dr[req->respcount].techint = tech;
1174 req->dr[req->respcount].expiration = expiration;
1175 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1176 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1177 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1178 ast_copy_string(req->dr[req->respcount].dest, ptr,
1179 sizeof(req->dr[req->respcount].dest));
1180 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1181 sizeof(req->dr[req->respcount].tech));
1183 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1184 } else if (req->dr[z].weight > weight)
1185 req->dr[z].weight = weight;
1189 /* We found *something* cached */
1190 if (expiration < *lowexpiration)
1191 *lowexpiration = expiration;
1194 ast_db_del("dundi/cache", key);
1196 ast_db_del("dundi/cache", key);
1202 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1206 char eidroot_str[20];
1210 char eid_str_full[20];
1215 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1216 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1217 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1218 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1219 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1220 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1221 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1222 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1223 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1225 if (!req->respcount) {
1227 /* Look and see if we have a hint that would preclude us from looking at this
1228 peer for this number. */
1229 if (!(tmp[x] = req->number[x]))
1232 /* Check for hints */
1233 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1234 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1235 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1236 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1237 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1238 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1240 if (strlen(tmp) > strlen(req->hmd->exten)) {
1241 /* Update meta data if appropriate */
1242 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1252 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1254 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1256 if (!trans->addr.sin_addr.s_addr)
1257 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1258 trans->us_eid = p->us_eid;
1259 trans->them_eid = p->eid;
1260 /* Enable encryption if appropriate */
1261 if (!ast_strlen_zero(p->inkey))
1262 ast_set_flag(trans, FLAG_ENCRYPT);
1264 trans->autokilltimeout = p->maxms;
1265 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1266 if (p->lastms > 1) {
1267 trans->retranstimer = p->lastms * 2;
1268 /* Keep it from being silly */
1269 if (trans->retranstimer < 150)
1270 trans->retranstimer = 150;
1272 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1273 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1275 trans->autokilltimeout = global_autokilltimeout;
1278 /*! \note Called with the peers list already locked */
1279 static int do_register_expire(const void *data)
1281 struct dundi_peer *peer = (struct dundi_peer *)data;
1284 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1285 peer->registerexpire = -1;
1287 memset(&peer->addr, 0, sizeof(peer->addr));
1291 static int update_key(struct dundi_peer *peer)
1293 unsigned char key[16];
1294 struct ast_key *ekey, *skey;
1297 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1299 ast_aes_encrypt_key(key, &peer->us_ecx);
1300 ast_aes_decrypt_key(key, &peer->us_dcx);
1301 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1303 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1304 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1307 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1309 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1310 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1313 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1314 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1317 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1318 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1321 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1322 peer->sentfullkey = 0;
1324 time(&peer->keyexpire);
1325 peer->keyexpire += dundi_key_ttl;
1330 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1332 unsigned char curblock[16];
1334 memcpy(curblock, iv, sizeof(curblock));
1337 curblock[x] ^= src[x];
1338 ast_aes_encrypt(curblock, dst, ecx);
1339 memcpy(curblock, dst, sizeof(curblock));
1346 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1348 unsigned char lastblock[16];
1350 memcpy(lastblock, iv, sizeof(lastblock));
1352 ast_aes_decrypt(src, dst, dcx);
1354 dst[x] ^= lastblock[x];
1355 memcpy(lastblock, src, sizeof(lastblock));
1363 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)
1365 int space = *dstlen;
1366 unsigned long bytes;
1367 struct dundi_hdr *h;
1368 unsigned char *decrypt_space;
1369 decrypt_space = alloca(srclen);
1372 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1374 h = (struct dundi_hdr *)dst;
1377 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1379 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1383 *dstlen = bytes + 6;
1384 /* Return new header */
1388 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1390 unsigned char *compress_space;
1393 unsigned long bytes;
1394 struct dundi_ie_data ied;
1395 struct dundi_peer *peer;
1396 unsigned char iv[16];
1397 len = pack->datalen + pack->datalen / 100 + 42;
1398 compress_space = alloca(len);
1399 if (compress_space) {
1400 memset(compress_space, 0, len);
1401 /* We care about everthing save the first 6 bytes of header */
1403 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1406 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1409 memset(&ied, 0, sizeof(ied));
1410 /* Say who we are */
1411 if (!pack->h->iseqno && !pack->h->oseqno) {
1412 /* Need the key in the first copy */
1413 if (!(peer = find_peer(&trans->them_eid)))
1415 if (update_key(peer))
1417 if (!peer->sentfullkey)
1418 ast_set_flag(trans, FLAG_SENDFULLKEY);
1419 /* Append key data */
1420 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1421 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1422 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1423 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1425 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1427 /* Setup contexts */
1428 trans->ecx = peer->us_ecx;
1429 trans->dcx = peer->us_dcx;
1431 /* We've sent the full key */
1432 peer->sentfullkey = 1;
1434 /* Build initialization vector */
1436 /* Add the field, rounded up to 16 bytes */
1437 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1439 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1440 ast_log(LOG_NOTICE, "Final packet too large!\n");
1443 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1444 ied.pos += ((bytes + 15) / 16) * 16;
1445 /* Reconstruct header */
1446 pack->datalen = sizeof(struct dundi_hdr);
1447 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1448 pack->h->cmdflags = 0;
1449 memcpy(pack->h->ies, ied.buf, ied.pos);
1450 pack->datalen += ied.pos;
1456 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1458 unsigned char dst[128];
1460 struct ast_key *key, *skey;
1463 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1464 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1467 } else if (!newkey || !newsig)
1469 if (!memcmp(peer->rxenckey, newkey, 128) &&
1470 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1471 /* By definition, a match */
1475 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1477 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1478 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1482 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1484 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1485 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1489 /* First check signature */
1490 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1494 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1497 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1500 /* Decrypted, passes signature */
1502 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1503 memcpy(peer->rxenckey, newkey, 128);
1504 memcpy(peer->rxenckey + 128, newsig, 128);
1505 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1506 ast_aes_decrypt_key(dst, &peer->them_dcx);
1507 ast_aes_encrypt_key(dst, &peer->them_ecx);
1511 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1513 struct permission *cur, *perm;
1515 memcpy(peer_dst, peer_src, sizeof(*peer_dst));
1517 memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1518 memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1520 AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1521 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1524 perm->allow = cur->allow;
1525 strcpy(perm->name, cur->name);
1527 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1530 AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1531 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1534 perm->allow = cur->allow;
1535 strcpy(perm->name, cur->name);
1537 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1541 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1543 /* Handle canonical command / response */
1544 int final = hdr->cmdresp & 0x80;
1545 int cmd = hdr->cmdresp & 0x7f;
1550 unsigned char *bufcpy;
1551 struct dundi_ie_data ied;
1552 struct dundi_ies ies;
1553 struct dundi_peer *peer = NULL;
1556 memset(&ied, 0, sizeof(ied));
1557 memset(&ies, 0, sizeof(ies));
1559 bufcpy = alloca(datalen);
1562 /* Make a copy for parsing */
1563 memcpy(bufcpy, hdr->ies, datalen);
1565 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1566 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1567 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1572 case DUNDI_COMMAND_DPDISCOVER:
1573 case DUNDI_COMMAND_EIDQUERY:
1574 case DUNDI_COMMAND_PRECACHERQ:
1575 if (cmd == DUNDI_COMMAND_EIDQUERY)
1576 resp = DUNDI_COMMAND_EIDRESPONSE;
1577 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1578 resp = DUNDI_COMMAND_PRECACHERP;
1580 resp = DUNDI_COMMAND_DPRESPONSE;
1581 /* A dialplan or entity discover -- qualify by highest level entity */
1582 peer = find_peer(ies.eids[0]);
1584 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1585 dundi_send(trans, resp, 0, 1, &ied);
1588 trans->us_eid = peer->us_eid;
1589 if (strlen(peer->inkey)) {
1590 hasauth = encrypted;
1594 /* Okay we're authentiated and all, now we check if they're authorized */
1595 if (!ies.called_context)
1596 ies.called_context = "e164";
1597 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1598 res = dundi_answer_entity(trans, &ies, ies.called_context);
1600 if (ast_strlen_zero(ies.called_number)) {
1601 /* They're not permitted to access that context */
1602 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1603 dundi_send(trans, resp, 0, 1, &ied);
1604 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1605 (peer->model & DUNDI_MODEL_INBOUND) &&
1606 has_permission(&peer->permit, ies.called_context)) {
1607 res = dundi_answer_query(trans, &ies, ies.called_context);
1609 /* There is no such dundi context */
1610 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1611 dundi_send(trans, resp, 0, 1, &ied);
1613 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1614 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1615 has_permission(&peer->include, ies.called_context)) {
1616 res = dundi_prop_precache(trans, &ies, ies.called_context);
1618 /* There is no such dundi context */
1619 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1620 dundi_send(trans, resp, 0, 1, &ied);
1623 /* They're not permitted to access that context */
1624 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1625 dundi_send(trans, resp, 0, 1, &ied);
1629 /* They're not permitted to access that context */
1630 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1631 dundi_send(trans, resp, 0, 1, &ied);
1635 case DUNDI_COMMAND_REGREQ:
1636 /* A register request -- should only have one entity */
1637 peer = find_peer(ies.eids[0]);
1639 /* if the peer is not found and we have a valid 'any_peer' setting */
1640 if (any_peer && peer == any_peer) {
1641 /* copy any_peer into a new peer object */
1642 peer = ast_calloc(1, sizeof(*peer));
1644 deep_copy_peer(peer, any_peer);
1646 /* set EID to remote EID */
1647 peer->eid = *ies.eids[0];
1649 AST_LIST_LOCK(&peers);
1650 AST_LIST_INSERT_HEAD(&peers, peer, list);
1651 AST_LIST_UNLOCK(&peers);
1655 if (!peer || !peer->dynamic) {
1656 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1657 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1660 trans->us_eid = peer->us_eid;
1661 if (!ast_strlen_zero(peer->inkey)) {
1662 hasauth = encrypted;
1666 int expire = default_expiration;
1669 if (peer->registerexpire > -1)
1670 ast_sched_del(sched, peer->registerexpire);
1671 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1672 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1673 ntohs(trans->addr.sin_port), expire);
1674 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1675 if (inaddrcmp(&peer->addr, &trans->addr)) {
1676 ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
1677 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1678 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1682 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1683 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1684 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1686 qualify_peer(peer, 1);
1690 case DUNDI_COMMAND_DPRESPONSE:
1691 /* A dialplan response, lets see what we got... */
1692 if (ies.cause < 1) {
1693 /* Success of some sort */
1695 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1696 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1697 authpass = encrypted;
1701 /* Pass back up answers */
1702 if (trans->parent && trans->parent->dr) {
1703 y = trans->parent->respcount;
1704 for (x=0;x<ies.anscount;x++) {
1705 if (trans->parent->respcount < trans->parent->maxcount) {
1706 /* Make sure it's not already there */
1707 for (z=0;z<trans->parent->respcount;z++) {
1708 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1709 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1712 if (z == trans->parent->respcount) {
1713 /* Copy into parent responses */
1714 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1715 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1716 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1717 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1718 if (ies.expiration > 0)
1719 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1721 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1722 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1723 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1724 &ies.answers[x]->eid);
1725 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1726 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1727 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1728 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1729 trans->parent->respcount++;
1730 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1731 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1732 /* Update weight if appropriate */
1733 trans->parent->dr[z].weight = ies.answers[x]->weight;
1736 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1737 trans->parent->number, trans->parent->dcontext);
1739 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1740 the cache know if this request was unaffected by our entity list. */
1741 cache_save(&trans->them_eid, trans->parent, y,
1742 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1744 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1745 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1746 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1747 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1748 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1749 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1750 sizeof(trans->parent->hmd->exten));
1753 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1756 if (ies.expiration > 0) {
1757 if (trans->parent->expiration > ies.expiration) {
1758 trans->parent->expiration = ies.expiration;
1762 /* Close connection if not final */
1764 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1768 /* Auth failure, check for data */
1770 /* Cancel if they didn't already */
1771 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1775 case DUNDI_COMMAND_EIDRESPONSE:
1776 /* A dialplan response, lets see what we got... */
1777 if (ies.cause < 1) {
1778 /* Success of some sort */
1780 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1781 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1782 authpass = encrypted;
1786 /* Pass back up answers */
1787 if (trans->parent && trans->parent->dei && ies.q_org) {
1788 if (!trans->parent->respcount) {
1789 trans->parent->respcount++;
1791 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1793 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1795 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1796 if (ies.q_stateprov)
1797 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1799 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1801 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1803 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1805 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1806 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1807 /* If it's them, update our address */
1808 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1812 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1813 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1816 /* Close connection if not final */
1818 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1822 /* Auth failure, check for data */
1824 /* Cancel if they didn't already */
1825 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1829 case DUNDI_COMMAND_REGRESPONSE:
1830 /* A dialplan response, lets see what we got... */
1831 if (ies.cause < 1) {
1833 /* Success of some sort */
1834 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1835 hasauth = encrypted;
1840 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1842 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1843 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1847 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),
1848 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1849 /* Close connection if not final */
1851 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1854 /* Auth failure, cancel if they didn't for some reason */
1856 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1860 case DUNDI_COMMAND_INVALID:
1861 case DUNDI_COMMAND_NULL:
1862 case DUNDI_COMMAND_PRECACHERP:
1863 /* Do nothing special */
1865 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1867 case DUNDI_COMMAND_ENCREJ:
1868 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1869 /* No really, it's over at this point */
1871 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1873 /* Send with full key */
1874 ast_set_flag(trans, FLAG_SENDFULLKEY);
1876 /* Ooops, we got a final message, start by sending ACK... */
1877 dundi_ack(trans, hdr->cmdresp & 0x80);
1878 trans->aseqno = trans->iseqno;
1879 /* Now, we gotta create a new transaction */
1880 if (!reset_transaction(trans)) {
1881 /* Make sure handle_frame doesn't destroy us */
1882 hdr->cmdresp &= 0x7f;
1883 /* Parse the message we transmitted */
1884 memset(&ies, 0, sizeof(ies));
1885 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1886 /* Reconstruct outgoing encrypted packet */
1887 memset(&ied, 0, sizeof(ied));
1888 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1889 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1890 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1892 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1893 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
1894 peer->sentfullkey = 1;
1899 case DUNDI_COMMAND_ENCRYPT:
1901 /* No nested encryption! */
1902 if ((trans->iseqno == 1) && !trans->oseqno) {
1903 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1904 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1905 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1907 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1911 apply_peer(trans, peer);
1912 /* Key passed, use new contexts for this session */
1913 trans->ecx = peer->them_ecx;
1914 trans->dcx = peer->them_dcx;
1916 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1917 struct dundi_hdr *dhdr;
1918 unsigned char decoded[MAX_PACKET_SIZE];
1920 ddatalen = sizeof(decoded);
1921 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1923 /* Handle decrypted response */
1925 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1926 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1927 /* Carry back final flag */
1928 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1932 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1937 /* Turn off encryption */
1938 ast_clear_flag(trans, FLAG_ENCRYPT);
1939 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1943 /* Send unknown command if we don't know it, with final flag IFF it's the
1944 first command in the dialog and only if we haven't recieved final notification */
1946 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1947 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1953 static void destroy_packet(struct dundi_packet *pack, int needfree);
1954 static void destroy_packets(struct packetlist *p)
1956 struct dundi_packet *pack;
1958 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1959 if (pack->retransid > -1)
1960 ast_sched_del(sched, pack->retransid);
1966 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1968 struct dundi_packet *pack;
1970 /* Ack transmitted packet corresponding to iseqno */
1971 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1972 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1973 destroy_packet(pack, 0);
1974 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1975 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1976 destroy_packets(&trans->lasttrans);
1978 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1979 if (trans->autokillid > -1)
1980 ast_sched_del(sched, trans->autokillid);
1981 trans->autokillid = -1;
1989 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
1991 struct dundi_transaction *trans;
1992 trans = find_transaction(h, sin);
1994 dundi_reject(h, sin);
1997 /* Got a transaction, see where this header fits in */
1998 if (h->oseqno == trans->iseqno) {
1999 /* Just what we were looking for... Anything but ack increments iseqno */
2000 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2001 /* If final, we're done */
2002 destroy_trans(trans, 0);
2005 if (h->cmdresp != DUNDI_COMMAND_ACK) {
2006 trans->oiseqno = trans->iseqno;
2008 handle_command_response(trans, h, datalen, 0);
2010 if (trans->aseqno != trans->iseqno) {
2011 dundi_ack(trans, h->cmdresp & 0x80);
2012 trans->aseqno = trans->iseqno;
2014 /* Delete any saved last transmissions */
2015 destroy_packets(&trans->lasttrans);
2016 if (h->cmdresp & 0x80) {
2017 /* Final -- destroy now */
2018 destroy_trans(trans, 0);
2020 } else if (h->oseqno == trans->oiseqno) {
2021 /* Last incoming sequence number -- send ACK without processing */
2022 dundi_ack(trans, 0);
2024 /* Out of window -- simply drop */
2026 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
2031 static int socket_read(int *id, int fd, short events, void *cbdata)
2033 struct sockaddr_in sin;
2035 struct dundi_hdr *h;
2036 char buf[MAX_PACKET_SIZE];
2037 socklen_t len = sizeof(sin);
2039 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
2041 if (errno != ECONNREFUSED)
2042 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2045 if (res < sizeof(struct dundi_hdr)) {
2046 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2050 h = (struct dundi_hdr *) buf;
2052 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2053 AST_LIST_LOCK(&peers);
2054 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2055 AST_LIST_UNLOCK(&peers);
2059 static void build_secret(char *secret, int seclen)
2061 unsigned char tmp[16];
2065 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2066 /* Eliminate potential bad characters */
2067 while((s = strchr(secret, ';'))) *s = '+';
2068 while((s = strchr(secret, '/'))) *s = '+';
2069 while((s = strchr(secret, ':'))) *s = '+';
2070 while((s = strchr(secret, '@'))) *s = '+';
2074 static void save_secret(const char *newkey, const char *oldkey)
2078 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2080 snprintf(tmp, sizeof(tmp), "%s", newkey);
2081 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2082 ast_db_put(secretpath, "secret", tmp);
2083 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2084 ast_db_put(secretpath, "secretexpiry", tmp);
2087 static void load_password(void)
2094 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2095 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2096 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2097 current = strchr(tmp, ';');
2104 if ((time(NULL) - expired) < 0) {
2105 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2106 expired = time(NULL) + DUNDI_SECRET_TIME;
2107 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2116 /* Current key is still valid, just setup rotatation properly */
2117 ast_copy_string(cursecret, current, sizeof(cursecret));
2118 rotatetime = expired;
2120 /* Current key is out of date, rotate or eliminate all together */
2121 build_secret(cursecret, sizeof(cursecret));
2122 save_secret(cursecret, last);
2126 static void check_password(void)
2133 printf("%ld/%ld\n", now, rotatetime);
2135 if ((now - rotatetime) >= 0) {
2136 /* Time to rotate keys */
2137 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2138 build_secret(cursecret, sizeof(cursecret));
2139 save_secret(cursecret, oldsecret);
2143 static void *network_thread(void *ignore)
2145 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2146 from the network, and queue them for delivery to the channels */
2148 /* Establish I/O callback for socket read */
2149 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2151 while (!dundi_shutdown) {
2152 res = ast_sched_wait(sched);
2153 if ((res > 1000) || (res < 0))
2155 res = ast_io_wait(io, res);
2157 AST_LIST_LOCK(&peers);
2158 ast_sched_runq(sched);
2159 AST_LIST_UNLOCK(&peers);
2164 netthreadid = AST_PTHREADT_NULL;
2169 static void *process_precache(void *ign)
2171 struct dundi_precache_queue *qe;
2177 while (!dundi_shutdown) {
2180 AST_LIST_LOCK(&pcq);
2181 if ((qe = AST_LIST_FIRST(&pcq))) {
2182 if (!qe->expiration) {
2183 /* Gone... Remove... */
2184 AST_LIST_REMOVE_HEAD(&pcq, list);
2186 } else if (qe->expiration < now) {
2187 /* Process this entry */
2189 ast_copy_string(context, qe->context, sizeof(context));
2190 ast_copy_string(number, qe->number, sizeof(number));
2194 AST_LIST_UNLOCK(&pcq);
2196 dundi_precache(context, number);
2201 precachethreadid = AST_PTHREADT_NULL;
2206 static int start_network_thread(void)
2208 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2209 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2213 static char *dundi_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2217 e->command = "dundi debug";
2219 "Usage: dundi debug\n"
2220 " Enables dumping of DUNDi packets for debugging purposes\n";
2226 return CLI_SHOWUSAGE;
2228 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2232 static char *dundi_do_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2236 e->command = "dundi store history";
2238 "Usage: dundi store history\n"
2239 " Enables storing of DUNDi requests and times for debugging\n"
2246 return CLI_SHOWUSAGE;
2247 global_storehistory = 1;
2248 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2252 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2257 e->command = "dundi flush [stats]";
2259 "Usage: dundi flush [stats]\n"
2260 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2261 "'stats' is present, clears timer statistics instead of normal\n"
2267 if ((a->argc < 2) || (a->argc > 3))
2268 return CLI_SHOWUSAGE;
2270 if (!strcasecmp(a->argv[2], "stats"))
2273 return CLI_SHOWUSAGE;
2276 /* Flush statistics */
2277 struct dundi_peer *p;
2279 AST_LIST_LOCK(&peers);
2280 AST_LIST_TRAVERSE(&peers, p, list) {
2281 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2283 ast_free(p->lookups[x]);
2284 p->lookups[x] = NULL;
2285 p->lookuptimes[x] = 0;
2289 AST_LIST_UNLOCK(&peers);
2291 ast_db_deltree("dundi/cache", NULL);
2292 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2297 static char *dundi_no_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2301 e->command = "dundi no debug";
2303 "Usage: dundi no debug\n"
2304 " Disables dumping of DUNDi packets for debugging purposes\n";
2310 return CLI_SHOWUSAGE;
2312 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2316 static char *dundi_no_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2320 e->command = "dundi no store history";
2322 "Usage: dundi no store history\n"
2323 " Disables storing of DUNDi requests and times for debugging\n"
2330 return CLI_SHOWUSAGE;
2331 global_storehistory = 0;
2332 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2336 static char *model2str(int model)
2339 case DUNDI_MODEL_INBOUND:
2341 case DUNDI_MODEL_OUTBOUND:
2343 case DUNDI_MODEL_SYMMETRIC:
2350 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2354 struct dundi_peer *p;
2359 AST_LIST_LOCK(&peers);
2361 AST_LIST_TRAVERSE(&peers, p, list) {
2362 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2363 if (!strncasecmp(word, s, len) && ++which > state) {
2364 ret = ast_strdup(s);
2368 AST_LIST_UNLOCK(&peers);
2372 static int rescomp(const void *a, const void *b)
2374 const struct dundi_result *resa, *resb;
2377 if (resa->weight < resb->weight)
2379 if (resa->weight > resb->weight)
2384 static void sort_results(struct dundi_result *results, int count)
2386 qsort(results, count, sizeof(results[0]), rescomp);
2389 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2397 struct dundi_result dr[MAX_RESULTS];
2398 struct timeval start;
2401 e->command = "dundi lookup";
2403 "Usage: dundi lookup <number>[@context] [bypass]\n"
2404 " Lookup the given number within the given DUNDi context\n"
2405 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2406 "keyword is specified.\n";
2412 if ((a->argc < 3) || (a->argc > 4))
2413 return CLI_SHOWUSAGE;
2415 if (!strcasecmp(a->argv[3], "bypass"))
2418 return CLI_SHOWUSAGE;
2420 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2421 context = strchr(tmp, '@');
2426 start = ast_tvnow();
2427 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2430 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2432 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2434 sort_results(dr, res);
2435 for (x=0;x<res;x++) {
2436 ast_cli(a->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));
2437 ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2439 ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2443 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2448 struct timeval start;
2451 e->command = "dundi precache";
2453 "Usage: dundi precache <number>[@context]\n"
2454 " Lookup the given number within the given DUNDi context\n"
2455 "(or e164 if none is specified) and precaches the results to any\n"
2456 "upstream DUNDi push servers.\n";
2461 if ((a->argc < 3) || (a->argc > 3))
2462 return CLI_SHOWUSAGE;
2463 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2464 context = strchr(tmp, '@');
2469 start = ast_tvnow();
2470 res = dundi_precache(context, tmp);
2473 ast_cli(a->fd, "DUNDi precache returned error.\n");
2475 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2476 ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2480 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2486 struct dundi_entity_info dei;
2489 e->command = "dundi query";
2491 "Usage: dundi query <entity>[@context]\n"
2492 " Attempts to retrieve contact information for a specific\n"
2493 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2494 "e164 if none is specified).\n";
2499 if ((a->argc < 3) || (a->argc > 3))
2500 return CLI_SHOWUSAGE;
2501 if (dundi_str_to_eid(&eid, a->argv[2])) {
2502 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2503 return CLI_SHOWUSAGE;
2505 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2506 context = strchr(tmp, '@');
2511 res = dundi_query_eid(&dei, context, eid);
2513 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2515 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2517 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2518 ast_cli(a->fd, "Department: %s\n", dei.orgunit);
2519 ast_cli(a->fd, "Organization: %s\n", dei.org);
2520 ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
2521 ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
2522 ast_cli(a->fd, "Country: %s\n", dei.country);
2523 ast_cli(a->fd, "E-mail: %s\n", dei.email);
2524 ast_cli(a->fd, "Phone: %s\n", dei.phone);
2525 ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
2530 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2532 struct dundi_peer *peer;
2533 struct permission *p;
2539 e->command = "dundi show peer";
2541 "Usage: dundi show peer [peer]\n"
2542 " Provide a detailed description of a specifid DUNDi peer.\n";
2545 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2548 return CLI_SHOWUSAGE;
2549 AST_LIST_LOCK(&peers);
2550 AST_LIST_TRAVERSE(&peers, peer, list) {
2551 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2555 switch(peer->order) {
2560 order = "Secondary";
2566 order = "Quartiary";
2571 ast_cli(a->fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2572 ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
2573 ast_cli(a->fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2574 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2575 ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2576 ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2577 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2578 if (!AST_LIST_EMPTY(&peer->include))
2579 ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2580 AST_LIST_TRAVERSE(&peer->include, p, list)
2581 ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2582 if (!AST_LIST_EMPTY(&peer->permit))
2583 ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2584 AST_LIST_TRAVERSE(&peer->permit, p, list)
2585 ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2587 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2588 if (peer->lookups[x]) {
2590 ast_cli(a->fd, "Last few query times:\n");
2591 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2596 ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2598 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2599 AST_LIST_UNLOCK(&peers);
2603 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2605 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2606 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2607 struct dundi_peer *peer;
2608 int registeredonly=0;
2611 int online_peers = 0;
2612 int offline_peers = 0;
2613 int unmonitored_peers = 0;
2614 int total_peers = 0;
2617 e->command = "dundi show peers [registered|include|exclude|begin]";
2619 "Usage: dundi show peers [registered|include|exclude|begin]\n"
2620 " Lists all known DUNDi peers.\n"
2621 " If 'registered' is present, only registered peers are shown.\n";
2627 if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5))
2628 return CLI_SHOWUSAGE;
2629 if ((a->argc == 4)) {
2630 if (!strcasecmp(a->argv[3], "registered")) {
2633 return CLI_SHOWUSAGE;
2635 AST_LIST_LOCK(&peers);
2636 ast_cli(a->fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2637 AST_LIST_TRAVERSE(&peers, peer, list) {
2639 int print_line = -1;
2642 if (registeredonly && !peer->addr.sin_addr.s_addr)
2645 if (peer->lastms < 0) {
2646 strcpy(status, "UNREACHABLE");
2649 else if (peer->lastms > peer->maxms) {
2650 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2653 else if (peer->lastms) {
2654 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2658 strcpy(status, "UNKNOWN");
2662 strcpy(status, "Unmonitored");
2663 unmonitored_peers++;
2666 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2668 strcpy(avgms, "Unavail");
2669 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2670 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2671 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2674 if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2676 } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2678 } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2686 ast_cli(a->fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2687 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2688 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2691 ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2692 AST_LIST_UNLOCK(&peers);
2698 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2700 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2701 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2702 struct dundi_transaction *trans;
2705 e->command = "dundi show trans";
2707 "Usage: dundi show trans\n"
2708 " Lists all known DUNDi transactions.\n";
2714 return CLI_SHOWUSAGE;
2715 AST_LIST_LOCK(&peers);
2716 ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2717 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2718 ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2719 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2721 AST_LIST_UNLOCK(&peers);
2727 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2732 e->command = "dundi show entityid";
2734 "Usage: dundi show entityid\n"
2735 " Displays the global entityid for this host.\n";
2741 return CLI_SHOWUSAGE;
2742 AST_LIST_LOCK(&peers);
2743 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2744 AST_LIST_UNLOCK(&peers);
2745 ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2749 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2751 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2752 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2753 struct dundi_request *req;
2757 e->command = "dundi show requests";
2759 "Usage: dundi show requests\n"
2760 " Lists all known pending DUNDi requests.\n";
2766 return CLI_SHOWUSAGE;
2767 AST_LIST_LOCK(&peers);
2768 ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2769 AST_LIST_TRAVERSE(&requests, req, list) {
2770 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2771 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2773 AST_LIST_UNLOCK(&peers);
2779 /* Grok-a-dial DUNDi */
2781 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2783 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2784 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2785 struct dundi_mapping *map;
2790 e->command = "dundi show mappings";
2792 "Usage: dundi show mappings\n"
2793 " Lists all known DUNDi mappings.\n";
2799 return CLI_SHOWUSAGE;
2800 AST_LIST_LOCK(&peers);
2801 ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2802 AST_LIST_TRAVERSE(&mappings, map, list) {
2803 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map));
2804 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2805 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2806 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2808 AST_LIST_UNLOCK(&peers);
2814 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2816 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2817 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2818 struct dundi_precache_queue *qe;
2823 e->command = "dundi show precache";
2825 "Usage: dundi show precache\n"
2826 " Lists all known DUNDi scheduled precache updates.\n";
2832 return CLI_SHOWUSAGE;
2834 ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2835 AST_LIST_LOCK(&pcq);
2836 AST_LIST_TRAVERSE(&pcq, qe, list) {
2837 s = qe->expiration - now;
2842 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2844 AST_LIST_UNLOCK(&pcq);
2851 static struct ast_cli_entry cli_dundi[] = {
2852 AST_CLI_DEFINE(dundi_do_debug, "Enable DUNDi debugging"),
2853 AST_CLI_DEFINE(dundi_no_debug, "Disable DUNDi debugging"),
2854 AST_CLI_DEFINE(dundi_do_store_history, "Enable DUNDi historic records"),
2855 AST_CLI_DEFINE(dundi_no_store_history, "Disable DUNDi historic records"),
2856 AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
2857 AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
2858 AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
2859 AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
2860 AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
2861 AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
2862 AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
2863 AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
2864 AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
2865 AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
2866 AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
2869 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2871 struct dundi_transaction *trans;
2874 /* Don't allow creation of transactions to non-registered peers */
2875 if (p && !p->addr.sin_addr.s_addr)
2877 tid = get_trans_id();
2880 if (!(trans = ast_calloc(1, sizeof(*trans))))
2883 if (global_storehistory) {
2884 trans->start = ast_tvnow();
2885 ast_set_flag(trans, FLAG_STOREHIST);
2887 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2888 trans->autokillid = -1;
2890 apply_peer(trans, p);
2891 if (!p->sentfullkey)
2892 ast_set_flag(trans, FLAG_SENDFULLKEY);
2894 trans->strans = tid;
2895 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2900 static int dundi_xmit(struct dundi_packet *pack)
2904 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2905 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2907 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2908 ast_inet_ntoa(pack->parent->addr.sin_addr),
2909 ntohs(pack->parent->addr.sin_port), strerror(errno));
2916 static void destroy_packet(struct dundi_packet *pack, int needfree)
2919 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2920 if (pack->retransid > -1)
2921 ast_sched_del(sched, pack->retransid);
2925 pack->retransid = -1;
2928 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
2930 struct dundi_peer *peer;
2935 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
2936 AST_LIST_TRAVERSE(&peers, peer, list) {
2937 if (peer->regtrans == trans)
2938 peer->regtrans = NULL;
2939 if (peer->qualtrans == trans) {
2941 if (peer->lastms > -1)
2942 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2945 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
2948 if (ms < peer->maxms) {
2949 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
2950 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2951 } else if (peer->lastms < peer->maxms) {
2952 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);
2956 peer->qualtrans = NULL;
2958 if (ast_test_flag(trans, FLAG_STOREHIST)) {
2959 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
2960 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
2963 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
2964 ast_free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
2965 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
2966 peer->lookuptimes[x] = peer->lookuptimes[x-1];
2967 peer->lookups[x] = peer->lookups[x-1];
2968 if (peer->lookups[x]) {
2969 peer->avgms += peer->lookuptimes[x];
2973 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
2974 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
2975 if (peer->lookups[0]) {
2976 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
2977 peer->avgms += peer->lookuptimes[0];
2987 if (trans->parent) {
2988 /* Unlink from parent if appropriate */
2989 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
2990 if (AST_LIST_EMPTY(&trans->parent->trans)) {
2991 /* Wake up sleeper */
2992 if (trans->parent->pfds[1] > -1) {
2993 write(trans->parent->pfds[1], "killa!", 6);
2997 /* Unlink from all trans */
2998 AST_LIST_REMOVE(&alltrans, trans, all);
2999 destroy_packets(&trans->packets);
3000 destroy_packets(&trans->lasttrans);
3001 if (trans->autokillid > -1)
3002 ast_sched_del(sched, trans->autokillid);
3003 trans->autokillid = -1;
3004 if (trans->thread) {
3005 /* If used by a thread, mark as dead and be done */
3006 ast_set_flag(trans, FLAG_DEAD);
3011 static int dundi_rexmit(const void *data)
3013 struct dundi_packet *pack = (struct dundi_packet *)data;
3015 AST_LIST_LOCK(&peers);
3016 if (pack->retrans < 1) {
3017 pack->retransid = -1;
3018 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
3019 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
3020 ast_inet_ntoa(pack->parent->addr.sin_addr),
3021 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
3022 destroy_trans(pack->parent, 1);
3025 /* Decrement retransmission, try again */
3030 AST_LIST_UNLOCK(&peers);
3034 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
3036 struct dundi_packet *pack;
3040 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
3041 /* Reserve enough space for encryption */
3042 if (ast_test_flag(trans, FLAG_ENCRYPT))
3044 pack = ast_calloc(1, len);
3046 pack->h = (struct dundi_hdr *)(pack->data);
3047 if (cmdresp != DUNDI_COMMAND_ACK) {
3048 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
3049 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3050 AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
3052 pack->parent = trans;
3053 pack->h->strans = htons(trans->strans);
3054 pack->h->dtrans = htons(trans->dtrans);
3055 pack->h->iseqno = trans->iseqno;
3056 pack->h->oseqno = trans->oseqno;
3057 pack->h->cmdresp = cmdresp;
3058 pack->datalen = sizeof(struct dundi_hdr);
3060 memcpy(pack->h->ies, ied->buf, ied->pos);
3061 pack->datalen += ied->pos;
3064 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3065 ast_set_flag(trans, FLAG_FINAL);
3067 pack->h->cmdflags = flags;
3068 if (cmdresp != DUNDI_COMMAND_ACK) {
3070 trans->oseqno = trans->oseqno % 256;
3072 trans->aseqno = trans->iseqno;
3073 /* If we have their public key, encrypt */
3074 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3076 case DUNDI_COMMAND_REGREQ:
3077 case DUNDI_COMMAND_REGRESPONSE:
3078 case DUNDI_COMMAND_DPDISCOVER:
3079 case DUNDI_COMMAND_DPRESPONSE:
3080 case DUNDI_COMMAND_EIDQUERY:
3081 case DUNDI_COMMAND_EIDRESPONSE:
3082 case DUNDI_COMMAND_PRECACHERQ:
3083 case DUNDI_COMMAND_PRECACHERP:
3085 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3086 res = dundi_encrypt(trans, pack);
3094 res = dundi_xmit(pack);
3096 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3098 if (cmdresp == DUNDI_COMMAND_ACK)
3105 static int do_autokill(const void *data)
3107 struct dundi_transaction *trans = (struct dundi_transaction *)data;
3109 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3110 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3111 trans->autokillid = -1;
3112 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3116 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3118 struct dundi_peer *p;
3119 if (!dundi_eid_cmp(eid, us)) {
3120 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3123 AST_LIST_LOCK(&peers);
3124 AST_LIST_TRAVERSE(&peers, p, list) {
3125 if (!dundi_eid_cmp(&p->eid, eid)) {
3126 if (has_permission(&p->include, context))
3127 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3129 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3134 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3135 AST_LIST_UNLOCK(&peers);
3138 static int dundi_discover(struct dundi_transaction *trans)
3140 struct dundi_ie_data ied;
3142 if (!trans->parent) {
3143 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3146 memset(&ied, 0, sizeof(ied));
3147 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3148 if (!dundi_eid_zero(&trans->us_eid))
3149 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3150 for (x=0;x<trans->eidcount;x++)
3151 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3152 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3153 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3154 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3155 if (trans->parent->cbypass)
3156 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3157 if (trans->autokilltimeout)
3158 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3159 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3162 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3164 struct dundi_ie_data ied;
3167 int expiration = dundi_cache_time;
3169 dundi_eid *avoid[1] = { NULL, };
3170 int direct[1] = { 0, };
3171 struct dundi_result dr[MAX_RESULTS];
3172 struct dundi_hint_metadata hmd;
3173 if (!trans->parent) {
3174 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3177 memset(&hmd, 0, sizeof(hmd));
3178 memset(&dr, 0, sizeof(dr));
3179 /* Look up the answers we're going to include */
3180 for (x=0;x<mapcount;x++)
3181 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3184 for (x=0;x<ouranswers;x++) {
3185 if (dr[x].weight < max)
3189 /* If we do not have a canonical result, keep looking */
3190 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);
3192 /* Append answer in result */
3197 if (ouranswers > 0) {
3198 *foundanswers += ouranswers;
3199 memset(&ied, 0, sizeof(ied));
3200 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3201 if (!dundi_eid_zero(&trans->us_eid))
3202 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3203 for (x=0;x<trans->eidcount;x++)
3204 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3205 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3206 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3207 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3208 for (x=0;x<ouranswers;x++) {
3210 if (dr[x].expiration && (expiration > dr[x].expiration))
3211 expiration = dr[x].expiration;
3212 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3214 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3215 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3216 if (trans->autokilltimeout)
3217 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3218 if (expiration < *minexp)
3219 *minexp = expiration;
3220 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3222 /* Oops, nothing to send... */
3223 destroy_trans(trans, 0);
3228 static int dundi_query(struct dundi_transaction *trans)
3230 struct dundi_ie_data ied;
3232 if (!trans->parent) {
3233 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3236 memset(&ied, 0, sizeof(ied));
3237 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3238 if (!dundi_eid_zero(&trans->us_eid))
3239 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3240 for (x=0;x<trans->eidcount;x++)
3241 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3242 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3243 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3244 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3245 if (trans->autokilltimeout)
3246 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3247 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3250 static int discover_transactions(struct dundi_request *dr)
3252 struct dundi_transaction *trans;
3253 AST_LIST_LOCK(&peers);
3254 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3255 dundi_discover(trans);
3257 AST_LIST_UNLOCK(&peers);
3261 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3263 struct dundi_transaction *trans;
3265 /* Mark all as "in thread" so they don't disappear */
3266 AST_LIST_LOCK(&peers);
3267 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3269 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3272 AST_LIST_UNLOCK(&peers);
3274 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3275 if (!ast_test_flag(trans, FLAG_DEAD))
3276 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3279 /* Cleanup any that got destroyed in the mean time */
3280 AST_LIST_LOCK(&peers);
3281 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
3283 if (ast_test_flag(trans, FLAG_DEAD)) {
3285 ast_log(LOG_DEBUG, "Our transaction went away!\n");
3286 /* This is going to remove the transaction from the dundi_request's list, as well
3287 * as the global transactions list */
3288 destroy_trans(trans, 0);
3291 AST_LIST_TRAVERSE_SAFE_END
3292 AST_LIST_UNLOCK(&peers);
3297 static int query_transactions(struct dundi_request *dr)
3299 struct dundi_transaction *trans;
3301 AST_LIST_LOCK(&peers);
3302 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3305 AST_LIST_UNLOCK(&peers);
3310 static int optimize_transactions(struct dundi_request *dr, int order)
3312 /* Minimize the message propagation through DUNDi by
3313 alerting the network to hops which should be not be considered */
3314 struct dundi_transaction *trans;
3315 struct dundi_peer *peer;
3320 AST_LIST_LOCK(&peers);
3321 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3322 /* Pop off the true root */
3323 if (trans->eidcount) {
3324 tmp = trans->eids[--trans->eidcount];
3327 tmp = trans->us_eid;
3331 AST_LIST_TRAVERSE(&peers, peer, list) {
3332 if (has_permission(&peer->include, dr->dcontext) &&
3333 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
3334 (peer->order <= order)) {
3335 /* For each other transaction, make sure we don't
3336 ask this EID about the others if they're not
3337 already in the list */
3338 if (!dundi_eid_cmp(&tmp, &peer->eid))
3341 for (x=0;x<trans->eidcount;x++) {