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 <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <sys/socket.h>
37 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
38 #include <sys/types.h>
39 #include <netinet/in_systm.h>
41 #include <netinet/ip.h>
42 #include <sys/ioctl.h>
44 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
45 #include <net/if_dl.h>
49 #include <sys/signal.h>
52 #include "asterisk/file.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/channel.h"
55 #include "asterisk/config.h"
56 #include "asterisk/options.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/module.h"
59 #include "asterisk/frame.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/md5.h"
63 #include "asterisk/dundi.h"
64 #include "asterisk/sched.h"
65 #include "asterisk/io.h"
66 #include "asterisk/utils.h"
67 #include "asterisk/netsock.h"
68 #include "asterisk/crypto.h"
69 #include "asterisk/astdb.h"
70 #include "asterisk/acl.h"
71 #include "asterisk/aes.h"
72 #include "asterisk/app.h"
74 #include "dundi-parser.h"
76 #define MAX_RESULTS 64
78 #define MAX_PACKET_SIZE 8192
80 #define MAX_WEIGHT 59999
82 #define DUNDI_MODEL_INBOUND (1 << 0)
83 #define DUNDI_MODEL_OUTBOUND (1 << 1)
84 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
86 /*! Keep times of last 10 lookups */
87 #define DUNDI_TIMING_HISTORY 10
90 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
91 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
92 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
93 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
94 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */
95 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
96 FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
99 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
102 #define DUNDI_SECRET_TIME 15 /* Testing only */
104 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
107 static struct io_context *io;
108 static struct sched_context *sched;
109 static int netsocket = -1;
110 static pthread_t netthreadid = AST_PTHREADT_NULL;
111 static pthread_t precachethreadid = AST_PTHREADT_NULL;
112 static unsigned int tos = 0;
113 static int dundidebug = 0;
114 static int authdebug = 0;
115 static int dundi_ttl = DUNDI_DEFAULT_TTL;
116 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
117 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
118 static int global_autokilltimeout = 0;
119 static dundi_eid global_eid;
120 static int default_expiration = 60;
121 static int global_storehistory = 0;
122 static char dept[80];
124 static char locality[80];
125 static char stateprov[80];
126 static char country[80];
127 static char email[80];
128 static char phone[80];
129 static char secretpath[80];
130 static char cursecret[80];
131 static char ipaddr[80];
132 static time_t rotatetime;
133 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
134 static int dundi_shutdown = 0;
137 AST_LIST_ENTRY(permission) list;
142 struct dundi_packet {
143 AST_LIST_ENTRY(dundi_packet) list;
146 struct dundi_transaction *parent;
149 unsigned char data[0];
152 struct dundi_hint_metadata {
153 unsigned short flags;
154 char exten[AST_MAX_EXTENSION];
157 struct dundi_precache_queue {
158 AST_LIST_ENTRY(dundi_precache_queue) list;
164 struct dundi_request;
166 struct dundi_transaction {
167 struct sockaddr_in addr; /*!< Other end of transaction */
168 struct timeval start; /*!< When this transaction was created */
169 dundi_eid eids[DUNDI_MAX_STACK + 1];
170 int eidcount; /*!< Number of eids in eids */
171 dundi_eid us_eid; /*!< Our EID, to them */
172 dundi_eid them_eid; /*!< Their EID, to us */
173 ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
174 ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
175 unsigned int flags; /*!< Has final packet been sent */
176 int ttl; /*!< Remaining TTL for queries on this one */
177 int thread; /*!< We have a calling thread */
178 int retranstimer; /*!< How long to wait before retransmissions */
179 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
180 int autokilltimeout; /*!< Recommended timeout for autokill */
181 unsigned short strans; /*!< Our transaction identifier */
182 unsigned short dtrans; /*!< Their transaction identifer */
183 unsigned char iseqno; /*!< Next expected received seqno */
184 unsigned char oiseqno; /*!< Last received incoming seqno */
185 unsigned char oseqno; /*!< Next transmitted seqno */
186 unsigned char aseqno; /*!< Last acknowledge seqno */
187 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
188 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
189 struct dundi_request *parent; /*!< Parent request (if there is one) */
190 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
191 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
194 struct dundi_request {
195 char dcontext[AST_MAX_EXTENSION];
196 char number[AST_MAX_EXTENSION];
199 struct dundi_result *dr;
200 struct dundi_entity_info *dei;
201 struct dundi_hint_metadata *hmd;
207 unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */
208 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
209 AST_LIST_ENTRY(dundi_request) list;
212 struct dundi_mapping {
213 char dcontext[AST_MAX_EXTENSION];
214 char lcontext[AST_MAX_EXTENSION];
220 char dest[AST_MAX_EXTENSION];
221 AST_LIST_ENTRY(dundi_mapping) list;
226 struct sockaddr_in addr; /*!< Address of DUNDi peer */
227 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
228 struct permissionlist include;
237 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
238 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
239 unsigned long us_keycrc32; /*!< CRC-32 of our key */
240 ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
241 ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
242 unsigned long them_keycrc32; /*!< CRC-32 of our key */
243 ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
244 ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
245 time_t keyexpire; /*!< When to expire/recreate key */
247 int lookuptimes[DUNDI_TIMING_HISTORY];
248 char *lookups[DUNDI_TIMING_HISTORY];
250 struct dundi_transaction *regtrans; /*!< Registration transaction */
251 struct dundi_transaction *qualtrans; /*!< Qualify transaction */
252 int model; /*!< Pull model */
253 int pcmodel; /*!< Push/precache model */
254 /*! Dynamic peers register with us */
255 unsigned int dynamic:1;
256 int lastms; /*!< Last measured latency */
257 int maxms; /*!< Max permissible latency */
258 struct timeval qualtx; /*!< Time of transmit */
259 AST_LIST_ENTRY(dundi_peer) list;
262 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
263 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
264 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
265 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
266 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
269 * \brief Wildcard peer
271 * This peer is created if the [*] entry is specified in dundi.conf
273 static struct dundi_peer *any_peer;
275 static int dundi_xmit(struct dundi_packet *pack);
277 static void dundi_debug_output(const char *data)
280 ast_verbose("%s", data);
283 static void dundi_error_output(const char *data)
285 ast_log(LOG_WARNING, "%s", data);
288 static int has_permission(struct permissionlist *permlist, char *cont)
290 struct permission *perm;
293 AST_LIST_TRAVERSE(permlist, perm, list) {
294 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
301 static char *tech2str(int tech)
304 case DUNDI_PROTO_NONE:
306 case DUNDI_PROTO_IAX:
308 case DUNDI_PROTO_SIP:
310 case DUNDI_PROTO_H323:
317 static int str2tech(char *str)
319 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
320 return DUNDI_PROTO_IAX;
321 else if (!strcasecmp(str, "SIP"))
322 return DUNDI_PROTO_SIP;
323 else if (!strcasecmp(str, "H323"))
324 return DUNDI_PROTO_H323;
329 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[]);
330 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
331 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
332 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
334 struct dundi_transaction *trans;
336 /* Look for an exact match first */
337 AST_LIST_TRAVERSE(&alltrans, trans, all) {
338 if (!inaddrcmp(&trans->addr, sin) &&
339 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
340 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
342 trans->dtrans = ntohs(hdr->strans) & 32767;
347 switch(hdr->cmdresp & 0x7f) {
348 case DUNDI_COMMAND_DPDISCOVER:
349 case DUNDI_COMMAND_EIDQUERY:
350 case DUNDI_COMMAND_PRECACHERQ:
351 case DUNDI_COMMAND_REGREQ:
352 case DUNDI_COMMAND_NULL:
353 case DUNDI_COMMAND_ENCRYPT:
356 /* Create new transaction */
357 if (!(trans = create_transaction(NULL)))
359 memcpy(&trans->addr, sin, sizeof(trans->addr));
360 trans->dtrans = ntohs(hdr->strans) & 32767;
368 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
370 static int dundi_ack(struct dundi_transaction *trans, int final)
372 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
374 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
377 struct dundi_packet pack;
378 struct dundi_hdr hdr;
380 struct dundi_transaction trans;
381 /* Never respond to an INVALID with another INVALID */
382 if (h->cmdresp == DUNDI_COMMAND_INVALID)
384 memset(&tmp, 0, sizeof(tmp));
385 memset(&trans, 0, sizeof(trans));
386 memcpy(&trans.addr, sin, sizeof(trans.addr));
387 tmp.hdr.strans = h->dtrans;
388 tmp.hdr.dtrans = h->strans;
389 tmp.hdr.iseqno = h->oseqno;
390 tmp.hdr.oseqno = h->iseqno;
391 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
392 tmp.hdr.cmdflags = 0;
393 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
394 tmp.pack.datalen = sizeof(struct dundi_hdr);
395 tmp.pack.parent = &trans;
396 dundi_xmit(&tmp.pack);
399 static void reset_global_eid(void)
401 #if defined(SIOCGIFHWADDR)
406 s = socket(AF_INET, SOCK_STREAM, 0);
409 for (x = 0; x < 10; x++) {
410 memset(&ifr, 0, sizeof(ifr));
411 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
412 if (ioctl(s, SIOCGIFHWADDR, &ifr))
414 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
416 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n",
417 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
424 #if defined(ifa_broadaddr) && !defined(SOLARIS)
426 struct ifaddrs *ifap;
428 if (getifaddrs(&ifap) == 0) {
430 for (p = ifap; p; p = p->ifa_next) {
431 if (p->ifa_addr->sa_family == AF_LINK) {
432 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
435 sdp->sdl_data + sdp->sdl_nlen, 6);
437 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);
446 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n");
449 static int get_trans_id(void)
451 struct dundi_transaction *t;
452 int stid = (ast_random() % 32766) + 1;
456 AST_LIST_TRAVERSE(&alltrans, t, all) {
457 if (t->strans == tid)
462 tid = (tid % 32766) + 1;
463 } while (tid != stid);
468 static int reset_transaction(struct dundi_transaction *trans)
471 tid = get_trans_id();
480 ast_clear_flag(trans, FLAG_FINAL);
484 static struct dundi_peer *find_peer(dundi_eid *eid)
486 struct dundi_peer *cur = NULL;
491 AST_LIST_TRAVERSE(&peers, cur, list) {
492 if (!dundi_eid_cmp(&cur->eid,eid))
496 if (!cur && any_peer)
502 static void build_iv(unsigned char *iv)
504 /* XXX Would be nice to be more random XXX */
505 unsigned int *fluffy;
507 fluffy = (unsigned int *)(iv);
509 fluffy[x] = ast_random();
512 struct dundi_query_state {
513 dundi_eid *eids[DUNDI_MAX_STACK + 1];
514 int directs[DUNDI_MAX_STACK + 1];
516 char called_context[AST_MAX_EXTENSION];
517 char called_number[AST_MAX_EXTENSION];
518 struct dundi_mapping *maps;
521 struct dundi_transaction *trans;
528 static int get_mapping_weight(struct dundi_mapping *map)
533 if (map->weightstr) {
534 pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
535 if (sscanf(buf, "%d", &map->_weight) != 1)
536 map->_weight = MAX_WEIGHT;
542 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)
544 struct ast_flags flags = {0};
546 if (!ast_strlen_zero(map->lcontext)) {
547 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
548 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
549 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
550 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
551 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
552 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
553 if (ast_ignore_pattern(map->lcontext, called_number))
554 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
556 /* Clearly we can't say 'don't ask' anymore if we found anything... */
557 if (ast_test_flag(&flags, AST_FLAGS_ALL))
558 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
560 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
561 /* Skip partial answers */
562 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
564 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
565 struct varshead headp;
566 struct ast_var_t *newvariable;
567 ast_set_flag(&flags, map->options & 0xffff);
568 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
569 dr[anscnt].techint = map->tech;
570 dr[anscnt].weight = get_mapping_weight(map);
571 dr[anscnt].expiration = dundi_cache_time;
572 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
573 dr[anscnt].eid = *us_eid;
574 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
575 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
576 AST_LIST_HEAD_INIT_NOLOCK(&headp);
577 newvariable = ast_var_assign("NUMBER", called_number);
578 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
579 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
580 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
581 newvariable = ast_var_assign("SECRET", cursecret);
582 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
583 newvariable = ast_var_assign("IPADDR", ipaddr);
584 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
585 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
586 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
587 ast_var_delete(newvariable);
589 dr[anscnt].dest[0] = '\0';
592 /* No answers... Find the fewest number of digits from the
593 number for which we have no answer. */
594 char tmp[AST_MAX_EXTENSION + 1] = "";
595 for (x = 0; x < (sizeof(tmp) - 1); x++) {
596 tmp[x] = called_number[x];
599 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
600 /* Oops found something we can't match. If this is longer
601 than the running hint, we have to consider it */
602 if (strlen(tmp) > strlen(hmd->exten)) {
603 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
613 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
615 static void *dundi_lookup_thread(void *data)
617 struct dundi_query_state *st = data;
618 struct dundi_result dr[MAX_RESULTS];
619 struct dundi_ie_data ied;
620 struct dundi_hint_metadata hmd;
625 int expiration = dundi_cache_time;
628 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
629 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
630 memset(&ied, 0, sizeof(ied));
631 memset(&dr, 0, sizeof(dr));
632 memset(&hmd, 0, sizeof(hmd));
633 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
634 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
635 for (x=0;x<st->nummaps;x++)
636 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
639 for (x=0;x<ouranswers;x++) {
640 if (dr[x].weight < max)
645 /* If we do not have a canonical result, keep looking */
646 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);
648 /* Append answer in result */
651 if ((res < -1) && (!ouranswers))
652 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
655 AST_LIST_LOCK(&peers);
656 /* Truncate if "don't ask" isn't present */
657 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
659 if (ast_test_flag(st->trans, FLAG_DEAD)) {
661 ast_log(LOG_DEBUG, "Our transaction went away!\n");
662 st->trans->thread = 0;
663 destroy_trans(st->trans, 0);
665 for (x=0;x<ouranswers;x++) {
667 if (dr[x].expiration && (expiration > dr[x].expiration))
668 expiration = dr[x].expiration;
669 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
671 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
672 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
673 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
674 st->trans->thread = 0;
676 AST_LIST_UNLOCK(&peers);
681 static void *dundi_precache_thread(void *data)
683 struct dundi_query_state *st = data;
684 struct dundi_ie_data ied;
685 struct dundi_hint_metadata hmd;
689 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
690 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
691 memset(&ied, 0, sizeof(ied));
693 /* Now produce precache */
694 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
696 AST_LIST_LOCK(&peers);
697 /* Truncate if "don't ask" isn't present */
698 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
700 if (ast_test_flag(st->trans, FLAG_DEAD)) {
702 ast_log(LOG_DEBUG, "Our transaction went away!\n");
703 st->trans->thread = 0;
704 destroy_trans(st->trans, 0);
706 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
707 st->trans->thread = 0;
709 AST_LIST_UNLOCK(&peers);
714 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[]);
716 static void *dundi_query_thread(void *data)
718 struct dundi_query_state *st = data;
719 struct dundi_entity_info dei;
720 struct dundi_ie_data ied;
721 struct dundi_hint_metadata hmd;
726 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
727 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
728 memset(&ied, 0, sizeof(ied));
729 memset(&dei, 0, sizeof(dei));
730 memset(&hmd, 0, sizeof(hmd));
731 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
734 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
735 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
736 ast_copy_string(dei.org, org, sizeof(dei.org));
737 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
738 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
739 ast_copy_string(dei.country, country, sizeof(dei.country));
740 ast_copy_string(dei.email, email, sizeof(dei.email));
741 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
744 /* If we do not have a canonical result, keep looking */
745 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
747 AST_LIST_LOCK(&peers);
748 if (ast_test_flag(st->trans, FLAG_DEAD)) {
750 ast_log(LOG_DEBUG, "Our transaction went away!\n");
751 st->trans->thread = 0;
752 destroy_trans(st->trans, 0);
755 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
756 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
757 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
758 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
759 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
760 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
761 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
762 if (!ast_strlen_zero(dei.ipaddr))
763 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
765 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
766 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
767 st->trans->thread = 0;
769 AST_LIST_UNLOCK(&peers);
774 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
776 struct dundi_query_state *st;
782 pthread_t lookupthread;
784 if (ies->eidcount > 1) {
785 /* Since it is a requirement that the first EID is the authenticating host
786 and the last EID is the root, it is permissible that the first and last EID
787 could be the same. In that case, we should go ahead copy only the "root" section
788 since we will not need it for authentication. */
789 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
792 totallen = sizeof(struct dundi_query_state);
793 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
794 st = ast_calloc(1, totallen);
796 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
797 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
799 st->ttl = ies->ttl - 1;
803 for (x=skipfirst;ies->eids[x];x++) {
804 st->eids[x-skipfirst] = (dundi_eid *)s;
805 *st->eids[x-skipfirst] = *ies->eids[x];
806 s += sizeof(dundi_eid);
809 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);
812 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
813 struct dundi_ie_data ied = { 0, };
815 ast_log(LOG_WARNING, "Unable to create thread!\n");
817 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
818 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
822 struct dundi_ie_data ied = { 0, };
823 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
824 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
830 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
835 char eidpeer_str[20];
836 char eidroot_str[20];
841 expiration = dundi_cache_time;
843 /* Only cache hint if "don't ask" is there... */
844 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
847 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
849 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
850 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
851 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
852 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
855 timeout += expiration;
856 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
858 ast_db_put("dundi/cache", key1, data);
860 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
861 ast_db_put("dundi/cache", key2, data);
863 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
867 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
873 char eidpeer_str[20];
874 char eidroot_str[20];
878 expiration = dundi_cache_time;
880 /* Keep pushes a little longer, cut pulls a little short */
887 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
888 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
889 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
890 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
891 /* Build request string */
893 timeout += expiration;
894 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
895 for (x=start;x<req->respcount;x++) {
896 /* Skip anything with an illegal pipe in it */
897 if (strchr(req->dr[x].dest, '|'))
899 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
900 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
901 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
903 ast_db_put("dundi/cache", key1, data);
904 ast_db_put("dundi/cache", key2, data);
908 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
910 struct dundi_query_state *st;
913 struct dundi_ie_data ied;
915 struct dundi_result dr2[MAX_RESULTS];
916 struct dundi_request dr;
917 struct dundi_hint_metadata hmd;
919 struct dundi_mapping *cur;
923 pthread_t lookupthread;
925 memset(&dr2, 0, sizeof(dr2));
926 memset(&dr, 0, sizeof(dr));
927 memset(&hmd, 0, sizeof(hmd));
929 /* Forge request structure to hold answers for cache */
930 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
932 dr.maxcount = MAX_RESULTS;
933 dr.expiration = dundi_cache_time;
935 dr.pfds[0] = dr.pfds[1] = -1;
937 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
938 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
940 for (x=0;x<ies->anscount;x++) {
941 if (trans->parent->respcount < trans->parent->maxcount) {
942 /* Make sure it's not already there */
943 for (z=0;z<trans->parent->respcount;z++) {
944 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
945 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
948 if (z == trans->parent->respcount) {
949 /* Copy into parent responses */
950 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
951 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
952 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
953 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
954 if (ies->expiration > 0)
955 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
957 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
958 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
959 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
960 &ies->answers[x]->eid);
961 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
962 sizeof(trans->parent->dr[trans->parent->respcount].dest));
963 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
964 sizeof(trans->parent->dr[trans->parent->respcount].tech));
965 trans->parent->respcount++;
966 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
967 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
968 /* Update weight if appropriate */
969 trans->parent->dr[z].weight = ies->answers[x]->weight;
972 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
973 trans->parent->number, trans->parent->dcontext);
976 /* Save all the results (if any) we had. Even if no results, still cache lookup. */
977 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
979 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
981 totallen = sizeof(struct dundi_query_state);
982 /* Count matching map entries */
984 AST_LIST_TRAVERSE(&mappings, cur, list) {
985 if (!strcasecmp(cur->dcontext, ccontext))
989 /* If no maps, return -1 immediately */
993 if (ies->eidcount > 1) {
994 /* Since it is a requirement that the first EID is the authenticating host
995 and the last EID is the root, it is permissible that the first and last EID
996 could be the same. In that case, we should go ahead copy only the "root" section
997 since we will not need it for authentication. */
998 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1002 /* Prepare to run a query and then propagate that as necessary */
1003 totallen += mapcount * sizeof(struct dundi_mapping);
1004 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1005 st = ast_calloc(1, totallen);
1007 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1008 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1010 st->ttl = ies->ttl - 1;
1011 st->nocache = ies->cbypass;
1015 for (x=skipfirst;ies->eids[x];x++) {
1016 st->eids[x-skipfirst] = (dundi_eid *)s;
1017 *st->eids[x-skipfirst] = *ies->eids[x];
1018 st->directs[x-skipfirst] = ies->eid_direct[x];
1019 s += sizeof(dundi_eid);
1021 /* Append mappings */
1023 st->maps = (struct dundi_mapping *)s;
1024 AST_LIST_TRAVERSE(&mappings, cur, list) {
1025 if (!strcasecmp(cur->dcontext, ccontext)) {
1028 st->maps[x].list.next = NULL;
1033 st->nummaps = mapcount;
1035 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1037 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1039 ast_log(LOG_WARNING, "Unable to create thread!\n");
1041 memset(&ied, 0, sizeof(ied));
1042 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1043 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1047 ast_log(LOG_WARNING, "Out of memory!\n");
1048 memset(&ied, 0, sizeof(ied));
1049 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1050 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1056 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1058 struct dundi_query_state *st;
1061 struct dundi_ie_data ied;
1063 struct dundi_mapping *cur;
1067 pthread_t lookupthread;
1068 totallen = sizeof(struct dundi_query_state);
1069 /* Count matching map entries */
1070 AST_LIST_TRAVERSE(&mappings, cur, list) {
1071 if (!strcasecmp(cur->dcontext, ccontext))
1074 /* If no maps, return -1 immediately */
1078 if (ies->eidcount > 1) {
1079 /* Since it is a requirement that the first EID is the authenticating host
1080 and the last EID is the root, it is permissible that the first and last EID
1081 could be the same. In that case, we should go ahead copy only the "root" section
1082 since we will not need it for authentication. */
1083 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1087 totallen += mapcount * sizeof(struct dundi_mapping);
1088 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1089 st = ast_calloc(1, totallen);
1091 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1092 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1094 st->ttl = ies->ttl - 1;
1095 st->nocache = ies->cbypass;
1099 for (x=skipfirst;ies->eids[x];x++) {
1100 st->eids[x-skipfirst] = (dundi_eid *)s;
1101 *st->eids[x-skipfirst] = *ies->eids[x];
1102 st->directs[x-skipfirst] = ies->eid_direct[x];
1103 s += sizeof(dundi_eid);
1105 /* Append mappings */
1107 st->maps = (struct dundi_mapping *)s;
1108 AST_LIST_TRAVERSE(&mappings, cur, list) {
1109 if (!strcasecmp(cur->dcontext, ccontext)) {
1112 st->maps[x].list.next = NULL;
1117 st->nummaps = mapcount;
1119 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1121 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1123 ast_log(LOG_WARNING, "Unable to create thread!\n");
1125 memset(&ied, 0, sizeof(ied));
1126 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1127 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1131 ast_log(LOG_WARNING, "Out of memory!\n");
1132 memset(&ied, 0, sizeof(ied));
1133 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1134 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1140 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1143 char *ptr, *term, *src;
1145 struct ast_flags flags;
1151 /* Build request string */
1152 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1155 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1156 int expiration = timeout - now;
1157 if (expiration > 0) {
1159 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
1161 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
1163 term = strchr(ptr, '|');
1166 src = strrchr(ptr, '/');
1173 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1174 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1175 /* Make sure it's not already there */
1176 for (z=0;z<req->respcount;z++) {
1177 if ((req->dr[z].techint == tech) &&
1178 !strcmp(req->dr[z].dest, ptr))
1181 if (z == req->respcount) {
1182 /* Copy into parent responses */
1183 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1184 req->dr[req->respcount].weight = weight;
1185 req->dr[req->respcount].techint = tech;
1186 req->dr[req->respcount].expiration = expiration;
1187 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1188 dundi_eid_to_str(req->dr[req->respcount].eid_str,
1189 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1190 ast_copy_string(req->dr[req->respcount].dest, ptr,
1191 sizeof(req->dr[req->respcount].dest));
1192 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1193 sizeof(req->dr[req->respcount].tech));
1195 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1196 } else if (req->dr[z].weight > weight)
1197 req->dr[z].weight = weight;
1201 /* We found *something* cached */
1202 if (expiration < *lowexpiration)
1203 *lowexpiration = expiration;
1206 ast_db_del("dundi/cache", key);
1208 ast_db_del("dundi/cache", key);
1214 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
1218 char eidroot_str[20];
1222 char eid_str_full[20];
1227 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1228 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1229 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1230 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
1231 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1232 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
1233 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1234 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1235 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1237 if (!req->respcount) {
1239 /* Look and see if we have a hint that would preclude us from looking at this
1240 peer for this number. */
1241 if (!(tmp[x] = req->number[x]))
1244 /* Check for hints */
1245 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
1246 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1247 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
1248 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1249 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1250 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1252 if (strlen(tmp) > strlen(req->hmd->exten)) {
1253 /* Update meta data if appropriate */
1254 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1264 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1266 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1268 if (!trans->addr.sin_addr.s_addr)
1269 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
1270 trans->us_eid = p->us_eid;
1271 trans->them_eid = p->eid;
1272 /* Enable encryption if appropriate */
1273 if (!ast_strlen_zero(p->inkey))
1274 ast_set_flag(trans, FLAG_ENCRYPT);
1276 trans->autokilltimeout = p->maxms;
1277 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1278 if (p->lastms > 1) {
1279 trans->retranstimer = p->lastms * 2;
1280 /* Keep it from being silly */
1281 if (trans->retranstimer < 150)
1282 trans->retranstimer = 150;
1284 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1285 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1287 trans->autokilltimeout = global_autokilltimeout;
1290 /*! \note Called with the peers list already locked */
1291 static int do_register_expire(const void *data)
1293 struct dundi_peer *peer = (struct dundi_peer *)data;
1296 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1297 peer->registerexpire = -1;
1299 memset(&peer->addr, 0, sizeof(peer->addr));
1303 static int update_key(struct dundi_peer *peer)
1305 unsigned char key[16];
1306 struct ast_key *ekey, *skey;
1309 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1311 ast_aes_encrypt_key(key, &peer->us_ecx);
1312 ast_aes_decrypt_key(key, &peer->us_dcx);
1313 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1315 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1316 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1319 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1321 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1322 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1325 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1326 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1329 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1330 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1333 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1334 peer->sentfullkey = 0;
1336 time(&peer->keyexpire);
1337 peer->keyexpire += dundi_key_ttl;
1342 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1344 unsigned char curblock[16];
1346 memcpy(curblock, iv, sizeof(curblock));
1349 curblock[x] ^= src[x];
1350 ast_aes_encrypt(curblock, dst, ecx);
1351 memcpy(curblock, dst, sizeof(curblock));
1358 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1360 unsigned char lastblock[16];
1362 memcpy(lastblock, iv, sizeof(lastblock));
1364 ast_aes_decrypt(src, dst, dcx);
1366 dst[x] ^= lastblock[x];
1367 memcpy(lastblock, src, sizeof(lastblock));
1375 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)
1377 int space = *dstlen;
1378 unsigned long bytes;
1379 struct dundi_hdr *h;
1380 unsigned char *decrypt_space;
1381 decrypt_space = alloca(srclen);
1384 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1386 h = (struct dundi_hdr *)dst;
1389 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1391 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
1395 *dstlen = bytes + 6;
1396 /* Return new header */
1400 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1402 unsigned char *compress_space;
1405 unsigned long bytes;
1406 struct dundi_ie_data ied;
1407 struct dundi_peer *peer;
1408 unsigned char iv[16];
1409 len = pack->datalen + pack->datalen / 100 + 42;
1410 compress_space = alloca(len);
1411 if (compress_space) {
1412 memset(compress_space, 0, len);
1413 /* We care about everthing save the first 6 bytes of header */
1415 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1418 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
1421 memset(&ied, 0, sizeof(ied));
1422 /* Say who we are */
1423 if (!pack->h->iseqno && !pack->h->oseqno) {
1424 /* Need the key in the first copy */
1425 if (!(peer = find_peer(&trans->them_eid)))
1427 if (update_key(peer))
1429 if (!peer->sentfullkey)
1430 ast_set_flag(trans, FLAG_SENDFULLKEY);
1431 /* Append key data */
1432 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1433 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1434 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1435 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1437 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1439 /* Setup contexts */
1440 trans->ecx = peer->us_ecx;
1441 trans->dcx = peer->us_dcx;
1443 /* We've sent the full key */
1444 peer->sentfullkey = 1;
1446 /* Build initialization vector */
1448 /* Add the field, rounded up to 16 bytes */
1449 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1451 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1452 ast_log(LOG_NOTICE, "Final packet too large!\n");
1455 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1456 ied.pos += ((bytes + 15) / 16) * 16;
1457 /* Reconstruct header */
1458 pack->datalen = sizeof(struct dundi_hdr);
1459 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1460 pack->h->cmdflags = 0;
1461 memcpy(pack->h->ies, ied.buf, ied.pos);
1462 pack->datalen += ied.pos;
1468 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
1470 unsigned char dst[128];
1472 struct ast_key *key, *skey;
1475 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
1476 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1479 } else if (!newkey || !newsig)
1481 if (!memcmp(peer->rxenckey, newkey, 128) &&
1482 !memcmp(peer->rxenckey + 128, newsig, 128)) {
1483 /* By definition, a match */
1487 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1489 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1490 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1494 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1496 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1497 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1501 /* First check signature */
1502 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1506 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1509 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1512 /* Decrypted, passes signature */
1514 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
1515 memcpy(peer->rxenckey, newkey, 128);
1516 memcpy(peer->rxenckey + 128, newsig, 128);
1517 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1518 ast_aes_decrypt_key(dst, &peer->them_dcx);
1519 ast_aes_encrypt_key(dst, &peer->them_ecx);
1523 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1525 struct permission *cur, *perm;
1527 memcpy(peer_dst, peer_src, sizeof(*peer_dst));
1529 memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1530 memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1532 AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1533 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1536 perm->allow = cur->allow;
1537 strcpy(perm->name, cur->name);
1539 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1542 AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1543 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1546 perm->allow = cur->allow;
1547 strcpy(perm->name, cur->name);
1549 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1553 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1555 /* Handle canonical command / response */
1556 int final = hdr->cmdresp & 0x80;
1557 int cmd = hdr->cmdresp & 0x7f;
1562 unsigned char *bufcpy;
1563 struct dundi_ie_data ied;
1564 struct dundi_ies ies;
1565 struct dundi_peer *peer = NULL;
1568 memset(&ied, 0, sizeof(ied));
1569 memset(&ies, 0, sizeof(ies));
1571 bufcpy = alloca(datalen);
1574 /* Make a copy for parsing */
1575 memcpy(bufcpy, hdr->ies, datalen);
1577 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1578 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1579 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1584 case DUNDI_COMMAND_DPDISCOVER:
1585 case DUNDI_COMMAND_EIDQUERY:
1586 case DUNDI_COMMAND_PRECACHERQ:
1587 if (cmd == DUNDI_COMMAND_EIDQUERY)
1588 resp = DUNDI_COMMAND_EIDRESPONSE;
1589 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1590 resp = DUNDI_COMMAND_PRECACHERP;
1592 resp = DUNDI_COMMAND_DPRESPONSE;
1593 /* A dialplan or entity discover -- qualify by highest level entity */
1594 peer = find_peer(ies.eids[0]);
1596 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1597 dundi_send(trans, resp, 0, 1, &ied);
1600 trans->us_eid = peer->us_eid;
1601 if (strlen(peer->inkey)) {
1602 hasauth = encrypted;
1606 /* Okay we're authentiated and all, now we check if they're authorized */
1607 if (!ies.called_context)
1608 ies.called_context = "e164";
1609 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1610 res = dundi_answer_entity(trans, &ies, ies.called_context);
1612 if (ast_strlen_zero(ies.called_number)) {
1613 /* They're not permitted to access that context */
1614 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1615 dundi_send(trans, resp, 0, 1, &ied);
1616 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1617 (peer->model & DUNDI_MODEL_INBOUND) &&
1618 has_permission(&peer->permit, ies.called_context)) {
1619 res = dundi_answer_query(trans, &ies, ies.called_context);
1621 /* There is no such dundi context */
1622 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1623 dundi_send(trans, resp, 0, 1, &ied);
1625 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1626 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1627 has_permission(&peer->include, ies.called_context)) {
1628 res = dundi_prop_precache(trans, &ies, ies.called_context);
1630 /* There is no such dundi context */
1631 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1632 dundi_send(trans, resp, 0, 1, &ied);
1635 /* They're not permitted to access that context */
1636 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1637 dundi_send(trans, resp, 0, 1, &ied);
1641 /* They're not permitted to access that context */
1642 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1643 dundi_send(trans, resp, 0, 1, &ied);
1647 case DUNDI_COMMAND_REGREQ:
1648 /* A register request -- should only have one entity */
1649 peer = find_peer(ies.eids[0]);
1651 /* if the peer is not found and we have a valid 'any_peer' setting */
1652 if (any_peer && peer == any_peer) {
1653 /* copy any_peer into a new peer object */
1654 peer = ast_calloc(1, sizeof(*peer));
1656 deep_copy_peer(peer, any_peer);
1658 /* set EID to remote EID */
1659 peer->eid = *ies.eids[0];
1661 AST_LIST_LOCK(&peers);
1662 AST_LIST_INSERT_HEAD(&peers, peer, list);
1663 AST_LIST_UNLOCK(&peers);
1667 if (!peer || !peer->dynamic) {
1668 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1669 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1672 trans->us_eid = peer->us_eid;
1673 if (!ast_strlen_zero(peer->inkey)) {
1674 hasauth = encrypted;
1678 int expire = default_expiration;
1681 if (peer->registerexpire > -1)
1682 ast_sched_del(sched, peer->registerexpire);
1683 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1684 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1685 ntohs(trans->addr.sin_port), expire);
1686 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1687 if (inaddrcmp(&peer->addr, &trans->addr)) {
1688 ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
1689 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1690 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1694 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1695 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
1696 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
1698 qualify_peer(peer, 1);
1702 case DUNDI_COMMAND_DPRESPONSE:
1703 /* A dialplan response, lets see what we got... */
1704 if (ies.cause < 1) {
1705 /* Success of some sort */
1707 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1708 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1709 authpass = encrypted;
1713 /* Pass back up answers */
1714 if (trans->parent && trans->parent->dr) {
1715 y = trans->parent->respcount;
1716 for (x=0;x<ies.anscount;x++) {
1717 if (trans->parent->respcount < trans->parent->maxcount) {
1718 /* Make sure it's not already there */
1719 for (z=0;z<trans->parent->respcount;z++) {
1720 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1721 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1724 if (z == trans->parent->respcount) {
1725 /* Copy into parent responses */
1726 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1727 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1728 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1729 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1730 if (ies.expiration > 0)
1731 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1733 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1734 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1735 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1736 &ies.answers[x]->eid);
1737 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1738 sizeof(trans->parent->dr[trans->parent->respcount].dest));
1739 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1740 sizeof(trans->parent->dr[trans->parent->respcount].tech));
1741 trans->parent->respcount++;
1742 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1743 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
1744 /* Update weight if appropriate */
1745 trans->parent->dr[z].weight = ies.answers[x]->weight;
1748 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1749 trans->parent->number, trans->parent->dcontext);
1751 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1752 the cache know if this request was unaffected by our entity list. */
1753 cache_save(&trans->them_eid, trans->parent, y,
1754 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1756 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1757 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1758 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1759 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1760 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1761 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1762 sizeof(trans->parent->hmd->exten));
1765 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1768 if (ies.expiration > 0) {
1769 if (trans->parent->expiration > ies.expiration) {
1770 trans->parent->expiration = ies.expiration;
1774 /* Close connection if not final */
1776 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1780 /* Auth failure, check for data */
1782 /* Cancel if they didn't already */
1783 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1787 case DUNDI_COMMAND_EIDRESPONSE:
1788 /* A dialplan response, lets see what we got... */
1789 if (ies.cause < 1) {
1790 /* Success of some sort */
1792 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
1793 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1794 authpass = encrypted;
1798 /* Pass back up answers */
1799 if (trans->parent && trans->parent->dei && ies.q_org) {
1800 if (!trans->parent->respcount) {
1801 trans->parent->respcount++;
1803 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1805 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1807 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1808 if (ies.q_stateprov)
1809 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1811 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1813 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1815 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1817 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1818 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1819 /* If it's them, update our address */
1820 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1824 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1825 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1828 /* Close connection if not final */
1830 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1834 /* Auth failure, check for data */
1836 /* Cancel if they didn't already */
1837 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1841 case DUNDI_COMMAND_REGRESPONSE:
1842 /* A dialplan response, lets see what we got... */
1843 if (ies.cause < 1) {
1845 /* Success of some sort */
1846 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1847 hasauth = encrypted;
1852 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1854 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1855 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
1859 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),
1860 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1861 /* Close connection if not final */
1863 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1866 /* Auth failure, cancel if they didn't for some reason */
1868 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1872 case DUNDI_COMMAND_INVALID:
1873 case DUNDI_COMMAND_NULL:
1874 case DUNDI_COMMAND_PRECACHERP:
1875 /* Do nothing special */
1877 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1879 case DUNDI_COMMAND_ENCREJ:
1880 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1881 /* No really, it's over at this point */
1883 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1885 /* Send with full key */
1886 ast_set_flag(trans, FLAG_SENDFULLKEY);
1888 /* Ooops, we got a final message, start by sending ACK... */
1889 dundi_ack(trans, hdr->cmdresp & 0x80);
1890 trans->aseqno = trans->iseqno;
1891 /* Now, we gotta create a new transaction */
1892 if (!reset_transaction(trans)) {
1893 /* Make sure handle_frame doesn't destroy us */
1894 hdr->cmdresp &= 0x7f;
1895 /* Parse the message we transmitted */
1896 memset(&ies, 0, sizeof(ies));
1897 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1898 /* Reconstruct outgoing encrypted packet */
1899 memset(&ied, 0, sizeof(ied));
1900 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1901 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1902 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1904 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1905 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
1906 peer->sentfullkey = 1;
1911 case DUNDI_COMMAND_ENCRYPT:
1913 /* No nested encryption! */
1914 if ((trans->iseqno == 1) && !trans->oseqno) {
1915 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1916 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1917 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1919 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1923 apply_peer(trans, peer);
1924 /* Key passed, use new contexts for this session */
1925 trans->ecx = peer->them_ecx;
1926 trans->dcx = peer->them_dcx;
1928 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1929 struct dundi_hdr *dhdr;
1930 unsigned char decoded[MAX_PACKET_SIZE];
1932 ddatalen = sizeof(decoded);
1933 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1935 /* Handle decrypted response */
1937 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1938 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1939 /* Carry back final flag */
1940 hdr->cmdresp |= dhdr->cmdresp & 0x80;
1944 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
1949 /* Turn off encryption */
1950 ast_clear_flag(trans, FLAG_ENCRYPT);
1951 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1955 /* Send unknown command if we don't know it, with final flag IFF it's the
1956 first command in the dialog and only if we haven't recieved final notification */
1958 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
1959 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
1965 static void destroy_packet(struct dundi_packet *pack, int needfree);
1966 static void destroy_packets(struct packetlist *p)
1968 struct dundi_packet *pack;
1970 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1971 if (pack->retransid > -1)
1972 ast_sched_del(sched, pack->retransid);
1978 static int ack_trans(struct dundi_transaction *trans, int iseqno)
1980 struct dundi_packet *pack;
1982 /* Ack transmitted packet corresponding to iseqno */
1983 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
1984 if ((pack->h->oseqno + 1) % 255 == iseqno) {
1985 destroy_packet(pack, 0);
1986 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
1987 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
1988 destroy_packets(&trans->lasttrans);
1990 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
1991 if (trans->autokillid > -1)
1992 ast_sched_del(sched, trans->autokillid);
1993 trans->autokillid = -1;
2001 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
2003 struct dundi_transaction *trans;
2004 trans = find_transaction(h, sin);
2006 dundi_reject(h, sin);
2009 /* Got a transaction, see where this header fits in */
2010 if (h->oseqno == trans->iseqno) {
2011 /* Just what we were looking for... Anything but ack increments iseqno */
2012 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2013 /* If final, we're done */
2014 destroy_trans(trans, 0);
2017 if (h->cmdresp != DUNDI_COMMAND_ACK) {
2018 trans->oiseqno = trans->iseqno;
2020 handle_command_response(trans, h, datalen, 0);
2022 if (trans->aseqno != trans->iseqno) {
2023 dundi_ack(trans, h->cmdresp & 0x80);
2024 trans->aseqno = trans->iseqno;
2026 /* Delete any saved last transmissions */
2027 destroy_packets(&trans->lasttrans);
2028 if (h->cmdresp & 0x80) {
2029 /* Final -- destroy now */
2030 destroy_trans(trans, 0);
2032 } else if (h->oseqno == trans->oiseqno) {
2033 /* Last incoming sequence number -- send ACK without processing */
2034 dundi_ack(trans, 0);
2036 /* Out of window -- simply drop */
2038 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
2043 static int socket_read(int *id, int fd, short events, void *cbdata)
2045 struct sockaddr_in sin;
2047 struct dundi_hdr *h;
2048 char buf[MAX_PACKET_SIZE];
2049 socklen_t len = sizeof(sin);
2051 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
2053 if (errno != ECONNREFUSED)
2054 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2057 if (res < sizeof(struct dundi_hdr)) {
2058 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2062 h = (struct dundi_hdr *) buf;
2064 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2065 AST_LIST_LOCK(&peers);
2066 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2067 AST_LIST_UNLOCK(&peers);
2071 static void build_secret(char *secret, int seclen)
2073 unsigned char tmp[16];
2077 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2078 /* Eliminate potential bad characters */
2079 while((s = strchr(secret, ';'))) *s = '+';
2080 while((s = strchr(secret, '/'))) *s = '+';
2081 while((s = strchr(secret, ':'))) *s = '+';
2082 while((s = strchr(secret, '@'))) *s = '+';
2086 static void save_secret(const char *newkey, const char *oldkey)
2090 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2092 snprintf(tmp, sizeof(tmp), "%s", newkey);
2093 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2094 ast_db_put(secretpath, "secret", tmp);
2095 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2096 ast_db_put(secretpath, "secretexpiry", tmp);
2099 static void load_password(void)
2106 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2107 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2108 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2109 current = strchr(tmp, ';');
2116 if ((time(NULL) - expired) < 0) {
2117 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2118 expired = time(NULL) + DUNDI_SECRET_TIME;
2119 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2128 /* Current key is still valid, just setup rotatation properly */
2129 ast_copy_string(cursecret, current, sizeof(cursecret));
2130 rotatetime = expired;
2132 /* Current key is out of date, rotate or eliminate all together */
2133 build_secret(cursecret, sizeof(cursecret));
2134 save_secret(cursecret, last);
2138 static void check_password(void)
2145 printf("%ld/%ld\n", now, rotatetime);
2147 if ((now - rotatetime) >= 0) {
2148 /* Time to rotate keys */
2149 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2150 build_secret(cursecret, sizeof(cursecret));
2151 save_secret(cursecret, oldsecret);
2155 static void *network_thread(void *ignore)
2157 /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2158 from the network, and queue them for delivery to the channels */
2160 /* Establish I/O callback for socket read */
2161 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2163 while (!dundi_shutdown) {
2164 res = ast_sched_wait(sched);
2165 if ((res > 1000) || (res < 0))
2167 res = ast_io_wait(io, res);
2169 AST_LIST_LOCK(&peers);
2170 ast_sched_runq(sched);
2171 AST_LIST_UNLOCK(&peers);
2176 netthreadid = AST_PTHREADT_NULL;
2181 static void *process_precache(void *ign)
2183 struct dundi_precache_queue *qe;
2189 while (!dundi_shutdown) {
2192 AST_LIST_LOCK(&pcq);
2193 if ((qe = AST_LIST_FIRST(&pcq))) {
2194 if (!qe->expiration) {
2195 /* Gone... Remove... */
2196 AST_LIST_REMOVE_HEAD(&pcq, list);
2198 } else if (qe->expiration < now) {
2199 /* Process this entry */
2201 ast_copy_string(context, qe->context, sizeof(context));
2202 ast_copy_string(number, qe->number, sizeof(number));
2206 AST_LIST_UNLOCK(&pcq);
2208 dundi_precache(context, number);
2213 precachethreadid = AST_PTHREADT_NULL;
2218 static int start_network_thread(void)
2220 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2221 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2225 static char *dundi_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2229 e->command = "dundi debug";
2231 "Usage: dundi debug\n"
2232 " Enables dumping of DUNDi packets for debugging purposes\n";
2238 return CLI_SHOWUSAGE;
2240 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2244 static char *dundi_do_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2248 e->command = "dundi store history";
2250 "Usage: dundi store history\n"
2251 " Enables storing of DUNDi requests and times for debugging\n"
2258 return CLI_SHOWUSAGE;
2259 global_storehistory = 1;
2260 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2264 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2269 e->command = "dundi flush [stats]";
2271 "Usage: dundi flush [stats]\n"
2272 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2273 "'stats' is present, clears timer statistics instead of normal\n"
2279 if ((a->argc < 2) || (a->argc > 3))
2280 return CLI_SHOWUSAGE;
2282 if (!strcasecmp(a->argv[2], "stats"))
2285 return CLI_SHOWUSAGE;
2288 /* Flush statistics */
2289 struct dundi_peer *p;
2291 AST_LIST_LOCK(&peers);
2292 AST_LIST_TRAVERSE(&peers, p, list) {
2293 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2295 ast_free(p->lookups[x]);
2296 p->lookups[x] = NULL;
2297 p->lookuptimes[x] = 0;
2301 AST_LIST_UNLOCK(&peers);
2303 ast_db_deltree("dundi/cache", NULL);
2304 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2309 static char *dundi_no_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2313 e->command = "dundi no debug";
2315 "Usage: dundi no debug\n"
2316 " Disables dumping of DUNDi packets for debugging purposes\n";
2322 return CLI_SHOWUSAGE;
2324 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2328 static char *dundi_no_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2332 e->command = "dundi no store history";
2334 "Usage: dundi no store history\n"
2335 " Disables storing of DUNDi requests and times for debugging\n"
2342 return CLI_SHOWUSAGE;
2343 global_storehistory = 0;
2344 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2348 static char *model2str(int model)
2351 case DUNDI_MODEL_INBOUND:
2353 case DUNDI_MODEL_OUTBOUND:
2355 case DUNDI_MODEL_SYMMETRIC:
2362 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2366 struct dundi_peer *p;
2371 AST_LIST_LOCK(&peers);
2373 AST_LIST_TRAVERSE(&peers, p, list) {
2374 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2375 if (!strncasecmp(word, s, len) && ++which > state) {
2376 ret = ast_strdup(s);
2380 AST_LIST_UNLOCK(&peers);
2384 static int rescomp(const void *a, const void *b)
2386 const struct dundi_result *resa, *resb;
2389 if (resa->weight < resb->weight)
2391 if (resa->weight > resb->weight)
2396 static void sort_results(struct dundi_result *results, int count)
2398 qsort(results, count, sizeof(results[0]), rescomp);
2401 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2409 struct dundi_result dr[MAX_RESULTS];
2410 struct timeval start;
2413 e->command = "dundi lookup";
2415 "Usage: dundi lookup <number>[@context] [bypass]\n"
2416 " Lookup the given number within the given DUNDi context\n"
2417 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2418 "keyword is specified.\n";
2424 if ((a->argc < 3) || (a->argc > 4))
2425 return CLI_SHOWUSAGE;
2427 if (!strcasecmp(a->argv[3], "bypass"))
2430 return CLI_SHOWUSAGE;
2432 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2433 context = strchr(tmp, '@');
2438 start = ast_tvnow();
2439 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2442 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2444 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2446 sort_results(dr, res);
2447 for (x=0;x<res;x++) {
2448 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));
2449 ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2451 ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2455 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2460 struct timeval start;
2463 e->command = "dundi precache";
2465 "Usage: dundi precache <number>[@context]\n"
2466 " Lookup the given number within the given DUNDi context\n"
2467 "(or e164 if none is specified) and precaches the results to any\n"
2468 "upstream DUNDi push servers.\n";
2473 if ((a->argc < 3) || (a->argc > 3))
2474 return CLI_SHOWUSAGE;
2475 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2476 context = strchr(tmp, '@');
2481 start = ast_tvnow();
2482 res = dundi_precache(context, tmp);
2485 ast_cli(a->fd, "DUNDi precache returned error.\n");
2487 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2488 ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2492 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2498 struct dundi_entity_info dei;
2501 e->command = "dundi query";
2503 "Usage: dundi query <entity>[@context]\n"
2504 " Attempts to retrieve contact information for a specific\n"
2505 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2506 "e164 if none is specified).\n";
2511 if ((a->argc < 3) || (a->argc > 3))
2512 return CLI_SHOWUSAGE;
2513 if (dundi_str_to_eid(&eid, a->argv[2])) {
2514 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2515 return CLI_SHOWUSAGE;
2517 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2518 context = strchr(tmp, '@');
2523 res = dundi_query_eid(&dei, context, eid);
2525 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2527 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2529 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2530 ast_cli(a->fd, "Department: %s\n", dei.orgunit);
2531 ast_cli(a->fd, "Organization: %s\n", dei.org);
2532 ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
2533 ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
2534 ast_cli(a->fd, "Country: %s\n", dei.country);
2535 ast_cli(a->fd, "E-mail: %s\n", dei.email);
2536 ast_cli(a->fd, "Phone: %s\n", dei.phone);
2537 ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
2542 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2544 struct dundi_peer *peer;
2545 struct permission *p;
2551 e->command = "dundi show peer";
2553 "Usage: dundi show peer [peer]\n"
2554 " Provide a detailed description of a specifid DUNDi peer.\n";
2557 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2560 return CLI_SHOWUSAGE;
2561 AST_LIST_LOCK(&peers);
2562 AST_LIST_TRAVERSE(&peers, peer, list) {
2563 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2567 switch(peer->order) {
2572 order = "Secondary";
2578 order = "Quartiary";
2583 ast_cli(a->fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2584 ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
2585 ast_cli(a->fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2586 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2587 ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2588 ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2589 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2590 if (!AST_LIST_EMPTY(&peer->include))
2591 ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2592 AST_LIST_TRAVERSE(&peer->include, p, list)
2593 ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2594 if (!AST_LIST_EMPTY(&peer->permit))
2595 ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2596 AST_LIST_TRAVERSE(&peer->permit, p, list)
2597 ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2599 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2600 if (peer->lookups[x]) {
2602 ast_cli(a->fd, "Last few query times:\n");
2603 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2608 ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2610 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2611 AST_LIST_UNLOCK(&peers);
2615 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2617 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
2618 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
2619 struct dundi_peer *peer;
2620 int registeredonly=0;
2623 int online_peers = 0;
2624 int offline_peers = 0;
2625 int unmonitored_peers = 0;
2626 int total_peers = 0;
2629 e->command = "dundi show peers [registered|include|exclude|begin]";
2631 "Usage: dundi show peers [registered|include|exclude|begin]\n"
2632 " Lists all known DUNDi peers.\n"
2633 " If 'registered' is present, only registered peers are shown.\n";
2639 if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5))
2640 return CLI_SHOWUSAGE;
2641 if ((a->argc == 4)) {
2642 if (!strcasecmp(a->argv[3], "registered")) {
2645 return CLI_SHOWUSAGE;
2647 AST_LIST_LOCK(&peers);
2648 ast_cli(a->fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
2649 AST_LIST_TRAVERSE(&peers, peer, list) {
2651 int print_line = -1;
2654 if (registeredonly && !peer->addr.sin_addr.s_addr)
2657 if (peer->lastms < 0) {
2658 strcpy(status, "UNREACHABLE");
2661 else if (peer->lastms > peer->maxms) {
2662 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2665 else if (peer->lastms) {
2666 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2670 strcpy(status, "UNKNOWN");
2674 strcpy(status, "Unmonitored");
2675 unmonitored_peers++;
2678 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2680 strcpy(avgms, "Unavail");
2681 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2682 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2683 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2686 if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2688 } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2690 } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2698 ast_cli(a->fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2699 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2700 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
2703 ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2704 AST_LIST_UNLOCK(&peers);
2710 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2712 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2713 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2714 struct dundi_transaction *trans;
2717 e->command = "dundi show trans";
2719 "Usage: dundi show trans\n"
2720 " Lists all known DUNDi transactions.\n";
2726 return CLI_SHOWUSAGE;
2727 AST_LIST_LOCK(&peers);
2728 ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2729 AST_LIST_TRAVERSE(&alltrans, trans, all) {
2730 ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2731 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2733 AST_LIST_UNLOCK(&peers);
2739 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2744 e->command = "dundi show entityid";
2746 "Usage: dundi show entityid\n"
2747 " Displays the global entityid for this host.\n";
2753 return CLI_SHOWUSAGE;
2754 AST_LIST_LOCK(&peers);
2755 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2756 AST_LIST_UNLOCK(&peers);
2757 ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2761 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2763 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2764 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2765 struct dundi_request *req;
2769 e->command = "dundi show requests";
2771 "Usage: dundi show requests\n"
2772 " Lists all known pending DUNDi requests.\n";
2778 return CLI_SHOWUSAGE;
2779 AST_LIST_LOCK(&peers);
2780 ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2781 AST_LIST_TRAVERSE(&requests, req, list) {
2782 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2783 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2785 AST_LIST_UNLOCK(&peers);
2791 /* Grok-a-dial DUNDi */
2793 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2795 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2796 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2797 struct dundi_mapping *map;
2802 e->command = "dundi show mappings";
2804 "Usage: dundi show mappings\n"
2805 " Lists all known DUNDi mappings.\n";
2811 return CLI_SHOWUSAGE;
2812 AST_LIST_LOCK(&peers);
2813 ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2814 AST_LIST_TRAVERSE(&mappings, map, list) {
2815 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map));
2816 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2817 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2818 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2820 AST_LIST_UNLOCK(&peers);
2826 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2828 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2829 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2830 struct dundi_precache_queue *qe;
2835 e->command = "dundi show precache";
2837 "Usage: dundi show precache\n"
2838 " Lists all known DUNDi scheduled precache updates.\n";
2844 return CLI_SHOWUSAGE;
2846 ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2847 AST_LIST_LOCK(&pcq);
2848 AST_LIST_TRAVERSE(&pcq, qe, list) {
2849 s = qe->expiration - now;
2854 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2856 AST_LIST_UNLOCK(&pcq);
2863 static struct ast_cli_entry cli_dundi[] = {
2864 AST_CLI_DEFINE(dundi_do_debug, "Enable DUNDi debugging"),
2865 AST_CLI_DEFINE(dundi_no_debug, "Disable DUNDi debugging"),
2866 AST_CLI_DEFINE(dundi_do_store_history, "Enable DUNDi historic records"),
2867 AST_CLI_DEFINE(dundi_no_store_history, "Disable DUNDi historic records"),
2868 AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
2869 AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
2870 AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
2871 AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
2872 AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
2873 AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
2874 AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
2875 AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
2876 AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
2877 AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
2878 AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
2881 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
2883 struct dundi_transaction *trans;
2886 /* Don't allow creation of transactions to non-registered peers */
2887 if (p && !p->addr.sin_addr.s_addr)
2889 tid = get_trans_id();
2892 if (!(trans = ast_calloc(1, sizeof(*trans))))
2895 if (global_storehistory) {
2896 trans->start = ast_tvnow();
2897 ast_set_flag(trans, FLAG_STOREHIST);
2899 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2900 trans->autokillid = -1;
2902 apply_peer(trans, p);
2903 if (!p->sentfullkey)
2904 ast_set_flag(trans, FLAG_SENDFULLKEY);
2906 trans->strans = tid;
2907 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
2912 static int dundi_xmit(struct dundi_packet *pack)
2916 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
2917 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
2919 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
2920 ast_inet_ntoa(pack->parent->addr.sin_addr),
2921 ntohs(pack->parent->addr.sin_port), strerror(errno));
2928 static void destroy_packet(struct dundi_packet *pack, int needfree)
2931 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
2932 if (pack->retransid > -1)
2933 ast_sched_del(sched, pack->retransid);